Skip to content

Commit 43c0a45

Browse files
committed
[mlir-vscode] Add better support for multiple workspace folders
We currently only launch one set of language clients when starting the extension, but this has the unfortunate effect of applying the same settings to all workspace folders. This commit adds support for multiple workspace folders by launching a server for each folder in the workspace. This allows for having different servers for different workspace folders, e.g. when there are multiple MLIR projects in the same workspace. Differential Revision: https://reviews.llvm.org/D122793
1 parent ade148d commit 43c0a45

File tree

4 files changed

+85
-27
lines changed

4 files changed

+85
-27
lines changed

mlir/utils/vscode/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "vscode-mlir",
33
"displayName": "MLIR",
44
"description": "MLIR Language Extension",
5-
"version": "0.0.4",
5+
"version": "0.0.5",
66
"publisher": "llvm-vs-code-extensions",
77
"homepage": "https://mlir.llvm.org/",
88
"icon": "icon.png",

mlir/utils/vscode/src/config.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import * as vscode from 'vscode';
22

33
/**
4-
* Gets the config value `mlir.<key>`.
4+
* Gets the config value `mlir.<key>`, with an optional workspace folder.
55
*/
6-
export function get<T>(key: string): T {
7-
return vscode.workspace.getConfiguration('mlir').get<T>(key);
6+
export function get<T>(key: string,
7+
workspaceFolder: vscode.WorkspaceFolder = null): T {
8+
return vscode.workspace.getConfiguration('mlir', workspaceFolder).get<T>(key);
89
}
910

1011
/**

mlir/utils/vscode/src/configWatcher.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,19 @@ async function promptRestart(settingName: string, promptMessage: string) {
3838
}
3939

4040
/**
41-
* Activate the watchers that track configuration changes which decide when to
42-
* restart the server.
41+
* Activate watchers that track configuration changes for the given workspace
42+
* folder, or null if the workspace is top-level.
4343
*/
4444
export async function activate(mlirContext: MLIRContext,
45+
workspaceFolder: vscode.WorkspaceFolder,
4546
serverPathsToWatch: string[]) {
4647
// When a configuration change happens, check to see if we should restart the
4748
// server.
4849
mlirContext.subscriptions.push(vscode.workspace.onDidChangeConfiguration(event => {
4950
const settings: string[] = [ 'server_path', 'pdll_server_path' ];
5051
for (const setting of settings) {
5152
const expandedSetting = `mlir.${setting}`;
52-
if (event.affectsConfiguration(expandedSetting)) {
53+
if (event.affectsConfiguration(expandedSetting, workspaceFolder)) {
5354
promptRestart(
5455
'onSettingsChanged',
5556
`setting '${

mlir/utils/vscode/src/mlirContext.ts

Lines changed: 76 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,46 +6,84 @@ import * as vscodelc from 'vscode-languageclient';
66
import * as config from './config';
77
import * as configWatcher from './configWatcher';
88

9+
/**
10+
* This class represents the context of a specific workspace folder.
11+
*/
12+
class WorkspaceFolderContext {
13+
constructor(mlirServer: vscodelc.LanguageClient,
14+
pdllServer: vscodelc.LanguageClient) {
15+
this.mlirServer = mlirServer;
16+
this.pdllServer = pdllServer;
17+
}
18+
mlirServer!: vscodelc.LanguageClient;
19+
pdllServer!: vscodelc.LanguageClient;
20+
}
21+
922
/**
1023
* This class manages all of the MLIR extension state,
1124
* including the language client.
1225
*/
1326
export class MLIRContext implements vscode.Disposable {
1427
subscriptions: vscode.Disposable[] = [];
15-
client!: vscodelc.LanguageClient;
16-
pdllClient!: vscodelc.LanguageClient;
28+
workspaceFolders: WorkspaceFolderContext[] = [];
1729

1830
/**
1931
* Activate the MLIR context, and start the language clients.
2032
*/
2133
async activate(outputChannel: vscode.OutputChannel,
2234
warnOnEmptyServerPath: boolean) {
23-
// Create the language clients for mlir and pdll.
24-
let mlirServerPath: string, pdllServerPath: string;
25-
[this.client, mlirServerPath] = await this.startLanguageClient(
26-
outputChannel, warnOnEmptyServerPath, 'server_path', 'mlir');
27-
[this.pdllClient, pdllServerPath] = await this.startLanguageClient(
28-
outputChannel, warnOnEmptyServerPath, 'pdll_server_path', 'pdll');
35+
// Start clients for each workspace folder.
36+
if (vscode.workspace.workspaceFolders &&
37+
vscode.workspace.workspaceFolders.length > 0) {
38+
for (const workspaceFolder of vscode.workspace.workspaceFolders) {
39+
this.workspaceFolders.push(await this.activateWorkspaceFolder(
40+
workspaceFolder, outputChannel, warnOnEmptyServerPath));
41+
}
42+
} else {
43+
this.workspaceFolders.push(await this.activateWorkspaceFolder(
44+
null, outputChannel, warnOnEmptyServerPath));
45+
}
46+
}
2947

30-
// Watch for configuration changes.
48+
/**
49+
* Activate the context for the given workspace folder, and start the
50+
* language clients.
51+
*/
52+
async activateWorkspaceFolder(workspaceFolder: vscode.WorkspaceFolder,
53+
outputChannel: vscode.OutputChannel,
54+
warnOnEmptyServerPath: boolean):
55+
Promise<WorkspaceFolderContext> {
56+
// Create the language clients for mlir and pdll.
57+
const [mlirServer, mlirServerPath] = await this.startLanguageClient(
58+
workspaceFolder, outputChannel, warnOnEmptyServerPath, 'server_path',
59+
'mlir');
60+
const [pdllServer, pdllServerPath] = await this.startLanguageClient(
61+
workspaceFolder, outputChannel, warnOnEmptyServerPath,
62+
'pdll_server_path', 'pdll');
63+
64+
// Watch for configuration changes on this folder.
3165
const serverPathsToWatch = [ mlirServerPath, pdllServerPath ];
32-
await configWatcher.activate(this, serverPathsToWatch);
66+
await configWatcher.activate(this, workspaceFolder, serverPathsToWatch);
67+
68+
return new WorkspaceFolderContext(mlirServer, pdllServer);
3369
}
3470

3571
/**
3672
* Start a new language client for the given language. Returns an array
3773
* containing the opened server, or null if the server could not be started,
3874
* and the resolved server path.
3975
*/
40-
async startLanguageClient(outputChannel: vscode.OutputChannel,
76+
async startLanguageClient(workspaceFolder: vscode.WorkspaceFolder,
77+
outputChannel: vscode.OutputChannel,
4178
warnOnEmptyServerPath: boolean,
4279
serverSettingName: string, languageName: string):
4380
Promise<[ vscodelc.LanguageClient, string ]> {
4481
const clientTitle = languageName.toUpperCase() + ' Language Client';
4582

4683
// Get the path of the lsp-server that is used to provide language
4784
// functionality.
48-
var serverPath = await this.resolveServerPath(serverSettingName);
85+
var serverPath =
86+
await this.resolveServerPath(serverSettingName, workspaceFolder);
4987

5088
// If we aren't emitting warnings on an empty server path, and the server
5189
// path is empty, bail.
@@ -84,16 +122,26 @@ export class MLIRContext implements vscode.Disposable {
84122
}
85123
};
86124

125+
// Configure file patterns relative to the workspace folder.
126+
let filePattern: vscode.GlobPattern = '**/*.' + languageName;
127+
let selectorPattern: string = null;
128+
if (workspaceFolder) {
129+
filePattern = new vscode.RelativePattern(workspaceFolder, filePattern);
130+
selectorPattern = `${workspaceFolder.uri.fsPath}/**/*`;
131+
}
132+
87133
// Configure the client options.
88134
const clientOptions: vscodelc.LanguageClientOptions = {
89-
documentSelector : [ {scheme : 'file', language : languageName} ],
135+
documentSelector : [
136+
{scheme : 'file', language : languageName, pattern : selectorPattern}
137+
],
90138
synchronize : {
91139
// Notify the server about file changes to language files contained in
92140
// the workspace.
93-
fileEvents :
94-
vscode.workspace.createFileSystemWatcher('**/*.' + languageName)
141+
fileEvents : vscode.workspace.createFileSystemWatcher(filePattern)
95142
},
96143
outputChannel : outputChannel,
144+
workspaceFolder : workspaceFolder
97145
};
98146

99147
// Create the language client and start the client.
@@ -117,10 +165,14 @@ export class MLIRContext implements vscode.Disposable {
117165
}
118166

119167
/**
120-
* Try to resolve the path for the given server setting.
168+
* Try to resolve the path for the given server setting, with an optional
169+
* workspace folder.
121170
*/
122-
async resolveServerPath(serverSettingName: string): Promise<string> {
123-
let configServerPath = config.get<string>(serverSettingName);
171+
async resolveServerPath(serverSettingName: string,
172+
workspaceFolder: vscode.WorkspaceFolder):
173+
Promise<string> {
174+
const configServerPath =
175+
config.get<string>(serverSettingName, workspaceFolder);
124176
let serverPath = configServerPath;
125177

126178
// If the path is already fully resolved, there is nothing to do.
@@ -138,8 +190,11 @@ export class MLIRContext implements vscode.Disposable {
138190
}
139191

140192
// Try to resolve the path relative to the workspace.
141-
const foundUris: vscode.Uri[] =
142-
await vscode.workspace.findFiles('**/' + serverPath, null, 1);
193+
let filePattern: vscode.GlobPattern = '**/' + serverPath;
194+
if (workspaceFolder) {
195+
filePattern = new vscode.RelativePattern(workspaceFolder, filePattern);
196+
}
197+
let foundUris = await vscode.workspace.findFiles(filePattern, null, 1);
143198
if (foundUris.length === 0) {
144199
// If we couldn't resolve it, just return the current configuration path
145200
// anyways. The file might not exist yet.
@@ -152,5 +207,6 @@ export class MLIRContext implements vscode.Disposable {
152207
dispose() {
153208
this.subscriptions.forEach((d) => { d.dispose(); });
154209
this.subscriptions = [];
210+
this.workspaceFolders = [];
155211
}
156212
}

0 commit comments

Comments
 (0)