Skip to content

Commit 87715d1

Browse files
authored
Ensure expected document state in LSP responses (#1334)
1 parent 60934de commit 87715d1

File tree

4 files changed

+256
-46
lines changed

4 files changed

+256
-46
lines changed

packages/langium/src/lsp/language-server.ts

Lines changed: 101 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -313,110 +313,127 @@ export function addCompletionHandler(connection: Connection, services: LangiumSh
313313
(services, document, params, cancelToken) => {
314314
return services.lsp?.CompletionProvider?.getCompletion(document, params, cancelToken);
315315
},
316-
services
316+
services,
317+
DocumentState.IndexedReferences
317318
));
318319
}
319320

320321
export function addFindReferencesHandler(connection: Connection, services: LangiumSharedServices): void {
321322
connection.onReferences(createRequestHandler(
322323
(services, document, params, cancelToken) => services.lsp?.ReferencesProvider?.findReferences(document, params, cancelToken),
323-
services
324+
services,
325+
DocumentState.IndexedReferences
324326
));
325327
}
326328

327329
export function addCodeActionHandler(connection: Connection, services: LangiumSharedServices): void {
328330
connection.onCodeAction(createRequestHandler(
329331
(services, document, params, cancelToken) => services.lsp?.CodeActionProvider?.getCodeActions(document, params, cancelToken),
330-
services
332+
services,
333+
DocumentState.Validated
331334
));
332335
}
333336

334337
export function addDocumentSymbolHandler(connection: Connection, services: LangiumSharedServices): void {
335338
connection.onDocumentSymbol(createRequestHandler(
336339
(services, document, params, cancelToken) => services.lsp?.DocumentSymbolProvider?.getSymbols(document, params, cancelToken),
337-
services
340+
services,
341+
DocumentState.Parsed
338342
));
339343
}
340344

341345
export function addGotoDefinitionHandler(connection: Connection, services: LangiumSharedServices): void {
342346
connection.onDefinition(createRequestHandler(
343347
(services, document, params, cancelToken) => services.lsp?.DefinitionProvider?.getDefinition(document, params, cancelToken),
344-
services
348+
services,
349+
DocumentState.IndexedReferences
345350
));
346351
}
347352

348353
export function addGoToTypeDefinitionHandler(connection: Connection, services: LangiumSharedServices): void {
349354
connection.onTypeDefinition(createRequestHandler(
350355
(services, document, params, cancelToken) => services.lsp?.TypeProvider?.getTypeDefinition(document, params, cancelToken),
351-
services
356+
services,
357+
DocumentState.IndexedReferences
352358
));
353359
}
354360

355361
export function addGoToImplementationHandler(connection: Connection, services: LangiumSharedServices) {
356362
connection.onImplementation(createRequestHandler(
357363
(services, document, params, cancelToken) => services.lsp?.ImplementationProvider?.getImplementation(document, params, cancelToken),
358-
services
364+
services,
365+
DocumentState.IndexedReferences
359366
));
360367
}
361368

362369
export function addGoToDeclarationHandler(connection: Connection, services: LangiumSharedServices) {
363370
connection.onDeclaration(createRequestHandler(
364371
(services, document, params, cancelToken) => services.lsp?.DeclarationProvider?.getDeclaration(document, params, cancelToken),
365-
services
372+
services,
373+
DocumentState.IndexedReferences
366374
));
367375
}
368376

369377
export function addDocumentHighlightsHandler(connection: Connection, services: LangiumSharedServices): void {
370378
connection.onDocumentHighlight(createRequestHandler(
371379
(services, document, params, cancelToken) => services.lsp?.DocumentHighlightProvider?.getDocumentHighlight(document, params, cancelToken),
372-
services
380+
services,
381+
DocumentState.IndexedReferences
373382
));
374383
}
375384

376385
export function addHoverHandler(connection: Connection, services: LangiumSharedServices): void {
377386
connection.onHover(createRequestHandler(
378387
(services, document, params, cancelToken) => services.lsp?.HoverProvider?.getHoverContent(document, params, cancelToken),
379-
services
388+
services,
389+
DocumentState.IndexedReferences
380390
));
381391
}
382392

