text: /* --------------------------------------------------------------------------------------------
   * Copyright (c) Microsoft Corporation. All rights reserved.
   * Licensed under the MIT License. See License.txt in the project root for license information.
   * ------------------------------------------------------------------------------------------ */
  'use strict';
  function __export(m) {
      for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
  }
  Object.defineProperty(exports, "__esModule", { value: true });
  const cp = require("child_process");
  const fs = require("fs");
  const client_1 = require("./client");
  const vscode_1 = require("vscode");
  const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol");
  const colorProvider_1 = require("./colorProvider");
  const configuration_1 = require("./configuration");
  const implementation_1 = require("./implementation");
  const typeDefinition_1 = require("./typeDefinition");
  const workspaceFolders_1 = require("./workspaceFolders");
  const Is = require("./utils/is");
  const electron = require("./utils/electron");
  const processes_1 = require("./utils/processes");
  __export(require("./client"));
  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;
      }
      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;
              }
          });
      }
      checkProcessDied(childProcess) {
          if (!childProcess) {
              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.
              }
          }, 2000);
      }
      handleConnectionClosed() {
          this._serverProcess = undefined;
          super.handleConnectionClosed();
      }
      createMessageTransports(encoding) {
          function getEnvironment(env) {
              if (!env) {
                  return process.env;
              }
              let result = Object.create(null);
              Object.keys(process.env).forEach(key => result[key] = process.env[key]);
              Object.keys(env).forEach(key => result[key] = env[key]);
              return result;
          }
          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)) {
              return server().then((result) => {
                  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.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
                      return { reader: new vscode_languageserver_protocol_1.StreamMessageReader(cp.stdout), writer: new vscode_languageserver_protocol_1.StreamMessageWriter(cp.stdin) };
                  }
              });
          }
          let json;
          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;
          }
          return this._getServerWorkingDir(json.options).then(serverWorkingDir => {
              if (NodeModule.is(json) && json.module) {
                  let node = json;
                  let transport = node.transport || TransportKind.stdio;
                  if (node.runtime) {
                      let args = [];
                      let options = node.options || Object.create(null);
                      if (options.execArgv) {
                          options.execArgv.forEach(element => args.push(element));
                      }
                      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 = undefined;
                      if (transport === TransportKind.ipc) {
                          // exec options not correctly typed in lib
                          execOptions.stdio = [null, null, null, 'ipc'];
                          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 || transport === TransportKind.stdio) {
                          let serverProcess = cp.spawn(node.runtime, args, execOptions);
                          if (!serverProcess || !serverProcess.pid) {
                              return Promise.reject(`Launching server using runtime ${node.runtime} failed.`);
                          }
                          this._serverProcess = serverProcess;
                          serverProcess.stderr.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
                          if (transport === TransportKind.ipc) {
                              serverProcess.stdout.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
                              return Promise.resolve({ reader: new vscode_languageserver_protocol_1.IPCMessageReader(serverProcess), writer: new vscode_languageserver_protocol_1.IPCMessageWriter(serverProcess) });
                          }
                          else {
                              return Promise.resolve({ reader: new vscode_languageserver_protocol_1.StreamMessageReader(serverProcess.stdout), writer: new vscode_languageserver_protocol_1.StreamMessageWriter(serverProcess.stdin) });
                          }
                      }
                      else if (transport == TransportKind.pipe) {
                          return vscode_languageserver_protocol_1.createClientPipeTransport(pipeName).then((transport) => {
                              let process = cp.spawn(node.runtime, args, execOptions);
                              if (!process || !process.pid) {
                                  return Promise.reject(`Launching server using runtime ${node.runtime} failed.`);
                              }
                              this._serverProcess = process;
                              process.stderr.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
                              process.stdout.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
                              return transport.onConnected().then((protocol) => {
                                  return { reader: protocol[0], writer: protocol[1] };
                              });
                          });
                      }
                      else if (Transport.isSocket(transport)) {
                          return vscode_languageserver_protocol_1.createClientSocketTransport(transport.port).then((transport) => {
                              let process = cp.spawn(node.runtime, args, execOptions);
                              if (!process || !process.pid) {
                                  return Promise.reject(`Launching server using runtime ${node.runtime} failed.`);
                              }
                              this._serverProcess = process;
                              process.stderr.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
                              process.stdout.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
                              return transport.onConnected().then((protocol) => {
                                  return { reader: protocol[0], writer: protocol[1] };
                              });
                          });
                      }
                  }
                  else {
                      let pipeName = undefined;
                      return new Promise((resolve, reject) => {
                          let args = node.args && node.args.slice() || [];
                          if (transport === TransportKind.ipc) {
                              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()}`);
                          let options = node.options || Object.create(null);
                          options.execArgv = options.execArgv || [];
                          options.cwd = serverWorkingDir;
                          if (transport === TransportKind.ipc || transport === TransportKind.stdio) {
                              electron.fork(node.module, args || [], options, (error, serverProcess) => {
                                  if (error || !serverProcess) {
                                      reject(error);
                                  }
                                  else {
                                      this._serverProcess = serverProcess;
                                      serverProcess.stderr.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
                                      if (transport === TransportKind.ipc) {
                                          serverProcess.stdout.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
                                          resolve({ reader: new vscode_languageserver_protocol_1.IPCMessageReader(this._serverProcess), writer: new vscode_languageserver_protocol_1.IPCMessageWriter(this._serverProcess) });
                                      }
                                      else {
                                          resolve({ reader: new vscode_languageserver_protocol_1.StreamMessageReader(serverProcess.stdout), writer: new vscode_languageserver_protocol_1.StreamMessageWriter(serverProcess.stdin) });
                                      }
                                  }
                              });
                          }
                          else if (transport === TransportKind.pipe) {
                              vscode_languageserver_protocol_1.createClientPipeTransport(pipeName).then((transport) => {
                                  electron.fork(node.module, args || [], options, (error, cp) => {
                                      if (error || !cp) {
                                          reject(error);
                                      }
                                      else {
                                          this._serverProcess = cp;
                                          cp.stderr.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
                                          cp.stdout.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
                                          transport.onConnected().then((protocol) => {
                                              resolve({ reader: protocol[0], writer: protocol[1] });
                                          });
                                      }
                                  });
                              });
                          }
                          else if (Transport.isSocket(transport)) {
                              vscode_languageserver_protocol_1.createClientSocketTransport(transport.port).then((transport) => {
                                  electron.fork(node.module, args || [], options, (error, cp) => {
                                      if (error || !cp) {
                                          reject(error);
                                      }
                                      else {
                                          this._serverProcess = cp;
                                          cp.stderr.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
                                          cp.stdout.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
                                          transport.onConnected().then((protocol) => {
                                              resolve({ reader: protocol[0], writer: protocol[1] });
                                          });
                                      }
                                  });
                              });
                          }
                      });
                  }
              }
              else if (Executable.is(json) && json.command) {
                  let command = json;
                  let args = command.args || [];
                  let options = command.options || Object.create(null);
                  options.cwd = serverWorkingDir;
                  let serverProcess = cp.spawn(command.command, args, options);
                  if (!serverProcess || !serverProcess.pid) {
                      return Promise.reject(`Launching server using command ${command.command} failed.`);
                  }
                  serverProcess.stderr.on('data', data => this.outputChannel.append(Is.string(data) ? data : data.toString(encoding)));
                  this._serverProcess = serverProcess;
                  this._isDetached = !!options.detached;
                  return Promise.resolve({ reader: new vscode_languageserver_protocol_1.StreamMessageReader(serverProcess.stdout), writer: new vscode_languageserver_protocol_1.StreamMessageWriter(serverProcess.stdin) });
              }
              return Promise.reject(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 colorProvider_1.ColorProviderFeature(this));
          this.registerFeature(new workspaceFolders_1.WorkspaceFoldersFeature(this));
      }
      _mainGetRootPath() {
          let folders = vscode_1.workspace.workspaceFolders;
          if (!folders || folders.length === 0) {
              return undefined;
          }
          let folder = folders[0];
          if (folder.uri.scheme === 'file') {
              return folder.uri.fsPath;
          }
          return undefined;
      }
      _getServerWorkingDir(options) {
          let cwd = options && options.cwd;
          if (!cwd) {
              cwd = this.clientOptions.workspaceFolder
                  ? this.clientOptions.workspaceFolder.uri.fsPath
                  : this._mainGetRootPath();
          }
          if (cwd) {
              // make sure the folder exists otherwise creating the process will fail
              return new Promise(s => {
                  fs.lstat(cwd, (err, stats) => {
                      s(!err && stats.isDirectory() ? cwd : undefined);
                  });
              });
          }
          return Promise.resolve(undefined);
      }
  }
  exports.LanguageClient = LanguageClient;
  class SettingMonitor {
      constructor(_client, _setting) {
          this._client = _client;
          this._setting = _setting;
          this._listeners = [];
      }
      start() {
          vscode_1.workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this._listeners);
          this.onDidChangeConfiguration();
          return new vscode_1.Disposable(() => {
              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 ? vscode_1.workspace.getConfiguration(primary).get(rest, false) : vscode_1.workspace.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 = {}));
  