[mlir-vscode] Add better resolution for server file paths

We currently require that server paths are full paths, which is
fairly inconvenient for a myriad of reasons. This commit
attempts to resolve a given server path with the current workspace.

This has a nice additional affect that we can now actually have
default server paths. This means that mlir-lsp-server and
mlir-pdll-lsp-server can be transparently picked up from
build directories (i.e. generally no need for upstream users to
configure the extension).

Fixes #54627

Differential Revision: https://reviews.llvm.org/D122792
This commit is contained in:
River Riddle 2022-03-31 01:04:21 -07:00
parent 2e51a32219
commit ade148d936
2 changed files with 67 additions and 18 deletions

View File

@ -1,5 +1,4 @@
import * as chokidar from 'chokidar'; import * as chokidar from 'chokidar';
import * as path from 'path';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as config from './config'; import * as config from './config';
@ -42,7 +41,8 @@ async function promptRestart(settingName: string, promptMessage: string) {
* Activate the watchers that track configuration changes which decide when to * Activate the watchers that track configuration changes which decide when to
* restart the server. * restart the server.
*/ */
export function activate(mlirContext: MLIRContext) { export async function activate(mlirContext: MLIRContext,
serverPathsToWatch: string[]) {
// When a configuration change happens, check to see if we should restart the // When a configuration change happens, check to see if we should restart the
// server. // server.
mlirContext.subscriptions.push(vscode.workspace.onDidChangeConfiguration(event => { mlirContext.subscriptions.push(vscode.workspace.onDidChangeConfiguration(event => {
@ -61,10 +61,7 @@ export function activate(mlirContext: MLIRContext) {
// Track the server file in case it changes. We use `fs` here because the // Track the server file in case it changes. We use `fs` here because the
// server may not be in a workspace directory. // server may not be in a workspace directory.
const settings: string[] = [ 'server_path', 'pdll_server_path' ]; for (const serverPath of serverPathsToWatch) {
for (const setting of settings) {
const serverPath = config.get<string>(setting);
// Check that the path actually exists. // Check that the path actually exists.
if (serverPath === '') { if (serverPath === '') {
continue; continue;

View File

@ -1,4 +1,5 @@
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as vscodelc from 'vscode-languageclient'; import * as vscodelc from 'vscode-languageclient';
@ -20,31 +21,36 @@ export class MLIRContext implements vscode.Disposable {
async activate(outputChannel: vscode.OutputChannel, async activate(outputChannel: vscode.OutputChannel,
warnOnEmptyServerPath: boolean) { warnOnEmptyServerPath: boolean) {
// Create the language clients for mlir and pdll. // Create the language clients for mlir and pdll.
this.pdllClient = this.startLanguageClient( 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'); outputChannel, warnOnEmptyServerPath, 'pdll_server_path', 'pdll');
this.client = this.startLanguageClient(outputChannel, warnOnEmptyServerPath,
'server_path', 'mlir');
// Watch for configuration changes. // Watch for configuration changes.
configWatcher.activate(this); const serverPathsToWatch = [ mlirServerPath, pdllServerPath ];
await configWatcher.activate(this, serverPathsToWatch);
} }
/** /**
* Start a new language client for the given language. * Start a new language client for the given language. Returns an array
* containing the opened server, or null if the server could not be started,
* and the resolved server path.
*/ */
startLanguageClient(outputChannel: vscode.OutputChannel, async startLanguageClient(outputChannel: vscode.OutputChannel,
warnOnEmptyServerPath: boolean, serverSettingName: string, warnOnEmptyServerPath: boolean,
languageName: string): vscodelc.LanguageClient { serverSettingName: string, languageName: string):
Promise<[ vscodelc.LanguageClient, string ]> {
const clientTitle = languageName.toUpperCase() + ' Language Client'; const clientTitle = languageName.toUpperCase() + ' Language Client';
// Get the path of the lsp-server that is used to provide language // Get the path of the lsp-server that is used to provide language
// functionality. // functionality.
const serverPath = config.get<string>(serverSettingName); var serverPath = await this.resolveServerPath(serverSettingName);
// If we aren't emitting warnings on an empty server path, and the server // If we aren't emitting warnings on an empty server path, and the server
// path is empty, bail. // path is empty, bail.
if (!warnOnEmptyServerPath && serverPath === '') { if (!warnOnEmptyServerPath && serverPath === '') {
return null; return [ null, serverPath ];
} }
// Check that the file actually exists. // Check that the file actually exists.
@ -61,7 +67,7 @@ export class MLIRContext implements vscode.Disposable {
{openToSide : false, query : `mlir.${serverSettingName}`}); {openToSide : false, query : `mlir.${serverSettingName}`});
} }
}); });
return null; return [ null, serverPath ];
} }
// Configure the server options. // Configure the server options.
@ -94,7 +100,53 @@ export class MLIRContext implements vscode.Disposable {
let languageClient = new vscodelc.LanguageClient( let languageClient = new vscodelc.LanguageClient(
languageName + '-lsp', clientTitle, serverOptions, clientOptions); languageName + '-lsp', clientTitle, serverOptions, clientOptions);
this.subscriptions.push(languageClient.start()); this.subscriptions.push(languageClient.start());
return languageClient; return [ languageClient, serverPath ];
}
/**
* Given a server setting, return the default server path.
*/
static getDefaultServerFilename(serverSettingName: string): string {
if (serverSettingName === 'pdll_server_path') {
return 'mlir-pdll-lsp-server';
}
if (serverSettingName === 'server_path') {
return 'mlir-lsp-server';
}
return '';
}
/**
* Try to resolve the path for the given server setting.
*/
async resolveServerPath(serverSettingName: string): Promise<string> {
let configServerPath = config.get<string>(serverSettingName);
let serverPath = configServerPath;
// If the path is already fully resolved, there is nothing to do.
if (path.isAbsolute(serverPath)) {
return serverPath;
}
// If a path hasn't been set, try to use the default path.
if (serverPath === '') {
serverPath = MLIRContext.getDefaultServerFilename(serverSettingName);
if (serverPath === '') {
return serverPath;
}
// Fallthrough to try resolving the default path.
}
// Try to resolve the path relative to the workspace.
const foundUris: vscode.Uri[] =
await vscode.workspace.findFiles('**/' + serverPath, 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.
return configServerPath;
}
// Otherwise, return the resolved path.
return foundUris[0].fsPath;
} }
dispose() { dispose() {