@spyglassmc/core 0.4.5 → 0.4.7
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/common/Dev.js +2 -5
- package/lib/common/ReadonlyProxy.js +1 -3
- package/lib/common/StateProxy.js +2 -8
- package/lib/common/externals/BrowserExternals.js +7 -12
- package/lib/common/externals/NodeJsExternals.js +12 -8
- package/lib/common/externals/index.d.ts +8 -1
- package/lib/common/util.js +3 -10
- package/lib/node/AstNode.js +7 -9
- package/lib/node/BooleanNode.js +1 -4
- package/lib/node/FloatNode.js +1 -5
- package/lib/node/IntegerNode.js +1 -5
- package/lib/node/ListNode.d.ts +1 -1
- package/lib/node/LiteralNode.js +1 -6
- package/lib/node/LongNode.js +1 -5
- package/lib/node/PrefixedNode.d.ts +13 -0
- package/lib/node/PrefixedNode.js +22 -0
- package/lib/node/ResourceLocationNode.d.ts +4 -1
- package/lib/node/ResourceLocationNode.js +2 -8
- package/lib/node/StringNode.d.ts +2 -1
- package/lib/node/StringNode.js +1 -4
- package/lib/node/SymbolNode.js +1 -6
- package/lib/node/index.d.ts +1 -0
- package/lib/node/index.js +1 -0
- package/lib/parser/comment.d.ts +1 -1
- package/lib/parser/comment.js +2 -6
- package/lib/parser/error.js +1 -4
- package/lib/parser/file.d.ts +2 -2
- package/lib/parser/file.js +1 -2
- package/lib/parser/float.js +2 -7
- package/lib/parser/index.d.ts +1 -0
- package/lib/parser/index.js +1 -0
- package/lib/parser/integer.js +3 -7
- package/lib/parser/list.d.ts +1 -1
- package/lib/parser/list.js +2 -6
- package/lib/parser/literal.js +2 -9
- package/lib/parser/long.js +2 -7
- package/lib/parser/prefixed.d.ts +8 -0
- package/lib/parser/prefixed.js +23 -0
- package/lib/parser/record.d.ts +1 -1
- package/lib/parser/record.js +11 -17
- package/lib/parser/resourceLocation.d.ts +1 -0
- package/lib/parser/resourceLocation.js +9 -4
- package/lib/parser/string.js +6 -9
- package/lib/parser/util.d.ts +14 -3
- package/lib/parser/util.js +48 -26
- package/lib/processor/ColorInfoProvider.js +8 -25
- package/lib/processor/binder/builtin.d.ts +1 -0
- package/lib/processor/binder/builtin.js +20 -30
- package/lib/processor/checker/builtin.d.ts +1 -0
- package/lib/processor/checker/builtin.js +13 -7
- package/lib/processor/colorizer/Colorizer.d.ts +1 -1
- package/lib/processor/colorizer/Colorizer.js +4 -7
- package/lib/processor/colorizer/builtin.js +2 -2
- package/lib/processor/completer/Completer.js +1 -3
- package/lib/processor/completer/builtin.d.ts +3 -2
- package/lib/processor/completer/builtin.js +70 -24
- package/lib/processor/formatter/Formatter.js +1 -3
- package/lib/processor/formatter/builtin.js +2 -4
- package/lib/processor/linter/builtin/undeclaredSymbol.js +27 -40
- package/lib/service/CacheService.d.ts +1 -1
- package/lib/service/CacheService.js +12 -13
- package/lib/service/Config.d.ts +36 -0
- package/lib/service/Config.js +28 -45
- package/lib/service/Context.d.ts +2 -0
- package/lib/service/Context.js +4 -12
- package/lib/service/Downloader.d.ts +3 -0
- package/lib/service/Downloader.js +9 -3
- package/lib/service/ErrorReporter.js +3 -0
- package/lib/service/FileService.js +10 -29
- package/lib/service/Hover.js +1 -4
- package/lib/service/MetaRegistry.d.ts +4 -2
- package/lib/service/MetaRegistry.js +16 -8
- package/lib/service/Project.d.ts +9 -3
- package/lib/service/Project.js +73 -49
- package/lib/service/Service.d.ts +1 -1
- package/lib/service/Service.js +11 -36
- package/lib/service/SymbolLocations.js +1 -4
- package/lib/service/fileUtil.d.ts +7 -0
- package/lib/service/fileUtil.js +29 -4
- package/lib/source/IndexMap.js +1 -4
- package/lib/source/Location.js +3 -9
- package/lib/source/Position.js +1 -2
- package/lib/source/PositionRange.js +2 -2
- package/lib/source/Range.js +9 -21
- package/lib/source/Source.d.ts +11 -1
- package/lib/source/Source.js +31 -5
- package/lib/symbol/Symbol.d.ts +14 -9
- package/lib/symbol/Symbol.js +80 -61
- package/lib/symbol/SymbolUtil.d.ts +3 -3
- package/lib/symbol/SymbolUtil.js +48 -72
- package/package.json +3 -2
|
@@ -13,9 +13,12 @@ export class Downloader {
|
|
|
13
13
|
async download(job, out = {}) {
|
|
14
14
|
const { id, cache, uri, options, transformer, ttl } = job;
|
|
15
15
|
if (ttl && this.#memoryCache.has(uri)) {
|
|
16
|
-
const
|
|
17
|
-
|
|
16
|
+
const memoryCacheEntry = this.#memoryCache.get(uri);
|
|
17
|
+
const { buffer, time, cacheUri, checksum } = memoryCacheEntry;
|
|
18
|
+
if (performance.now() <= time + ttl) {
|
|
18
19
|
this.logger.info(`[Downloader] [${id}] Skipped thanks to valid cache in memory`);
|
|
20
|
+
out.cacheUri = cacheUri;
|
|
21
|
+
out.checksum = checksum;
|
|
19
22
|
return await transformer(buffer);
|
|
20
23
|
}
|
|
21
24
|
else {
|
|
@@ -28,7 +31,8 @@ export class Downloader {
|
|
|
28
31
|
if (cache) {
|
|
29
32
|
const { checksumJob, checksumExtension } = cache;
|
|
30
33
|
out.cacheUri = cacheUri = new Uri(`downloader/${id}`, this.cacheRoot).toString();
|
|
31
|
-
cacheChecksumUri = new Uri(`downloader/${id}${checksumExtension}`, this.cacheRoot)
|
|
34
|
+
cacheChecksumUri = new Uri(`downloader/${id}${checksumExtension}`, this.cacheRoot)
|
|
35
|
+
.toString();
|
|
32
36
|
try {
|
|
33
37
|
out.checksum = checksum = await this.download({
|
|
34
38
|
...checksumJob,
|
|
@@ -42,6 +46,8 @@ export class Downloader {
|
|
|
42
46
|
if (ttl) {
|
|
43
47
|
this.#memoryCache.set(uri, {
|
|
44
48
|
buffer: cachedBuffer,
|
|
49
|
+
cacheUri,
|
|
50
|
+
checksum,
|
|
45
51
|
time: performance.now(),
|
|
46
52
|
});
|
|
47
53
|
}
|
|
@@ -7,6 +7,9 @@ export class ErrorReporter {
|
|
|
7
7
|
* Reports a new error.
|
|
8
8
|
*/
|
|
9
9
|
report(message, range, severity = 3 /* ErrorSeverity.Error */, info) {
|
|
10
|
+
if (message.trim() === '') {
|
|
11
|
+
throw new Error('Tried to report an error with no message');
|
|
12
|
+
}
|
|
10
13
|
this.errors.push(LanguageError.create(message, Range.get(range), severity, info));
|
|
11
14
|
}
|
|
12
15
|
/**
|
|
@@ -77,8 +77,7 @@ export class FileServiceImpl {
|
|
|
77
77
|
try {
|
|
78
78
|
let mappedUri = this.map.getKey(virtualUri);
|
|
79
79
|
if (mappedUri === undefined) {
|
|
80
|
-
mappedUri = `${this.virtualUrisRoot}${await this.externals.crypto
|
|
81
|
-
.getSha1(virtualUri)}/${fileUtil.basename(virtualUri)}`;
|
|
80
|
+
mappedUri = `${this.virtualUrisRoot}${await this.externals.crypto.getSha1(virtualUri)}/${fileUtil.basename(virtualUri)}`;
|
|
82
81
|
// Delete old mapped file if it exists. This makes sure the
|
|
83
82
|
// readonly permission on the file is not removed by it being
|
|
84
83
|
// overwritten.
|
|
@@ -140,8 +139,7 @@ export class FileUriSupporter {
|
|
|
140
139
|
const files = new Map();
|
|
141
140
|
for (let { uri } of dependencies) {
|
|
142
141
|
try {
|
|
143
|
-
if (fileUtil.isFileUri(uri) &&
|
|
144
|
-
(await externals.fs.stat(uri)).isDirectory()) {
|
|
142
|
+
if (fileUtil.isFileUri(uri) && (await externals.fs.stat(uri)).isDirectory()) {
|
|
145
143
|
uri = fileUtil.ensureEndingSlash(uri);
|
|
146
144
|
roots.push(uri);
|
|
147
145
|
files.set(uri, await externals.fs.getAllFiles(uri));
|
|
@@ -154,21 +152,11 @@ export class FileUriSupporter {
|
|
|
154
152
|
return new FileUriSupporter(externals, roots, files);
|
|
155
153
|
}
|
|
156
154
|
}
|
|
157
|
-
// namespace ArchiveUri {
|
|
158
|
-
// export function is(uri: Uri): boolean {
|
|
159
|
-
// return uri.protocol === Protocol && uri.hostname === Hostname
|
|
160
|
-
// }
|
|
161
|
-
// }
|
|
162
155
|
export class ArchiveUriSupporter {
|
|
163
156
|
externals;
|
|
164
157
|
entries;
|
|
165
158
|
static Protocol = 'archive:';
|
|
166
|
-
static SupportedArchiveExtnames = [
|
|
167
|
-
'.tar',
|
|
168
|
-
'.tar.bz2',
|
|
169
|
-
'.tar.gz',
|
|
170
|
-
'.zip',
|
|
171
|
-
];
|
|
159
|
+
static SupportedArchiveExtnames = ['.tar', '.tar.bz2', '.tar.gz', '.zip'];
|
|
172
160
|
protocol = ArchiveUriSupporter.Protocol;
|
|
173
161
|
/**
|
|
174
162
|
* @param entries A map from archive names to unzipped entries.
|
|
@@ -202,10 +190,10 @@ export class ArchiveUriSupporter {
|
|
|
202
190
|
}
|
|
203
191
|
const entry = entries.get(pathInArchive);
|
|
204
192
|
if (!entry) {
|
|
205
|
-
throw
|
|
193
|
+
throw this.externals.error.createKind('ENOENT', `Path “${pathInArchive}” does not exist in archive “${archiveName}”`);
|
|
206
194
|
}
|
|
207
195
|
if (entry.type !== 'file') {
|
|
208
|
-
throw
|
|
196
|
+
throw this.externals.error.createKind('EISDIR', `Path “${pathInArchive}” in archive “${archiveName}” is not a file`);
|
|
209
197
|
}
|
|
210
198
|
return entry.data;
|
|
211
199
|
}
|
|
@@ -235,27 +223,20 @@ export class ArchiveUriSupporter {
|
|
|
235
223
|
if (!path) {
|
|
236
224
|
throw new Error(`Missing path in archive uri “${uri.toString()}”`);
|
|
237
225
|
}
|
|
238
|
-
return {
|
|
239
|
-
archiveName: uri.host,
|
|
240
|
-
pathInArchive: path.charAt(0) === '/' ? path.slice(1) : path,
|
|
241
|
-
};
|
|
226
|
+
return { archiveName: uri.host, pathInArchive: path.charAt(0) === '/' ? path.slice(1) : path };
|
|
242
227
|
}
|
|
243
228
|
static async create(dependencies, externals, logger) {
|
|
244
229
|
const entries = new Map();
|
|
245
230
|
for (const { uri, info } of dependencies) {
|
|
246
231
|
try {
|
|
247
|
-
if (uri.startsWith('file:')
|
|
248
|
-
ArchiveUriSupporter.SupportedArchiveExtnames.some((ext) => uri.endsWith(ext))
|
|
249
|
-
(await externals.fs.stat(uri)).isFile()) {
|
|
232
|
+
if (uri.startsWith('file:')
|
|
233
|
+
&& ArchiveUriSupporter.SupportedArchiveExtnames.some((ext) => uri.endsWith(ext))
|
|
234
|
+
&& (await externals.fs.stat(uri)).isFile()) {
|
|
250
235
|
const archiveName = fileUtil.basename(uri);
|
|
251
236
|
if (entries.has(archiveName)) {
|
|
252
237
|
throw new Error(`A different URI with ${archiveName} already exists`);
|
|
253
238
|
}
|
|
254
|
-
const files = await externals.archive.decompressBall(await externals.fs.readFile(uri), {
|
|
255
|
-
stripLevel: typeof info?.startDepth === 'number'
|
|
256
|
-
? info.startDepth
|
|
257
|
-
: 0,
|
|
258
|
-
});
|
|
239
|
+
const files = await externals.archive.decompressBall(await externals.fs.readFile(uri), { stripLevel: typeof info?.startDepth === 'number' ? info.startDepth : 0 });
|
|
259
240
|
entries.set(archiveName, new Map(files.map((f) => [f.path.replace(/\\/g, '/'), f])));
|
|
260
241
|
}
|
|
261
242
|
}
|
package/lib/service/Hover.js
CHANGED
|
@@ -3,10 +3,7 @@ export var Hover;
|
|
|
3
3
|
(function (Hover) {
|
|
4
4
|
/* istanbul ignore next */
|
|
5
5
|
function create(range, markdown) {
|
|
6
|
-
return {
|
|
7
|
-
range: Range.get(range),
|
|
8
|
-
markdown,
|
|
9
|
-
};
|
|
6
|
+
return { range: Range.get(range), markdown };
|
|
10
7
|
}
|
|
11
8
|
Hover.create = create;
|
|
12
9
|
})(Hover || (Hover = {}));
|
|
@@ -16,7 +16,7 @@ export interface LanguageOptions {
|
|
|
16
16
|
*/
|
|
17
17
|
extensions: FileExtension[];
|
|
18
18
|
triggerCharacters?: string[];
|
|
19
|
-
parser
|
|
19
|
+
parser?: Parser<AstNode>;
|
|
20
20
|
completer?: Completer<any>;
|
|
21
21
|
}
|
|
22
22
|
interface LinterRegistration {
|
|
@@ -102,11 +102,13 @@ export declare class MetaRegistry {
|
|
|
102
102
|
* @returns The corresponding `Parser` for the language ID.
|
|
103
103
|
* @throws If there's no such language in the registry.
|
|
104
104
|
*/
|
|
105
|
-
getParserForLanguageId<N extends AstNode>(languageID: string): Parser<N
|
|
105
|
+
getParserForLanguageId<N extends AstNode>(languageID: string): Parser<N> | undefined;
|
|
106
106
|
registerSignatureHelpProvider(provider: SignatureHelpProvider<any>): void;
|
|
107
107
|
get signatureHelpProviders(): Set<SignatureHelpProvider<any>>;
|
|
108
108
|
registerSymbolRegistrar(id: string, registrar: SymbolRegistrarRegistration): void;
|
|
109
109
|
get symbolRegistrars(): Map<string, SymbolRegistrarRegistration>;
|
|
110
|
+
registerCustom<T>(group: string, id: string, object: T): void;
|
|
111
|
+
getCustom<T>(group: string): Map<string, T> | undefined;
|
|
110
112
|
registerUriBinder(uriBinder: UriBinder): void;
|
|
111
113
|
get uriBinders(): Set<UriBinder>;
|
|
112
114
|
setUriSorter(uriSorter: UriSorterRegistration): void;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Lazy } from '../common/index.js';
|
|
2
|
-
import { binder, checker, colorizer, completer, formatter, linter
|
|
2
|
+
import { binder, checker, 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.
|
|
@@ -20,6 +20,7 @@ export class MetaRegistry {
|
|
|
20
20
|
#parsers = new Map();
|
|
21
21
|
#signatureHelpProviders = new Set();
|
|
22
22
|
#symbolRegistrars = new Map();
|
|
23
|
+
#custom = new Map();
|
|
23
24
|
#uriBinders = new Set();
|
|
24
25
|
#uriSorter = () => 0;
|
|
25
26
|
constructor() {
|
|
@@ -109,8 +110,7 @@ export class MetaRegistry {
|
|
|
109
110
|
}
|
|
110
111
|
shouldComplete(languageID, triggerCharacter) {
|
|
111
112
|
const language = this.#languages.get(languageID);
|
|
112
|
-
return (!triggerCharacter ||
|
|
113
|
-
!!language?.triggerCharacters?.includes(triggerCharacter));
|
|
113
|
+
return (!triggerCharacter || !!language?.triggerCharacters?.includes(triggerCharacter));
|
|
114
114
|
}
|
|
115
115
|
getCompleterForLanguageID(languageID) {
|
|
116
116
|
return this.#languages.get(languageID)?.completer ?? completer.fallback;
|
|
@@ -137,11 +137,8 @@ export class MetaRegistry {
|
|
|
137
137
|
return this.#inlayHintProviders;
|
|
138
138
|
}
|
|
139
139
|
getLinter(ruleName) {
|
|
140
|
-
return (this.#linters.get(ruleName)
|
|
141
|
-
configValidator: () => false,
|
|
142
|
-
linter: linter.noop,
|
|
143
|
-
nodePredicate: () => false,
|
|
144
|
-
});
|
|
140
|
+
return (this.#linters.get(ruleName)
|
|
141
|
+
?? { configValidator: () => false, linter: linter.noop, nodePredicate: () => false });
|
|
145
142
|
}
|
|
146
143
|
registerLinter(ruleName, options) {
|
|
147
144
|
this.#linters.set(ruleName, options);
|
|
@@ -184,6 +181,17 @@ export class MetaRegistry {
|
|
|
184
181
|
get symbolRegistrars() {
|
|
185
182
|
return this.#symbolRegistrars;
|
|
186
183
|
}
|
|
184
|
+
registerCustom(group, id, object) {
|
|
185
|
+
let groupRegistry = this.#custom.get(group);
|
|
186
|
+
if (!groupRegistry) {
|
|
187
|
+
groupRegistry = new Map();
|
|
188
|
+
this.#custom.set(group, groupRegistry);
|
|
189
|
+
}
|
|
190
|
+
groupRegistry.set(id, object);
|
|
191
|
+
}
|
|
192
|
+
getCustom(group) {
|
|
193
|
+
return this.#custom.get(group);
|
|
194
|
+
}
|
|
187
195
|
registerUriBinder(uriBinder) {
|
|
188
196
|
this.#uriBinders.add(uriBinder);
|
|
189
197
|
}
|
package/lib/service/Project.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Ignore } from 'ignore';
|
|
1
2
|
import type { TextDocumentContentChangeEvent } from 'vscode-languageserver-textdocument';
|
|
2
3
|
import { TextDocument } from 'vscode-languageserver-textdocument';
|
|
3
4
|
import type { ExternalEventEmitter, Externals } from '../common/index.js';
|
|
@@ -13,7 +14,7 @@ import { FileService } from './FileService.js';
|
|
|
13
14
|
import type { RootUriString } from './fileUtil.js';
|
|
14
15
|
import { MetaRegistry } from './MetaRegistry.js';
|
|
15
16
|
import { ProfilerFactory } from './Profiler.js';
|
|
16
|
-
export type ProjectInitializerContext = Pick<Project, 'cacheRoot' | 'config' | 'downloader' | 'externals' | 'logger' | 'meta' | 'projectRoot'>;
|
|
17
|
+
export type ProjectInitializerContext = Pick<Project, 'cacheRoot' | 'config' | 'downloader' | 'externals' | 'isDebugging' | 'logger' | 'meta' | 'projectRoot'>;
|
|
17
18
|
export type SyncProjectInitializer = (this: void, ctx: ProjectInitializerContext) => Record<string, string> | void;
|
|
18
19
|
export type AsyncProjectInitializer = (this: void, ctx: ProjectInitializerContext) => PromiseLike<Record<string, string> | void>;
|
|
19
20
|
export type ProjectInitializer = SyncProjectInitializer | AsyncProjectInitializer;
|
|
@@ -24,6 +25,7 @@ export interface ProjectOptions {
|
|
|
24
25
|
externals: Externals;
|
|
25
26
|
fs?: FileService;
|
|
26
27
|
initializers?: readonly ProjectInitializer[];
|
|
28
|
+
isDebugging?: boolean;
|
|
27
29
|
logger?: Logger;
|
|
28
30
|
profilers?: ProfilerFactory;
|
|
29
31
|
/**
|
|
@@ -55,7 +57,7 @@ interface SymbolRegistrarEvent {
|
|
|
55
57
|
id: string;
|
|
56
58
|
checksum: string | undefined;
|
|
57
59
|
}
|
|
58
|
-
export type ProjectData = Pick<Project, 'cacheRoot' | 'config' | 'downloader' | 'ensureBindingStarted' | 'externals' | 'fs' | 'logger' | 'meta' | 'profilers' | 'projectRoot' | 'roots' | 'symbols' | 'ctx'>;
|
|
60
|
+
export type ProjectData = Pick<Project, 'cacheRoot' | 'config' | 'downloader' | 'ensureBindingStarted' | 'externals' | 'fs' | 'isDebugging' | 'logger' | 'meta' | 'profilers' | 'projectRoot' | 'roots' | 'symbols' | 'ctx'>;
|
|
59
61
|
/**
|
|
60
62
|
* Manage all tracked documents and errors.
|
|
61
63
|
*
|
|
@@ -96,12 +98,15 @@ export type ProjectData = Pick<Project, 'cacheRoot' | 'config' | 'downloader' |
|
|
|
96
98
|
export declare class Project implements ExternalEventEmitter {
|
|
97
99
|
#private;
|
|
98
100
|
private static readonly RootSuffix;
|
|
101
|
+
private static readonly GitIgnore;
|
|
99
102
|
readonly cacheService: CacheService;
|
|
100
103
|
get isReady(): boolean;
|
|
101
104
|
config: Config;
|
|
105
|
+
ignore: Ignore;
|
|
102
106
|
readonly downloader: Downloader;
|
|
103
107
|
readonly externals: Externals;
|
|
104
108
|
readonly fs: FileService;
|
|
109
|
+
readonly isDebugging: boolean;
|
|
105
110
|
readonly logger: Logger;
|
|
106
111
|
readonly meta: MetaRegistry;
|
|
107
112
|
readonly profilers: ProfilerFactory;
|
|
@@ -154,8 +159,9 @@ export declare class Project implements ExternalEventEmitter {
|
|
|
154
159
|
* are not loaded into the memory.
|
|
155
160
|
*/
|
|
156
161
|
getTrackedFiles(): string[];
|
|
157
|
-
constructor({ cacheRoot, defaultConfig, downloader, externals, fs, initializers, logger, profilers, projectRoot, }: ProjectOptions);
|
|
162
|
+
constructor({ cacheRoot, defaultConfig, downloader, externals, fs, initializers, isDebugging, logger, profilers, projectRoot, }: ProjectOptions);
|
|
158
163
|
private setInitPromise;
|
|
164
|
+
private readGitignore;
|
|
159
165
|
private setReadyPromise;
|
|
160
166
|
/**
|
|
161
167
|
* Load the config file and initialize parsers and processors.
|
package/lib/service/Project.js
CHANGED
|
@@ -4,12 +4,13 @@ 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 ignore from 'ignore';
|
|
7
8
|
import { TextDocument } from 'vscode-languageserver-textdocument';
|
|
8
9
|
import { bufferToString, Logger, normalizeUri, SingletonPromise, StateProxy, } from '../common/index.js';
|
|
9
10
|
import { FileNode } from '../node/index.js';
|
|
10
11
|
import { file } from '../parser/index.js';
|
|
11
12
|
import { traversePreOrder } from '../processor/index.js';
|
|
12
|
-
import { LanguageError, Source } from '../source/index.js';
|
|
13
|
+
import { LanguageError, Range, Source } from '../source/index.js';
|
|
13
14
|
import { SymbolUtil } from '../symbol/index.js';
|
|
14
15
|
import { CacheService } from './CacheService.js';
|
|
15
16
|
import { ConfigService, LinterConfigValue } from './Config.js';
|
|
@@ -17,7 +18,7 @@ import { BinderContext, CheckerContext, LinterContext, ParserContext, UriBinderC
|
|
|
17
18
|
import { DependencyKey } from './Dependency.js';
|
|
18
19
|
import { Downloader } from './Downloader.js';
|
|
19
20
|
import { LinterErrorReporter } from './ErrorReporter.js';
|
|
20
|
-
import { ArchiveUriSupporter, FileService, FileUriSupporter
|
|
21
|
+
import { ArchiveUriSupporter, FileService, FileUriSupporter } from './FileService.js';
|
|
21
22
|
import { fileUtil } from './fileUtil.js';
|
|
22
23
|
import { MetaRegistry } from './MetaRegistry.js';
|
|
23
24
|
import { ProfilerFactory } from './Profiler.js';
|
|
@@ -62,6 +63,7 @@ const CacheAutoSaveInterval = 600_000; // 10 Minutes.
|
|
|
62
63
|
*/
|
|
63
64
|
export class Project {
|
|
64
65
|
static RootSuffix = '/pack.mcmeta';
|
|
66
|
+
static GitIgnore = '.gitignore';
|
|
65
67
|
/** Prevent circular binding. */
|
|
66
68
|
#bindingInProgressUris = new Set();
|
|
67
69
|
#cacheSaverIntervalId;
|
|
@@ -83,9 +85,11 @@ export class Project {
|
|
|
83
85
|
return this.#isReady;
|
|
84
86
|
}
|
|
85
87
|
config;
|
|
88
|
+
ignore = ignore();
|
|
86
89
|
downloader;
|
|
87
90
|
externals;
|
|
88
91
|
fs;
|
|
92
|
+
isDebugging;
|
|
89
93
|
logger;
|
|
90
94
|
meta = new MetaRegistry();
|
|
91
95
|
profilers;
|
|
@@ -121,12 +125,11 @@ export class Project {
|
|
|
121
125
|
return this.#cacheRoot;
|
|
122
126
|
}
|
|
123
127
|
updateRoots() {
|
|
124
|
-
const rawRoots = [...this.#dependencyRoots, this.projectRoot];
|
|
128
|
+
const rawRoots = [...this.#dependencyRoots ?? [], this.projectRoot];
|
|
125
129
|
const ans = new Set(rawRoots);
|
|
126
130
|
// Identify roots indicated by `pack.mcmeta`.
|
|
127
131
|
for (const file of this.getTrackedFiles()) {
|
|
128
|
-
if (file.endsWith(Project.RootSuffix) &&
|
|
129
|
-
rawRoots.some((r) => file.startsWith(r))) {
|
|
132
|
+
if (file.endsWith(Project.RootSuffix) && rawRoots.some((r) => file.startsWith(r))) {
|
|
130
133
|
ans.add(file.slice(0, 1 - Project.RootSuffix.length));
|
|
131
134
|
}
|
|
132
135
|
}
|
|
@@ -152,30 +155,31 @@ export class Project {
|
|
|
152
155
|
*/
|
|
153
156
|
getTrackedFiles() {
|
|
154
157
|
const extensions = this.meta.getSupportedFileExtensions();
|
|
155
|
-
|
|
158
|
+
const supportedFiles = [...this.#dependencyFiles ?? [], ...this.#watchedFiles]
|
|
159
|
+
.filter((file) => extensions.includes(fileUtil.extname(file) ?? ''));
|
|
160
|
+
const filteredFiles = this.ignore.filter(supportedFiles);
|
|
161
|
+
return filteredFiles;
|
|
156
162
|
}
|
|
157
|
-
constructor({ cacheRoot, defaultConfig, downloader, externals, fs = FileService.create(externals, cacheRoot), initializers = [], logger = Logger.create(), profilers = ProfilerFactory.noop(), projectRoot, }) {
|
|
163
|
+
constructor({ cacheRoot, defaultConfig, downloader, externals, fs = FileService.create(externals, cacheRoot), initializers = [], isDebugging = false, logger = Logger.create(), profilers = ProfilerFactory.noop(), projectRoot, }) {
|
|
158
164
|
this.#cacheRoot = cacheRoot;
|
|
159
165
|
this.#eventEmitter = new externals.event.EventEmitter();
|
|
160
166
|
this.externals = externals;
|
|
161
167
|
this.fs = fs;
|
|
162
168
|
this.#initializers = initializers;
|
|
169
|
+
this.isDebugging = isDebugging;
|
|
163
170
|
this.logger = logger;
|
|
164
171
|
this.profilers = profilers;
|
|
165
172
|
this.projectRoot = projectRoot;
|
|
166
173
|
this.cacheService = new CacheService(cacheRoot, this);
|
|
167
174
|
this.#configService = new ConfigService(this, defaultConfig);
|
|
168
|
-
this.downloader = downloader ??
|
|
169
|
-
new Downloader(cacheRoot, externals, logger);
|
|
175
|
+
this.downloader = downloader ?? new Downloader(cacheRoot, externals, logger);
|
|
170
176
|
this.symbols = new SymbolUtil({}, externals.event.EventEmitter);
|
|
171
177
|
this.#ctx = {};
|
|
172
178
|
this.logger.info(`[Project] [init] cacheRoot = “${cacheRoot}”`);
|
|
173
|
-
this.#configService
|
|
174
|
-
.on('changed', ({ config }) => {
|
|
179
|
+
this.#configService.on('changed', ({ config }) => {
|
|
175
180
|
this.config = config;
|
|
176
181
|
this.logger.info('[Project] [Config] Changed');
|
|
177
|
-
})
|
|
178
|
-
.on('error', ({ error, uri }) => this.logger.error(`[Project] [Config] Failed loading “${uri}”`, error));
|
|
182
|
+
}).on('error', ({ error, uri }) => this.logger.error(`[Project] [Config] Failed loading “${uri}”`, error));
|
|
179
183
|
this.setInitPromise();
|
|
180
184
|
this.setReadyPromise();
|
|
181
185
|
this.#cacheSaverIntervalId = setInterval(() => this.cacheService.save(), CacheAutoSaveInterval);
|
|
@@ -188,32 +192,27 @@ export class Project {
|
|
|
188
192
|
uri: doc.uri,
|
|
189
193
|
version: doc.version,
|
|
190
194
|
});
|
|
191
|
-
})
|
|
192
|
-
.on('documentRemoved', ({ uri }) => {
|
|
195
|
+
}).on('documentRemoved', ({ uri }) => {
|
|
193
196
|
this.emit('documentErrored', { errors: [], uri });
|
|
194
|
-
})
|
|
195
|
-
.on('fileCreated', async ({ uri }) => {
|
|
197
|
+
}).on('fileCreated', async ({ uri }) => {
|
|
196
198
|
if (uri.endsWith(Project.RootSuffix)) {
|
|
197
199
|
this.updateRoots();
|
|
198
200
|
}
|
|
199
201
|
this.bindUri(uri);
|
|
200
202
|
return this.ensureBindingStarted(uri);
|
|
201
|
-
})
|
|
202
|
-
.on('fileModified', async ({ uri }) => {
|
|
203
|
+
}).on('fileModified', async ({ uri }) => {
|
|
203
204
|
this.#symbolUpToDateUris.delete(uri);
|
|
204
205
|
if (this.isOnlyWatched(uri)) {
|
|
205
206
|
await this.ensureBindingStarted(uri);
|
|
206
207
|
}
|
|
207
|
-
})
|
|
208
|
-
.on('fileDeleted', ({ uri }) => {
|
|
208
|
+
}).on('fileDeleted', ({ uri }) => {
|
|
209
209
|
if (uri.endsWith(Project.RootSuffix)) {
|
|
210
210
|
this.updateRoots();
|
|
211
211
|
}
|
|
212
212
|
this.#symbolUpToDateUris.delete(uri);
|
|
213
213
|
this.symbols.clear({ uri });
|
|
214
214
|
this.tryClearingCache(uri);
|
|
215
|
-
})
|
|
216
|
-
.on('ready', () => {
|
|
215
|
+
}).on('ready', () => {
|
|
217
216
|
this.#isReady = true;
|
|
218
217
|
// // Recheck client managed files after the READY process, as they may have incomplete results and are user-facing.
|
|
219
218
|
// const promises: Promise<unknown>[] = []
|
|
@@ -226,6 +225,18 @@ export class Project {
|
|
|
226
225
|
setInitPromise() {
|
|
227
226
|
const loadConfig = async () => {
|
|
228
227
|
this.config = await this.#configService.load();
|
|
228
|
+
this.ignore = ignore();
|
|
229
|
+
for (const pattern of this.config.env.exclude) {
|
|
230
|
+
if (pattern === '@gitignore') {
|
|
231
|
+
const gitignore = await this.readGitignore();
|
|
232
|
+
if (gitignore) {
|
|
233
|
+
this.ignore.add(gitignore);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
this.ignore.add(pattern);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
229
240
|
};
|
|
230
241
|
const callIntializers = async () => {
|
|
231
242
|
const initCtx = {
|
|
@@ -233,6 +244,7 @@ export class Project {
|
|
|
233
244
|
config: this.config,
|
|
234
245
|
downloader: this.downloader,
|
|
235
246
|
externals: this.externals,
|
|
247
|
+
isDebugging: this.isDebugging,
|
|
236
248
|
logger: this.logger,
|
|
237
249
|
meta: this.meta,
|
|
238
250
|
projectRoot: this.projectRoot,
|
|
@@ -262,6 +274,19 @@ export class Project {
|
|
|
262
274
|
};
|
|
263
275
|
this.#initPromise = init();
|
|
264
276
|
}
|
|
277
|
+
async readGitignore() {
|
|
278
|
+
try {
|
|
279
|
+
const uri = this.projectRoot + Project.GitIgnore;
|
|
280
|
+
const contents = await this.externals.fs.readFile(uri);
|
|
281
|
+
return bufferToString(contents);
|
|
282
|
+
}
|
|
283
|
+
catch (e) {
|
|
284
|
+
if (!this.externals.error.isKind(e, 'ENOENT')) {
|
|
285
|
+
this.logger.error(`[Project] [readGitignore]`, e);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
265
290
|
setReadyPromise() {
|
|
266
291
|
const getDependencies = async () => {
|
|
267
292
|
const ans = [];
|
|
@@ -297,30 +322,24 @@ export class Project {
|
|
|
297
322
|
const listProjectFiles = () => new Promise((resolve) => {
|
|
298
323
|
this.#watchedFiles.clear();
|
|
299
324
|
this.#watcherReady = false;
|
|
300
|
-
this.#watcher = this.externals.fs
|
|
301
|
-
.watch(this.projectRoot)
|
|
302
|
-
.once('ready', () => {
|
|
325
|
+
this.#watcher = this.externals.fs.watch(this.projectRoot).once('ready', () => {
|
|
303
326
|
this.#watcherReady = true;
|
|
304
327
|
resolve();
|
|
305
|
-
})
|
|
306
|
-
.on('add', (uri) => {
|
|
328
|
+
}).on('add', (uri) => {
|
|
307
329
|
this.#watchedFiles.add(uri);
|
|
308
330
|
if (this.#watcherReady) {
|
|
309
331
|
this.emit('fileCreated', { uri });
|
|
310
332
|
}
|
|
311
|
-
})
|
|
312
|
-
.on('change', (uri) => {
|
|
333
|
+
}).on('change', (uri) => {
|
|
313
334
|
if (this.#watcherReady) {
|
|
314
335
|
this.emit('fileModified', { uri });
|
|
315
336
|
}
|
|
316
|
-
})
|
|
317
|
-
.on('unlink', (uri) => {
|
|
337
|
+
}).on('unlink', (uri) => {
|
|
318
338
|
this.#watchedFiles.delete(uri);
|
|
319
339
|
if (this.#watcherReady) {
|
|
320
340
|
this.emit('fileDeleted', { uri });
|
|
321
341
|
}
|
|
322
|
-
})
|
|
323
|
-
.on('error', (e) => {
|
|
342
|
+
}).on('error', (e) => {
|
|
324
343
|
this.logger.error('[Project] [chokidar]', e);
|
|
325
344
|
});
|
|
326
345
|
});
|
|
@@ -350,9 +369,7 @@ export class Project {
|
|
|
350
369
|
this.emit('documentErrored', { errors: values, uri });
|
|
351
370
|
}
|
|
352
371
|
__profiler.task('Pop Errors');
|
|
353
|
-
const { addedFiles, changedFiles, removedFiles } = await this
|
|
354
|
-
.cacheService
|
|
355
|
-
.validate();
|
|
372
|
+
const { addedFiles, changedFiles, removedFiles } = await this.cacheService.validate();
|
|
356
373
|
for (const uri of removedFiles) {
|
|
357
374
|
this.emit('fileDeleted', { uri });
|
|
358
375
|
}
|
|
@@ -506,9 +523,18 @@ export class Project {
|
|
|
506
523
|
}
|
|
507
524
|
parse(doc) {
|
|
508
525
|
const ctx = ParserContext.create(this, { doc });
|
|
526
|
+
const parser = ctx.meta.getParserForLanguageId(ctx.doc.languageId);
|
|
527
|
+
if (!parser) {
|
|
528
|
+
return {
|
|
529
|
+
type: 'file',
|
|
530
|
+
range: Range.create(0),
|
|
531
|
+
children: [],
|
|
532
|
+
locals: Object.create(null),
|
|
533
|
+
parserErrors: [],
|
|
534
|
+
};
|
|
535
|
+
}
|
|
509
536
|
const src = new Source(doc.getText());
|
|
510
|
-
|
|
511
|
-
return node;
|
|
537
|
+
return file(parser)(src, ctx);
|
|
512
538
|
}
|
|
513
539
|
async bind(doc, node) {
|
|
514
540
|
if (node.binderErrors) {
|
|
@@ -562,8 +588,7 @@ export class Project {
|
|
|
562
588
|
continue;
|
|
563
589
|
}
|
|
564
590
|
const { ruleSeverity, ruleValue } = result;
|
|
565
|
-
const { configValidator, linter, nodePredicate } = this.meta
|
|
566
|
-
.getLinter(ruleName);
|
|
591
|
+
const { configValidator, linter, nodePredicate } = this.meta.getLinter(ruleName);
|
|
567
592
|
if (!configValidator(ruleName, ruleValue, this.logger)) {
|
|
568
593
|
// Config value is invalid.
|
|
569
594
|
continue;
|
|
@@ -590,8 +615,7 @@ export class Project {
|
|
|
590
615
|
// @SingletonPromise()
|
|
591
616
|
async ensureBindingStarted(uri) {
|
|
592
617
|
uri = this.normalizeUri(uri);
|
|
593
|
-
if (this.#symbolUpToDateUris.has(uri) ||
|
|
594
|
-
this.#bindingInProgressUris.has(uri)) {
|
|
618
|
+
if (this.#symbolUpToDateUris.has(uri) || this.#bindingInProgressUris.has(uri)) {
|
|
595
619
|
return;
|
|
596
620
|
}
|
|
597
621
|
this.#bindingInProgressUris.add(uri);
|
|
@@ -702,14 +726,14 @@ export class Project {
|
|
|
702
726
|
}
|
|
703
727
|
}
|
|
704
728
|
shouldRemove(uri) {
|
|
705
|
-
return (!this.#clientManagedUris.has(uri)
|
|
706
|
-
!this.#dependencyFiles
|
|
707
|
-
!this.#watchedFiles.has(uri));
|
|
729
|
+
return (!this.#clientManagedUris.has(uri)
|
|
730
|
+
&& !this.#dependencyFiles?.has(uri)
|
|
731
|
+
&& !this.#watchedFiles.has(uri));
|
|
708
732
|
}
|
|
709
733
|
isOnlyWatched(uri) {
|
|
710
|
-
return (this.#watchedFiles.has(uri)
|
|
711
|
-
!this.#clientManagedUris.has(uri)
|
|
712
|
-
!this.#dependencyFiles
|
|
734
|
+
return (this.#watchedFiles.has(uri)
|
|
735
|
+
&& !this.#clientManagedUris.has(uri)
|
|
736
|
+
&& !this.#dependencyFiles?.has(uri));
|
|
713
737
|
}
|
|
714
738
|
}
|
|
715
739
|
__decorate([
|
package/lib/service/Service.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export declare class Service {
|
|
|
22
22
|
readonly logger: Logger;
|
|
23
23
|
readonly profilers: ProfilerFactory;
|
|
24
24
|
readonly project: Project;
|
|
25
|
-
constructor({ isDebugging, logger, profilers, project
|
|
25
|
+
constructor({ isDebugging, logger, profilers, project }: Options);
|
|
26
26
|
private debug;
|
|
27
27
|
colorize(node: FileNode<AstNode>, doc: TextDocument, range?: Range): readonly ColorToken[];
|
|
28
28
|
getColorInfo(node: FileNode<AstNode>, doc: TextDocument): ColorInfo[];
|