@spyglassmc/mcfunction 0.2.7 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/colorizer/index.d.ts +0 -2
- package/lib/colorizer/index.js +2 -4
- package/lib/colorizer/macro.d.ts +4 -0
- package/lib/colorizer/macro.js +21 -0
- package/lib/completer/index.d.ts +2 -2
- package/lib/completer/index.js +9 -14
- package/lib/node/command.d.ts +1 -7
- package/lib/node/command.js +4 -16
- package/lib/node/entry.d.ts +3 -2
- package/lib/node/index.d.ts +1 -0
- package/lib/node/index.js +1 -0
- package/lib/node/macro.d.ts +18 -0
- package/lib/node/macro.js +14 -0
- package/lib/parser/command.js +9 -18
- package/lib/parser/entry.d.ts +8 -2
- package/lib/parser/entry.js +14 -15
- package/lib/parser/index.d.ts +1 -0
- package/lib/parser/index.js +1 -0
- package/lib/parser/macro.d.ts +9 -0
- package/lib/parser/macro.js +93 -0
- package/lib/tree/index.d.ts +0 -1
- package/lib/tree/index.js +0 -1
- package/lib/tree/util.js +1 -3
- package/package.json +3 -3
- package/lib/tree/registry.d.ts +0 -27
- package/lib/tree/registry.js +0 -42
package/lib/colorizer/index.d.ts
CHANGED
package/lib/colorizer/index.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import * as core from '@spyglassmc/core';
|
|
2
|
+
import { macro } from './macro.js';
|
|
2
3
|
export function register(meta) {
|
|
3
|
-
meta.registerColorizer('mcfunction:command_macro', macro);
|
|
4
4
|
meta.registerColorizer('mcfunction:command_child/literal', core.colorizer.literal);
|
|
5
5
|
meta.registerColorizer('mcfunction:command_child/trailing', core.colorizer.error);
|
|
6
|
+
meta.registerColorizer('mcfunction:macro', macro);
|
|
6
7
|
}
|
|
7
|
-
export const macro = (node, ctx) => {
|
|
8
|
-
return [core.ColorToken.create(node, 'string')];
|
|
9
|
-
};
|
|
10
8
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as core from '@spyglassmc/core';
|
|
2
|
+
export const macro = (node, ctx) => {
|
|
3
|
+
const tokens = [];
|
|
4
|
+
tokens.push(core.ColorToken.create(core.Range.create(node.range.start, node.range.start + 1), 'literal')); // Dollar Sign
|
|
5
|
+
for (const child of node.children) {
|
|
6
|
+
if (child.type === 'mcfunction:macro/other') {
|
|
7
|
+
tokens.push(core.ColorToken.create(child.range, 'string'));
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
const { start, end } = child.range;
|
|
11
|
+
// $(
|
|
12
|
+
tokens.push(core.ColorToken.create(core.Range.create(start, start + 2), 'literal'));
|
|
13
|
+
// Key
|
|
14
|
+
tokens.push(core.ColorToken.create(core.Range.create(start + 2, end - 1), 'property'));
|
|
15
|
+
// )
|
|
16
|
+
tokens.push(core.ColorToken.create(core.Range.create(end - 1, end), 'literal'));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return tokens;
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=macro.js.map
|
package/lib/completer/index.d.ts
CHANGED
|
@@ -2,11 +2,11 @@ import * as core from '@spyglassmc/core';
|
|
|
2
2
|
import type { McfunctionNode } from '../node/index.js';
|
|
3
3
|
import { CommandNode } from '../node/index.js';
|
|
4
4
|
import type { ArgumentTreeNode, RootTreeNode } from '../tree/index.js';
|
|
5
|
-
export type MockNodesGetter = (treeNode: ArgumentTreeNode, range: core.
|
|
5
|
+
export type MockNodesGetter = (treeNode: ArgumentTreeNode, range: core.CompleterContext) => core.Arrayable<core.AstNode>;
|
|
6
6
|
/**
|
|
7
7
|
* @param getMockNodes A function that returns a mock AST Node from given {@link ArgumentTreeNode}. These mock nodes
|
|
8
8
|
* will be used for completing the argument.
|
|
9
9
|
*/
|
|
10
|
-
export declare function entry(
|
|
10
|
+
export declare function entry(tree: RootTreeNode, getMockNodes: MockNodesGetter): core.Completer<McfunctionNode>;
|
|
11
11
|
export declare function command(tree: RootTreeNode, getMockNodes: MockNodesGetter): core.Completer<CommandNode>;
|
|
12
12
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/completer/index.js
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import * as core from '@spyglassmc/core';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { categorizeTreeChildren, CommandTreeRegistry, redirect, resolveParentTreeNode, } from '../tree/index.js';
|
|
2
|
+
import { CommandNode, MacroNode } from '../node/index.js';
|
|
3
|
+
import { categorizeTreeChildren, redirect, resolveParentTreeNode } from '../tree/index.js';
|
|
5
4
|
/**
|
|
6
5
|
* @param getMockNodes A function that returns a mock AST Node from given {@link ArgumentTreeNode}. These mock nodes
|
|
7
6
|
* will be used for completing the argument.
|
|
8
7
|
*/
|
|
9
|
-
export function entry(
|
|
8
|
+
export function entry(tree, getMockNodes) {
|
|
10
9
|
return (node, ctx) => {
|
|
11
|
-
const tree = CommandTreeRegistry.instance.get(commandTreeName);
|
|
12
10
|
const childNode = core.AstNode.findChild(node, ctx.offset, true);
|
|
13
|
-
if (core.CommentNode.is(childNode) ||
|
|
11
|
+
if (core.CommentNode.is(childNode) || MacroNode.is(childNode)) {
|
|
14
12
|
return [];
|
|
15
13
|
}
|
|
16
14
|
else {
|
|
@@ -21,15 +19,14 @@ export function entry(commandTreeName, getMockNodes) {
|
|
|
21
19
|
export function command(tree, getMockNodes) {
|
|
22
20
|
return (node, ctx) => {
|
|
23
21
|
const index = core.AstNode.findChildIndex(node, ctx.offset, true);
|
|
24
|
-
const selectedChildNode = node.children[index]
|
|
22
|
+
const selectedChildNode = node.children[index]
|
|
23
|
+
?.children[0];
|
|
25
24
|
if (selectedChildNode) {
|
|
26
25
|
return core.completer.dispatch(selectedChildNode, ctx);
|
|
27
26
|
}
|
|
28
27
|
const lastChildNode = core.AstNode.findLastChild(node, ctx.offset);
|
|
29
28
|
if (!lastChildNode) {
|
|
30
|
-
return Object.keys(tree.children ?? {}).map((v) => core.CompletionItem.create(v, ctx.offset, {
|
|
31
|
-
kind: 14 /* core.CompletionKind.Keyword */,
|
|
32
|
-
}));
|
|
29
|
+
return Object.keys(tree.children ?? {}).map((v) => core.CompletionItem.create(v, ctx.offset, { kind: 14 /* core.CompletionKind.Keyword */ }));
|
|
33
30
|
}
|
|
34
31
|
const treePath = lastChildNode.path;
|
|
35
32
|
const { treeNode: parentTreeNode } = resolveParentTreeNode(redirect(tree, treePath), tree);
|
|
@@ -38,10 +35,8 @@ export function command(tree, getMockNodes) {
|
|
|
38
35
|
}
|
|
39
36
|
const { literalTreeNodes, argumentTreeNodes } = categorizeTreeChildren(parentTreeNode.children);
|
|
40
37
|
return [
|
|
41
|
-
...literalTreeNodes.map(([name]) => core.CompletionItem.create(name, ctx.offset, {
|
|
42
|
-
|
|
43
|
-
})),
|
|
44
|
-
...argumentTreeNodes.flatMap(([_name, treeNode]) => core.Arrayable.toArray(getMockNodes(treeNode, ctx.offset)).flatMap((n) => core.completer.dispatch(n, ctx))),
|
|
38
|
+
...literalTreeNodes.map(([name]) => core.CompletionItem.create(name, ctx.offset, { kind: 14 /* core.CompletionKind.Keyword */ })),
|
|
39
|
+
...argumentTreeNodes.flatMap(([_name, treeNode]) => core.Arrayable.toArray(getMockNodes(treeNode, ctx)).flatMap((n) => core.completer.dispatch(n, ctx))),
|
|
45
40
|
];
|
|
46
41
|
};
|
|
47
42
|
}
|
package/lib/node/command.d.ts
CHANGED
|
@@ -4,15 +4,9 @@ export interface CommandNode extends core.SequenceNode<CommandChildNode> {
|
|
|
4
4
|
slash?: core.Range;
|
|
5
5
|
}
|
|
6
6
|
export declare namespace CommandNode {
|
|
7
|
-
function is(node:
|
|
7
|
+
function is<T extends core.DeepReadonly<core.AstNode> | undefined>(node: T): node is core.InheritReadonly<CommandNode, T>;
|
|
8
8
|
function mock(range: core.RangeLike): CommandNode;
|
|
9
9
|
}
|
|
10
|
-
export interface CommandMacroNode extends core.AstNode {
|
|
11
|
-
type: 'mcfunction:command_macro';
|
|
12
|
-
}
|
|
13
|
-
export declare namespace CommandMacroNode {
|
|
14
|
-
function is<T extends core.DeepReadonly<core.AstNode> | undefined>(obj: T): obj is core.InheritReadonly<CommandMacroNode, T>;
|
|
15
|
-
}
|
|
16
10
|
export interface CommandChildNode extends core.AstNode {
|
|
17
11
|
type: 'mcfunction:command_child';
|
|
18
12
|
/**
|
package/lib/node/command.js
CHANGED
|
@@ -3,26 +3,14 @@ export var CommandNode;
|
|
|
3
3
|
(function (CommandNode) {
|
|
4
4
|
/* istanbul ignore next */
|
|
5
5
|
function is(node) {
|
|
6
|
-
return node
|
|
6
|
+
return node?.type === 'mcfunction:command';
|
|
7
7
|
}
|
|
8
8
|
CommandNode.is = is;
|
|
9
9
|
function mock(range) {
|
|
10
|
-
return {
|
|
11
|
-
type: 'mcfunction:command',
|
|
12
|
-
range: core.Range.get(range),
|
|
13
|
-
children: [],
|
|
14
|
-
};
|
|
10
|
+
return { type: 'mcfunction:command', range: core.Range.get(range), children: [] };
|
|
15
11
|
}
|
|
16
12
|
CommandNode.mock = mock;
|
|
17
13
|
})(CommandNode || (CommandNode = {}));
|
|
18
|
-
export var CommandMacroNode;
|
|
19
|
-
(function (CommandMacroNode) {
|
|
20
|
-
function is(obj) {
|
|
21
|
-
return obj?.type ===
|
|
22
|
-
'mcfunction:command_macro';
|
|
23
|
-
}
|
|
24
|
-
CommandMacroNode.is = is;
|
|
25
|
-
})(CommandMacroNode || (CommandMacroNode = {}));
|
|
26
14
|
export var CommandChildNode;
|
|
27
15
|
(function (CommandChildNode) {
|
|
28
16
|
function is(node) {
|
|
@@ -33,8 +21,8 @@ export var CommandChildNode;
|
|
|
33
21
|
export var LiteralCommandChildNode;
|
|
34
22
|
(function (LiteralCommandChildNode) {
|
|
35
23
|
function is(node) {
|
|
36
|
-
return (node?.type
|
|
37
|
-
'mcfunction:command_child/literal');
|
|
24
|
+
return (node?.type
|
|
25
|
+
=== 'mcfunction:command_child/literal');
|
|
38
26
|
}
|
|
39
27
|
LiteralCommandChildNode.is = is;
|
|
40
28
|
})(LiteralCommandChildNode || (LiteralCommandChildNode = {}));
|
package/lib/node/entry.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type * as core from '@spyglassmc/core';
|
|
2
|
-
import type {
|
|
3
|
-
|
|
2
|
+
import type { CommandNode } from './command.js';
|
|
3
|
+
import type { MacroNode } from './macro.js';
|
|
4
|
+
export interface McfunctionNode extends core.SequenceNode<CommandNode | MacroNode | core.CommentNode> {
|
|
4
5
|
type: 'mcfunction:entry';
|
|
5
6
|
}
|
|
6
7
|
export declare namespace McfunctionNode {
|
package/lib/node/index.d.ts
CHANGED
package/lib/node/index.js
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as core from '@spyglassmc/core';
|
|
2
|
+
export interface MacroNode extends core.SequenceNode<MacroOtherNode | MacroArgumentNode> {
|
|
3
|
+
type: 'mcfunction:macro';
|
|
4
|
+
children: (MacroOtherNode | MacroArgumentNode)[];
|
|
5
|
+
}
|
|
6
|
+
export declare namespace MacroNode {
|
|
7
|
+
function is<T extends core.DeepReadonly<core.AstNode> | undefined>(obj: T): obj is core.InheritReadonly<MacroNode, T>;
|
|
8
|
+
function mock(range: core.RangeLike): MacroNode;
|
|
9
|
+
}
|
|
10
|
+
export interface MacroOtherNode extends core.AstNode {
|
|
11
|
+
type: 'mcfunction:macro/other';
|
|
12
|
+
value?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface MacroArgumentNode extends core.AstNode {
|
|
15
|
+
type: 'mcfunction:macro/argument';
|
|
16
|
+
value?: string;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=macro.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as core from '@spyglassmc/core';
|
|
2
|
+
export var MacroNode;
|
|
3
|
+
(function (MacroNode) {
|
|
4
|
+
/* istanbul ignore next */
|
|
5
|
+
function is(obj) {
|
|
6
|
+
return obj?.type === 'mcfunction:macro';
|
|
7
|
+
}
|
|
8
|
+
MacroNode.is = is;
|
|
9
|
+
function mock(range) {
|
|
10
|
+
return { type: 'mcfunction:macro', range: core.Range.get(range), children: [] };
|
|
11
|
+
}
|
|
12
|
+
MacroNode.mock = mock;
|
|
13
|
+
})(MacroNode || (MacroNode = {}));
|
|
14
|
+
//# sourceMappingURL=macro.js.map
|
package/lib/parser/command.js
CHANGED
|
@@ -48,10 +48,7 @@ function dispatch(ans, src, ctx, path, rootTreeNode, parentTreeNode, argument) {
|
|
|
48
48
|
return false;
|
|
49
49
|
}
|
|
50
50
|
const { literalTreeNodes, argumentTreeNodes } = categorizeTreeChildren(children);
|
|
51
|
-
const argumentParsers = argumentTreeNodes.map(([name, treeNode]) => ({
|
|
52
|
-
name,
|
|
53
|
-
parser: argument(treeNode) ?? unknown(treeNode),
|
|
54
|
-
}));
|
|
51
|
+
const argumentParsers = argumentTreeNodes.map(([name, treeNode]) => ({ name, parser: argument(treeNode) ?? unknown(treeNode) }));
|
|
55
52
|
const literalParser = literalTreeNodes.length
|
|
56
53
|
? literal(literalTreeNodes.map(([name, _treeNode]) => name), parent.type === 'root')
|
|
57
54
|
: undefined;
|
|
@@ -60,11 +57,14 @@ function dispatch(ans, src, ctx, path, rootTreeNode, parentTreeNode, argument) {
|
|
|
60
57
|
...argumentParsers.map((v) => v.parser),
|
|
61
58
|
];
|
|
62
59
|
const out = { index: 0 };
|
|
60
|
+
if (parsers.length === 0) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
63
|
const parser = parsers.length > 1 ? core.any(parsers, out) : parsers[0];
|
|
64
64
|
const result = parser(src, ctx);
|
|
65
65
|
if (result !== core.Failure) {
|
|
66
|
-
const takenName = argumentParsers[out.index - (literalParser ? 1 : 0)]?.name
|
|
67
|
-
result.value;
|
|
66
|
+
const takenName = argumentParsers[out.index - (literalParser ? 1 : 0)]?.name
|
|
67
|
+
?? result.value;
|
|
68
68
|
const childPath = [...path, takenName];
|
|
69
69
|
ans.push({
|
|
70
70
|
type: 'mcfunction:command_child',
|
|
@@ -80,8 +80,7 @@ function dispatch(ans, src, ctx, path, rootTreeNode, parentTreeNode, argument) {
|
|
|
80
80
|
if (ctx.config.env.permissionLevel < requiredPermissionLevel) {
|
|
81
81
|
ctx.err.report(localize('mcfunction.parser.no-permission', requiredPermissionLevel, ctx.config.env.permissionLevel), result);
|
|
82
82
|
}
|
|
83
|
-
if (result.type ===
|
|
84
|
-
'mcfunction:command_child/unknown') {
|
|
83
|
+
if (result.type === 'mcfunction:command_child/unknown') {
|
|
85
84
|
// Encountered an unsupported parser. Stop parsing this command.
|
|
86
85
|
return false;
|
|
87
86
|
}
|
|
@@ -114,11 +113,7 @@ function unknown(treeNode) {
|
|
|
114
113
|
const value = src.readUntilLineEnd();
|
|
115
114
|
const range = core.Range.create(start, src);
|
|
116
115
|
ctx.err.report(localize('mcfunction.parser.unknown-parser', localeQuote(treeNode.parser)), range, 0 /* core.ErrorSeverity.Hint */);
|
|
117
|
-
return {
|
|
118
|
-
type: 'mcfunction:command_child/unknown',
|
|
119
|
-
range,
|
|
120
|
-
value,
|
|
121
|
-
};
|
|
116
|
+
return { type: 'mcfunction:command_child/unknown', range, value };
|
|
122
117
|
};
|
|
123
118
|
}
|
|
124
119
|
const trailing = (src, ctx) => {
|
|
@@ -126,11 +121,7 @@ const trailing = (src, ctx) => {
|
|
|
126
121
|
const value = src.readUntilLineEnd();
|
|
127
122
|
const range = core.Range.create(start, src);
|
|
128
123
|
ctx.err.report(localize('mcfunction.parser.trailing', localeQuote(value)), range);
|
|
129
|
-
return {
|
|
130
|
-
type: 'mcfunction:command_child/trailing',
|
|
131
|
-
range,
|
|
132
|
-
value,
|
|
133
|
-
};
|
|
124
|
+
return { type: 'mcfunction:command_child/trailing', range, value };
|
|
134
125
|
};
|
|
135
126
|
function wrapWithBrackets(syntax, executable) {
|
|
136
127
|
return executable ? `[${syntax}]` : syntax;
|
package/lib/parser/entry.d.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import * as core from '@spyglassmc/core';
|
|
2
2
|
import type { McfunctionNode } from '../node/index.js';
|
|
3
|
+
import type { RootTreeNode } from '../tree/index.js';
|
|
3
4
|
import type { ArgumentParserGetter } from './argument.js';
|
|
4
5
|
/**
|
|
5
|
-
* @
|
|
6
|
+
* @param supportsBackslashContinuation Whether or not to concatenate lines together on trailing backslashes.
|
|
7
|
+
* @param supportsMacros Whether or not to parse macro lines as an error.
|
|
8
|
+
* Disabled by default.
|
|
6
9
|
*/
|
|
7
|
-
export declare
|
|
10
|
+
export declare const entry: (commandTree: RootTreeNode, argument: ArgumentParserGetter, { supportsBackslashContinuation, supportsMacros }?: {
|
|
11
|
+
supportsBackslashContinuation?: boolean | undefined;
|
|
12
|
+
supportsMacros?: boolean | undefined;
|
|
13
|
+
}) => core.Parser<McfunctionNode>;
|
|
8
14
|
//# sourceMappingURL=entry.d.ts.map
|
package/lib/parser/entry.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import * as core from '@spyglassmc/core';
|
|
2
|
-
import { CommandTreeRegistry } from '../tree/index.js';
|
|
3
2
|
import { command } from './command.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*/
|
|
7
|
-
export function entry(commandTreeName, argument) {
|
|
3
|
+
import { macro } from './macro.js';
|
|
4
|
+
function mcfunction(commandTree, argument, { supportsMacros }) {
|
|
8
5
|
return (src, ctx) => {
|
|
9
6
|
const ans = {
|
|
10
7
|
type: 'mcfunction:entry',
|
|
@@ -17,15 +14,10 @@ export function entry(commandTreeName, argument) {
|
|
|
17
14
|
result = comment(src, ctx);
|
|
18
15
|
}
|
|
19
16
|
else if (src.peek() === '$') {
|
|
20
|
-
|
|
21
|
-
src.skipLine();
|
|
22
|
-
result = {
|
|
23
|
-
type: 'mcfunction:command_macro',
|
|
24
|
-
range: core.Range.create(start, src),
|
|
25
|
-
};
|
|
17
|
+
result = macro(supportsMacros)(src, ctx);
|
|
26
18
|
}
|
|
27
19
|
else {
|
|
28
|
-
result = command(
|
|
20
|
+
result = command(commandTree, argument)(src, ctx);
|
|
29
21
|
}
|
|
30
22
|
ans.children.push(result);
|
|
31
23
|
src.nextLine();
|
|
@@ -34,7 +26,14 @@ export function entry(commandTreeName, argument) {
|
|
|
34
26
|
return ans;
|
|
35
27
|
};
|
|
36
28
|
}
|
|
37
|
-
const comment = core.comment({
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
const comment = core.comment({ singleLinePrefixes: new Set(['#']) });
|
|
30
|
+
/**
|
|
31
|
+
* @param supportsBackslashContinuation Whether or not to concatenate lines together on trailing backslashes.
|
|
32
|
+
* @param supportsMacros Whether or not to parse macro lines as an error.
|
|
33
|
+
* Disabled by default.
|
|
34
|
+
*/
|
|
35
|
+
export const entry = (commandTree, argument, { supportsBackslashContinuation = false, supportsMacros = false } = {}) => {
|
|
36
|
+
const parser = mcfunction(commandTree, argument, { supportsMacros });
|
|
37
|
+
return supportsBackslashContinuation ? core.concatOnTrailingBackslash(parser) : parser;
|
|
38
|
+
};
|
|
40
39
|
//# sourceMappingURL=entry.js.map
|
package/lib/parser/index.d.ts
CHANGED
package/lib/parser/index.js
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as core from '@spyglassmc/core';
|
|
2
|
+
import type { MacroNode } from '../node/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Parse a macro line.
|
|
5
|
+
* @param supportsMacros When false, reports the entire line as an error (due to
|
|
6
|
+
* the Minecraft version not supporting macros).
|
|
7
|
+
*/
|
|
8
|
+
export declare function macro(supportsMacros?: boolean): core.Parser<MacroNode | core.ErrorNode>;
|
|
9
|
+
//# sourceMappingURL=macro.d.ts.map
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import * as core from '@spyglassmc/core';
|
|
2
|
+
import { localeQuote, localize } from '@spyglassmc/locales';
|
|
3
|
+
/**
|
|
4
|
+
* Parse a macro line.
|
|
5
|
+
* @param supportsMacros When false, reports the entire line as an error (due to
|
|
6
|
+
* the Minecraft version not supporting macros).
|
|
7
|
+
*/
|
|
8
|
+
export function macro(supportsMacros = true) {
|
|
9
|
+
return (src, ctx) => {
|
|
10
|
+
const ans = {
|
|
11
|
+
type: 'mcfunction:macro',
|
|
12
|
+
range: core.Range.create(src.cursor),
|
|
13
|
+
children: [],
|
|
14
|
+
};
|
|
15
|
+
if (!supportsMacros) {
|
|
16
|
+
src.skipLine();
|
|
17
|
+
ans.range.end = src.cursor;
|
|
18
|
+
ctx.err.report(localize('mcfunction.parser.macro.disallowed'), ans.range);
|
|
19
|
+
return { ...ans, type: 'error' };
|
|
20
|
+
}
|
|
21
|
+
// Skip the starting '$'
|
|
22
|
+
src.skip();
|
|
23
|
+
let start = src.cursor;
|
|
24
|
+
let hasMacroArgs = false;
|
|
25
|
+
while (src.canReadInLine()) {
|
|
26
|
+
src.skipUntilOrEnd(core.LF, core.CR, '$');
|
|
27
|
+
if (src.peek(2) === '$(') {
|
|
28
|
+
hasMacroArgs = true;
|
|
29
|
+
// Add the other stuff before this macro key
|
|
30
|
+
const other = src.sliceToCursor(start);
|
|
31
|
+
if (other.length > 0) {
|
|
32
|
+
ans.children.push({
|
|
33
|
+
type: 'mcfunction:macro/other',
|
|
34
|
+
range: core.Range.create(start, src.cursor),
|
|
35
|
+
value: other,
|
|
36
|
+
});
|
|
37
|
+
start = src.cursor;
|
|
38
|
+
}
|
|
39
|
+
// Parse the macro key
|
|
40
|
+
const key = validateMacroArgument(src, ctx, start);
|
|
41
|
+
ans.children.push({
|
|
42
|
+
type: 'mcfunction:macro/argument',
|
|
43
|
+
range: core.Range.create(start, src.cursor),
|
|
44
|
+
value: key,
|
|
45
|
+
});
|
|
46
|
+
start = src.cursor;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
if (src.peek() === '$') {
|
|
50
|
+
src.skip();
|
|
51
|
+
}
|
|
52
|
+
if (!src.canReadInLine()) {
|
|
53
|
+
// No more macro arguments, add the remaining other stuff
|
|
54
|
+
ans.children.push({
|
|
55
|
+
type: 'mcfunction:macro/other',
|
|
56
|
+
range: core.Range.create(start, src.cursor),
|
|
57
|
+
value: src.sliceToCursor(start),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// A line with no macro arguments is invalid
|
|
63
|
+
if (!hasMacroArgs) {
|
|
64
|
+
ctx.err.report(localize('expected', localize('mcfunction.parser.macro.at-least-one')), core.Range.create(start, src.cursor));
|
|
65
|
+
}
|
|
66
|
+
ans.range.end = src.cursor;
|
|
67
|
+
return ans;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Error checking for a macro argument/key.
|
|
72
|
+
*/
|
|
73
|
+
function validateMacroArgument(src, ctx, start) {
|
|
74
|
+
src.skip(2);
|
|
75
|
+
const keyStart = src.cursor;
|
|
76
|
+
src.skipUntilOrEnd(core.LF, core.CR, ')');
|
|
77
|
+
if (src.peek() != ')') {
|
|
78
|
+
// Macro key was not closed
|
|
79
|
+
ctx.err.report(localize('expected', localeQuote(')')), core.Range.create(keyStart, src.cursor));
|
|
80
|
+
}
|
|
81
|
+
else if (src.cursor <= keyStart) {
|
|
82
|
+
// Encountered $()
|
|
83
|
+
ctx.err.report(localize('expected', localize('mcfunction.parser.macro.key')), core.Range.create(start, src.cursor + 1));
|
|
84
|
+
}
|
|
85
|
+
const key = src.sliceToCursor(keyStart);
|
|
86
|
+
const matchedInvalid = key.replace(/[a-zA-Z0-9_]*/, '');
|
|
87
|
+
if (matchedInvalid.length > 0) {
|
|
88
|
+
ctx.err.report(localize('mcfunction.parser.macro.illegal-key', matchedInvalid.charAt(0)), core.Range.create(keyStart, src.cursor));
|
|
89
|
+
}
|
|
90
|
+
src.skip();
|
|
91
|
+
return key;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=macro.js.map
|
package/lib/tree/index.d.ts
CHANGED
package/lib/tree/index.js
CHANGED
package/lib/tree/util.js
CHANGED
|
@@ -8,9 +8,7 @@ export function resolveParentTreeNode(parentTreeNode, rootTreeNode, parentPath)
|
|
|
8
8
|
path: [...parentTreeNode.redirect],
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
|
-
else if (parentTreeNode &&
|
|
12
|
-
!parentTreeNode.children &&
|
|
13
|
-
!parentTreeNode.executable) {
|
|
11
|
+
else if (parentTreeNode && !parentTreeNode.children && !parentTreeNode.executable) {
|
|
14
12
|
// The `execute.run` literal tree node doesn't have any property.
|
|
15
13
|
// We should use children from the root tree node in this case.
|
|
16
14
|
return { treeNode: rootTreeNode, path: [] };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spyglassmc/mcfunction",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"url": "https://github.com/SpyglassMC/Spyglass/issues"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@spyglassmc/core": "0.4.
|
|
29
|
-
"@spyglassmc/locales": "0.3.
|
|
28
|
+
"@spyglassmc/core": "0.4.7",
|
|
29
|
+
"@spyglassmc/locales": "0.3.7"
|
|
30
30
|
}
|
|
31
31
|
}
|
package/lib/tree/registry.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { PartialRootTreeNode, RootTreeNode } from './type.js';
|
|
2
|
-
/**
|
|
3
|
-
* The registry for mcfunction command trees.
|
|
4
|
-
* This is a singleton; use the `instance` static property to get an instance.
|
|
5
|
-
*/
|
|
6
|
-
export declare class CommandTreeRegistry {
|
|
7
|
-
#private;
|
|
8
|
-
/**
|
|
9
|
-
* Register command tree for an arbitrary version.
|
|
10
|
-
*
|
|
11
|
-
* @param version The game version. e.g. `1.15-tdn`.
|
|
12
|
-
* @param tree The command tree for this version.
|
|
13
|
-
* @param treePatch A custom command tree patch that will be merged onto `tree`.
|
|
14
|
-
*/
|
|
15
|
-
register(version: string, tree: RootTreeNode, treePatch?: PartialRootTreeNode): void;
|
|
16
|
-
/**
|
|
17
|
-
* @throws When there's no command tree associated with the version.
|
|
18
|
-
*/
|
|
19
|
-
get(version: string): RootTreeNode;
|
|
20
|
-
/**
|
|
21
|
-
* An instance of `CommandTreeRegistry`.
|
|
22
|
-
*/
|
|
23
|
-
static get instance(): CommandTreeRegistry;
|
|
24
|
-
private constructor();
|
|
25
|
-
private static _instance;
|
|
26
|
-
}
|
|
27
|
-
//# sourceMappingURL=registry.d.ts.map
|
package/lib/tree/registry.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { merge } from '@spyglassmc/core';
|
|
2
|
-
/* istanbul ignore next */
|
|
3
|
-
/**
|
|
4
|
-
* The registry for mcfunction command trees.
|
|
5
|
-
* This is a singleton; use the `instance` static property to get an instance.
|
|
6
|
-
*/
|
|
7
|
-
export class CommandTreeRegistry {
|
|
8
|
-
#trees = new Map();
|
|
9
|
-
/**
|
|
10
|
-
* Register command tree for an arbitrary version.
|
|
11
|
-
*
|
|
12
|
-
* @param version The game version. e.g. `1.15-tdn`.
|
|
13
|
-
* @param tree The command tree for this version.
|
|
14
|
-
* @param treePatch A custom command tree patch that will be merged onto `tree`.
|
|
15
|
-
*/
|
|
16
|
-
register(version, tree, treePatch) {
|
|
17
|
-
this.#trees.set(version, treePatch ? merge(tree, treePatch) : tree);
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* @throws When there's no command tree associated with the version.
|
|
21
|
-
*/
|
|
22
|
-
get(version) {
|
|
23
|
-
const ans = this.#trees.get(version);
|
|
24
|
-
if (!ans) {
|
|
25
|
-
throw new Error(`No command tree exist for version “${version}”`);
|
|
26
|
-
}
|
|
27
|
-
return ans;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* An instance of `CommandTreeRegistry`.
|
|
31
|
-
*/
|
|
32
|
-
static get instance() {
|
|
33
|
-
return this._instance ?? (this._instance = new CommandTreeRegistry());
|
|
34
|
-
}
|
|
35
|
-
constructor() {
|
|
36
|
-
if (CommandTreeRegistry._instance) {
|
|
37
|
-
throw new Error('Use the `instance` static property to get an instance.');
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
static _instance;
|
|
41
|
-
}
|
|
42
|
-
//# sourceMappingURL=registry.js.map
|