diff --git a/src/app/components/generator/McdocRenderer.tsx b/src/app/components/generator/McdocRenderer.tsx index 6727f4e8..4f9d2ee5 100644 --- a/src/app/components/generator/McdocRenderer.tsx +++ b/src/app/components/generator/McdocRenderer.tsx @@ -4,16 +4,13 @@ import * as json from '@spyglassmc/json' import { JsonArrayNode, JsonBooleanNode, JsonNumberNode, JsonObjectNode, JsonStringNode } from '@spyglassmc/json' import type { ListType, LiteralType, McdocType, NumericType, PrimitiveArrayType } from '@spyglassmc/mcdoc' import { TypeDefSymbolData } from '@spyglassmc/mcdoc/lib/binder/index.js' -import type { SimplifiedStructType } from '@spyglassmc/mcdoc/lib/runtime/checker/index.js' +import type { McdocCheckerContext, SimplifiedMcdocType, SimplifiedStructType, SimplifyValueNode } from '@spyglassmc/mcdoc/lib/runtime/checker/index.js' +import { simplify } from '@spyglassmc/mcdoc/lib/runtime/checker/index.js' import { useCallback } from 'preact/hooks' -import type { TextDocument } from 'vscode-languageserver-textdocument' import { useLocale } from '../../contexts/Locale.jsx' import { Octicon } from '../Octicon.jsx' -export interface McdocContext { - doc: TextDocument, - symbols: core.SymbolUtil, -} +export interface McdocContext extends core.CheckerContext {} type MakeEdit = (edit: (range: core.Range) => JsonNode | undefined) => void @@ -31,19 +28,18 @@ export function McdocRoot({ node, makeEdit, ctx } : Props) { return <>
- +
- + } interface HeadProps extends Props { - simpleType: McdocType + type: SimplifiedMcdocType optional?: boolean } -function Head({ simpleType, optional, node, makeEdit, ctx }: HeadProps) { +function Head({ type, optional, node, makeEdit, ctx }: HeadProps) { const { locale } = useLocale() - const type = node?.typeDef ?? simpleType if (type.kind === 'string') { return } @@ -64,9 +60,9 @@ function Head({ simpleType, optional, node, makeEdit, ctx }: HeadProps) { } if (type.kind === 'union') { return } if (type.kind === 'struct' && optional) { @@ -180,11 +176,7 @@ function ListHead({ type, node, makeEdit, ctx }: ListHeadProps) { const onAdd = useCallback(() => { if (canAdd) { 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' } + const itemType = getItemType(type, ctx) const newValue = getDefault(itemType, range, ctx) const newItem: core.ItemNode = { type: 'item', @@ -215,25 +207,24 @@ function ListHead({ type, node, makeEdit, ctx }: ListHeadProps) { } interface BodyProps extends Props { - simpleType: McdocType + type: SimplifiedMcdocType } -function Body({ simpleType, node, makeEdit, ctx }: BodyProps) { - const type = node?.typeDef ?? simpleType - if (node?.typeDef?.kind === 'struct') { - if (node.typeDef.fields.length === 0) { +function Body({ type, node, makeEdit, ctx }: BodyProps) { + if (type.kind === 'struct') { + if (type.fields.length === 0) { return <> } return
- +
} - if (node?.typeDef?.kind === 'list') { - const fixedRange = node.typeDef.lengthRange?.min !== undefined && node.typeDef.lengthRange.min === node.typeDef.lengthRange.max - if (!fixedRange && node.children?.length === 0) { + if (type.kind === 'list' || type.kind === 'byte_array' || type.kind === 'int_array' || type.kind === 'long_array') { + const fixedRange = type.lengthRange?.min !== undefined && type.lengthRange.min === type.lengthRange.max + if (!fixedRange && (!node || node.children?.length === 0)) { return <> } return
- +
} if (type.kind === 'byte' || type.kind === 'short' || type.kind === 'int' || type.kind === 'boolean') { @@ -246,10 +237,11 @@ function Body({ simpleType, node, makeEdit, ctx }: BodyProps) { interface StructBodyProps extends Props { type: SimplifiedStructType } -function StructBody({ type, node, makeEdit, ctx }: StructBodyProps) { +function StructBody({ type: outerType, node, makeEdit, ctx }: StructBodyProps) { if (!JsonObjectNode.is(node)) { return <> } + const type = node.typeDef?.kind === 'struct' ? node.typeDef : outerType const staticFields = type.fields.filter(field => field.key.kind === 'literal' && field.key.value.kind === 'string') const dynamicFields = type.fields.filter(field => @@ -264,6 +256,7 @@ function StructBody({ type, node, makeEdit, ctx }: StructBodyProps) { const childIndex = node.children.findIndex(p => p.key?.value === key) const child = childIndex === -1 ? undefined : node.children[childIndex] const childValue = child?.value + const fieldType = simplifyType(field.type, ctx) const makeFieldEdit: MakeEdit = (edit) => { if (child) { makeEdit(() => { @@ -304,9 +297,9 @@ function StructBody({ type, node, makeEdit, ctx }: StructBodyProps) { return
- +
- +
})} @@ -319,13 +312,14 @@ function Key({ label }: { label: string | number | boolean }) { } interface ListBodyProps extends Props { - type: ListType + type: ListType | PrimitiveArrayType } -function ListBody({ type, node, makeEdit, ctx }: ListBodyProps) { +function ListBody({ type: outerType, node, makeEdit, ctx }: ListBodyProps) { const { locale } = useLocale() if (!JsonArrayNode.is(node)) { return <> } + const type = (node.typeDef?.kind === 'list' || node.typeDef?.kind === 'byte_array' || node.typeDef?.kind === 'int_array' || node.typeDef?.kind === 'long_array') ? node.typeDef : outerType const onRemoveItem = useCallback((index: number) => { makeEdit(() => { node.children.splice(index, 1) @@ -335,6 +329,7 @@ function ListBody({ type, node, makeEdit, ctx }: ListBodyProps) { return <> {node.children.map((item, index) => { const child = item.value + const itemType = getItemType(type, ctx) const makeItemEdit: MakeEdit = (edit) => { makeEdit(() => { const newChild = edit(child?.range ?? item.range) @@ -360,9 +355,9 @@ function ListBody({ type, node, makeEdit, ctx }: ListBodyProps) { } - + - + })} @@ -439,3 +434,37 @@ function getDefault(type: McdocType, range: core.Range, ctx: McdocContext): Json } return { type: 'json:null', range } } + +function simplifyType(type: McdocType, ctx: McdocContext): SimplifiedMcdocType { + const node: SimplifyValueNode = { + entryNode: { + parent: undefined, + runtimeKey: undefined, + }, + node: { + originalNode: null, + inferredType: { kind: 'any' }, + }, + } + const context: McdocCheckerContext = { + ...ctx, + allowMissingKeys: false, + requireCanonical: false, + isEquivalent: () => false, + getChildren: () => [], + reportError: () => {}, + attachTypeInfo: () => {}, + nodeAttacher: () => {}, + stringAttacher: () => {}, + } + const result = simplify(type, { node, ctx: context }) + return result.typeDef +} + +function getItemType(type: ListType | PrimitiveArrayType, ctx: McdocContext): SimplifiedMcdocType { + return type.kind === 'list' ? simplifyType(type.item, ctx) + : type.kind === 'byte_array' ? { kind: 'byte' } + : type.kind === 'int_array' ? { kind: 'int' } + : type.kind === 'long_array' ? { kind: 'long' } + : { kind: 'any' } +} diff --git a/src/app/components/generator/Tree.tsx b/src/app/components/generator/Tree.tsx index de19de03..3ecc7f61 100644 --- a/src/app/components/generator/Tree.tsx +++ b/src/app/components/generator/Tree.tsx @@ -49,7 +49,7 @@ export function Tree({ docAndNode, onError }: TreePanelProps) { if (!service) { return undefined } - return { doc: docAndNode.doc, symbols: service.getSymbols() } + return service.getCheckerContext(docAndNode.doc) }, [docAndNode, service]) return
diff --git a/src/app/services/Spyglass.ts b/src/app/services/Spyglass.ts index 4aab10b1..35ce9416 100644 --- a/src/app/services/Spyglass.ts +++ b/src/app/services/Spyglass.ts @@ -1,4 +1,5 @@ import * as core from '@spyglassmc/core' +import { ErrorReporter } from '@spyglassmc/core' import { BrowserExternals } from '@spyglassmc/core/lib/browser.js' import type { McmetaSummary } from '@spyglassmc/java-edition/lib/dependency/index.js' import { Fluids, ReleaseVersion, symbolRegistrar } from '@spyglassmc/java-edition/lib/dependency/index.js' @@ -56,8 +57,9 @@ export class SpyglassService { }) } - public getSymbols() { - return this.service.project.symbols + public getCheckerContext(doc: TextDocument) { + const err = new ErrorReporter() + return core.CheckerContext.create(this.service.project, { doc, err }) } public async getFile(uri: string, emptyContent?: () => string) {