383393
export function addFoldingRangeHandler(connection: Connection, services: LangiumSharedServices): void {
384394
connection.onFoldingRanges(createRequestHandler(
385395
(services, document, params, cancelToken) => services.lsp?.FoldingRangeProvider?.getFoldingRanges(document, params, cancelToken),
386-
services
396+
services,
397+
DocumentState.Parsed
387398
));
388399
}
389400

390401
export function addFormattingHandler(connection: Connection, services: LangiumSharedServices): void {
391402
connection.onDocumentFormatting(createRequestHandler(
392403
(services, document, params, cancelToken) => services.lsp?.Formatter?.formatDocument(document, params, cancelToken),
393-
services
404+
services,
405+
DocumentState.Parsed
394406
));
395407
connection.onDocumentRangeFormatting(createRequestHandler(
396408
(services, document, params, cancelToken) => services.lsp?.Formatter?.formatDocumentRange(document, params, cancelToken),
397-
services
409+
services,
410+
DocumentState.Parsed
398411
));
399412
connection.onDocumentOnTypeFormatting(createRequestHandler(
400413
(services, document, params, cancelToken) => services.lsp?.Formatter?.formatDocumentOnType(document, params, cancelToken),
401-
services
414+
services,
415+
DocumentState.Parsed
402416
));
403417
}
404418

405419
export function addRenameHandler(connection: Connection, services: LangiumSharedServices): void {
406420
connection.onRenameRequest(createRequestHandler(
407421
(services, document, params, cancelToken) => services.lsp?.RenameProvider?.rename(document, params, cancelToken),
408-
services
422+
services,
423+
DocumentState.IndexedReferences
409424
));
410425
connection.onPrepareRename(createRequestHandler(
411426
(services, document, params, cancelToken) => services.lsp?.RenameProvider?.prepareRename(document, params, cancelToken),
412-
services
427+
services,
428+
DocumentState.IndexedReferences
413429
));
414430
}
415431

416432
export function addInlayHintHandler(connection: Connection, services: LangiumSharedServices): void {
417433
connection.languages.inlayHint.on(createServerRequestHandler(
418434
(services, document, params, cancelToken) => services.lsp?.InlayHintProvider?.getInlayHints(document, params, cancelToken),
419-
services
435+
services,
436+
DocumentState.IndexedReferences
420437
));
421438
}
422439

