diff --git a/examples/domainmodel/src/cli/cli-util.ts b/examples/domainmodel/src/cli/cli-util.ts index 060a4b9f4..0e33a05ac 100644 --- a/examples/domainmodel/src/cli/cli-util.ts +++ b/examples/domainmodel/src/cli/cli-util.ts @@ -4,8 +4,7 @@ * terms of the MIT License, which is available in the project root. ******************************************************************************/ -import type { AstNode, LangiumDocument, LangiumCoreServices } from 'langium'; -import type { WorkspaceFolder } from 'vscode-languageserver'; +import type { AstNode, LangiumDocument, LangiumCoreServices, WorkspaceFolder } from 'langium'; import * as fs from 'node:fs'; import * as path from 'node:path'; import chalk from 'chalk'; @@ -53,7 +52,7 @@ export async function setRootFolder(fileName: string, services: LangiumServices, } const folders: WorkspaceFolder[] = [{ name: path.basename(root), - uri: URI.file(root).toString() + uri: URI.file(root) }]; await services.shared.workspace.WorkspaceManager.initializeWorkspace(folders); } diff --git a/examples/domainmodel/test/domainmodel-cli.test.ts b/examples/domainmodel/test/domainmodel-cli.test.ts index a0541e411..bfeded0a4 100644 --- a/examples/domainmodel/test/domainmodel-cli.test.ts +++ b/examples/domainmodel/test/domainmodel-cli.test.ts @@ -37,6 +37,7 @@ describe('Test the domainmodel CLI', () => { } test('Test action without CLI', async () => { + // TODO @montymxb, revert to quiet: true when the import issue is fixed await generateAction(rawfileName, { destination, quiet: false }); commonExpectations(); }); diff --git a/examples/requirements/src/cli/cli-util.ts b/examples/requirements/src/cli/cli-util.ts index 5fc57f414..45fed91c9 100644 --- a/examples/requirements/src/cli/cli-util.ts +++ b/examples/requirements/src/cli/cli-util.ts @@ -4,9 +4,8 @@ * terms of the MIT License, which is available in the project root. ******************************************************************************/ -import type { LangiumDocument, LangiumCoreServices } from 'langium'; +import type { LangiumDocument, LangiumCoreServices, WorkspaceFolder } from 'langium'; import type { RequirementModel, TestModel } from '../language-server/generated/ast.js'; -import type { WorkspaceFolder } from 'vscode-languageclient'; import chalk from 'chalk'; import * as path from 'node:path'; import * as fs from 'node:fs'; @@ -34,7 +33,7 @@ export async function extractDocuments(fileName: string, services: LangiumCoreSe } const folders: WorkspaceFolder[] = [{ - uri: URI.file(path.resolve(path.dirname(fileName))).toString(), + uri: URI.file(path.resolve(path.dirname(fileName))), name: 'main' }]; await services.shared.workspace.WorkspaceManager.initializeWorkspace(folders); diff --git a/packages/langium-sprotty/src/diagram-server-manager.ts b/packages/langium-sprotty/src/diagram-server-manager.ts index 14ab67d56..16f3f58b8 100644 --- a/packages/langium-sprotty/src/diagram-server-manager.ts +++ b/packages/langium-sprotty/src/diagram-server-manager.ts @@ -6,7 +6,7 @@ import type { CancellationToken, Connection } from 'vscode-languageserver'; import type { ActionMessage, DiagramOptions, DiagramServer, RequestModelAction } from 'sprotty-protocol'; -import type { LangiumDocument, CoreServiceRegistryI, URI } from 'langium'; +import type { LangiumDocument, ICoreServiceRegistry, URI } from 'langium'; import type { LangiumSprottyServices, LangiumSprottySharedServices } from './sprotty-services.js'; import type { LangiumDiagramGeneratorArguments } from './diagram-generator.js'; import { isRequestAction, RejectAction } from 'sprotty-protocol'; @@ -34,7 +34,7 @@ export interface DiagramServerManager { export class DefaultDiagramServerManager implements DiagramServerManager { protected readonly connection?: Connection; - protected readonly serviceRegistry: CoreServiceRegistryI; + protected readonly serviceRegistry: ICoreServiceRegistry; protected readonly diagramServerFactory: (clientId: string, options?: DiagramOptions) => DiagramServer; protected readonly diagramServerMap: Map = new Map(); diff --git a/packages/langium-vscode/src/language-server/grammar-workspace-manager.ts b/packages/langium-vscode/src/language-server/grammar-workspace-manager.ts index ef7261b43..7401df8a5 100644 --- a/packages/langium-vscode/src/language-server/grammar-workspace-manager.ts +++ b/packages/langium-vscode/src/language-server/grammar-workspace-manager.ts @@ -5,10 +5,9 @@ ******************************************************************************/ import type { Ignore } from 'ignore'; -import type { LangiumSharedCoreServices, ConfigurationProvider, FileSystemNode } from 'langium'; -import type { WorkspaceFolder } from 'vscode-languageserver-protocol'; +import type { LangiumSharedCoreServices, ConfigurationProvider, FileSystemNode, WorkspaceFolder } from 'langium'; import ignore from 'ignore'; -import { DefaultWorkspaceManager, URI, UriUtils } from 'langium'; +import { DefaultWorkspaceManager, UriUtils } from 'langium'; import * as path from 'path'; import { CancellationToken } from 'vscode-languageserver-protocol'; @@ -41,7 +40,7 @@ export class LangiumGrammarWorkspaceManager extends DefaultWorkspaceManager { protected override includeEntry(workspaceFolder: WorkspaceFolder, entry: FileSystemNode, fileExtensions: string[]): boolean { if (this.matcher) { // create path relative to workspace folder root: /user/foo/workspace/entry.txt -> entry.txt - const relPath = path.relative(URI.parse(workspaceFolder.uri).path, entry.uri.path); + const relPath = path.relative(workspaceFolder.uri.path, entry.uri.path); const ignored = this.matcher.ignores(relPath); return !ignored && (entry.isDirectory || (entry.isFile && fileExtensions.includes(UriUtils.extname(entry.uri)))); } diff --git a/packages/langium/src/lsp/workspace/default-configuration.ts b/packages/langium/src/lsp/workspace/default-configuration.ts index 65e614074..edbc1a6b6 100644 --- a/packages/langium/src/lsp/workspace/default-configuration.ts +++ b/packages/langium/src/lsp/workspace/default-configuration.ts @@ -6,7 +6,7 @@ import type { Connection, DidChangeConfigurationParams } from 'vscode-languageserver'; import type { ConfigurationItem } from 'vscode-languageserver-protocol'; -import type { CoreServiceRegistryI } from '../../service-registry.js'; +import type { ICoreServiceRegistry } from '../../service-registry.js'; import { DidChangeConfigurationNotification } from 'vscode-languageserver-protocol'; import type { ConfigurationProvider } from '../../workspace/configuration.js'; import type { LangiumSharedServices } from '../lsp-services.js'; @@ -21,7 +21,7 @@ export class LSPConfigurationProvider implements ConfigurationProvider { protected settings: Record> = {}; protected workspaceConfig = false; protected initialized = false; - protected readonly serviceRegistry: CoreServiceRegistryI; + protected readonly serviceRegistry: ICoreServiceRegistry; protected readonly connection: Connection | undefined; constructor(services: LangiumSharedServices) { diff --git a/packages/langium/src/lsp/workspace/documents.ts b/packages/langium/src/lsp/workspace/documents.ts index 6c58588a5..4596de250 100644 --- a/packages/langium/src/lsp/workspace/documents.ts +++ b/packages/langium/src/lsp/workspace/documents.ts @@ -6,7 +6,7 @@ import type { TextDocuments } from 'vscode-languageserver'; import type { ParseResult } from '../../parser/langium-parser.js'; -import type { CoreServiceRegistryI } from '../../service-registry.js'; +import type { ICoreServiceRegistry } from '../../service-registry.js'; import type { AstNode } from '../../syntax-tree.js'; import type { Mutable } from '../../utils/ast-util.js'; import type { FileSystemProvider } from './../../workspace/file-system-provider.js'; @@ -20,7 +20,7 @@ import type { LangiumSharedServices } from '../lsp-services.js'; */ export class LSPLangiumDocumentFactory implements LangiumDocumentFactory { - protected readonly serviceRegistry: CoreServiceRegistryI; + protected readonly serviceRegistry: ICoreServiceRegistry; protected readonly textDocuments: TextDocuments; protected readonly fileSystemProvider: FileSystemProvider; diff --git a/packages/langium/src/lsp/workspace/workspace-manager.ts b/packages/langium/src/lsp/workspace/workspace-manager.ts index 40a066367..7ac31e341 100644 --- a/packages/langium/src/lsp/workspace/workspace-manager.ts +++ b/packages/langium/src/lsp/workspace/workspace-manager.ts @@ -4,41 +4,21 @@ * terms of the MIT License, which is available in the project root. ******************************************************************************/ -import { CancellationToken } from 'vscode-languageserver'; -import { interruptAndCheck } from '../../utils/promise-util.js'; -import { URI, UriUtils } from '../../utils/uri-util.js'; -import type { WorkspaceManager } from './../../workspace/workspace-manager.js'; -import type { WorkspaceFolder } from 'vscode-languageserver'; -import type { CoreServiceRegistryI } from '../../service-registry.js'; -import type { MutexLock } from '../../utils/promise-util.js'; -import type { BuildOptions, DocumentBuilder } from './../../workspace/document-builder.js'; -import type { LangiumDocument, LangiumDocuments } from './../../workspace/documents.js'; -import type { FileSystemNode, FileSystemProvider } from './../../workspace/file-system-provider.js'; +import { DefaultWorkspaceManager, type WorkspaceFolder } from './../../workspace/workspace-manager.js'; import type { LangiumSharedServices } from '../lsp-services.js'; /** * Langium Workspace manager that uses LSP workspace folders */ -export class LSPWorkspaceManager implements WorkspaceManager { +export class LSPWorkspaceManager extends DefaultWorkspaceManager { - initialBuildOptions: BuildOptions = {}; - - protected readonly serviceRegistry: CoreServiceRegistryI; - protected readonly langiumDocuments: LangiumDocuments; - protected readonly documentBuilder: DocumentBuilder; - protected readonly fileSystemProvider: FileSystemProvider; - protected readonly mutex: MutexLock; protected folders?: WorkspaceFolder[]; constructor(services: LangiumSharedServices) { - this.serviceRegistry = services.ServiceRegistry; - this.langiumDocuments = services.workspace.LangiumDocuments; - this.documentBuilder = services.workspace.DocumentBuilder; - this.fileSystemProvider = services.workspace.FileSystemProvider; - this.mutex = services.workspace.MutexLock; - + super(services); services.lsp.LanguageServer.onInitialize(params => { - this.folders = params.workspaceFolders ?? undefined; + // convert lsp workspace folders to langium workspace folders (equivalent type plays nice with the existing non-lsp core services) + this.folders = (params.workspaceFolders as unknown as WorkspaceFolder[]) ?? undefined; }); services.lsp.LanguageServer.onInitialized(_params => { @@ -47,81 +27,4 @@ export class LSPWorkspaceManager implements WorkspaceManager { this.mutex.lock(token => this.initializeWorkspace(this.folders ?? [], token)); }); } - - async initializeWorkspace(folders: WorkspaceFolder[], cancelToken = CancellationToken.None): Promise { - const fileExtensions = this.serviceRegistry.all.flatMap(e => e.LanguageMetaData.fileExtensions); - const documents: LangiumDocument[] = []; - const collector = (document: LangiumDocument) => { - 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); - await Promise.all( - folders.map(wf => [wf, this.getRootFolder(wf)] as [WorkspaceFolder, URI]) - .map(async entry => this.traverseFolder(...entry, fileExtensions, collector)) - ); - // 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); - } - - /** - * 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 { - 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. - */ - protected getRootFolder(workspaceFolder: WorkspaceFolder): URI { - 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 collector. - */ - protected async traverseFolder(workspaceFolder: WorkspaceFolder, folderPath: URI, fileExtensions: string[], collector: (document: LangiumDocument) => void): Promise { - const content = await this.fileSystemProvider.readDirectory(folderPath); - await Promise.all(content.map(async entry => { - if (this.includeEntry(workspaceFolder, entry, fileExtensions)) { - if (entry.isDirectory) { - await this.traverseFolder(workspaceFolder, entry.uri, fileExtensions, collector); - } else if (entry.isFile) { - const document = this.langiumDocuments.getOrCreateDocument(entry.uri); - collector(document); - } - } - })); - } - - /** - * Determine whether the given folder entry shall be included while indexing the workspace. - */ - protected includeEntry(workspaceFolder: WorkspaceFolder, entry: FileSystemNode, fileExtensions: string[]): boolean { - const name = UriUtils.basename(entry.uri); - if (name.startsWith('.')) { - return false; - } - if (entry.isDirectory) { - return name !== 'node_modules' && name !== 'out'; - } else if (entry.isFile) { - const extname = UriUtils.extname(entry.uri); - return fileExtensions.includes(extname); - } - return false; - } - } diff --git a/packages/langium/src/service-registry.ts b/packages/langium/src/service-registry.ts index 37f576d29..160ce466d 100644 --- a/packages/langium/src/service-registry.ts +++ b/packages/langium/src/service-registry.ts @@ -8,10 +8,10 @@ import type { LangiumCoreServices } from './services.js'; import { UriUtils, type URI } from './utils/uri-util.js'; /** - * The core service registry provides access to the core language-specific services. These are resolved + * The core service registry provides access to the language-specific core services. These are resolved * via the URI of a text document. */ -export interface CoreServiceRegistryI { +export interface ICoreServiceRegistry { /** * Register a language via its injected services. @@ -33,7 +33,7 @@ export interface CoreServiceRegistryI { /** * Registry for Langium's core services */ -export class CoreServiceRegistry implements CoreServiceRegistryI { +export class CoreServiceRegistry implements ICoreServiceRegistry { protected singleton?: LangiumCoreServices; protected map?: Record; diff --git a/packages/langium/src/services.ts b/packages/langium/src/services.ts index 16c79012c..3dfa82cc4 100644 --- a/packages/langium/src/services.ts +++ b/packages/langium/src/services.ts @@ -21,7 +21,7 @@ import type { References } from './references/references.js'; import type { ScopeComputation } from './references/scope-computation.js'; import type { ScopeProvider } from './references/scope-provider.js'; import type { JsonSerializer } from './serializer/json-serializer.js'; -import type { CoreServiceRegistryI } from './service-registry.js'; +import type { ICoreServiceRegistry } from './service-registry.js'; import type { AstReflection } from './syntax-tree.js'; import type { MutexLock } from './utils/promise-util.js'; import type { DocumentValidator } from './validation/document-validator.js'; @@ -107,7 +107,7 @@ export type LangiumGeneratedSharedServices = { * Services shared between multiple languages where Langium provides default implementations. */ export type LangiumDefaultSharedServices = { - ServiceRegistry: CoreServiceRegistryI + ServiceRegistry: ICoreServiceRegistry workspace: { DocumentBuilder: DocumentBuilder IndexManager: IndexManager diff --git a/packages/langium/src/workspace/configuration.ts b/packages/langium/src/workspace/configuration.ts index 430a2d756..2fbf30c96 100644 --- a/packages/langium/src/workspace/configuration.ts +++ b/packages/langium/src/workspace/configuration.ts @@ -5,7 +5,7 @@ ******************************************************************************/ import type { DidChangeConfigurationParams } from 'vscode-languageserver'; -import type { CoreServiceRegistryI } from '../service-registry.js'; +import type { ICoreServiceRegistry } from '../service-registry.js'; import type { LangiumSharedCoreServices } from '../services.js'; /* eslint-disable @typescript-eslint/no-explicit-any */ @@ -37,7 +37,7 @@ export class EmptyConfigurationProvider implements ConfigurationProvider { protected settings: Record> = {}; protected workspaceConfig = false; protected initialized = false; - protected readonly serviceRegistry: CoreServiceRegistryI; + protected readonly serviceRegistry: ICoreServiceRegistry; constructor(services: LangiumSharedCoreServices) { this.serviceRegistry = services.ServiceRegistry; diff --git a/packages/langium/src/workspace/document-builder.ts b/packages/langium/src/workspace/document-builder.ts index 27f4021ac..e88fc88fe 100644 --- a/packages/langium/src/workspace/document-builder.ts +++ b/packages/langium/src/workspace/document-builder.ts @@ -5,7 +5,7 @@ ******************************************************************************/ import type { URI } from '../utils/uri-util.js'; -import type { CoreServiceRegistryI } from '../service-registry.js'; +import type { ICoreServiceRegistry } from '../service-registry.js'; import type { LangiumSharedCoreServices } from '../services.js'; import type { AstNode } from '../syntax-tree.js'; import type { MaybePromise } from '../utils/promise-util.js'; @@ -95,7 +95,7 @@ export class DefaultDocumentBuilder implements DocumentBuilder { protected readonly langiumDocuments: LangiumDocuments; protected readonly langiumDocumentFactory: LangiumDocumentFactory; protected readonly indexManager: IndexManager; - protected readonly serviceRegistry: CoreServiceRegistryI; + protected readonly serviceRegistry: ICoreServiceRegistry; protected readonly updateListeners: DocumentUpdateListener[] = []; protected readonly buildPhaseListeners: MultiMap = new MultiMap(); protected readonly buildState: Map = new Map(); diff --git a/packages/langium/src/workspace/documents.ts b/packages/langium/src/workspace/documents.ts index 4fee7af67..20d9a30ea 100644 --- a/packages/langium/src/workspace/documents.ts +++ b/packages/langium/src/workspace/documents.ts @@ -7,7 +7,7 @@ import type { Range } from 'vscode-languageserver-textdocument'; import type { Diagnostic } from 'vscode-languageserver'; import type { ParseResult } from '../parser/langium-parser.js'; -import type { CoreServiceRegistryI } from '../service-registry.js'; +import type { ICoreServiceRegistry } from '../service-registry.js'; import type { LangiumSharedCoreServices } from '../services.js'; import type { AstNode, AstNodeDescription, Reference } from '../syntax-tree.js'; import type { Mutable } from '../utils/ast-util.js'; @@ -140,7 +140,7 @@ export interface LangiumDocumentFactory { export class DefaultLangiumDocumentFactory implements LangiumDocumentFactory { - protected readonly serviceRegistry: CoreServiceRegistryI; + protected readonly serviceRegistry: ICoreServiceRegistry; protected readonly fileSystemProvider: FileSystemProvider; constructor(services: LangiumSharedCoreServices) { diff --git a/packages/langium/src/workspace/index-manager.ts b/packages/langium/src/workspace/index-manager.ts index 069cc7ebf..f8c4070e6 100644 --- a/packages/langium/src/workspace/index-manager.ts +++ b/packages/langium/src/workspace/index-manager.ts @@ -5,7 +5,7 @@ ******************************************************************************/ import type { URI } from '../utils/uri-util.js'; -import type { CoreServiceRegistryI } from '../service-registry.js'; +import type { ICoreServiceRegistry } from '../service-registry.js'; import type { LangiumSharedCoreServices } from '../services.js'; import type { AstNode, AstNodeDescription, AstReflection } from '../syntax-tree.js'; import type { Stream } from '../utils/stream.js'; @@ -83,7 +83,7 @@ export interface IndexManager { export class DefaultIndexManager implements IndexManager { - protected readonly serviceRegistry: CoreServiceRegistryI; + protected readonly serviceRegistry: ICoreServiceRegistry; protected readonly documents: LangiumDocuments; protected readonly astReflection: AstReflection; diff --git a/packages/langium/src/workspace/workspace-manager.ts b/packages/langium/src/workspace/workspace-manager.ts index 1f93a8377..9446ad023 100644 --- a/packages/langium/src/workspace/workspace-manager.ts +++ b/packages/langium/src/workspace/workspace-manager.ts @@ -5,14 +5,95 @@ ******************************************************************************/ import { URI, UriUtils } from '../utils/uri-util.js'; -import type { CancellationToken, WorkspaceFolder } from 'vscode-languageserver'; -import type { CoreServiceRegistryI } from '../service-registry.js'; +import type { ICoreServiceRegistry } from '../service-registry.js'; import type { LangiumSharedCoreServices } from '../services.js'; -import type { MutexLock } from '../utils/promise-util.js'; +import { interruptAndCheck, type MutexLock } from '../utils/promise-util.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'; +// +// BEGIN VSCODE-LANGUAGESERVER PLACEHOLDER TYPES +// + +/** + * A workspace folder inside a client. + */ +export interface WorkspaceFolder { + /** + * The associated URI for this workspace folder. + */ + uri: URI; + /** + * The name of the workspace folder. Used to refer to this + * workspace folder in the user interface. + */ + name: string; +} + +declare namespace WorkspaceFolder { + function is(value: unknown): value is WorkspaceFolder; +} + +interface Disposable { + /** + * Dispose this object. + */ + dispose(): void; +} +declare namespace Disposable { + function create(func: () => void): Disposable; +} + +/** + * Represents a typed event. + */ +interface Event { + /** + * + * @param listener The listener function will be called when the event happens. + * @param thisArgs The 'this' which will be used when calling the event listener. + * @param disposables An array to which a {{IDisposable}} will be added. + * @return + */ + (listener: (e: T) => unknown, thisArgs?: unknown, disposables?: Disposable[]): Disposable; +} +declare namespace Event { + const None: Event; +} + +/** + * Defines a CancellationToken. This interface is not + * intended to be implemented. A CancellationToken must + * be created via a CancellationTokenSource. + */ +export interface CancellationToken { + /** + * Is `true` when the token has been cancelled, `false` otherwise. + */ + readonly isCancellationRequested: boolean; + /** + * An {@link Event event} which fires upon cancellation. + */ + readonly onCancellationRequested: Event; +} + +/** + * Placeholder object for a no-cancel token. + * Replaces the usage of None from the namespace + * `CancellationToken` from `vscode-languageserver-protocol`. + */ +const CancellationToken = { + None: { + isCancellationRequested: false, + onCancellationRequested: Event.None + } +}; + +// +// END VSCODE-LANGUAGESERVER PLACEHOLDER TYPES +// + /** * The workspace manager is responsible for finding source files in the workspace. * This service is shared between all languages of a language server. @@ -37,12 +118,11 @@ export class DefaultWorkspaceManager implements WorkspaceManager { initialBuildOptions: BuildOptions = {}; - protected readonly serviceRegistry: CoreServiceRegistryI; + protected readonly serviceRegistry: ICoreServiceRegistry; protected readonly langiumDocuments: LangiumDocuments; protected readonly documentBuilder: DocumentBuilder; protected readonly fileSystemProvider: FileSystemProvider; protected readonly mutex: MutexLock; - // protected folders?: WorkspaceFolder[]; constructor(services: LangiumSharedCoreServices) { this.serviceRegistry = services.ServiceRegistry; @@ -51,11 +131,31 @@ export class DefaultWorkspaceManager implements WorkspaceManager { this.fileSystemProvider = services.workspace.FileSystemProvider; this.mutex = services.workspace.MutexLock; - // for usage w/ LSP services, reference LSPWorkspaceManager instead + // for usage w/ LSP services, refer to the LSPWorkspaceManager instead } - async initializeWorkspace(_folders: WorkspaceFolder[], _cancelToken: CancellationToken): Promise { - return Promise.resolve(); + async initializeWorkspace(folders: WorkspaceFolder[], cancelToken = CancellationToken.None): Promise { + // return Promise.resolve(); + const fileExtensions = this.serviceRegistry.all.flatMap(e => e.LanguageMetaData.fileExtensions); + const documents: LangiumDocument[] = []; + const collector = (document: LangiumDocument) => { + 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); + await Promise.all( + folders.map(wf => [wf, this.getRootFolder(wf)] as [WorkspaceFolder, URI]) + .map(async entry => this.traverseFolder(...entry, fileExtensions, collector)) + ); + // 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); } /** @@ -73,7 +173,7 @@ export class DefaultWorkspaceManager implements WorkspaceManager { * this to return a subfolder like `src` instead. */ protected getRootFolder(workspaceFolder: WorkspaceFolder): URI { - return URI.parse(workspaceFolder.uri); + return workspaceFolder.uri; } /**