Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Made all LSP services potentially asynchronous #269

Merged
merged 2 commits into from
Oct 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/langium/src/default-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
******************************************************************************/

import { TextDocument } from 'vscode-languageserver-textdocument';
import { Connection, TextDocuments } from 'vscode-languageserver/node';
import { Connection, TextDocuments } from 'vscode-languageserver';
import { Module } from './dependency-injection';
import { DefaultLangiumDocumentFactory, DefaultLangiumDocuments, DefaultTextDocumentFactory } from './documents/document';
import { DefaultDocumentBuilder } from './documents/document-builder';
Expand Down
6 changes: 4 additions & 2 deletions packages/langium/src/documents/document-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,22 @@ import { AstNode } from '../syntax-tree';
export interface DocumentBuilder {
/**
* Inserts the document into the index and rebuilds affected documents
*
* @param document which should be built
* @param cancelToken allows to cancel the current operation
* @throws `OperationCanceled` if a user action occurs during execution
* @throws `OperationCancelled` if cancellation is detected during execution
*/
build(document: LangiumDocument, cancelToken?: CancellationToken): Promise<BuildResult>
/**
* This method is called when a document change is detected.
* Implementation should updates the state of that `LangiumDocument` and make sure
* that the index information of the affected documents are also updated.
*
* @param uri of the document that was changed
* @param cancelToken allows to cancel the current operation
* @see IndexManager.update()
* @see LangiumDocuments.invalidateDocument()
* @throws `OperationCanceled` if a user action occurs during execution
* @throws `OperationCancelled` if cancellation is detected during execution
*/
documentChanged(uri: URI, cancelToken?: CancellationToken): Promise<void>;
}
Expand Down
4 changes: 3 additions & 1 deletion packages/langium/src/grammar/langium-grammar-code-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import { CodeActionParams } from 'vscode-languageserver-protocol';
import { Command, CodeAction } from 'vscode-languageserver-types';
import { LangiumDocument } from '../documents/document';
import { CodeActionProvider } from '../lsp/code-action';
import { MaybePromise } from '../utils/promise-util';
import { IssueCodes } from './langium-grammar-validator';

