From 6e68de01aadfe1c40eaa16786a6a4d225305e29a Mon Sep 17 00:00:00 2001 From: Misode Date: Wed, 23 Oct 2024 21:41:12 +0200 Subject: [PATCH] Refactor makeEdit so it returns the result node --- .../components/generator/McdocRenderer.tsx | 205 ++++++++++-------- src/app/components/generator/Tree.tsx | 18 +- src/app/components/previews/LootTable.ts | 5 +- src/app/components/previews/LootTable1204.ts | 5 +- src/app/services/Spyglass.ts | 6 +- 5 files changed, 136 insertions(+), 103 deletions(-) diff --git a/src/app/components/generator/McdocRenderer.tsx b/src/app/components/generator/McdocRenderer.tsx index 58f4d6d5..6727f4e8 100644 --- a/src/app/components/generator/McdocRenderer.tsx +++ b/src/app/components/generator/McdocRenderer.tsx @@ -8,7 +8,6 @@ import type { SimplifiedStructType } from '@spyglassmc/mcdoc/lib/runtime/checker import { useCallback } from 'preact/hooks' import type { TextDocument } from 'vscode-languageserver-textdocument' import { useLocale } from '../../contexts/Locale.jsx' -import type { AstEdit } from '../../services/Spyglass.js' import { Octicon } from '../Octicon.jsx' export interface McdocContext { @@ -16,9 +15,11 @@ export interface McdocContext { symbols: core.SymbolUtil, } +type MakeEdit = (edit: (range: core.Range) => JsonNode | undefined) => void + interface Props { node: JsonNode | undefined - makeEdit: (edit: AstEdit) => void + makeEdit: MakeEdit ctx: McdocContext } export function McdocRoot({ node, makeEdit, ctx } : Props) { @@ -90,18 +91,17 @@ function StringHead({ node, makeEdit }: Props) { const value = JsonStringNode.is(node) ? node.value : undefined const onChangeValue = useCallback((value: string) => { - makeEdit(() => { - replaceNode(node, (range, parent) => { - const newNode: JsonStringNode = { - type: 'json:string', - range, - options: json.parser.JsonStringOptions, - value: value, - valueMap: [{ inner: core.Range.create(0), outer: core.Range.create(range.start) }], - parent, - } - return newNode - }) + makeEdit((range) => { + if (value.length === 0) { + return undefined + } + return { + type: 'json:string', + range, + options: json.parser.JsonStringOptions, + value: value, + valueMap: [{ inner: core.Range.create(0), outer: core.Range.create(range.start) }], + } }) }, [node, makeEdit]) @@ -121,25 +121,21 @@ function NumericHead({ type, node, makeEdit }: NumericHeadProps) { if (number !== undefined && Number.isNaN(number)) { return } - makeEdit(() => { + makeEdit((range) => { if (number === undefined) { - removeNode(node) - return + return undefined } - replaceNode(node, (range, parent) => { - const newValue: core.FloatNode | core.LongNode = isFloat - ? { type: 'float', range, value: number } - : { type: 'long', range, value: BigInt(number) } - const newNode: JsonNumberNode = { - type: 'json:number', - range, - value: newValue, - children: [newValue], - parent, - } - newValue.parent = newNode - return newNode - }) + const newValue: core.FloatNode | core.LongNode = isFloat + ? { type: 'float', range, value: number } + : { type: 'long', range, value: BigInt(number) } + const newNode: JsonNumberNode = { + type: 'json:number', + range, + value: newValue, + children: [newValue], + } + newValue.parent = newNode + return newNode }) }, [isFloat, node, makeEdit]) @@ -150,19 +146,14 @@ function BooleanHead({ node, makeEdit }: Props) { const value = node && JsonBooleanNode.is(node) ? node.value : undefined const onSelect = useCallback((newValue: boolean) => { - makeEdit(() => { + makeEdit((range) => { if (value === newValue) { - removeNode(node) - } else { - replaceNode(node, (range, parent) => { - const newNode: JsonBooleanNode = { - type: 'json:boolean', - range, - value: newValue, - parent, - } - return newNode - }) + return undefined + } + return { + type: 'json:boolean', + range, + value: newValue, } }) }, [node, makeEdit, value]) @@ -188,23 +179,32 @@ function ListHead({ type, node, makeEdit, ctx }: ListHeadProps) { const onAdd = useCallback(() => { if (canAdd) { - makeEdit(() => { + makeEdit((range) => { const itemType: McdocType = type.kind === 'list' ? type.item : type.kind === 'byte_array' ? { kind: 'byte' } : type.kind === 'int_array' ? { kind: 'int' } : type.kind === 'long_array' ? { kind: 'long' } : { kind: 'any' } - addNode(node, (range, parent) => { - const newValue = getDefault(itemType, range, ctx) - const newNode: core.ItemNode = { - type: 'item', - range, - children: [newValue], - value: newValue, - parent, - } - return newNode - }) + const newValue = getDefault(itemType, range, ctx) + const newItem: core.ItemNode = { + type: 'item', + range, + children: [newValue], + value: newValue, + } + newValue.parent = newItem + if (JsonArrayNode.is(node)) { + node.children.push(newItem) + newItem.parent = node + return node + } + const newArray: JsonArrayNode = { + type: 'json:array', + range, + children: [newItem], + } + newItem.parent = newArray + return newArray }) } }, [type, node, makeEdit, canAdd]) @@ -260,14 +260,53 @@ function StructBody({ type, node, makeEdit, ctx }: StructBodyProps) { } return <> {staticFields.map(field => { - const key = (field.key as LiteralType).value.value - const child = node.children.find(p => p.key?.value === key)?.value + const key = (field.key as LiteralType).value.value.toString() + const childIndex = node.children.findIndex(p => p.key?.value === key) + const child = childIndex === -1 ? undefined : node.children[childIndex] + const childValue = child?.value + const makeFieldEdit: MakeEdit = (edit) => { + if (child) { + makeEdit(() => { + const newChild = edit(childValue?.range ?? core.Range.create(child.range.end)) + if (newChild === undefined) { + node.children.splice(childIndex, 1) + } else { + node.children[childIndex] = { + type: 'pair', + range: child.range, + key: child.key, + value: newChild, + } + } + return node + }) + } else { + const newChild = edit(core.Range.create(node.range.end)) + if (newChild) { + makeEdit(() => { + node.children.push({ + type: 'pair', + range: newChild.range, + key: { + type: 'json:string', + range: newChild.range, + options: json.parser.JsonStringOptions, + value: key, + valueMap: [{ inner: core.Range.create(0), outer: newChild.range }], + }, + value: newChild, + }) + return node + }) + } + } + } return
- +
- +
})} @@ -290,11 +329,23 @@ function ListBody({ type, node, makeEdit, ctx }: ListBodyProps) { const onRemoveItem = useCallback((index: number) => { makeEdit(() => { node.children.splice(index, 1) + return node }) }, [makeEdit, node]) return <> {node.children.map((item, index) => { const child = item.value + const makeItemEdit: MakeEdit = (edit) => { + makeEdit(() => { + const newChild = edit(child?.range ?? item.range) + node.children[index] = { + type: 'item', + range: item.range, + value: newChild, + } + return node + }) + } return
} - +
- + })} } -function addNode(parent: T | undefined, getNewNode: (range: core.Range, parent: T) => core.AstNode) { - if (parent?.children !== undefined) { - const range = core.Range.create(parent.range.end) - const newNode = getNewNode(range, parent) - parent.children.push(newNode) - } -} - -function replaceNode(node: T | undefined, getNewNode: (range: core.Range, parent: core.AstNode) => T) { - if (node !== undefined && node.parent?.children) { - const index = node.parent.children.findIndex(c => c === node) - if (index !== -1) { - const newNode = getNewNode(node.range, node.parent) - node.parent.children[index] = newNode - if (core.PairNode.is(node.parent)) { - ;(node.parent as core.Mutable>).value = newNode - } - } - } -} - -function removeNode(node: T | undefined) { - if (node !== undefined && node.parent?.children) { - if (core.PairNode.is(node.parent)) { - removeNode(node.parent) - } else { - const index = node.parent.children.findIndex(c => c === node) - node.parent.children.splice(index, 1) - } - } -} - function getDefault(type: McdocType, range: core.Range, ctx: McdocContext): JsonNode { if (type.kind === 'string') { return JsonStringNode.mock(range) diff --git a/src/app/components/generator/Tree.tsx b/src/app/components/generator/Tree.tsx index 61b9a438..de19de03 100644 --- a/src/app/components/generator/Tree.tsx +++ b/src/app/components/generator/Tree.tsx @@ -1,9 +1,9 @@ -import type { DocAndNode } from '@spyglassmc/core' +import type { DocAndNode, Range } from '@spyglassmc/core' +import type { JsonNode } from '@spyglassmc/json' import { JsonFileNode } from '@spyglassmc/json' import { useCallback, useErrorBoundary, useMemo } from 'preact/hooks' import { useLocale } from '../../contexts/index.js' import { useDocAndNode, useSpyglass } from '../../contexts/Spyglass.jsx' -import type { AstEdit } from '../../services/Spyglass.js' import type { McdocContext } from './McdocRenderer.jsx' import { McdocRoot } from './McdocRenderer.jsx' @@ -28,11 +28,21 @@ export function Tree({ docAndNode, onError }: TreePanelProps) { }) if (error) return <> - const makeEdit = useCallback((edit: AstEdit) => { + const makeEdit = useCallback((edit: (range: Range) => JsonNode | undefined) => { if (!service) { return } - service.applyEdit(docAndNode.doc.uri, edit) + service.applyEdit(docAndNode.doc.uri, (fileNode) => { + const jsonFile = fileNode.children[0] + if (JsonFileNode.is(jsonFile)) { + const original = jsonFile.children[0] + const newNode = edit(original.range) + if (newNode !== undefined) { + newNode.parent = fileNode + fileNode.children[0] = newNode + } + } + }) }, [service, docAndNode]) const ctx = useMemo(() => { diff --git a/src/app/components/previews/LootTable.ts b/src/app/components/previews/LootTable.ts index 39c1d900..1066cc12 100644 --- a/src/app/components/previews/LootTable.ts +++ b/src/app/components/previews/LootTable.ts @@ -122,8 +122,11 @@ function shuffle(array: T[], ctx: LootContext) { } function generateTable(table: any, consumer: ItemConsumer, ctx: LootContext) { + if (!Array.isArray(table.pools)) { + return + } const tableConsumer = decorateFunctions(table.functions ?? [], consumer, ctx) - for (const pool of table.pools ?? []) { + for (const pool of table.pools) { generatePool(pool, tableConsumer, ctx) } } diff --git a/src/app/components/previews/LootTable1204.ts b/src/app/components/previews/LootTable1204.ts index c485112c..fb901844 100644 --- a/src/app/components/previews/LootTable1204.ts +++ b/src/app/components/previews/LootTable1204.ts @@ -117,8 +117,11 @@ function shuffle(array: T[], ctx: LootContext) { } function generateTable(table: any, consumer: ItemConsumer, ctx: LootContext) { + if (!Array.isArray(table.pools)) { + return + } const tableConsumer = decorateFunctions(table.functions ?? [], consumer, ctx) - for (const pool of table.pools ?? []) { + for (const pool of table.pools) { generatePool(pool, tableConsumer, ctx) } } diff --git a/src/app/services/Spyglass.ts b/src/app/services/Spyglass.ts index d90e7144..4aab10b1 100644 --- a/src/app/services/Spyglass.ts +++ b/src/app/services/Spyglass.ts @@ -18,8 +18,6 @@ import { computeIfAbsent, genPath } from '../Utils.js' import { fetchBlockStates, fetchRegistries, fetchVanillaMcdoc, getVersionChecksum } from './DataFetcher.js' import type { VersionId } from './Versions.js' -export type AstEdit = (docAndNode: core.DocAndNode) => void - interface ClientDocument { doc: TextDocument undoStack: string[] @@ -111,7 +109,7 @@ export class SpyglassService { } } - public async applyEdit(uri: string, edit: AstEdit) { + public async applyEdit(uri: string, edit: (node: core.FileNode) => void) { const document = this.client.documents.get(uri) if (document !== undefined) { document.undoStack.push(document.doc.getText()) @@ -120,7 +118,7 @@ export class SpyglassService { if (!docAndNode) { throw new Error(`[Spyglass#openFile] Cannot get doc and node: ${uri}`) } - edit(docAndNode) + edit(docAndNode.node) const newText = this.service.format(docAndNode.node, docAndNode.doc, 2, true) TextDocument.update(document.doc, [{ text: newText }], document.doc.version + 1) await this.service.project.externals.fs.writeFile(uri, document.doc.getText())