coc.nvim
Version:
LSP based intellisense engine for neovim & vim8.
460 lines • 19.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const child_process_1 = tslib_1.__importDefault(require("child_process"));
const fs_1 = tslib_1.__importDefault(require("fs"));
const os_1 = tslib_1.__importDefault(require("os"));
const path_1 = tslib_1.__importDefault(require("path"));
const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol");
const types_1 = require("../types");
const util_1 = require("../util");
const Is = tslib_1.__importStar(require("../util/is"));
const processes_1 = require("../util/processes");
const workspace_1 = tslib_1.__importDefault(require("../workspace"));
const which_1 = tslib_1.__importDefault(require("which"));
const client_1 = require("./client");
const colorProvider_1 = require("./colorProvider");
const configuration_1 = require("./configuration");
const declaration_1 = require("./declaration");
const foldingRange_1 = require("./foldingRange");
const implementation_1 = require("./implementation");
const typeDefinition_1 = require("./typeDefinition");
const workspaceFolders_1 = require("./workspaceFolders");
const string_1 = require("../util/string");
const logger = require('../util/logger')('language-client-index');
tslib_1.__exportStar(require("./client"), exports);
var Executable;
(function (Executable) {
function is(value) {
return Is.string(value.command);
}
Executable.is = is;
})(Executable || (Executable = {}));
var TransportKind;
(function (TransportKind) {
TransportKind[TransportKind["stdio"] = 0] = "stdio";
TransportKind[TransportKind["ipc"] = 1] = "ipc";
TransportKind[TransportKind["pipe"] = 2] = "pipe";
TransportKind[TransportKind["socket"] = 3] = "socket";
})(TransportKind = exports.TransportKind || (exports.TransportKind = {}));
var Transport;
(function (Transport) {
function isSocket(value) {
let candidate = value;
return (candidate &&
candidate.kind === TransportKind.socket &&
Is.number(candidate.port));
}
Transport.isSocket = isSocket;
})(Transport || (Transport = {}));
var NodeModule;
(function (NodeModule) {
function is(value) {
return Is.string(value.module);
}
NodeModule.is = is;
})(NodeModule || (NodeModule = {}));
var StreamInfo;
(function (StreamInfo) {
function is(value) {
let candidate = value;
return (candidate && candidate.writer !== void 0 && candidate.reader !== void 0);
}
StreamInfo.is = is;
})(StreamInfo || (StreamInfo = {}));
var ChildProcessInfo;
(function (ChildProcessInfo) {
function is(value) {
let candidate = value;
return (candidate &&
candidate.process !== void 0 &&
typeof candidate.detached === 'boolean');
}
ChildProcessInfo.is = is;
})(ChildProcessInfo || (ChildProcessInfo = {}));
class LanguageClient extends client_1.BaseLanguageClient {
constructor(arg1, arg2, arg3, arg4, arg5) {
let id;
let name;
let serverOptions;
let clientOptions;
let forceDebug;
if (Is.string(arg2)) {
id = arg1;
name = arg2;
serverOptions = arg3;
clientOptions = arg4;
forceDebug = !!arg5;
}
else {
id = arg1.toLowerCase();
name = arg1;
serverOptions = arg2;
clientOptions = arg3;
forceDebug = arg4;
}
if (forceDebug === void 0) {
forceDebug = false;
}
super(id, name, clientOptions);
this._serverOptions = serverOptions;
this._forceDebug = forceDebug;
this.registerProposedFeatures();
}
stop() {
return super.stop().then(() => {
if (this._serverProcess) {
let toCheck = this._serverProcess;
this._serverProcess = undefined;
if (this._isDetached === void 0 || !this._isDetached) {
this.checkProcessDied(toCheck);
}
this._isDetached = undefined;
}
});
}
get serviceState() {
let state = this._state;
switch (state) {
case client_1.ClientState.Initial:
return types_1.ServiceStat.Initial;
case client_1.ClientState.Running:
return types_1.ServiceStat.Running;
case client_1.ClientState.StartFailed:
return types_1.ServiceStat.StartFailed;
case client_1.ClientState.Starting:
return types_1.ServiceStat.Starting;
case client_1.ClientState.Stopped:
return types_1.ServiceStat.Stopped;
case client_1.ClientState.Stopping:
return types_1.ServiceStat.Stopping;
default:
logger.error(`Unknown state: ${state}`);
return types_1.ServiceStat.Stopped;
}
}
static stateName(state) {
switch (state) {
case client_1.ClientState.Initial:
return 'Initial';
case client_1.ClientState.Running:
return 'Running';
case client_1.ClientState.StartFailed:
return 'StartFailed';
case client_1.ClientState.Starting:
return 'Starting';
case client_1.ClientState.Stopped:
return 'Stopped';
case client_1.ClientState.Stopping:
return 'Stopping';
default:
return 'Unknonw';
}
}
checkProcessDied(childProcess) {
if (!childProcess || global.hasOwnProperty('__TEST__'))
return;
setTimeout(() => {
// Test if the process is still alive. Throws an exception if not
try {
process.kill(childProcess.pid, 0);
processes_1.terminate(childProcess);
}
catch (error) {
// All is fine.
}
}, 1000);
}
handleConnectionClosed() {
this._serverProcess = undefined;
super.handleConnectionClosed();
}
async createMessageTransports(encoding) {
function getEnvironment(env) {
if (!env)
return process.env;
return Object.assign({}, process.env, env);
}
function startedInDebugMode() {
let args = process.execArgv;
if (args) {
return args.some(arg => /^--debug=?/.test(arg) ||
/^--debug-brk=?/.test(arg) ||
/^--inspect=?/.test(arg) ||
/^--inspect-brk=?/.test(arg));
}
return false;
}
let server = this._serverOptions;
// We got a function.
if (Is.func(server)) {
let result = await Promise.resolve(server());
if (client_1.MessageTransports.is(result)) {
this._isDetached = !!result.detached;
return result;
}
else if (StreamInfo.is(result)) {
this._isDetached = !!result.detached;
return {
reader: new vscode_languageserver_protocol_1.StreamMessageReader(result.reader),
writer: new vscode_languageserver_protocol_1.StreamMessageWriter(result.writer)
};
}
else {
let cp;
if (ChildProcessInfo.is(result)) {
cp = result.process;
this._isDetached = result.detached;
}
else {
cp = result;
this._isDetached = false;
}
cp.stderr.on('data', data => this.appendOutput(data, encoding));
return {
reader: new vscode_languageserver_protocol_1.StreamMessageReader(cp.stdout),
writer: new vscode_languageserver_protocol_1.StreamMessageWriter(cp.stdin)
};
}
}
let json = server;
let runDebug = server;
if (runDebug.run || runDebug.debug) {
// We are under debugging. So use debug as well.
if (typeof v8debug === 'object' || this._forceDebug || startedInDebugMode()) {
json = runDebug.debug;
}
else {
json = runDebug.run;
}
}
else {
json = server;
}
let serverWorkingDir = await this._getServerWorkingDir(json.options);
if (NodeModule.is(json) && json.module) {
let node = json;
let transport = node.transport || TransportKind.stdio;
let args = [];
let options = node.options || Object.create(null);
let runtime = node.runtime || process.execPath;
if (options.execArgv)
options.execArgv.forEach(element => args.push(element));
if (transport != TransportKind.ipc)
args.push(node.module);
if (node.args)
node.args.forEach(element => args.push(element));
let execOptions = Object.create(null);
execOptions.cwd = serverWorkingDir;
execOptions.env = getEnvironment(options.env);
let pipeName;
if (transport === TransportKind.ipc) {
execOptions.stdio = [null, null, null];
args.push('--node-ipc');
}
else if (transport === TransportKind.stdio) {
args.push('--stdio');
}
else if (transport === TransportKind.pipe) {
pipeName = vscode_languageserver_protocol_1.generateRandomPipeName();
args.push(`--pipe=${pipeName}`);
}
else if (Transport.isSocket(transport)) {
args.push(`--socket=${transport.port}`);
}
args.push(`--clientProcessId=${process.pid.toString()}`);
if (transport === TransportKind.ipc) {
let forkOptions = {
cwd: serverWorkingDir,
env: getEnvironment(options.env),
stdio: [null, null, null, 'ipc'],
execPath: runtime,
execArgv: options.execArgv || [],
};
let serverProcess = child_process_1.default.fork(node.module, args, forkOptions);
if (!serverProcess || !serverProcess.pid) {
throw new Error(`Launching server ${node.module} failed.`);
}
logger.info(`${this.id} started with ${serverProcess.pid}`);
this._serverProcess = serverProcess;
serverProcess.stdout.on('data', data => this.appendOutput(data, encoding));
serverProcess.stderr.on('data', data => this.appendOutput(data, encoding));
return {
reader: new vscode_languageserver_protocol_1.IPCMessageReader(serverProcess),
writer: new vscode_languageserver_protocol_1.IPCMessageWriter(serverProcess)
};
}
else if (transport === TransportKind.stdio) {
let serverProcess = child_process_1.default.spawn(runtime, args, execOptions);
if (!serverProcess || !serverProcess.pid) {
throw new Error(`Launching server ${node.module} failed.`);
}
logger.info(`${this.id} started with ${serverProcess.pid}`);
this._serverProcess = serverProcess;
serverProcess.stderr.on('data', data => this.appendOutput(data, encoding));
return {
reader: new vscode_languageserver_protocol_1.StreamMessageReader(serverProcess.stdout),
writer: new vscode_languageserver_protocol_1.StreamMessageWriter(serverProcess.stdin)
};
}
else if (transport == TransportKind.pipe) {
let transport = await Promise.resolve(vscode_languageserver_protocol_1.createClientPipeTransport(pipeName));
let process = child_process_1.default.spawn(runtime, args, execOptions);
if (!process || !process.pid) {
throw new Error(`Launching server ${node.module} failed.`);
}
logger.info(`${this.id} started with ${process.pid}`);
this._serverProcess = process;
process.stderr.on('data', data => this.appendOutput(data, encoding));
process.stdout.on('data', data => this.appendOutput(data, encoding));
let protocol = await Promise.resolve(transport.onConnected());
return { reader: protocol[0], writer: protocol[1] };
}
else if (Transport.isSocket(node.transport)) {
let transport = await Promise.resolve(vscode_languageserver_protocol_1.createClientSocketTransport(node.transport.port));
let process = child_process_1.default.spawn(runtime, args, execOptions);
if (!process || !process.pid) {
throw new Error(`Launching server ${node.module} failed.`);
}
logger.info(`${this.id} started with ${process.pid}`);
this._serverProcess = process;
process.stderr.on('data', data => this.appendOutput(data, encoding));
process.stdout.on('data', data => this.appendOutput(data, encoding));
let protocol = await Promise.resolve(transport.onConnected());
return { reader: protocol[0], writer: protocol[1] };
}
}
else if (Executable.is(json) && json.command) {
let command = json;
let args = command.args || [];
let options = Object.assign({}, command.options);
options.env = options.env ? Object.assign(options.env, process.env) : process.env;
options.cwd = options.cwd || serverWorkingDir;
let cmd = json.command;
if (cmd.startsWith('~')) {
cmd = os_1.default.homedir() + cmd.slice(1);
}
if (cmd.indexOf('$') !== -1) {
cmd = string_1.resolveVariables(cmd, { workspaceFolder: workspace_1.default.rootPath });
}
try {
which_1.default.sync(cmd);
}
catch (e) {
throw new Error(`Command "${cmd}" of ${this.id} is not executable: ${e}`);
}
let serverProcess = child_process_1.default.spawn(cmd, args, options);
if (!serverProcess || !serverProcess.pid) {
throw new Error(`Launching server using command ${command.command} failed.`);
}
logger.info(`${this.id} started with ${serverProcess.pid}`);
serverProcess.on('exit', code => {
if (code != 0)
this.error(`${command.command} exited with code: ${code}`);
});
serverProcess.stderr.on('data', data => this.appendOutput(data, encoding));
this._serverProcess = serverProcess;
this._isDetached = !!options.detached;
return {
reader: new vscode_languageserver_protocol_1.StreamMessageReader(serverProcess.stdout),
writer: new vscode_languageserver_protocol_1.StreamMessageWriter(serverProcess.stdin)
};
}
throw new Error(`Unsupported server configuration ` + JSON.stringify(server, null, 4));
}
registerProposedFeatures() {
this.registerFeatures(ProposedFeatures.createAll(this));
}
registerBuiltinFeatures() {
super.registerBuiltinFeatures();
this.registerFeature(new configuration_1.ConfigurationFeature(this));
this.registerFeature(new typeDefinition_1.TypeDefinitionFeature(this));
this.registerFeature(new implementation_1.ImplementationFeature(this));
this.registerFeature(new declaration_1.DeclarationFeature(this));
this.registerFeature(new colorProvider_1.ColorProviderFeature(this));
this.registerFeature(new foldingRange_1.FoldingRangeFeature(this));
if (!this.clientOptions.disableWorkspaceFolders) {
this.registerFeature(new workspaceFolders_1.WorkspaceFoldersFeature(this));
}
}
_getServerWorkingDir(options) {
let cwd = options && options.cwd;
if (cwd && !path_1.default.isAbsolute(cwd))
cwd = path_1.default.join(workspace_1.default.cwd, cwd);
if (!cwd)
cwd = workspace_1.default.cwd;
if (cwd) {
// make sure the folder exists otherwise creating the process will fail
return new Promise(s => {
fs_1.default.lstat(cwd, (err, stats) => {
s(!err && stats.isDirectory() ? cwd : undefined);
});
});
}
return Promise.resolve(undefined);
}
appendOutput(data, encoding) {
let msg = Is.string(data) ? data : data.toString(encoding);
if (global.hasOwnProperty('__TEST__')) {
console.log(msg); // tslint:disable-line
return;
}
if (process.env.NVIM_COC_LOG_LEVEL == 'debug') {
logger.debug(`[${this.id}]`, msg);
}
this.outputChannel.append(msg.endsWith('\n') ? msg : msg + '\n');
}
}
exports.LanguageClient = LanguageClient;
class SettingMonitor {
constructor(_client, _setting) {
this._client = _client;
this._setting = _setting;
this._listeners = [];
}
start() {
workspace_1.default.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(this._setting)) {
this.onDidChangeConfiguration();
}
}, null, this._listeners);
this.onDidChangeConfiguration();
return {
dispose: () => {
util_1.disposeAll(this._listeners);
if (this._client.needsStop()) {
this._client.stop();
}
}
};
}
onDidChangeConfiguration() {
let index = this._setting.indexOf('.');
let primary = index >= 0 ? this._setting.substr(0, index) : this._setting;
let rest = index >= 0 ? this._setting.substr(index + 1) : undefined;
let enabled = rest
? workspace_1.default.getConfiguration(primary).get(rest, true)
: workspace_1.default.getConfiguration(primary);
if (enabled && this._client.needsStart()) {
this._client.start();
}
else if (!enabled && this._client.needsStop()) {
this._client.stop();
}
}
}
exports.SettingMonitor = SettingMonitor;
// Exporting proposed protocol.
var ProposedFeatures;
(function (ProposedFeatures) {
function createAll(_client) {
let result = [];
return result;
}
ProposedFeatures.createAll = createAll;
})(ProposedFeatures = exports.ProposedFeatures || (exports.ProposedFeatures = {}));
//# sourceMappingURL=index.js.map