From 51ecd67f38044b91495fd4172d691947edd9da11 Mon Sep 17 00:00:00 2001 From: TimberStalker Date: Tue, 27 Jan 2026 16:05:26 -0800 Subject: [PATCH 01/10] initial commit, current bug is that isAssignable gives false positives for non recursive nodes --- src/app/components/BtnMenu.tsx | 7 +- src/app/components/Octicon.tsx | 2 + src/app/components/generator/JsonFileView.tsx | 4 +- src/app/components/generator/McdocHelpers.ts | 8 +- .../components/generator/McdocRenderer.tsx | 218 +++++++++++++++++- src/styles/nodes.css | 3 + 6 files changed, 230 insertions(+), 12 deletions(-) diff --git a/src/app/components/BtnMenu.tsx b/src/app/components/BtnMenu.tsx index 0cc1eee3..7e7aaf07 100644 --- a/src/app/components/BtnMenu.tsx +++ b/src/app/components/BtnMenu.tsx @@ -9,15 +9,16 @@ interface BtnMenuProps extends JSX.HTMLAttributes { relative?: boolean, tooltip?: string, tooltipLoc?: 'se' | 'sw' | 'nw', + menuDir?: 'left' | 'right' children: ComponentChildren, } export function BtnMenu(props: BtnMenuProps) { - const { icon, label, relative, tooltip, tooltipLoc, children } = props + const { icon, label, relative, tooltip, tooltipLoc, menuDir, children } = props const [active, setActive] = useFocus() - return
+ return
setActive()} /> - {active &&
+ {active &&
{children}
}
diff --git a/src/app/components/Octicon.tsx b/src/app/components/Octicon.tsx index c2754857..d65b7bf8 100644 --- a/src/app/components/Octicon.tsx +++ b/src/app/components/Octicon.tsx @@ -71,4 +71,6 @@ export const Octicon = { upload: , x: , x_circle: , + move_to_bottom: , + wrap_inside: } diff --git a/src/app/components/generator/JsonFileView.tsx b/src/app/components/generator/JsonFileView.tsx index 734c63c0..440ff3ff 100644 --- a/src/app/components/generator/JsonFileView.tsx +++ b/src/app/components/generator/JsonFileView.tsx @@ -62,11 +62,11 @@ export function JsonFileView({ docAndNode, node }: JsonFileViewProps) { } const rootType = getRootType(resourceType) const type = simplifyType(rootType, ctx) - return type + return {type, rootType} }, [resourceType, ctx]) return
- {(ctx && mcdocType) && } + {(ctx && mcdocType) && }
} diff --git a/src/app/components/generator/McdocHelpers.ts b/src/app/components/generator/McdocHelpers.ts index 9928e15c..d8fb7140 100644 --- a/src/app/components/generator/McdocHelpers.ts +++ b/src/app/components/generator/McdocHelpers.ts @@ -42,7 +42,7 @@ export function getRootDefault(id: string, ctx: core.CheckerContext) { return getDefault(type, core.Range.create(0), ctx) } -export function getDefault(type: SimplifiedMcdocType, range: core.Range, ctx: core.CheckerContext): JsonNode { +export function getDefault(type: McdocType, range: core.Range, ctx: core.CheckerContext): JsonNode { if (type.kind === 'string') { return JsonStringNode.mock(range) } @@ -391,6 +391,7 @@ export function isSelectRegistry(registry: string) { const defaultCollapsedTypes = new Set([ '::java::data::worldgen::surface_rule::SurfaceRule', + '::java::data::worldgen::density_function::DensityFunctionRef' ]) export function isDefaultCollapsedType(type: McdocType) { @@ -399,7 +400,10 @@ export function isDefaultCollapsedType(type: McdocType) { } return false } - +export function canCollapse(node:JsonNode | undefined) +{ + return node !== undefined && (node.type === 'json:array' || node.type == 'json:object'); +} interface SimplifyNodeContext { key?: JsonStringNode parent?: JsonObjectNode diff --git a/src/app/components/generator/McdocRenderer.tsx b/src/app/components/generator/McdocRenderer.tsx index eeb5035e..f3c84b69 100644 --- a/src/app/components/generator/McdocRenderer.tsx +++ b/src/app/components/generator/McdocRenderer.tsx @@ -4,9 +4,10 @@ import type { JsonPairNode } from '@spyglassmc/json' import * as json from '@spyglassmc/json' import { JsonArrayNode, JsonBooleanNode, JsonNode, JsonNumberNode, JsonObjectNode, JsonStringNode } from '@spyglassmc/json' import { localeQuote } from '@spyglassmc/locales' -import type { ListType, LiteralType, McdocType, NumericType, PrimitiveArrayType, StringType, TupleType, UnionType } from '@spyglassmc/mcdoc' +import { DispatcherType, type ListType, type LiteralType, type McdocType, type NumericType, type PrimitiveArrayType, type ReferenceType, type StringType, type TupleType, type UnionType } from '@spyglassmc/mcdoc' +import { TypeDefSymbolData } from '@spyglassmc/mcdoc/lib/binder/index.js' import { handleAttributes } from '@spyglassmc/mcdoc/lib/runtime/attribute/index.js' -import type { SimplifiedEnum, SimplifiedMcdocType, SimplifiedMcdocTypeNoUnion, SimplifiedStructType, SimplifiedStructTypePairField } from '@spyglassmc/mcdoc/lib/runtime/checker/index.js' +import { isAssignable, type SimplifiedEnum, type SimplifiedMcdocType, type SimplifiedMcdocTypeNoUnion, type SimplifiedStructType, type SimplifiedStructTypePairField } from '@spyglassmc/mcdoc/lib/runtime/checker/index.js' import { getValues } from '@spyglassmc/mcdoc/lib/runtime/completer/index.js' import { Identifier as Identifier1204, ItemStack as ItemStack1204 } from 'deepslate-1.20.4/core' import { Identifier, ItemStack } from 'deepslate/core' @@ -20,10 +21,11 @@ import { useFocus } from '../../hooks/useFocus.js' import { checkVersion } from '../../services/Versions.js' import { generateColor, hexId, intToHexRgb, randomInt, randomSeed } from '../../Utils.js' import { Btn } from '../Btn.jsx' +import { BtnMenu } from '../BtnMenu.jsx' import { ItemDisplay } from '../ItemDisplay.jsx' import { ItemDisplay1204 } from '../ItemDisplay1204.jsx' import { Octicon } from '../Octicon.jsx' -import { formatIdentifier, getCategory, getChange, getDefault, getItemType, isDefaultCollapsedType, isFixedList, isInlineTuple, isListOrArray, isNumericType, isSelectRegistry, quickEqualTypes, simplifyType } from './McdocHelpers.js' +import { canCollapse, formatIdentifier, getCategory, getChange, getDefault, getItemType, isDefaultCollapsedType, isFixedList, isInlineTuple, isListOrArray, isNumericType, isSelectRegistry, quickEqualTypes, simplifyType } from './McdocHelpers.js' export interface McdocContext extends core.CheckerContext { makeEdit: MakeEdit @@ -38,7 +40,10 @@ interface Props { node: JsonNode | undefined ctx: McdocContext } -export function McdocRoot({ type, node, ctx } : Props) { +interface McdocRootProps extends Props{ + rootType: McdocType +} +export function McdocRoot({ type, rootType, node, ctx } : McdocRootProps) { const { locale } = useLocale() if (type.kind === 'struct' && type.fields.length > 0 && JsonObjectNode.is(node)) { @@ -50,6 +55,7 @@ export function McdocRoot({ type, node, ctx } : Props) { + {/* isRecursiveType(rootType) && */node != undefined && }
@@ -590,7 +596,7 @@ function StaticField({ pair, index, field, fieldKey, staticFields, isToggled, ex const child = pair?.value const childType = simplifyType(field.type, ctx, { key: pair?.key, parent: node }) - const canToggle = isDefaultCollapsedType(field.type) + const canToggle = isDefaultCollapsedType(field.type) && canCollapse(child) const isCollapsed = canToggle && isToggled !== true const makeFieldEdit = useCallback((edit) => { @@ -660,11 +666,213 @@ function StaticField({ pair, index, field, fieldKey, staticFields, isToggled, ex )} {!isCollapsed && } + {/* isRecursiveType(field.type) && */child != undefined && }
{!isCollapsed && } } +function isRecursiveType(type: McdocType | undefined) : type is ReferenceType | DispatcherType{ + const recursiveReferences = ['::java::data::worldgen::density_function::DensityFunctionRef'] + const recursiveDispatchers = ['worldgen/density_function'] + return (type?.kind == 'reference' && type.path != undefined && recursiveReferences.includes(type.path) ) + || (type?.kind == 'dispatcher' && type.parallelIndices.some(v => + { + if(v.kind == 'static') + { + return recursiveDispatchers.includes(v.value) + } else if(v.kind == 'dynamic') + { + for(const index of v.accessor) + { + if(recursiveDispatchers.includes(index.toString())) + { + return true; + } + } + } + return false + }) + ) +} +type RecursiveSlots = { + identifier: string + dispatcher: DispatcherType + fieldKey: string + fieldCount: number +} +interface RecursiveContextMenuProps{ + type: McdocType + node: JsonNode + ctx: McdocContext +} +function RecursiveContextMenu({type, node, ctx} : RecursiveContextMenuProps){ + const wrapTargets = useMemo(() => { + function addRecursiveDefinitions(mcdoc:core.SymbolMap, targetType: McdocType, referencedType:McdocType | undefined, recursiveSlots:RecursiveSlots[]) { + if(referencedType == undefined) return; + if(referencedType.kind === 'union') { + for(const member of referencedType.members){ + addRecursiveDefinitions(mcdoc, targetType, member, recursiveSlots) + } + } else if(referencedType.kind === 'reference' && referencedType.path != undefined) { + addRecursiveDefinitions(mcdoc, targetType, (mcdoc[referencedType.path].data as TypeDefSymbolData | undefined)?.typeDef , recursiveSlots); + } else if(referencedType.kind === 'struct') { + for(const field of referencedType.fields) { + if(field.kind === 'spread') + { + if(field.type.kind === 'dispatcher'){ + const symbols = ctx.symbols.global['mcdoc/dispatcher']?.[field.type.registry] + for(const key in symbols?.members) + { + const item = symbols.members[key] as core.Symbol + if(item == undefined || item.data == undefined) continue; + + const itemData = item.data as TypeDefSymbolData; + if(itemData.typeDef.kind != 'reference' || itemData.typeDef.path == undefined) continue; + + const simplified = simplifyType(itemData.typeDef, ctx); + if(simplified.kind != 'struct') continue; + + for(const spreadField of simplified.fields){ + if(spreadField.key.kind !== 'literal') continue; + const simpleSpread = simplifyType(spreadField.type, ctx); + const canAssign = isAssignable(targetType, simpleSpread, ctx, (inferred, def) => { + if (def.kind === 'boolean') { + // TODO: this should check whether the value is 0 or 1 + return inferred.kind === 'byte'; + } + if (inferred.kind === 'list') { + return def.kind === 'list' || def.kind === 'tuple'; + } + switch (inferred.kind) { + case 'struct': + return def.kind === 'struct'; + case 'byte': + case 'short': + case 'int': + case 'long': + return ['byte', 'short', 'int', 'long', 'float', 'double'].includes(def.kind); + case 'float': + case 'double': + return ['float', 'double'].includes(def.kind); + default: + return false; + } + }); + if(canAssign) + { + //spreadField.type.kind === 'reference' && spreadField.type.path === targetType.path + recursiveSlots.push({identifier: 'minecraft:' + key, dispatcher: field.type, fieldKey: spreadField.key.value.value.toString(), fieldCount: simplified.fields.length}) + } + } + } + } + } + } + } + } + + let recursiveSlots:RecursiveSlots[] = [] + const mcdoc = ctx.symbols.global['mcdoc']; + if(mcdoc == undefined) return recursiveSlots; + if(type.kind === 'reference') + { + if(type.path == undefined) return recursiveSlots; + const docValue = ctx.symbols.query(ctx.doc, 'mcdoc', type.path).symbol; + if(docValue?.data){ + addRecursiveDefinitions(mcdoc, simplifyType(type, ctx), (docValue.data as TypeDefSymbolData).typeDef, recursiveSlots); + } + //const denseVal = mcdoc["::java::data::worldgen::density_function::DensityFunction"]; + //const symbols = ctx.symbols.global['mcdoc/dispatcher']?.['minecraft:density_function'] + } else if(type.kind === 'dispatcher') + { + for(const index of type.parallelIndices) + { + if(index.kind === 'static') + { + const docValue = ctx.symbols.query(ctx.doc, 'mcdoc/dispatcher', type.registry, index.value).symbol + if(docValue?.data) + addRecursiveDefinitions(mcdoc, simplifyType(type, ctx), (docValue.data as TypeDefSymbolData).typeDef, recursiveSlots); + } else if(index.kind === 'dynamic') + { + for(const accessor of index.accessor) + { + const docValue = ctx.symbols.query(ctx.doc, 'mcdoc/dispatcher', type.registry, accessor.toString()).symbol + if(docValue?.data) + addRecursiveDefinitions(mcdoc, simplifyType(type, ctx), (docValue.data as TypeDefSymbolData).typeDef, recursiveSlots); + } + } + } + } + + return recursiveSlots + }, [type, ctx.symbols.global]); + + const onclick = useCallback((wrappable: RecursiveSlots) => { + + ctx.makeEdit(range => { + //const keyNode = JsonStringNode.mock(core.Range.create(0)) + //keyNode.value = 'type' + const objectNode = JsonObjectNode.mock(range) + for(const index of wrappable.dispatcher.parallelIndices) + { + let valueNode = JsonStringNode.mock(core.Range.create(0)); + valueNode.value = wrappable.identifier + + if(index.kind == 'static') + { + let keyNode = JsonStringNode.mock(core.Range.create(0)); + keyNode.value = index.value + + objectNode.children.push({ + type: 'pair', + range: core.Range.create(0), + key: keyNode, + value: valueNode, + }); + } else if(index.kind == 'dynamic') + { + for(const accessor of index.accessor) + { + let keyNode = JsonStringNode.mock(core.Range.create(0)); + keyNode.value = accessor.toString() + + objectNode.children.push({ + type: 'pair', + range: core.Range.create(0), + key: keyNode, + value: valueNode, + }); + } + } + } + + let childKeyNode = JsonStringNode.mock(core.Range.create(0)); + childKeyNode.value = wrappable.fieldKey + const newPair: core.PairNode = { + type: 'pair', + range: core.Range.create(0), + key: childKeyNode, + value: node, + } + objectNode.children.push(newPair) + + return objectNode + }) + }, [node, ctx]); + if(wrapTargets.length == 0) return <>; + return + {wrapTargets.map(v => { + let label = formatIdentifier(v.identifier) + if(v.fieldCount > 1) + { + label += '/' + formatIdentifier(v.fieldKey) + } + return onclick(v)}/> + } + )} + +} interface DynamicKeyProps { keyType: SimplifiedMcdocType valueType: McdocType diff --git a/src/styles/nodes.css b/src/styles/nodes.css index 5336c764..f2f707a4 100644 --- a/src/styles/nodes.css +++ b/src/styles/nodes.css @@ -124,6 +124,9 @@ padding-left: 9px; background-color: var(--node-background-input); } +.node-header > .btn-menu > .btn{ + border-radius: 0; +} .node-header > input[type="color"] { padding: 0 2px; From 87a99d9452ac657bdb3e4a4a2e39a1446d4fd9ce Mon Sep 17 00:00:00 2001 From: TimberStalker Date: Wed, 28 Jan 2026 18:53:14 -0800 Subject: [PATCH 02/10] moved most functionality to McdocHelpers, and returned to old comparison logic --- src/app/components/generator/McdocHelpers.ts | 190 +++++++++++++++++- .../components/generator/McdocRenderer.tsx | 115 +---------- 2 files changed, 191 insertions(+), 114 deletions(-) diff --git a/src/app/components/generator/McdocHelpers.ts b/src/app/components/generator/McdocHelpers.ts index d8fb7140..5e74fbea 100644 --- a/src/app/components/generator/McdocHelpers.ts +++ b/src/app/components/generator/McdocHelpers.ts @@ -2,12 +2,13 @@ import * as core from '@spyglassmc/core' import type { JsonNode, JsonPairNode } from '@spyglassmc/json' import { JsonArrayNode, JsonObjectNode, JsonStringNode } from '@spyglassmc/json' import { JsonStringOptions } from '@spyglassmc/json/lib/parser/string.js' -import type { Attributes, AttributeValue, ListType, McdocType, NumericType, PrimitiveArrayType, TupleType, UnionType } from '@spyglassmc/mcdoc' +import type { Attributes, AttributeValue, DispatcherType, ListType, McdocType, NumericType, PrimitiveArrayType, ReferenceType, StructTypePairField, TupleType, UnionType } from '@spyglassmc/mcdoc' import { NumericRange, RangeKind } from '@spyglassmc/mcdoc' import type { McdocCheckerContext, SimplifiedMcdocType, SimplifiedMcdocTypeNoUnion, SimplifyValueNode } from '@spyglassmc/mcdoc/lib/runtime/checker/index.js' import { simplify } from '@spyglassmc/mcdoc/lib/runtime/checker/index.js' import config from '../../Config.js' import { randomInt, randomSeed } from '../../Utils.js' +import { TypeDefSymbolData } from '@spyglassmc/mcdoc/lib/binder/index.js' export function getRootType(id: string): McdocType { if (id === 'pack_mcmeta') { @@ -404,11 +405,37 @@ export function canCollapse(node:JsonNode | undefined) { return node !== undefined && (node.type === 'json:array' || node.type == 'json:object'); } + +export function isRecursiveType(type: McdocType | undefined) { + const recursiveReferences = ['::java::data::worldgen::density_function::DensityFunctionRef'] + const recursiveDispatchers = ['worldgen/density_function',] + return (type?.kind == 'reference' && type.path != undefined && recursiveReferences.includes(type.path) ) + || (type?.kind == 'dispatcher' && type.parallelIndices.some(v => + { + if(v.kind == 'static') + { + return recursiveDispatchers.includes(v.value) + } else if(v.kind == 'dynamic') + { + for(const index of v.accessor) + { + if(recursiveDispatchers.includes(index.toString())) + { + return true; + } + } + } + return false + }) + ) +} + interface SimplifyNodeContext { + original?: JsonNode key?: JsonStringNode parent?: JsonObjectNode } -export function simplifyType(type: McdocType, ctx: core.CheckerContext, { key, parent }: SimplifyNodeContext = {}): SimplifiedMcdocType { +export function simplifyType(type: McdocType, ctx: core.CheckerContext, { original, key, parent }: SimplifyNodeContext = {}): SimplifiedMcdocType { const simplifyNode: SimplifyValueNode = { entryNode: { parent: parent ? { @@ -427,7 +454,54 @@ export function simplifyType(type: McdocType, ctx: core.CheckerContext, { key, p } : undefined, }, node: { - originalNode: undefined, + originalNode: original, + inferredType: original ? inferType(original) : { kind: 'any' }, + }, + } + const context: McdocCheckerContext = { + ...ctx, + allowMissingKeys: false, + requireCanonical: false, + isEquivalent: () => false, + getChildren: (node) => { + if (JsonObjectNode.is(node)) { + return node.children.filter(kvp => kvp.key).map(kvp => ({ + key: { originalNode: kvp.key!, inferredType: inferType(kvp.key!) }, + possibleValues: kvp.value + ? [{ originalNode: kvp.value, inferredType: inferType(kvp.value) }] + : [], + })) + } + return [] + }, + reportError: () => {}, + attachTypeInfo: () => {}, + nodeAttacher: () => {}, + stringAttacher: () => {}, + } + const result = simplify(type, { node: simplifyNode, ctx: context }) + return result.typeDef +} +export function simplifyValue(type: McdocType, node:JsonNode, ctx: core.CheckerContext, { key, parent }: SimplifyNodeContext = {}): SimplifiedMcdocType { + const simplifyNode: SimplifyValueNode = { + entryNode: { + parent: parent ? { + entryNode: { + parent: undefined, + runtimeKey: undefined, + }, + node: { + originalNode: parent, + inferredType: inferType(parent), + }, + } : undefined, + runtimeKey: key ? { + originalNode: key, + inferredType: inferType(key), + } : undefined, + }, + node: { + originalNode: node, inferredType: { kind: 'any' }, }, } @@ -455,7 +529,6 @@ export function simplifyType(type: McdocType, ctx: core.CheckerContext, { key, p const result = simplify(type, { node: simplifyNode, ctx: context }) return result.typeDef } - function inferType(node: JsonNode): Exclude { switch (node.type) { case 'json:boolean': @@ -476,6 +549,115 @@ function inferType(node: JsonNode): Exclude { } } +export type RecursiveSlot = { + identifier: string + dispatcher: DispatcherType + fieldKey: string + fieldCount: number +} +export function collectRecursiveDefinitions(ctx:core.CheckerContext, type: McdocType){ + let recursiveSlots:RecursiveSlot[] = [] + if(type.kind === 'reference') + { + let finalRefType = type; + while(true) + { + const newTypeDef = (ctx.symbols.query(ctx.doc, 'mcdoc', finalRefType.path!).symbol?.data as TypeDefSymbolData | undefined)?.typeDef + if(newTypeDef?.kind === 'reference') + finalRefType = newTypeDef + else break; + } + addRecursiveDefinitions(ctx, finalRefType, finalRefType, recursiveSlots); + } + else if(type.kind === 'dispatcher') + { + for(const index of type.parallelIndices) + { + if(index.kind === 'static') + { + const querySymbol = ctx.symbols.query(ctx.doc, 'mcdoc/dispatcher', type.registry, index.value).symbol + const targetType = (querySymbol?.data as TypeDefSymbolData | undefined)?.typeDef + if(targetType?.kind === 'reference') + { + let finalRefType = targetType; + while(true) + { + const newTypeDef = (ctx.symbols.query(ctx.doc, 'mcdoc', finalRefType.path!).symbol?.data as TypeDefSymbolData | undefined)?.typeDef + if(newTypeDef?.kind === 'reference') + finalRefType = newTypeDef + else break; + } + addRecursiveDefinitions(ctx, finalRefType, finalRefType, recursiveSlots); + } + } + } + } + + return recursiveSlots +} +function addRecursiveDefinitions(ctx:core.CheckerContext, targetType: ReferenceType, referencedType:McdocType | undefined, recursiveSlots:RecursiveSlots[]) { + if(referencedType == undefined) return; + if(referencedType.kind === 'union') { + for(const member of referencedType.members){ + addRecursiveDefinitions(ctx, targetType, member, recursiveSlots) + } + } else if(referencedType.kind === 'reference' && referencedType.path != undefined) { + + if(referencedType.path == undefined) return; + const docValue = ctx.symbols.query(ctx.doc, 'mcdoc', referencedType.path).symbol; + if(!docValue?.data) return; + addRecursiveDefinitions(ctx, targetType, (docValue.data as TypeDefSymbolData | undefined)?.typeDef , recursiveSlots); + } else if(referencedType.kind === 'dispatcher') + { + for(const index of referencedType.parallelIndices) + { + if(index.kind === 'static') + { + const docValue = ctx.symbols.query(ctx.doc, 'mcdoc/dispatcher', referencedType.registry, index.value).symbol + if(docValue?.data) + addRecursiveDefinitions(ctx, targetType, (docValue.data as TypeDefSymbolData).typeDef, recursiveSlots); + } + } + } + else if(referencedType.kind === 'struct') { + for(const field of referencedType.fields) { + if(field.kind === 'spread') + { + if(field.type.kind === 'dispatcher'){ + const symbols = ctx.symbols.global['mcdoc/dispatcher']?.[field.type.registry] + for(const key in symbols?.members) + { + const item = symbols.members[key] as core.Symbol + if(item == undefined || item.data == undefined) continue + const itemData = item.data as TypeDefSymbolData; + if(itemData.typeDef.kind != 'reference' || itemData.typeDef.path == undefined) continue + const simplified = simplifyType(itemData.typeDef, ctx); + if(simplified.kind != 'struct') continue; + + for(const spreadField of simplified.fields){ + if(spreadField.key.kind !== 'literal') continue; + let checkType: McdocType | undefined = spreadField.type; + while(checkType !== undefined && checkType.kind === 'reference' && checkType.path) + { + if(checkType.path === targetType.path) + { + recursiveSlots.push({identifier: 'minecraft:' + key, dispatcher: field.type, fieldKey: spreadField.key.value.value.toString(), fieldCount: simplified.fields.length}) + break; + } + else{ + const querySymbol:core.Symbol | undefined = ctx.symbols.query(ctx.doc, 'mcdoc', checkType.path).symbol + if(!querySymbol?.data) break; + checkType = (querySymbol.data as TypeDefSymbolData | undefined)?.typeDef + + } + } + } + } + } + } + } +} + export function quickEqualTypes(a: SimplifiedMcdocTypeNoUnion, b: SimplifiedMcdocTypeNoUnion): boolean { if (a === b) { return true diff --git a/src/app/components/generator/McdocRenderer.tsx b/src/app/components/generator/McdocRenderer.tsx index f3c84b69..4722fc45 100644 --- a/src/app/components/generator/McdocRenderer.tsx +++ b/src/app/components/generator/McdocRenderer.tsx @@ -5,9 +5,8 @@ import * as json from '@spyglassmc/json' import { JsonArrayNode, JsonBooleanNode, JsonNode, JsonNumberNode, JsonObjectNode, JsonStringNode } from '@spyglassmc/json' import { localeQuote } from '@spyglassmc/locales' import { DispatcherType, type ListType, type LiteralType, type McdocType, type NumericType, type PrimitiveArrayType, type ReferenceType, type StringType, type TupleType, type UnionType } from '@spyglassmc/mcdoc' -import { TypeDefSymbolData } from '@spyglassmc/mcdoc/lib/binder/index.js' import { handleAttributes } from '@spyglassmc/mcdoc/lib/runtime/attribute/index.js' -import { isAssignable, type SimplifiedEnum, type SimplifiedMcdocType, type SimplifiedMcdocTypeNoUnion, type SimplifiedStructType, type SimplifiedStructTypePairField } from '@spyglassmc/mcdoc/lib/runtime/checker/index.js' +import { type SimplifiedEnum, type SimplifiedMcdocType, type SimplifiedMcdocTypeNoUnion, type SimplifiedStructType, type SimplifiedStructTypePairField } from '@spyglassmc/mcdoc/lib/runtime/checker/index.js' import { getValues } from '@spyglassmc/mcdoc/lib/runtime/completer/index.js' import { Identifier as Identifier1204, ItemStack as ItemStack1204 } from 'deepslate-1.20.4/core' import { Identifier, ItemStack } from 'deepslate/core' @@ -25,7 +24,7 @@ import { BtnMenu } from '../BtnMenu.jsx' import { ItemDisplay } from '../ItemDisplay.jsx' import { ItemDisplay1204 } from '../ItemDisplay1204.jsx' import { Octicon } from '../Octicon.jsx' -import { canCollapse, formatIdentifier, getCategory, getChange, getDefault, getItemType, isDefaultCollapsedType, isFixedList, isInlineTuple, isListOrArray, isNumericType, isSelectRegistry, quickEqualTypes, simplifyType } from './McdocHelpers.js' +import { canCollapse, collectRecursiveDefinitions, formatIdentifier, getCategory, getChange, getDefault, getItemType, isDefaultCollapsedType, isFixedList, isInlineTuple, isListOrArray, isNumericType, isSelectRegistry, quickEqualTypes, RecursiveSlot, simplifyType } from './McdocHelpers.js' export interface McdocContext extends core.CheckerContext { makeEdit: MakeEdit @@ -694,12 +693,6 @@ function isRecursiveType(type: McdocType | undefined) : type is ReferenceType | }) ) } -type RecursiveSlots = { - identifier: string - dispatcher: DispatcherType - fieldKey: string - fieldCount: number -} interface RecursiveContextMenuProps{ type: McdocType node: JsonNode @@ -707,108 +700,10 @@ interface RecursiveContextMenuProps{ } function RecursiveContextMenu({type, node, ctx} : RecursiveContextMenuProps){ const wrapTargets = useMemo(() => { - function addRecursiveDefinitions(mcdoc:core.SymbolMap, targetType: McdocType, referencedType:McdocType | undefined, recursiveSlots:RecursiveSlots[]) { - if(referencedType == undefined) return; - if(referencedType.kind === 'union') { - for(const member of referencedType.members){ - addRecursiveDefinitions(mcdoc, targetType, member, recursiveSlots) - } - } else if(referencedType.kind === 'reference' && referencedType.path != undefined) { - addRecursiveDefinitions(mcdoc, targetType, (mcdoc[referencedType.path].data as TypeDefSymbolData | undefined)?.typeDef , recursiveSlots); - } else if(referencedType.kind === 'struct') { - for(const field of referencedType.fields) { - if(field.kind === 'spread') - { - if(field.type.kind === 'dispatcher'){ - const symbols = ctx.symbols.global['mcdoc/dispatcher']?.[field.type.registry] - for(const key in symbols?.members) - { - const item = symbols.members[key] as core.Symbol - if(item == undefined || item.data == undefined) continue; - - const itemData = item.data as TypeDefSymbolData; - if(itemData.typeDef.kind != 'reference' || itemData.typeDef.path == undefined) continue; - - const simplified = simplifyType(itemData.typeDef, ctx); - if(simplified.kind != 'struct') continue; - - for(const spreadField of simplified.fields){ - if(spreadField.key.kind !== 'literal') continue; - const simpleSpread = simplifyType(spreadField.type, ctx); - const canAssign = isAssignable(targetType, simpleSpread, ctx, (inferred, def) => { - if (def.kind === 'boolean') { - // TODO: this should check whether the value is 0 or 1 - return inferred.kind === 'byte'; - } - if (inferred.kind === 'list') { - return def.kind === 'list' || def.kind === 'tuple'; - } - switch (inferred.kind) { - case 'struct': - return def.kind === 'struct'; - case 'byte': - case 'short': - case 'int': - case 'long': - return ['byte', 'short', 'int', 'long', 'float', 'double'].includes(def.kind); - case 'float': - case 'double': - return ['float', 'double'].includes(def.kind); - default: - return false; - } - }); - if(canAssign) - { - //spreadField.type.kind === 'reference' && spreadField.type.path === targetType.path - recursiveSlots.push({identifier: 'minecraft:' + key, dispatcher: field.type, fieldKey: spreadField.key.value.value.toString(), fieldCount: simplified.fields.length}) - } - } - } - } - } - } - } - } - - let recursiveSlots:RecursiveSlots[] = [] - const mcdoc = ctx.symbols.global['mcdoc']; - if(mcdoc == undefined) return recursiveSlots; - if(type.kind === 'reference') - { - if(type.path == undefined) return recursiveSlots; - const docValue = ctx.symbols.query(ctx.doc, 'mcdoc', type.path).symbol; - if(docValue?.data){ - addRecursiveDefinitions(mcdoc, simplifyType(type, ctx), (docValue.data as TypeDefSymbolData).typeDef, recursiveSlots); - } - //const denseVal = mcdoc["::java::data::worldgen::density_function::DensityFunction"]; - //const symbols = ctx.symbols.global['mcdoc/dispatcher']?.['minecraft:density_function'] - } else if(type.kind === 'dispatcher') - { - for(const index of type.parallelIndices) - { - if(index.kind === 'static') - { - const docValue = ctx.symbols.query(ctx.doc, 'mcdoc/dispatcher', type.registry, index.value).symbol - if(docValue?.data) - addRecursiveDefinitions(mcdoc, simplifyType(type, ctx), (docValue.data as TypeDefSymbolData).typeDef, recursiveSlots); - } else if(index.kind === 'dynamic') - { - for(const accessor of index.accessor) - { - const docValue = ctx.symbols.query(ctx.doc, 'mcdoc/dispatcher', type.registry, accessor.toString()).symbol - if(docValue?.data) - addRecursiveDefinitions(mcdoc, simplifyType(type, ctx), (docValue.data as TypeDefSymbolData).typeDef, recursiveSlots); - } - } - } - } - - return recursiveSlots - }, [type, ctx.symbols.global]); + return collectRecursiveDefinitions(ctx, type) + }, [type, ctx.doc, ctx.symbols]); - const onclick = useCallback((wrappable: RecursiveSlots) => { - + const onclick = useCallback((wrappable: RecursiveSlot) => { ctx.makeEdit(range => { //const keyNode = JsonStringNode.mock(core.Range.create(0)) //keyNode.value = 'type' From 10b1e0a401f0d5dc9ed862fe2b09d50b0f696b2e Mon Sep 17 00:00:00 2001 From: TimberStalker Date: Wed, 28 Jan 2026 18:57:47 -0800 Subject: [PATCH 03/10] removed 'isRecursiveType' as it is not used --- src/app/components/generator/McdocHelpers.ts | 24 -------------------- 1 file changed, 24 deletions(-) diff --git a/src/app/components/generator/McdocHelpers.ts b/src/app/components/generator/McdocHelpers.ts index 5e74fbea..f3b41a77 100644 --- a/src/app/components/generator/McdocHelpers.ts +++ b/src/app/components/generator/McdocHelpers.ts @@ -406,30 +406,6 @@ export function canCollapse(node:JsonNode | undefined) return node !== undefined && (node.type === 'json:array' || node.type == 'json:object'); } -export function isRecursiveType(type: McdocType | undefined) { - const recursiveReferences = ['::java::data::worldgen::density_function::DensityFunctionRef'] - const recursiveDispatchers = ['worldgen/density_function',] - return (type?.kind == 'reference' && type.path != undefined && recursiveReferences.includes(type.path) ) - || (type?.kind == 'dispatcher' && type.parallelIndices.some(v => - { - if(v.kind == 'static') - { - return recursiveDispatchers.includes(v.value) - } else if(v.kind == 'dynamic') - { - for(const index of v.accessor) - { - if(recursiveDispatchers.includes(index.toString())) - { - return true; - } - } - } - return false - }) - ) -} - interface SimplifyNodeContext { original?: JsonNode key?: JsonStringNode From eefe1b32898475f24316c38847e5cd69cabbb2d9 Mon Sep 17 00:00:00 2001 From: TimberStalker Date: Wed, 28 Jan 2026 18:58:30 -0800 Subject: [PATCH 04/10] removed duplicate unused 'isRecursiveType' --- .../components/generator/McdocRenderer.tsx | 25 +------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/src/app/components/generator/McdocRenderer.tsx b/src/app/components/generator/McdocRenderer.tsx index 4722fc45..70904801 100644 --- a/src/app/components/generator/McdocRenderer.tsx +++ b/src/app/components/generator/McdocRenderer.tsx @@ -4,7 +4,7 @@ import type { JsonPairNode } from '@spyglassmc/json' import * as json from '@spyglassmc/json' import { JsonArrayNode, JsonBooleanNode, JsonNode, JsonNumberNode, JsonObjectNode, JsonStringNode } from '@spyglassmc/json' import { localeQuote } from '@spyglassmc/locales' -import { DispatcherType, type ListType, type LiteralType, type McdocType, type NumericType, type PrimitiveArrayType, type ReferenceType, type StringType, type TupleType, type UnionType } from '@spyglassmc/mcdoc' +import { type ListType, type LiteralType, type McdocType, type NumericType, type PrimitiveArrayType, type StringType, type TupleType, type UnionType } from '@spyglassmc/mcdoc' import { handleAttributes } from '@spyglassmc/mcdoc/lib/runtime/attribute/index.js' import { type SimplifiedEnum, type SimplifiedMcdocType, type SimplifiedMcdocTypeNoUnion, type SimplifiedStructType, type SimplifiedStructTypePairField } from '@spyglassmc/mcdoc/lib/runtime/checker/index.js' import { getValues } from '@spyglassmc/mcdoc/lib/runtime/completer/index.js' @@ -670,29 +670,6 @@ function StaticField({ pair, index, field, fieldKey, staticFields, isToggled, ex {!isCollapsed && } } -function isRecursiveType(type: McdocType | undefined) : type is ReferenceType | DispatcherType{ - const recursiveReferences = ['::java::data::worldgen::density_function::DensityFunctionRef'] - const recursiveDispatchers = ['worldgen/density_function'] - return (type?.kind == 'reference' && type.path != undefined && recursiveReferences.includes(type.path) ) - || (type?.kind == 'dispatcher' && type.parallelIndices.some(v => - { - if(v.kind == 'static') - { - return recursiveDispatchers.includes(v.value) - } else if(v.kind == 'dynamic') - { - for(const index of v.accessor) - { - if(recursiveDispatchers.includes(index.toString())) - { - return true; - } - } - } - return false - }) - ) -} interface RecursiveContextMenuProps{ type: McdocType node: JsonNode From 0d48e8a31874753cc6f2232270cc394bd96923f9 Mon Sep 17 00:00:00 2001 From: TimberStalker Date: Wed, 28 Jan 2026 19:00:25 -0800 Subject: [PATCH 05/10] rearranged type imports and removed commented out usages of 'isRecursiveType' --- src/app/components/generator/McdocRenderer.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/components/generator/McdocRenderer.tsx b/src/app/components/generator/McdocRenderer.tsx index 70904801..6b0cb631 100644 --- a/src/app/components/generator/McdocRenderer.tsx +++ b/src/app/components/generator/McdocRenderer.tsx @@ -4,9 +4,9 @@ import type { JsonPairNode } from '@spyglassmc/json' import * as json from '@spyglassmc/json' import { JsonArrayNode, JsonBooleanNode, JsonNode, JsonNumberNode, JsonObjectNode, JsonStringNode } from '@spyglassmc/json' import { localeQuote } from '@spyglassmc/locales' -import { type ListType, type LiteralType, type McdocType, type NumericType, type PrimitiveArrayType, type StringType, type TupleType, type UnionType } from '@spyglassmc/mcdoc' +import type { ListType, LiteralType, McdocType, NumericType, PrimitiveArrayType, StringType, TupleType, UnionType } from '@spyglassmc/mcdoc' import { handleAttributes } from '@spyglassmc/mcdoc/lib/runtime/attribute/index.js' -import { type SimplifiedEnum, type SimplifiedMcdocType, type SimplifiedMcdocTypeNoUnion, type SimplifiedStructType, type SimplifiedStructTypePairField } from '@spyglassmc/mcdoc/lib/runtime/checker/index.js' +import type { SimplifiedEnum, SimplifiedMcdocType, SimplifiedMcdocTypeNoUnion, SimplifiedStructType, SimplifiedStructTypePairField } from '@spyglassmc/mcdoc/lib/runtime/checker/index.js' import { getValues } from '@spyglassmc/mcdoc/lib/runtime/completer/index.js' import { Identifier as Identifier1204, ItemStack as ItemStack1204 } from 'deepslate-1.20.4/core' import { Identifier, ItemStack } from 'deepslate/core' @@ -54,7 +54,7 @@ export function McdocRoot({ type, rootType, node, ctx } : McdocRootProps) { - {/* isRecursiveType(rootType) && */node != undefined && } + {node != undefined && } @@ -665,7 +665,7 @@ function StaticField({ pair, index, field, fieldKey, staticFields, isToggled, ex )} {!isCollapsed && } - {/* isRecursiveType(field.type) && */child != undefined && } + {child != undefined && } {!isCollapsed && } From 57334cab8aa68dc4a02b5341455d9a9dd3f077b9 Mon Sep 17 00:00:00 2001 From: TimberStalker Date: Wed, 28 Jan 2026 19:03:29 -0800 Subject: [PATCH 06/10] fixed missing curly bracket and incorrectly named parameter type --- src/app/components/generator/McdocHelpers.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/components/generator/McdocHelpers.ts b/src/app/components/generator/McdocHelpers.ts index f3b41a77..c806fd7d 100644 --- a/src/app/components/generator/McdocHelpers.ts +++ b/src/app/components/generator/McdocHelpers.ts @@ -2,13 +2,13 @@ import * as core from '@spyglassmc/core' import type { JsonNode, JsonPairNode } from '@spyglassmc/json' import { JsonArrayNode, JsonObjectNode, JsonStringNode } from '@spyglassmc/json' import { JsonStringOptions } from '@spyglassmc/json/lib/parser/string.js' -import type { Attributes, AttributeValue, DispatcherType, ListType, McdocType, NumericType, PrimitiveArrayType, ReferenceType, StructTypePairField, TupleType, UnionType } from '@spyglassmc/mcdoc' +import type { Attributes, AttributeValue, DispatcherType, ListType, McdocType, NumericType, PrimitiveArrayType, ReferenceType, TupleType, UnionType } from '@spyglassmc/mcdoc' import { NumericRange, RangeKind } from '@spyglassmc/mcdoc' +import { TypeDefSymbolData } from '@spyglassmc/mcdoc/lib/binder/index.js' import type { McdocCheckerContext, SimplifiedMcdocType, SimplifiedMcdocTypeNoUnion, SimplifyValueNode } from '@spyglassmc/mcdoc/lib/runtime/checker/index.js' import { simplify } from '@spyglassmc/mcdoc/lib/runtime/checker/index.js' import config from '../../Config.js' import { randomInt, randomSeed } from '../../Utils.js' -import { TypeDefSymbolData } from '@spyglassmc/mcdoc/lib/binder/index.js' export function getRootType(id: string): McdocType { if (id === 'pack_mcmeta') { @@ -571,7 +571,7 @@ export function collectRecursiveDefinitions(ctx:core.CheckerContext, type: Mcdoc return recursiveSlots } -function addRecursiveDefinitions(ctx:core.CheckerContext, targetType: ReferenceType, referencedType:McdocType | undefined, recursiveSlots:RecursiveSlots[]) { +function addRecursiveDefinitions(ctx:core.CheckerContext, targetType: ReferenceType, referencedType:McdocType | undefined, recursiveSlots:RecursiveSlot[]) { if(referencedType == undefined) return; if(referencedType.kind === 'union') { for(const member of referencedType.members){ @@ -624,7 +624,7 @@ function addRecursiveDefinitions(ctx:core.CheckerContext, targetType: ReferenceT const querySymbol:core.Symbol | undefined = ctx.symbols.query(ctx.doc, 'mcdoc', checkType.path).symbol if(!querySymbol?.data) break; checkType = (querySymbol.data as TypeDefSymbolData | undefined)?.typeDef - + } } } } From 61a366ecf0511205126fb2dab7753c2543b06c45 Mon Sep 17 00:00:00 2001 From: TimberStalker Date: Wed, 28 Jan 2026 19:05:48 -0800 Subject: [PATCH 07/10] undid unused simplifyType Change --- src/app/components/generator/McdocHelpers.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/app/components/generator/McdocHelpers.ts b/src/app/components/generator/McdocHelpers.ts index c806fd7d..d90e20df 100644 --- a/src/app/components/generator/McdocHelpers.ts +++ b/src/app/components/generator/McdocHelpers.ts @@ -407,11 +407,10 @@ export function canCollapse(node:JsonNode | undefined) } interface SimplifyNodeContext { - original?: JsonNode key?: JsonStringNode parent?: JsonObjectNode } -export function simplifyType(type: McdocType, ctx: core.CheckerContext, { original, key, parent }: SimplifyNodeContext = {}): SimplifiedMcdocType { +export function simplifyType(type: McdocType, ctx: core.CheckerContext, { key, parent }: SimplifyNodeContext = {}): SimplifiedMcdocType { const simplifyNode: SimplifyValueNode = { entryNode: { parent: parent ? { @@ -430,8 +429,8 @@ export function simplifyType(type: McdocType, ctx: core.CheckerContext, { origin } : undefined, }, node: { - originalNode: original, - inferredType: original ? inferType(original) : { kind: 'any' }, + originalNode: undefined, + inferredType: { kind: 'any' }, }, } const context: McdocCheckerContext = { From d8bb02a73dabbf382fc4c29ef833b1a83602f102 Mon Sep 17 00:00:00 2001 From: TimberStalker Date: Wed, 28 Jan 2026 19:07:57 -0800 Subject: [PATCH 08/10] removed incorrectly added simplifyValue --- src/app/components/generator/McdocHelpers.ts | 48 +------------------- 1 file changed, 1 insertion(+), 47 deletions(-) diff --git a/src/app/components/generator/McdocHelpers.ts b/src/app/components/generator/McdocHelpers.ts index d90e20df..6215399d 100644 --- a/src/app/components/generator/McdocHelpers.ts +++ b/src/app/components/generator/McdocHelpers.ts @@ -457,53 +457,7 @@ export function simplifyType(type: McdocType, ctx: core.CheckerContext, { key, p const result = simplify(type, { node: simplifyNode, ctx: context }) return result.typeDef } -export function simplifyValue(type: McdocType, node:JsonNode, ctx: core.CheckerContext, { key, parent }: SimplifyNodeContext = {}): SimplifiedMcdocType { - const simplifyNode: SimplifyValueNode = { - entryNode: { - parent: parent ? { - entryNode: { - parent: undefined, - runtimeKey: undefined, - }, - node: { - originalNode: parent, - inferredType: inferType(parent), - }, - } : undefined, - runtimeKey: key ? { - originalNode: key, - inferredType: inferType(key), - } : undefined, - }, - node: { - originalNode: node, - inferredType: { kind: 'any' }, - }, - } - const context: McdocCheckerContext = { - ...ctx, - allowMissingKeys: false, - requireCanonical: false, - isEquivalent: () => false, - getChildren: (node) => { - if (JsonObjectNode.is(node)) { - return node.children.filter(kvp => kvp.key).map(kvp => ({ - key: { originalNode: kvp.key!, inferredType: inferType(kvp.key!) }, - possibleValues: kvp.value - ? [{ originalNode: kvp.value, inferredType: inferType(kvp.value) }] - : [], - })) - } - return [] - }, - reportError: () => {}, - attachTypeInfo: () => {}, - nodeAttacher: () => {}, - stringAttacher: () => {}, - } - const result = simplify(type, { node: simplifyNode, ctx: context }) - return result.typeDef -} + function inferType(node: JsonNode): Exclude { switch (node.type) { case 'json:boolean': From ce6bc89a31e0333643b5a175dab6cc7ede3af6e8 Mon Sep 17 00:00:00 2001 From: TimberStalker Date: Wed, 28 Jan 2026 19:09:48 -0800 Subject: [PATCH 09/10] removed icon that was not used --- src/app/components/Octicon.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/components/Octicon.tsx b/src/app/components/Octicon.tsx index d65b7bf8..c57dc887 100644 --- a/src/app/components/Octicon.tsx +++ b/src/app/components/Octicon.tsx @@ -71,6 +71,5 @@ export const Octicon = { upload: , x: , x_circle: , - move_to_bottom: , wrap_inside: } From 4c0b2bab0c18c60c9676274d812eff91c8dbef82 Mon Sep 17 00:00:00 2001 From: TimberStalker Date: Thu, 29 Jan 2026 17:19:59 -0800 Subject: [PATCH 10/10] added support for some cases where the reference is in a list, for example when a AnyOf or AllOf is supported --- src/app/components/generator/McdocHelpers.ts | 79 +++++++++++-------- .../components/generator/McdocRenderer.tsx | 15 +++- 2 files changed, 59 insertions(+), 35 deletions(-) diff --git a/src/app/components/generator/McdocHelpers.ts b/src/app/components/generator/McdocHelpers.ts index 6215399d..7b133808 100644 --- a/src/app/components/generator/McdocHelpers.ts +++ b/src/app/components/generator/McdocHelpers.ts @@ -482,21 +482,27 @@ export type RecursiveSlot = { identifier: string dispatcher: DispatcherType fieldKey: string + isArrayField?: boolean fieldCount: number } +function simplifyReference(type: ReferenceType, ctx:core.CheckerContext) +{ + let finalRefType = type; + while(true) + { + const newTypeDef = (ctx.symbols.query(ctx.doc, 'mcdoc', finalRefType.path!).symbol?.data as TypeDefSymbolData | undefined)?.typeDef + if(newTypeDef?.kind === 'reference') + finalRefType = newTypeDef + else break; + } + return finalRefType; +} export function collectRecursiveDefinitions(ctx:core.CheckerContext, type: McdocType){ let recursiveSlots:RecursiveSlot[] = [] if(type.kind === 'reference') { - let finalRefType = type; - while(true) - { - const newTypeDef = (ctx.symbols.query(ctx.doc, 'mcdoc', finalRefType.path!).symbol?.data as TypeDefSymbolData | undefined)?.typeDef - if(newTypeDef?.kind === 'reference') - finalRefType = newTypeDef - else break; - } - addRecursiveDefinitions(ctx, finalRefType, finalRefType, recursiveSlots); + const targetType = simplifyReference(type, ctx); + addRecursiveDefinitions(ctx, targetType, targetType, recursiveSlots); } else if(type.kind === 'dispatcher') { @@ -505,18 +511,11 @@ export function collectRecursiveDefinitions(ctx:core.CheckerContext, type: Mcdoc if(index.kind === 'static') { const querySymbol = ctx.symbols.query(ctx.doc, 'mcdoc/dispatcher', type.registry, index.value).symbol - const targetType = (querySymbol?.data as TypeDefSymbolData | undefined)?.typeDef - if(targetType?.kind === 'reference') + const dispatchedType = (querySymbol?.data as TypeDefSymbolData | undefined)?.typeDef + if(dispatchedType?.kind === 'reference') { - let finalRefType = targetType; - while(true) - { - const newTypeDef = (ctx.symbols.query(ctx.doc, 'mcdoc', finalRefType.path!).symbol?.data as TypeDefSymbolData | undefined)?.typeDef - if(newTypeDef?.kind === 'reference') - finalRefType = newTypeDef - else break; - } - addRecursiveDefinitions(ctx, finalRefType, finalRefType, recursiveSlots); + const targetType = simplifyReference(dispatchedType, ctx); + addRecursiveDefinitions(ctx, targetType, targetType, recursiveSlots); } } } @@ -561,23 +560,35 @@ function addRecursiveDefinitions(ctx:core.CheckerContext, targetType: ReferenceT const itemData = item.data as TypeDefSymbolData; if(itemData.typeDef.kind != 'reference' || itemData.typeDef.path == undefined) continue const simplified = simplifyType(itemData.typeDef, ctx); - if(simplified.kind != 'struct') continue; - - for(const spreadField of simplified.fields){ - if(spreadField.key.kind !== 'literal') continue; - let checkType: McdocType | undefined = spreadField.type; - while(checkType !== undefined && checkType.kind === 'reference' && checkType.path) - { - if(checkType.path === targetType.path) + if(simplified.kind === 'struct') + { + for(const spreadField of simplified.fields){ + if(spreadField.key.kind !== 'literal') continue; + if(spreadField.type.kind === 'reference') { - recursiveSlots.push({identifier: 'minecraft:' + key, dispatcher: field.type, fieldKey: spreadField.key.value.value.toString(), fieldCount: simplified.fields.length}) - break; + if(simplifyReference(spreadField.type, ctx).path === targetType.path) + { + recursiveSlots.push({identifier: 'minecraft:' + key, dispatcher: field.type, fieldKey: spreadField.key.value.value.toString(), fieldCount: simplified.fields.length}) + } } - else{ - const querySymbol:core.Symbol | undefined = ctx.symbols.query(ctx.doc, 'mcdoc', checkType.path).symbol - if(!querySymbol?.data) break; - checkType = (querySymbol.data as TypeDefSymbolData | undefined)?.typeDef + else if(spreadField.type.kind === 'list' && spreadField.type.item.kind === 'reference'){ + if(simplifyReference(spreadField.type.item, ctx).path === targetType.path) + { + recursiveSlots.push({identifier: 'minecraft:' + key, dispatcher: field.type, fieldKey: spreadField.key.value.value.toString(), isArrayField: true, fieldCount: simplified.fields.length}) + } } + // else if(spreadField.type.kind === 'struct') { + // for(const childField of spreadField.type.fields) + // { + // console.log('todo', childField); + // } + // } + // else if(spreadField.type.kind === 'union') { + // for(const childMember of spreadField.type.members) + // { + // console.log('todo', childMember); + // } + // } } } } diff --git a/src/app/components/generator/McdocRenderer.tsx b/src/app/components/generator/McdocRenderer.tsx index 6b0cb631..7a31fe7b 100644 --- a/src/app/components/generator/McdocRenderer.tsx +++ b/src/app/components/generator/McdocRenderer.tsx @@ -718,6 +718,19 @@ function RecursiveContextMenu({type, node, ctx} : RecursiveContextMenuProps){ } } + let assignedNode = node; + + if(wrappable.isArrayField) + { + const arrayNode = JsonArrayNode.mock(core.Range.create(0)); + arrayNode.children.push( + { + type: 'item', + range: core.Range.create(0), + value: assignedNode + }); + assignedNode = arrayNode + } let childKeyNode = JsonStringNode.mock(core.Range.create(0)); childKeyNode.value = wrappable.fieldKey @@ -725,7 +738,7 @@ function RecursiveContextMenu({type, node, ctx} : RecursiveContextMenuProps){ type: 'pair', range: core.Range.create(0), key: childKeyNode, - value: node, + value: assignedNode, } objectNode.children.push(newPair)