langium
Version:
A language engineering tool for the Language Server Protocol
270 lines • 14.6 kB
TypeScript
/******************************************************************************
* Copyright 2021 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/
/**
* Re-export 'TextDocument' from 'vscode-languageserver-textdocument' for convenience,
* including both type _and_ symbol (namespace), as we here and there also refer to the symbol,
* the overhead is very small, just a few kilobytes.
* Everything else of that package (at the time contributing) is also defined
* in 'vscode-languageserver-protocol' or 'vscode-languageserver-types'.
*/
export { TextDocument } from 'vscode-languageserver-textdocument';
import type { Diagnostic, Range } from 'vscode-languageserver-types';
import type { FileSystemProvider } from './file-system-provider.js';
import type { ParseResult, ParserOptions } from '../parser/langium-parser.js';
import type { ServiceRegistry } from '../service-registry.js';
import type { LangiumSharedCoreServices } from '../services.js';
import type { AstNode, AstNodeDescription, MultiReference, Mutable, Reference } from '../syntax-tree.js';
import type { Stream } from '../utils/stream.js';
import { TextDocument } from './documents.js';
import { CancellationToken } from '../utils/cancellation.js';
import { URI, UriTrie } from '../utils/uri-utils.js';
/**
* A Langium document holds the parse result (AST and CST) and any additional state that is derived
* from the AST, e.g. the result of scope precomputation.
*/
export interface LangiumDocument<T extends AstNode = AstNode> {
/** The Uniform Resource Identifier (URI) of the document */
readonly uri: URI;
/** The text document used to convert between offsets and positions */
readonly textDocument: TextDocument;
/** The current state of the document */
state: DocumentState;
/** The parse result holds the Abstract Syntax Tree (AST) and potentially also parser / lexer errors */
parseResult: ParseResult<T>;
/** Result of the scope precomputation phase */
localSymbols?: LocalSymbols;
/** An array of all cross-references found in the AST while linking */
references: Array<Reference | MultiReference>;
/** Result of the validation phase */
diagnostics?: Diagnostic[];
}
/**
* A document is subject to several phases that are run in predefined order. Any state value implies that
* smaller state values are finished as well.
*/
export declare enum DocumentState {
/**
* The text content has changed and needs to be parsed again. The AST held by this outdated
* document instance is no longer valid.
*/
Changed = 0,
/**
* An AST has been created from the text content. The document structure can be traversed,
* but cross-references cannot be resolved yet. If necessary, the structure can be manipulated
* at this stage as a preprocessing step.
*/
Parsed = 1,
/**
* The `IndexManager` service has processed AST nodes of this document. This means the
* exported symbols are available in the global scope and can be resolved from other documents.
*/
IndexedContent = 2,
/**
* The `ScopeComputation` service has processed this document. This means the document's locally accessible
* symbols are captured in a `DocumentSymbols` table and can be looked up by the `ScopeProvider` service.
* Once a document has reached this state, you may follow every reference - it will lazily
* resolve its `ref` property and yield either the target AST node or `undefined` in case
* the target is not in scope.
*/
ComputedScopes = 3,
/**
* The `Linker` service has processed this document. All outgoing references have been
* resolved or marked as erroneous.
*/
Linked = 4,
/**
* The `IndexManager` service has processed AST node references of this document. This is
* necessary to determine which documents are affected by a change in one of the workspace
* documents.
*/
IndexedReferences = 5,
/**
* The `DocumentValidator` service has processed this document. The language server listens
* to the results of this phase and sends diagnostics to the client.
*/
Validated = 6
}
/**
* Result of the scope pre-computation phase performed by the `ScopeComputation` service.
* It maps AST nodes of a document to their corresponding sets of symbols that are accessible
* by those nodes/subtrees, provided any symbols corresponding specifically to those nodes/subtrees exist.
* The sets of symbols are assumed to be un-ordered. Hence, no assumptions about the order of
* symbols in the sets should be made. The default `ScopeComputation` implementation uses an
* instance of `MultiMap<AstNode, AstNodeDescription>`, which conforms to this interface.
*/
export interface LocalSymbols {
has(node: AstNode): boolean;
getStream(key: AstNode): Stream<AstNodeDescription>;
}
export interface DocumentSegment {
readonly range: Range;
readonly offset: number;
readonly length: number;
readonly end: number;
}
/**
* Surrogate definition of the `TextDocuments` interface from the `vscode-languageserver` package.
* No implementation object is expected to be offered by `LangiumCoreServices`, but only by `LangiumLSPServices`.
*/
export type TextDocumentProvider = {
get(uri: string | URI): TextDocument | undefined;
};
/**
* Shared service for creating `LangiumDocument` instances.
*
* Register a custom implementation if special (additional) behavior is required for your language(s).
* Note: If you specialize {@link fromString} or {@link fromTextDocument} you probably might want to
* specialize {@link update}, too!
*/
export interface LangiumDocumentFactory {
/**
* Create a Langium document from a `TextDocument` (usually associated with a file).
*/
fromTextDocument<T extends AstNode = AstNode>(textDocument: TextDocument, uri?: URI, options?: ParserOptions): LangiumDocument<T>;
/**
* Create a Langium document from a `TextDocument` asynchronously. This action can be cancelled if a cancellable parser implementation has been provided.
*/
fromTextDocument<T extends AstNode = AstNode>(textDocument: TextDocument, uri: URI | undefined, cancellationToken: CancellationToken): Promise<LangiumDocument<T>>;
/**
* Create an Langium document from an in-memory string.
*/
fromString<T extends AstNode = AstNode>(text: string, uri: URI, options?: ParserOptions): LangiumDocument<T>;
/**
* Create a Langium document from an in-memory string asynchronously. This action can be cancelled if a cancellable parser implementation has been provided.
*/
fromString<T extends AstNode = AstNode>(text: string, uri: URI, cancellationToken: CancellationToken): Promise<LangiumDocument<T>>;
/**
* Create an Langium document from a model that has been constructed in memory.
*/
fromModel<T extends AstNode = AstNode>(model: T, uri: URI): LangiumDocument<T>;
/**
* Create an Langium document from a specified `URI`. The factory will use the `FileSystemAccess` service to read the file.
*/
fromUri<T extends AstNode = AstNode>(uri: URI, cancellationToken?: CancellationToken): Promise<LangiumDocument<T>>;
/**
* Update the given document after changes in the corresponding textual representation.
* Method is called by the document builder after it has been requested to build an existing
* document and the document's state is {@link DocumentState.Changed}.
* The text parsing is expected to be done the same way as in {@link fromTextDocument}
* and {@link fromString}.
*/
update<T extends AstNode = AstNode>(document: LangiumDocument<T>, cancellationToken: CancellationToken): Promise<LangiumDocument<T>>;
}
export declare class DefaultLangiumDocumentFactory implements LangiumDocumentFactory {
protected readonly serviceRegistry: ServiceRegistry;
protected readonly textDocuments?: TextDocumentProvider;
protected readonly fileSystemProvider: FileSystemProvider;
constructor(services: LangiumSharedCoreServices);
fromUri<T extends AstNode = AstNode>(uri: URI, cancellationToken?: CancellationToken): Promise<LangiumDocument<T>>;
fromTextDocument<T extends AstNode = AstNode>(textDocument: TextDocument, uri?: URI, options?: ParserOptions): LangiumDocument<T>;
fromTextDocument<T extends AstNode = AstNode>(textDocument: TextDocument, uri: URI | undefined, cancellationToken: CancellationToken): Promise<LangiumDocument<T>>;
fromString<T extends AstNode = AstNode>(text: string, uri: URI, options?: ParserOptions): LangiumDocument<T>;
fromString<T extends AstNode = AstNode>(text: string, uri: URI, cancellationToken: CancellationToken): Promise<LangiumDocument<T>>;
fromModel<T extends AstNode = AstNode>(model: T, uri: URI): LangiumDocument<T>;
protected create<T extends AstNode = AstNode>(uri: URI, content: string | TextDocument | {
$model: T;
}, options?: ParserOptions): LangiumDocument<T>;
protected createAsync<T extends AstNode = AstNode>(uri: URI, content: string | TextDocument, cancelToken: CancellationToken): Promise<LangiumDocument<T>>;
/**
* Create a LangiumDocument from a given parse result.
*
* A TextDocument is created on demand if it is not provided as argument here. Usually this
* should not be necessary because the main purpose of the TextDocument is to convert between
* text ranges and offsets, which is done solely in LSP request handling.
*
* With the introduction of {@link update} below this method is supposed to be mainly called
* during workspace initialization and on addition/recognition of new files, while changes in
* existing documents are processed via {@link update}.
*/
protected createLangiumDocument<T extends AstNode = AstNode>(parseResult: ParseResult<T>, uri: URI, textDocument?: TextDocument, text?: string): LangiumDocument<T>;
update<T extends AstNode = AstNode>(document: Mutable<LangiumDocument<T>>, cancellationToken: CancellationToken): Promise<LangiumDocument<T>>;
protected parse<T extends AstNode>(uri: URI, text: string, options?: ParserOptions): ParseResult<T>;
protected parseAsync<T extends AstNode>(uri: URI, text: string, cancellationToken: CancellationToken): Promise<ParseResult<T>>;
protected createTextDocumentGetter(uri: URI, text?: string): () => TextDocument;
}
/**
* Shared service for managing Langium documents.
*/
export interface LangiumDocuments {
/**
* A stream of all documents managed under this service.
*/
readonly all: Stream<LangiumDocument>;
/**
* Manage a new document under this service.
* @throws an error if a document with the same URI is already present.
*/
addDocument(document: LangiumDocument): void;
/**
* Retrieve the document with the given URI, if present. Otherwise returns `undefined`.
*/
getDocument(uri: URI): LangiumDocument | undefined;
/**
* Retrieve the document with the given URI. If not present, a new one will be created using the file system access.
* The new document will be added to the list of documents managed under this service.
*/
getOrCreateDocument(uri: URI, cancellationToken?: CancellationToken): Promise<LangiumDocument>;
/**
* Creates a new document with the given URI and text content.
* The new document is automatically added to this service and can be retrieved using {@link getDocument}.
*
* @throws an error if a document with the same URI is already present.
*/
createDocument(uri: URI, text: string): LangiumDocument;
/**
* Creates a new document with the given URI and text content asynchronously.
* The process can be interrupted with a cancellation token.
* The new document is automatically added to this service and can be retrieved using {@link getDocument}.
*
* @throws an error if a document with the same URI is already present.
*/
createDocument(uri: URI, text: string, cancellationToken: CancellationToken): Promise<LangiumDocument>;
/**
* Returns `true` if a document with the given URI is managed under this service.
*/
hasDocument(uri: URI): boolean;
/**
* Flag the document with the given URI as `Changed`, if present, meaning that its content
* is no longer valid. The content (parseResult) stays untouched, while internal data may
* be dropped to reduce memory footprint.
*
* @returns the affected {@link LangiumDocument} if existing for convenience
*/
invalidateDocument(uri: URI): LangiumDocument | undefined;
/**
* Remove the document with the given URI, if present, and mark it as `Changed`, meaning
* that its content is no longer valid. The next call to `getOrCreateDocument` with the same
* URI will create a new document instance.
*
* @returns the affected {@link LangiumDocument} if existing for convenience
*/
deleteDocument(uri: URI): LangiumDocument | undefined;
/**
* If the given URI is a directory, remove all documents within this directory.
* If it is a file, just remove that single document from the documents.
*
* @returns the affected {@link LangiumDocument}s if existing for convenience
*/
deleteDocuments(uri: URI): LangiumDocument[];
}
export declare class DefaultLangiumDocuments implements LangiumDocuments {
protected readonly langiumDocumentFactory: LangiumDocumentFactory;
protected readonly serviceRegistry: ServiceRegistry;
protected readonly documentTrie: UriTrie<LangiumDocument<AstNode>>;
constructor(services: LangiumSharedCoreServices);
get all(): Stream<LangiumDocument>;
addDocument(document: LangiumDocument): void;
getDocument(uri: URI): LangiumDocument | undefined;
getDocuments(folder: URI): LangiumDocument[];
getOrCreateDocument(uri: URI, cancellationToken?: CancellationToken): Promise<LangiumDocument>;
createDocument(uri: URI, text: string): LangiumDocument;
createDocument(uri: URI, text: string, cancellationToken: CancellationToken): Promise<LangiumDocument>;
hasDocument(uri: URI): boolean;
invalidateDocument(uri: URI): LangiumDocument | undefined;
deleteDocument(uri: URI): LangiumDocument | undefined;
deleteDocuments(folder: URI): LangiumDocument[];
}
//# sourceMappingURL=documents.d.ts.map