@@ -430,7 +447,8 @@ export function addSemanticTokenHandler(connection: Connection, services: Langiu
430447
}
431448
return emptyResult;
432449
},
433-
services
450+
services,
451+
DocumentState.IndexedReferences
434452
));
435453
connection.languages.semanticTokens.onDelta(createServerRequestHandler<SemanticTokensDeltaParams, SemanticTokens | SemanticTokensDelta, SemanticTokensDeltaPartialResult, void>(
436454
(services, document, params, cancelToken) => {
@@ -439,7 +457,8 @@ export function addSemanticTokenHandler(connection: Connection, services: Langiu
439457
}
440458
return emptyResult;
441459
},
442-
services
460+
services,
461+
DocumentState.IndexedReferences
443462
));
444463
connection.languages.semanticTokens.onRange(createServerRequestHandler<SemanticTokensRangeParams, SemanticTokens, SemanticTokensPartialResult, void>(
445464
(services, document, params, cancelToken) => {
@@ -448,7 +467,8 @@ export function addSemanticTokenHandler(connection: Connection, services: Langiu
448467
}
449468
return emptyResult;
450469
},
451-
services
470+
services,
471+
DocumentState.IndexedReferences
452472
));
453473
}
454474
export function addConfigurationChangeHandler(connection: Connection, services: LangiumSharedServices): void {
@@ -475,29 +495,34 @@ export function addExecuteCommandHandler(connection: Connection, services: Langi
475495
export function addDocumentLinkHandler(connection: Connection, services: LangiumSharedServices): void {
476496
connection.onDocumentLinks(createServerRequestHandler(
477497
(services, document, params, cancelToken) => services.lsp?.DocumentLinkProvider?.getDocumentLinks(document, params, cancelToken),
478-
services
498+
services,
499+
DocumentState.Parsed
479500
));
480501
}
481502

482503
export function addSignatureHelpHandler(connection: Connection, services: LangiumSharedServices): void {
483504
connection.onSignatureHelp(createServerRequestHandler(
484505
(services, document, params, cancelToken) => services.lsp?.SignatureHelp?.provideSignatureHelp(document, params, cancelToken),
485-
services
506+
services,
507+
DocumentState.IndexedReferences
486508
));
487509
}
488510

489511
export function addCodeLensHandler(connection: Connection, services: LangiumSharedServices): void {
490512
connection.onCodeLens(createServerRequestHandler(
491513
(services, document, params, cancelToken) => services.lsp?.CodeLensProvider?.provideCodeLens(document, params, cancelToken),
492-
services
514+
services,
515+
DocumentState.IndexedReferences
493516
));
494517
}
495518

496519
export function addWorkspaceSymbolHandler(connection: Connection, services: LangiumSharedServices): void {
497520
const workspaceSymbolProvider = services.lsp.WorkspaceSymbolProvider;
498521
if (workspaceSymbolProvider) {
522+
const documentBuilder = services.workspace.DocumentBuilder;
499523
connection.onWorkspaceSymbol(async (params, token) => {
500524
try {
525+
await documentBuilder.waitUntil(DocumentState.IndexedContent, token);
501526
return await workspaceSymbolProvider.getSymbols(params, token);
502527
} catch (err) {
503528
return responseError(err);
@@ -507,6 +532,7 @@ export function addWorkspaceSymbolHandler(connection: Connection, services: Lang
507532
if (resolveWorkspaceSymbol) {
508533
connection.onWorkspaceSymbolResolve(async (workspaceSymbol, token) => {
509534
try {
535+
await documentBuilder.waitUntil(DocumentState.IndexedContent, token);
510536
return await resolveWorkspaceSymbol(workspaceSymbol, token);
511537
} catch (err) {
512538
return responseError(err);
@@ -525,7 +551,8 @@ export function addCallHierarchyHandler(connection: Connection, services: Langiu
525551
}
526552
return null;
527553
},
528-
services
554+
services,
555+
DocumentState.IndexedReferences
529556
));
530557

531558
connection.languages.callHierarchy.onIncomingCalls(createHierarchyRequestHandler(
@@ -558,24 +585,34 @@ export function addTypeHierarchyHandler(connection: Connection, sharedServices:
558585
}
559586

560587
connection.languages.typeHierarchy.onPrepare(
561-
createServerRequestHandler(async (services, document, params, cancelToken) => {
562-
const result = await services.lsp?.TypeHierarchyProvider?.prepareTypeHierarchy(document, params, cancelToken);
563-
return result ?? null;
564-
}, sharedServices),
588+
createServerRequestHandler(
589+
async (services, document, params, cancelToken) => {
590+
const result = await services.lsp?.TypeHierarchyProvider?.prepareTypeHierarchy(document, params, cancelToken);
591+
return result ?? null;
592+
},
593+
sharedServices,
594+
DocumentState.IndexedReferences
595+
),
565596
);
566597

567598
connection.languages.typeHierarchy.onSupertypes(
568-
createHierarchyRequestHandler(async (services, params, cancelToken) => {
569-
const result = await services.lsp?.TypeHierarchyProvider?.supertypes(params, cancelToken);
570-
return result ?? null;
571-
}, sharedServices),
599+
createHierarchyRequestHandler(
600+
async (services, params, cancelToken) => {
601+
const result = await services.lsp?.TypeHierarchyProvider?.supertypes(params, cancelToken);
602+
return result ?? null;
603+
},
604+
sharedServices
605+
),
572606
);
573607

574608
connection.languages.typeHierarchy.onSubtypes(
575-
createHierarchyRequestHandler(async (services, params, cancelToken) => {
576-
const result = await services.lsp?.TypeHierarchyProvider?.subtypes(params, cancelToken);
577-
return result ?? null;
578-
}, sharedServices),
609+
createHierarchyRequestHandler(
610+
async (services, params, cancelToken) => {
611+
const result = await services.lsp?.TypeHierarchyProvider?.subtypes(params, cancelToken);
612+
return result ?? null;
613+
},
614+
sharedServices
615+
),
579616
);
580617
}
581618

@@ -586,6 +623,10 @@ export function createHierarchyRequestHandler<P extends TypeHierarchySupertypesP
586623
const serviceRegistry = sharedServices.ServiceRegistry;
587624
return async (params: P, cancelToken: CancellationToken) => {
588625
const uri = URI.parse(params.item.uri);
626+
const cancellationError = await waitUntilPhase<E>(sharedServices, cancelToken, uri, DocumentState.IndexedReferences);
627+
if (cancellationError) {
628+
return cancellationError;
629+
}
589630
const language = serviceRegistry.getServices(uri);
590631
if (!language) {
591632
const message = `Could not find service instance for uri: '${uri.toString()}'`;
@@ -602,12 +643,17 @@ export function createHierarchyRequestHandler<P extends TypeHierarchySupertypesP
602643

603644
export function createServerRequestHandler<P extends { textDocument: TextDocumentIdentifier }, R, PR, E = void>(
604645
serviceCall: (services: LangiumCoreAndPartialLSPServices, document: LangiumDocument, params: P, cancelToken: CancellationToken) => HandlerResult<R, E>,
605-
sharedServices: LangiumSharedServices
646+
sharedServices: LangiumSharedServices,
647+
targetState?: DocumentState
606648
): ServerRequestHandler<P, R, PR, E> {
607649
const documents = sharedServices.workspace.LangiumDocuments;
608650
const serviceRegistry = sharedServices.ServiceRegistry;
609651
return async (params: P, cancelToken: CancellationToken) => {
610652
const uri = URI.parse(params.textDocument.uri);
653+
const cancellationError = await waitUntilPhase<E>(sharedServices, cancelToken, uri, targetState);
654+
if (cancellationError) {
655+
return cancellationError;
656+
}
611657
const language = serviceRegistry.getServices(uri);
612658
if (!language) {
613659
const errorText = `Could not find service instance for uri: '${uri}'`;
@@ -625,12 +671,17 @@ export function createServerRequestHandler<P extends { textDocument: TextDocumen
625671

626672
export function createRequestHandler<P extends { textDocument: TextDocumentIdentifier }, R, E = void>(
627673
serviceCall: (services: LangiumCoreAndPartialLSPServices, document: LangiumDocument, params: P, cancelToken: CancellationToken) => HandlerResult<R, E>,
628-
sharedServices: LangiumSharedServices
674+
sharedServices: LangiumSharedServices,
675+
targetState?: DocumentState
629676
): RequestHandler<P, R | null, E> {
630677
const documents = sharedServices.workspace.LangiumDocuments;
631678
const serviceRegistry = sharedServices.ServiceRegistry;
632679
return async (params: P, cancelToken: CancellationToken) => {
633680
const uri = URI.parse(params.textDocument.uri);
681+
const cancellationError = await waitUntilPhase<E>(sharedServices, cancelToken, uri, targetState);
682+
if (cancellationError) {
683+
return cancellationError;
684+
}
634685
const language = serviceRegistry.getServices(uri);
635686
if (!language) {
636687
console.error(`Could not find service instance for uri: '${uri.toString()}'`);
@@ -648,6 +699,18 @@ export function createRequestHandler<P extends { textDocument: TextDocumentIdent
648699
};
649700
}
650701

702+
async function waitUntilPhase<E>(services: LangiumSharedServices, cancelToken: CancellationToken, uri?: URI, targetState?: DocumentState): Promise<ResponseError<E> | undefined> {
703+
if (targetState !== undefined) {
704+
const documentBuilder = services.workspace.DocumentBuilder;
705+
try {
706+
await documentBuilder.waitUntil(targetState, uri, cancelToken);
707+
} catch (err) {
708+
return responseError(err);
709+
}
710+
}
711+
return undefined;
712+
}
713+
651714
function responseError<E = void>(err: unknown): ResponseError<E> {
652715
if (isOperationCancelled(err)) {
653716
return new ResponseError(LSPErrorCodes.RequestCancelled, 'The request has been cancelled.');

0 commit comments

Comments
 (0)