/
workspace-manager.ts
114 lines (98 loc) · 4.95 KB
/
workspace-manager.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/******************************************************************************
* 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 { URI, UriUtils } from '../utils/uri-util.js';
import type { CancellationToken, WorkspaceFolder } from 'vscode-languageserver';
import type { CoreServiceRegistryI } from '../service-registry.js';
import type { LangiumSharedCoreServices } from '../services.js';
import 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';
/**
* 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;
/**
* 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.
*/
initializeWorkspace(folders: WorkspaceFolder[], cancelToken?: CancellationToken): Promise<void>;
}
export class DefaultWorkspaceManager implements WorkspaceManager {
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: LangiumSharedCoreServices) {
this.serviceRegistry = services.ServiceRegistry;
this.langiumDocuments = services.workspace.LangiumDocuments;
this.documentBuilder = services.workspace.DocumentBuilder;
this.fileSystemProvider = services.workspace.FileSystemProvider;
this.mutex = services.workspace.MutexLock;
// for usage w/ LSP services, reference LSPWorkspaceManager instead
}
async initializeWorkspace(_folders: WorkspaceFolder[], _cancelToken: CancellationToken): Promise<void> {
return Promise.resolve();
}
/**
* 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> {
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<void> {
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;
}
}