@spyglassmc/core 0.3.0 → 0.4.0
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/parser/util.d.ts +1 -0
- package/lib/parser/util.js +2 -2
- package/lib/processor/colorizer/builtin.js +2 -1
- package/lib/service/CacheService.d.ts +5 -2
- package/lib/service/CacheService.js +12 -3
- package/lib/service/MetaRegistry.d.ts +5 -0
- package/lib/service/Project.d.ts +9 -7
- package/lib/service/Project.js +27 -7
- package/lib/source/LanguageError.d.ts +18 -4
- package/lib/source/LanguageError.js +16 -6
- package/lib/source/PositionRange.d.ts +1 -1
- package/lib/source/PositionRange.js +4 -4
- package/lib/source/Source.d.ts +2 -0
- package/lib/source/Source.js +6 -0
- package/package.json +1 -1
package/lib/parser/util.d.ts
CHANGED
package/lib/parser/util.js
CHANGED
|
@@ -150,8 +150,8 @@ export function recover(parser, defaultValue) {
|
|
|
150
150
|
}
|
|
151
151
|
export function select(cases) {
|
|
152
152
|
return (src, ctx) => {
|
|
153
|
-
for (const { predicate, prefix, parser } of cases) {
|
|
154
|
-
if (predicate?.(src) ?? (prefix !== undefined ? src.tryPeek(prefix) : undefined) ?? true) {
|
|
153
|
+
for (const { predicate, prefix, parser, regex } of cases) {
|
|
154
|
+
if (predicate?.(src) ?? (prefix !== undefined ? src.tryPeek(prefix) : undefined) ?? (regex && src.matchPattern(regex)) ?? true) {
|
|
155
155
|
const callableParser = typeof parser === 'object' ? parser.get() : parser;
|
|
156
156
|
return callableParser(src, ctx);
|
|
157
157
|
}
|
|
@@ -20,7 +20,8 @@ export const comment = node => {
|
|
|
20
20
|
return [ColorToken.create(node, 'comment')];
|
|
21
21
|
};
|
|
22
22
|
export const error = node => {
|
|
23
|
-
return [ColorToken.create(node, 'error')]
|
|
23
|
+
// return [ColorToken.create(node, 'error')]
|
|
24
|
+
return [];
|
|
24
25
|
};
|
|
25
26
|
export const literal = node => {
|
|
26
27
|
return [ColorToken.create(node, node.options.colorTokenType ?? 'literal')];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { TextDocument } from 'vscode-languageserver-textdocument';
|
|
2
|
+
import type { PosRangeLanguageError } from '../source/index.js';
|
|
2
3
|
import { SymbolTable } from '../symbol/index.js';
|
|
3
4
|
import type { RootUriString } from './fileUtil.js';
|
|
4
5
|
import type { Project } from './Project.js';
|
|
@@ -6,7 +7,7 @@ import type { Project } from './Project.js';
|
|
|
6
7
|
* The format version of the cache. Should be increased when any changes that
|
|
7
8
|
* could invalidate the cache are introduced to the Spyglass codebase.
|
|
8
9
|
*/
|
|
9
|
-
export declare const LatestCacheVersion =
|
|
10
|
+
export declare const LatestCacheVersion = 2;
|
|
10
11
|
/**
|
|
11
12
|
* Checksums of cached files or roots.
|
|
12
13
|
*/
|
|
@@ -18,6 +19,7 @@ interface Checksums {
|
|
|
18
19
|
declare namespace Checksums {
|
|
19
20
|
function create(): Checksums;
|
|
20
21
|
}
|
|
22
|
+
declare type ErrorCache = Record<string, readonly PosRangeLanguageError[]>;
|
|
21
23
|
interface LoadResult {
|
|
22
24
|
symbols: SymbolTable;
|
|
23
25
|
}
|
|
@@ -32,6 +34,7 @@ export declare class CacheService {
|
|
|
32
34
|
private readonly cacheRoot;
|
|
33
35
|
private readonly project;
|
|
34
36
|
checksums: Checksums;
|
|
37
|
+
errors: ErrorCache;
|
|
35
38
|
/**
|
|
36
39
|
* @param cacheRoot File path to the directory where cache files by Spyglass should be stored.
|
|
37
40
|
* @param project
|
|
@@ -50,7 +53,7 @@ export declare class CacheService {
|
|
|
50
53
|
*/
|
|
51
54
|
save(): Promise<boolean>;
|
|
52
55
|
hasFileChangedSinceCache(doc: TextDocument): Promise<boolean>;
|
|
53
|
-
reset():
|
|
56
|
+
reset(): LoadResult;
|
|
54
57
|
}
|
|
55
58
|
export {};
|
|
56
59
|
//# sourceMappingURL=CacheService.d.ts.map
|
|
@@ -5,7 +5,7 @@ import { fileUtil } from './fileUtil.js';
|
|
|
5
5
|
* The format version of the cache. Should be increased when any changes that
|
|
6
6
|
* could invalidate the cache are introduced to the Spyglass codebase.
|
|
7
7
|
*/
|
|
8
|
-
export const LatestCacheVersion =
|
|
8
|
+
export const LatestCacheVersion = 2;
|
|
9
9
|
var Checksums;
|
|
10
10
|
(function (Checksums) {
|
|
11
11
|
function create() {
|
|
@@ -21,6 +21,7 @@ export class CacheService {
|
|
|
21
21
|
cacheRoot;
|
|
22
22
|
project;
|
|
23
23
|
checksums = Checksums.create();
|
|
24
|
+
errors = {};
|
|
24
25
|
#hasValidatedFiles = false;
|
|
25
26
|
/**
|
|
26
27
|
* @param cacheRoot File path to the directory where cache files by Spyglass should be stored.
|
|
@@ -63,6 +64,9 @@ export class CacheService {
|
|
|
63
64
|
this.checksums.symbolRegistrars[id] = checksum;
|
|
64
65
|
}
|
|
65
66
|
});
|
|
67
|
+
this.project.on('documentErrored', ({ uri, errors }) => {
|
|
68
|
+
this.errors[uri] = errors;
|
|
69
|
+
});
|
|
66
70
|
}
|
|
67
71
|
#cacheFilePath;
|
|
68
72
|
/**
|
|
@@ -84,6 +88,7 @@ export class CacheService {
|
|
|
84
88
|
__profiler.task('Read File');
|
|
85
89
|
if (cache.version === LatestCacheVersion) {
|
|
86
90
|
this.checksums = cache.checksums;
|
|
91
|
+
this.errors = cache.errors;
|
|
87
92
|
ans.symbols = SymbolTable.link(cache.symbols);
|
|
88
93
|
__profiler.task('Link Symbols');
|
|
89
94
|
}
|
|
@@ -163,10 +168,11 @@ export class CacheService {
|
|
|
163
168
|
try {
|
|
164
169
|
filePath = await this.getCacheFileUri();
|
|
165
170
|
const cache = {
|
|
166
|
-
|
|
171
|
+
version: LatestCacheVersion,
|
|
167
172
|
projectRoot: this.project.projectRoot,
|
|
173
|
+
checksums: this.checksums,
|
|
168
174
|
symbols: SymbolTable.unlink(this.project.symbols.global),
|
|
169
|
-
|
|
175
|
+
errors: this.errors,
|
|
170
176
|
};
|
|
171
177
|
__profiler.task('Unlink Symbols');
|
|
172
178
|
await fileUtil.writeGzippedJson(this.project.externals, filePath, cache);
|
|
@@ -182,7 +188,10 @@ export class CacheService {
|
|
|
182
188
|
return this.checksums.files[doc.uri] !== await this.project.externals.crypto.getSha1(doc.getText());
|
|
183
189
|
}
|
|
184
190
|
reset() {
|
|
191
|
+
this.#hasValidatedFiles = false;
|
|
185
192
|
this.checksums = Checksums.create();
|
|
193
|
+
this.errors = {};
|
|
194
|
+
return { symbols: {} };
|
|
186
195
|
}
|
|
187
196
|
}
|
|
188
197
|
//# sourceMappingURL=CacheService.js.map
|
|
@@ -26,6 +26,11 @@ interface LinterRegistration {
|
|
|
26
26
|
}
|
|
27
27
|
interface SymbolRegistrarRegistration {
|
|
28
28
|
registrar: SymbolRegistrar;
|
|
29
|
+
/**
|
|
30
|
+
* A checksum associated with this symbol registrar.
|
|
31
|
+
* If the cached checksum is equal to this provided checksum,
|
|
32
|
+
* the symbol registrar is not executed.
|
|
33
|
+
*/
|
|
29
34
|
checksum: string | undefined;
|
|
30
35
|
}
|
|
31
36
|
/**
|
package/lib/service/Project.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { ExternalEventEmitter, Externals } from '../common/index.js';
|
|
|
4
4
|
import { Logger } from '../common/index.js';
|
|
5
5
|
import type { AstNode } from '../node/index.js';
|
|
6
6
|
import { FileNode } from '../node/index.js';
|
|
7
|
-
import type {
|
|
7
|
+
import type { PosRangeLanguageError } from '../source/index.js';
|
|
8
8
|
import { SymbolUtil } from '../symbol/index.js';
|
|
9
9
|
import { CacheService } from './CacheService.js';
|
|
10
10
|
import type { Config } from './Config.js';
|
|
@@ -38,8 +38,10 @@ export interface DocAndNode {
|
|
|
38
38
|
}
|
|
39
39
|
interface DocumentEvent extends DocAndNode {
|
|
40
40
|
}
|
|
41
|
-
interface DocumentErrorEvent
|
|
42
|
-
errors:
|
|
41
|
+
interface DocumentErrorEvent {
|
|
42
|
+
errors: readonly PosRangeLanguageError[];
|
|
43
|
+
uri: string;
|
|
44
|
+
version?: number;
|
|
43
45
|
}
|
|
44
46
|
interface FileEvent {
|
|
45
47
|
uri: string;
|
|
@@ -124,21 +126,21 @@ export declare class Project implements ExternalEventEmitter {
|
|
|
124
126
|
*/
|
|
125
127
|
get cacheRoot(): RootUriString;
|
|
126
128
|
private updateRoots;
|
|
127
|
-
on(event: '
|
|
129
|
+
on(event: 'documentErrored', callbackFn: (data: DocumentErrorEvent) => void): this;
|
|
128
130
|
on(event: 'documentUpdated', callbackFn: (data: DocumentEvent) => void): this;
|
|
129
131
|
on(event: 'documentRemoved', callbackFn: (data: FileEvent) => void): this;
|
|
130
132
|
on(event: `file${'Created' | 'Modified' | 'Deleted'}`, callbackFn: (data: FileEvent) => void): this;
|
|
131
133
|
on(event: 'ready', callbackFn: (data: EmptyEvent) => void): this;
|
|
132
134
|
on(event: 'rootsUpdated', callbackFn: (data: RootsEvent) => void): this;
|
|
133
135
|
on(event: 'symbolRegistrarExecuted', callbackFn: (data: SymbolRegistrarEvent) => void): this;
|
|
134
|
-
once(event: '
|
|
136
|
+
once(event: 'documentErrored', callbackFn: (data: DocumentErrorEvent) => void): this;
|
|
135
137
|
once(event: 'documentUpdated', callbackFn: (data: DocumentEvent) => void): this;
|
|
136
138
|
once(event: 'documentRemoved', callbackFn: (data: FileEvent) => void): this;
|
|
137
139
|
once(event: `file${'Created' | 'Modified' | 'Deleted'}`, callbackFn: (data: FileEvent) => void): this;
|
|
138
140
|
once(event: 'ready', callbackFn: (data: EmptyEvent) => void): this;
|
|
139
141
|
once(event: 'rootsUpdated', callbackFn: (data: RootsEvent) => void): this;
|
|
140
142
|
once(event: 'symbolRegistrarExecuted', callbackFn: (data: SymbolRegistrarEvent) => void): this;
|
|
141
|
-
emit(event: '
|
|
143
|
+
emit(event: 'documentErrored', data: DocumentErrorEvent): boolean;
|
|
142
144
|
emit(event: 'documentUpdated', data: DocumentEvent): boolean;
|
|
143
145
|
emit(event: 'documentRemoved', data: FileEvent): boolean;
|
|
144
146
|
emit(event: `file${'Created' | 'Modified' | 'Deleted'}`, data: FileEvent): boolean;
|
|
@@ -168,7 +170,7 @@ export declare class Project implements ExternalEventEmitter {
|
|
|
168
170
|
*/
|
|
169
171
|
close(): Promise<void>;
|
|
170
172
|
restart(): Promise<void>;
|
|
171
|
-
resetCache(): void
|
|
173
|
+
resetCache(): Promise<void>;
|
|
172
174
|
normalizeUri(uri: string): string;
|
|
173
175
|
private static readonly TextDocumentCacheMaxLength;
|
|
174
176
|
private removeCachedTextDocument;
|
package/lib/service/Project.js
CHANGED
|
@@ -9,7 +9,7 @@ import { bufferToString, Logger, SingletonPromise, StateProxy } from '../common/
|
|
|
9
9
|
import { FileNode } from '../node/index.js';
|
|
10
10
|
import { file } from '../parser/index.js';
|
|
11
11
|
import { traversePreOrder } from '../processor/index.js';
|
|
12
|
-
import { Source } from '../source/index.js';
|
|
12
|
+
import { LanguageError, Source } from '../source/index.js';
|
|
13
13
|
import { SymbolUtil } from '../symbol/index.js';
|
|
14
14
|
import { CacheService } from './CacheService.js';
|
|
15
15
|
import { ConfigService, LinterConfigValue } from './Config.js';
|
|
@@ -183,11 +183,14 @@ export class Project {
|
|
|
183
183
|
// if (!this.#isReady) {
|
|
184
184
|
// return
|
|
185
185
|
// }
|
|
186
|
-
this.emit('
|
|
187
|
-
doc,
|
|
188
|
-
|
|
189
|
-
|
|
186
|
+
this.emit('documentErrored', {
|
|
187
|
+
errors: FileNode.getErrors(node).map(e => LanguageError.withPosRange(e, doc)),
|
|
188
|
+
uri: doc.uri,
|
|
189
|
+
version: doc.version,
|
|
190
190
|
});
|
|
191
|
+
})
|
|
192
|
+
.on('documentRemoved', ({ uri }) => {
|
|
193
|
+
this.emit('documentErrored', { errors: [], uri });
|
|
191
194
|
})
|
|
192
195
|
.on('fileCreated', async ({ uri }) => {
|
|
193
196
|
if (uri.endsWith(Project.RootSuffix)) {
|
|
@@ -292,6 +295,7 @@ export class Project {
|
|
|
292
295
|
this.fs.register(ArchiveUriSupporter.Protocol, archiveUriSupporter, true);
|
|
293
296
|
};
|
|
294
297
|
const listProjectFiles = () => new Promise(resolve => {
|
|
298
|
+
this.#watchedFiles.clear();
|
|
295
299
|
this.#watcherReady = false;
|
|
296
300
|
this.#watcher = this.externals.fs
|
|
297
301
|
.watch(this.projectRoot)
|
|
@@ -345,9 +349,13 @@ export class Project {
|
|
|
345
349
|
}
|
|
346
350
|
}
|
|
347
351
|
__profiler.task('Register Symbols');
|
|
352
|
+
for (const [uri, values] of Object.entries(this.cacheService.errors)) {
|
|
353
|
+
this.emit('documentErrored', { errors: values, uri });
|
|
354
|
+
}
|
|
355
|
+
__profiler.task('Pop Errors');
|
|
348
356
|
const { addedFiles, changedFiles, removedFiles } = await this.cacheService.validate();
|
|
349
357
|
for (const uri of removedFiles) {
|
|
350
|
-
this.
|
|
358
|
+
this.emit('fileDeleted', { uri });
|
|
351
359
|
}
|
|
352
360
|
__profiler.task('Validate Cache');
|
|
353
361
|
if (addedFiles.length > 0) {
|
|
@@ -366,6 +374,7 @@ export class Project {
|
|
|
366
374
|
__profiler.finalize();
|
|
367
375
|
this.emit('ready', {});
|
|
368
376
|
};
|
|
377
|
+
this.#isReady = false;
|
|
369
378
|
this.#readyPromise = ready();
|
|
370
379
|
}
|
|
371
380
|
/**
|
|
@@ -393,6 +402,8 @@ export class Project {
|
|
|
393
402
|
async restart() {
|
|
394
403
|
try {
|
|
395
404
|
await this.#watcher.close();
|
|
405
|
+
this.#bindingInProgressUris.clear();
|
|
406
|
+
this.#symbolUpToDateUris.clear();
|
|
396
407
|
this.setReadyPromise();
|
|
397
408
|
await this.ready();
|
|
398
409
|
}
|
|
@@ -401,7 +412,16 @@ export class Project {
|
|
|
401
412
|
}
|
|
402
413
|
}
|
|
403
414
|
resetCache() {
|
|
404
|
-
|
|
415
|
+
this.logger.info('[Project#resetCache] Initiated...');
|
|
416
|
+
// Clear existing errors.
|
|
417
|
+
for (const uri of Object.keys(this.cacheService.errors)) {
|
|
418
|
+
this.emit('documentErrored', { errors: [], uri });
|
|
419
|
+
}
|
|
420
|
+
// Reset cache.
|
|
421
|
+
const { symbols } = this.cacheService.reset();
|
|
422
|
+
this.symbols = new SymbolUtil(symbols, this.externals.event.EventEmitter);
|
|
423
|
+
this.symbols.buildCache();
|
|
424
|
+
return this.restart();
|
|
405
425
|
}
|
|
406
426
|
normalizeUri(uri) {
|
|
407
427
|
return this.fs.mapFromDisk(this.externals.uri.normalize(uri));
|
|
@@ -1,14 +1,28 @@
|
|
|
1
|
+
import type { TextDocument } from 'vscode-languageserver-textdocument';
|
|
1
2
|
import type { Location } from './Location.js';
|
|
3
|
+
import { PositionRange } from './PositionRange.js';
|
|
2
4
|
import type { Range } from './Range.js';
|
|
3
|
-
export interface
|
|
5
|
+
export interface LanguageErrorData {
|
|
4
6
|
message: string;
|
|
5
|
-
range: Range;
|
|
6
7
|
severity: ErrorSeverity;
|
|
7
8
|
info?: LanguageErrorInfo;
|
|
8
9
|
}
|
|
9
|
-
export
|
|
10
|
-
|
|
10
|
+
export interface LanguageError extends LanguageErrorData {
|
|
11
|
+
range: Range;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A language error that uses {@link PositionRange} instead of {@link Range} to represent the span of the error.
|
|
15
|
+
*/
|
|
16
|
+
export interface PosRangeLanguageError extends LanguageErrorData {
|
|
17
|
+
posRange: PositionRange;
|
|
11
18
|
}
|
|
19
|
+
export declare const LanguageError: Readonly<{
|
|
20
|
+
create(message: string, range: Range, severity?: ErrorSeverity, info?: LanguageErrorInfo): LanguageError;
|
|
21
|
+
/**
|
|
22
|
+
* @returns A {@link PosRangeLanguageError}.
|
|
23
|
+
*/
|
|
24
|
+
withPosRange(error: LanguageError, doc: TextDocument): PosRangeLanguageError;
|
|
25
|
+
}>;
|
|
12
26
|
export declare const enum ErrorSeverity {
|
|
13
27
|
Hint = 0,
|
|
14
28
|
Information = 1,
|
|
@@ -1,12 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { PositionRange } from './PositionRange.js';
|
|
2
|
+
export const LanguageError = Object.freeze({
|
|
3
|
+
create(message, range, severity = 3 /* ErrorSeverity.Error */, info) {
|
|
4
4
|
const ans = { range, message, severity };
|
|
5
5
|
if (info) {
|
|
6
6
|
ans.info = info;
|
|
7
7
|
}
|
|
8
8
|
return ans;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
},
|
|
10
|
+
/**
|
|
11
|
+
* @returns A {@link PosRangeLanguageError}.
|
|
12
|
+
*/
|
|
13
|
+
withPosRange(error, doc) {
|
|
14
|
+
return {
|
|
15
|
+
posRange: PositionRange.from(error.range, doc),
|
|
16
|
+
message: error.message,
|
|
17
|
+
severity: error.severity,
|
|
18
|
+
...error.info && { info: error.info },
|
|
19
|
+
};
|
|
20
|
+
},
|
|
21
|
+
});
|
|
12
22
|
//# sourceMappingURL=LanguageError.js.map
|
|
@@ -12,7 +12,7 @@ export declare namespace PositionRange {
|
|
|
12
12
|
/**
|
|
13
13
|
* @returns A `PositionRange` converted from a `RangeLike`.
|
|
14
14
|
*/
|
|
15
|
-
function from(
|
|
15
|
+
function from(rangeLike: RangeLike, doc: TextDocument): PositionRange;
|
|
16
16
|
/**
|
|
17
17
|
* ```typescript
|
|
18
18
|
* {
|
|
@@ -27,11 +27,11 @@ export var PositionRange;
|
|
|
27
27
|
/**
|
|
28
28
|
* @returns A `PositionRange` converted from a `RangeLike`.
|
|
29
29
|
*/
|
|
30
|
-
function from(
|
|
31
|
-
const
|
|
30
|
+
function from(rangeLike, doc) {
|
|
31
|
+
const range = Range.get(rangeLike);
|
|
32
32
|
const ans = {
|
|
33
|
-
start: doc.positionAt(
|
|
34
|
-
end: doc.positionAt(
|
|
33
|
+
start: doc.positionAt(range.start),
|
|
34
|
+
end: doc.positionAt(range.end),
|
|
35
35
|
};
|
|
36
36
|
return ans;
|
|
37
37
|
}
|
package/lib/source/Source.d.ts
CHANGED
|
@@ -43,6 +43,8 @@ export declare class ReadonlySource {
|
|
|
43
43
|
tryPeekAfterWhitespace(expectedValue: string): boolean;
|
|
44
44
|
peekUntil(...terminators: string[]): string;
|
|
45
45
|
peekLine(): string;
|
|
46
|
+
peekRemaining(): string;
|
|
47
|
+
matchPattern(regex: RegExp): boolean;
|
|
46
48
|
hasNonSpaceAheadInLine(): boolean;
|
|
47
49
|
slice(start: number, end?: number): string;
|
|
48
50
|
slice(rangeLike: Range | RangeContainer): string;
|
package/lib/source/Source.js
CHANGED
|
@@ -70,6 +70,12 @@ export class ReadonlySource {
|
|
|
70
70
|
peekLine() {
|
|
71
71
|
return this.peekUntil(CR, LF);
|
|
72
72
|
}
|
|
73
|
+
peekRemaining() {
|
|
74
|
+
return this.string.slice(this.innerCursor);
|
|
75
|
+
}
|
|
76
|
+
matchPattern(regex) {
|
|
77
|
+
return regex.test(this.peekRemaining());
|
|
78
|
+
}
|
|
73
79
|
hasNonSpaceAheadInLine() {
|
|
74
80
|
for (let cursor = this.innerCursor; cursor < this.string.length; cursor++) {
|
|
75
81
|
const c = this.string.charAt(cursor);
|