@prisma-next/language-server 0.14.0-dev.37 → 0.14.0-dev.39
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 +7 -4
- package/dist/exports/index.d.mts +1 -0
- package/dist/exports/index.d.mts.map +1 -1
- package/dist/exports/index.mjs +488 -25
- package/dist/exports/index.mjs.map +1 -1
- package/package.json +9 -9
- package/src/completion-context.ts +432 -0
- package/src/completion-provider.ts +454 -0
- package/src/semantic-tokens.ts +9 -12
- package/src/server.ts +98 -5
package/src/server.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { findNearestConfigPathForFile } from '@prisma-next/config-loader';
|
|
|
4
4
|
import type { SymbolTable } from '@prisma-next/psl-parser';
|
|
5
5
|
import { type FormatOptions, format } from '@prisma-next/psl-parser/format';
|
|
6
6
|
import {
|
|
7
|
+
type CompletionItem,
|
|
7
8
|
type Connection,
|
|
8
9
|
type Diagnostic,
|
|
9
10
|
DiagnosticSeverity,
|
|
@@ -11,6 +12,8 @@ import {
|
|
|
11
12
|
type FoldingRange,
|
|
12
13
|
type InitializeParams,
|
|
13
14
|
type InitializeResult,
|
|
15
|
+
type Position,
|
|
16
|
+
type PublishDiagnosticsParams,
|
|
14
17
|
type Range,
|
|
15
18
|
RegistrationRequest,
|
|
16
19
|
type SemanticTokens,
|
|
@@ -19,6 +22,8 @@ import {
|
|
|
19
22
|
type TextEdit,
|
|
20
23
|
} from 'vscode-languageserver';
|
|
21
24
|
import { TextDocument } from 'vscode-languageserver-textdocument';
|
|
25
|
+
import { classifyPslCompletionContext } from './completion-context';
|
|
26
|
+
import { providePslCompletionItems } from './completion-provider';
|
|
22
27
|
import { CONFIG_FILENAME, resolveConfigInputs } from './config-resolution';
|
|
23
28
|
import { ParseDiagnosticSeverity } from './diagnostic-mapping';
|
|
24
29
|
import { computeFoldingRanges } from './folding-ranges';
|
|
@@ -63,6 +68,22 @@ export function createServer(connection: Connection): LanguageServer {
|
|
|
63
68
|
let rootPath = process.cwd();
|
|
64
69
|
let watchedConfigGlob = join(rootPath, '**', CONFIG_FILENAME);
|
|
65
70
|
let supportsWatchedFilesRegistration = false;
|
|
71
|
+
let clientSupportsSnippets = false;
|
|
72
|
+
let disposed = false;
|
|
73
|
+
|
|
74
|
+
function sendDiagnostics(params: PublishDiagnosticsParams): void {
|
|
75
|
+
if (disposed) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
void connection.sendDiagnostics(params);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function logWarn(message: string): void {
|
|
82
|
+
if (disposed) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
connection.console.warn(message);
|
|
86
|
+
}
|
|
66
87
|
|
|
67
88
|
async function publish(uri: string): Promise<void> {
|
|
68
89
|
const project = await resolveProjectForDocument(uri);
|
|
@@ -81,7 +102,7 @@ export function createServer(connection: Connection): LanguageServer {
|
|
|
81
102
|
project.controlStack,
|
|
82
103
|
);
|
|
83
104
|
if (computed === null) {
|
|
84
|
-
|
|
105
|
+
sendDiagnostics({ uri, diagnostics: [] });
|
|
85
106
|
return;
|
|
86
107
|
}
|
|
87
108
|
const diagnostics: Diagnostic[] = computed.map((diagnostic) => ({
|
|
@@ -91,7 +112,7 @@ export function createServer(connection: Connection): LanguageServer {
|
|
|
91
112
|
severity: toLspSeverity(diagnostic.severity),
|
|
92
113
|
source: 'prisma-next',
|
|
93
114
|
}));
|
|
94
|
-
|
|
115
|
+
sendDiagnostics({ uri, diagnostics });
|
|
95
116
|
}
|
|
96
117
|
|
|
97
118
|
async function resolveProjectForDocument(uri: string): Promise<ProjectState | undefined> {
|
|
@@ -191,7 +212,7 @@ export function createServer(connection: Connection): LanguageServer {
|
|
|
191
212
|
if (documentConfigPaths.get(document.uri) === configPath) {
|
|
192
213
|
documentConfigPaths.delete(document.uri);
|
|
193
214
|
if (hadProject) {
|
|
194
|
-
|
|
215
|
+
sendDiagnostics({ uri: document.uri, diagnostics: [] });
|
|
195
216
|
}
|
|
196
217
|
}
|
|
197
218
|
}
|
|
@@ -219,6 +240,9 @@ export function createServer(connection: Connection): LanguageServer {
|
|
|
219
240
|
|
|
220
241
|
function publishSafely(uri: string): void {
|
|
221
242
|
void publish(uri).catch((error: unknown) => {
|
|
243
|
+
if (disposed) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
222
246
|
connection.console.error(error instanceof Error ? error.message : String(error));
|
|
223
247
|
});
|
|
224
248
|
}
|
|
@@ -288,10 +312,72 @@ export function createServer(connection: Connection): LanguageServer {
|
|
|
288
312
|
return buildSemanticTokens(source, range);
|
|
289
313
|
}
|
|
290
314
|
|
|
315
|
+
async function completeDocument(uri: string, position: Position): Promise<CompletionItem[]> {
|
|
316
|
+
const document = documents.get(uri);
|
|
317
|
+
if (document === undefined) {
|
|
318
|
+
return [];
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
let project: ProjectState | undefined;
|
|
322
|
+
try {
|
|
323
|
+
project = await resolveProjectForDocument(uri);
|
|
324
|
+
} catch {
|
|
325
|
+
return [];
|
|
326
|
+
}
|
|
327
|
+
if (project === undefined || !project.inputs.includes(uri)) {
|
|
328
|
+
return [];
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const cached = currentDocumentArtifact(project, uri, document.getText());
|
|
332
|
+
const symbolTable = project.artifacts.getSymbolTable();
|
|
333
|
+
if (cached === undefined || symbolTable === undefined) {
|
|
334
|
+
return [];
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
try {
|
|
338
|
+
const context = classifyPslCompletionContext({
|
|
339
|
+
document: cached.document,
|
|
340
|
+
sourceFile: cached.sourceFile,
|
|
341
|
+
position,
|
|
342
|
+
});
|
|
343
|
+
return [
|
|
344
|
+
...providePslCompletionItems({
|
|
345
|
+
context,
|
|
346
|
+
sourceFile: cached.sourceFile,
|
|
347
|
+
candidates: {
|
|
348
|
+
scalarTypes: project.controlStack.scalarTypes,
|
|
349
|
+
pslBlockDescriptors: project.controlStack.pslBlockDescriptors,
|
|
350
|
+
symbolTable,
|
|
351
|
+
},
|
|
352
|
+
clientSupportsSnippets,
|
|
353
|
+
}),
|
|
354
|
+
];
|
|
355
|
+
} catch {
|
|
356
|
+
return [];
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function currentDocumentArtifact(
|
|
361
|
+
project: ProjectState,
|
|
362
|
+
uri: string,
|
|
363
|
+
text: string,
|
|
364
|
+
): CachedDocument | undefined {
|
|
365
|
+
const cached = project.artifacts.getDocument(uri);
|
|
366
|
+
if (cached?.sourceFile.text === text) {
|
|
367
|
+
return cached;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (project.artifacts.update(uri, text, project.inputs, project.controlStack) === null) {
|
|
371
|
+
return undefined;
|
|
372
|
+
}
|
|
373
|
+
return project.artifacts.getDocument(uri);
|
|
374
|
+
}
|
|
375
|
+
|
|
291
376
|
connection.onInitialize(async (params): Promise<InitializeResult> => {
|
|
292
377
|
rootPath = resolveRootPath(params.rootUri, params.rootPath);
|
|
293
378
|
watchedConfigGlob = join(rootPath, '**', CONFIG_FILENAME);
|
|
294
379
|
supportsWatchedFilesRegistration = clientSupportsWatchedFilesRegistration(params);
|
|
380
|
+
clientSupportsSnippets = clientSupportsCompletionSnippets(params);
|
|
295
381
|
|
|
296
382
|
return {
|
|
297
383
|
capabilities: {
|
|
@@ -303,6 +389,7 @@ export function createServer(connection: Connection): LanguageServer {
|
|
|
303
389
|
full: true,
|
|
304
390
|
range: true,
|
|
305
391
|
},
|
|
392
|
+
completionProvider: { triggerCharacters: ['.'] },
|
|
306
393
|
},
|
|
307
394
|
};
|
|
308
395
|
});
|
|
@@ -321,7 +408,7 @@ export function createServer(connection: Connection): LanguageServer {
|
|
|
321
408
|
})
|
|
322
409
|
.catch(() => undefined);
|
|
323
410
|
} else {
|
|
324
|
-
|
|
411
|
+
logWarn(
|
|
325
412
|
'Client does not support dynamic file-watcher registration; Prisma Next config changes will not be picked up without a restart.',
|
|
326
413
|
);
|
|
327
414
|
}
|
|
@@ -343,6 +430,7 @@ export function createServer(connection: Connection): LanguageServer {
|
|
|
343
430
|
});
|
|
344
431
|
|
|
345
432
|
connection.onDocumentFormatting((params) => formatDocument(params.textDocument.uri));
|
|
433
|
+
connection.onCompletion((params) => completeDocument(params.textDocument.uri, params.position));
|
|
346
434
|
|
|
347
435
|
connection.languages.semanticTokens.on((params) =>
|
|
348
436
|
semanticTokensForDocument(params.textDocument.uri),
|
|
@@ -381,7 +469,7 @@ export function createServer(connection: Connection): LanguageServer {
|
|
|
381
469
|
projects.get(configPath)?.artifacts.remove(uri);
|
|
382
470
|
}
|
|
383
471
|
documentConfigPaths.delete(uri);
|
|
384
|
-
|
|
472
|
+
sendDiagnostics({ uri, diagnostics: [] });
|
|
385
473
|
});
|
|
386
474
|
|
|
387
475
|
documents.listen(connection);
|
|
@@ -394,6 +482,7 @@ export function createServer(connection: Connection): LanguageServer {
|
|
|
394
482
|
|
|
395
483
|
return {
|
|
396
484
|
dispose: () => {
|
|
485
|
+
disposed = true;
|
|
397
486
|
connection.dispose();
|
|
398
487
|
},
|
|
399
488
|
getDocumentAst: (uri) => artifactsForDocument(uri)?.getDocument(uri),
|
|
@@ -422,6 +511,10 @@ function clientSupportsWatchedFilesRegistration(params: InitializeParams): boole
|
|
|
422
511
|
return params.capabilities.workspace?.didChangeWatchedFiles?.dynamicRegistration === true;
|
|
423
512
|
}
|
|
424
513
|
|
|
514
|
+
function clientSupportsCompletionSnippets(params: InitializeParams): boolean {
|
|
515
|
+
return params.capabilities.textDocument?.completion?.completionItem?.snippetSupport === true;
|
|
516
|
+
}
|
|
517
|
+
|
|
425
518
|
function resolveRootPath(
|
|
426
519
|
rootUri: string | null | undefined,
|
|
427
520
|
rootPath: string | null | undefined,
|