From 8600c834873ef993068f6f8e657dae496f3cbf45 Mon Sep 17 00:00:00 2001 From: Misode Date: Fri, 5 Jul 2024 16:39:06 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20pack=20format=20mcdoc=20attri?= =?UTF-8?q?bute=20(#1469)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/src/processor/completer/Completer.ts | 1 + packages/java-edition/src/index.ts | 41 +++++++++++++- packages/json/src/checker/index.ts | 1 + packages/json/src/completer/index.ts | 4 +- packages/language-server/src/util/toLS.ts | 1 + packages/locales/src/locales/en.json | 2 + packages/mcdoc/src/runtime/attribute/index.ts | 11 +++- packages/mcdoc/src/runtime/checker/context.ts | 4 ++ packages/mcdoc/src/runtime/checker/index.ts | 55 ++++++++++--------- packages/mcdoc/src/runtime/completer/index.ts | 42 +++++++++++++- packages/nbt/src/checker/index.ts | 6 ++ packages/nbt/src/completer/index.ts | 4 +- 12 files changed, 142 insertions(+), 30 deletions(-) diff --git a/packages/core/src/processor/completer/Completer.ts b/packages/core/src/processor/completer/Completer.ts index 556092a5c..a07028697 100644 --- a/packages/core/src/processor/completer/Completer.ts +++ b/packages/core/src/processor/completer/Completer.ts @@ -43,6 +43,7 @@ export interface CompletionItem { label: string range: Range kind?: CompletionKind + labelSuffix?: string detail?: string documentation?: string deprecated?: boolean diff --git a/packages/java-edition/src/index.ts b/packages/java-edition/src/index.ts index e315714ca..a1923c788 100644 --- a/packages/java-edition/src/index.ts +++ b/packages/java-edition/src/index.ts @@ -1,9 +1,10 @@ import * as core from '@spyglassmc/core' import * as json from '@spyglassmc/json' +import { localize } from '@spyglassmc/locales' import * as mcdoc from '@spyglassmc/mcdoc' import * as nbt from '@spyglassmc/nbt' import { uriBinder } from './binder/index.js' -import type { McmetaSummary } from './dependency/index.js' +import type { McmetaSummary, McmetaVersion } from './dependency/index.js' import { getMcmetaSummary, getVanillaDatapack, @@ -156,6 +157,44 @@ export const initialize: core.ProjectInitializer = async (ctx) => { }, }, ) + const packFormats = new Map() + for (const version of versions) { + if (version.type === 'release' && !packFormats.has(version.data_pack_version)) { + packFormats.set(version.data_pack_version, version) + } + } + mcdoc.runtime.registerAttribute(meta, 'pack_format', () => undefined, { + checker: (_, typeDef) => { + if (typeDef.kind !== 'literal' || typeof typeDef.value.value !== 'number') { + return undefined + } + const target = typeDef.value.value + return (node, ctx) => { + const targetVersion = packFormats.get(target) + if (!targetVersion) { + ctx.err.report( + localize('java-edition.pack-format.unsupported', target), + node, + core.ErrorSeverity.Warning, + ) + } else if (targetVersion.id !== release) { + ctx.err.report( + localize('java-edition.pack-format.not-loaded', target, release), + node, + core.ErrorSeverity.Warning, + ) + } + } + }, + numericCompleter: (_, ctx) => { + return [...packFormats.values()].map((v, i) => ({ + range: core.Range.create(ctx.offset), + label: `${v.data_pack_version}`, + labelSuffix: ` (${v.id})`, + sortText: `${i}`.padStart(4, '0'), + } satisfies core.CompletionItem)) + }, + }) json.initialize(ctx) jeJson.initialize(ctx) diff --git a/packages/json/src/checker/index.ts b/packages/json/src/checker/index.ts index f1ca082f4..40d71a27e 100644 --- a/packages/json/src/checker/index.ts +++ b/packages/json/src/checker/index.ts @@ -98,6 +98,7 @@ export function index( }\n\`\`\`\n${desc}` } }, + nodeAttacher: (node, attacher) => attacher(node), stringAttacher: (node, attacher) => { if (!JsonStringNode.is(node)) { return diff --git a/packages/json/src/completer/index.ts b/packages/json/src/completer/index.ts index b2420a9d1..67654d5b1 100644 --- a/packages/json/src/completer/index.ts +++ b/packages/json/src/completer/index.ts @@ -83,12 +83,14 @@ function getValues( ctx: core.CompleterContext, ): core.CompletionItem[] { return mcdoc.runtime.completer.getValues(typeDef, ctx) - .map(({ value, detail, kind, completionKind, insertText }) => + .map(({ value, labelSuffix, detail, kind, completionKind, insertText, sortText }) => core.CompletionItem.create(value, range, { kind: completionKind ?? core.CompletionKind.Value, + labelSuffix, detail, filterText: kind === 'string' ? `"${value}"` : value, insertText: kind === 'string' ? `"${insertText ?? value}"` : insertText ?? value, + sortText, }) ) } diff --git a/packages/language-server/src/util/toLS.ts b/packages/language-server/src/util/toLS.ts index de54468fd..ef0b7bb21 100644 --- a/packages/language-server/src/util/toLS.ts +++ b/packages/language-server/src/util/toLS.ts @@ -195,6 +195,7 @@ export function completionItem( const ans: ls.CompletionItem = { label: completion.label, kind: completion.kind, + ...(completion.labelSuffix ? { labelDetails: { detail: completion.labelSuffix } } : {}), detail: completion.detail, documentation: completion.documentation, filterText: completion.filterText, diff --git a/packages/locales/src/locales/en.json b/packages/locales/src/locales/en.json index 035a55b81..027c31636 100644 --- a/packages/locales/src/locales/en.json +++ b/packages/locales/src/locales/en.json @@ -46,6 +46,8 @@ "invalid-key-combination": "Invalid combination of keys %0%", "java-edition.binder.wrong-folder": "Files in the %0% folder are not recognized in loaded version %1%, did you meant to use the %2% folder?", "java-edition.binder.wrong-version": "Files in the %0% folder are not recognized in loaded version %1%", + "java-edition.pack-format.unsupported": "Pack format %0% does not have a corresponding release version. Snapshot versions are unsupported.", + "java-edition.pack-format.not-loaded": "Pack format %0% does not match the actively loaded version %1%. You may need to reload Spyglass.", "json.doc.advancement.display": "Advancement display settings. If present, the advancement will be visible in the advancement tabs.", "json.checker.array.length-between": "%0% with length between %1% and %2%", "json.checker.object.field.union-empty-members": "Disallowed property", diff --git a/packages/mcdoc/src/runtime/attribute/index.ts b/packages/mcdoc/src/runtime/attribute/index.ts index da06ce315..7f266fb1b 100644 --- a/packages/mcdoc/src/runtime/attribute/index.ts +++ b/packages/mcdoc/src/runtime/attribute/index.ts @@ -1,5 +1,5 @@ import * as core from '@spyglassmc/core' -import type { Attribute, StructTypePairField } from '../../type/index.js' +import type { Attribute, McdocType, StructTypePairField, UnionType } from '../../type/index.js' import type { McdocCheckerContext, SimplifiedMcdocType, @@ -32,11 +32,20 @@ export interface McdocAttribute { typeDef: SimplifiedMcdocTypeNoUnion, ctx: McdocCheckerContext, ) => core.InfallibleParser | undefined + checker?: ( + config: C, + inferred: Exclude, + ctx: McdocCheckerContext, + ) => core.SyncChecker | undefined stringMocker?: ( config: C, typeDef: core.DeepReadonly, ctx: McdocCompleterContext, ) => core.AstNode | undefined + numericCompleter?: ( + config: C, + ctx: McdocCompleterContext, + ) => core.CompletionItem[] } export function registerAttribute( diff --git a/packages/mcdoc/src/runtime/checker/context.ts b/packages/mcdoc/src/runtime/checker/context.ts index dd13c478a..534dab3fe 100644 --- a/packages/mcdoc/src/runtime/checker/context.ts +++ b/packages/mcdoc/src/runtime/checker/context.ts @@ -26,6 +26,8 @@ export type TypeInfoAttacher = ( description?: string, ) => void +export type NodeAttacher = (node: T, attacher: (node: core.AstNode) => void) => void + export type StringAttacher = (node: T, attacher: (node: core.StringBaseNode) => void) => void export type ChildrenGetter = ( @@ -42,6 +44,7 @@ export interface McdocCheckerContext extends core.CheckerContext { getChildren: ChildrenGetter reportError: ErrorReporter attachTypeInfo?: TypeInfoAttacher + nodeAttacher?: NodeAttacher stringAttacher?: StringAttacher } type McdocCheckerContextOptions = Partial> @@ -58,6 +61,7 @@ export namespace McdocCheckerContext { getChildren: options.getChildren ?? (() => []), reportError: options.reportError ?? (() => {}), attachTypeInfo: options.attachTypeInfo, + nodeAttacher: options.nodeAttacher, stringAttacher: options.stringAttacher, } } diff --git a/packages/mcdoc/src/runtime/checker/index.ts b/packages/mcdoc/src/runtime/checker/index.ts index fe85c7760..4a84d3249 100644 --- a/packages/mcdoc/src/runtime/checker/index.ts +++ b/packages/mcdoc/src/runtime/checker/index.ts @@ -351,7 +351,7 @@ function attachTypeInfo(node: CheckerTreeRuntimeNode, ctx: McdocCheckerCon if (definitions.length === 1) { const { typeDef, groupNode } = definitions[0] ctx.attachTypeInfo?.(node.node.originalNode, typeDef, groupNode.desc) - handleStringAttachers(node.node, typeDef, ctx) + handleNodeAttachers(node.node, typeDef, ctx) if (node.entryNode.runtimeKey && groupNode.keyDefinition) { ctx.attachTypeInfo?.( @@ -359,7 +359,7 @@ function attachTypeInfo(node: CheckerTreeRuntimeNode, ctx: McdocCheckerCon groupNode.keyDefinition, groupNode.desc, ) - handleStringAttachers(node.entryNode.runtimeKey, groupNode.keyDefinition, ctx) + handleNodeAttachers(node.entryNode.runtimeKey, groupNode.keyDefinition, ctx) } } else if (definitions.length > 1) { ctx.attachTypeInfo?.(node.node.originalNode, { @@ -382,38 +382,43 @@ function attachTypeInfo(node: CheckerTreeRuntimeNode, ctx: McdocCheckerCon } } -function handleStringAttachers( +function handleNodeAttachers( runtimeValue: RuntimeNode, typeDef: SimplifiedMcdocTypeNoUnion, ctx: McdocCheckerContext, ) { - const { stringAttacher } = ctx - if (!stringAttacher) { + const { nodeAttacher, stringAttacher } = ctx + if (!nodeAttacher && !stringAttacher) { return } handleAttributes(typeDef.attributes, ctx, (handler, config) => { const parser = handler.stringParser?.(config, typeDef, ctx) - if (!parser) { - return + if (parser && stringAttacher) { + stringAttacher(runtimeValue.originalNode, (node) => { + const src = new Source(node.value, node.valueMap) + const start = src.cursor + const child = parser(src, ctx) + if (!child) { + ctx.err.report( + localize('expected', localize('mcdoc.runtime.checker.value')), + Range.create(start, src.skipRemaining()), + ) + return + } else if (src.canRead()) { + ctx.err.report( + localize('mcdoc.runtime.checker.trailing'), + Range.create(src.cursor, src.skipRemaining()), + ) + } + node.children = [child] + }) + } + const checker = handler.checker?.(config, runtimeValue.inferredType, ctx) + if (checker && nodeAttacher) { + nodeAttacher(runtimeValue.originalNode, (node) => { + checker(node, ctx) + }) } - stringAttacher(runtimeValue.originalNode, (node) => { - const src = new Source(node.value, node.valueMap) - const start = src.cursor - const child = parser(src, ctx) - if (!child) { - ctx.err.report( - localize('expected', localize('mcdoc.runtime.checker.value')), - Range.create(start, src.skipRemaining()), - ) - return - } else if (src.canRead()) { - ctx.err.report( - localize('mcdoc.runtime.checker.trailing'), - Range.create(src.cursor, src.skipRemaining()), - ) - } - node.children = [child] - }) }) } diff --git a/packages/mcdoc/src/runtime/completer/index.ts b/packages/mcdoc/src/runtime/completer/index.ts index 582680cb7..ac17091c5 100644 --- a/packages/mcdoc/src/runtime/completer/index.ts +++ b/packages/mcdoc/src/runtime/completer/index.ts @@ -1,6 +1,12 @@ import type * as core from '@spyglassmc/core' import { TypeDefSymbolData } from '../../binder/index.js' -import type { LiteralType, McdocType, StringType, StructTypePairField } from '../../type/index.js' +import type { + LiteralType, + McdocType, + NumericType, + StringType, + StructTypePairField, +} from '../../type/index.js' import { handleAttributes } from '../attribute/index.js' import type { SimplifiedEnum, SimplifiedMcdocType } from '../checker/index.js' @@ -55,9 +61,11 @@ export function getFields( export type SimpleCompletionValue = { value: string detail?: string + labelSuffix?: string kind?: McdocType['kind'] completionKind?: core.CompletionKind insertText?: string + sortText?: string } // TODO: only accept SimplifiedMcdocType here @@ -124,6 +132,13 @@ export function getValues( detail: v.identifier, kind: typeDef.enumKind ?? 'string', })) + case 'byte': + case 'short': + case 'int': + case 'long': + case 'float': + case 'double': + return getNumericCompletions(typeDef, ctx) default: return [] } @@ -144,9 +159,11 @@ function getStringCompletions( ...items.map(item => ({ value: item.label, kind: 'string', + labelSuffix: item.labelSuffix, detail: item.detail, completionKind: item.kind, insertText: item.insertText, + sortText: item.sortText, })), ) }) @@ -155,3 +172,26 @@ function getStringCompletions( } return ans } + +function getNumericCompletions( + typeDef: core.DeepReadonly, + ctx: McdocCompleterContext, +) { + const ans: SimpleCompletionValue[] = [] + handleAttributes(typeDef.attributes, ctx, (handler, config) => { + const items = handler.numericCompleter?.(config, ctx) + if (!items) { + return + } + ans.push(...items.map(item => ({ + value: item.label, + kind: typeDef.kind, + labelSuffix: item.labelSuffix, + detail: item.detail, + completionKind: item.kind, + insertText: item.insertText, + sortText: item.sortText, + }))) + }) + return ans +} diff --git a/packages/nbt/src/checker/index.ts b/packages/nbt/src/checker/index.ts index e52c4c325..c7a2533c1 100644 --- a/packages/nbt/src/checker/index.ts +++ b/packages/nbt/src/checker/index.ts @@ -163,6 +163,7 @@ export function typeDefinition( }\n\`\`\`\n${desc}` } }, + nodeAttacher: (node, attacher) => attacher(node), stringAttacher: (node, attacher) => { if (!NbtStringNode.is(node)) { return @@ -372,6 +373,11 @@ export function path( }\n\`\`\`\n${desc}` } }, + nodeAttacher: (link, attacher) => { + if (link.node.type !== 'leaf') { + attacher(link.node) + } + }, stringAttacher: (link, attacher) => { if (!NbtPathKeyNode.is(link.node)) { return diff --git a/packages/nbt/src/completer/index.ts b/packages/nbt/src/completer/index.ts index bc6d5384c..9b2d19780 100644 --- a/packages/nbt/src/completer/index.ts +++ b/packages/nbt/src/completer/index.ts @@ -140,12 +140,14 @@ function getValues( ctx: mcdoc.runtime.completer.McdocCompleterContext, ): core.CompletionItem[] { return mcdoc.runtime.completer.getValues(typeDef, ctx) - .map(({ value, detail, kind, completionKind, insertText }) => + .map(({ value, labelSuffix, detail, kind, completionKind, insertText, sortText }) => core.CompletionItem.create(value, range, { kind: completionKind ?? core.CompletionKind.Value, + labelSuffix, detail, filterText: formatValue(value, kind), insertText: formatValue(insertText ?? value, kind), + sortText, }) ) }