export class LangiumGrammarCodeActionProvider implements CodeActionProvider {
getCodeActions(document: LangiumDocument, params: CodeActionParams): Array<Command | CodeAction> | null {

getCodeActions(document: LangiumDocument, params: CodeActionParams): MaybePromise<Array<Command | CodeAction>> {
const result: CodeAction[] = [];
for (const diagnostic of params.context.diagnostics) {
const codeAction = this.createCodeAction(diagnostic, document);
Expand Down
13 changes: 10 additions & 3 deletions packages/langium/src/lsp/code-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@
* terms of the MIT License, which is available in the project root.
******************************************************************************/

import { CodeAction, CodeActionParams, Command } from 'vscode-languageserver';
import { CancellationToken, CodeAction, CodeActionParams, Command } from 'vscode-languageserver';
import { LangiumDocument } from '../documents/document';
import { MaybePromise } from '../utils/promise-util';

export interface CodeActionProvider {
getCodeActions(document: LangiumDocument, params: CodeActionParams): Array<Command | CodeAction> | null;
}
/**
* Handle a code action request.
*
* @throws `OperationCancelled` if cancellation is detected during execution
* @throws `ResponseError` if an error is detected that should be sent as response to the client
*/
getCodeActions(document: LangiumDocument, params: CodeActionParams, cancelToken?: CancellationToken): MaybePromise<Array<Command | CodeAction> | undefined>;
}
18 changes: 13 additions & 5 deletions packages/langium/src/lsp/completion/completion-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
* terms of the MIT License, which is available in the project root.
******************************************************************************/

import { CompletionItem, CompletionItemKind, CompletionList } from 'vscode-languageserver';
import { CancellationToken, CompletionItem, CompletionItemKind, CompletionList, CompletionParams } from 'vscode-languageserver';
import { TextDocument, TextEdit } from 'vscode-languageserver-textdocument';
import { LangiumDocument } from '../../documents/document';
import * as ast from '../../grammar/generated/ast';
import { getTypeNameAtElement } from '../../grammar/grammar-util';
import { isNamed } from '../../references/naming';
Expand All @@ -14,14 +15,21 @@ import { LangiumServices } from '../../services';
import { AstNode, CstNode } from '../../syntax-tree';
import { getContainerOfType, isAstNode, findLeafNodeAtOffset } from '../../utils/ast-util';
import { findRelevantNode, flatten } from '../../utils/cst-util';
import { MaybePromise } from '../../utils/promise-util';
import { stream } from '../../utils/stream';
import { findFirstFeatures, findNextFeatures } from './follow-element-computation';
import { RuleInterpreter } from './rule-interpreter';

export type CompletionAcceptor = (value: string | AstNode | AstNodeDescription, item?: Partial<CompletionItem>) => void

export interface CompletionProvider {
getCompletion(root: AstNode, offset: number): CompletionList
/**
* Handle a completion request.
*
* @throws `OperationCancelled` if cancellation is detected during execution
* @throws `ResponseError` if an error is detected that should be sent as response to the client
*/
getCompletion(document: LangiumDocument, offset: number, params: CompletionParams, cancelToken?: CancellationToken): MaybePromise<CompletionList>
}

export class DefaultCompletionProvider {
Expand All @@ -36,12 +44,12 @@ export class DefaultCompletionProvider {
this.grammar = services.Grammar;
}

getCompletion(root: AstNode, offset: number): CompletionList {
getCompletion(document: LangiumDocument, offset: number): MaybePromise<CompletionList> {
const root = document.parseResult.value;
const cst = root.$cstNode;
const items: CompletionItem[] = [];
const acceptor = (value: string | AstNode | AstNodeDescription, item?: Partial<CompletionItem>) => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const completionItem = this.fillCompletionItem(root.$document!.textDocument, offset, value, item);
const completionItem = this.fillCompletionItem(document.textDocument, offset, value, item);
if (completionItem) {
items.push(completionItem);
}
Expand Down
48 changes: 34 additions & 14 deletions packages/langium/src/lsp/document-highlighter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,24 @@
* terms of the MIT License, which is available in the project root.
******************************************************************************/

import * as vscodeLanguageserver from 'vscode-languageserver';
import { CancellationToken, DocumentHighlight, DocumentHighlightKind, DocumentHighlightParams } from 'vscode-languageserver';
import { LangiumDocument } from '../documents/document';
import { NameProvider } from '../references/naming';
import { References } from '../references/references';
import { LangiumServices } from '../services';
import { AstNode, CstNode } from '../syntax-tree';
import { AstNode, CstNode, Reference } from '../syntax-tree';
import { findLeafNodeAtOffset, findLocalReferences, getDocument } from '../utils/ast-util';
import { toRange } from '../utils/cst-util';
import { MaybePromise } from '../utils/promise-util';

export interface DocumentHighlighter {
findHighlights(document: LangiumDocument, params: vscodeLanguageserver.DocumentHighlightParams): vscodeLanguageserver.Location[];
/**
* Handle a document highlight request.
*
* @throws `OperationCancelled` if cancellation is detected during execution
* @throws `ResponseError` if an error is detected that should be sent as response to the client
*/
findHighlights(document: LangiumDocument, params: DocumentHighlightParams, cancelToken?: CancellationToken): MaybePromise<DocumentHighlight[] | undefined>;
}

export class DefaultDocumentHighlighter implements DocumentHighlighter {
Expand All @@ -26,37 +33,50 @@ export class DefaultDocumentHighlighter implements DocumentHighlighter {
this.nameProvider = services.references.NameProvider;
}

findHighlights(document: LangiumDocument, params: vscodeLanguageserver.DocumentHighlightParams): vscodeLanguageserver.Location[] {
findHighlights(document: LangiumDocument, params: DocumentHighlightParams): MaybePromise<DocumentHighlight[] | undefined> {
const rootNode = document.parseResult.value.$cstNode;
if (!rootNode) {
return [];
return undefined;
}
const refs: CstNode[] = [];
const selectedNode = findLeafNodeAtOffset(rootNode, document.textDocument.offsetAt(params.position));
if (!selectedNode) {
return [];
return undefined;
}
const targetAstNode = this.references.findDeclaration(selectedNode)?.element;
if (targetAstNode) {
const refs: Array<[CstNode, DocumentHighlightKind]> = [];
if (getDocument(targetAstNode).uri.toString() === document.uri.toString()) {
const nameNode = this.findNameNode(targetAstNode);
if (nameNode) {
refs.push(nameNode);
refs.push([nameNode, this.getHighlightKind(nameNode)]);
}
}
findLocalReferences(targetAstNode, rootNode.element).forEach((element) => {
refs.push(element.$refNode);
findLocalReferences(targetAstNode, rootNode.element).forEach(ref => {
refs.push([ref.$refNode, this.getHighlightKind(ref.$refNode, ref)]);
});
return refs.map(([node, kind]) =>
DocumentHighlight.create(toRange(node, document), kind)
);
}
return refs.map(node => vscodeLanguageserver.Location.create(
document.textDocument.uri,
toRange(node, document)
));
return undefined;
}

protected findNameNode(node: AstNode): CstNode | undefined {
const nameNode = this.nameProvider.getNameNode(node);
if (nameNode)
return nameNode;
return node.$cstNode;
}

/**
* Override this method to determine the highlight kind of the given CST node.
*/
protected getHighlightKind(node: CstNode, reference?: Reference<AstNode>): DocumentHighlightKind {
if (reference) {
return DocumentHighlightKind.Read;
} else {
return DocumentHighlightKind.Text;
}
}

}
13 changes: 10 additions & 3 deletions packages/langium/src/lsp/document-symbol-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,22 @@
* terms of the MIT License, which is available in the project root.
******************************************************************************/

import { DocumentSymbol, SymbolKind } from 'vscode-languageserver';
import { CancellationToken, DocumentSymbol, DocumentSymbolParams, SymbolKind } from 'vscode-languageserver';
import { LangiumDocument } from '../documents/document';
import { NameProvider } from '../references/naming';
import { LangiumServices } from '../services';
import { AstNode } from '../syntax-tree';
import { streamContents } from '../utils/ast-util';
import { MaybePromise } from '../utils/promise-util';

export interface DocumentSymbolProvider {
getSymbols(document: LangiumDocument): DocumentSymbol[];
/**
* Handle a document symbols request.
*
* @throws `OperationCancelled` if cancellation is detected during execution
* @throws `ResponseError` if an error is detected that should be sent as response to the client
*/
getSymbols(document: LangiumDocument, params: DocumentSymbolParams, cancelToken?: CancellationToken): MaybePromise<DocumentSymbol[]>;
}

export class DefaultDocumentSymbolProvider implements DocumentSymbolProvider {
Expand All @@ -23,7 +30,7 @@ export class DefaultDocumentSymbolProvider implements DocumentSymbolProvider {
this.nameProvider = services.references.NameProvider;
}

getSymbols(document: LangiumDocument): DocumentSymbol[] {
getSymbols(document: LangiumDocument): MaybePromise<DocumentSymbol[]> {
return this.getSymbol(document, document.parseResult.value);
}

Expand Down
13 changes: 10 additions & 3 deletions packages/langium/src/lsp/folding-range-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,23 @@
* terms of the MIT License, which is available in the project root.
******************************************************************************/

import { FoldingRange, FoldingRangeKind } from 'vscode-languageserver';
import { CancellationToken, FoldingRange, FoldingRangeKind, FoldingRangeParams } from 'vscode-languageserver';
import { LangiumDocument } from '../documents/document';
import { LeafCstNodeImpl } from '../parser/cst-node-builder';
import { LangiumServices } from '../services';
import { AstNode, CstNode } from '../syntax-tree';
import { AstNodeContent, streamAllContents } from '../utils/ast-util';
import { streamCst } from '../utils/cst-util';
import { MaybePromise } from '../utils/promise-util';

export interface FoldingRangeProvider {
getFoldingRanges(document: LangiumDocument): FoldingRange[]
/**
* Handle a folding range request.
*
* @throws `OperationCancelled` if cancellation is detected during execution
* @throws `ResponseError` if an error is detected that should be sent as response to the client
*/
getFoldingRanges(document: LangiumDocument, params: FoldingRangeParams, cancelToken?: CancellationToken): MaybePromise<FoldingRange[]>;
}

export type FoldingRangeAcceptor = (foldingRange: FoldingRange) => void;
Expand All @@ -26,7 +33,7 @@ export class DefaultFoldingRangeProvider implements FoldingRangeProvider {
this.commentNames = services.parser.GrammarConfig.multilineCommentRules;
}

getFoldingRanges(document: LangiumDocument): FoldingRange[] {
getFoldingRanges(document: LangiumDocument): MaybePromise<FoldingRange[]> {
const foldings: FoldingRange[] = [];
const acceptor: FoldingRangeAcceptor = (foldingRange) => foldings.push(foldingRange);
this.collectFolding(document, acceptor);
Expand Down
13 changes: 10 additions & 3 deletions packages/langium/src/lsp/goto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,24 @@
* terms of the MIT License, which is available in the project root.
******************************************************************************/

import { LocationLink, TextDocumentPositionParams } from 'vscode-languageserver';
import { CancellationToken, DefinitionParams, LocationLink } from 'vscode-languageserver';
import { LangiumDocument } from '../documents/document';
import { NameProvider } from '../references/naming';
import { References } from '../references/references';
import { LangiumServices } from '../services';
import { CstNode } from '../syntax-tree';
import { findLeafNodeAtOffset, getDocument } from '../utils/ast-util';
import { toRange } from '../utils/cst-util';
import { MaybePromise } from '../utils/promise-util';

export interface GoToResolver {
goToDefinition(document: LangiumDocument, position: TextDocumentPositionParams): LocationLink[]
/**
* Handle a go to definition request.
*
* @throws `OperationCancelled` if cancellation is detected during execution
* @throws `ResponseError` if an error is detected that should be sent as response to the client
*/
goToDefinition(document: LangiumDocument, params: DefinitionParams, cancelToken?: CancellationToken): MaybePromise<LocationLink[] | undefined>;
}

export class DefaultGoToResolverProvider implements GoToResolver {
Expand All @@ -27,7 +34,7 @@ export class DefaultGoToResolverProvider implements GoToResolver {
this.references = services.references.References;
}

goToDefinition(document: LangiumDocument, params: TextDocumentPositionParams): LocationLink[] {
goToDefinition(document: LangiumDocument, params: DefinitionParams): MaybePromise<LocationLink[] | undefined> {
const rootNode = document.parseResult.value;
const targetCstNodes: Array<{ source: CstNode, target: CstNode, targetDocument: LangiumDocument }> = [];
if (rootNode.$cstNode) {
Expand Down
21 changes: 14 additions & 7 deletions packages/langium/src/lsp/hover-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,24 @@
* terms of the MIT License, which is available in the project root.
******************************************************************************/

import { Hover, HoverParams } from 'vscode-languageserver';
import { CancellationToken, Hover, HoverParams } from 'vscode-languageserver';
import { LangiumDocument } from '../documents/document';
import { GrammarConfig } from '../grammar/grammar-config';
import { References } from '../references/references';
import { LangiumServices } from '../services';
import { AstNode } from '../syntax-tree';
import { findLeafNodeAtOffset } from '../utils/ast-util';
import { findCommentNode } from '../utils/cst-util';
import { MaybePromise } from '../utils/promise-util';

export interface HoverProvider {
getHoverContent(document: LangiumDocument, params: HoverParams): Hover | undefined;
/**
* Handle a hover request.
*
* @throws `OperationCancelled` if cancellation is detected during execution
* @throws `ResponseError` if an error is detected that should be sent as response to the client
*/
getHoverContent(document: LangiumDocument, params: HoverParams, cancelToken?: CancellationToken): MaybePromise<Hover | undefined>;
}

export abstract class AstNodeHoverProvider implements HoverProvider {
Expand All @@ -25,7 +32,7 @@ export abstract class AstNodeHoverProvider implements HoverProvider {
this.references = services.references.References;
}

getHoverContent(document: LangiumDocument, params: HoverParams): Hover | undefined {
getHoverContent(document: LangiumDocument, params: HoverParams): MaybePromise<Hover | undefined> {
const rootNode = document.parseResult?.value?.$cstNode;
if (rootNode) {
const offset = document.textDocument.offsetAt(params.position);
Expand All @@ -37,10 +44,10 @@ export abstract class AstNodeHoverProvider implements HoverProvider {
}
}
}
return;
return undefined;
}

abstract getAstNodeHoverContent(node: AstNode): Hover | undefined;
protected abstract getAstNodeHoverContent(node: AstNode): MaybePromise<Hover | undefined>;

}

Expand All @@ -54,7 +61,7 @@ export class MultilineCommentHoverProvider extends AstNodeHoverProvider {
this.grammarConfig = services.parser.GrammarConfig;
}

getAstNodeHoverContent(node: AstNode): Hover | undefined {
protected getAstNodeHoverContent(node: AstNode): MaybePromise<Hover | undefined> {
const lastNode = findCommentNode(node.$cstNode, this.grammarConfig.multilineCommentRules);
let content: string | undefined;
if (lastNode) {
Expand All @@ -74,7 +81,7 @@ export class MultilineCommentHoverProvider extends AstNodeHoverProvider {
return undefined;
}

getCommentContent(commentText: string): string {
protected getCommentContent(commentText: string): string {
const split = commentText.split('\n').map(e => {
e = e.trim();
if (e.startsWith('*')) {
Expand Down
Loading