coc-mlir
Version:
MLIR Language Extension
357 lines • 17.2 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MLIRContext = void 0;
const path = require("path");
const vscode = require("coc.nvim");
const vscodelc = require("coc.nvim");
const config = require("./config");
const configWatcher = require("./configWatcher");
/**
* This class represents the context of a specific workspace folder.
*/
class WorkspaceFolderContext {
constructor() {
this.clients = new Map();
}
dispose() {
this.clients.forEach((client) => __awaiter(this, void 0, void 0, function* () { return yield client.stop(); }));
this.clients.clear();
}
}
/**
* This class manages all of the MLIR extension state,
* including the language client.
*/
class MLIRContext {
constructor() {
this.subscriptions = [];
this.workspaceFolders = new Map();
}
/**
* Activate the MLIR context, and start the language clients.
*/
activate(outputChannel) {
return __awaiter(this, void 0, void 0, function* () {
this.outputChannel = outputChannel;
// This lambda is used to lazily start language clients for the given
// document. It removes the need to pro-actively start language clients for
// every folder within the workspace and every language type we provide.
const startClientOnOpenDocument = (document) => __awaiter(this, void 0, void 0, function* () {
yield this.getOrActivateLanguageClient(vscode.Uri.parse(document.uri), document.languageId);
});
// Process any existing documents.
for (const textDoc of vscode.workspace.textDocuments) {
yield startClientOnOpenDocument(textDoc);
}
// Watch any new documents to spawn servers when necessary.
this.subscriptions.push(vscode.workspace.onDidOpenTextDocument(startClientOnOpenDocument));
this.subscriptions.push(vscode.workspace.onDidChangeWorkspaceFolders((event) => {
for (const folder of event.removed) {
const client = this.workspaceFolders.get(folder.uri.toString());
if (client) {
client.dispose();
this.workspaceFolders.delete(folder.uri.toString());
}
}
}));
});
}
/**
* Open or return a language server for the given uri and language.
*/
getOrActivateLanguageClient(uri, languageId) {
return __awaiter(this, void 0, void 0, function* () {
let serverSettingName;
if (languageId === 'mlir') {
serverSettingName = 'server_path';
}
else if (languageId === 'pdll') {
serverSettingName = 'pdll_server_path';
}
else if (languageId === 'tablegen') {
serverSettingName = 'tablegen_server_path';
}
else {
return null;
}
// Check the scheme of the uri.
let validSchemes = ['file', 'mlir.bytecode-mlir'];
if (!validSchemes.includes(uri.scheme)) {
return null;
}
// Resolve the workspace folder if this document is in one. We use the
// workspace folder when determining if a server needs to be started.
let workspaceFolder = vscode.workspace.getWorkspaceFolder(uri);
let workspaceFolderStr = workspaceFolder ? workspaceFolder.uri.toString() : "";
// Get or create a client context for this folder.
let folderContext = this.workspaceFolders.get(workspaceFolderStr);
if (!folderContext) {
folderContext = new WorkspaceFolderContext();
this.workspaceFolders.set(workspaceFolderStr, folderContext);
}
// Start the client for this language if necessary.
let client = folderContext.clients.get(languageId);
if (!client) {
client = yield this.activateWorkspaceFolder(workspaceFolder, serverSettingName, languageId, this.outputChannel);
folderContext.clients.set(languageId, client);
}
return client;
});
}
/**
* Prepare a compilation database option for a server.
*/
prepareCompilationDatabaseServerOptions(languageName, workspaceFolder, configsToWatch, pathsToWatch, additionalServerArgs) {
var _a, e_1, _b, _c;
return __awaiter(this, void 0, void 0, function* () {
// Process the compilation databases attached for the workspace folder.
let databases = config.get(`${languageName}_compilation_databases`, workspaceFolder, []);
// If no databases were explicitly specified, default to a database in the
// 'build' directory within the current workspace.
if (databases.length === 0) {
if (workspaceFolder) {
databases.push(vscode.Uri.parse(workspaceFolder.uri).fsPath +
`/build/${languageName}_compile_commands.yml`);
}
// Otherwise, try to resolve each of the paths.
}
else {
try {
for (var _d = true, databases_1 = __asyncValues(databases), databases_1_1; databases_1_1 = yield databases_1.next(), _a = databases_1_1.done, !_a;) {
_c = databases_1_1.value;
_d = false;
try {
let database = _c;
database = yield this.resolvePath(database, '', workspaceFolder);
}
finally {
_d = true;
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (!_d && !_a && (_b = databases_1.return)) yield _b.call(databases_1);
}
finally { if (e_1) throw e_1.error; }
}
}
configsToWatch.push(`${languageName}_compilation_databases`);
pathsToWatch.push(...databases);
// Setup the compilation databases as additional arguments to pass to the
// server.
databases.filter(database => database !== '');
additionalServerArgs.push(...databases.map((database) => `--${languageName}-compilation-database=${database}`));
});
}
/**
* Prepare the server options for a PDLL server, e.g. populating any
* accessible compilation databases.
*/
preparePDLLServerOptions(workspaceFolder, configsToWatch, pathsToWatch, additionalServerArgs) {
return __awaiter(this, void 0, void 0, function* () {
yield this.prepareCompilationDatabaseServerOptions('pdll', workspaceFolder, configsToWatch, pathsToWatch, additionalServerArgs);
});
}
/**
* Prepare the server options for a TableGen server, e.g. populating any
* accessible compilation databases.
*/
prepareTableGenServerOptions(workspaceFolder, configsToWatch, pathsToWatch, additionalServerArgs) {
return __awaiter(this, void 0, void 0, function* () {
yield this.prepareCompilationDatabaseServerOptions('tablegen', workspaceFolder, configsToWatch, pathsToWatch, additionalServerArgs);
});
}
/**
* Activate the language client for the given language in the given workspace
* folder.
*/
activateWorkspaceFolder(workspaceFolder, serverSettingName, languageName, outputChannel) {
return __awaiter(this, void 0, void 0, function* () {
let configsToWatch = [];
let filepathsToWatch = [];
let additionalServerArgs = [];
// Initialize additional configurations for this server.
if (languageName === 'pdll') {
yield this.preparePDLLServerOptions(workspaceFolder, configsToWatch, filepathsToWatch, additionalServerArgs);
}
else if (languageName == 'tablegen') {
yield this.prepareTableGenServerOptions(workspaceFolder, configsToWatch, filepathsToWatch, additionalServerArgs);
}
// Try to activate the language client.
const [server, serverPath] = yield this.startLanguageClient(workspaceFolder, outputChannel, serverSettingName, languageName, additionalServerArgs);
configsToWatch.push(serverSettingName);
filepathsToWatch.push(serverPath);
// Watch for configuration changes on this folder.
yield configWatcher.activate(this, workspaceFolder, configsToWatch, filepathsToWatch);
return server;
});
}
/**
* 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(workspaceFolder, outputChannel, serverSettingName, languageName, additionalServerArgs) {
return __awaiter(this, void 0, void 0, function* () {
const clientTitle = languageName.toUpperCase() + ' Language Client';
// Get the path of the lsp-server that is used to provide language
// functionality.
var serverPath = yield this.resolveServerPath(serverSettingName, workspaceFolder);
// If the server path is empty, bail. We don't emit errors if the user
// hasn't explicitly configured the server.
if (serverPath === '') {
return [null, serverPath];
}
// Configure the server options.
const serverOptions = {
command: serverPath,
args: additionalServerArgs
};
// Configure file patterns relative to the workspace folder.
let filePattern = '**/*.' + languageName;
let selectorPattern = null;
if (workspaceFolder) {
filePattern = new vscode.RelativePattern(workspaceFolder, filePattern);
selectorPattern = `${vscode.Uri.parse(workspaceFolder.uri).fsPath}/**/*`;
}
// Configure the middleware of the client. This is sort of abused to allow
// for defining a "fallback" language server that operates on non-workspace
// folders. Workspace folder language servers can properly filter out
// documents not within the folder, but we can't effectively filter for
// documents outside of the workspace. To support this, and avoid having two
// servers targeting the same set of files, we use middleware to inject the
// dynamic logic for checking if a document is in the workspace.
let middleware = {};
if (!workspaceFolder) {
middleware = {
didOpen: (document, next) => {
if (!vscode.workspace.getWorkspaceFolder(document.uri)) {
return next(document);
}
return Promise.resolve();
}
};
}
// Configure the client options.
const clientOptions = {
documentSelector: [
{ language: languageName, pattern: selectorPattern },
],
synchronize: {
// Notify the server about file changes to language files contained in
// the workspace.
fileEvents: vscode.workspace.createFileSystemWatcher(filePattern)
},
outputChannel: outputChannel,
workspaceFolder: workspaceFolder,
middleware: middleware,
// Don't switch to output window when the server returns output.
revealOutputChannelOn: vscodelc.RevealOutputChannelOn.Never,
};
// Create the language client and start the client.
let languageClient = new vscodelc.LanguageClient(languageName + '-lsp', clientTitle, serverOptions, clientOptions);
languageClient.start();
return [languageClient, serverPath];
});
}
/**
* Given a server setting, return the default server path.
*/
static getDefaultServerFilename(serverSettingName) {
if (serverSettingName === 'pdll_server_path') {
return 'mlir-pdll-lsp-server';
}
if (serverSettingName === 'server_path') {
return 'mlir-lsp-server';
}
if (serverSettingName === 'tablegen_server_path') {
return 'tblgen-lsp-server';
}
return '';
}
/**
* Try to resolve the given path, or the default path, with an optional
* workspace folder. If a path could not be resolved, just returns the
* input filePath.
*/
resolvePath(filePath, defaultPath, workspaceFolder) {
return __awaiter(this, void 0, void 0, function* () {
const configPath = filePath;
// If the path is already fully resolved, there is nothing to do.
if (path.isAbsolute(filePath)) {
return filePath;
}
// If a path hasn't been set, try to use the default path.
if (filePath === '') {
if (defaultPath === '') {
return filePath;
}
return defaultPath;
// Fallthrough to try resolving the default path.
}
// Try to resolve the path relative to the workspace.
let filePattern = '**/' + filePath;
if (workspaceFolder) {
filePattern = new vscode.RelativePattern(workspaceFolder, filePattern);
}
let foundUris = yield vscode.workspace.findFiles(filePattern, null, 1);
if (foundUris.length === 0) {
// If we couldn't resolve it, just return the original path anyways. The
// file might not exist yet.
return configPath;
}
// Otherwise, return the resolved path.
return foundUris[0].fsPath;
});
}
/**
* Try to resolve the path for the given server setting, with an optional
* workspace folder.
*/
resolveServerPath(serverSettingName, workspaceFolder) {
return __awaiter(this, void 0, void 0, function* () {
const serverPath = config.get(serverSettingName, workspaceFolder);
const defaultPath = MLIRContext.getDefaultServerFilename(serverSettingName);
return this.resolvePath(serverPath, defaultPath, workspaceFolder);
});
}
/**
* Return the language client for the given language and uri, or null if no
* client is active.
*/
getLanguageClient(uri, languageName) {
let workspaceFolder = vscode.workspace.getWorkspaceFolder(uri);
let workspaceFolderStr = workspaceFolder ? workspaceFolder.uri.toString() : "";
let folderContext = this.workspaceFolders.get(workspaceFolderStr);
if (!folderContext) {
return null;
}
return folderContext.clients.get(languageName);
}
dispose() {
this.subscriptions.forEach((d) => { d.dispose(); });
this.subscriptions = [];
this.workspaceFolders.forEach((d) => { d.dispose(); });
this.workspaceFolders.clear();
}
}
exports.MLIRContext = MLIRContext;
//# sourceMappingURL=mlirContext.js.map