UNPKG

atom-languageclient

Version:
951 lines 136 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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LanguageClientConnection = void 0; const cp = require("child_process"); const rpc = require("vscode-jsonrpc"); const rpcNode = require("vscode-jsonrpc/node"); const path = require("path"); const convert_js_1 = require("./convert.js"); const apply_edit_adapter_1 = require("./adapters/apply-edit-adapter"); const autocomplete_adapter_1 = require("./adapters/autocomplete-adapter"); const CallHierarchyAdapter = require("./adapters/call-hierarchy-adapter"); const code_action_adapter_1 = require("./adapters/code-action-adapter"); const code_format_adapter_1 = require("./adapters/code-format-adapter"); const code_highlight_adapter_1 = require("./adapters/code-highlight-adapter"); const datatip_adapter_1 = require("./adapters/datatip-adapter"); const definition_adapter_1 = require("./adapters/definition-adapter"); const document_sync_adapter_1 = require("./adapters/document-sync-adapter"); const find_references_adapter_1 = require("./adapters/find-references-adapter"); const linter_push_v2_adapter_1 = require("./adapters/linter-push-v2-adapter"); const logging_console_adapter_1 = require("./adapters/logging-console-adapter"); const notifications_adapter_1 = require("./adapters/notifications-adapter"); const outline_view_adapter_1 = require("./adapters/outline-view-adapter"); const rename_adapter_1 = require("./adapters/rename-adapter"); const signature_help_adapter_1 = require("./adapters/signature-help-adapter"); const ShowDocumentAdapter = require("./adapters/show-document-adapter"); const Utils = require("./utils"); const languageclient_1 = require("./languageclient"); Object.defineProperty(exports, "LanguageClientConnection", { enumerable: true, get: function () { return languageclient_1.LanguageClientConnection; } }); const logger_1 = require("./logger"); const server_manager_js_1 = require("./server-manager.js"); const atom_1 = require("atom"); const path_1 = require("path"); /** * Public: AutoLanguageClient provides a simple way to have all the supported Atom-IDE services wired up entirely for * you by just subclassing it and implementing at least * * - `startServerProcess` * - `getGrammarScopes` * - `getLanguageName` * - `getServerName` */ class AutoLanguageClient { constructor() { this._isDeactivating = false; this._serverAdapters = new WeakMap(); this.processStdErr = ""; /** * If this is set to `true` (the default value), the servers will shut down gracefully. If it is set to `false`, the * servers will be killed without awaiting shutdown response. */ this.shutdownGracefully = true; this.reportBusyWhile = (title, f) => __awaiter(this, void 0, void 0, function* () { if (this.busySignalService) { return this.busySignalService.reportBusyWhile(title, f); } else { return this.reportBusyWhileDefault(title, f); } }); this.reportBusyWhileDefault = (title, f) => __awaiter(this, void 0, void 0, function* () { this.logger.info(`[Started] ${title}`); let res; try { res = yield f(); } finally { this.logger.info(`[Finished] ${title}`); } return res; }); } // You must implement these so we know how to deal with your language and server // ------------------------------------------------------------------------- /** Return an array of the grammar scopes you handle, e.g. [ 'source.js' ] */ getGrammarScopes() { throw Error("Must implement getGrammarScopes when extending AutoLanguageClient"); } /** Return the name of the language you support, e.g. 'JavaScript' */ getLanguageName() { throw Error("Must implement getLanguageName when extending AutoLanguageClient"); } /** Return the name of your server, e.g. 'Eclipse JDT' */ getServerName() { throw Error("Must implement getServerName when extending AutoLanguageClient"); } /** Start your server process */ startServerProcess(_projectPath) { throw Error("Must override startServerProcess to start language server process when extending AutoLanguageClient"); } // You might want to override these for different behavior // --------------------------------------------------------------------------- /** (Optional) Determine whether we should start a server for a given editor if we don't have one yet */ shouldStartForEditor(editor) { return this.getGrammarScopes().includes(editor.getGrammar().scopeName); } /** (Optional) Return the parameters used to initialize a client - you may want to extend capabilities */ getInitializeParams(projectPath, lsProcess) { const rootUri = convert_js_1.default.pathToUri(projectPath); return { processId: lsProcess.pid !== undefined ? lsProcess.pid : null, rootPath: projectPath, rootUri, locale: atom.config.get("atom-i18n.locale") || "en", workspaceFolders: [{ uri: rootUri, name: path_1.basename(projectPath) }], // The capabilities supported. // TODO the capabilities set to false/undefined are TODO. See {ls.ServerCapabilities} for a full list. capabilities: { workspace: { applyEdit: true, configuration: false, workspaceEdit: { documentChanges: true, normalizesLineEndings: false, changeAnnotationSupport: undefined, resourceOperations: ["create", "rename", "delete"], }, workspaceFolders: true, didChangeConfiguration: { dynamicRegistration: false, }, didChangeWatchedFiles: { dynamicRegistration: false, }, // BLOCKED: on atom/symbols-view symbol: { dynamicRegistration: false, }, executeCommand: { dynamicRegistration: false, }, semanticTokens: undefined, codeLens: undefined, fileOperations: { // BLOCKED: on tree-view not providing hooks for "before file/dir created" willCreate: false, // BLOCKED: on tree-view not providing hooks for "before file/dir renamed" willRename: false, // BLOCKED: on tree-view not providing hooks for "before file/dir deleted" willDelete: false, }, }, textDocument: { synchronization: { dynamicRegistration: false, willSave: true, willSaveWaitUntil: true, didSave: true, }, completion: { dynamicRegistration: false, completionItem: { snippetSupport: true, commitCharactersSupport: false, documentationFormat: [], deprecatedSupport: false, preselectSupport: false, tagSupport: { valueSet: [], }, insertReplaceSupport: false, resolveSupport: { properties: [], }, insertTextModeSupport: { valueSet: [], }, }, completionItemKind: { valueSet: [], }, contextSupport: true, }, hover: { dynamicRegistration: false, }, signatureHelp: { dynamicRegistration: false, }, declaration: undefined, references: { dynamicRegistration: false, }, documentHighlight: { dynamicRegistration: false, }, documentSymbol: { dynamicRegistration: false, hierarchicalDocumentSymbolSupport: true, }, formatting: { dynamicRegistration: false, }, rangeFormatting: { dynamicRegistration: false, }, onTypeFormatting: { dynamicRegistration: false, }, definition: { dynamicRegistration: false, }, codeAction: { dynamicRegistration: false, codeActionLiteralSupport: { codeActionKind: { valueSet: [""], // TODO explicitly support more? }, }, }, codeLens: { dynamicRegistration: false, }, documentLink: { dynamicRegistration: false, }, rename: { dynamicRegistration: false, }, moniker: { dynamicRegistration: false, }, publishDiagnostics: { relatedInformation: true, tagSupport: { // BLOCKED: on steelbrain/linter supporting ways of denoting useless code and deprecated symbols valueSet: [], }, versionSupport: false, codeDescriptionSupport: true, dataSupport: true, }, callHierarchy: { dynamicRegistration: false, }, implementation: undefined, typeDefinition: undefined, colorProvider: undefined, foldingRange: undefined, selectionRange: undefined, linkedEditingRange: undefined, semanticTokens: undefined, }, general: { regularExpressions: undefined, markdown: undefined, }, window: { workDoneProgress: false, showMessage: undefined, showDocument: { support: true }, }, experimental: {}, }, }; } /** (Optional) Early wire-up of listeners before initialize method is sent */ preInitialization(_connection) { } /** (Optional) Late wire-up of listeners after initialize method has been sent */ postInitialization(_server) { } /** (Optional) Determine whether to use ipc, stdio or socket to connect to the server */ getConnectionType() { return this.socket != null ? "socket" : "stdio"; } /** (Optional) Return the name of your root configuration key */ getRootConfigurationKey() { return ""; } /** (Optional) Transform the configuration object before it is sent to the server */ mapConfigurationObject(configuration) { return configuration; } /** * (Optional) Determines the `languageId` string used for `textDocument/didOpen` notification. The default is to use * the grammar name. * * You can override this like this: * * class MyLanguageClient extends AutoLanguageClient { * getLanguageIdFromEditor(editor: TextEditor) { * if (editor.getGrammar().scopeName === "source.myLanguage") { * return "myCustumLanguageId" * } * return super.getLanguageIdFromEditor(editor) * } * } * * @param editor A {TextEditor} which is opened. * @returns A {string} of `languageId` used for `textDocument/didOpen` notification. */ getLanguageIdFromEditor(editor) { return editor.getGrammar().name; } // Helper methods that are useful for implementors // --------------------------------------------------------------------------- /** Gets a LanguageClientConnection for a given TextEditor */ getConnectionForEditor(editor) { return __awaiter(this, void 0, void 0, function* () { const server = yield this._serverManager.getServer(editor); return server ? server.connection : null; }); } /** Restart all active language servers for this language client in the workspace */ restartAllServers() { return __awaiter(this, void 0, void 0, function* () { yield this._serverManager.restartAllServers(); }); } // Default implementation of the rest of the AutoLanguageClient // --------------------------------------------------------------------------- /** Activate does very little for perf reasons - hooks in via ServerManager for later 'activation' */ activate() { this._disposable = new atom_1.CompositeDisposable(); this.name = `${this.getLanguageName()} (${this.getServerName()})`; this.logger = this.getLogger(); this._serverManager = new server_manager_js_1.ServerManager((p) => this.startServer(p), this.logger, (e) => this.shouldStartForEditor(e), (filepath) => this.filterChangeWatchedFiles(filepath), this.reportBusyWhile, this.getServerName(), (textEditor) => this.determineProjectPath(textEditor), this.shutdownGracefully); this._serverManager.startListening(); process.on("exit", () => this.exitCleanup.bind(this)); } exitCleanup() { this._serverManager.terminate(); } /** Deactivate disposes the resources we're using */ deactivate() { return __awaiter(this, void 0, void 0, function* () { this._isDeactivating = true; this._disposable.dispose(); this._serverManager.stopListening(); yield this._serverManager.stopAllServers(); }); } /** * Spawn a general language server. Use this inside the `startServerProcess` override if the language server is a * general executable. Also see the `spawnChildNode` method. If the name is provided as the first argument, it checks * `bin/platform-arch/exeName` by default, and if doesn't exists uses the exe on PATH. For example on Windows x64, by * passing `serve-d`, `bin/win32-x64/exeName.exe` is spawned by default. * * @param exe The `name` or `path` of the executable * @param args Args passed to spawn the exe. Defaults to `[]`. * @param options: Child process spawn options. Defaults to `{}`. * @param rootPath The path of the folder of the exe file. Defaults to `join("bin", `${process.platform}-${process.arch} `)`. * @param exeExtention The extention of the exe file. Defaults to `process.platform === "win32" ? ".exe" : ""` */ spawn(exe, args = [], options = {}, rootPath = Utils.rootPathDefault, exeExtention = Utils.exeExtentionDefault) { this.logger.debug(`starting "${exe} ${args.join(" ")}"`); return cp.spawn(Utils.getExePath(exe, rootPath, exeExtention), args, options); } /** * Spawn a language server using Atom's Nodejs process Use this inside the `startServerProcess` override if the * language server is a JavaScript file. Also see the `spawn` method */ spawnChildNode(args, options = {}) { this.logger.debug(`starting child Node "${args.join(" ")}"`); options.env = options.env || Object.create(process.env); if (options.env) { options.env.ELECTRON_RUN_AS_NODE = "1"; options.env.ELECTRON_NO_ATTACH_CONSOLE = "1"; } return cp.spawn(process.execPath, args, options); } /** LSP logging is only set for warnings & errors by default unless you turn on the core.debugLSP setting */ getLogger() { const filter = atom.config.get("core.debugLSP") ? logger_1.FilteredLogger.DeveloperLevelFilter : logger_1.FilteredLogger.UserLevelFilter; return new logger_1.FilteredLogger(new logger_1.ConsoleLogger(this.name), filter); } /** Starts the server by starting the process, then initializing the language server and starting adapters */ startServer(projectPath) { return __awaiter(this, void 0, void 0, function* () { const lsProcess = yield this.reportBusyWhile(`Starting ${this.getServerName()} for ${path.basename(projectPath)}`, () => __awaiter(this, void 0, void 0, function* () { return this.startServerProcess(projectPath); })); this.captureServerErrors(lsProcess, projectPath); const connection = new languageclient_1.LanguageClientConnection(this.createRpcConnection(lsProcess), this.logger); this.preInitialization(connection); const initializeParams = this.getInitializeParams(projectPath, lsProcess); const initialization = connection.initialize(initializeParams); this.reportBusyWhile(`${this.getServerName()} initializing for ${path.basename(projectPath)}`, () => initialization); const initializeResponse = yield initialization; const newServer = { projectPath, process: lsProcess, connection, capabilities: initializeResponse.capabilities, disposable: new atom_1.CompositeDisposable(), additionalPaths: new Set(), }; this.postInitialization(newServer); connection.initialized(); connection.on("close", () => { if (!this._isDeactivating) { this._serverManager.stopServer(newServer); if (!this._serverManager.hasServerReachedRestartLimit(newServer)) { this.logger.debug(`Restarting language server for project '${newServer.projectPath}'`); this._serverManager.startServer(projectPath); } else { this.logger.warn(`Language server has exceeded auto-restart limit for project '${newServer.projectPath}'`); atom.notifications.addError(`The ${this.name} language server has exited and exceeded the restart limit for project '${newServer.projectPath}'`); } } }); const configurationKey = this.getRootConfigurationKey(); if (configurationKey) { newServer.disposable.add(atom.config.observe(configurationKey, (config) => { const mappedConfig = this.mapConfigurationObject(config || {}); if (mappedConfig) { connection.didChangeConfiguration({ settings: mappedConfig, }); } })); } this.startExclusiveAdapters(newServer); return newServer; }); } captureServerErrors(lsProcess, projectPath) { var _a, _b; lsProcess.on("error", (err) => this.onSpawnError(err)); lsProcess.on("close", (code, signal) => this.onSpawnClose(code, signal)); lsProcess.on("disconnect", () => this.onSpawnDisconnect()); lsProcess.on("exit", (code, signal) => this.onSpawnExit(code, signal)); (_a = lsProcess.stderr) === null || _a === void 0 ? void 0 : _a.setEncoding("utf8"); (_b = lsProcess.stderr) === null || _b === void 0 ? void 0 : _b.on("data", (chunk) => this.onSpawnStdErrData(chunk, projectPath)); } /** * The function called whenever the spawned server `error`s. Extend (call super.onSpawnError) or override this if you * need custom error handling */ onSpawnError(err) { atom.notifications.addError(`${this.getServerName()} language server for ${this.getLanguageName()} unable to start`, { dismissable: true, description: err.toString(), }); } /** * The function called whenever the spawned server `close`s. Extend (call super.onSpawnClose) or override this if you * need custom close handling */ onSpawnClose(code, signal) { if (code !== 0 && signal === null) { atom.notifications.addError(`${this.getServerName()} language server for ${this.getLanguageName()} was closed with code: ${code}.`); } } /** * The function called whenever the spawned server `disconnect`s. Extend (call super.onSpawnDisconnect) or override * this if you need custom disconnect handling */ onSpawnDisconnect() { this.logger.debug(`${this.getServerName()} language server for ${this.getLanguageName()} got disconnected.`); } /** * The function called whenever the spawned server `exit`s. Extend (call super.onSpawnExit) or override this if you * need custom exit handling */ onSpawnExit(code, signal) { this.logger.debug(`exit: code ${code} signal ${signal}`); } /** (Optional) Finds the project path. If there is a custom logic for finding projects override this method. */ determineProjectPath(textEditor) { const filePath = textEditor.getPath(); // TODO can filePath be null if (filePath === null || filePath === undefined) { return null; } const projectPath = this._serverManager.getNormalizedProjectPaths().find((d) => filePath.startsWith(d)); if (projectPath !== undefined) { return projectPath; } const serverWithClaim = this._serverManager .getActiveServers() .find((server) => { var _a; return (_a = server.additionalPaths) === null || _a === void 0 ? void 0 : _a.has(path.dirname(filePath)); }); if (serverWithClaim !== undefined) { return server_manager_js_1.normalizePath(serverWithClaim.projectPath); } return null; } /** * The function called whenever the spawned server returns `data` in `stderr` Extend (call super.onSpawnStdErrData) or * override this if you need custom stderr data handling */ onSpawnStdErrData(chunk, projectPath) { const errorString = chunk.toString(); this.handleServerStderr(errorString, projectPath); // Keep the last 5 lines for packages to use in messages this.processStdErr = (this.processStdErr + errorString).split("\n").slice(-5).join("\n"); } /** Creates the RPC connection which can be ipc, socket or stdio */ createRpcConnection(lsProcess) { let reader; let writer; const connectionType = this.getConnectionType(); switch (connectionType) { case "ipc": reader = new rpcNode.IPCMessageReader(lsProcess); writer = new rpcNode.IPCMessageWriter(lsProcess); break; case "socket": reader = new rpcNode.SocketMessageReader(this.socket); writer = new rpcNode.SocketMessageWriter(this.socket); break; case "stdio": if (lsProcess.stdin !== null && lsProcess.stdout !== null) { reader = new rpcNode.StreamMessageReader(lsProcess.stdout); writer = new rpcNode.StreamMessageWriter(lsProcess.stdin); } else { this.logger.error(`The language server process for ${this.getLanguageName()} does not have a valid stdin and stdout`); return Utils.assertUnreachable("stdio"); } break; default: return Utils.assertUnreachable(connectionType); } return rpc.createMessageConnection(reader, writer, { log: (..._args) => { }, warn: (..._args) => { }, info: (..._args) => { }, error: (...args) => { this.logger.error(args); }, }); } /** Start adapters that are not shared between servers */ startExclusiveAdapters(server) { apply_edit_adapter_1.default.attach(server.connection); notifications_adapter_1.default.attach(server.connection, this.name, server.projectPath); if (document_sync_adapter_1.default.canAdapt(server.capabilities)) { const docSyncAdapter = new document_sync_adapter_1.default(server.connection, (editor) => this.shouldSyncForEditor(editor, server.projectPath), server.capabilities.textDocumentSync, this.reportBusyWhile, (editor) => this.getLanguageIdFromEditor(editor)); server.disposable.add(docSyncAdapter); } const linterPushV2 = new linter_push_v2_adapter_1.default(server.connection); if (this._linterDelegate != null) { linterPushV2.attach(this._linterDelegate); } server.disposable.add(linterPushV2); const loggingConsole = new logging_console_adapter_1.default(server.connection); if (this._consoleDelegate != null) { loggingConsole.attach(this._consoleDelegate({ id: this.name, name: this.getLanguageName() })); } server.disposable.add(loggingConsole); let signatureHelpAdapter; if (signature_help_adapter_1.default.canAdapt(server.capabilities)) { signatureHelpAdapter = new signature_help_adapter_1.default(server, this.getGrammarScopes()); if (this._signatureHelpRegistry != null) { signatureHelpAdapter.attach(this._signatureHelpRegistry); } server.disposable.add(signatureHelpAdapter); } this._serverAdapters.set(server, { linterPushV2, loggingConsole, signatureHelpAdapter, }); ShowDocumentAdapter.attach(server.connection); server.connection.onWorkspaceFolders(() => this._serverManager.getWorkspaceFolders()); } shouldSyncForEditor(editor, projectPath) { return this.isFileInProject(editor, projectPath) && this.shouldStartForEditor(editor); } isFileInProject(editor, projectPath) { return (editor.getPath() || "").startsWith(projectPath); } // Autocomplete+ via LS completion--------------------------------------- /** * A method to override to return an array of grammar scopes that should not be used for autocompletion. * * Usually that's used for disabling autocomplete inside comments, * * @example If the grammar scopes are [ '.source.js' ], `getAutocompleteDisabledScopes` may return [ '.source.js .comment' ]. */ getAutocompleteDisabledScopes() { return []; } provideAutocomplete() { return { selector: this.getGrammarScopes() .map((g) => autocomplete_adapter_1.grammarScopeToAutoCompleteSelector(g)) .join(", "), disableForSelector: this.getAutocompleteDisabledScopes() .map((g) => autocomplete_adapter_1.grammarScopeToAutoCompleteSelector(g)) .join(", "), inclusionPriority: 1, suggestionPriority: 2, excludeLowerPriority: false, filterSuggestions: true, getSuggestions: this.getSuggestions.bind(this), onDidInsertSuggestion: (event) => { autocomplete_adapter_1.default.applyAdditionalTextEdits(event); this.onDidInsertSuggestion(event); }, getSuggestionDetailsOnSelect: this.getSuggestionDetailsOnSelect.bind(this), }; } getSuggestions(request) { return __awaiter(this, void 0, void 0, function* () { const server = yield this._serverManager.getServer(request.editor); if (server == null || !autocomplete_adapter_1.default.canAdapt(server.capabilities)) { return []; } this.autoComplete = this.autoComplete || new autocomplete_adapter_1.default(); this._lastAutocompleteRequest = request; return this.autoComplete.getSuggestions(server, request, this.onDidConvertAutocomplete, atom.config.get("autocomplete-plus.minimumWordLength")); }); } getSuggestionDetailsOnSelect(suggestion) { return __awaiter(this, void 0, void 0, function* () { const request = this._lastAutocompleteRequest; if (request == null) { return null; } const server = yield this._serverManager.getServer(request.editor); if (server == null || !autocomplete_adapter_1.default.canResolve(server.capabilities) || this.autoComplete == null) { return null; } return this.autoComplete.completeSuggestion(server, suggestion, request, this.onDidConvertAutocomplete); }); } onDidConvertAutocomplete(_completionItem, _suggestion, _request) { } onDidInsertSuggestion(_arg) { } // Definitions via LS documentHighlight and gotoDefinition------------ provideDefinitions() { return { name: this.name, priority: 20, grammarScopes: this.getGrammarScopes(), wordRegExp: null, getDefinition: this.getDefinition.bind(this), }; } getDefinition(editor, point) { return __awaiter(this, void 0, void 0, function* () { const server = yield this._serverManager.getServer(editor); if (server == null || !definition_adapter_1.default.canAdapt(server.capabilities)) { return null; } this.definitions = this.definitions || new definition_adapter_1.default(); const query = yield this.definitions.getDefinition(server.connection, server.capabilities, this.getLanguageName(), editor, point); if (query !== null && server.additionalPaths !== undefined) { // populate additionalPaths based on definitions // Indicates that the language server can support LSP functionality for out of project files indicated by `textDocument/definition` responses. for (const def of query.definitions) { server_manager_js_1.considerAdditionalPath(server, path.dirname(def.path)); } } return query; }); } // Outline View via LS documentSymbol--------------------------------- provideOutlines() { return { name: this.name, grammarScopes: this.getGrammarScopes(), priority: 1, getOutline: this.getOutline.bind(this), }; } getOutline(editor) { return __awaiter(this, void 0, void 0, function* () { const server = yield this._serverManager.getServer(editor); if (server == null || !outline_view_adapter_1.default.canAdapt(server.capabilities)) { return null; } this.outlineView = this.outlineView || new outline_view_adapter_1.default(); return this.outlineView.getOutline(server.connection, editor); }); } // Call Hierarchy View via LS callHierarchy--------------------------------- provideCallHierarchy() { return { name: this.name, grammarScopes: this.getGrammarScopes(), priority: 1, getIncomingCallHierarchy: this.getIncomingCallHierarchy.bind(this), getOutgoingCallHierarchy: this.getOutgoingCallHierarchy.bind(this), }; } getIncomingCallHierarchy(editor, point) { var _a; return __awaiter(this, void 0, void 0, function* () { const server = yield this._serverManager.getServer(editor); if (server === null || !CallHierarchyAdapter.canAdapt(server.capabilities)) { return null; } this.callHierarchy = (_a = this.callHierarchy) !== null && _a !== void 0 ? _a : CallHierarchyAdapter; return this.callHierarchy.getCallHierarchy(server.connection, editor, point, "incoming"); }); } getOutgoingCallHierarchy(editor, point) { var _a; return __awaiter(this, void 0, void 0, function* () { const server = yield this._serverManager.getServer(editor); if (server === null || !CallHierarchyAdapter.canAdapt(server.capabilities)) { return null; } this.callHierarchy = (_a = this.callHierarchy) !== null && _a !== void 0 ? _a : CallHierarchyAdapter; return this.callHierarchy.getCallHierarchy(server.connection, editor, point, "outgoing"); }); } // Linter push v2 API via LS publishDiagnostics consumeLinterV2(registerIndie) { this._linterDelegate = registerIndie({ name: this.name }); if (this._linterDelegate == null) { return; } for (const server of this._serverManager.getActiveServers()) { const linterPushV2 = this.getServerAdapter(server, "linterPushV2"); if (linterPushV2 != null) { linterPushV2.attach(this._linterDelegate); } } } // Find References via LS findReferences------------------------------ provideFindReferences() { return { isEditorSupported: (editor) => this.getGrammarScopes().includes(editor.getGrammar().scopeName), findReferences: this.getReferences.bind(this), }; } getReferences(editor, point) { return __awaiter(this, void 0, void 0, function* () { const server = yield this._serverManager.getServer(editor); if (server == null || !find_references_adapter_1.default.canAdapt(server.capabilities)) { return null; } this.findReferences = this.findReferences || new find_references_adapter_1.default(); return this.findReferences.getReferences(server.connection, editor, point, server.projectPath); }); } // Datatip via LS textDocument/hover---------------------------------- consumeDatatip(service) { this._disposable.add(service.addProvider({ providerName: this.name, priority: 1, grammarScopes: this.getGrammarScopes(), validForScope: (scopeName) => { return this.getGrammarScopes().includes(scopeName); }, datatip: this.getDatatip.bind(this), })); } getDatatip(editor, point) { return __awaiter(this, void 0, void 0, function* () { const server = yield this._serverManager.getServer(editor); if (server == null || !datatip_adapter_1.default.canAdapt(server.capabilities)) { return null; } this.datatip = this.datatip || new datatip_adapter_1.default(); return this.datatip.getDatatip(server.connection, editor, point); }); } // Console via LS logging--------------------------------------------- consumeConsole(createConsole) { this._consoleDelegate = createConsole; for (const server of this._serverManager.getActiveServers()) { const loggingConsole = this.getServerAdapter(server, "loggingConsole"); if (loggingConsole) { loggingConsole.attach(this._consoleDelegate({ id: this.name, name: this.getLanguageName() })); } } // No way of detaching from client connections today return new atom_1.Disposable(() => { }); } // Code Format via LS formatDocument & formatDocumentRange------------ provideCodeFormat() { return { grammarScopes: this.getGrammarScopes(), priority: 1, formatCode: this.getCodeFormat.bind(this), }; } getCodeFormat(editor, range) { return __awaiter(this, void 0, void 0, function* () { const server = yield this._serverManager.getServer(editor); if (server == null || !code_format_adapter_1.default.canAdapt(server.capabilities)) { return []; } return code_format_adapter_1.default.format(server.connection, server.capabilities, editor, range); }); } provideRangeCodeFormat() { return { grammarScopes: this.getGrammarScopes(), priority: 1, formatCode: this.getRangeCodeFormat.bind(this), }; } getRangeCodeFormat(editor, range) { return __awaiter(this, void 0, void 0, function* () { const server = yield this._serverManager.getServer(editor); if (server == null || !server.capabilities.documentRangeFormattingProvider) { return []; } return code_format_adapter_1.default.formatRange(server.connection, editor, range); }); } provideFileCodeFormat() { return { grammarScopes: this.getGrammarScopes(), priority: 1, formatEntireFile: this.getFileCodeFormat.bind(this), }; } provideOnSaveCodeFormat() { return { grammarScopes: this.getGrammarScopes(), priority: 1, formatOnSave: this.getFileCodeFormat.bind(this), }; } getFileCodeFormat(editor) { return __awaiter(this, void 0, void 0, function* () { const server = yield this._serverManager.getServer(editor); if (server == null || !server.capabilities.documentFormattingProvider) { return []; } return code_format_adapter_1.default.formatDocument(server.connection, editor); }); } provideOnTypeCodeFormat() { return { grammarScopes: this.getGrammarScopes(), priority: 1, formatAtPosition: this.getOnTypeCodeFormat.bind(this), }; } getOnTypeCodeFormat(editor, point, character) { return __awaiter(this, void 0, void 0, function* () { const server = yield this._serverManager.getServer(editor); if (server == null || !server.capabilities.documentOnTypeFormattingProvider) { return []; } return code_format_adapter_1.default.formatOnType(server.connection, editor, point, character); }); } provideCodeHighlight() { return { grammarScopes: this.getGrammarScopes(), priority: 1, highlight: (editor, position) => { return this.getCodeHighlight(editor, position); }, }; } getCodeHighlight(editor, position) { return __awaiter(this, void 0, void 0, function* () { const server = yield this._serverManager.getServer(editor); if (server == null || !code_highlight_adapter_1.default.canAdapt(server.capabilities)) { return null; } return code_highlight_adapter_1.default.highlight(server.connection, server.capabilities, editor, position); }); } provideCodeActions() { return { grammarScopes: this.getGrammarScopes(), priority: 1, getCodeActions: (editor, range, diagnostics) => { return this.getCodeActions(editor, range, diagnostics); }, }; } getCodeActions(editor, range, diagnostics) { return __awaiter(this, void 0, void 0, function* () { const server = yield this._serverManager.getServer(editor); if (server == null || !code_action_adapter_1.default.canAdapt(server.capabilities)) { return null; } return code_action_adapter_1.default.getCodeActions(server.connection, server.capabilities, this.getServerAdapter(server, "linterPushV2"), editor, range, diagnostics, this.filterCodeActions.bind(this), this.onApplyCodeActions.bind(this)); }); } /** Optionally filter code action before they're displayed */ filterCodeActions(actions) { return actions; } /** * Optionally handle a code action before default handling. Return `false` to prevent default handling, `true` to * continue with default handling. */ onApplyCodeActions(_action) { return __awaiter(this, void 0, void 0, function* () { return true; }); } provideRefactor() { return { grammarScopes: this.getGrammarScopes(), priority: 1, rename: this.getRename.bind(this), }; } getRename(editor, position, newName) { return __awaiter(this, void 0, void 0, function* () { const server = yield this._serverManager.getServer(editor); if (server == null || !rename_adapter_1.default.canAdapt(server.capabilities)) { return null; } return rename_adapter_1.default.getRename(server.connection, editor, position, newName); }); } consumeSignatureHelp(registry) { this._signatureHelpRegistry = registry; for (const server of this._serverManager.getActiveServers()) { const signatureHelpAdapter = this.getServerAdapter(server, "signatureHelpAdapter"); if (signatureHelpAdapter != null) { signatureHelpAdapter.attach(registry); } } return new atom_1.Disposable(() => { this._signatureHelpRegistry = undefined; }); } consumeBusySignal(service) { this.busySignalService = service; return new atom_1.Disposable(() => delete this.busySignalService); } /** * `didChangeWatchedFiles` message filtering, override for custom logic. * * @param filePath Path of a file that has changed in the project path * @returns `false` => message will not be sent to the language server */ filterChangeWatchedFiles(_filePath) { return true; } /** * Called on language server stderr output. * * @param stderr A chunk of stderr from a language server instance */ handleServerStderr(stderr, _projectPath) { stderr .split("\n") .filter((l) => l) .forEach((line) => this.logger.warn(`stderr ${line}`)); } getServerAdapter(server, adapter) { const adapters = this._serverAdapters.get(server); return adapters && adapters[adapter]; } } exports.default = AutoLanguageClient; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0by1sYW5ndWFnZWNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL2xpYi9hdXRvLWxhbmd1YWdlY2xpZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7OztBQUFBLG9DQUFtQztBQUVuQyxzQ0FBcUM7QUFDckMsK0NBQThDO0FBQzlDLDZCQUE0QjtBQUc1Qiw2Q0FBa0M7QUFDbEMsc0VBQTREO0FBQzVELDBFQUF5RztBQUN6RywwRUFBeUU7QUFDekUsd0VBQThEO0FBQzlELHdFQUE4RDtBQUM5RCw4RUFBb0U7QUFDcEUsZ0VBQXVEO0FBQ3ZELHNFQUE2RDtBQUM3RCw0RUFBa0U7QUFDbEUsZ0ZBQXNFO0FBQ3RFLDhFQUFtRTtBQUNuRSxnRkFBc0U7QUFDdEUsNEVBQW1FO0FBQ25FLDBFQUFnRTtBQUNoRSw4REFBcUQ7QUFDckQsOEVBQW9FO0FBQ3BFLHdFQUF1RTtBQUN2RSxpQ0FBZ0M7QUFFaEMscURBQTJEO0FBYXBDLHlHQWJkLHlDQUF3QixPQWFjO0FBWi9DLHFDQUFnRTtBQUNoRSwyREFNNEI7QUFDNUIsK0JBQWdGO0FBRWhGLCtCQUErQjtBQVcvQjs7Ozs7Ozs7R0FRRztBQUNILE1BQXFCLGtCQUFrQjtJQUF2QztRQU9VLG9CQUFlLEdBQVksS0FBSyxDQUFBO1FBQ2hDLG9CQUFlLEdBQUcsSUFBSSxPQUFPLEVBQWdDLENBQUE7UUFLM0Qsa0JBQWEsR0FBVyxFQUFFLENBQUE7UUE2K0JwQzs7O1dBR0c7UUFDTyx1QkFBa0IsR0FBWSxJQUFJLENBQUE7UUFzQmxDLG9CQUFlLEdBQTBCLENBQU8sS0FBSyxFQUFFLENBQUMsRUFBRSxFQUFFO1lBQ3BFLElBQUksSUFBSSxDQUFDLGlCQUFpQixFQUFFO2dCQUMxQixPQUFPLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxlQUFlLENBQUMsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFBO2FBQ3hEO2lCQUFNO2dCQUNMLE9BQU8sSUFBSSxDQUFDLHNCQUFzQixDQUFD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