UNPKG

coc-mlir

Version:

MLIR Language Extension

357 lines 17.2 kB
"use strict"; 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