@spyglassmc/core 0.4.19 → 0.4.21
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/node/RecordNode.d.ts +1 -0
- package/lib/parser/list.js +14 -1
- package/lib/parser/record.js +16 -1
- package/lib/parser/resourceLocation.js +14 -1
- package/lib/processor/codeActions/CodeAction.d.ts +8 -0
- package/lib/processor/codeActions/CodeAction.js +2 -0
- package/lib/processor/codeActions/builtin.d.ts +8 -0
- package/lib/processor/codeActions/builtin.js +29 -0
- package/lib/processor/codeActions/index.d.ts +3 -0
- package/lib/processor/codeActions/index.js +3 -0
- package/lib/processor/completer/builtin.js +1 -1
- package/lib/processor/index.d.ts +1 -0
- package/lib/processor/index.js +1 -0
- package/lib/processor/linter/builtin/undeclaredSymbol.js +12 -1
- package/lib/service/CacheService.js +2 -2
- package/lib/service/Config.d.ts +1 -1
- package/lib/service/Config.js +5 -1
- package/lib/service/Context.d.ts +9 -0
- package/lib/service/Context.js +7 -0
- package/lib/service/MetaRegistry.d.ts +8 -1
- package/lib/service/MetaRegistry.js +22 -1
- package/lib/service/Project.d.ts +1 -4
- package/lib/service/Project.js +29 -38
- package/lib/service/Service.d.ts +2 -1
- package/lib/service/Service.js +13 -1
- package/lib/service/UriBuilder.d.ts +11 -0
- package/lib/service/UriBuilder.js +2 -0
- package/lib/service/fileUtil.d.ts +2 -0
- package/lib/service/fileUtil.js +14 -0
- package/lib/service/index.d.ts +1 -0
- package/lib/service/index.js +1 -0
- package/lib/source/LanguageError.d.ts +14 -1
- package/package.json +4 -3
package/lib/node/RecordNode.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Range } from '../source/index.js';
|
|
|
2
2
|
import type { AstNode } from './AstNode.js';
|
|
3
3
|
export interface RecordBaseNode<K extends AstNode, V extends AstNode> extends AstNode {
|
|
4
4
|
readonly children: PairNode<K, V>[];
|
|
5
|
+
innerRange?: Range;
|
|
5
6
|
}
|
|
6
7
|
export interface RecordNode<K extends AstNode, V extends AstNode> extends RecordBaseNode<K, V> {
|
|
7
8
|
type: 'record';
|
package/lib/parser/list.js
CHANGED
|
@@ -45,7 +45,20 @@ export function list({ start, value, sep, trailingSep, end }) {
|
|
|
45
45
|
}
|
|
46
46
|
// Trailing item sep.
|
|
47
47
|
if (hasValueSep && !trailingSep) {
|
|
48
|
-
|
|
48
|
+
const trailingRange = ans.children[ans.children.length - 1].sep;
|
|
49
|
+
ctx.err.report(localize('parser.list.trailing-sep'), trailingRange, 3 /* ErrorSeverity.Error */, {
|
|
50
|
+
codeAction: {
|
|
51
|
+
title: localize('code-action.remove-trailing-separation'),
|
|
52
|
+
isPreferred: true,
|
|
53
|
+
changes: [
|
|
54
|
+
{
|
|
55
|
+
type: 'edit',
|
|
56
|
+
range: trailingRange,
|
|
57
|
+
text: '',
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
});
|
|
49
62
|
}
|
|
50
63
|
// End.
|
|
51
64
|
if (!src.trySkip(end)) {
|
package/lib/parser/record.js
CHANGED
|
@@ -9,6 +9,7 @@ export function record({ start, pair, end }) {
|
|
|
9
9
|
return (src, ctx) => {
|
|
10
10
|
const ans = { type: 'record', range: Range.create(src), children: [] };
|
|
11
11
|
if (src.trySkip(start)) {
|
|
12
|
+
ans.innerRange = Range.create(src);
|
|
12
13
|
src.skipWhitespace();
|
|
13
14
|
let requiresPairEnd = false;
|
|
14
15
|
let hasPairEnd = false;
|
|
@@ -80,9 +81,23 @@ export function record({ start, pair, end }) {
|
|
|
80
81
|
}
|
|
81
82
|
// Trailing pair end.
|
|
82
83
|
if (hasPairEnd && !pair.trailingEnd) {
|
|
83
|
-
|
|
84
|
+
const trailingRange = ans.children[ans.children.length - 1].end;
|
|
85
|
+
ctx.err.report(localize('parser.record.trailing-end'), trailingRange, 3 /* ErrorSeverity.Error */, {
|
|
86
|
+
codeAction: {
|
|
87
|
+
title: localize('code-action.remove-trailing-separation'),
|
|
88
|
+
isPreferred: true,
|
|
89
|
+
changes: [
|
|
90
|
+
{
|
|
91
|
+
type: 'edit',
|
|
92
|
+
range: trailingRange,
|
|
93
|
+
text: '',
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
});
|
|
84
98
|
}
|
|
85
99
|
// End.
|
|
100
|
+
ans.innerRange.end = src.cursor;
|
|
86
101
|
if (!src.trySkip(end)) {
|
|
87
102
|
ctx.err.report(localize('expected', localeQuote(end)), src);
|
|
88
103
|
}
|
|
@@ -104,7 +104,20 @@ export function resourceLocation(options) {
|
|
|
104
104
|
ctx.err.report(localize('parser.resource-location.tag-required'), ans);
|
|
105
105
|
}
|
|
106
106
|
if (!ans.namespace && options.requireCanonical) {
|
|
107
|
-
ctx.err.report(localize('parser.resource-location.namespace-expected'), ans
|
|
107
|
+
ctx.err.report(localize('parser.resource-location.namespace-expected'), ans, 3 /* ErrorSeverity.Error */, {
|
|
108
|
+
codeAction: {
|
|
109
|
+
title: localize('code-action.add-default-namespace'),
|
|
110
|
+
isPreferred: true,
|
|
111
|
+
changes: [
|
|
112
|
+
{
|
|
113
|
+
type: 'edit',
|
|
114
|
+
range: Range.create(start),
|
|
115
|
+
text: ResourceLocation.DefaultNamespace
|
|
116
|
+
+ ResourceLocation.NamespacePathSep,
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
});
|
|
108
121
|
}
|
|
109
122
|
}
|
|
110
123
|
return ans;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AstNode } from '../../node/index.js';
|
|
2
|
+
import type { CodeActionProviderContext } from '../../service/index.js';
|
|
3
|
+
import type { LanguageError, LanguageErrorAction } from '../../source/index.js';
|
|
4
|
+
export interface CodeAction extends LanguageErrorAction {
|
|
5
|
+
errors?: LanguageError[];
|
|
6
|
+
}
|
|
7
|
+
export type CodeActionProvider<N extends AstNode = AstNode> = (node: N, ctx: CodeActionProviderContext) => readonly CodeAction[];
|
|
8
|
+
//# sourceMappingURL=CodeAction.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AstNode } from '../../node/index.js';
|
|
2
|
+
import { FileNode } from '../../node/index.js';
|
|
3
|
+
import type { MetaRegistry } from '../../service/index.js';
|
|
4
|
+
import type { CodeActionProvider } from './CodeAction.js';
|
|
5
|
+
export declare const fallback: CodeActionProvider<AstNode>;
|
|
6
|
+
export declare const file: CodeActionProvider<FileNode<AstNode>>;
|
|
7
|
+
export declare function registerProviders(meta: MetaRegistry): void;
|
|
8
|
+
//# sourceMappingURL=builtin.d.ts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { FileNode } from '../../node/index.js';
|
|
2
|
+
import { Range } from '../../source/index.js';
|
|
3
|
+
import { traversePreOrder } from '../util.js';
|
|
4
|
+
export const fallback = (node, ctx) => {
|
|
5
|
+
const ans = [];
|
|
6
|
+
traversePreOrder(node, (node) => Range.containsRange(node.range, ctx.range, true), (node) => ctx.meta.hasCodeActionProvider(node.type), (node) => ans.push(...ctx.meta.getCodeActionProvider(node.type)(node, ctx)));
|
|
7
|
+
return ans;
|
|
8
|
+
};
|
|
9
|
+
export const file = (node, ctx) => {
|
|
10
|
+
const ans = [];
|
|
11
|
+
for (const error of FileNode.getErrors(node)) {
|
|
12
|
+
const action = error.info?.codeAction;
|
|
13
|
+
if (!action) {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
if (!Range.containsRange(error.range, ctx.range, true)) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
ans.push({
|
|
20
|
+
...action,
|
|
21
|
+
errors: [error],
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return ans;
|
|
25
|
+
};
|
|
26
|
+
export function registerProviders(meta) {
|
|
27
|
+
meta.registerCodeActionProvider('file', file);
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=builtin.js.map
|
|
@@ -62,7 +62,7 @@ const prefixed = (node, ctx) => {
|
|
|
62
62
|
};
|
|
63
63
|
export function record(o) {
|
|
64
64
|
return (node, ctx) => {
|
|
65
|
-
if (!Range.contains(
|
|
65
|
+
if (!node.innerRange || !Range.contains(node.innerRange, ctx.offset, true)) {
|
|
66
66
|
return [];
|
|
67
67
|
}
|
|
68
68
|
const completeKeys = (pair) => o.key(node, pair, ctx, pair?.key ?? ctx.offset, false, false, existingKeys);
|
package/lib/processor/index.d.ts
CHANGED
package/lib/processor/index.js
CHANGED
|
@@ -14,10 +14,21 @@ export const undeclaredSymbol = (node, ctx) => {
|
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
16
|
if (Config.Action.isReport(action)) {
|
|
17
|
+
const info = {};
|
|
18
|
+
const uriBuilder = ctx.meta.getUriBuilder(node.symbol.category);
|
|
19
|
+
if (uriBuilder) {
|
|
20
|
+
const uri = uriBuilder(node.symbol.identifier, ctx);
|
|
21
|
+
if (uri) {
|
|
22
|
+
info.codeAction = {
|
|
23
|
+
title: localize('code-action.create-undeclared-file', node.symbol.category, localeQuote(node.symbol.identifier)),
|
|
24
|
+
changes: [{ type: 'create', uri }],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}
|
|
17
28
|
const severityOverride = action.report === 'inherit'
|
|
18
29
|
? undefined
|
|
19
30
|
: LinterSeverity.toErrorSeverity(action.report);
|
|
20
|
-
ctx.err.lint(localize('linter.undeclared-symbol.message', node.symbol.category, localeQuote(node.symbol.identifier)), node,
|
|
31
|
+
ctx.err.lint(localize('linter.undeclared-symbol.message', node.symbol.category, localeQuote(node.symbol.identifier)), node, info, severityOverride);
|
|
21
32
|
}
|
|
22
33
|
};
|
|
23
34
|
function getAction(config, symbol, ctx) {
|
|
@@ -130,8 +130,8 @@ export class CacheService {
|
|
|
130
130
|
ans.unchangedFiles.push(uri);
|
|
131
131
|
continue;
|
|
132
132
|
}
|
|
133
|
-
if (this.project.
|
|
134
|
-
ans.
|
|
133
|
+
if (this.project.shouldExclude(uri)) {
|
|
134
|
+
ans.removedFiles.push(uri);
|
|
135
135
|
continue;
|
|
136
136
|
}
|
|
137
137
|
try {
|
package/lib/service/Config.d.ts
CHANGED
|
@@ -59,7 +59,7 @@ export interface EnvConfig {
|
|
|
59
59
|
*/
|
|
60
60
|
dependencies: string[];
|
|
61
61
|
/**
|
|
62
|
-
* A list of file patterns to exclude.
|
|
62
|
+
* A list of file patterns to exclude.
|
|
63
63
|
*/
|
|
64
64
|
exclude: string[];
|
|
65
65
|
/**
|
package/lib/service/Config.js
CHANGED
|
@@ -107,7 +107,11 @@ export const VanillaConfig = {
|
|
|
107
107
|
env: {
|
|
108
108
|
dataSource: 'GitHub',
|
|
109
109
|
dependencies: ['@vanilla-datapack', '@vanilla-resourcepack', '@vanilla-mcdoc'],
|
|
110
|
-
exclude: [
|
|
110
|
+
exclude: [
|
|
111
|
+
'.*/**',
|
|
112
|
+
'**/node_modules/**',
|
|
113
|
+
'**/__pycache__/**',
|
|
114
|
+
],
|
|
111
115
|
customResources: {},
|
|
112
116
|
feature: {
|
|
113
117
|
codeActions: true,
|
package/lib/service/Context.d.ts
CHANGED
|
@@ -112,6 +112,15 @@ interface FormatterContextOptions extends ProcessorContextOptions {
|
|
|
112
112
|
export declare namespace FormatterContext {
|
|
113
113
|
function create(project: ProjectData, opts: FormatterContextOptions): FormatterContext;
|
|
114
114
|
}
|
|
115
|
+
export interface CodeActionProviderContext extends ProcessorContext {
|
|
116
|
+
range: Range;
|
|
117
|
+
}
|
|
118
|
+
export interface CodeActionProviderContextOptions extends ProcessorContextOptions {
|
|
119
|
+
range: Range;
|
|
120
|
+
}
|
|
121
|
+
export declare namespace CodeActionProviderContext {
|
|
122
|
+
function create(project: ProjectData, opts: CodeActionProviderContextOptions): CodeActionProviderContext;
|
|
123
|
+
}
|
|
115
124
|
export interface ColorizerContext extends ProcessorWithRangeContext {
|
|
116
125
|
}
|
|
117
126
|
export interface ColorizerContextOptions extends ProcessorWithRangeContextOptions {
|
package/lib/service/Context.js
CHANGED
|
@@ -104,6 +104,13 @@ export var FormatterContext;
|
|
|
104
104
|
}
|
|
105
105
|
FormatterContext.create = create;
|
|
106
106
|
})(FormatterContext || (FormatterContext = {}));
|
|
107
|
+
export var CodeActionProviderContext;
|
|
108
|
+
(function (CodeActionProviderContext) {
|
|
109
|
+
function create(project, opts) {
|
|
110
|
+
return { ...ProcessorContext.create(project, opts), range: opts.range };
|
|
111
|
+
}
|
|
112
|
+
CodeActionProviderContext.create = create;
|
|
113
|
+
})(CodeActionProviderContext || (CodeActionProviderContext = {}));
|
|
107
114
|
export var ColorizerContext;
|
|
108
115
|
(function (ColorizerContext) {
|
|
109
116
|
function create(project, opts) {
|
|
@@ -3,12 +3,13 @@ import { Lazy } from '../common/index.js';
|
|
|
3
3
|
import type { AstNode } from '../node/index.js';
|
|
4
4
|
import type { Parser } from '../parser/index.js';
|
|
5
5
|
import type { Formatter } from '../processor/formatter/index.js';
|
|
6
|
-
import type { Binder, Checker, Colorizer, Completer, InlayHintProvider } from '../processor/index.js';
|
|
6
|
+
import type { Binder, Checker, CodeActionProvider, Colorizer, Completer, InlayHintProvider } from '../processor/index.js';
|
|
7
7
|
import type { Linter } from '../processor/linter/Linter.js';
|
|
8
8
|
import type { SignatureHelpProvider } from '../processor/SignatureHelpProvider.js';
|
|
9
9
|
import type { DependencyKey, DependencyProvider } from './Dependency.js';
|
|
10
10
|
import type { FileExtension } from './fileUtil.js';
|
|
11
11
|
import type { SymbolRegistrar } from './SymbolRegistrar.js';
|
|
12
|
+
import type { UriBuilder } from './UriBuilder.js';
|
|
12
13
|
import type { UriBinder, UriSorter, UriSorterRegistration } from './UriProcessor.js';
|
|
13
14
|
export interface LanguageOptions {
|
|
14
15
|
/**
|
|
@@ -69,6 +70,9 @@ export declare class MetaRegistry {
|
|
|
69
70
|
hasChecker<N extends AstNode>(type: N['type']): boolean;
|
|
70
71
|
getChecker<N extends AstNode>(type: N['type']): Checker<N>;
|
|
71
72
|
registerChecker<N extends AstNode>(type: N['type'], checker: Checker<N>): void;
|
|
73
|
+
hasCodeActionProvider<N extends AstNode>(type: N['type']): boolean;
|
|
74
|
+
getCodeActionProvider<N extends AstNode>(type: N['type']): CodeActionProvider<N>;
|
|
75
|
+
registerCodeActionProvider<N extends AstNode>(type: N['type'], codeActionProvider: CodeActionProvider<N>): void;
|
|
72
76
|
hasColorizer<N extends AstNode>(type: N['type']): boolean;
|
|
73
77
|
getColorizer<N extends AstNode>(type: N['type']): Colorizer<N>;
|
|
74
78
|
registerColorizer<N extends AstNode>(type: N['type'], colorizer: Colorizer<N>): void;
|
|
@@ -111,6 +115,9 @@ export declare class MetaRegistry {
|
|
|
111
115
|
getCustom<T>(group: string): Map<string, T> | undefined;
|
|
112
116
|
registerUriBinder(uriBinder: UriBinder): void;
|
|
113
117
|
get uriBinders(): Set<UriBinder>;
|
|
118
|
+
hasUriBuilder(category: string): boolean;
|
|
119
|
+
getUriBuilder(category: string): UriBuilder | undefined;
|
|
120
|
+
registerUriBuilder(category: string, builder: UriBuilder): void;
|
|
114
121
|
setUriSorter(uriSorter: UriSorterRegistration): void;
|
|
115
122
|
get uriSorter(): UriSorter;
|
|
116
123
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Lazy } from '../common/index.js';
|
|
2
|
-
import { binder, checker, colorizer, completer, formatter, linter } from '../processor/index.js';
|
|
2
|
+
import { binder, checker, codeActions, colorizer, completer, formatter, linter, } from '../processor/index.js';
|
|
3
3
|
/* istanbul ignore next */
|
|
4
4
|
/**
|
|
5
5
|
* The meta registry of Spyglass. You can register new parsers, processors, and languages here.
|
|
@@ -12,6 +12,7 @@ export class MetaRegistry {
|
|
|
12
12
|
#binders = new Map();
|
|
13
13
|
#checkers = new Map();
|
|
14
14
|
#colorizers = new Map();
|
|
15
|
+
#codeActionProviders = new Map();
|
|
15
16
|
#completers = new Map();
|
|
16
17
|
#dependencyProviders = new Map();
|
|
17
18
|
#formatters = new Map();
|
|
@@ -22,10 +23,12 @@ export class MetaRegistry {
|
|
|
22
23
|
#symbolRegistrars = new Map();
|
|
23
24
|
#custom = new Map();
|
|
24
25
|
#uriBinders = new Set();
|
|
26
|
+
#uriBuilders = new Map();
|
|
25
27
|
#uriSorter = () => 0;
|
|
26
28
|
constructor() {
|
|
27
29
|
binder.registerBinders(this);
|
|
28
30
|
checker.registerCheckers(this);
|
|
31
|
+
codeActions.registerProviders(this);
|
|
29
32
|
colorizer.registerColorizers(this);
|
|
30
33
|
completer.registerCompleters(this);
|
|
31
34
|
formatter.registerFormatters(this);
|
|
@@ -90,6 +93,15 @@ export class MetaRegistry {
|
|
|
90
93
|
registerChecker(type, checker) {
|
|
91
94
|
this.#checkers.set(type, checker);
|
|
92
95
|
}
|
|
96
|
+
hasCodeActionProvider(type) {
|
|
97
|
+
return this.#codeActionProviders.has(type);
|
|
98
|
+
}
|
|
99
|
+
getCodeActionProvider(type) {
|
|
100
|
+
return this.#codeActionProviders.get(type) ?? codeActions.fallback;
|
|
101
|
+
}
|
|
102
|
+
registerCodeActionProvider(type, codeActionProvider) {
|
|
103
|
+
this.#codeActionProviders.set(type, codeActionProvider);
|
|
104
|
+
}
|
|
93
105
|
hasColorizer(type) {
|
|
94
106
|
return this.#colorizers.has(type);
|
|
95
107
|
}
|
|
@@ -198,6 +210,15 @@ export class MetaRegistry {
|
|
|
198
210
|
get uriBinders() {
|
|
199
211
|
return this.#uriBinders;
|
|
200
212
|
}
|
|
213
|
+
hasUriBuilder(category) {
|
|
214
|
+
return this.#uriBuilders.has(category);
|
|
215
|
+
}
|
|
216
|
+
getUriBuilder(category) {
|
|
217
|
+
return this.#uriBuilders.get(category);
|
|
218
|
+
}
|
|
219
|
+
registerUriBuilder(category, builder) {
|
|
220
|
+
this.#uriBuilders.set(category, builder);
|
|
221
|
+
}
|
|
201
222
|
setUriSorter(uriSorter) {
|
|
202
223
|
const nextSorter = this.#uriSorter;
|
|
203
224
|
this.#uriSorter = (a, b) => uriSorter(a, b, nextSorter);
|
package/lib/service/Project.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { Ignore } from 'ignore';
|
|
2
1
|
import type { TextDocumentContentChangeEvent } from 'vscode-languageserver-textdocument';
|
|
3
2
|
import { TextDocument } from 'vscode-languageserver-textdocument';
|
|
4
3
|
import type { ExternalEventEmitter, Externals } from '../common/index.js';
|
|
@@ -98,11 +97,9 @@ export type ProjectData = Pick<Project, 'cacheRoot' | 'config' | 'downloader' |
|
|
|
98
97
|
export declare class Project implements ExternalEventEmitter {
|
|
99
98
|
#private;
|
|
100
99
|
private static readonly RootSuffix;
|
|
101
|
-
private static readonly GitIgnore;
|
|
102
100
|
readonly cacheService: CacheService;
|
|
103
101
|
get isReady(): boolean;
|
|
104
102
|
config: Config;
|
|
105
|
-
ignore: Ignore;
|
|
106
103
|
readonly downloader: Downloader;
|
|
107
104
|
readonly externals: Externals;
|
|
108
105
|
readonly fs: FileService;
|
|
@@ -161,7 +158,6 @@ export declare class Project implements ExternalEventEmitter {
|
|
|
161
158
|
getTrackedFiles(): string[];
|
|
162
159
|
constructor({ cacheRoot, defaultConfig, downloader, externals, fs, initializers, isDebugging, logger, profilers, projectRoots, }: ProjectOptions);
|
|
163
160
|
private setInitPromise;
|
|
164
|
-
private readGitignore;
|
|
165
161
|
private setReadyPromise;
|
|
166
162
|
/**
|
|
167
163
|
* Load the config file and initialize parsers and processors.
|
|
@@ -203,6 +199,7 @@ export declare class Project implements ExternalEventEmitter {
|
|
|
203
199
|
ensureClientManagedChecked(uri: string): Promise<DocAndNode | undefined>;
|
|
204
200
|
getClientManaged(uri: string): DocAndNode | undefined;
|
|
205
201
|
showCacheRoot(): Promise<void>;
|
|
202
|
+
shouldExclude(uri: string): boolean;
|
|
206
203
|
private tryClearingCache;
|
|
207
204
|
private shouldRemove;
|
|
208
205
|
private isOnlyWatched;
|
package/lib/service/Project.js
CHANGED
|
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import
|
|
7
|
+
import picomatch from 'picomatch';
|
|
8
8
|
import { TextDocument } from 'vscode-languageserver-textdocument';
|
|
9
9
|
import { bufferToString, Logger, normalizeUri, SingletonPromise, StateProxy, } from '../common/index.js';
|
|
10
10
|
import { FileNode } from '../node/index.js';
|
|
@@ -63,7 +63,6 @@ const CacheAutoSaveInterval = 600_000; // 10 Minutes.
|
|
|
63
63
|
*/
|
|
64
64
|
export class Project {
|
|
65
65
|
static RootSuffix = '/pack.mcmeta';
|
|
66
|
-
static GitIgnore = '.gitignore';
|
|
67
66
|
/** Prevent circular binding. */
|
|
68
67
|
#bindingInProgressUris = new Set();
|
|
69
68
|
#cacheSaverIntervalId;
|
|
@@ -85,7 +84,6 @@ export class Project {
|
|
|
85
84
|
return this.#isReady;
|
|
86
85
|
}
|
|
87
86
|
config;
|
|
88
|
-
ignore = ignore();
|
|
89
87
|
downloader;
|
|
90
88
|
externals;
|
|
91
89
|
fs;
|
|
@@ -159,9 +157,7 @@ export class Project {
|
|
|
159
157
|
const supportedFiles = [...this.#dependencyFiles ?? [], ...this.#watchedFiles]
|
|
160
158
|
.filter((file) => extensions.includes(fileUtil.extname(file) ?? ''));
|
|
161
159
|
this.logger.info(`[Project#getTrackedFiles] Listed ${supportedFiles.length} supported files`);
|
|
162
|
-
|
|
163
|
-
this.logger.info(`[Project#getTrackedFiles] After ignoring, keeping ${filteredFiles.length} tracked files`);
|
|
164
|
-
return filteredFiles;
|
|
160
|
+
return supportedFiles;
|
|
165
161
|
}
|
|
166
162
|
constructor({ cacheRoot, defaultConfig, downloader, externals, fs = FileService.create(externals, cacheRoot), initializers = [], isDebugging = false, logger = Logger.create(), profilers = ProfilerFactory.noop(), projectRoots, }) {
|
|
167
163
|
this.#cacheRoot = cacheRoot;
|
|
@@ -227,21 +223,6 @@ export class Project {
|
|
|
227
223
|
});
|
|
228
224
|
}
|
|
229
225
|
setInitPromise() {
|
|
230
|
-
const loadConfig = async () => {
|
|
231
|
-
this.config = await this.#configService.load();
|
|
232
|
-
this.ignore = ignore();
|
|
233
|
-
for (const pattern of this.config.env.exclude) {
|
|
234
|
-
if (pattern === '@gitignore') {
|
|
235
|
-
const gitignore = await this.readGitignore();
|
|
236
|
-
if (gitignore) {
|
|
237
|
-
this.ignore.add(gitignore);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
else {
|
|
241
|
-
this.ignore.add(pattern);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
};
|
|
245
226
|
const callIntializers = async () => {
|
|
246
227
|
const initCtx = {
|
|
247
228
|
cacheRoot: this.cacheRoot,
|
|
@@ -271,29 +252,13 @@ export class Project {
|
|
|
271
252
|
this.symbols = new SymbolUtil(symbols, this.externals.event.EventEmitter);
|
|
272
253
|
this.symbols.buildCache();
|
|
273
254
|
__profiler.task('Load Cache');
|
|
274
|
-
await
|
|
255
|
+
this.config = await this.#configService.load();
|
|
275
256
|
__profiler.task('Load Config');
|
|
276
257
|
await callIntializers();
|
|
277
258
|
__profiler.task('Initialize').finalize();
|
|
278
259
|
};
|
|
279
260
|
this.#initPromise = init();
|
|
280
261
|
}
|
|
281
|
-
async readGitignore() {
|
|
282
|
-
if (this.projectRoots.length === 0) {
|
|
283
|
-
return undefined;
|
|
284
|
-
}
|
|
285
|
-
try {
|
|
286
|
-
const uri = this.projectRoots[0] + Project.GitIgnore;
|
|
287
|
-
const contents = await this.externals.fs.readFile(uri);
|
|
288
|
-
return bufferToString(contents);
|
|
289
|
-
}
|
|
290
|
-
catch (e) {
|
|
291
|
-
if (!this.externals.error.isKind(e, 'ENOENT')) {
|
|
292
|
-
this.logger.error(`[Project] [readGitignore]`, e);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
return undefined;
|
|
296
|
-
}
|
|
297
262
|
setReadyPromise() {
|
|
298
263
|
const getDependencies = async () => {
|
|
299
264
|
const ans = [];
|
|
@@ -339,15 +304,24 @@ export class Project {
|
|
|
339
304
|
this.#watcherReady = true;
|
|
340
305
|
resolve();
|
|
341
306
|
}).on('add', (uri) => {
|
|
307
|
+
if (this.shouldExclude(uri)) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
342
310
|
this.#watchedFiles.add(uri);
|
|
343
311
|
if (this.#watcherReady) {
|
|
344
312
|
this.emit('fileCreated', { uri });
|
|
345
313
|
}
|
|
346
314
|
}).on('change', (uri) => {
|
|
315
|
+
if (this.shouldExclude(uri)) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
347
318
|
if (this.#watcherReady) {
|
|
348
319
|
this.emit('fileModified', { uri });
|
|
349
320
|
}
|
|
350
321
|
}).on('unlink', (uri) => {
|
|
322
|
+
if (this.shouldExclude(uri)) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
351
325
|
this.#watchedFiles.delete(uri);
|
|
352
326
|
if (this.#watcherReady) {
|
|
353
327
|
this.emit('fileDeleted', { uri });
|
|
@@ -671,6 +645,9 @@ export class Project {
|
|
|
671
645
|
if (!fileUtil.isFileUri(uri)) {
|
|
672
646
|
return; // We only accept `file:` scheme for client-managed URIs.
|
|
673
647
|
}
|
|
648
|
+
if (this.shouldExclude(uri)) {
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
674
651
|
const doc = TextDocument.create(uri, languageID, version, content);
|
|
675
652
|
const node = this.parse(doc);
|
|
676
653
|
this.#clientManagedUris.add(uri);
|
|
@@ -690,6 +667,9 @@ export class Project {
|
|
|
690
667
|
if (!fileUtil.isFileUri(uri)) {
|
|
691
668
|
return; // We only accept `file:` scheme for client-managed URIs.
|
|
692
669
|
}
|
|
670
|
+
if (this.shouldExclude(uri)) {
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
693
673
|
const doc = this.#clientManagedDocAndNodes.get(uri)?.doc;
|
|
694
674
|
if (!doc) {
|
|
695
675
|
throw new Error(`TextDocument for ${uri} is not cached. This should not happen. Did the language client send a didChange notification without sending a didOpen one, or is there a logic error on our side resulting the 'read' function overriding the 'TextDocument' created in the 'didOpen' notification handler?`);
|
|
@@ -744,6 +724,17 @@ export class Project {
|
|
|
744
724
|
this.logger.error('[Service#showCacheRoot]', e);
|
|
745
725
|
}
|
|
746
726
|
}
|
|
727
|
+
shouldExclude(uri) {
|
|
728
|
+
if (this.config.env.exclude.length === 0) {
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
for (const rel of fileUtil.getRels(uri, this.projectRoots)) {
|
|
732
|
+
if (picomatch(this.config.env.exclude, { dot: true, posixSlashes: false })(rel)) {
|
|
733
|
+
return true;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
return false;
|
|
737
|
+
}
|
|
747
738
|
tryClearingCache(uri) {
|
|
748
739
|
if (this.shouldRemove(uri)) {
|
|
749
740
|
this.removeCachedTextDocument(uri);
|
package/lib/service/Service.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { TextDocument } from 'vscode-languageserver-textdocument';
|
|
|
2
2
|
import type { Logger } from '../common/index.js';
|
|
3
3
|
import type { FileNode } from '../node/index.js';
|
|
4
4
|
import { AstNode } from '../node/index.js';
|
|
5
|
-
import type { Color, ColorInfo, ColorToken, InlayHint, SignatureHelp } from '../processor/index.js';
|
|
5
|
+
import type { CodeAction, Color, ColorInfo, ColorToken, InlayHint, SignatureHelp } from '../processor/index.js';
|
|
6
6
|
import { ColorPresentation } from '../processor/index.js';
|
|
7
7
|
import { Range } from '../source/index.js';
|
|
8
8
|
import type { SymbolUsageType } from '../symbol/index.js';
|
|
@@ -25,6 +25,7 @@ export declare class Service {
|
|
|
25
25
|
constructor({ isDebugging, logger, profilers, project }: Options);
|
|
26
26
|
private debug;
|
|
27
27
|
colorize(node: FileNode<AstNode>, doc: TextDocument, range?: Range): readonly ColorToken[];
|
|
28
|
+
getCodeActions(node: FileNode<AstNode>, doc: TextDocument, range: Range): readonly CodeAction[];
|
|
28
29
|
getColorInfo(node: FileNode<AstNode>, doc: TextDocument): ColorInfo[];
|
|
29
30
|
getColorPresentation(file: FileNode<AstNode>, doc: TextDocument, range: Range, color: Color): ColorPresentation[];
|
|
30
31
|
complete(node: FileNode<AstNode>, doc: TextDocument, offset: number, triggerCharacter?: string): import("../processor/index.js").CompletionItem[];
|
package/lib/service/Service.js
CHANGED
|
@@ -2,7 +2,7 @@ import { AstNode } from '../node/index.js';
|
|
|
2
2
|
import { ColorPresentation, completer, traversePreOrder } from '../processor/index.js';
|
|
3
3
|
import { Range } from '../source/index.js';
|
|
4
4
|
import { SymbolUsageTypes } from '../symbol/index.js';
|
|
5
|
-
import { ColorizerContext, CompleterContext, FormatterContext, ProcessorContext, SignatureHelpProviderContext, } from './Context.js';
|
|
5
|
+
import { CodeActionProviderContext, ColorizerContext, CompleterContext, FormatterContext, ProcessorContext, SignatureHelpProviderContext, } from './Context.js';
|
|
6
6
|
import { fileUtil } from './fileUtil.js';
|
|
7
7
|
import { Hover } from './Hover.js';
|
|
8
8
|
import { ProfilerFactory } from './Profiler.js';
|
|
@@ -36,6 +36,18 @@ export class Service {
|
|
|
36
36
|
}
|
|
37
37
|
return [];
|
|
38
38
|
}
|
|
39
|
+
getCodeActions(node, doc, range) {
|
|
40
|
+
try {
|
|
41
|
+
this.debug(`Getting code actions ${doc.uri} # ${doc.version} @ ${Range.toString(range)}`);
|
|
42
|
+
const codeActionProvider = this.project.meta.getCodeActionProvider(node.type);
|
|
43
|
+
const ctx = CodeActionProviderContext.create(this.project, { doc, range });
|
|
44
|
+
return codeActionProvider(node, ctx);
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
this.logger.error(`[Service] [getCodeActions] Failed for ${doc.uri} # ${doc.version}`, e);
|
|
48
|
+
}
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
39
51
|
getColorInfo(node, doc) {
|
|
40
52
|
try {
|
|
41
53
|
this.debug(`Getting color info for ${doc.uri} # ${doc.version}`);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TextDocument } from 'vscode-languageserver-textdocument';
|
|
2
|
+
import type { RootUriString } from '../service/index.js';
|
|
3
|
+
import type { Config } from './Config.js';
|
|
4
|
+
export type UriBuilder = (identifier: string, ctx: UriBuilderContext) => string | undefined;
|
|
5
|
+
export interface UriBuilderContext {
|
|
6
|
+
doc: TextDocument;
|
|
7
|
+
project: Record<string, string>;
|
|
8
|
+
roots: readonly RootUriString[];
|
|
9
|
+
config: Config;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=UriBuilder.d.ts.map
|
|
@@ -31,6 +31,8 @@ export declare namespace fileUtil {
|
|
|
31
31
|
* getRel(['file:///root/foo/', 'file:///root/'], 'file:///outsider.json') // -> undefined
|
|
32
32
|
*/
|
|
33
33
|
function getRel(uri: string, rootUris: readonly RootUriString[]): string | undefined;
|
|
34
|
+
function getRoots(uri: string, rootUris: readonly RootUriString[]): Generator<RootUriString, undefined, unknown>;
|
|
35
|
+
function getRoot(uri: string, rootUris: readonly RootUriString[]): string | undefined;
|
|
34
36
|
function isRootUri(uri: string): uri is RootUriString;
|
|
35
37
|
function ensureEndingSlash(uri: string): RootUriString;
|
|
36
38
|
function join(fromUri: string, toUri: string): string;
|
package/lib/service/fileUtil.js
CHANGED
|
@@ -60,6 +60,20 @@ export var fileUtil;
|
|
|
60
60
|
return getRels(uri, rootUris).next().value;
|
|
61
61
|
}
|
|
62
62
|
fileUtil.getRel = getRel;
|
|
63
|
+
function* getRoots(uri, rootUris) {
|
|
64
|
+
for (const root of rootUris) {
|
|
65
|
+
const rel = getRelativeUriFromBase(uri, root);
|
|
66
|
+
if (rel !== undefined) {
|
|
67
|
+
yield root;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
fileUtil.getRoots = getRoots;
|
|
73
|
+
function getRoot(uri, rootUris) {
|
|
74
|
+
return getRoots(uri, rootUris).next().value;
|
|
75
|
+
}
|
|
76
|
+
fileUtil.getRoot = getRoot;
|
|
63
77
|
function isRootUri(uri) {
|
|
64
78
|
return uri.endsWith('/');
|
|
65
79
|
}
|
package/lib/service/index.d.ts
CHANGED
package/lib/service/index.js
CHANGED
|
@@ -31,7 +31,7 @@ export declare const enum ErrorSeverity {
|
|
|
31
31
|
Error = 3
|
|
32
32
|
}
|
|
33
33
|
export interface LanguageErrorInfo {
|
|
34
|
-
codeAction?:
|
|
34
|
+
codeAction?: LanguageErrorAction;
|
|
35
35
|
deprecated?: boolean;
|
|
36
36
|
unnecessary?: boolean;
|
|
37
37
|
related?: {
|
|
@@ -39,4 +39,17 @@ export interface LanguageErrorInfo {
|
|
|
39
39
|
message: string;
|
|
40
40
|
}[];
|
|
41
41
|
}
|
|
42
|
+
export interface LanguageErrorAction {
|
|
43
|
+
title: string;
|
|
44
|
+
isPreferred?: boolean;
|
|
45
|
+
changes?: CodeActionChange[];
|
|
46
|
+
}
|
|
47
|
+
export type CodeActionChange = {
|
|
48
|
+
type: 'edit';
|
|
49
|
+
range: Range;
|
|
50
|
+
text: string;
|
|
51
|
+
} | {
|
|
52
|
+
type: 'create';
|
|
53
|
+
uri: string;
|
|
54
|
+
};
|
|
42
55
|
//# sourceMappingURL=LanguageError.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spyglassmc/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.21",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -19,16 +19,17 @@
|
|
|
19
19
|
"chokidar": "^3.5.2",
|
|
20
20
|
"decompress": "^4.2.1",
|
|
21
21
|
"follow-redirects": "^1.14.8",
|
|
22
|
-
"
|
|
22
|
+
"picomatch": "^4.0.2",
|
|
23
23
|
"pako": "^2.0.4",
|
|
24
24
|
"rfdc": "^1.3.0",
|
|
25
25
|
"vscode-languageserver-textdocument": "^1.0.4",
|
|
26
26
|
"whatwg-url": "^14.0.0",
|
|
27
|
-
"@spyglassmc/locales": "0.3.
|
|
27
|
+
"@spyglassmc/locales": "0.3.12"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/decompress": "^4.2.3",
|
|
31
31
|
"@types/follow-redirects": "^1.14.1",
|
|
32
|
+
"@types/picomatch": "^3.0.1",
|
|
32
33
|
"@types/pako": "^2.0.0",
|
|
33
34
|
"@types/whatwg-url": "^11.0.4"
|
|
34
35
|
},
|