@prisma-next/language-server 0.14.0-dev.46 → 0.14.0-dev.48
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/README.md +8 -8
- package/dist/exports/index.d.mts +3 -3
- package/dist/exports/index.d.mts.map +1 -1
- package/dist/exports/index.mjs +211 -133
- package/dist/exports/index.mjs.map +1 -1
- package/package.json +10 -9
- package/src/completion-provider.ts +4 -12
- package/src/project-artifacts.ts +86 -39
- package/src/schema-inputs.ts +3 -0
- package/src/semantic-tokens.ts +18 -20
- package/src/server.ts +268 -126
package/src/project-artifacts.ts
CHANGED
|
@@ -1,60 +1,107 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { buildSymbolTable, type SymbolTable } from '@prisma-next/psl-parser';
|
|
2
2
|
import type { DocumentAst, SourceFile } from '@prisma-next/psl-parser/syntax';
|
|
3
3
|
import type { LspDiagnostic } from './diagnostic-mapping';
|
|
4
4
|
import { computeDocumentDiagnostics } from './document-diagnostics';
|
|
5
5
|
import type { PipelineInputs } from './pipeline';
|
|
6
6
|
import type { SchemaInputSet } from './schema-inputs';
|
|
7
7
|
|
|
8
|
-
export interface
|
|
8
|
+
export interface DocumentArtifacts {
|
|
9
9
|
readonly document: DocumentAst;
|
|
10
10
|
readonly sourceFile: SourceFile;
|
|
11
|
-
readonly
|
|
11
|
+
readonly diagnostics: readonly LspDiagnostic[];
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
export interface ProjectArtifactsOptions {
|
|
15
|
+
readonly inputs: SchemaInputSet;
|
|
16
|
+
readonly controlStack: PipelineInputs;
|
|
17
|
+
readonly getText: (uri: string) => string | undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Reads can never observe stale artifacts: the vscode-languageserver runtime
|
|
22
|
+
* dispatches messages in order and the server raises `documentChanged` /
|
|
23
|
+
* `documentClosed` synchronously against the already-updated text mirror, so
|
|
24
|
+
* every mutation that could affect a read lands before that read runs. A
|
|
25
|
+
* config reload replaces the store wholesale.
|
|
26
|
+
*/
|
|
14
27
|
export interface ProjectArtifacts {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
):
|
|
23
|
-
remove(uri: string): void;
|
|
28
|
+
/**
|
|
29
|
+
* `undefined` when the document is not open in the text mirror or is not
|
|
30
|
+
* one of the project's configured inputs.
|
|
31
|
+
*/
|
|
32
|
+
document(uri: string): DocumentArtifacts | undefined;
|
|
33
|
+
symbolTable(): SymbolTable;
|
|
34
|
+
documentChanged(uri: string): void;
|
|
35
|
+
documentClosed(uri: string): void;
|
|
24
36
|
}
|
|
25
37
|
|
|
26
|
-
export function createProjectArtifacts(): ProjectArtifacts {
|
|
27
|
-
const
|
|
38
|
+
export function createProjectArtifacts(options: ProjectArtifactsOptions): ProjectArtifacts {
|
|
39
|
+
const { inputs, controlStack, getText } = options;
|
|
40
|
+
const documents = new Map<string, DocumentArtifacts>();
|
|
28
41
|
let symbolTable: SymbolTable | undefined;
|
|
29
42
|
|
|
43
|
+
function drop(uri: string): void {
|
|
44
|
+
if (documents.delete(uri)) {
|
|
45
|
+
symbolTable = undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function readDocument(uri: string): DocumentArtifacts | undefined {
|
|
50
|
+
const existing = documents.get(uri);
|
|
51
|
+
if (existing !== undefined) {
|
|
52
|
+
return existing;
|
|
53
|
+
}
|
|
54
|
+
const text = getText(uri);
|
|
55
|
+
if (text === undefined) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
const computed = computeDocumentDiagnostics(uri, text, inputs, controlStack);
|
|
59
|
+
if (computed === null) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
const artifacts: DocumentArtifacts = {
|
|
63
|
+
document: computed.document,
|
|
64
|
+
sourceFile: computed.sourceFile,
|
|
65
|
+
diagnostics: computed.diagnostics,
|
|
66
|
+
};
|
|
67
|
+
documents.set(uri, artifacts);
|
|
68
|
+
// Single-input by design: the project-wide symbolTable is rebuilt from the
|
|
69
|
+
// one open configured input; merging multiple inputs (and reading unopened
|
|
70
|
+
// ones from disk) is deferred cross-file work.
|
|
71
|
+
symbolTable = computed.symbolTable;
|
|
72
|
+
return artifacts;
|
|
73
|
+
}
|
|
74
|
+
|
|
30
75
|
return {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (computed === null) {
|
|
36
|
-
if (documents.delete(uri)) {
|
|
37
|
-
symbolTable = undefined;
|
|
38
|
-
}
|
|
39
|
-
return null;
|
|
76
|
+
document: readDocument,
|
|
77
|
+
symbolTable: () => {
|
|
78
|
+
if (symbolTable !== undefined) {
|
|
79
|
+
return symbolTable;
|
|
40
80
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
symbolTable = undefined;
|
|
81
|
+
for (const uri of inputs.uris()) {
|
|
82
|
+
const artifacts = readDocument(uri);
|
|
83
|
+
if (artifacts === undefined) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
// A read that hits existing artifacts leaves the slot unset (the
|
|
87
|
+
// contributing input may have closed since); rebuild from the
|
|
88
|
+
// artifacts without reparsing.
|
|
89
|
+
symbolTable ??= buildSymbolTable({
|
|
90
|
+
document: artifacts.document,
|
|
91
|
+
sourceFile: artifacts.sourceFile,
|
|
92
|
+
scalarTypes: controlStack.scalarTypes,
|
|
93
|
+
pslBlockDescriptors: controlStack.pslBlockDescriptors,
|
|
94
|
+
}).table;
|
|
95
|
+
return symbolTable;
|
|
57
96
|
}
|
|
97
|
+
// The server's lifecycle makes this unreachable: it drops a project
|
|
98
|
+
// once its last open input closes. Throwing loudly beats serving a
|
|
99
|
+
// fabricated empty symbolTable that would mask the broken invariant.
|
|
100
|
+
throw new Error(
|
|
101
|
+
'invariant violated: project has no open configured input — the server must drop such projects',
|
|
102
|
+
);
|
|
58
103
|
},
|
|
104
|
+
documentChanged: drop,
|
|
105
|
+
documentClosed: drop,
|
|
59
106
|
};
|
|
60
107
|
}
|
package/src/schema-inputs.ts
CHANGED
|
@@ -11,6 +11,7 @@ export interface SchemaInputConfig {
|
|
|
11
11
|
|
|
12
12
|
export interface SchemaInputSet {
|
|
13
13
|
includes(uri: string): boolean;
|
|
14
|
+
uris(): Iterable<string>;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
export function hasPslInputs(config: SchemaInputConfig): boolean {
|
|
@@ -24,9 +25,11 @@ export function resolveSchemaInputs(config: SchemaInputConfig): SchemaInputSet {
|
|
|
24
25
|
|
|
25
26
|
return {
|
|
26
27
|
includes: (uri) => uris.has(uri),
|
|
28
|
+
uris: () => uris,
|
|
27
29
|
};
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
export const emptySchemaInputSet: SchemaInputSet = {
|
|
31
33
|
includes: () => false,
|
|
34
|
+
uris: () => [],
|
|
32
35
|
};
|
package/src/semantic-tokens.ts
CHANGED
|
@@ -86,7 +86,7 @@ export const semanticTokensLegend: SemanticTokensLegend = {
|
|
|
86
86
|
export interface SemanticTokenSource {
|
|
87
87
|
readonly document: DocumentAst;
|
|
88
88
|
readonly sourceFile: SourceFile;
|
|
89
|
-
readonly symbolTable: SymbolTable
|
|
89
|
+
readonly symbolTable: SymbolTable;
|
|
90
90
|
readonly scalarTypes: readonly string[];
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -528,7 +528,7 @@ function classifyTypeReference(
|
|
|
528
528
|
const table = source.symbolTable;
|
|
529
529
|
const namespaceName = path.length > 1 ? path[path.length - 2] : namespace;
|
|
530
530
|
const namespaceScope =
|
|
531
|
-
namespaceName !== undefined ? table
|
|
531
|
+
namespaceName !== undefined ? table.topLevel.namespaces[namespaceName] : undefined;
|
|
532
532
|
|
|
533
533
|
if (namespaceScope !== undefined) {
|
|
534
534
|
if (Object.hasOwn(namespaceScope.models, name)) {
|
|
@@ -542,22 +542,20 @@ function classifyTypeReference(
|
|
|
542
542
|
}
|
|
543
543
|
}
|
|
544
544
|
|
|
545
|
-
if (table
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
return { tokenType: 'type' };
|
|
560
|
-
}
|
|
545
|
+
if (Object.hasOwn(table.topLevel.models, name)) {
|
|
546
|
+
return { tokenType: 'class' };
|
|
547
|
+
}
|
|
548
|
+
if (Object.hasOwn(table.topLevel.compositeTypes, name)) {
|
|
549
|
+
return { tokenType: 'struct' };
|
|
550
|
+
}
|
|
551
|
+
if (Object.hasOwn(table.topLevel.scalars, name)) {
|
|
552
|
+
return { tokenType: 'type', modifierBitset: semanticTokenModifierBits.defaultLibrary };
|
|
553
|
+
}
|
|
554
|
+
if (
|
|
555
|
+
Object.hasOwn(table.topLevel.typeAliases, name) ||
|
|
556
|
+
Object.hasOwn(table.topLevel.blocks, name)
|
|
557
|
+
) {
|
|
558
|
+
return { tokenType: 'type' };
|
|
561
559
|
}
|
|
562
560
|
|
|
563
561
|
if (source.scalarTypes.includes(name)) {
|
|
@@ -567,8 +565,8 @@ function classifyTypeReference(
|
|
|
567
565
|
return { tokenType: 'type' };
|
|
568
566
|
}
|
|
569
567
|
|
|
570
|
-
function isKnownNamespace(name: string, table: SymbolTable
|
|
571
|
-
return
|
|
568
|
+
function isKnownNamespace(name: string, table: SymbolTable): boolean {
|
|
569
|
+
return Object.hasOwn(table.topLevel.namespaces, name);
|
|
572
570
|
}
|
|
573
571
|
|
|
574
572
|
function identifierSegments(name: QualifiedNameAst): readonly IdentifierSegment[] {
|