+74
@@ -0,0 +1,74 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
******************************************************************************/
|
||||
import type { URI } from '../utils/uri-utils.js';
|
||||
import type { NameProvider } from '../references/name-provider.js';
|
||||
import type { LangiumCoreServices } from '../services.js';
|
||||
import type { AstNode, AstNodeDescription, ReferenceInfo } from '../syntax-tree.js';
|
||||
import type { AstNodeLocator } from './ast-node-locator.js';
|
||||
import type { DocumentSegment, LangiumDocument } from './documents.js';
|
||||
import { CancellationToken } from '../utils/cancellation.js';
|
||||
/**
|
||||
* Language-specific service for creating descriptions of AST nodes to be used for cross-reference resolutions.
|
||||
*/
|
||||
export interface AstNodeDescriptionProvider {
|
||||
/**
|
||||
* Create a description for the given AST node. This service method is typically used while indexing
|
||||
* the contents of a document and during scope computation.
|
||||
*
|
||||
* @param node An AST node.
|
||||
* @param name The name to be used to refer to the AST node. By default, this is determined by the
|
||||
* `NameProvider` service, but alternative names may be provided according to the semantics
|
||||
* of your language.
|
||||
* @param document The document containing the AST node. If omitted, it is taken from the root AST node.
|
||||
*/
|
||||
createDescription(node: AstNode, name: string | undefined, document?: LangiumDocument): AstNodeDescription;
|
||||
}
|
||||
export declare class DefaultAstNodeDescriptionProvider implements AstNodeDescriptionProvider {
|
||||
protected readonly astNodeLocator: AstNodeLocator;
|
||||
protected readonly nameProvider: NameProvider;
|
||||
constructor(services: LangiumCoreServices);
|
||||
createDescription(node: AstNode, name: string | undefined, document?: LangiumDocument): AstNodeDescription;
|
||||
}
|
||||
/**
|
||||
* Describes a cross-reference within a document or between two documents.
|
||||
*/
|
||||
export interface ReferenceDescription {
|
||||
/** URI of the document that holds a reference */
|
||||
sourceUri: URI;
|
||||
/** Path to AstNode that holds a reference */
|
||||
sourcePath: string;
|
||||
/** Target document uri */
|
||||
targetUri: URI;
|
||||
/** Path to the target AstNode inside the document */
|
||||
targetPath: string;
|
||||
/** Segment of the reference text. */
|
||||
segment: DocumentSegment;
|
||||
/** Marks a local reference i.e. a cross reference inside a document. */
|
||||
local?: boolean;
|
||||
}
|
||||
/**
|
||||
* Language-specific service to create descriptions of all cross-references in a document. These are used by the `IndexManager`
|
||||
* to determine which documents are affected and should be rebuilt when a document is changed.
|
||||
*/
|
||||
export interface ReferenceDescriptionProvider {
|
||||
/**
|
||||
* Create descriptions of all cross-references found in the given document. These descriptions are
|
||||
* gathered by the `IndexManager` and stored in the global index so they can be considered when
|
||||
* a document change is reported by the client.
|
||||
*
|
||||
* @param document The document in which to gather cross-references.
|
||||
* @param cancelToken Indicates when to cancel the current operation.
|
||||
* @throws `OperationCanceled` if a user action occurs during execution
|
||||
*/
|
||||
createDescriptions(document: LangiumDocument, cancelToken?: CancellationToken): Promise<ReferenceDescription[]>;
|
||||
}
|
||||
export declare class DefaultReferenceDescriptionProvider implements ReferenceDescriptionProvider {
|
||||
protected readonly nodeLocator: AstNodeLocator;
|
||||
constructor(services: LangiumCoreServices);
|
||||
createDescriptions(document: LangiumDocument, cancelToken?: CancellationToken): Promise<ReferenceDescription[]>;
|
||||
protected createInfoDescriptions(refInfo: ReferenceInfo): ReferenceDescription[];
|
||||
}
|
||||
//# sourceMappingURL=ast-descriptions.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ast-descriptions.d.ts","sourceRoot":"","sources":["../../src/workspace/ast-descriptions.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAO7D;;GAEG;AACH,MAAM,WAAW,0BAA0B;IAEvC;;;;;;;;;OASG;IACH,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,CAAC,EAAE,eAAe,GAAG,kBAAkB,CAAC;CAE9G;AAED,qBAAa,iCAAkC,YAAW,0BAA0B;IAEhF,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IAClD,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;gBAElC,QAAQ,EAAE,mBAAmB;IAKzC,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,CAAC,EAAE,eAAe,GAAG,kBAAkB;CAsB7G;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,iDAAiD;IACjD,SAAS,EAAE,GAAG,CAAA;IACd,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAA;IAClB,0BAA0B;IAC1B,SAAS,EAAE,GAAG,CAAA;IACd,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAA;IAClB,qCAAqC;IACrC,OAAO,EAAE,eAAe,CAAA;IACxB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,OAAO,CAAA;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,4BAA4B;IACzC;;;;;;;;OAQG;IACH,kBAAkB,CAAC,QAAQ,EAAE,eAAe,EAAE,WAAW,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;CACnH;AAED,qBAAa,mCAAoC,YAAW,4BAA4B;IAEpF,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,cAAc,CAAC;gBAEnC,QAAQ,EAAE,mBAAmB;IAInC,kBAAkB,CAAC,QAAQ,EAAE,eAAe,EAAE,WAAW,oBAAyB,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAc1H,SAAS,CAAC,sBAAsB,CAAC,OAAO,EAAE,aAAa,GAAG,oBAAoB,EAAE;CA4BnF"}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
******************************************************************************/
|
||||
import { CancellationToken } from '../utils/cancellation.js';
|
||||
import { isMultiReference, isReference } from '../syntax-tree.js';
|
||||
import { getDocument, streamAst, streamReferences } from '../utils/ast-utils.js';
|
||||
import { toDocumentSegment } from '../utils/cst-utils.js';
|
||||
import { interruptAndCheck } from '../utils/promise-utils.js';
|
||||
import { UriUtils } from '../utils/uri-utils.js';
|
||||
export class DefaultAstNodeDescriptionProvider {
|
||||
constructor(services) {
|
||||
this.astNodeLocator = services.workspace.AstNodeLocator;
|
||||
this.nameProvider = services.references.NameProvider;
|
||||
}
|
||||
createDescription(node, name, document) {
|
||||
const doc = document ?? getDocument(node);
|
||||
name ?? (name = this.nameProvider.getName(node));
|
||||
const path = this.astNodeLocator.getAstNodePath(node);
|
||||
if (!name) {
|
||||
throw new Error(`Node at path ${path} has no name.`);
|
||||
}
|
||||
let nameNodeSegment;
|
||||
const nameSegmentGetter = () => nameNodeSegment ?? (nameNodeSegment = toDocumentSegment(this.nameProvider.getNameNode(node) ?? node.$cstNode));
|
||||
return {
|
||||
node,
|
||||
name,
|
||||
get nameSegment() {
|
||||
return nameSegmentGetter();
|
||||
},
|
||||
selectionSegment: toDocumentSegment(node.$cstNode),
|
||||
type: node.$type,
|
||||
documentUri: doc.uri,
|
||||
path
|
||||
};
|
||||
}
|
||||
}
|
||||
export class DefaultReferenceDescriptionProvider {
|
||||
constructor(services) {
|
||||
this.nodeLocator = services.workspace.AstNodeLocator;
|
||||
}
|
||||
async createDescriptions(document, cancelToken = CancellationToken.None) {
|
||||
const descr = [];
|
||||
const rootNode = document.parseResult.value;
|
||||
for (const astNode of streamAst(rootNode)) {
|
||||
await interruptAndCheck(cancelToken);
|
||||
streamReferences(astNode).forEach(refInfo => {
|
||||
if (!refInfo.reference.error) {
|
||||
descr.push(...this.createInfoDescriptions(refInfo));
|
||||
}
|
||||
});
|
||||
}
|
||||
return descr;
|
||||
}
|
||||
createInfoDescriptions(refInfo) {
|
||||
const reference = refInfo.reference;
|
||||
if (reference.error || !reference.$refNode) {
|
||||
return [];
|
||||
}
|
||||
let items = [];
|
||||
if (isReference(reference) && reference.$nodeDescription) {
|
||||
items = [reference.$nodeDescription];
|
||||
}
|
||||
else if (isMultiReference(reference)) {
|
||||
items = reference.items.map(e => e.$nodeDescription).filter(e => e !== undefined);
|
||||
}
|
||||
const sourceUri = getDocument(refInfo.container).uri;
|
||||
const sourcePath = this.nodeLocator.getAstNodePath(refInfo.container);
|
||||
const descriptions = [];
|
||||
const segment = toDocumentSegment(reference.$refNode);
|
||||
for (const item of items) {
|
||||
descriptions.push({
|
||||
sourceUri,
|
||||
sourcePath,
|
||||
targetUri: item.documentUri,
|
||||
targetPath: item.path,
|
||||
segment,
|
||||
local: UriUtils.equals(item.documentUri, sourceUri)
|
||||
});
|
||||
}
|
||||
return descriptions;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=ast-descriptions.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ast-descriptions.js","sourceRoot":"","sources":["../../src/workspace/ast-descriptions.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAQhF,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAqBjD,MAAM,OAAO,iCAAiC;IAK1C,YAAY,QAA6B;QACrC,IAAI,CAAC,cAAc,GAAG,QAAQ,CAAC,SAAS,CAAC,cAAc,CAAC;QACxD,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC,UAAU,CAAC,YAAY,CAAC;IACzD,CAAC;IAED,iBAAiB,CAAC,IAAa,EAAE,IAAwB,EAAE,QAA0B;QACjF,MAAM,GAAG,GAAG,QAAQ,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,KAAJ,IAAI,GAAK,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,EAAC;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QACtD,IAAI,CAAC,IAAI,EAAE,CAAC;YACR,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,eAAe,CAAC,CAAC;QACzD,CAAC;QACD,IAAI,eAA4C,CAAC;QACjD,MAAM,iBAAiB,GAAG,GAAG,EAAE,CAAC,eAAe,KAAf,eAAe,GAAK,iBAAiB,CAAC,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAA,CAAC;QAC5H,OAAO;YACH,IAAI;YACJ,IAAI;YACJ,IAAI,WAAW;gBACX,OAAO,iBAAiB,EAAE,CAAC;YAC/B,CAAC;YACD,gBAAgB,EAAE,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC;YAClD,IAAI,EAAE,IAAI,CAAC,KAAK;YAChB,WAAW,EAAE,GAAG,CAAC,GAAG;YACpB,IAAI;SACP,CAAC;IACN,CAAC;CAEJ;AAqCD,MAAM,OAAO,mCAAmC;IAI5C,YAAY,QAA6B;QACrC,IAAI,CAAC,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC,cAAc,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,QAAyB,EAAE,WAAW,GAAG,iBAAiB,CAAC,IAAI;QACpF,MAAM,KAAK,GAA2B,EAAE,CAAC;QACzC,MAAM,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC;QAC5C,KAAK,MAAM,OAAO,IAAI,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;YACxC,MAAM,iBAAiB,CAAC,WAAW,CAAC,CAAC;YACrC,gBAAgB,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;gBACxC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;oBAC3B,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC;gBACxD,CAAC;YACL,CAAC,CAAC,CAAC;QACP,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAES,sBAAsB,CAAC,OAAsB;QACnD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACpC,IAAI,SAAS,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;YACzC,OAAO,EAAE,CAAC;QACd,CAAC;QACD,IAAI,KAAK,GAAyB,EAAE,CAAC;QACrC,IAAI,WAAW,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,gBAAgB,EAAE,CAAC;YACvD,KAAK,GAAG,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACzC,CAAC;aAAM,IAAI,gBAAgB,CAAC,SAAS,CAAC,EAAE,CAAC;YACrC,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;QACtF,CAAC;QACD,MAAM,SAAS,GAAG,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC;QACrD,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACtE,MAAM,YAAY,GAA2B,EAAE,CAAC;QAChD,MAAM,OAAO,GAAG,iBAAiB,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACtD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,YAAY,CAAC,IAAI,CAAC;gBACd,SAAS;gBACT,UAAU;gBACV,SAAS,EAAE,IAAI,CAAC,WAAW;gBAC3B,UAAU,EAAE,IAAI,CAAC,IAAI;gBACrB,OAAO;gBACP,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC;aACtD,CAAC,CAAC;QACP,CAAC;QACD,OAAO,YAAY,CAAC;IACxB,CAAC;CAEJ"}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
******************************************************************************/
|
||||
import type { AstNode } from '../syntax-tree.js';
|
||||
/**
|
||||
* Language-specific service for locating an `AstNode` in a document.
|
||||
*/
|
||||
export interface AstNodeLocator {
|
||||
/**
|
||||
* Creates a path represented by a `string` that identifies an `AstNode` inside its document.
|
||||
* It must be possible to retrieve exactly the same `AstNode` from the document using this path.
|
||||
*
|
||||
* @param node The `AstNode` for which to create the path.
|
||||
* @returns a path represented by a `string` that identifies `node` inside its document.
|
||||
* @see {@link getAstNode}
|
||||
*/
|
||||
getAstNodePath(node: AstNode): string;
|
||||
/**
|
||||
* Locates an `AstNode` inside another node by following the given path.
|
||||
*
|
||||
* @param node Parent element.
|
||||
* @param path Describes how to locate the `AstNode` inside the given `node`.
|
||||
* @returns The `AstNode` located under the given path, or `undefined` if the path cannot be resolved.
|
||||
* @see {@link getAstNodePath}
|
||||
*/
|
||||
getAstNode<T extends AstNode = AstNode>(node: AstNode, path: string): T | undefined;
|
||||
}
|
||||
export declare class DefaultAstNodeLocator implements AstNodeLocator {
|
||||
protected segmentSeparator: string;
|
||||
protected indexSeparator: string;
|
||||
getAstNodePath(node: AstNode): string;
|
||||
protected getPathSegment({ $containerProperty, $containerIndex }: AstNode): string;
|
||||
getAstNode<T extends AstNode = AstNode>(node: AstNode, path: string): T | undefined;
|
||||
}
|
||||
//# sourceMappingURL=ast-node-locator.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ast-node-locator.d.ts","sourceRoot":"","sources":["../../src/workspace/ast-node-locator.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,cAAc;IAE3B;;;;;;;OAOG;IACH,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IAEtC;;;;;;;OAOG;IACH,UAAU,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;CAEvF;AAED,qBAAa,qBAAsB,YAAW,cAAc;IACxD,SAAS,CAAC,gBAAgB,SAAO;IACjC,SAAS,CAAC,cAAc,SAAO;IAE/B,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM;IAUrC,SAAS,CAAC,cAAc,CAAC,EAAE,kBAAkB,EAAE,eAAe,EAAE,EAAE,OAAO,GAAG,MAAM;IAUlF,UAAU,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;CAiBtF"}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
******************************************************************************/
|
||||
export class DefaultAstNodeLocator {
|
||||
constructor() {
|
||||
this.segmentSeparator = '/';
|
||||
this.indexSeparator = '@';
|
||||
}
|
||||
getAstNodePath(node) {
|
||||
if (node.$container) {
|
||||
const containerPath = this.getAstNodePath(node.$container);
|
||||
const newSegment = this.getPathSegment(node);
|
||||
const nodePath = containerPath + this.segmentSeparator + newSegment;
|
||||
return nodePath;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
getPathSegment({ $containerProperty, $containerIndex }) {
|
||||
if (!$containerProperty) {
|
||||
throw new Error("Missing '$containerProperty' in AST node.");
|
||||
}
|
||||
if ($containerIndex !== undefined) {
|
||||
return $containerProperty + this.indexSeparator + $containerIndex;
|
||||
}
|
||||
return $containerProperty;
|
||||
}
|
||||
getAstNode(node, path) {
|
||||
const segments = path.split(this.segmentSeparator);
|
||||
return segments.reduce((previousValue, currentValue) => {
|
||||
if (!previousValue || currentValue.length === 0) {
|
||||
return previousValue;
|
||||
}
|
||||
const propertyIndex = currentValue.indexOf(this.indexSeparator);
|
||||
if (propertyIndex > 0) {
|
||||
const property = currentValue.substring(0, propertyIndex);
|
||||
const arrayIndex = parseInt(currentValue.substring(propertyIndex + 1));
|
||||
const array = previousValue[property];
|
||||
return array?.[arrayIndex];
|
||||
}
|
||||
return previousValue[currentValue];
|
||||
}, node);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=ast-node-locator.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ast-node-locator.js","sourceRoot":"","sources":["../../src/workspace/ast-node-locator.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AA+BhF,MAAM,OAAO,qBAAqB;IAAlC;QACc,qBAAgB,GAAG,GAAG,CAAC;QACvB,mBAAc,GAAG,GAAG,CAAC;IAuCnC,CAAC;IArCG,cAAc,CAAC,IAAa;QACxB,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAClB,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC3D,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;YAC7C,MAAM,QAAQ,GAAG,aAAa,GAAG,IAAI,CAAC,gBAAgB,GAAG,UAAU,CAAC;YACpE,OAAO,QAAQ,CAAC;QACpB,CAAC;QACD,OAAO,EAAE,CAAC;IACd,CAAC;IAES,cAAc,CAAC,EAAE,kBAAkB,EAAE,eAAe,EAAW;QACrE,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QACjE,CAAC;QACD,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;YAChC,OAAO,kBAAkB,GAAG,IAAI,CAAC,cAAc,GAAG,eAAe,CAAC;QACtE,CAAC;QACD,OAAO,kBAAkB,CAAC;IAC9B,CAAC;IAED,UAAU,CAA8B,IAAa,EAAE,IAAY;QAC/D,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACnD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,YAAY,EAAE,EAAE;YACnD,IAAI,CAAC,aAAa,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC9C,OAAO,aAAa,CAAC;YACzB,CAAC;YACD,MAAM,aAAa,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAChE,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;gBACpB,MAAM,QAAQ,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;gBAC1D,MAAM,UAAU,GAAG,QAAQ,CAAC,YAAY,CAAC,SAAS,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC;gBACvE,MAAM,KAAK,GAAI,aAAsD,CAAC,QAAQ,CAAC,CAAC;gBAChF,OAAO,KAAK,EAAE,CAAC,UAAU,CAAC,CAAC;YAC/B,CAAC;YACD,OAAQ,aAAoD,CAAC,YAAY,CAAC,CAAC;QAC/E,CAAC,EAAE,IAAI,CAAM,CAAC;IAClB,CAAC;CAEJ"}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2022 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.
|
||||
******************************************************************************/
|
||||
import { Emitter } from '../utils/event.js';
|
||||
import type { ConfigurationItem, DidChangeConfigurationParams, DidChangeConfigurationRegistrationOptions, Disposable, Event, InitializeParams, InitializedParams } from 'vscode-languageserver-protocol';
|
||||
import type { ServiceRegistry } from '../service-registry.js';
|
||||
import type { LangiumSharedCoreServices } from '../services.js';
|
||||
import { Deferred } from '../utils/promise-utils.js';
|
||||
export interface ConfigurationProvider {
|
||||
/**
|
||||
* A promise that resolves when the configuration provider is ready to be used.
|
||||
*/
|
||||
readonly ready: Promise<void>;
|
||||
/**
|
||||
* When used in a language server context, this method is called when the server receives
|
||||
* the `initialize` request.
|
||||
*/
|
||||
initialize(params: InitializeParams): void;
|
||||
/**
|
||||
* When used in a language server context, this method is called when the server receives
|
||||
* the `initialized` notification.
|
||||
*/
|
||||
initialized(params: ConfigurationInitializedParams): Promise<void>;
|
||||
/**
|
||||
* Returns a configuration value stored for the given language.
|
||||
*
|
||||
* @param language The language id
|
||||
* @param configuration Configuration name
|
||||
*/
|
||||
getConfiguration(language: string, configuration: string): Promise<any>;
|
||||
/**
|
||||
* Updates the cached configurations using the `change` notification parameters.
|
||||
*
|
||||
* @param change The parameters of a change configuration notification.
|
||||
* `settings` property of the change object could be expressed as `Record<string, Record<string, any>>`
|
||||
*/
|
||||
updateConfiguration(change: DidChangeConfigurationParams): void;
|
||||
/**
|
||||
* Get notified after a configuration section has been updated.
|
||||
*/
|
||||
onConfigurationSectionUpdate(callback: ConfigurationSectionUpdateListener): Disposable;
|
||||
}
|
||||
export interface ConfigurationInitializedParams extends InitializedParams {
|
||||
register?: (params: DidChangeConfigurationRegistrationOptions) => void;
|
||||
fetchConfiguration?: (configuration: ConfigurationItem[]) => Promise<any>;
|
||||
}
|
||||
export interface ConfigurationSectionUpdate {
|
||||
/**
|
||||
* The name of the configuration section that has been updated.
|
||||
*/
|
||||
section: string;
|
||||
/**
|
||||
* The updated configuration section.
|
||||
*/
|
||||
configuration: any;
|
||||
}
|
||||
export type ConfigurationSectionUpdateListener = (update: ConfigurationSectionUpdate) => void;
|
||||
/**
|
||||
* Base configuration provider for building up other configuration providers
|
||||
*/
|
||||
export declare class DefaultConfigurationProvider implements ConfigurationProvider {
|
||||
protected readonly serviceRegistry: ServiceRegistry;
|
||||
protected readonly _ready: Deferred<void>;
|
||||
protected readonly onConfigurationSectionUpdateEmitter: Emitter<ConfigurationSectionUpdate>;
|
||||
protected settings: Record<string, Record<string, any>>;
|
||||
protected workspaceConfig: boolean;
|
||||
constructor(services: LangiumSharedCoreServices);
|
||||
get ready(): Promise<void>;
|
||||
initialize(params: InitializeParams): void;
|
||||
initialized(params: ConfigurationInitializedParams): Promise<void>;
|
||||
/**
|
||||
* Updates the cached configurations using the `change` notification parameters.
|
||||
*
|
||||
* @param change The parameters of a change configuration notification.
|
||||
* `settings` property of the change object could be expressed as `Record<string, Record<string, any>>`
|
||||
*/
|
||||
updateConfiguration(change: DidChangeConfigurationParams): void;
|
||||
protected updateSectionConfiguration(section: string, configuration: any): void;
|
||||
/**
|
||||
* Returns a configuration value stored for the given language.
|
||||
*
|
||||
* @param language The language id
|
||||
* @param configuration Configuration name
|
||||
*/
|
||||
getConfiguration(language: string, configuration: string): Promise<any>;
|
||||
protected toSectionName(languageId: string): string;
|
||||
get onConfigurationSectionUpdate(): Event<ConfigurationSectionUpdate>;
|
||||
}
|
||||
//# sourceMappingURL=configuration.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"configuration.d.ts","sourceRoot":"","sources":["../../src/workspace/configuration.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,EACR,iBAAiB,EACjB,4BAA4B,EAC5B,yCAAyC,EACzC,UAAU,EACV,KAAK,EACL,gBAAgB,EAChB,iBAAiB,EACpB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAIrD,MAAM,WAAW,qBAAqB;IAElC;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9B;;;OAGG;IACH,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAE3C;;;OAGG;IACH,WAAW,CAAC,MAAM,EAAE,8BAA8B,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnE;;;;;OAKG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAExE;;;;;OAKG;IACH,mBAAmB,CAAC,MAAM,EAAE,4BAA4B,GAAG,IAAI,CAAC;IAEhE;;OAEG;IACH,4BAA4B,CAAC,QAAQ,EAAE,kCAAkC,GAAG,UAAU,CAAA;CACzF;AAED,MAAM,WAAW,8BAA+B,SAAQ,iBAAiB;IACrE,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,yCAAyC,KAAK,IAAI,CAAC;IACvE,kBAAkB,CAAC,EAAE,CAAC,aAAa,EAAE,iBAAiB,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;CAC5E;AAED,MAAM,WAAW,0BAA0B;IACvC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,aAAa,EAAE,GAAG,CAAC;CACtB;AAED,MAAM,MAAM,kCAAkC,GAAG,CAAC,MAAM,EAAE,0BAA0B,KAAK,IAAI,CAAC;AAE9F;;GAEG;AACH,qBAAa,4BAA6B,YAAW,qBAAqB;IAEtE,SAAS,CAAC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IACpD,SAAS,CAAC,QAAQ,CAAC,MAAM,iBAAwB;IACjD,SAAS,CAAC,QAAQ,CAAC,mCAAmC,sCAA6C;IACnG,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAM;IAC7D,SAAS,CAAC,eAAe,UAAS;gBAEtB,QAAQ,EAAE,yBAAyB;IAI/C,IAAI,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAEzB;IAED,UAAU,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI;IAIpC,WAAW,CAAC,MAAM,EAAE,8BAA8B,GAAG,OAAO,CAAC,IAAI,CAAC;IA+BxE;;;;;OAKG;IACH,mBAAmB,CAAC,MAAM,EAAE,4BAA4B,GAAG,IAAI;IAU/D,SAAS,CAAC,0BAA0B,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,GAAG,IAAI;IAI/E;;;;;MAKE;IACI,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAS7E,SAAS,CAAC,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAInD,IAAI,4BAA4B,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAEpE;CACJ"}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2022 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.
|
||||
******************************************************************************/
|
||||
import { Emitter } from '../utils/event.js';
|
||||
import { Deferred } from '../utils/promise-utils.js';
|
||||
/**
|
||||
* Base configuration provider for building up other configuration providers
|
||||
*/
|
||||
export class DefaultConfigurationProvider {
|
||||
constructor(services) {
|
||||
this._ready = new Deferred();
|
||||
this.onConfigurationSectionUpdateEmitter = new Emitter();
|
||||
this.settings = {};
|
||||
this.workspaceConfig = false;
|
||||
this.serviceRegistry = services.ServiceRegistry;
|
||||
}
|
||||
get ready() {
|
||||
return this._ready.promise;
|
||||
}
|
||||
initialize(params) {
|
||||
this.workspaceConfig = params.capabilities.workspace?.configuration ?? false;
|
||||
}
|
||||
async initialized(params) {
|
||||
if (this.workspaceConfig) {
|
||||
if (params.register) {
|
||||
// params.register(...) is a function to be provided by the calling language server for the sake of
|
||||
// decoupling this implementation from the concrete LSP implementations, specifically the LSP Connection
|
||||
const languages = this.serviceRegistry.all;
|
||||
params.register({
|
||||
// Listen to configuration changes for all languages
|
||||
section: languages.map(lang => this.toSectionName(lang.LanguageMetaData.languageId))
|
||||
});
|
||||
}
|
||||
if (params.fetchConfiguration) {
|
||||
// params.fetchConfiguration(...) is a function to be provided by the calling language server for the sake of
|
||||
// decoupling this implementation from the concrete LSP implementations, specifically the LSP Connection
|
||||
const configToUpdate = this.serviceRegistry.all.map(lang => ({
|
||||
// Fetch the configuration changes for all languages
|
||||
section: this.toSectionName(lang.LanguageMetaData.languageId)
|
||||
}));
|
||||
// get workspace configurations (default scope URI)
|
||||
const configs = await params.fetchConfiguration(configToUpdate);
|
||||
configToUpdate.forEach((conf, idx) => {
|
||||
this.updateSectionConfiguration(conf.section, configs[idx]);
|
||||
});
|
||||
}
|
||||
}
|
||||
this._ready.resolve();
|
||||
}
|
||||
/**
|
||||
* Updates the cached configurations using the `change` notification parameters.
|
||||
*
|
||||
* @param change The parameters of a change configuration notification.
|
||||
* `settings` property of the change object could be expressed as `Record<string, Record<string, any>>`
|
||||
*/
|
||||
updateConfiguration(change) {
|
||||
if (typeof change.settings !== 'object' || change.settings === null) {
|
||||
return;
|
||||
}
|
||||
Object.entries(change.settings).forEach(([section, configuration]) => {
|
||||
this.updateSectionConfiguration(section, configuration);
|
||||
this.onConfigurationSectionUpdateEmitter.fire({ section, configuration });
|
||||
});
|
||||
}
|
||||
updateSectionConfiguration(section, configuration) {
|
||||
this.settings[section] = configuration;
|
||||
}
|
||||
/**
|
||||
* Returns a configuration value stored for the given language.
|
||||
*
|
||||
* @param language The language id
|
||||
* @param configuration Configuration name
|
||||
*/
|
||||
async getConfiguration(language, configuration) {
|
||||
await this.ready;
|
||||
const sectionName = this.toSectionName(language);
|
||||
if (this.settings[sectionName]) {
|
||||
return this.settings[sectionName][configuration];
|
||||
}
|
||||
}
|
||||
toSectionName(languageId) {
|
||||
return `${languageId}`;
|
||||
}
|
||||
get onConfigurationSectionUpdate() {
|
||||
return this.onConfigurationSectionUpdateEmitter.event;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=configuration.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"configuration.js","sourceRoot":"","sources":["../../src/workspace/configuration.ts"],"names":[],"mappings":"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"}
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
******************************************************************************/
|
||||
import { CancellationToken } from '../utils/cancellation.js';
|
||||
import { Disposable } from '../utils/disposable.js';
|
||||
import type { ServiceRegistry } from '../service-registry.js';
|
||||
import type { LangiumSharedCoreServices } from '../services.js';
|
||||
import type { AstNode } from '../syntax-tree.js';
|
||||
import type { MaybePromise } from '../utils/promise-utils.js';
|
||||
import type { Deferred } from '../utils/promise-utils.js';
|
||||
import type { ValidationOptions } from '../validation/document-validator.js';
|
||||
import type { IndexManager } from '../workspace/index-manager.js';
|
||||
import type { LangiumDocument, LangiumDocuments, LangiumDocumentFactory, TextDocumentProvider } from './documents.js';
|
||||
import { MultiMap } from '../utils/collections.js';
|
||||
import { type URI } from '../utils/uri-utils.js';
|
||||
import type { ValidationCategory } from '../validation/validation-registry.js';
|
||||
import { DocumentState } from './documents.js';
|
||||
import type { FileSystemProvider } from './file-system-provider.js';
|
||||
import type { WorkspaceManager } from './workspace-manager.js';
|
||||
export interface BuildOptions {
|
||||
/**
|
||||
* Control the linking and references indexing phase with this option. The default if not specified is `true`.
|
||||
* If set to `false`, references can still be resolved - that's done lazily when you access the `ref` property of
|
||||
* a reference. But you won't get any diagnostics for linking errors and the references won't be considered
|
||||
* when updating other documents.
|
||||
*/
|
||||
eagerLinking?: boolean;
|
||||
/**
|
||||
* Control the validation phase with this option:
|
||||
* - `true` enables all validation checks and forces revalidating the documents
|
||||
* In order to include additional, custom validation categories, override `DefaultDocumentBuilder.getAllValidationCategories(...)`.
|
||||
* - `false` or `undefined` disables all validation checks
|
||||
* - An object runs only the necessary validation checks; the `categories` property restricts this to a specific subset (which might include custom categories as well).
|
||||
*/
|
||||
validation?: boolean | ValidationOptions;
|
||||
}
|
||||
export interface DocumentBuildState {
|
||||
/** Whether a document has completed its last build process. */
|
||||
completed: boolean;
|
||||
/** The options used for the last build process. */
|
||||
options: BuildOptions;
|
||||
/** Additional information about the last build result. */
|
||||
result?: {
|
||||
validationChecks?: ValidationCategory[];
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Shared-service for building and updating `LangiumDocument`s.
|
||||
*/
|
||||
export interface DocumentBuilder {
|
||||
/** The options used for rebuilding documents after an update. */
|
||||
updateBuildOptions: BuildOptions;
|
||||
/**
|
||||
* Execute all necessary build steps for the given documents.
|
||||
*
|
||||
* @param documents Set of documents to be built.
|
||||
* @param options Options for the document builder.
|
||||
* @param cancelToken Indicates when to cancel the current operation.
|
||||
* @throws `OperationCanceled` if a user action occurs during execution
|
||||
*/
|
||||
build<T extends AstNode>(documents: Array<LangiumDocument<T>>, options?: BuildOptions, cancelToken?: CancellationToken): Promise<void>;
|
||||
/**
|
||||
* This method is called when a document change is detected. It updates the state of all
|
||||
* affected documents, including those with references to the changed ones, so they are rebuilt.
|
||||
*
|
||||
* @param changed URIs of changed or created documents
|
||||
* @param deleted URIs of deleted documents
|
||||
* @param cancelToken allows to cancel the current operation
|
||||
* @throws `OperationCancelled` if cancellation is detected during execution
|
||||
*/
|
||||
update(changed: URI[], deleted: URI[], cancelToken?: CancellationToken): Promise<void>;
|
||||
/**
|
||||
* Notify the given callback when a document update was triggered, but before any document
|
||||
* is rebuilt. Listeners to this event should not perform any long-running task.
|
||||
*/
|
||||
onUpdate(callback: DocumentUpdateListener): Disposable;
|
||||
/**
|
||||
* Reset the state of a document to the specified state, removing any derived data as needed.
|
||||
*
|
||||
* @param document The document to reset.
|
||||
* @param state The state to reset the document to.
|
||||
*/
|
||||
resetToState<T extends AstNode>(document: LangiumDocument<T>, state: DocumentState): void;
|
||||
/**
|
||||
* Notify the given callback when a set of documents has been built reaching the specified target state.
|
||||
*/
|
||||
onBuildPhase(targetState: DocumentState, callback: DocumentBuildListener): Disposable;
|
||||
/**
|
||||
* Notify the specified callback when a document has been built reaching the specified target state.
|
||||
* Unlike {@link onBuildPhase} the listener is called for every single document.
|
||||
*
|
||||
* There are two main advantages compared to {@link onBuildPhase}:
|
||||
* 1. If the build is cancelled, {@link onDocumentPhase} will still fire for documents that have reached a specific state.
|
||||
* Meanwhile, {@link onBuildPhase} won't fire for that state.
|
||||
* 2. The {@link DocumentBuilder} ensures that all {@link DocumentPhaseListener} instances are called for a built document.
|
||||
* Even if the build is cancelled before those listeners were called.
|
||||
*/
|
||||
onDocumentPhase(targetState: DocumentState, callback: DocumentPhaseListener): Disposable;
|
||||
/**
|
||||
* Wait until the workspace has reached the specified state for all documents.
|
||||
*
|
||||
* @param state The desired state. The promise won't resolve until all documents have reached this state
|
||||
* @param cancelToken Optionally allows to cancel the wait operation, disposing any listeners in the process
|
||||
* @throws `OperationCancelled` if cancellation has been requested before the state has been reached
|
||||
*/
|
||||
waitUntil(state: DocumentState, cancelToken?: CancellationToken): Promise<void>;
|
||||
/**
|
||||
* Wait until the document specified by the {@link uri} has reached the specified state.
|
||||
*
|
||||
* @param state The desired state. The promise won't resolve until the document has reached this state.
|
||||
* @param uri The specified URI that points to the document. If the URI does not exist, the promise will resolve once the workspace has reached the specified state.
|
||||
* @param cancelToken Optionally allows to cancel the wait operation, disposing any listeners in the process.
|
||||
* @return The URI of the document that has reached the desired state, or `undefined` if the document does not exist.
|
||||
* @throws `OperationCancelled` if cancellation has been requested before the state has been reached
|
||||
*/
|
||||
waitUntil(state: DocumentState, uri?: URI, cancelToken?: CancellationToken): Promise<URI | undefined>;
|
||||
}
|
||||
export type DocumentUpdateListener = (changed: URI[], deleted: URI[]) => void | Promise<void>;
|
||||
export type DocumentBuildListener = (built: LangiumDocument[], cancelToken: CancellationToken) => void | Promise<void>;
|
||||
export type DocumentPhaseListener = (built: LangiumDocument, cancelToken: CancellationToken) => void | Promise<void>;
|
||||
export declare class DefaultDocumentBuilder implements DocumentBuilder {
|
||||
updateBuildOptions: BuildOptions;
|
||||
protected readonly langiumDocuments: LangiumDocuments;
|
||||
protected readonly langiumDocumentFactory: LangiumDocumentFactory;
|
||||
protected readonly textDocuments: TextDocumentProvider | undefined;
|
||||
protected readonly indexManager: IndexManager;
|
||||
protected readonly fileSystemProvider: FileSystemProvider;
|
||||
protected readonly workspaceManager: () => WorkspaceManager;
|
||||
protected readonly serviceRegistry: ServiceRegistry;
|
||||
protected readonly updateListeners: DocumentUpdateListener[];
|
||||
protected readonly buildPhaseListeners: MultiMap<DocumentState, DocumentBuildListener>;
|
||||
protected readonly documentPhaseListeners: MultiMap<DocumentState, DocumentPhaseListener>;
|
||||
protected readonly buildState: Map<string, DocumentBuildState>;
|
||||
protected readonly documentBuildWaiters: Map<string, Deferred<void>>;
|
||||
protected currentState: DocumentState;
|
||||
constructor(services: LangiumSharedCoreServices);
|
||||
build<T extends AstNode>(documents: Array<LangiumDocument<T>>, options?: BuildOptions, cancelToken?: CancellationToken): Promise<void>;
|
||||
update(changed: URI[], deleted: URI[], cancelToken?: CancellationToken): Promise<void>;
|
||||
protected resultsAreIncomplete(document: LangiumDocument, options: BuildOptions | undefined): boolean;
|
||||
protected findMissingValidationCategories(document: LangiumDocument, options: BuildOptions | undefined): ValidationCategory[];
|
||||
protected findChangedUris(changed: URI): Promise<URI[]>;
|
||||
protected emitUpdate(changed: URI[], deleted: URI[]): Promise<void>;
|
||||
/**
|
||||
* Sort the given documents by priority. By default, documents with an open text document are prioritized.
|
||||
* This is useful to ensure that visible documents show their diagnostics before all other documents.
|
||||
*
|
||||
* This improves the responsiveness in large workspaces as users usually don't care about diagnostics
|
||||
* in files that are currently not opened in the editor.
|
||||
*/
|
||||
protected sortDocuments(documents: LangiumDocument[]): LangiumDocument[];
|
||||
private hasTextDocument;
|
||||
/**
|
||||
* Check whether the given document should be relinked after changes were found in the given URIs.
|
||||
*/
|
||||
protected shouldRelink(document: LangiumDocument, changedUris: Set<string>): boolean;
|
||||
onUpdate(callback: DocumentUpdateListener): Disposable;
|
||||
resetToState<T extends AstNode>(document: LangiumDocument<T>, state: DocumentState): void;
|
||||
protected cleanUpDeleted<T extends AstNode>(document: LangiumDocument<T>): void;
|
||||
/**
|
||||
* Build the given documents by stepping through all build phases. If a document's state indicates
|
||||
* that a certain build phase is already done, the phase is skipped for that document.
|
||||
*
|
||||
* @param documents The documents to build.
|
||||
* @param options the {@link BuildOptions} to use.
|
||||
* @param cancelToken A cancellation token that can be used to cancel the build.
|
||||
* @returns A promise that resolves when the build is done.
|
||||
*/
|
||||
protected buildDocuments(documents: LangiumDocument[], options: BuildOptions, cancelToken: CancellationToken): Promise<void>;
|
||||
protected markAsCompleted(document: LangiumDocument): void;
|
||||
/**
|
||||
* Runs prior to beginning the build process to update the {@link DocumentBuildState} for each document
|
||||
*
|
||||
* @param documents collection of documents to be built
|
||||
* @param options the {@link BuildOptions} to use
|
||||
*/
|
||||
protected prepareBuild(documents: LangiumDocument[], options: BuildOptions): void;
|
||||
/**
|
||||
* Runs a cancelable operation on a set of documents to bring them to a specified {@link DocumentState}.
|
||||
*
|
||||
* @param documents The array of documents to process.
|
||||
* @param targetState The target {@link DocumentState} to bring the documents to.
|
||||
* @param cancelToken A token that can be used to cancel the operation.
|
||||
* @param callback A function to be called for each document.
|
||||
* @returns A promise that resolves when all documents have been processed or the operation is canceled.
|
||||
* @throws Will throw `OperationCancelled` if the operation is canceled via a `CancellationToken`.
|
||||
*/
|
||||
protected runCancelable(documents: LangiumDocument[], targetState: DocumentState, cancelToken: CancellationToken, callback: (document: LangiumDocument) => MaybePromise<unknown>): Promise<void>;
|
||||
onBuildPhase(targetState: DocumentState, callback: DocumentBuildListener): Disposable;
|
||||
onDocumentPhase(targetState: DocumentState, callback: DocumentPhaseListener): Disposable;
|
||||
waitUntil(state: DocumentState, cancelToken?: CancellationToken): Promise<void>;
|
||||
waitUntil(state: DocumentState, uri?: URI, cancelToken?: CancellationToken): Promise<URI>;
|
||||
protected awaitDocumentState(state: DocumentState, uri: URI, cancelToken: CancellationToken): Promise<URI>;
|
||||
protected awaitBuilderState(state: DocumentState, cancelToken: CancellationToken): Promise<void>;
|
||||
protected notifyDocumentPhase(document: LangiumDocument, state: DocumentState, cancelToken: CancellationToken): Promise<void>;
|
||||
protected notifyBuildPhase(documents: LangiumDocument[], state: DocumentState, cancelToken: CancellationToken): Promise<void>;
|
||||
/**
|
||||
* Determine whether the given document should be linked during a build. The default
|
||||
* implementation checks the `eagerLinking` property of the build options. If it's set to `true`
|
||||
* or `undefined`, the document is included in the linking phase. This also affects the
|
||||
* references indexing phase, which depends on eager linking.
|
||||
*/
|
||||
protected shouldLink(document: LangiumDocument): boolean;
|
||||
/**
|
||||
* Determine whether the given document should be validated during a build. The default
|
||||
* implementation checks the `validation` property of the build options. If it's set to `true`
|
||||
* or a `ValidationOptions` object, the document is included in the validation phase.
|
||||
*/
|
||||
protected shouldValidate(document: LangiumDocument): boolean;
|
||||
/**
|
||||
* Run validation checks on the given document and store the resulting diagnostics in the document.
|
||||
* If the document already contains diagnostics, the new ones are added to the list.
|
||||
*/
|
||||
protected validate(document: LangiumDocument, cancelToken: CancellationToken): Promise<void>;
|
||||
protected getBuildOptions(document: LangiumDocument): BuildOptions;
|
||||
}
|
||||
//# sourceMappingURL=document-builder.d.ts.map
|
||||
+1
File diff suppressed because one or more lines are too long
+503
@@ -0,0 +1,503 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
******************************************************************************/
|
||||
import { LSPErrorCodes, ResponseError } from 'vscode-languageserver-protocol';
|
||||
import { CancellationToken } from '../utils/cancellation.js';
|
||||
import { Disposable } from '../utils/disposable.js';
|
||||
import { MultiMap } from '../utils/collections.js';
|
||||
import { OperationCancelled, interruptAndCheck, isOperationCancelled } from '../utils/promise-utils.js';
|
||||
import { stream } from '../utils/stream.js';
|
||||
import { UriUtils } from '../utils/uri-utils.js';
|
||||
import { DocumentState } from './documents.js';
|
||||
export class DefaultDocumentBuilder {
|
||||
constructor(services) {
|
||||
this.updateBuildOptions = {
|
||||
// Default: run only the built-in validation checks and those in the _fast_ category (includes those without category)
|
||||
validation: {
|
||||
categories: ['built-in', 'fast']
|
||||
}
|
||||
};
|
||||
this.updateListeners = [];
|
||||
this.buildPhaseListeners = new MultiMap();
|
||||
this.documentPhaseListeners = new MultiMap();
|
||||
this.buildState = new Map();
|
||||
this.documentBuildWaiters = new Map();
|
||||
this.currentState = DocumentState.Changed;
|
||||
this.langiumDocuments = services.workspace.LangiumDocuments;
|
||||
this.langiumDocumentFactory = services.workspace.LangiumDocumentFactory;
|
||||
this.textDocuments = services.workspace.TextDocuments;
|
||||
this.indexManager = services.workspace.IndexManager;
|
||||
this.fileSystemProvider = services.workspace.FileSystemProvider;
|
||||
this.workspaceManager = () => services.workspace.WorkspaceManager;
|
||||
this.serviceRegistry = services.ServiceRegistry;
|
||||
}
|
||||
async build(documents, options = {}, cancelToken = CancellationToken.None) {
|
||||
for (const document of documents) {
|
||||
const key = document.uri.toString();
|
||||
if (document.state === DocumentState.Validated) {
|
||||
if (typeof options.validation === 'boolean' && options.validation) {
|
||||
// Force re-running all validation checks
|
||||
this.resetToState(document, DocumentState.IndexedReferences);
|
||||
}
|
||||
else if (typeof options.validation === 'object') {
|
||||
// Validation with explicit options was requested for a document that has already been partly validated.
|
||||
// In this case, we need to execute only the missing validation categories.
|
||||
const categories = this.findMissingValidationCategories(document, options);
|
||||
if (categories.length > 0) {
|
||||
// Validate this document, since some of the requested validation categories are not executed yet.
|
||||
// In all other cases/else-branches, the document is not build at all.
|
||||
this.buildState.set(key, {
|
||||
completed: false,
|
||||
options: {
|
||||
validation: {
|
||||
categories
|
||||
}
|
||||
},
|
||||
result: this.buildState.get(key)?.result,
|
||||
});
|
||||
// Reset the state, but keep the existing validation markers of the already completed validation categories.
|
||||
document.state = DocumentState.IndexedReferences;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Default: forget any previous build options
|
||||
this.buildState.delete(key);
|
||||
}
|
||||
}
|
||||
this.currentState = DocumentState.Changed;
|
||||
await this.emitUpdate(documents.map(e => e.uri), []);
|
||||
await this.buildDocuments(documents, options, cancelToken);
|
||||
}
|
||||
async update(changed, deleted, cancelToken = CancellationToken.None) {
|
||||
this.currentState = DocumentState.Changed;
|
||||
// Remove all metadata of documents that are reported as deleted
|
||||
const deletedUris = [];
|
||||
for (const deletedUri of deleted) {
|
||||
// Since the deleted URI might point to a directory, we delete all documents within
|
||||
const deletedDocs = this.langiumDocuments.deleteDocuments(deletedUri);
|
||||
for (const doc of deletedDocs) {
|
||||
deletedUris.push(doc.uri);
|
||||
this.cleanUpDeleted(doc);
|
||||
}
|
||||
}
|
||||
// Since the changed URI might point to a directory, we need to check all (nested) documents in that directory
|
||||
const changedUris = (await Promise.all(changed.map(uri => this.findChangedUris(uri)))).flat();
|
||||
// Set the state of all changed documents to `Changed` so they are completely rebuilt
|
||||
for (const changedUri of changedUris) {
|
||||
let changedDocument = this.langiumDocuments.getDocument(changedUri);
|
||||
if (changedDocument === undefined) {
|
||||
// We create an unparsed, invalid document.
|
||||
// This will be parsed as soon as we reach the first document builder phase.
|
||||
// This allows to cancel the parsing process later in case we need it.
|
||||
changedDocument = this.langiumDocumentFactory.fromModel({ $type: 'INVALID' }, changedUri);
|
||||
changedDocument.state = DocumentState.Changed; // required, since `langiumDocumentFactory.fromModel` marks the new document as `DocumentState.Parsed`
|
||||
this.langiumDocuments.addDocument(changedDocument);
|
||||
}
|
||||
this.resetToState(changedDocument, DocumentState.Changed);
|
||||
}
|
||||
// Set the state of all documents that should be relinked to `ComputedScopes` (if not already lower)
|
||||
const allChangedUris = stream(changedUris).concat(deletedUris).map(uri => uri.toString()).toSet();
|
||||
this.langiumDocuments.all
|
||||
.filter(doc => !allChangedUris.has(doc.uri.toString()) && this.shouldRelink(doc, allChangedUris))
|
||||
.forEach(doc => this.resetToState(doc, DocumentState.ComputedScopes));
|
||||
// Notify listeners of the update
|
||||
await this.emitUpdate(changedUris, deletedUris);
|
||||
// Only allow interrupting the execution after all state changes are done
|
||||
await interruptAndCheck(cancelToken);
|
||||
// Collect and sort all documents that we should rebuild
|
||||
const rebuildDocuments = this.sortDocuments(this.langiumDocuments.all
|
||||
.filter(doc =>
|
||||
// This includes those that were reported as changed and those that we selected for relinking
|
||||
doc.state < DocumentState.Validated
|
||||
// This includes those for which a previous build has been cancelled
|
||||
|| !this.buildState.get(doc.uri.toString())?.completed
|
||||
// `updateBuildOptions` changed between the last build (which is completed) and the current build,
|
||||
// leading to incomplete results, e.g. some validation categories are requested, which are not executed during the last build
|
||||
|| this.resultsAreIncomplete(doc, this.updateBuildOptions))
|
||||
.toArray());
|
||||
await this.buildDocuments(rebuildDocuments, this.updateBuildOptions, cancelToken);
|
||||
}
|
||||
resultsAreIncomplete(document, options) {
|
||||
return this.findMissingValidationCategories(document, options).length >= 1;
|
||||
}
|
||||
findMissingValidationCategories(document, options) {
|
||||
const state = this.buildState.get(document.uri.toString());
|
||||
const allCategories = this.serviceRegistry.getServices(document.uri).validation.ValidationRegistry.getAllValidationCategories(document);
|
||||
const executedCategories = state?.result?.validationChecks ? new Set(state?.result?.validationChecks) : state?.completed ? allCategories : new Set();
|
||||
const requestedCategories = (options === undefined || options.validation === true) ? allCategories
|
||||
: typeof options.validation === 'object' ? (options.validation.categories ?? allCategories) : [];
|
||||
return stream(requestedCategories).filter(requested => !executedCategories.has(requested)).toArray();
|
||||
}
|
||||
async findChangedUris(changed) {
|
||||
// Most common case is that the document/textDocument at the specified URI has changed
|
||||
const document = this.langiumDocuments.getDocument(changed) ?? this.textDocuments?.get(changed);
|
||||
if (document) {
|
||||
return [changed];
|
||||
}
|
||||
// If the document doesn't exist yet, we need to check what kind of file has changed
|
||||
try {
|
||||
const stat = await this.fileSystemProvider.stat(changed);
|
||||
if (stat.isDirectory) {
|
||||
// If a directory has changed, we need to check all documents in that directory
|
||||
const uris = await this.workspaceManager().searchFolder(changed);
|
||||
return uris;
|
||||
}
|
||||
else if (this.workspaceManager().shouldIncludeEntry(stat)) {
|
||||
// Return the changed URI if it's a file that we can handle
|
||||
return [changed];
|
||||
}
|
||||
}
|
||||
catch {
|
||||
// If we can't determine the file type, we discard the change
|
||||
}
|
||||
return [];
|
||||
}
|
||||
async emitUpdate(changed, deleted) {
|
||||
await Promise.all(this.updateListeners.map(listener => listener(changed, deleted)));
|
||||
}
|
||||
/**
|
||||
* Sort the given documents by priority. By default, documents with an open text document are prioritized.
|
||||
* This is useful to ensure that visible documents show their diagnostics before all other documents.
|
||||
*
|
||||
* This improves the responsiveness in large workspaces as users usually don't care about diagnostics
|
||||
* in files that are currently not opened in the editor.
|
||||
*/
|
||||
sortDocuments(documents) {
|
||||
let left = 0;
|
||||
let right = documents.length - 1;
|
||||
while (left < right) {
|
||||
while (left < documents.length && this.hasTextDocument(documents[left])) {
|
||||
left++;
|
||||
}
|
||||
while (right >= 0 && !this.hasTextDocument(documents[right])) {
|
||||
right--;
|
||||
}
|
||||
if (left < right) {
|
||||
[documents[left], documents[right]] = [documents[right], documents[left]];
|
||||
}
|
||||
}
|
||||
return documents;
|
||||
}
|
||||
hasTextDocument(doc) {
|
||||
return Boolean(this.textDocuments?.get(doc.uri));
|
||||
}
|
||||
/**
|
||||
* Check whether the given document should be relinked after changes were found in the given URIs.
|
||||
*/
|
||||
shouldRelink(document, changedUris) {
|
||||
// Relink documents with linking errors -- maybe those references can be resolved now
|
||||
if (document.references.some(ref => ref.error !== undefined)) {
|
||||
return true;
|
||||
}
|
||||
// Check whether the document is affected by any of the changed URIs
|
||||
return this.indexManager.isAffected(document, changedUris);
|
||||
}
|
||||
onUpdate(callback) {
|
||||
this.updateListeners.push(callback);
|
||||
return Disposable.create(() => {
|
||||
const index = this.updateListeners.indexOf(callback);
|
||||
if (index >= 0) {
|
||||
this.updateListeners.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
resetToState(document, state) {
|
||||
switch (state) {
|
||||
case DocumentState.Changed: {
|
||||
// Fall through
|
||||
}
|
||||
case DocumentState.Parsed:
|
||||
this.indexManager.removeContent(document.uri);
|
||||
// Fall through
|
||||
case DocumentState.IndexedContent:
|
||||
document.localSymbols = undefined;
|
||||
// Fall through
|
||||
case DocumentState.ComputedScopes: {
|
||||
const linker = this.serviceRegistry.getServices(document.uri).references.Linker;
|
||||
linker.unlink(document);
|
||||
// Fall through
|
||||
}
|
||||
case DocumentState.Linked:
|
||||
this.indexManager.removeReferences(document.uri);
|
||||
// Fall through
|
||||
case DocumentState.IndexedReferences:
|
||||
document.diagnostics = undefined;
|
||||
this.buildState.delete(document.uri.toString());
|
||||
// Fall through
|
||||
case DocumentState.Validated:
|
||||
// do nothing and keep the buildState
|
||||
}
|
||||
if (document.state > state) {
|
||||
document.state = state;
|
||||
}
|
||||
}
|
||||
cleanUpDeleted(document) {
|
||||
this.buildState.delete(document.uri.toString());
|
||||
this.indexManager.remove(document.uri);
|
||||
// Since this method `cleanUpDeleted` is not available from outside, the following line is not necessary, since the state is already set before.
|
||||
// This line does not hurt and makes the code to be in sync with `resetToState`.
|
||||
// If `cleanUpDeleted` is called in custom document builders at some more places, this line becomes necessary.
|
||||
document.state = DocumentState.Changed;
|
||||
}
|
||||
/**
|
||||
* Build the given documents by stepping through all build phases. If a document's state indicates
|
||||
* that a certain build phase is already done, the phase is skipped for that document.
|
||||
*
|
||||
* @param documents The documents to build.
|
||||
* @param options the {@link BuildOptions} to use.
|
||||
* @param cancelToken A cancellation token that can be used to cancel the build.
|
||||
* @returns A promise that resolves when the build is done.
|
||||
*/
|
||||
async buildDocuments(documents, options, cancelToken) {
|
||||
this.prepareBuild(documents, options);
|
||||
// 0. Parse content
|
||||
await this.runCancelable(documents, DocumentState.Parsed, cancelToken, doc => this.langiumDocumentFactory.update(doc, cancelToken));
|
||||
// 1. Index content: collect the documents' symbols being accessible by other documents
|
||||
await this.runCancelable(documents, DocumentState.IndexedContent, cancelToken, doc => this.indexManager.updateContent(doc, cancelToken));
|
||||
// 2. Local symbols: collect each documents' symbols being accessible within the document (only)
|
||||
await this.runCancelable(documents, DocumentState.ComputedScopes, cancelToken, async (doc) => {
|
||||
const scopeComputation = this.serviceRegistry.getServices(doc.uri).references.ScopeComputation;
|
||||
doc.localSymbols = await scopeComputation.collectLocalSymbols(doc, cancelToken);
|
||||
});
|
||||
// 3. Linking
|
||||
const toBeLinked = documents.filter(doc => this.shouldLink(doc));
|
||||
await this.runCancelable(toBeLinked, DocumentState.Linked, cancelToken, doc => {
|
||||
const linker = this.serviceRegistry.getServices(doc.uri).references.Linker;
|
||||
return linker.link(doc, cancelToken);
|
||||
});
|
||||
// 4. Index references
|
||||
await this.runCancelable(toBeLinked, DocumentState.IndexedReferences, cancelToken, doc => this.indexManager.updateReferences(doc, cancelToken));
|
||||
// 5. Validation
|
||||
const toBeValidated = documents.filter(doc => {
|
||||
if (this.shouldValidate(doc)) {
|
||||
return true; // the build state is marked as completed after finishing the validation for the current document
|
||||
}
|
||||
else {
|
||||
this.markAsCompleted(doc); // since the validation is skipped for this document, it is already completed now
|
||||
return false;
|
||||
}
|
||||
});
|
||||
await this.runCancelable(toBeValidated, DocumentState.Validated, cancelToken, async (doc) => {
|
||||
await this.validate(doc, cancelToken);
|
||||
this.markAsCompleted(doc);
|
||||
});
|
||||
}
|
||||
markAsCompleted(document) {
|
||||
const state = this.buildState.get(document.uri.toString());
|
||||
if (state) {
|
||||
state.completed = true;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Runs prior to beginning the build process to update the {@link DocumentBuildState} for each document
|
||||
*
|
||||
* @param documents collection of documents to be built
|
||||
* @param options the {@link BuildOptions} to use
|
||||
*/
|
||||
prepareBuild(documents, options) {
|
||||
for (const doc of documents) {
|
||||
const key = doc.uri.toString();
|
||||
const state = this.buildState.get(key);
|
||||
if (!state // If the document has no previous build state, we set it.
|
||||
|| state.completed // If it has one, but it's already marked as completed, we overwrite it.
|
||||
) {
|
||||
this.buildState.set(key, {
|
||||
completed: false,
|
||||
options,
|
||||
result: state?.result
|
||||
});
|
||||
}
|
||||
else {
|
||||
// If the previous build was not completed, we keep its DocumentState and continue from the DocumentState where it was cancelled,
|
||||
// e.g. the previous build options are used, including the previously requested validation categories.
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Runs a cancelable operation on a set of documents to bring them to a specified {@link DocumentState}.
|
||||
*
|
||||
* @param documents The array of documents to process.
|
||||
* @param targetState The target {@link DocumentState} to bring the documents to.
|
||||
* @param cancelToken A token that can be used to cancel the operation.
|
||||
* @param callback A function to be called for each document.
|
||||
* @returns A promise that resolves when all documents have been processed or the operation is canceled.
|
||||
* @throws Will throw `OperationCancelled` if the operation is canceled via a `CancellationToken`.
|
||||
*/
|
||||
async runCancelable(documents, targetState, cancelToken, callback) {
|
||||
for (const document of documents) {
|
||||
if (document.state < targetState) {
|
||||
await interruptAndCheck(cancelToken);
|
||||
await callback(document);
|
||||
document.state = targetState;
|
||||
await this.notifyDocumentPhase(document, targetState, cancelToken);
|
||||
}
|
||||
}
|
||||
// Do not use `filtered` here, as that will miss documents that have previously reached the current target state.
|
||||
// For example, this happens in case the cancellation triggers between the processing of two documents
|
||||
// or files that were picked up during the workspace initialization.
|
||||
const targetStateDocs = documents.filter(doc => doc.state === targetState);
|
||||
await this.notifyBuildPhase(targetStateDocs, targetState, cancelToken);
|
||||
this.currentState = targetState;
|
||||
}
|
||||
onBuildPhase(targetState, callback) {
|
||||
this.buildPhaseListeners.add(targetState, callback);
|
||||
return Disposable.create(() => {
|
||||
this.buildPhaseListeners.delete(targetState, callback);
|
||||
});
|
||||
}
|
||||
onDocumentPhase(targetState, callback) {
|
||||
this.documentPhaseListeners.add(targetState, callback);
|
||||
return Disposable.create(() => {
|
||||
this.documentPhaseListeners.delete(targetState, callback);
|
||||
});
|
||||
}
|
||||
waitUntil(state, uriOrToken, cancelToken) {
|
||||
let uri = undefined;
|
||||
if (uriOrToken && 'path' in uriOrToken) {
|
||||
uri = uriOrToken;
|
||||
}
|
||||
else {
|
||||
cancelToken = uriOrToken;
|
||||
}
|
||||
cancelToken ?? (cancelToken = CancellationToken.None);
|
||||
if (uri) {
|
||||
return this.awaitDocumentState(state, uri, cancelToken);
|
||||
}
|
||||
else {
|
||||
return this.awaitBuilderState(state, cancelToken);
|
||||
}
|
||||
}
|
||||
awaitDocumentState(state, uri, cancelToken) {
|
||||
const document = this.langiumDocuments.getDocument(uri);
|
||||
if (!document) {
|
||||
return Promise.reject(new ResponseError(LSPErrorCodes.ServerCancelled, `No document found for URI: ${uri.toString()}`));
|
||||
}
|
||||
else if (document.state >= state) {
|
||||
return Promise.resolve(uri);
|
||||
}
|
||||
else if (cancelToken.isCancellationRequested) {
|
||||
return Promise.reject(OperationCancelled);
|
||||
}
|
||||
else if (this.currentState >= state && state > document.state) {
|
||||
// this would imply that the document has been excluded from linking or validation, for example;
|
||||
// this should never occur, the LS need to make sure that the affected document is properly built,
|
||||
// alternatively, the build state requirement need to be relaxed.
|
||||
return Promise.reject(new ResponseError(LSPErrorCodes.RequestFailed, `Document state of ${uri.toString()} is ${DocumentState[document.state]}, requiring ${DocumentState[state]}, but workspace state is already ${DocumentState[this.currentState]}. Returning undefined.`));
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const buildDisposable = this.onDocumentPhase(state, (doc) => {
|
||||
if (UriUtils.equals(doc.uri, uri)) {
|
||||
buildDisposable.dispose();
|
||||
cancelDisposable.dispose();
|
||||
resolve(doc.uri);
|
||||
}
|
||||
});
|
||||
const cancelDisposable = cancelToken.onCancellationRequested(() => {
|
||||
buildDisposable.dispose();
|
||||
cancelDisposable.dispose();
|
||||
reject(OperationCancelled);
|
||||
});
|
||||
});
|
||||
}
|
||||
awaitBuilderState(state, cancelToken) {
|
||||
if (this.currentState >= state) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
else if (cancelToken.isCancellationRequested) {
|
||||
return Promise.reject(OperationCancelled);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const buildDisposable = this.onBuildPhase(state, () => {
|
||||
buildDisposable.dispose();
|
||||
cancelDisposable.dispose();
|
||||
resolve();
|
||||
});
|
||||
const cancelDisposable = cancelToken.onCancellationRequested(() => {
|
||||
buildDisposable.dispose();
|
||||
cancelDisposable.dispose();
|
||||
reject(OperationCancelled);
|
||||
});
|
||||
});
|
||||
}
|
||||
async notifyDocumentPhase(document, state, cancelToken) {
|
||||
const listeners = this.documentPhaseListeners.get(state);
|
||||
const listenersCopy = listeners.slice();
|
||||
for (const listener of listenersCopy) {
|
||||
try {
|
||||
await interruptAndCheck(cancelToken);
|
||||
await listener(document, cancelToken);
|
||||
}
|
||||
catch (err) {
|
||||
// Ignore cancellation errors
|
||||
// We want to finish the listeners before throwing
|
||||
if (!isOperationCancelled(err)) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
async notifyBuildPhase(documents, state, cancelToken) {
|
||||
if (documents.length === 0) {
|
||||
// Don't notify when no document has been processed
|
||||
return;
|
||||
}
|
||||
const listeners = this.buildPhaseListeners.get(state);
|
||||
const listenersCopy = listeners.slice();
|
||||
for (const listener of listenersCopy) {
|
||||
await interruptAndCheck(cancelToken);
|
||||
await listener(documents, cancelToken);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Determine whether the given document should be linked during a build. The default
|
||||
* implementation checks the `eagerLinking` property of the build options. If it's set to `true`
|
||||
* or `undefined`, the document is included in the linking phase. This also affects the
|
||||
* references indexing phase, which depends on eager linking.
|
||||
*/
|
||||
shouldLink(document) {
|
||||
return this.getBuildOptions(document).eagerLinking ?? true;
|
||||
}
|
||||
/**
|
||||
* Determine whether the given document should be validated during a build. The default
|
||||
* implementation checks the `validation` property of the build options. If it's set to `true`
|
||||
* or a `ValidationOptions` object, the document is included in the validation phase.
|
||||
*/
|
||||
shouldValidate(document) {
|
||||
return Boolean(this.getBuildOptions(document).validation);
|
||||
}
|
||||
/**
|
||||
* Run validation checks on the given document and store the resulting diagnostics in the document.
|
||||
* If the document already contains diagnostics, the new ones are added to the list.
|
||||
*/
|
||||
async validate(document, cancelToken) {
|
||||
const validator = this.serviceRegistry.getServices(document.uri).validation.DocumentValidator;
|
||||
const options = this.getBuildOptions(document);
|
||||
const validationOptions = typeof options.validation === 'object' ? { ...options.validation } : {};
|
||||
validationOptions.categories = this.findMissingValidationCategories(document, options); // execute only not-yet-executed categories
|
||||
const diagnostics = await validator.validateDocument(document, validationOptions, cancelToken);
|
||||
if (document.diagnostics) {
|
||||
document.diagnostics.push(...diagnostics); // keep diagnostics of previously executed categories
|
||||
}
|
||||
else {
|
||||
document.diagnostics = diagnostics;
|
||||
}
|
||||
// Store information about the executed validation in the build state
|
||||
const state = this.buildState.get(document.uri.toString());
|
||||
if (state) {
|
||||
state.result ?? (state.result = {});
|
||||
if (state.result.validationChecks) {
|
||||
state.result.validationChecks = stream(state.result.validationChecks).concat(validationOptions.categories).distinct().toArray();
|
||||
}
|
||||
else {
|
||||
state.result.validationChecks = [...validationOptions.categories];
|
||||
}
|
||||
}
|
||||
}
|
||||
getBuildOptions(document) {
|
||||
return this.buildState.get(document.uri.toString())?.options ?? {};
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=document-builder.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+282
@@ -0,0 +1,282 @@
|
||||
/******************************************************************************
|
||||
* 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;
|
||||
/**
|
||||
* If the given URI is a directory, all documents within this directory are retrieved.
|
||||
* If it is a file, just that single document is retrieved.
|
||||
*/
|
||||
getDocuments(folder: URI): LangiumDocument[];
|
||||
/**
|
||||
* 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>;
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @deprecated Since 4.2 use `DocumentBuilder.resetToState(DocumentState.Changed)` instead
|
||||
*/
|
||||
invalidateDocument(uri: URI): LangiumDocument | undefined;
|
||||
/**
|
||||
* Returns `true` if a document with the given URI is managed under this service.
|
||||
*/
|
||||
hasDocument(uri: URI): boolean;
|
||||
/**
|
||||
* 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 services: LangiumSharedCoreServices;
|
||||
protected readonly langiumDocumentFactory: LangiumDocumentFactory;
|
||||
private documentBuilder;
|
||||
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;
|
||||
/**
|
||||
* @deprecated Since 4.2 use `DocumentBuilder.resetToState(DocumentState.Changed)` instead
|
||||
* TODO remove this for the next major release
|
||||
*/
|
||||
invalidateDocument(uri: URI): LangiumDocument | undefined;
|
||||
deleteDocument(uri: URI): LangiumDocument | undefined;
|
||||
deleteDocuments(folder: URI): LangiumDocument[];
|
||||
}
|
||||
//# sourceMappingURL=documents.d.ts.map
|
||||
+1
File diff suppressed because one or more lines are too long
+278
@@ -0,0 +1,278 @@
|
||||
/******************************************************************************
|
||||
* 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 { TextDocument } from './documents.js';
|
||||
import { CancellationToken } from '../utils/cancellation.js';
|
||||
import { stream } from '../utils/stream.js';
|
||||
import { URI, UriTrie } from '../utils/uri-utils.js';
|
||||
/**
|
||||
* 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 var DocumentState;
|
||||
(function (DocumentState) {
|
||||
/**
|
||||
* The text content has changed and needs to be parsed again. The AST held by this outdated
|
||||
* document instance is no longer valid.
|
||||
*/
|
||||
DocumentState[DocumentState["Changed"] = 0] = "Changed";
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
DocumentState[DocumentState["Parsed"] = 1] = "Parsed";
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
DocumentState[DocumentState["IndexedContent"] = 2] = "IndexedContent";
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
DocumentState[DocumentState["ComputedScopes"] = 3] = "ComputedScopes";
|
||||
/**
|
||||
* The `Linker` service has processed this document. All outgoing references have been
|
||||
* resolved or marked as erroneous.
|
||||
*/
|
||||
DocumentState[DocumentState["Linked"] = 4] = "Linked";
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
DocumentState[DocumentState["IndexedReferences"] = 5] = "IndexedReferences";
|
||||
/**
|
||||
* The `DocumentValidator` service has processed this document. The language server listens
|
||||
* to the results of this phase and sends diagnostics to the client.
|
||||
*/
|
||||
DocumentState[DocumentState["Validated"] = 6] = "Validated";
|
||||
})(DocumentState || (DocumentState = {}));
|
||||
export class DefaultLangiumDocumentFactory {
|
||||
constructor(services) {
|
||||
this.serviceRegistry = services.ServiceRegistry;
|
||||
this.textDocuments = services.workspace.TextDocuments;
|
||||
this.fileSystemProvider = services.workspace.FileSystemProvider;
|
||||
}
|
||||
async fromUri(uri, cancellationToken = CancellationToken.None) {
|
||||
const content = await this.fileSystemProvider.readFile(uri);
|
||||
return this.createAsync(uri, content, cancellationToken);
|
||||
}
|
||||
fromTextDocument(textDocument, uri, token) {
|
||||
uri = uri ?? URI.parse(textDocument.uri);
|
||||
if (CancellationToken.is(token)) {
|
||||
return this.createAsync(uri, textDocument, token);
|
||||
}
|
||||
else {
|
||||
return this.create(uri, textDocument, token);
|
||||
}
|
||||
}
|
||||
fromString(text, uri, token) {
|
||||
if (CancellationToken.is(token)) {
|
||||
return this.createAsync(uri, text, token);
|
||||
}
|
||||
else {
|
||||
return this.create(uri, text, token);
|
||||
}
|
||||
}
|
||||
fromModel(model, uri) {
|
||||
return this.create(uri, { $model: model });
|
||||
}
|
||||
create(uri, content, options) {
|
||||
if (typeof content === 'string') {
|
||||
const parseResult = this.parse(uri, content, options);
|
||||
return this.createLangiumDocument(parseResult, uri, undefined, content);
|
||||
}
|
||||
else if ('$model' in content) {
|
||||
const parseResult = { value: content.$model, parserErrors: [], lexerErrors: [] };
|
||||
return this.createLangiumDocument(parseResult, uri);
|
||||
}
|
||||
else {
|
||||
const parseResult = this.parse(uri, content.getText(), options);
|
||||
return this.createLangiumDocument(parseResult, uri, content);
|
||||
}
|
||||
}
|
||||
async createAsync(uri, content, cancelToken) {
|
||||
if (typeof content === 'string') {
|
||||
const parseResult = await this.parseAsync(uri, content, cancelToken);
|
||||
return this.createLangiumDocument(parseResult, uri, undefined, content);
|
||||
}
|
||||
else {
|
||||
const parseResult = await this.parseAsync(uri, content.getText(), cancelToken);
|
||||
return this.createLangiumDocument(parseResult, uri, content);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
createLangiumDocument(parseResult, uri, textDocument, text) {
|
||||
let document;
|
||||
if (textDocument) {
|
||||
document = {
|
||||
parseResult,
|
||||
uri,
|
||||
state: DocumentState.Parsed,
|
||||
references: [],
|
||||
textDocument
|
||||
};
|
||||
}
|
||||
else {
|
||||
const textDocumentGetter = this.createTextDocumentGetter(uri, text);
|
||||
document = {
|
||||
parseResult,
|
||||
uri,
|
||||
state: DocumentState.Parsed,
|
||||
references: [],
|
||||
get textDocument() {
|
||||
return textDocumentGetter();
|
||||
}
|
||||
};
|
||||
}
|
||||
parseResult.value.$document = document;
|
||||
return document;
|
||||
}
|
||||
async update(document, cancellationToken) {
|
||||
// The CST full text property contains the original text that was used to create the AST.
|
||||
const oldText = document.parseResult.value.$cstNode?.root.fullText;
|
||||
const textDocument = this.textDocuments?.get(document.uri.toString());
|
||||
const text = textDocument ? textDocument.getText() : await this.fileSystemProvider.readFile(document.uri);
|
||||
if (textDocument) {
|
||||
Object.defineProperty(document, 'textDocument', {
|
||||
value: textDocument
|
||||
});
|
||||
}
|
||||
else {
|
||||
const textDocumentGetter = this.createTextDocumentGetter(document.uri, text);
|
||||
Object.defineProperty(document, 'textDocument', {
|
||||
get: textDocumentGetter
|
||||
});
|
||||
}
|
||||
// Some of these documents can be pretty large, so parsing them again can be quite expensive.
|
||||
// Therefore, we only parse if the text has actually changed.
|
||||
if (oldText !== text) {
|
||||
document.parseResult = await this.parseAsync(document.uri, text, cancellationToken);
|
||||
document.parseResult.value.$document = document;
|
||||
}
|
||||
document.state = DocumentState.Parsed;
|
||||
return document;
|
||||
}
|
||||
parse(uri, text, options) {
|
||||
const services = this.serviceRegistry.getServices(uri);
|
||||
return services.parser.LangiumParser.parse(text, options);
|
||||
}
|
||||
parseAsync(uri, text, cancellationToken) {
|
||||
const services = this.serviceRegistry.getServices(uri);
|
||||
return services.parser.AsyncParser.parse(text, cancellationToken);
|
||||
}
|
||||
createTextDocumentGetter(uri, text) {
|
||||
const serviceRegistry = this.serviceRegistry;
|
||||
let textDoc = undefined;
|
||||
return () => {
|
||||
return textDoc ?? (textDoc = TextDocument.create(uri.toString(), serviceRegistry.getServices(uri).LanguageMetaData.languageId, 0, text ?? ''));
|
||||
};
|
||||
}
|
||||
}
|
||||
export class DefaultLangiumDocuments {
|
||||
constructor(services) {
|
||||
this.documentTrie = new UriTrie();
|
||||
this.services = services;
|
||||
this.langiumDocumentFactory = services.workspace.LangiumDocumentFactory;
|
||||
this.documentBuilder = () => services.workspace.DocumentBuilder;
|
||||
}
|
||||
get all() {
|
||||
return stream(this.documentTrie.all());
|
||||
}
|
||||
addDocument(document) {
|
||||
const uriString = document.uri.toString();
|
||||
if (this.documentTrie.has(uriString)) {
|
||||
throw new Error(`A document with the URI '${uriString}' is already present.`);
|
||||
}
|
||||
this.documentTrie.insert(uriString, document);
|
||||
}
|
||||
getDocument(uri) {
|
||||
const uriString = uri.toString();
|
||||
return this.documentTrie.find(uriString);
|
||||
}
|
||||
getDocuments(folder) {
|
||||
const uriString = folder.toString();
|
||||
return this.documentTrie.findAll(uriString);
|
||||
}
|
||||
async getOrCreateDocument(uri, cancellationToken) {
|
||||
let document = this.getDocument(uri);
|
||||
if (document) {
|
||||
return document;
|
||||
}
|
||||
document = await this.langiumDocumentFactory.fromUri(uri, cancellationToken);
|
||||
this.addDocument(document);
|
||||
return document;
|
||||
}
|
||||
createDocument(uri, text, cancellationToken) {
|
||||
if (cancellationToken) {
|
||||
return this.langiumDocumentFactory.fromString(text, uri, cancellationToken).then(document => {
|
||||
this.addDocument(document);
|
||||
return document;
|
||||
});
|
||||
}
|
||||
else {
|
||||
const document = this.langiumDocumentFactory.fromString(text, uri);
|
||||
this.addDocument(document);
|
||||
return document;
|
||||
}
|
||||
}
|
||||
hasDocument(uri) {
|
||||
return this.documentTrie.has(uri.toString());
|
||||
}
|
||||
/**
|
||||
* @deprecated Since 4.2 use `DocumentBuilder.resetToState(DocumentState.Changed)` instead
|
||||
* TODO remove this for the next major release
|
||||
*/
|
||||
invalidateDocument(uri) {
|
||||
const uriString = uri.toString();
|
||||
const langiumDoc = this.documentTrie.find(uriString);
|
||||
if (langiumDoc) {
|
||||
this.documentBuilder().resetToState(langiumDoc, DocumentState.Changed);
|
||||
}
|
||||
return langiumDoc;
|
||||
}
|
||||
deleteDocument(uri) {
|
||||
const uriString = uri.toString();
|
||||
const langiumDoc = this.documentTrie.find(uriString);
|
||||
if (langiumDoc) {
|
||||
langiumDoc.state = DocumentState.Changed;
|
||||
this.documentTrie.delete(uriString);
|
||||
}
|
||||
return langiumDoc;
|
||||
}
|
||||
deleteDocuments(folder) {
|
||||
const uriString = folder.toString();
|
||||
const langiumDocs = this.documentTrie.findAll(uriString);
|
||||
for (const langiumDoc of langiumDocs) {
|
||||
langiumDoc.state = DocumentState.Changed;
|
||||
}
|
||||
this.documentTrie.delete(uriString);
|
||||
return langiumDocs;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=documents.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+85
@@ -0,0 +1,85 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2022 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.
|
||||
******************************************************************************/
|
||||
import type { URI } from '../utils/uri-utils.js';
|
||||
export interface FileSystemNode {
|
||||
readonly isFile: boolean;
|
||||
readonly isDirectory: boolean;
|
||||
readonly uri: URI;
|
||||
}
|
||||
export type FileSystemFilter = (node: FileSystemNode) => boolean;
|
||||
/**
|
||||
* Provides methods to interact with an abstract file system. The default implementation is based on the node.js `fs` API.
|
||||
*/
|
||||
export interface FileSystemProvider {
|
||||
/**
|
||||
* Gets the status of a file or directory.
|
||||
* The status includes meta data such as whether the node is a file or directory.
|
||||
* @param uri The URI of the file or directory.
|
||||
*/
|
||||
stat(uri: URI): Promise<FileSystemNode>;
|
||||
/**
|
||||
* Gets the status of a file or directory synchronously.
|
||||
* The status includes meta data such as whether the node is a file or directory.
|
||||
* @param uri The URI of the file or directory.
|
||||
*/
|
||||
statSync(uri: URI): FileSystemNode;
|
||||
/**
|
||||
* Checks if a file exists at the specified URI.
|
||||
* @returns `true` if a file exists at the specified URI, `false` otherwise.
|
||||
*/
|
||||
exists(uri: URI): Promise<boolean>;
|
||||
/**
|
||||
* Checks if a file exists at the specified URI synchronously.
|
||||
* @returns `true` if a file exists at the specified URI, `false` otherwise.
|
||||
*/
|
||||
existsSync(uri: URI): boolean;
|
||||
/**
|
||||
* Reads a binary file asynchronously from a given URI.
|
||||
* @returns The binary content of the file with the specified URI.
|
||||
*/
|
||||
readBinary(uri: URI): Promise<Uint8Array>;
|
||||
/**
|
||||
* Reads a binary file synchronously from a given URI.
|
||||
* @returns The binary content of the file with the specified URI.
|
||||
*/
|
||||
readBinarySync(uri: URI): Uint8Array;
|
||||
/**
|
||||
* Reads a document asynchronously from a given URI.
|
||||
* @returns The string content of the file with the specified URI.
|
||||
*/
|
||||
readFile(uri: URI): Promise<string>;
|
||||
/**
|
||||
* Reads a document synchronously from a given URI.
|
||||
* @returns The string content of the file with the specified
|
||||
*/
|
||||
readFileSync(uri: URI): string;
|
||||
/**
|
||||
* Reads the directory information for the given URI.
|
||||
* @returns The list of file system entries that are contained within the specified directory.
|
||||
*/
|
||||
readDirectory(uri: URI): Promise<FileSystemNode[]>;
|
||||
/**
|
||||
* Reads the directory information for the given URI synchronously.
|
||||
* @returns The list of file system entries that are contained within the specified directory.
|
||||
*/
|
||||
readDirectorySync(uri: URI): FileSystemNode[];
|
||||
}
|
||||
export declare class EmptyFileSystemProvider implements FileSystemProvider {
|
||||
stat(_uri: URI): Promise<FileSystemNode>;
|
||||
statSync(_uri: URI): FileSystemNode;
|
||||
exists(): Promise<boolean>;
|
||||
existsSync(): boolean;
|
||||
readBinary(): Promise<Uint8Array>;
|
||||
readBinarySync(): Uint8Array;
|
||||
readFile(): Promise<string>;
|
||||
readFileSync(): string;
|
||||
readDirectory(): Promise<FileSystemNode[]>;
|
||||
readDirectorySync(): FileSystemNode[];
|
||||
}
|
||||
export declare const EmptyFileSystem: {
|
||||
fileSystemProvider: () => EmptyFileSystemProvider;
|
||||
};
|
||||
//# sourceMappingURL=file-system-provider.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"file-system-provider.d.ts","sourceRoot":"","sources":["../../src/workspace/file-system-provider.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAEjD,MAAM,WAAW,cAAc;IAC3B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;CACrB;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAC/B;;;;OAIG;IACH,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,cAAc,CAAC;IACnC;;;OAGG;IACH,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC;;;OAGG;IACH,UAAU,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC;IAC9B;;;OAGG;IACH,UAAU,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1C;;;OAGG;IACH,cAAc,CAAC,GAAG,EAAE,GAAG,GAAG,UAAU,CAAC;IACrC;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpC;;;OAGG;IACH,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAAC;IAC/B;;;OAGG;IACH,aAAa,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IACnD;;;OAGG;IACH,iBAAiB,CAAC,GAAG,EAAE,GAAG,GAAG,cAAc,EAAE,CAAC;CACjD;AAED,qBAAa,uBAAwB,YAAW,kBAAkB;IAE9D,IAAI,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,cAAc,CAAC;IAIxC,QAAQ,CAAC,IAAI,EAAE,GAAG,GAAG,cAAc;IAG7B,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAIhC,UAAU,IAAI,OAAO;IAIrB,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC;IAIjC,cAAc,IAAI,UAAU;IAI5B,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAI3B,YAAY,IAAI,MAAM;IAIhB,aAAa,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAIhD,iBAAiB,IAAI,cAAc,EAAE;CAIxC;AAED,eAAO,MAAM,eAAe;;CAE3B,CAAC"}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2022 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.
|
||||
******************************************************************************/
|
||||
export class EmptyFileSystemProvider {
|
||||
stat(_uri) {
|
||||
throw new Error('No file system is available.');
|
||||
}
|
||||
statSync(_uri) {
|
||||
throw new Error('No file system is available.');
|
||||
}
|
||||
async exists() {
|
||||
return false;
|
||||
}
|
||||
existsSync() {
|
||||
return false;
|
||||
}
|
||||
readBinary() {
|
||||
throw new Error('No file system is available.');
|
||||
}
|
||||
readBinarySync() {
|
||||
throw new Error('No file system is available.');
|
||||
}
|
||||
readFile() {
|
||||
throw new Error('No file system is available.');
|
||||
}
|
||||
readFileSync() {
|
||||
throw new Error('No file system is available.');
|
||||
}
|
||||
async readDirectory() {
|
||||
return [];
|
||||
}
|
||||
readDirectorySync() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
export const EmptyFileSystem = {
|
||||
fileSystemProvider: () => new EmptyFileSystemProvider()
|
||||
};
|
||||
//# sourceMappingURL=file-system-provider.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"file-system-provider.js","sourceRoot":"","sources":["../../src/workspace/file-system-provider.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAsEhF,MAAM,OAAO,uBAAuB;IAEhC,IAAI,CAAC,IAAS;QACV,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACpD,CAAC;IAED,QAAQ,CAAC,IAAS;QACd,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACpD,CAAC;IACD,KAAK,CAAC,MAAM;QACR,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,UAAU;QACN,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,UAAU;QACN,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACpD,CAAC;IAED,cAAc;QACV,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACpD,CAAC;IAED,QAAQ;QACJ,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACpD,CAAC;IAED,YAAY;QACR,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACpD,CAAC;IAED,KAAK,CAAC,aAAa;QACf,OAAO,EAAE,CAAC;IACd,CAAC;IAED,iBAAiB;QACb,OAAO,EAAE,CAAC;IACd,CAAC;CAEJ;AAED,MAAM,CAAC,MAAM,eAAe,GAAG;IAC3B,kBAAkB,EAAE,GAAG,EAAE,CAAC,IAAI,uBAAuB,EAAE;CAC1D,CAAC"}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
******************************************************************************/
|
||||
import type { ServiceRegistry } from '../service-registry.js';
|
||||
import type { LangiumSharedCoreServices } from '../services.js';
|
||||
import type { AstNode, AstNodeDescription, AstReflection } from '../syntax-tree.js';
|
||||
import { ContextCache } from '../utils/caching.js';
|
||||
import { CancellationToken } from '../utils/cancellation.js';
|
||||
import type { Stream } from '../utils/stream.js';
|
||||
import type { URI } from '../utils/uri-utils.js';
|
||||
import type { ReferenceDescription } from './ast-descriptions.js';
|
||||
import type { LangiumDocument, LangiumDocuments } from './documents.js';
|
||||
/**
|
||||
* The index manager is responsible for keeping metadata about symbols and cross-references
|
||||
* in the workspace. It is used to look up symbols in the global scope, mostly during linking
|
||||
* and completion. This service is shared between all languages of a language server.
|
||||
*/
|
||||
export interface IndexManager {
|
||||
/**
|
||||
* Remove the specified document URI from the index.
|
||||
* Necessary when documents are deleted and not referenceable anymore.
|
||||
*
|
||||
* @param uri The URI of the document for which index data shall be removed
|
||||
*/
|
||||
remove(uri: URI): void;
|
||||
/**
|
||||
* Remove only the information about the exportable content of a document.
|
||||
*/
|
||||
removeContent(uri: URI): void;
|
||||
/**
|
||||
* Remove only the information about the cross-references of a document.
|
||||
*/
|
||||
removeReferences(uri: URI): void;
|
||||
/**
|
||||
* Update the information about the exportable content of a document inside the index.
|
||||
*
|
||||
* @param document Document to be updated
|
||||
* @param cancelToken Indicates when to cancel the current operation.
|
||||
* @throws `OperationCanceled` if a user action occurs during execution
|
||||
*/
|
||||
updateContent(document: LangiumDocument, cancelToken?: CancellationToken): Promise<void>;
|
||||
/**
|
||||
* Update the information about the cross-references of a document inside the index.
|
||||
*
|
||||
* @param document Document to be updated
|
||||
* @param cancelToken Indicates when to cancel the current operation.
|
||||
* @throws `OperationCanceled` if a user action occurs during execution
|
||||
*/
|
||||
updateReferences(document: LangiumDocument, cancelToken?: CancellationToken): Promise<void>;
|
||||
/**
|
||||
* Determine whether the given document could be affected by changes of the documents
|
||||
* identified by the given URIs (second parameter). The document is typically regarded as
|
||||
* affected if it contains a reference to any of the changed files.
|
||||
*
|
||||
* @param document Document to check whether it's affected
|
||||
* @param changedUris URIs of the changed documents
|
||||
*/
|
||||
isAffected(document: LangiumDocument, changedUris: Set<string>): boolean;
|
||||
/**
|
||||
* Compute a list of all exported elements, optionally filtered using a type identifier and document URIs.
|
||||
*
|
||||
* @param nodeType The type to filter with, or `undefined` to return descriptions of all types.
|
||||
* @param uris If specified, only returns elements from the given URIs.
|
||||
* @returns a `Stream` containing all globally visible nodes (of a given type).
|
||||
*/
|
||||
allElements(nodeType?: string, uris?: Set<string>): Stream<AstNodeDescription>;
|
||||
/**
|
||||
* Returns all known references that are pointing to the given `targetNode`.
|
||||
*
|
||||
* @param targetNode the `AstNode` to look up references for
|
||||
* @param astNodePath the path that points to the `targetNode` inside the document. See also `AstNodeLocator`
|
||||
*
|
||||
* @returns a `Stream` of references that are targeting the `targetNode`
|
||||
*/
|
||||
findAllReferences(targetNode: AstNode, astNodePath: string): Stream<ReferenceDescription>;
|
||||
}
|
||||
export declare class DefaultIndexManager implements IndexManager {
|
||||
protected readonly serviceRegistry: ServiceRegistry;
|
||||
protected readonly documents: LangiumDocuments;
|
||||
protected readonly astReflection: AstReflection;
|
||||
/**
|
||||
* The symbol index stores all `AstNodeDescription` items exported by a document.
|
||||
* The key used in this map is the string representation of the specific document URI.
|
||||
*/
|
||||
protected readonly symbolIndex: Map<string, AstNodeDescription[]>;
|
||||
/**
|
||||
* This is a cache for the `allElements()` method.
|
||||
* It caches the descriptions from `symbolIndex` grouped by types.
|
||||
*/
|
||||
protected readonly symbolByTypeIndex: ContextCache<string, string, AstNodeDescription[], string>;
|
||||
/**
|
||||
* This index keeps track of all `ReferenceDescription` items exported by a document.
|
||||
* This is used to compute which elements are affected by a document change
|
||||
* and for finding references to an AST node.
|
||||
*/
|
||||
protected readonly referenceIndex: Map<string, ReferenceDescription[]>;
|
||||
constructor(services: LangiumSharedCoreServices);
|
||||
findAllReferences(targetNode: AstNode, astNodePath: string): Stream<ReferenceDescription>;
|
||||
allElements(nodeType?: string, uris?: Set<string>): Stream<AstNodeDescription>;
|
||||
protected getFileDescriptions(uri: string, nodeType?: string): AstNodeDescription[];
|
||||
remove(uri: URI): void;
|
||||
removeContent(uri: URI): void;
|
||||
removeReferences(uri: URI): void;
|
||||
updateContent(document: LangiumDocument, cancelToken?: CancellationToken): Promise<void>;
|
||||
updateReferences(document: LangiumDocument, cancelToken?: CancellationToken): Promise<void>;
|
||||
isAffected(document: LangiumDocument, changedUris: Set<string>): boolean;
|
||||
}
|
||||
//# sourceMappingURL=index-manager.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index-manager.d.ts","sourceRoot":"","sources":["../../src/workspace/index-manager.ts"],"names":[],"mappings":"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"}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/******************************************************************************
|
||||
* 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.
|
||||
******************************************************************************/
|
||||
import { getDocument } from '../utils/ast-utils.js';
|
||||
import { ContextCache } from '../utils/caching.js';
|
||||
import { CancellationToken } from '../utils/cancellation.js';
|
||||
import { stream } from '../utils/stream.js';
|
||||
import { UriUtils } from '../utils/uri-utils.js';
|
||||
export class DefaultIndexManager {
|
||||
constructor(services) {
|
||||
/**
|
||||
* The symbol index stores all `AstNodeDescription` items exported by a document.
|
||||
* The key used in this map is the string representation of the specific document URI.
|
||||
*/
|
||||
this.symbolIndex = new Map();
|
||||
/**
|
||||
* This is a cache for the `allElements()` method.
|
||||
* It caches the descriptions from `symbolIndex` grouped by types.
|
||||
*/
|
||||
this.symbolByTypeIndex = new ContextCache();
|
||||
/**
|
||||
* This index keeps track of all `ReferenceDescription` items exported by a document.
|
||||
* This is used to compute which elements are affected by a document change
|
||||
* and for finding references to an AST node.
|
||||
*/
|
||||
this.referenceIndex = new Map();
|
||||
this.documents = services.workspace.LangiumDocuments;
|
||||
this.serviceRegistry = services.ServiceRegistry;
|
||||
this.astReflection = services.AstReflection;
|
||||
}
|
||||
findAllReferences(targetNode, astNodePath) {
|
||||
const targetDocUri = getDocument(targetNode).uri;
|
||||
const result = [];
|
||||
this.referenceIndex.forEach(docRefs => {
|
||||
docRefs.forEach(refDescr => {
|
||||
if (UriUtils.equals(refDescr.targetUri, targetDocUri) && refDescr.targetPath === astNodePath) {
|
||||
result.push(refDescr);
|
||||
}
|
||||
});
|
||||
});
|
||||
return stream(result);
|
||||
}
|
||||
allElements(nodeType, uris) {
|
||||
let documentUris = stream(this.symbolIndex.keys());
|
||||
if (uris) {
|
||||
documentUris = documentUris.filter(uri => !uris || uris.has(uri));
|
||||
}
|
||||
return documentUris
|
||||
.map(uri => this.getFileDescriptions(uri, nodeType))
|
||||
.flat();
|
||||
}
|
||||
getFileDescriptions(uri, nodeType) {
|
||||
if (!nodeType) {
|
||||
return this.symbolIndex.get(uri) ?? [];
|
||||
}
|
||||
const descriptions = this.symbolByTypeIndex.get(uri, nodeType, () => {
|
||||
const allFileDescriptions = this.symbolIndex.get(uri) ?? [];
|
||||
return allFileDescriptions.filter(e => this.astReflection.isSubtype(e.type, nodeType));
|
||||
});
|
||||
return descriptions;
|
||||
}
|
||||
remove(uri) {
|
||||
this.removeContent(uri);
|
||||
this.removeReferences(uri);
|
||||
}
|
||||
removeContent(uri) {
|
||||
const uriString = uri.toString();
|
||||
this.symbolIndex.delete(uriString);
|
||||
this.symbolByTypeIndex.clear(uriString);
|
||||
}
|
||||
removeReferences(uri) {
|
||||
const uriString = uri.toString();
|
||||
this.referenceIndex.delete(uriString);
|
||||
}
|
||||
async updateContent(document, cancelToken = CancellationToken.None) {
|
||||
const services = this.serviceRegistry.getServices(document.uri);
|
||||
const exports = await services.references.ScopeComputation.collectExportedSymbols(document, cancelToken);
|
||||
const uri = document.uri.toString();
|
||||
this.symbolIndex.set(uri, exports);
|
||||
this.symbolByTypeIndex.clear(uri);
|
||||
}
|
||||
async updateReferences(document, cancelToken = CancellationToken.None) {
|
||||
const services = this.serviceRegistry.getServices(document.uri);
|
||||
const indexData = await services.workspace.ReferenceDescriptionProvider.createDescriptions(document, cancelToken);
|
||||
this.referenceIndex.set(document.uri.toString(), indexData);
|
||||
}
|
||||
isAffected(document, changedUris) {
|
||||
const references = this.referenceIndex.get(document.uri.toString());
|
||||
if (!references) {
|
||||
return false;
|
||||
}
|
||||
return references.some(ref => !ref.local && changedUris.has(ref.targetUri.toString()));
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=index-manager.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index-manager.js","sourceRoot":"","sources":["../../src/workspace/index-manager.ts"],"names":[],"mappings":"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"}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2023 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.
|
||||
******************************************************************************/
|
||||
export * from './ast-descriptions.js';
|
||||
export * from './ast-node-locator.js';
|
||||
export * from './configuration.js';
|
||||
export * from './document-builder.js';
|
||||
export * from './documents.js';
|
||||
export * from './file-system-provider.js';
|
||||
export * from './index-manager.js';
|
||||
export * from './workspace-lock.js';
|
||||
export * from './workspace-manager.js';
|
||||
export * from './profiler.js';
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/workspace/index.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC"}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2023 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.
|
||||
******************************************************************************/
|
||||
export * from './ast-descriptions.js';
|
||||
export * from './ast-node-locator.js';
|
||||
export * from './configuration.js';
|
||||
export * from './document-builder.js';
|
||||
export * from './documents.js';
|
||||
export * from './file-system-provider.js';
|
||||
export * from './index-manager.js';
|
||||
export * from './workspace-lock.js';
|
||||
export * from './workspace-manager.js';
|
||||
export * from './profiler.js';
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workspace/index.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC"}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2025 Y. Daveluy
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the MIT License, which is available in the project root.
|
||||
******************************************************************************/
|
||||
import { MultiMap } from '../utils/collections.js';
|
||||
import type { Stream } from '../utils/stream.js';
|
||||
export type ProfilingCategory = 'validating' | 'parsing' | 'linking';
|
||||
export interface LangiumProfiler {
|
||||
/**
|
||||
* Checks if the given category is active.
|
||||
* @param category The category to check.
|
||||
* @returns `true` if the category is active, `false` otherwise.
|
||||
*/
|
||||
isActive(category: ProfilingCategory): boolean;
|
||||
/**
|
||||
* Starts the profiling for the given categories. If none are provided, all categories are started.
|
||||
* @param categories The categories to start profiling for.
|
||||
*/
|
||||
start(...categories: ProfilingCategory[]): void;
|
||||
/**
|
||||
* Stops the profiling for the given categories. If none are provided, all categories are stopped.
|
||||
* @param categories The categories to stop profiling for.
|
||||
*/
|
||||
stop(...categories: ProfilingCategory[]): void;
|
||||
/**
|
||||
* Creates a new {@link ProfilingTask} for the given category.
|
||||
* @param category The category to create the task for.
|
||||
* @param taskId The identifier of the task.
|
||||
*/
|
||||
createTask(category: ProfilingCategory, taskId: string): ProfilingTask;
|
||||
/**
|
||||
* Gets the {@link ProfilingRecord}s for the given categories. If none are provided, all records are returned.
|
||||
* @param categories The categories to get the records for.
|
||||
* @returns A stream of profiling records.
|
||||
*/
|
||||
getRecords(...categories: ProfilingCategory[]): Stream<ProfilingRecord>;
|
||||
}
|
||||
export declare class DefaultLangiumProfiler implements LangiumProfiler {
|
||||
protected activeCategories: Set<ProfilingCategory>;
|
||||
protected readonly allCategories: ReadonlySet<ProfilingCategory>;
|
||||
protected readonly records: MultiMap<string, ProfilingRecord>;
|
||||
constructor(activeCategories?: Set<ProfilingCategory>);
|
||||
isActive(category: ProfilingCategory): boolean;
|
||||
start(...categories: ProfilingCategory[]): void;
|
||||
stop(...categories: ProfilingCategory[]): void;
|
||||
createTask(category: ProfilingCategory, taskId: string): ProfilingTask;
|
||||
protected dumpRecord(category: string, record: ProfilingRecord): ProfilingRecord;
|
||||
getRecords(...categories: ProfilingCategory[]): Stream<ProfilingRecord>;
|
||||
}
|
||||
export interface ProfilingRecord {
|
||||
identifier: string;
|
||||
date: Date;
|
||||
duration: number;
|
||||
entries: MultiMap<string, number>;
|
||||
}
|
||||
export declare class ProfilingTask {
|
||||
protected startTime?: number;
|
||||
protected readonly addRecord: (record: ProfilingRecord) => void;
|
||||
protected readonly identifier: string;
|
||||
protected readonly stack: Array<{
|
||||
id: string;
|
||||
start: number;
|
||||
content: number;
|
||||
}>;
|
||||
protected readonly entries: MultiMap<string, number>;
|
||||
constructor(addRecord: (record: ProfilingRecord) => void, identifier: string);
|
||||
start(): void;
|
||||
stop(): void;
|
||||
startSubTask(subTaskId: string): void;
|
||||
stopSubTask(subTaskId: string): void;
|
||||
}
|
||||
//# sourceMappingURL=profiler.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"profiler.d.ts","sourceRoot":"","sources":["../../src/workspace/profiler.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG,SAAS,GAAG,SAAS,CAAC;AAErE,MAAM,WAAW,eAAe;IAE5B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC;IAE/C;;;OAGG;IACH,KAAK,CAAC,GAAG,UAAU,EAAE,iBAAiB,EAAE,GAAG,IAAI,CAAC;IAEhD;;;OAGG;IACH,IAAI,CAAC,GAAG,UAAU,EAAE,iBAAiB,EAAE,GAAG,IAAI,CAAC;IAE/C;;;;OAIG;IACH,UAAU,CAAC,QAAQ,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC;IAEvE;;;;OAIG;IACH,UAAU,CAAC,GAAG,UAAU,EAAE,iBAAiB,EAAE,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC;CAC3E;AAED,qBAAa,sBAAuB,YAAW,eAAe;IAC1D,SAAS,CAAC,gBAAgB,EAAE,GAAG,CAAC,iBAAiB,CAAC,CAAgC;IAClF,SAAS,CAAC,QAAQ,CAAC,aAAa,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAAsE;IACtI,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;gBAElD,gBAAgB,CAAC,EAAE,GAAG,CAAC,iBAAiB,CAAC;IAKrD,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO;IAI9C,KAAK,CAAC,GAAG,UAAU,EAAE,iBAAiB,EAAE,GAAG,IAAI;IAU/C,IAAI,CAAC,GAAG,UAAU,EAAE,iBAAiB,EAAE,GAAG,IAAI;IAQ9C,UAAU,CAAC,QAAQ,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa;IAQtE,SAAS,CAAC,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,eAAe;IAqBhF,UAAU,CAAC,GAAG,UAAU,EAAE,iBAAiB,EAAE,GAAG,MAAM,CAAC,eAAe,CAAC;CAS1E;AAED,MAAM,WAAW,eAAe;IAE5B,UAAU,EAAE,MAAM,CAAA;IAElB,IAAI,EAAE,IAAI,CAAA;IAEV,QAAQ,EAAE,MAAM,CAAA;IAGhB,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACpC;AAED,qBAAa,aAAa;IACtB,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC7B,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAEhE,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IACtC,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAM;IACrF,SAAS,CAAC,QAAQ,CAAC,OAAO,2BAAkC;gBAEhD,SAAS,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,EAAE,UAAU,EAAE,MAAM;IAK5E,KAAK,IAAI,IAAI;IAOb,IAAI,IAAI,IAAI;IAkBZ,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;CAkBvC"}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2025 Y. Daveluy
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the MIT License, which is available in the project root.
|
||||
******************************************************************************/
|
||||
import { MultiMap } from '../utils/collections.js';
|
||||
export class DefaultLangiumProfiler {
|
||||
constructor(activeCategories) {
|
||||
this.activeCategories = new Set();
|
||||
this.allCategories = new Set(['validating', 'parsing', 'linking']);
|
||||
this.activeCategories = activeCategories ?? new Set(this.allCategories);
|
||||
this.records = new MultiMap();
|
||||
}
|
||||
isActive(category) {
|
||||
return this.activeCategories.has(category);
|
||||
}
|
||||
start(...categories) {
|
||||
if (!categories) {
|
||||
// Create a new set with all categories (immutable copy)
|
||||
this.activeCategories = new Set(this.allCategories);
|
||||
}
|
||||
else {
|
||||
categories.forEach(category => this.activeCategories.add(category));
|
||||
}
|
||||
}
|
||||
stop(...categories) {
|
||||
if (!categories) {
|
||||
this.activeCategories.clear();
|
||||
}
|
||||
else {
|
||||
categories.forEach(category => this.activeCategories.delete(category));
|
||||
}
|
||||
}
|
||||
createTask(category, taskId) {
|
||||
if (!this.isActive(category)) {
|
||||
throw new Error(`Category "${category}" is not active.`);
|
||||
}
|
||||
console.log(`Creating profiling task for '${category}.${taskId}'.`);
|
||||
return new ProfilingTask((record) => this.records.add(category, this.dumpRecord(category, record)), taskId);
|
||||
}
|
||||
dumpRecord(category, record) {
|
||||
console.info(`Task ${category}.${record.identifier} executed in ${record.duration.toFixed(2)}ms and ended at ${record.date.toISOString()}`);
|
||||
const result = [];
|
||||
for (const key of record.entries.keys()) {
|
||||
const values = record.entries.get(key);
|
||||
const duration = values.reduce((p, c) => p + c);
|
||||
result.push({ name: `${record.identifier}.${key}`, count: values.length, duration: duration });
|
||||
}
|
||||
// sum all duration
|
||||
const taskInternalDuration = record.duration - result.map(r => r.duration).reduce((a, b) => a + b, 0);
|
||||
result.push({ name: record.identifier, count: 1, duration: taskInternalDuration });
|
||||
result.sort((a, b) => b.duration - a.duration);
|
||||
function Round(value) { return Math.round(100 * value) / 100; }
|
||||
console.table(result.map(e => { return { Element: e.name, Count: e.count, 'Self %': Round(100 * e.duration / record.duration), 'Time (ms)': Round(e.duration) }; }));
|
||||
return record;
|
||||
}
|
||||
getRecords(...categories) {
|
||||
if (categories.length === 0) {
|
||||
// return all records
|
||||
return this.records.values();
|
||||
}
|
||||
else {
|
||||
// return records for the given categories
|
||||
return this.records.entries().filter((e) => categories.some(c => c === e[0])).flatMap(e => e[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
export class ProfilingTask {
|
||||
constructor(addRecord, identifier) {
|
||||
this.stack = [];
|
||||
this.entries = new MultiMap();
|
||||
this.addRecord = addRecord;
|
||||
this.identifier = identifier;
|
||||
}
|
||||
start() {
|
||||
if (this.startTime !== undefined) {
|
||||
throw new Error(`Task "${this.identifier}" is already started.`);
|
||||
}
|
||||
this.startTime = performance.now();
|
||||
}
|
||||
stop() {
|
||||
if (this.startTime === undefined) {
|
||||
throw new Error(`Task "${this.identifier}" was not started.`);
|
||||
}
|
||||
if (this.stack.length !== 0) {
|
||||
throw new Error(`Task "${this.identifier}" cannot be stopped before sub-task(s): ${this.stack.map(s => s.id).join(', ')}.`);
|
||||
}
|
||||
const record = {
|
||||
identifier: this.identifier,
|
||||
date: new Date(),
|
||||
duration: performance.now() - this.startTime,
|
||||
entries: this.entries
|
||||
};
|
||||
this.addRecord(record);
|
||||
this.startTime = undefined;
|
||||
this.entries.clear();
|
||||
}
|
||||
startSubTask(subTaskId) {
|
||||
this.stack.push({ id: subTaskId, start: performance.now(), content: 0 });
|
||||
}
|
||||
stopSubTask(subTaskId) {
|
||||
const subStack = this.stack.pop();
|
||||
if (!subStack) {
|
||||
throw new Error(`Task "${this.identifier}.${subTaskId}" was not started.`);
|
||||
}
|
||||
if (subStack.id !== subTaskId) {
|
||||
throw new Error(`Sub-Task "${subStack.id}" is not already stopped.`);
|
||||
}
|
||||
const duration = performance.now() - subStack.start;
|
||||
if (this.stack.at(-1) !== undefined) {
|
||||
this.stack[this.stack.length - 1].content += duration;
|
||||
}
|
||||
// we are interested here by the duration of the current sub-task without the duration of nested sub-tasks.
|
||||
const selfDuration = duration - subStack.content;
|
||||
this.entries.add(subTaskId, selfDuration);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=profiler.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+46
@@ -0,0 +1,46 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2023 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.
|
||||
******************************************************************************/
|
||||
import { CancellationToken } from '../utils/cancellation.js';
|
||||
import { type MaybePromise } from '../utils/promise-utils.js';
|
||||
/**
|
||||
* Utility service to execute mutually exclusive actions.
|
||||
*/
|
||||
export interface WorkspaceLock {
|
||||
/**
|
||||
* Performs a single async action, like initializing the workspace or processing document changes.
|
||||
* Only one action will be executed at a time.
|
||||
*
|
||||
* When another action is queued up, the token provided for the action will be cancelled.
|
||||
* Assuming the action makes use of this token, the next action only has to wait for the current action to finish cancellation.
|
||||
*/
|
||||
write(action: (token: CancellationToken) => MaybePromise<void>): Promise<void>;
|
||||
/**
|
||||
* Performs a single action, like computing completion results or providing workspace symbols.
|
||||
* Read actions will only be executed after all write actions have finished. They will be executed in parallel if possible.
|
||||
*
|
||||
* If a write action is currently running, the read action will be queued up and executed afterwards.
|
||||
* If a new write action is queued up while a read action is waiting, the write action will receive priority and will be handled before the read action.
|
||||
*
|
||||
* Note that read actions are not allowed to modify anything in the workspace. Please use {@link write} instead.
|
||||
*/
|
||||
read<T>(action: () => MaybePromise<T>): Promise<T>;
|
||||
/**
|
||||
* Cancels the last queued write action. All previous write actions already have been cancelled.
|
||||
*/
|
||||
cancelWrite(): void;
|
||||
}
|
||||
export declare class DefaultWorkspaceLock implements WorkspaceLock {
|
||||
private previousTokenSource;
|
||||
private writeQueue;
|
||||
private readQueue;
|
||||
private done;
|
||||
write(action: (token: CancellationToken) => MaybePromise<void>): Promise<void>;
|
||||
read<T>(action: () => MaybePromise<T>): Promise<T>;
|
||||
private enqueue;
|
||||
private performNextOperation;
|
||||
cancelWrite(): void;
|
||||
}
|
||||
//# sourceMappingURL=workspace-lock.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"workspace-lock.d.ts","sourceRoot":"","sources":["../../src/workspace/workspace-lock.ts"],"names":[],"mappings":"AAAA;;;;gFAIgF;AAEhF,OAAO,EAAwC,iBAAiB,EAA2B,MAAM,0BAA0B,CAAC;AAC5H,OAAO,EAA4D,KAAK,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAExH;;GAEG;AACH,MAAM,WAAW,aAAa;IAC1B;;;;;;OAMG;IACH,KAAK,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,YAAY,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/E;;;;;;;;OAQG;IACH,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAEnD;;OAEG;IACH,WAAW,IAAI,IAAI,CAAC;CACvB;AAUD,qBAAa,oBAAqB,YAAW,aAAa;IAEtD,OAAO,CAAC,mBAAmB,CAAkE;IAC7F,OAAO,CAAC,UAAU,CAAmB;IACrC,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,IAAI,CAAQ;IAEpB,KAAK,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,YAAY,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAO9E,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAIlD,OAAO,CAAC,OAAO;YAYD,oBAAoB;IAiClC,WAAW,IAAI,IAAI;CAGtB"}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2023 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.
|
||||
******************************************************************************/
|
||||
import { CancellationToken, CancellationTokenSource } from '../utils/cancellation.js';
|
||||
import { Deferred, isOperationCancelled, startCancelableOperation } from '../utils/promise-utils.js';
|
||||
export class DefaultWorkspaceLock {
|
||||
constructor() {
|
||||
this.previousTokenSource = new CancellationTokenSource();
|
||||
this.writeQueue = [];
|
||||
this.readQueue = [];
|
||||
this.done = true;
|
||||
}
|
||||
write(action) {
|
||||
this.cancelWrite();
|
||||
const tokenSource = startCancelableOperation();
|
||||
this.previousTokenSource = tokenSource;
|
||||
return this.enqueue(this.writeQueue, action, tokenSource.token);
|
||||
}
|
||||
read(action) {
|
||||
return this.enqueue(this.readQueue, action);
|
||||
}
|
||||
enqueue(queue, action, cancellationToken = CancellationToken.None) {
|
||||
const deferred = new Deferred();
|
||||
const entry = {
|
||||
action,
|
||||
deferred,
|
||||
cancellationToken
|
||||
};
|
||||
queue.push(entry);
|
||||
this.performNextOperation();
|
||||
return deferred.promise;
|
||||
}
|
||||
async performNextOperation() {
|
||||
if (!this.done) {
|
||||
return;
|
||||
}
|
||||
const entries = [];
|
||||
if (this.writeQueue.length > 0) {
|
||||
// Just perform the next write action
|
||||
entries.push(this.writeQueue.shift());
|
||||
}
|
||||
else if (this.readQueue.length > 0) {
|
||||
// Empty the read queue and perform all actions in parallel
|
||||
entries.push(...this.readQueue.splice(0, this.readQueue.length));
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
this.done = false;
|
||||
await Promise.all(entries.map(async ({ action, deferred, cancellationToken }) => {
|
||||
try {
|
||||
// Move the execution of the action to the next event loop tick via `Promise.resolve()`
|
||||
const result = await Promise.resolve().then(() => action(cancellationToken));
|
||||
deferred.resolve(result);
|
||||
}
|
||||
catch (err) {
|
||||
if (isOperationCancelled(err)) {
|
||||
// If the operation was cancelled, we don't want to reject the promise
|
||||
deferred.resolve(undefined);
|
||||
}
|
||||
else {
|
||||
deferred.reject(err);
|
||||
}
|
||||
}
|
||||
}));
|
||||
this.done = true;
|
||||
this.performNextOperation();
|
||||
}
|
||||
cancelWrite() {
|
||||
this.previousTokenSource.cancel();
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=workspace-lock.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"workspace-lock.js","sourceRoot":"","sources":["../../src/workspace/workspace-lock.ts"],"names":[],"mappings":"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"}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2022 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.
|
||||
******************************************************************************/
|
||||
import type { InitializeParams, InitializedParams } from 'vscode-languageserver-protocol';
|
||||
import type { WorkspaceFolder } from 'vscode-languageserver-types';
|
||||
import type { ServiceRegistry } from '../service-registry.js';
|
||||
import type { LangiumSharedCoreServices } from '../services.js';
|
||||
import { CancellationToken } from '../utils/cancellation.js';
|
||||
import { Deferred } from '../utils/promise-utils.js';
|
||||
import { URI } from '../utils/uri-utils.js';
|
||||
import type { BuildOptions, DocumentBuilder } from './document-builder.js';
|
||||
import type { LangiumDocument, LangiumDocuments } from './documents.js';
|
||||
import type { FileSystemNode, FileSystemProvider } from './file-system-provider.js';
|
||||
import type { WorkspaceLock } from './workspace-lock.js';
|
||||
import { type Stream } from '../utils/stream.js';
|
||||
export type { WorkspaceFolder };
|
||||
/**
|
||||
* The workspace manager is responsible for finding source files in the workspace.
|
||||
* This service is shared between all languages of a language server.
|
||||
*/
|
||||
export interface WorkspaceManager {
|
||||
/** The options used for the initial workspace build. */
|
||||
initialBuildOptions: BuildOptions | undefined;
|
||||
/**
|
||||
* A promise that resolves when the workspace manager is ready to be used.
|
||||
* Use this to ensure that the workspace manager has finished its initialization.
|
||||
*/
|
||||
readonly ready: Promise<void>;
|
||||
/**
|
||||
* The workspace folders of the current workspace.
|
||||
* Available only after the `ready` promise resolves.
|
||||
*/
|
||||
get workspaceFolders(): readonly WorkspaceFolder[] | undefined;
|
||||
/**
|
||||
* When used in a language server context, this method is called when the server receives
|
||||
* the `initialize` request.
|
||||
*/
|
||||
initialize(params: InitializeParams): void;
|
||||
/**
|
||||
* When used in a language server context, this method is called when the server receives
|
||||
* the `initialized` notification.
|
||||
*/
|
||||
initialized(params: InitializedParams): Promise<void>;
|
||||
/**
|
||||
* Does the initial indexing of workspace folders.
|
||||
* Collects information about exported and referenced AstNodes in
|
||||
* each language file and stores it locally.
|
||||
*
|
||||
* @param folders The set of workspace folders to be indexed.
|
||||
* @param cancelToken A cancellation token that can be used to cancel the operation.
|
||||
*
|
||||
* @throws OperationCancelled if a cancellation event has been detected
|
||||
*/
|
||||
initializeWorkspace(folders: WorkspaceFolder[], cancelToken?: CancellationToken): Promise<void>;
|
||||
/**
|
||||
* Searches for workspace files in the given folder and its subdirectories.
|
||||
* Note that this method does not create documents for the found files.
|
||||
* @param uri The URI of the folder to search in.
|
||||
* @returns A promise that resolves to an array of URIs of the found files.
|
||||
*/
|
||||
searchFolder(uri: URI): Promise<URI[]>;
|
||||
/**
|
||||
* Determine whether the given file system node shall be included in the workspace.
|
||||
* @param entry The file system node to check.
|
||||
* @returns `true` if the entry shall be included, `false` otherwise.
|
||||
*/
|
||||
shouldIncludeEntry(entry: FileSystemNode): boolean;
|
||||
}
|
||||
/**
|
||||
* The FileSelector provides file names and extensions used by this extension.
|
||||
*/
|
||||
export interface FileSelector {
|
||||
/** Allowed file extensions (e.g., ["ts", "js"]). */
|
||||
fileExtensions: string[];
|
||||
/** Allowed file names (e.g., ["config", "settings"]). */
|
||||
fileNames: string[];
|
||||
}
|
||||
export declare class DefaultWorkspaceManager implements WorkspaceManager {
|
||||
initialBuildOptions: BuildOptions;
|
||||
protected readonly serviceRegistry: ServiceRegistry;
|
||||
protected readonly langiumDocuments: LangiumDocuments;
|
||||
protected readonly documentBuilder: DocumentBuilder;
|
||||
protected readonly fileSystemProvider: FileSystemProvider;
|
||||
protected readonly mutex: WorkspaceLock;
|
||||
protected readonly _ready: Deferred<void>;
|
||||
protected folders?: WorkspaceFolder[];
|
||||
constructor(services: LangiumSharedCoreServices);
|
||||
get ready(): Promise<void>;
|
||||
get workspaceFolders(): readonly WorkspaceFolder[] | undefined;
|
||||
initialize(params: InitializeParams): void;
|
||||
initialized(_params: InitializedParams): Promise<void>;
|
||||
initializeWorkspace(folders: WorkspaceFolder[], cancelToken?: CancellationToken): Promise<void>;
|
||||
/**
|
||||
* Performs the uninterruptable startup sequence of the workspace manager.
|
||||
* This methods loads all documents in the workspace and other documents and returns them.
|
||||
*/
|
||||
protected performStartup(folders: WorkspaceFolder[]): Promise<LangiumDocument[]>;
|
||||
protected loadWorkspaceDocuments(uris: Stream<URI>, collector: (document: LangiumDocument) => void): Promise<void>;
|
||||
/**
|
||||
* Load all additional documents that shall be visible in the context of the given workspace
|
||||
* folders and add them to the collector. This can be used to include built-in libraries of
|
||||
* your language, which can be either loaded from provided files or constructed in memory.
|
||||
*/
|
||||
protected loadAdditionalDocuments(_folders: WorkspaceFolder[], _collector: (document: LangiumDocument) => void): Promise<void>;
|
||||
/**
|
||||
* Determine the root folder of the source documents in the given workspace folder.
|
||||
* The default implementation returns the URI of the workspace folder, but you can override
|
||||
* this to return a subfolder like `src` instead.
|
||||
*/
|
||||
protected getRootFolder(workspaceFolder: WorkspaceFolder): URI;
|
||||
/**
|
||||
* Traverse the file system folder identified by the given URI and its subfolders. All
|
||||
* contained files that match the file extensions are added to the `uris` array.
|
||||
*/
|
||||
protected traverseFolder(folderPath: URI, uris: URI[]): Promise<void>;
|
||||
searchFolder(uri: URI): Promise<URI[]>;
|
||||
/**
|
||||
* Determine whether the given folder entry shall be included while indexing the workspace.
|
||||
*/
|
||||
shouldIncludeEntry(entry: FileSystemNode): boolean;
|
||||
}
|
||||
//# sourceMappingURL=workspace-manager.d.ts.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"workspace-manager.d.ts","sourceRoot":"","sources":["../../src/workspace/workspace-manager.ts"],"names":[],"mappings":"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"}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
/******************************************************************************
|
||||
* Copyright 2022 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.
|
||||
******************************************************************************/
|
||||
import { CancellationToken } from '../utils/cancellation.js';
|
||||
import { Deferred, interruptAndCheck } from '../utils/promise-utils.js';
|
||||
import { URI, UriUtils } from '../utils/uri-utils.js';
|
||||
import { stream } from '../utils/stream.js';
|
||||
export class DefaultWorkspaceManager {
|
||||
constructor(services) {
|
||||
this.initialBuildOptions = {};
|
||||
this._ready = new Deferred();
|
||||
this.serviceRegistry = services.ServiceRegistry;
|
||||
this.langiumDocuments = services.workspace.LangiumDocuments;
|
||||
this.documentBuilder = services.workspace.DocumentBuilder;
|
||||
this.fileSystemProvider = services.workspace.FileSystemProvider;
|
||||
this.mutex = services.workspace.WorkspaceLock;
|
||||
}
|
||||
get ready() {
|
||||
return this._ready.promise;
|
||||
}
|
||||
get workspaceFolders() {
|
||||
return this.folders;
|
||||
}
|
||||
initialize(params) {
|
||||
this.folders = params.workspaceFolders ?? undefined;
|
||||
}
|
||||
initialized(_params) {
|
||||
// Initialize the workspace even if there are no workspace folders
|
||||
// We still want to load additional documents (language library or similar) during initialization
|
||||
return this.mutex.write(token => this.initializeWorkspace(this.folders ?? [], token));
|
||||
}
|
||||
async initializeWorkspace(folders, cancelToken = CancellationToken.None) {
|
||||
const documents = await this.performStartup(folders);
|
||||
// Only after creating all documents do we check whether we need to cancel the initialization
|
||||
// The document builder will later pick up on all unprocessed documents
|
||||
await interruptAndCheck(cancelToken);
|
||||
await this.documentBuilder.build(documents, this.initialBuildOptions, cancelToken);
|
||||
}
|
||||
/**
|
||||
* Performs the uninterruptable startup sequence of the workspace manager.
|
||||
* This methods loads all documents in the workspace and other documents and returns them.
|
||||
*/
|
||||
async performStartup(folders) {
|
||||
const documents = [];
|
||||
const collector = (document) => {
|
||||
documents.push(document);
|
||||
if (!this.langiumDocuments.hasDocument(document.uri)) {
|
||||
this.langiumDocuments.addDocument(document);
|
||||
}
|
||||
};
|
||||
// Even though we don't await the initialization of the workspace manager,
|
||||
// we can still assume that all library documents and file documents are loaded by the time we start building documents.
|
||||
// The mutex prevents anything from performing a workspace build until we check the cancellation token
|
||||
await this.loadAdditionalDocuments(folders, collector);
|
||||
const uris = [];
|
||||
await Promise.all(folders.map(wf => this.getRootFolder(wf))
|
||||
.map(async (entry) => this.traverseFolder(entry, uris)));
|
||||
const uniqueUris = stream(uris)
|
||||
// Ensure that we only create one document per URI/file
|
||||
.distinct(uri => uri.toString())
|
||||
// Also ensure that the documents don't already exist
|
||||
.filter(uri => !this.langiumDocuments.hasDocument(uri));
|
||||
await this.loadWorkspaceDocuments(uniqueUris, collector);
|
||||
this._ready.resolve();
|
||||
return documents;
|
||||
}
|
||||
async loadWorkspaceDocuments(uris, collector) {
|
||||
await Promise.all(uris.map(async (uri) => {
|
||||
const document = await this.langiumDocuments.getOrCreateDocument(uri);
|
||||
collector(document);
|
||||
}));
|
||||
}
|
||||
/**
|
||||
* Load all additional documents that shall be visible in the context of the given workspace
|
||||
* folders and add them to the collector. This can be used to include built-in libraries of
|
||||
* your language, which can be either loaded from provided files or constructed in memory.
|
||||
*/
|
||||
loadAdditionalDocuments(_folders, _collector) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
/**
|
||||
* Determine the root folder of the source documents in the given workspace folder.
|
||||
* The default implementation returns the URI of the workspace folder, but you can override
|
||||
* this to return a subfolder like `src` instead.
|
||||
*/
|
||||
getRootFolder(workspaceFolder) {
|
||||
return URI.parse(workspaceFolder.uri);
|
||||
}
|
||||
/**
|
||||
* Traverse the file system folder identified by the given URI and its subfolders. All
|
||||
* contained files that match the file extensions are added to the `uris` array.
|
||||
*/
|
||||
async traverseFolder(folderPath, uris) {
|
||||
try {
|
||||
const content = await this.fileSystemProvider.readDirectory(folderPath);
|
||||
await Promise.all(content.map(async (entry) => {
|
||||
if (this.shouldIncludeEntry(entry)) {
|
||||
if (entry.isDirectory) {
|
||||
await this.traverseFolder(entry.uri, uris);
|
||||
}
|
||||
else if (entry.isFile) {
|
||||
uris.push(entry.uri);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
catch (e) {
|
||||
console.error('Failure to read directory content of ' + folderPath.toString(true), e);
|
||||
}
|
||||
}
|
||||
async searchFolder(uri) {
|
||||
const uris = [];
|
||||
await this.traverseFolder(uri, uris);
|
||||
return uris;
|
||||
}
|
||||
/**
|
||||
* Determine whether the given folder entry shall be included while indexing the workspace.
|
||||
*/
|
||||
shouldIncludeEntry(entry) {
|
||||
const name = UriUtils.basename(entry.uri);
|
||||
if (name.startsWith('.')) {
|
||||
return false;
|
||||
}
|
||||
if (entry.isDirectory) {
|
||||
return name !== 'node_modules' && name !== 'out';
|
||||
}
|
||||
else if (entry.isFile) {
|
||||
return this.serviceRegistry.hasServices(entry.uri);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=workspace-manager.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"workspace-manager.js","sourceRoot":"","sources":["../../src/workspace/workspace-manager.ts"],"names":[],"mappings":"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"}
|
||||
Reference in New Issue
Block a user