Skip to content

Commit

Permalink
✨ Add pack format mcdoc attribute (#1469)
Browse files Browse the repository at this point in the history
  • Loading branch information
misode committed Jul 5, 2024
1 parent 673b073 commit 8600c83
Show file tree
Hide file tree
Showing 12 changed files with 142 additions and 30 deletions.
1 change: 1 addition & 0 deletions packages/core/src/processor/completer/Completer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export interface CompletionItem {
label: string
range: Range
kind?: CompletionKind
labelSuffix?: string
detail?: string
documentation?: string
deprecated?: boolean
Expand Down
41 changes: 40 additions & 1 deletion packages/java-edition/src/index.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -156,6 +157,44 @@ export const initialize: core.ProjectInitializer = async (ctx) => {
},
},
)
const packFormats = new Map<number, McmetaVersion>()
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)
Expand Down
1 change: 1 addition & 0 deletions packages/json/src/checker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export function index(
}\n\`\`\`\n${desc}`
}
},
nodeAttacher: (node, attacher) => attacher(node),
stringAttacher: (node, attacher) => {
if (!JsonStringNode.is(node)) {
return
Expand Down
4 changes: 3 additions & 1 deletion packages/json/src/completer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})
)
}
Expand Down
1 change: 1 addition & 0 deletions packages/language-server/src/util/toLS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions packages/locales/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
11 changes: 10 additions & 1 deletion packages/mcdoc/src/runtime/attribute/index.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -32,11 +32,20 @@ export interface McdocAttribute<C = unknown> {
typeDef: SimplifiedMcdocTypeNoUnion,
ctx: McdocCheckerContext<T>,
) => core.InfallibleParser<core.AstNode | undefined> | undefined
checker?: <T>(
config: C,
inferred: Exclude<McdocType, UnionType>,
ctx: McdocCheckerContext<T>,
) => core.SyncChecker<core.AstNode> | undefined
stringMocker?: (
config: C,
typeDef: core.DeepReadonly<SimplifiedMcdocTypeNoUnion>,
ctx: McdocCompleterContext,
) => core.AstNode | undefined
numericCompleter?: (
config: C,
ctx: McdocCompleterContext,
) => core.CompletionItem[]
}

export function registerAttribute<C extends core.Returnable>(
Expand Down
4 changes: 4 additions & 0 deletions packages/mcdoc/src/runtime/checker/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export type TypeInfoAttacher<T> = (
description?: string,
) => void

export type NodeAttacher<T> = (node: T, attacher: (node: core.AstNode) => void) => void

export type StringAttacher<T> = (node: T, attacher: (node: core.StringBaseNode) => void) => void

export type ChildrenGetter<T> = (
Expand All @@ -42,6 +44,7 @@ export interface McdocCheckerContext<T> extends core.CheckerContext {
getChildren: ChildrenGetter<T>
reportError: ErrorReporter<T>
attachTypeInfo?: TypeInfoAttacher<T>
nodeAttacher?: NodeAttacher<T>
stringAttacher?: StringAttacher<T>
}
type McdocCheckerContextOptions<T> = Partial<McdocCheckerContext<T>>
Expand All @@ -58,6 +61,7 @@ export namespace McdocCheckerContext {
getChildren: options.getChildren ?? (() => []),
reportError: options.reportError ?? (() => {}),
attachTypeInfo: options.attachTypeInfo,
nodeAttacher: options.nodeAttacher,
stringAttacher: options.stringAttacher,
}
}
Expand Down
55 changes: 30 additions & 25 deletions packages/mcdoc/src/runtime/checker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,15 +351,15 @@ function attachTypeInfo<T>(node: CheckerTreeRuntimeNode<T>, 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?.(
node.entryNode.runtimeKey.originalNode,
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, {
Expand All @@ -382,38 +382,43 @@ function attachTypeInfo<T>(node: CheckerTreeRuntimeNode<T>, ctx: McdocCheckerCon
}
}

function handleStringAttachers<T>(
function handleNodeAttachers<T>(
runtimeValue: RuntimeNode<T>,
typeDef: SimplifiedMcdocTypeNoUnion,
ctx: McdocCheckerContext<T>,
) {
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]
})
})
}

Expand Down
42 changes: 41 additions & 1 deletion packages/mcdoc/src/runtime/completer/index.ts
Original file line number Diff line number Diff line change
@@ -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'

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 []
}
Expand All @@ -144,9 +159,11 @@ function getStringCompletions(
...items.map<SimpleCompletionValue>(item => ({
value: item.label,
kind: 'string',
labelSuffix: item.labelSuffix,
detail: item.detail,
completionKind: item.kind,
insertText: item.insertText,
sortText: item.sortText,
})),
)
})
Expand All @@ -155,3 +172,26 @@ function getStringCompletions(
}
return ans
}

function getNumericCompletions(
typeDef: core.DeepReadonly<NumericType>,
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
}
6 changes: 6 additions & 0 deletions packages/nbt/src/checker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ export function typeDefinition(
}\n\`\`\`\n${desc}`
}
},
nodeAttacher: (node, attacher) => attacher(node),
stringAttacher: (node, attacher) => {
if (!NbtStringNode.is(node)) {
return
Expand Down Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion packages/nbt/src/completer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})
)
}
Expand Down

0 comments on commit 8600c83

Please sign in to comment.