diff --git a/mlir/utils/vscode/package.json b/mlir/utils/vscode/package.json --- a/mlir/utils/vscode/package.json +++ b/mlir/utils/vscode/package.json @@ -2,7 +2,7 @@ "name": "vscode-mlir", "displayName": "MLIR", "description": "MLIR Language Extension", - "version": "0.0.4", + "version": "0.0.5", "publisher": "llvm-vs-code-extensions", "homepage": "https://mlir.llvm.org/", "icon": "icon.png", diff --git a/mlir/utils/vscode/src/config.ts b/mlir/utils/vscode/src/config.ts --- a/mlir/utils/vscode/src/config.ts +++ b/mlir/utils/vscode/src/config.ts @@ -1,10 +1,11 @@ import * as vscode from 'vscode'; /** - * Gets the config value `mlir.`. + * Gets the config value `mlir.`, with an optional workspace folder. */ -export function get(key: string): T { - return vscode.workspace.getConfiguration('mlir').get(key); +export function get(key: string, + workspaceFolder: vscode.WorkspaceFolder = null): T { + return vscode.workspace.getConfiguration('mlir', workspaceFolder).get(key); } /** diff --git a/mlir/utils/vscode/src/configWatcher.ts b/mlir/utils/vscode/src/configWatcher.ts --- a/mlir/utils/vscode/src/configWatcher.ts +++ b/mlir/utils/vscode/src/configWatcher.ts @@ -38,10 +38,11 @@ } /** - * Activate the watchers that track configuration changes which decide when to - * restart the server. + * Activate watchers that track configuration changes for the given workspace + * folder, or null if the workspace is top-level. */ export async function activate(mlirContext: MLIRContext, + workspaceFolder: vscode.WorkspaceFolder, serverPathsToWatch: string[]) { // When a configuration change happens, check to see if we should restart the // server. @@ -49,7 +50,7 @@ const settings: string[] = [ 'server_path', 'pdll_server_path' ]; for (const setting of settings) { const expandedSetting = `mlir.${setting}`; - if (event.affectsConfiguration(expandedSetting)) { + if (event.affectsConfiguration(expandedSetting, workspaceFolder)) { promptRestart( 'onSettingsChanged', `setting '${ diff --git a/mlir/utils/vscode/src/mlirContext.ts b/mlir/utils/vscode/src/mlirContext.ts --- a/mlir/utils/vscode/src/mlirContext.ts +++ b/mlir/utils/vscode/src/mlirContext.ts @@ -6,30 +6,66 @@ import * as config from './config'; import * as configWatcher from './configWatcher'; +/** + * This class represents the context of a specific workspace folder. + */ +class WorkspaceFolderContext { + constructor(mlirServer: vscodelc.LanguageClient, + pdllServer: vscodelc.LanguageClient) { + this.mlirServer = mlirServer; + this.pdllServer = pdllServer; + } + mlirServer!: vscodelc.LanguageClient; + pdllServer!: vscodelc.LanguageClient; +} + /** * This class manages all of the MLIR extension state, * including the language client. */ export class MLIRContext implements vscode.Disposable { subscriptions: vscode.Disposable[] = []; - client!: vscodelc.LanguageClient; - pdllClient!: vscodelc.LanguageClient; + workspaceFolders: WorkspaceFolderContext[] = []; /** * Activate the MLIR context, and start the language clients. */ async activate(outputChannel: vscode.OutputChannel, warnOnEmptyServerPath: boolean) { - // Create the language clients for mlir and pdll. - let mlirServerPath: string, pdllServerPath: string; - [this.client, mlirServerPath] = await this.startLanguageClient( - outputChannel, warnOnEmptyServerPath, 'server_path', 'mlir'); - [this.pdllClient, pdllServerPath] = await this.startLanguageClient( - outputChannel, warnOnEmptyServerPath, 'pdll_server_path', 'pdll'); + // Start clients for each workspace folder. + if (vscode.workspace.workspaceFolders && + vscode.workspace.workspaceFolders.length > 0) { + for (const workspaceFolder of vscode.workspace.workspaceFolders) { + this.workspaceFolders.push(await this.activateWorkspaceFolder( + workspaceFolder, outputChannel, warnOnEmptyServerPath)); + } + } else { + this.workspaceFolders.push(await this.activateWorkspaceFolder( + null, outputChannel, warnOnEmptyServerPath)); + } + } - // Watch for configuration changes. + /** + * Activate the context for the given workspace folder, and start the + * language clients. + */ + async activateWorkspaceFolder(workspaceFolder: vscode.WorkspaceFolder, + outputChannel: vscode.OutputChannel, + warnOnEmptyServerPath: boolean): + Promise { + // Create the language clients for mlir and pdll. + const [mlirServer, mlirServerPath] = await this.startLanguageClient( + workspaceFolder, outputChannel, warnOnEmptyServerPath, 'server_path', + 'mlir'); + const [pdllServer, pdllServerPath] = await this.startLanguageClient( + workspaceFolder, outputChannel, warnOnEmptyServerPath, + 'pdll_server_path', 'pdll'); + + // Watch for configuration changes on this folder. const serverPathsToWatch = [ mlirServerPath, pdllServerPath ]; - await configWatcher.activate(this, serverPathsToWatch); + await configWatcher.activate(this, workspaceFolder, serverPathsToWatch); + + return new WorkspaceFolderContext(mlirServer, pdllServer); } /** @@ -37,7 +73,8 @@ * containing the opened server, or null if the server could not be started, * and the resolved server path. */ - async startLanguageClient(outputChannel: vscode.OutputChannel, + async startLanguageClient(workspaceFolder: vscode.WorkspaceFolder, + outputChannel: vscode.OutputChannel, warnOnEmptyServerPath: boolean, serverSettingName: string, languageName: string): Promise<[ vscodelc.LanguageClient, string ]> { @@ -45,7 +82,8 @@ // Get the path of the lsp-server that is used to provide language // functionality. - var serverPath = await this.resolveServerPath(serverSettingName); + var serverPath = + await this.resolveServerPath(serverSettingName, workspaceFolder); // If we aren't emitting warnings on an empty server path, and the server // path is empty, bail. @@ -84,16 +122,26 @@ } }; + // Configure file patterns relative to the workspace folder. + let filePattern: vscode.GlobPattern = '**/*.' + languageName; + let selectorPattern: string = null; + if (workspaceFolder) { + filePattern = new vscode.RelativePattern(workspaceFolder, filePattern); + selectorPattern = `${workspaceFolder.uri.fsPath}/**/*`; + } + // Configure the client options. const clientOptions: vscodelc.LanguageClientOptions = { - documentSelector : [ {scheme : 'file', language : languageName} ], + documentSelector : [ + {scheme : 'file', language : languageName, pattern : selectorPattern} + ], synchronize : { // Notify the server about file changes to language files contained in // the workspace. - fileEvents : - vscode.workspace.createFileSystemWatcher('**/*.' + languageName) + fileEvents : vscode.workspace.createFileSystemWatcher(filePattern) }, outputChannel : outputChannel, + workspaceFolder : workspaceFolder }; // Create the language client and start the client. @@ -117,10 +165,14 @@ } /** - * Try to resolve the path for the given server setting. + * Try to resolve the path for the given server setting, with an optional + * workspace folder. */ - async resolveServerPath(serverSettingName: string): Promise { - let configServerPath = config.get(serverSettingName); + async resolveServerPath(serverSettingName: string, + workspaceFolder: vscode.WorkspaceFolder): + Promise { + const configServerPath = + config.get(serverSettingName, workspaceFolder); let serverPath = configServerPath; // If the path is already fully resolved, there is nothing to do. @@ -138,8 +190,11 @@ } // Try to resolve the path relative to the workspace. - const foundUris: vscode.Uri[] = - await vscode.workspace.findFiles('**/' + serverPath, null, 1); + let filePattern: vscode.GlobPattern = '**/' + serverPath; + if (workspaceFolder) { + filePattern = new vscode.RelativePattern(workspaceFolder, filePattern); + } + let foundUris = await vscode.workspace.findFiles(filePattern, null, 1); if (foundUris.length === 0) { // If we couldn't resolve it, just return the current configuration path // anyways. The file might not exist yet. @@ -152,5 +207,6 @@ dispose() { this.subscriptions.forEach((d) => { d.dispose(); }); this.subscriptions = []; + this.workspaceFolders = []; } }