From 54d03a04c14c45210c2a0f6a958b36659ed766fb Mon Sep 17 00:00:00 2001
From: chemzqm <chemzqm@gmail.com>
Date: Fri, 7 Sep 2018 20:40:51 +0800
Subject: [PATCH] init

---
 .gitignore                                    |    1 +
 .npmignore                                    |    4 +
 Readme.md                                     |   50 +
 package.json                                  |  397 +++
 src/index.ts                                  |   55 +
 src/server/LICENSE.txt                        |   23 +
 src/server/commands.ts                        |   79 +
 src/server/features/baseCodeLensProvider.ts   |  152 +
 src/server/features/bufferSyncSupport.ts      |  191 ++
 src/server/features/completionItemProvider.ts |  375 +++
 src/server/features/definitionProvider.ts     |   64 +
 src/server/features/diagnostics.ts            |  161 ++
 .../features/directiveCommentCompletions.ts   |   76 +
 src/server/features/documentHighlight.ts      |   48 +
 src/server/features/documentSymbol.ts         |  143 +
 .../features/fileConfigurationManager.ts      |  175 ++
 src/server/features/folding.ts                |   70 +
 src/server/features/formatting.ts             |  158 ++
 src/server/features/hover.ts                  |   54 +
 .../features/implementationsCodeLens.ts       |  100 +
 src/server/features/organizeImports.ts        |  101 +
 src/server/features/projectError.ts           |   49 +
 src/server/features/quickfix.ts               |  284 ++
 src/server/features/refactor.ts               |  221 ++
 src/server/features/references.ts             |   46 +
 src/server/features/referencesCodeLens.ts     |  102 +
 src/server/features/rename.ts                 |   70 +
 src/server/features/resourceMap.ts            |   81 +
 src/server/features/signatureHelp.ts          |   73 +
 src/server/features/tagCompletion.ts          |   53 +
 src/server/features/updatePathOnRename.ts     |  105 +
 src/server/features/watchBuild.ts             |  172 ++
 src/server/features/workspaceSymbols.ts       |   96 +
 src/server/index.ts                           |   94 +
 src/server/languageProvider.ts                |  349 +++
 src/server/protocol.const.ts                  |   39 +
 src/server/protocol.d.ts                      | 2475 +++++++++++++++++
 src/server/schema.json                        |  298 ++
 src/server/typescriptService.ts               |  215 ++
 src/server/typescriptServiceClient.ts         |  834 ++++++
 src/server/typescriptServiceClientHost.ts     |  201 ++
 src/server/utils/api.ts                       |   53 +
 src/server/utils/async.ts                     |   61 +
 src/server/utils/codeAction.ts                |   47 +
 src/server/utils/completionItem.ts            |  149 +
 src/server/utils/configuration.ts             |  109 +
 src/server/utils/fs.ts                        |   30 +
 src/server/utils/is.ts                        |   18 +
 src/server/utils/languageDescription.ts       |   32 +
 src/server/utils/languageModeIds.ts           |   14 +
 src/server/utils/logger.ts                    |   67 +
 src/server/utils/previewer.ts                 |   73 +
 src/server/utils/process.ts                   |  154 +
 src/server/utils/regexp.ts                    |    8 +
 src/server/utils/tracer.ts                    |  101 +
 src/server/utils/tsconfig.ts                  |   26 +
 src/server/utils/typeConverters.ts            |   91 +
 src/server/utils/typingsStatus.ts             |  115 +
 src/server/utils/versionProvider.ts           |  137 +
 src/server/utils/wireProtocol.ts              |  138 +
 tsconfig.json                                 |   18 +
 tslint.json                                   |   10 +
 yarn.lock                                     |  644 +++++
 63 files changed, 10429 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 .npmignore
 create mode 100644 Readme.md
 create mode 100644 package.json
 create mode 100644 src/index.ts
 create mode 100644 src/server/LICENSE.txt
 create mode 100644 src/server/commands.ts
 create mode 100644 src/server/features/baseCodeLensProvider.ts
 create mode 100644 src/server/features/bufferSyncSupport.ts
 create mode 100644 src/server/features/completionItemProvider.ts
 create mode 100644 src/server/features/definitionProvider.ts
 create mode 100644 src/server/features/diagnostics.ts
 create mode 100644 src/server/features/directiveCommentCompletions.ts
 create mode 100644 src/server/features/documentHighlight.ts
 create mode 100644 src/server/features/documentSymbol.ts
 create mode 100644 src/server/features/fileConfigurationManager.ts
 create mode 100644 src/server/features/folding.ts
 create mode 100644 src/server/features/formatting.ts
 create mode 100644 src/server/features/hover.ts
 create mode 100644 src/server/features/implementationsCodeLens.ts
 create mode 100644 src/server/features/organizeImports.ts
 create mode 100644 src/server/features/projectError.ts
 create mode 100644 src/server/features/quickfix.ts
 create mode 100644 src/server/features/refactor.ts
 create mode 100644 src/server/features/references.ts
 create mode 100644 src/server/features/referencesCodeLens.ts
 create mode 100644 src/server/features/rename.ts
 create mode 100644 src/server/features/resourceMap.ts
 create mode 100644 src/server/features/signatureHelp.ts
 create mode 100644 src/server/features/tagCompletion.ts
 create mode 100644 src/server/features/updatePathOnRename.ts
 create mode 100644 src/server/features/watchBuild.ts
 create mode 100644 src/server/features/workspaceSymbols.ts
 create mode 100644 src/server/index.ts
 create mode 100644 src/server/languageProvider.ts
 create mode 100644 src/server/protocol.const.ts
 create mode 100644 src/server/protocol.d.ts
 create mode 100644 src/server/schema.json
 create mode 100644 src/server/typescriptService.ts
 create mode 100644 src/server/typescriptServiceClient.ts
 create mode 100644 src/server/typescriptServiceClientHost.ts
 create mode 100644 src/server/utils/api.ts
 create mode 100644 src/server/utils/async.ts
 create mode 100644 src/server/utils/codeAction.ts
 create mode 100644 src/server/utils/completionItem.ts
 create mode 100644 src/server/utils/configuration.ts
 create mode 100644 src/server/utils/fs.ts
 create mode 100644 src/server/utils/is.ts
 create mode 100644 src/server/utils/languageDescription.ts
 create mode 100644 src/server/utils/languageModeIds.ts
 create mode 100644 src/server/utils/logger.ts
 create mode 100644 src/server/utils/previewer.ts
 create mode 100644 src/server/utils/process.ts
 create mode 100644 src/server/utils/regexp.ts
 create mode 100644 src/server/utils/tracer.ts
 create mode 100644 src/server/utils/tsconfig.ts
 create mode 100644 src/server/utils/typeConverters.ts
 create mode 100644 src/server/utils/typingsStatus.ts
 create mode 100644 src/server/utils/versionProvider.ts
 create mode 100644 src/server/utils/wireProtocol.ts
 create mode 100644 tsconfig.json
 create mode 100644 tslint.json
 create mode 100644 yarn.lock

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a65b417
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+lib
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..9db86d7
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,4 @@
+src
+tsconfig.json
+tslint.json
+*.map
diff --git a/Readme.md b/Readme.md
new file mode 100644
index 0000000..3889bde
--- /dev/null
+++ b/Readme.md
@@ -0,0 +1,50 @@
+# coc-tsserver
+
+Tsserver language server extension for [coc.nvim](https://github.com/neoclide/coc.nvim).
+
+Most code from `typescript-language-features` extension which bundled with VSCode.
+
+## Install
+
+In your vim/neovim, run command:
+
+```
+:CocInstall coc-tsserver
+```
+
+## Features
+
+Almost same as VSCode.
+
+* Support javascript & typescript and jsx/tsx.
+* Install typings automatically.
+* Commands to work with tsserver.
+* Code completion support.
+* Go to definition.
+* Code validation.
+* Document highlight.
+* Document symbols of current buffer.
+* Folding and folding range of current buffer.
+* Format current buffer, range format and format on type.
+* Hover for documentation.
+* Implementations codeLens and references codeLens.
+* Organize imports command.
+* Quickfix using code actions.
+* Code refactor using code actions.
+* Find references.
+* Signature help.
+* Rename symbols support.
+* Rename imports on file rename.
+* Search for workspace symbols.
+
+## Configuration options
+
+* `tsserver.enable` set to `false` to disable tsserver language server.
+* `tsserver.trace.server` trace LSP traffic in output channel.
+
+And many more, which are same as VSCode, trigger completion in your
+`coc-settings.json` to get full list.
+
+## License
+
+MIT
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..0ed5999
--- /dev/null
+++ b/package.json
@@ -0,0 +1,397 @@
+{
+  "name": "coc-tsserver",
+  "version": "1.0.0",
+  "description": "tsserver extension for coc",
+  "main": "lib/index.js",
+  "publisher": "chemzqm",
+  "engines": {
+    "coc": "^0.0.15"
+  },
+  "scripts": {
+    "clean": "rimraf lib",
+    "build": "tsc -p tsconfig.json",
+    "prepare": "yarn clean && yarn build"
+  },
+  "contributes": {
+    "commands": [
+      {
+        "title": "Reload current project",
+        "category": "TSServer",
+        "command": "tsserver.reloadProjects"
+      },
+      {
+        "title": "Open log file of tsserver.",
+        "category": "TSServer",
+        "command": "tsserver.openTsServerLog"
+      },
+      {
+        "title": "Open project config file.",
+        "category": "TSServer",
+        "command": "tsserver.goToProjectConfig"
+      },
+      {
+        "title": "Restart tsserver.",
+        "category": "TSServer",
+        "command": "tsserver.restart"
+      },
+      {
+        "title": "Format current buffer.",
+        "category": "TSServer",
+        "command": "tsserver.format"
+      },
+      {
+        "title": "Orgnize imports of current buffer.",
+        "category": "TSServer",
+        "command": "tsserver.organizeImports"
+      },
+      {
+        "title": "Run `tsc --watch` for current project in terminal buffer.",
+        "category": "TSServer",
+        "command": "tsserver.watchBuild"
+      }
+    ],
+    "configuration": {
+      "type": "object",
+      "title": "Tsserver",
+      "properties": {
+        "tsserver.enable": {
+          "type": "boolean",
+          "default": true,
+          "description": "Enable tsserver extension"
+        },
+        "tsserver.locale": {
+          "type": "string",
+          "default": "",
+          "description": "Locale of tsserver"
+        },
+        "tsserver.typingsCacheLocation": {
+          "type": "string",
+          "default": "",
+          "description": "Folder path for cache typings"
+        },
+        "tsserver.formatOnSave": {
+          "type": "boolean",
+          "default": false,
+          "description": "Format document on buffer will save"
+        },
+        "tsserver.orgnizeImportOnSave": {
+          "type": "boolean",
+          "default": false,
+          "description": "Orgnize import on buffer will save"
+        },
+        "tsserver.formatOnType": {
+          "type": "boolean",
+          "default": true,
+          "description": "Run format on type special characters."
+        },
+        "tsserver.enableJavascript": {
+          "type": "boolean",
+          "default": true,
+          "description": "Use tsserver for javascript files"
+        },
+        "tsserver.tsdk": {
+          "type": "string",
+          "default": "",
+          "description": "Directory contains tsserver.js, works for workspace only"
+        },
+        "tsserver.npm": {
+          "type": "string",
+          "default": "",
+          "description": "Executable path of npm for download typings"
+        },
+        "tsserver.log": {
+          "type": "string",
+          "default": "off",
+          "enum": [
+            "normal",
+            "terse",
+            "verbose",
+            "off"
+          ],
+          "description": "Log level of tsserver"
+        },
+        "tsserver.trace.server": {
+          "type": "string",
+          "default": "off",
+          "enum": [
+            "off",
+            "messages",
+            "verbose"
+          ],
+          "description": "Trace level of tsserver"
+        },
+        "tserver.pluginNames": {
+          "type": "array",
+          "default": [],
+          "items": {
+            "type": "string"
+          },
+          "description": "Module names of tsserver plugins"
+        },
+        "tsserver.pluginRoot": {
+          "type": "string",
+          "default": "",
+          "description": "Folder contains tsserver plugins"
+        },
+        "tsserver.debugPort": {
+          "type": "number",
+          "description": "Debug port number of tsserver"
+        },
+        "tsserver.reportStyleChecksAsWarnings": {
+          "type": "boolean",
+          "default": true
+        },
+        "tsserver.implicitProjectConfig.checkJs": {
+          "type": "boolean",
+          "default": false,
+          "description": "Enable checkJs for implicit project"
+        },
+        "tsserver.implicitProjectConfig.experimentalDecorators": {
+          "type": "boolean",
+          "default": false,
+          "description": "Enable experimentalDecorators for implicit project"
+        },
+        "tsserver.disableAutomaticTypeAcquisition": {
+          "type": "boolean",
+          "default": false,
+          "description": "Disable download of typings"
+        },
+        "typescript.updateImportsOnFileMove.enable": {
+          "type": "boolean",
+          "default": true,
+          "description": "Enable update imports on file move."
+        },
+        "typescript.implementationsCodeLens.enable": {
+          "type": "boolean",
+          "default": true,
+          "description": "Enable codeLens for implementations"
+        },
+        "typescript.referencesCodeLens.enable": {
+          "type": "boolean",
+          "default": true,
+          "description": "Enable codeLens for references"
+        },
+        "typescript.preferences.completion.useCodeSnippetsOnMethodSuggest": {
+          "type": "boolean",
+          "default": true,
+          "description": "Enable snippet for method suggestion"
+        },
+        "typescript.preferences.completion.nameSuggestions": {
+          "type": "boolean",
+          "default": true,
+          "description": "Complete for warning type of tsserver"
+        },
+        "typescript.preferences.completion.autoImportSuggestions": {
+          "type": "boolean",
+          "default": true,
+          "description": "Enable auto import suggestions for completion"
+        },
+        "typescript.preferences.completion.commaAfterImport": {
+          "type": "boolean",
+          "default": true,
+          "description": "Add comma after import"
+        },
+        "typescript.preferences.completion.moduleExports": {
+          "type": "boolean",
+          "default": true,
+          "description": "Include completion for module.exports"
+        },
+        "typescript.preferences.importModuleSpecifier": {
+          "type": "string",
+          "default": "non-relative",
+          "enum": [
+            "non-relative",
+            "relative"
+          ]
+        },
+        "typescript.preferences.suggestionActions.enabled": {
+          "type": "boolean",
+          "default": true
+        },
+        "typescript.preferences.quoteStyle": {
+          "type": "string",
+          "default": "single",
+          "enum": [
+            "single",
+            "double"
+          ]
+        },
+        "typescript.format.insertSpaceAfterCommaDelimiter": {
+          "type": "boolean",
+          "default": true
+        },
+        "typescript.format.insertSpaceAfterConstructor": {
+          "type": "boolean",
+          "default": false
+        },
+        "typescript.format.insertSpaceAfterSemicolonInForStatements": {
+          "type": "boolean",
+          "default": true
+        },
+        "typescript.format.insertSpaceBeforeAndAfterBinaryOperators": {
+          "type": "boolean",
+          "default": true
+        },
+        "typescript.format.insertSpaceAfterKeywordsInControlFlowStatements": {
+          "type": "boolean",
+          "default": true
+        },
+        "typescript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": {
+          "type": "boolean",
+          "default": true
+        },
+        "typescript.format.insertSpaceBeforeFunctionParenthesis": {
+          "type": "boolean",
+          "default": false
+        },
+        "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": {
+          "type": "boolean",
+          "default": false
+        },
+        "typescript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": {
+          "type": "boolean",
+          "default": false
+        },
+        "typescript.format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": {
+          "type": "boolean",
+          "default": false
+        },
+        "typescript.format.insertSpaceAfterTypeAssertion": {
+          "type": "boolean",
+          "default": false
+        },
+        "typescript.format.placeOpenBraceOnNewLineForFunctions": {
+          "type": "boolean",
+          "default": false
+        },
+        "typescript.format.placeOpenBraceOnNewLineForControlBlocks": {
+          "type": "boolean",
+          "default": false
+        },
+        "javascript.updateImportsOnFileMove.enable": {
+          "type": "boolean",
+          "default": true
+        },
+        "javascript.implementationsCodeLens.enable": {
+          "type": "boolean",
+          "default": true
+        },
+        "javascript.referencesCodeLens.enable": {
+          "type": "boolean",
+          "default": true
+        },
+        "javascript.preferences.completion.useCodeSnippetsOnMethodSuggest": {
+          "type": "boolean",
+          "default": true
+        },
+        "javascript.preferences.completion.nameSuggestions": {
+          "type": "boolean",
+          "default": true
+        },
+        "javascript.preferences.completion.autoImportSuggestions": {
+          "type": "boolean",
+          "default": true
+        },
+        "javascript.preferences.completion.commaAfterImport": {
+          "type": "boolean",
+          "default": true
+        },
+        "javascript.preferences.completion.moduleExports": {
+          "type": "boolean",
+          "default": true,
+          "description": "Include completion for module.exports"
+        },
+        "javascript.preferences.importModuleSpecifier": {
+          "type": "string",
+          "default": "non-relative",
+          "enum": [
+            "non-relative",
+            "relative"
+          ]
+        },
+        "javascript.preferences.suggestionActions.enabled": {
+          "type": "boolean",
+          "default": true
+        },
+        "javascript.preferences.quoteStyle": {
+          "type": "string",
+          "default": "single",
+          "enum": [
+            "single",
+            "double"
+          ]
+        },
+        "javascript.format.insertSpaceAfterCommaDelimiter": {
+          "type": "boolean",
+          "default": true
+        },
+        "javascript.format.insertSpaceAfterConstructor": {
+          "type": "boolean",
+          "default": false
+        },
+        "javascript.format.insertSpaceAfterSemicolonInForStatements": {
+          "type": "boolean",
+          "default": true
+        },
+        "javascript.format.insertSpaceBeforeAndAfterBinaryOperators": {
+          "type": "boolean",
+          "default": true
+        },
+        "javascript.format.insertSpaceAfterKeywordsInControlFlowStatements": {
+          "type": "boolean",
+          "default": true
+        },
+        "javascript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": {
+          "type": "boolean",
+          "default": true
+        },
+        "javascript.format.insertSpaceBeforeFunctionParenthesis": {
+          "type": "boolean",
+          "default": false
+        },
+        "javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": {
+          "type": "boolean",
+          "default": false
+        },
+        "javascript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": {
+          "type": "boolean",
+          "default": false
+        },
+        "javascript.format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": {
+          "type": "boolean",
+          "default": false
+        },
+        "javascript.format.insertSpaceAfterTypeAssertion": {
+          "type": "boolean",
+          "default": false
+        },
+        "javascript.format.placeOpenBraceOnNewLineForFunctions": {
+          "type": "boolean",
+          "default": false
+        },
+        "javascript.format.placeOpenBraceOnNewLineForControlBlocks": {
+          "type": "boolean",
+          "default": false
+        }
+      }
+    }
+  },
+  "author": "chemzqm@gmail.com",
+  "license": "MIT",
+  "devDependencies": {
+    "@chemzqm/tsconfig": "^0.0.3",
+    "@chemzqm/tslint-config": "^1.0.17",
+    "@types/node": "^10.9.4",
+    "coc.nvim": "^0.0.15",
+    "rimraf": "^2.6.2",
+    "tslint": "^5.11.0",
+    "typescript": "^3.0.3"
+  },
+  "dependencies": {
+    "semver": "^5.5.1",
+    "tslib": "^1.9.3",
+    "vscode-languageserver-protocol": "^3.12.0",
+    "vscode-uri": "^1.0.6",
+    "which": "^1.3.1"
+  }
+}
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 0000000..01be497
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,55 @@
+import { services, commands, languages, ExtensionContext, workspace, TextDocumentWillSaveEvent, ServiceStat } from 'coc.nvim'
+import TsserverService from './server'
+import { languageIds } from './server/utils/languageModeIds'
+import { OpenTsServerLogCommand, ReloadProjectsCommand, TypeScriptGoToProjectConfigCommand } from './server/commands'
+import { TextEdit } from 'vscode-languageserver-types'
+import { Command } from './server/commands'
+
+export async function activate(context: ExtensionContext): Promise<void> {
+  let { subscriptions } = context
+  const config = workspace.getConfiguration().get('tsserver', {}) as any
+  if (!config.enable) return
+  const service = new TsserverService()
+
+  subscriptions.push(
+    (services as any).regist(service)
+  )
+
+  function onWillSave(event: TextDocumentWillSaveEvent): void {
+    if (service.state != ServiceStat.Running) return
+    let config = service.config
+    let formatOnSave = config.get<boolean>('formatOnSave')
+    if (!formatOnSave) return
+    let { languageId } = event.document
+    if (languageIds.indexOf(languageId) == -1) return
+    let willSaveWaitUntil = async (): Promise<TextEdit[]> => {
+      let options = await workspace.getFormatOptions(event.document.uri)
+      let textEdits = await languages.provideDocumentFormattingEdits(event.document, options)
+      return textEdits
+    }
+    event.waitUntil(willSaveWaitUntil())
+  }
+
+  function registCommand(cmd: Command): void {
+    let { id, execute } = cmd
+    subscriptions.push(commands.registerCommand(id as string, execute, cmd))
+  }
+
+  registCommand(new ReloadProjectsCommand(service.clientHost))
+  registCommand(new OpenTsServerLogCommand(service.clientHost))
+  registCommand(new TypeScriptGoToProjectConfigCommand(service.clientHost))
+  registCommand(commands.register({
+    id: 'tsserver.restart',
+    execute: (): void => {
+      service.stop().then(() => {
+        setTimeout(() => {
+          service.restart()
+        }, 100)
+      })
+    }
+  }))
+
+  subscriptions.push(
+    workspace.onWillSaveUntil(onWillSave, null, 'tsserver')
+  )
+}
diff --git a/src/server/LICENSE.txt b/src/server/LICENSE.txt
new file mode 100644
index 0000000..9afc63d
--- /dev/null
+++ b/src/server/LICENSE.txt
@@ -0,0 +1,23 @@
+MIT License
+
+Copyright (c) 2015 - present Microsoft Corporation
+
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/server/commands.ts b/src/server/commands.ts
new file mode 100644
index 0000000..2bd684d
--- /dev/null
+++ b/src/server/commands.ts
@@ -0,0 +1,79 @@
+import { CancellationToken } from 'vscode-languageserver-protocol'
+import URI from 'vscode-uri'
+import { workspace } from 'coc.nvim'
+import { ProjectInfoResponse } from './protocol'
+import TypeScriptServiceClientHost from './typescriptServiceClientHost'
+
+export interface Command {
+  readonly id: string | string[]
+  execute(...args: any[]): void | Promise<any>
+}
+
+export class ReloadProjectsCommand implements Command {
+  public readonly id = 'tsserver.reloadProjects'
+
+  public constructor(
+    private readonly client: TypeScriptServiceClientHost
+  ) { }
+
+  public execute(): void {
+    this.client.reloadProjects()
+    workspace.showMessage('projects reloaded')
+  }
+}
+
+export class OpenTsServerLogCommand implements Command {
+  public readonly id = 'tsserver.openTsServerLog'
+
+  public constructor(
+    private readonly client: TypeScriptServiceClientHost
+  ) { }
+
+  public execute(): void {
+    this.client.serviceClient.openTsServerLogFile() // tslint:disable-line
+  }
+}
+
+export class TypeScriptGoToProjectConfigCommand implements Command {
+  public readonly id = 'tsserver.goToProjectConfig'
+
+  public constructor(
+    private readonly client: TypeScriptServiceClientHost
+  ) { }
+
+  public async execute(): Promise<void> {
+    let doc = await workspace.document
+    await goToProjectConfig(this.client, doc.uri)
+  }
+}
+
+async function goToProjectConfig(clientHost: TypeScriptServiceClientHost, uri: string): Promise<void> {
+  if (!clientHost.handles(uri)) {
+    workspace.showMessage('Could not determine TypeScript or JavaScript project. Unsupported file type', 'warning')
+    return
+  }
+  const client = clientHost.serviceClient
+  const file = client.toPath(uri)
+  let res: ProjectInfoResponse | undefined
+  try {
+    res = await client.execute('projectInfo', { file, needFileNameList: false }, CancellationToken.None)
+  } catch {
+    // noop
+  }
+  if (!res || !res.body) {
+    workspace.showMessage('Could not determine TypeScript or JavaScript project.', 'warning')
+    return
+  }
+
+  const { configFileName } = res.body
+  if (configFileName && !isImplicitProjectConfigFile(configFileName)) {
+    await workspace.openResource(URI.file(configFileName).toString())
+    return
+  }
+
+  workspace.showMessage('Config file not found', 'warning')
+}
+
+function isImplicitProjectConfigFile(configFileName: string): boolean {
+  return configFileName.indexOf('/dev/null/') === 0
+}
diff --git a/src/server/features/baseCodeLensProvider.ts b/src/server/features/baseCodeLensProvider.ts
new file mode 100644
index 0000000..e906159
--- /dev/null
+++ b/src/server/features/baseCodeLensProvider.ts
@@ -0,0 +1,152 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { CancellationToken, CodeLens, Emitter, Event, Range, TextDocument } from 'vscode-languageserver-protocol'
+import { CodeLensProvider } from 'coc.nvim/lib/provider'
+import * as Proto from '../protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import { escapeRegExp } from '../utils/regexp'
+import * as typeConverters from '../utils/typeConverters'
+
+export class CachedNavTreeResponse {
+  private response?: Promise<Proto.NavTreeResponse>
+  private version = -1
+  private document = ''
+
+  public execute(document: TextDocument, f: () => Promise<Proto.NavTreeResponse>): Promise<Proto.NavTreeResponse> {
+    if (this.matches(document)) {
+      return this.response
+    }
+
+    return this.update(document, f())
+  }
+
+  private matches(document: TextDocument): boolean {
+    return (
+      this.version === document.version &&
+      this.document === document.uri.toString()
+    )
+  }
+
+  private update(
+    document: TextDocument,
+    response: Promise<Proto.NavTreeResponse>
+  ): Promise<Proto.NavTreeResponse> {
+    this.response = response
+    this.version = document.version
+    this.document = document.uri.toString()
+    return response
+  }
+}
+
+export abstract class TypeScriptBaseCodeLensProvider implements CodeLensProvider {
+  private onDidChangeCodeLensesEmitter = new Emitter<void>()
+
+  public constructor(
+    protected client: ITypeScriptServiceClient,
+    private cachedResponse: CachedNavTreeResponse
+  ) { }
+
+  public get onDidChangeCodeLenses(): Event<void> {
+    return this.onDidChangeCodeLensesEmitter.event
+  }
+
+  public async provideCodeLenses(
+    document: TextDocument,
+    token: CancellationToken
+  ): Promise<CodeLens[]> {
+    const filepath = this.client.toPath(document.uri)
+    if (!filepath) {
+      return []
+    }
+
+    try {
+      const response = await this.cachedResponse.execute(document, () =>
+        this.client.execute('navtree', { file: filepath }, token)
+      )
+      if (!response) {
+        return []
+      }
+
+      const tree = response.body
+      const referenceableSpans: Range[] = []
+      if (tree && tree.childItems) {
+        tree.childItems.forEach(item =>
+          this.walkNavTree(document, item, null, referenceableSpans)
+        )
+      }
+      return referenceableSpans.map(
+        range => {
+          return {
+            range,
+            data: { uri: document.uri }
+          }
+        }
+      )
+    } catch {
+      return []
+    }
+  }
+
+  protected abstract extractSymbol(
+    document: TextDocument,
+    item: Proto.NavigationTree,
+    parent: Proto.NavigationTree | null
+  ): Range | null
+
+  private walkNavTree(
+    document: TextDocument,
+    item: Proto.NavigationTree,
+    parent: Proto.NavigationTree | null,
+    results: Range[]
+  ): void {
+    if (!item) {
+      return
+    }
+
+    const range = this.extractSymbol(document, item, parent)
+    if (range) {
+      results.push(range)
+    }
+    if (item.childItems) {
+      item.childItems.forEach(child =>
+        this.walkNavTree(document, child, item, results)
+      )
+    }
+  }
+  protected getSymbolRange(
+    document: TextDocument,
+    item: Proto.NavigationTree
+  ): Range | null {
+    if (!item) {
+      return null
+    }
+
+    // TS 3.0+ provides a span for just the symbol
+    if ((item as any).nameSpan) {
+      return typeConverters.Range.fromTextSpan((item as any).nameSpan)
+    }
+
+    // In older versions, we have to calculate this manually. See #23924
+    const span = item.spans && item.spans[0]
+    if (!span) {
+      return null
+    }
+
+    const range = typeConverters.Range.fromTextSpan(span)
+    const text = document.getText(range)
+
+    const identifierMatch = new RegExp(
+      `^(.*?(\\b|\\W))${escapeRegExp(item.text || '')}(\\b|\\W)`,
+      'gm'
+    )
+    const match = identifierMatch.exec(text)
+    const prefixLength = match ? match.index + match[1].length : 0
+    const startOffset = document.offsetAt(range.start) + prefixLength
+    return {
+      start: document.positionAt(startOffset),
+      end: document.positionAt(startOffset + item.text.length)
+    }
+  }
+}
diff --git a/src/server/features/bufferSyncSupport.ts b/src/server/features/bufferSyncSupport.ts
new file mode 100644
index 0000000..7fcaf73
--- /dev/null
+++ b/src/server/features/bufferSyncSupport.ts
@@ -0,0 +1,191 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { DidChangeTextDocumentParams, Disposable, TextDocument } from 'vscode-languageserver-protocol'
+import { disposeAll, workspace } from 'coc.nvim'
+import Proto from '../protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import API from '../utils/api'
+import { Delayer } from '../utils/async'
+import * as languageModeIds from '../utils/languageModeIds'
+
+function mode2ScriptKind(
+  mode: string
+): 'TS' | 'TSX' | 'JS' | 'JSX' | undefined {
+  switch (mode) {
+    case languageModeIds.typescript:
+      return 'TS'
+    case languageModeIds.typescripttsx:
+      return 'TSX'
+    case languageModeIds.typescriptjsx:
+      return 'TSX'
+    case languageModeIds.typescriptreact:
+      return 'TSX'
+    case languageModeIds.javascript:
+      return 'JS'
+    case languageModeIds.javascriptreact:
+      return 'JSX'
+  }
+  return undefined
+}
+
+export default class BufferSyncSupport {
+  private readonly client: ITypeScriptServiceClient
+
+  private _validate: boolean
+  private readonly modeIds: Set<string>
+  private readonly uris: Set<string> = new Set()
+  private readonly disposables: Disposable[] = []
+
+  private readonly pendingDiagnostics = new Map<string, number>()
+  private readonly diagnosticDelayer: Delayer<any>
+
+  constructor(
+    client: ITypeScriptServiceClient,
+    modeIds: string[],
+    validate: boolean
+  ) {
+    this.client = client
+    this.modeIds = new Set<string>(modeIds)
+    this._validate = validate || false
+    this.diagnosticDelayer = new Delayer<any>(300)
+  }
+
+  public listen(): void {
+    workspace.onDidOpenTextDocument(
+      this.onDidOpenTextDocument,
+      this,
+      this.disposables
+    )
+    workspace.onDidCloseTextDocument(
+      this.onDidCloseTextDocument,
+      this,
+      this.disposables
+    )
+    workspace.onDidChangeTextDocument(
+      this.onDidChangeTextDocument,
+      this,
+      this.disposables
+    )
+    workspace.textDocuments.forEach(this.onDidOpenTextDocument, this)
+  }
+
+  public reInitialize(): void {
+    workspace.textDocuments.forEach(this.onDidOpenTextDocument, this)
+  }
+
+  public set validate(value: boolean) {
+    this._validate = value
+  }
+
+  public dispose(): void {
+    this.pendingDiagnostics.clear()
+    disposeAll(this.disposables)
+  }
+
+  private onDidOpenTextDocument(document: TextDocument): void {
+    if (!this.modeIds.has(document.languageId)) return
+    let { uri } = document
+    let filepath = this.client.toPath(uri)
+    this.uris.add(uri)
+    const args: Proto.OpenRequestArgs = {
+      file: filepath,
+      fileContent: document.getText()
+    }
+
+    if (this.client.apiVersion.gte(API.v203)) {
+      const scriptKind = mode2ScriptKind(document.languageId)
+      if (scriptKind) {
+        args.scriptKindName = scriptKind
+      }
+    }
+    this.client.execute('open', args, false) // tslint:disable-line
+    this.requestDiagnostic(uri)
+  }
+
+  private onDidCloseTextDocument(document: TextDocument): void {
+    let { uri } = document
+    if (!this.uris.has(uri)) return
+    let filepath = this.client.toPath(uri)
+    const args: Proto.FileRequestArgs = {
+      file: filepath
+    }
+    this.client.execute('close', args, false) // tslint:disable-line
+  }
+
+  private onDidChangeTextDocument(e: DidChangeTextDocumentParams): void {
+    let { textDocument, contentChanges } = e
+    let { uri } = textDocument
+    if (!this.uris.has(uri)) return
+    let filepath = this.client.toPath(uri)
+    for (const { range, text } of contentChanges) {
+      const args: Proto.ChangeRequestArgs = {
+        file: filepath,
+        line: range ? range.start.line + 1 : 1,
+        offset: range ? range.start.character + 1 : 1,
+        endLine: range ? range.end.line + 1 : 2 ** 24,
+        endOffset: range ? range.end.character + 1 : 1,
+        insertString: text
+      }
+      this.client.execute('change', args, false) // tslint:disable-line
+    }
+    this.requestDiagnostic(uri)
+  }
+
+  public requestAllDiagnostics(): void {
+    if (!this._validate) {
+      return
+    }
+    for (const uri of this.uris) {
+      this.pendingDiagnostics.set(uri, Date.now())
+    }
+    this.diagnosticDelayer.trigger(() => { // tslint:disable-line
+      this.sendPendingDiagnostics()
+    }, 200)
+  }
+
+  public requestDiagnostic(uri: string): void {
+    if (!this._validate) {
+      return
+    }
+    let document = workspace.getDocument(uri)
+    if (!document) return
+    this.pendingDiagnostics.set(uri, Date.now())
+    let delay = 300
+    const lineCount = document.lineCount
+    delay = Math.min(Math.max(Math.ceil(lineCount / 20), 300), 800)
+    this.diagnosticDelayer.trigger(() => {
+      this.sendPendingDiagnostics()
+    }, delay) // tslint:disable-line
+  }
+
+  public hasPendingDiagnostics(uri: string): boolean {
+    return this.pendingDiagnostics.has(uri)
+  }
+
+  private sendPendingDiagnostics(): void {
+    if (!this._validate) {
+      return
+    }
+    const files = Array.from(this.pendingDiagnostics.entries())
+      .sort((a, b) => a[1] - b[1])
+      .map(entry => this.client.toPath(entry[0]))
+
+    // Add all open TS buffers to the geterr request. They might be visible
+    for (const uri of this.uris) {
+      if (!this.pendingDiagnostics.get(uri)) {
+        let file = this.client.toPath(uri)
+        files.push(file)
+      }
+    }
+    if (files.length) {
+      const args: Proto.GeterrRequestArgs = {
+        delay: 0,
+        files
+      }
+      this.client.execute('geterr', args, false) // tslint:disable-line
+    }
+    this.pendingDiagnostics.clear()
+  }
+}
diff --git a/src/server/features/completionItemProvider.ts b/src/server/features/completionItemProvider.ts
new file mode 100644
index 0000000..5acd8b3
--- /dev/null
+++ b/src/server/features/completionItemProvider.ts
@@ -0,0 +1,375 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { CancellationToken, Command, CompletionContext, CompletionItem, InsertTextFormat, MarkupContent, MarkupKind, Position, TextDocument, TextEdit } from 'vscode-languageserver-protocol'
+import { commands, workspace } from 'coc.nvim'
+import { CompletionItemProvider } from 'coc.nvim/lib/provider'
+import Proto from '../protocol'
+import * as PConst from '../protocol.const'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import API from '../utils/api'
+import { applyCodeAction } from '../utils/codeAction'
+import { convertCompletionEntry, resolveItem } from '../utils/completionItem'
+import * as Previewer from '../utils/previewer'
+import * as typeConverters from '../utils/typeConverters'
+import TypingsStatus from '../utils/typingsStatus'
+import FileConfigurationManager, { CompletionOptions } from './fileConfigurationManager'
+
+// command center
+export interface CommandItem {
+  readonly id: string | string[]
+  execute(...args: any[]): void | Promise<any>
+}
+
+class ApplyCompletionCodeActionCommand implements CommandItem {
+  public static readonly ID = '_typescript.applyCompletionCodeAction'
+  public readonly id = ApplyCompletionCodeActionCommand.ID
+  public constructor(
+    private readonly client: ITypeScriptServiceClient
+  ) {
+  }
+
+  // apply code action on complete
+  public async execute(codeActions: Proto.CodeAction[]): Promise<void> {
+    if (codeActions.length === 0) {
+      return
+    }
+    if (codeActions.length === 1) {
+      await applyCodeAction(this.client, codeActions[0])
+      return
+    }
+    const idx = await workspace.showQuickpick(codeActions.map(o => o.description), 'Select code action to apply')
+    if (idx < 0) return
+    const action = codeActions[idx]
+    await applyCodeAction(this.client, action)
+    return
+  }
+}
+
+export default class TypeScriptCompletionItemProvider implements CompletionItemProvider {
+
+  public static readonly triggerCharacters = ['.', '@', '<']
+  private completeOption: CompletionOptions
+
+  constructor(
+    private readonly client: ITypeScriptServiceClient,
+    private readonly typingsStatus: TypingsStatus,
+    private readonly fileConfigurationManager: FileConfigurationManager,
+    languageId: string
+  ) {
+
+    this.setCompleteOption(languageId)
+    commands.register(new ApplyCompletionCodeActionCommand(this.client))
+  }
+
+  private setCompleteOption(languageId: string): void {
+    this.completeOption = this.fileConfigurationManager.getCompleteOptions(languageId)
+  }
+
+  /**
+   * Get completionItems
+   *
+   * @public
+   * @param {TextDocument} document
+   * @param {Position} position
+   * @param {CancellationToken} token
+   * @param {string} triggerCharacter
+   * @returns {Promise<CompletionItem[]>}
+   */
+  public async provideCompletionItems(
+    document: TextDocument,
+    position: Position,
+    token: CancellationToken,
+    context: CompletionContext,
+  ): Promise<CompletionItem[]> {
+    if (this.typingsStatus.isAcquiringTypings) {
+      workspace.showMessage('Acquiring typings...', 'warning')
+      return []
+    }
+    let { uri } = document
+    const file = this.client.toPath(document.uri)
+    if (!file) return []
+    let preText = document.getText({
+      start: { line: position.line, character: 0 },
+      end: position
+    })
+    let { triggerCharacter } = context
+
+    if (!this.shouldTrigger(triggerCharacter, preText)) {
+      return []
+    }
+
+    const { completeOption } = this
+
+    const args: Proto.CompletionsRequestArgs = {
+      ...typeConverters.Position.toFileLocationRequestArgs(file, position),
+      includeExternalModuleExports: completeOption.autoImportSuggestions,
+      includeInsertTextCompletions: true,
+      triggerCharacter: triggerCharacter && triggerCharacter === '.' ? triggerCharacter : undefined
+    }
+
+    let msg: Proto.CompletionEntry[] | undefined
+    try {
+      const response = await this.client.execute('completions', args, token)
+      msg = response.body
+      if (!msg) {
+        return []
+      }
+    } catch {
+      return []
+    }
+
+    const completionItems: CompletionItem[] = []
+    for (const element of msg) {
+      let { kind } = element
+      if (kind === PConst.Kind.warning
+        || kind === PConst.Kind.script) {
+        if (!completeOption.nameSuggestions || triggerCharacter == '.') {
+          continue
+        }
+      }
+      if (!completeOption.autoImportSuggestions && element.hasAction) {
+        continue
+      }
+      const item = convertCompletionEntry(
+        element,
+        uri,
+        position,
+        completeOption.useCodeSnippetsOnMethodSuggest,
+      )
+      completionItems.push(item)
+    }
+
+    return completionItems
+  }
+
+  /**
+   * Resolve complete item, could have documentation added
+   *
+   * @public
+   * @param {CompletionItem} item
+   * @param {CancellationToken} token
+   * @returns {Promise<CompletionItem>}
+   */
+  public async resolveCompletionItem(
+    item: CompletionItem,
+    token: CancellationToken
+  ): Promise<CompletionItem> {
+    if (item == null) return undefined
+
+    let { uri, position, source } = item.data
+    const filepath = this.client.toPath(uri)
+    if (!filepath) return undefined
+    let document = workspace.getDocument(uri)
+    if (!document) return undefined
+    resolveItem(item, document)
+    const args: Proto.CompletionDetailsRequestArgs = {
+      ...typeConverters.Position.toFileLocationRequestArgs(
+        filepath,
+        position
+      ),
+      entryNames: [
+        source
+          ? { name: item.label, source }
+          : item.label
+      ]
+    }
+
+    let response: Proto.CompletionDetailsResponse
+    try {
+      response = await this.client.execute(
+        'completionEntryDetails',
+        args,
+        token
+      )
+    } catch {
+      return item
+    }
+
+    const details = response.body
+    if (!details || !details.length || !details[0]) {
+      return item
+    }
+    const detail = details[0]
+    item.detail = detail.displayParts.length
+      ? Previewer.plain(detail.displayParts)
+      : undefined
+
+    item.documentation = this.getDocumentation(detail)
+    const { command, additionalTextEdits } = this.getCodeActions(detail, filepath)
+    if (command) item.command = command
+    item.additionalTextEdits = additionalTextEdits
+    if (detail && item.insertTextFormat == InsertTextFormat.Snippet) {
+      this.createSnippetOfFunctionCall(item, detail)
+    }
+
+    return item
+  }
+
+  private getCodeActions(
+    detail: Proto.CompletionEntryDetails,
+    filepath: string
+  ): { command?: Command; additionalTextEdits?: TextEdit[] } {
+    if (!detail.codeActions || !detail.codeActions.length) {
+      return {}
+    }
+    let { commaAfterImport } = this.completeOption
+    // Try to extract out the additionalTextEdits for the current file.
+    // Also check if we still have to apply other workspace edits
+    const additionalTextEdits: TextEdit[] = []
+    let hasReaminingCommandsOrEdits = false
+    for (const tsAction of detail.codeActions) {
+      if (tsAction.commands) {
+        hasReaminingCommandsOrEdits = true
+      }
+      // Convert all edits in the current file using `additionalTextEdits`
+      if (tsAction.changes) {
+        for (const change of tsAction.changes) {
+          if (change.fileName === filepath) {
+            additionalTextEdits.push(
+              ...change.textChanges.map(typeConverters.TextEdit.fromCodeEdit)
+            )
+          } else {
+            hasReaminingCommandsOrEdits = true
+          }
+        }
+      }
+    }
+
+    let command = null
+
+    if (hasReaminingCommandsOrEdits) {
+      // Create command that applies all edits not in the current file.
+      command = {
+        title: '',
+        command: ApplyCompletionCodeActionCommand.ID,
+        arguments: [
+          detail.codeActions.map((x): Proto.CodeAction => ({
+            commands: x.commands,
+            description: x.description,
+            changes: x.changes.filter(x => x.fileName !== filepath)
+          }))
+        ]
+      }
+    }
+    if (additionalTextEdits.length && !commaAfterImport) {
+      // remove comma
+      additionalTextEdits.forEach(o => {
+        o.newText = o.newText.replace(/;/, '')
+      })
+    }
+    return {
+      command,
+      additionalTextEdits: additionalTextEdits.length
+        ? additionalTextEdits
+        : undefined
+    }
+  }
+
+  private shouldTrigger(
+    triggerCharacter: string,
+    pre: string,
+  ): boolean {
+    if (triggerCharacter === '.') {
+      if (pre.match(/[\s\.'"]\.$/)) {
+        return false
+      }
+    } else if (triggerCharacter === '@') {
+      // make sure we are in something that looks like the start of a jsdoc comment
+      if (!pre.match(/^\s*\*[ ]?@/) && !pre.match(/\/\*\*+[ ]?@/)) {
+        return false
+      }
+    } else if (triggerCharacter === '<') {
+      return this.client.apiVersion.gte(API.v290)
+    }
+
+    return true
+  }
+
+  // complete item documentation
+  private getDocumentation(detail: Proto.CompletionEntryDetails): MarkupContent | undefined {
+    let documentation = ''
+    if (detail.source) {
+      const importPath = `'${Previewer.plain(detail.source)}'`
+      const autoImportLabel = `Auto import from ${importPath}`
+      documentation += `${autoImportLabel}\n`
+    }
+    let parts = [
+      Previewer.plain(detail.documentation),
+      Previewer.tagsMarkdownPreview(detail.tags)
+    ]
+    parts = parts.filter(s => s && s.trim() != '')
+    documentation += parts.join('\n\n')
+    if (documentation.length) {
+      return {
+        kind: MarkupKind.Markdown,
+        value: documentation
+      }
+    }
+    return undefined
+  }
+
+  private createSnippetOfFunctionCall(
+    item: CompletionItem,
+    detail: Proto.CompletionEntryDetails
+  ): void {
+    let hasOptionalParameters = false
+    let hasAddedParameters = false
+
+    let snippet = ''
+    const methodName = detail.displayParts.find(
+      part => part.kind === 'methodName'
+    )
+    let { textEdit, data } = item
+    let { position, uri } = data
+
+    if (textEdit) {
+      snippet += item.insertText || textEdit.newText // tslint:disable-line
+    } else {
+      let document = workspace.getDocument(uri)
+      if (!document) return
+      let range = document.getWordRangeAtPosition(position)
+      textEdit = { range, newText: '' }
+      snippet += item.insertText || (methodName && methodName.text) || item.label // tslint:disable-line
+    }
+    snippet += '('
+    let holderIndex = 1
+    let parenCount = 0
+    let i = 0
+    for (; i < detail.displayParts.length; ++i) {
+      const part = detail.displayParts[i]
+      // Only take top level paren names
+      if (part.kind === 'parameterName' && parenCount === 1) {
+        const next = detail.displayParts[i + 1]
+        // Skip optional parameters
+        const nameIsFollowedByOptionalIndicator = next && next.text === '?'
+        if (!nameIsFollowedByOptionalIndicator) {
+          if (hasAddedParameters) snippet += ', '
+          hasAddedParameters = true
+          snippet += '${' + holderIndex + ':' + part.text + '}'
+          holderIndex = holderIndex + 1
+        }
+        hasOptionalParameters =
+          hasOptionalParameters || nameIsFollowedByOptionalIndicator
+      } else if (part.kind === 'punctuation') {
+        if (part.text === '(') {
+          ++parenCount
+        } else if (part.text === ')') {
+          --parenCount
+        } else if (part.text === '...' && parenCount === 1) {
+          // Found rest parmeter. Do not fill in any further arguments
+          hasOptionalParameters = true
+          break
+        }
+      }
+    }
+    if (hasOptionalParameters) {
+      snippet += '${' + holderIndex + '}'
+    }
+    snippet += ')'
+    snippet += '$0'
+    textEdit.newText = snippet
+    item.textEdit = textEdit
+  }
+}
diff --git a/src/server/features/definitionProvider.ts b/src/server/features/definitionProvider.ts
new file mode 100644
index 0000000..63a5e18
--- /dev/null
+++ b/src/server/features/definitionProvider.ts
@@ -0,0 +1,64 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { CancellationToken, Definition, Location, Position, TextDocument } from 'vscode-languageserver-protocol'
+import { DefinitionProvider, ImplementationProvider, TypeDefinitionProvider } from 'coc.nvim/lib/provider'
+import * as Proto from '../protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import * as typeConverters from '../utils/typeConverters'
+
+export default class TypeScriptDefinitionProvider implements DefinitionProvider, TypeDefinitionProvider, ImplementationProvider {
+  constructor(private client: ITypeScriptServiceClient) { }
+
+  protected async getSymbolLocations(
+    definitionType: 'definition' | 'implementation' | 'typeDefinition',
+    document: TextDocument,
+    position: Position,
+    token: CancellationToken | boolean
+  ): Promise<Location[] | undefined> {
+    const filepath = this.client.toPath(document.uri)
+    if (!filepath) {
+      return undefined
+    }
+
+    const args = typeConverters.Position.toFileLocationRequestArgs(
+      filepath,
+      position
+    )
+    try {
+      const response = await this.client.execute(definitionType, args, token)
+      const locations: Proto.FileSpan[] = (response && response.body) || []
+      return locations.map(location =>
+        typeConverters.Location.fromTextSpan(
+          this.client.toResource(location.file),
+          location
+        )
+      )
+    } catch {
+      return []
+    }
+  }
+
+  public provideDefinition(
+    document: TextDocument,
+    position: Position,
+    token: CancellationToken | boolean
+  ): Promise<Definition | undefined> {
+    return this.getSymbolLocations('definition', document, position, token)
+  }
+
+  public provideTypeDefinition(
+    document: TextDocument,
+    position: Position,
+    token: CancellationToken): Promise<Definition> {
+    return this.getSymbolLocations('typeDefinition', document, position, token)
+  }
+
+  public provideImplementation(
+    document: TextDocument,
+    position: Position,
+    token: CancellationToken): Promise<Definition> {
+    return this.getSymbolLocations('implementation', document, position, token)
+  }
+}
diff --git a/src/server/features/diagnostics.ts b/src/server/features/diagnostics.ts
new file mode 100644
index 0000000..a956177
--- /dev/null
+++ b/src/server/features/diagnostics.ts
@@ -0,0 +1,161 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { Diagnostic } from 'vscode-languageserver-protocol'
+import { languages, DiagnosticCollection } from 'coc.nvim'
+import { ResourceMap } from './resourceMap'
+
+export class DiagnosticSet {
+  private _map = new ResourceMap<Diagnostic[]>()
+
+  public set(uri: string, diagnostics: Diagnostic[]): void {
+    this._map.set(uri, diagnostics)
+  }
+
+  public get(uri: string): Diagnostic[] {
+    return this._map.get(uri) || []
+  }
+
+  public clear(): void {
+    this._map = new ResourceMap<Diagnostic[]>()
+  }
+}
+
+export enum DiagnosticKind {
+  Syntax,
+  Semantic,
+  Suggestion
+}
+
+const allDiagnosticKinds = [
+  DiagnosticKind.Syntax,
+  DiagnosticKind.Semantic,
+  DiagnosticKind.Suggestion
+]
+
+export class DiagnosticsManager {
+  private readonly _diagnostics = new Map<DiagnosticKind, DiagnosticSet>()
+  private readonly _currentDiagnostics: DiagnosticCollection
+  private _pendingUpdates = new ResourceMap<any>()
+  private _validate = true
+  private _enableSuggestions = true
+
+  private readonly updateDelay = 200
+
+  constructor() {
+    for (const kind of allDiagnosticKinds) {
+      this._diagnostics.set(kind, new DiagnosticSet())
+    }
+    this._currentDiagnostics = languages.createDiagnosticCollection('tsserver')
+  }
+
+  public dispose(): void {
+    this._currentDiagnostics.dispose()
+    for (const value of this._pendingUpdates.values) {
+      clearTimeout(value)
+    }
+    this._pendingUpdates = new ResourceMap<any>()
+  }
+
+  public reInitialize(): void {
+    this._currentDiagnostics.clear()
+    for (const diagnosticSet of this._diagnostics.values()) {
+      diagnosticSet.clear()
+    }
+  }
+
+  public set validate(value: boolean) {
+    if (this._validate === value) {
+      return
+    }
+
+    this._validate = value
+    if (!value) {
+      this._currentDiagnostics.clear()
+    }
+  }
+
+  public set enableSuggestions(value: boolean) {
+    if (this._enableSuggestions === value) {
+      return
+    }
+
+    this._enableSuggestions = value
+    if (!value) {
+      this._currentDiagnostics.clear()
+    }
+  }
+
+  public diagnosticsReceived(
+    kind: DiagnosticKind,
+    uri: string,
+    diagnostics: Diagnostic[]
+  ): void {
+    const collection = this._diagnostics.get(kind)
+    if (!collection) {
+      return
+    }
+
+    if (diagnostics.length === 0) {
+      const existing = collection.get(uri)
+      if (existing.length === 0) {
+        // No need to update
+        return
+      }
+    }
+
+    collection.set(uri, diagnostics)
+
+    this.scheduleDiagnosticsUpdate(uri)
+  }
+
+  public delete(uri: string): void {
+    this._currentDiagnostics.delete(uri)
+  }
+
+  public getDiagnostics(uri: string): Diagnostic[] {
+    return this._currentDiagnostics.get(uri) || []
+    return []
+  }
+
+  private scheduleDiagnosticsUpdate(uri: string): void {
+    if (!this._pendingUpdates.has(uri)) {
+      this._pendingUpdates.set(
+        uri,
+        setTimeout(() => this.updateCurrentDiagnostics(uri), this.updateDelay)
+      )
+    }
+  }
+
+  private updateCurrentDiagnostics(uri: string): void {
+    if (this._pendingUpdates.has(uri)) {
+      clearTimeout(this._pendingUpdates.get(uri))
+      this._pendingUpdates.delete(uri)
+    }
+
+    if (!this._validate) {
+      return
+    }
+
+    const allDiagnostics = [
+      ...this._diagnostics.get(DiagnosticKind.Syntax)!.get(uri),
+      ...this._diagnostics.get(DiagnosticKind.Semantic)!.get(uri),
+      ...this.getSuggestionDiagnostics(uri)
+    ]
+    this._currentDiagnostics.set(uri, allDiagnostics)
+  }
+
+  private getSuggestionDiagnostics(uri: string): Diagnostic[] {
+    return this._diagnostics
+      .get(DiagnosticKind.Suggestion)!
+      .get(uri)
+      .filter(x => {
+        if (!this._enableSuggestions) {
+          // Still show unused
+          return x.code && x.code == 6133
+        }
+        return true
+      })
+  }
+}
diff --git a/src/server/features/directiveCommentCompletions.ts b/src/server/features/directiveCommentCompletions.ts
new file mode 100644
index 0000000..b04ea76
--- /dev/null
+++ b/src/server/features/directiveCommentCompletions.ts
@@ -0,0 +1,76 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { CancellationToken, CompletionContext, CompletionItem, CompletionItemKind, CompletionList, Position, Range, TextDocument } from 'vscode-languageserver-protocol'
+import { workspace } from 'coc.nvim'
+import { ITypeScriptServiceClient } from '../typescriptService'
+
+interface Directive {
+  readonly value: string
+  readonly description: string
+}
+
+const directives: Directive[] = [
+  {
+    value: '@ts-check',
+    description: 'Enables semantic checking in a JavaScript file. Must be at the top of a file.'
+  },
+  {
+    value: '@ts-nocheck',
+    description: 'Disables semantic checking in a JavaScript file. Must be at the top of a file.'
+  },
+  {
+    value: '@ts-ignore',
+    description: 'Suppresses @ts-check errors on the next line of a file.'
+  }
+]
+
+export default class DirectiveCommentCompletionProvider {
+  constructor(private readonly client: ITypeScriptServiceClient) { }
+
+  public provideCompletionItems(
+    document: TextDocument,
+    position: Position,
+    _token: CancellationToken,
+    context: CompletionContext
+  ): CompletionItem[] | CompletionList {
+    if (context.triggerCharacter != '@') {
+      return []
+    }
+    const file = this.client.toPath(document.uri)
+    if (!file) {
+      return []
+    }
+    const doc = workspace.getDocument(document.uri)
+
+    const line = doc.getline(position.line)
+    const prefix = line.slice(0, position.character)
+    const match = prefix.match(/^\s*\/\/+\s?(@[a-zA-Z\-]*)?$/)
+    if (match) {
+      let items = directives.map(directive => {
+        const item = CompletionItem.create(directive.value)
+        item.kind = CompletionItemKind.Snippet
+        item.detail = directive.description
+        item.textEdit = {
+          range: Range.create(
+            position.line,
+            Math.max(0, position.character - (match[1] ? match[1].length : 0)),
+            position.line,
+            position.character
+          ),
+          newText: directive.value
+        }
+        return item
+      })
+      let res: any = {
+        isIncomplete: false,
+        items
+      }
+      res.startcol = doc.fixStartcol(position, ['@'])
+      return res as any
+    }
+    return []
+  }
+}
diff --git a/src/server/features/documentHighlight.ts b/src/server/features/documentHighlight.ts
new file mode 100644
index 0000000..5a9d04e
--- /dev/null
+++ b/src/server/features/documentHighlight.ts
@@ -0,0 +1,48 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { CancellationToken, DocumentHighlight, DocumentHighlightKind, Position, TextDocument } from 'vscode-languageserver-protocol'
+import { DocumentHighlightProvider } from 'coc.nvim/lib/provider'
+import Proto from '../protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import * as typeConverters from '../utils/typeConverters'
+
+export default class TypeScriptDocumentHighlightProvider implements DocumentHighlightProvider {
+  public constructor(private readonly client: ITypeScriptServiceClient) { }
+
+  public async provideDocumentHighlights(
+    resource: TextDocument,
+    position: Position,
+    token: CancellationToken
+  ): Promise<DocumentHighlight[]> {
+    const file = this.client.toPath(resource.uri)
+    if (!file) return []
+
+    const args = typeConverters.Position.toFileLocationRequestArgs(
+      file,
+      position
+    )
+    try {
+      const response = await this.client.execute('occurrences', args, token)
+      if (response && response.body) {
+        return response.body
+          .filter(x => !x.isInString)
+          .map(documentHighlightFromOccurance)
+      }
+    } catch {
+      // noop
+    }
+
+    return []
+  }
+}
+
+function documentHighlightFromOccurance(
+  occurrence: Proto.OccurrencesResponseItem // tslint:disable-line
+): DocumentHighlight {
+  return {
+    range: typeConverters.Range.fromTextSpan(occurrence),
+    kind: occurrence.isWriteAccess ? DocumentHighlightKind.Write : DocumentHighlightKind.Read
+  }
+}
diff --git a/src/server/features/documentSymbol.ts b/src/server/features/documentSymbol.ts
new file mode 100644
index 0000000..0d2078f
--- /dev/null
+++ b/src/server/features/documentSymbol.ts
@@ -0,0 +1,143 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { CancellationToken, DocumentSymbol, Range, SymbolKind, TextDocument } from 'vscode-languageserver-protocol'
+import { DocumentSymbolProvider } from 'coc.nvim/lib/provider'
+import * as Proto from '../protocol'
+import * as PConst from '../protocol.const'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import * as typeConverters from '../utils/typeConverters'
+
+const getSymbolKind = (kind: string): SymbolKind => {
+  switch (kind) {
+    case PConst.Kind.module:
+      return SymbolKind.Module
+    case PConst.Kind.class:
+      return SymbolKind.Class
+    case PConst.Kind.enum:
+      return SymbolKind.Enum
+    case PConst.Kind.interface:
+      return SymbolKind.Interface
+    case PConst.Kind.memberFunction:
+      return SymbolKind.Method
+    case PConst.Kind.memberVariable:
+      return SymbolKind.Property
+    case PConst.Kind.memberGetAccessor:
+      return SymbolKind.Property
+    case PConst.Kind.memberSetAccessor:
+      return SymbolKind.Property
+    case PConst.Kind.variable:
+      return SymbolKind.Variable
+    case PConst.Kind.const:
+      return SymbolKind.Variable
+    case PConst.Kind.localVariable:
+      return SymbolKind.Variable
+    case PConst.Kind.variable:
+      return SymbolKind.Variable
+    case PConst.Kind.constructSignature:
+    case PConst.Kind.constructorImplementation:
+    case PConst.Kind.function:
+    case PConst.Kind.localFunction:
+      return SymbolKind.Function
+  }
+  return SymbolKind.Variable
+}
+
+export default class TypeScriptDocumentSymbolProvider implements DocumentSymbolProvider {
+  public constructor(private readonly client: ITypeScriptServiceClient) { }
+
+  public async provideDocumentSymbols(
+    resource: TextDocument,
+    token: CancellationToken
+  ): Promise<DocumentSymbol[]> {
+    const filepath = this.client.toPath(resource.uri)
+    if (!filepath) return []
+
+    const args: Proto.FileRequestArgs = {
+      file: filepath
+    }
+
+    try {
+      const response = await this.client.execute('navtree', args, token)
+      if (response.body) {
+        // The root represents the file. Ignore this when showing in the UI
+        const tree = response.body
+        if (tree.childItems) {
+          const result = new Array<DocumentSymbol>()
+          tree.childItems.forEach(item =>
+            TypeScriptDocumentSymbolProvider.convertNavTree(
+              result,
+              item
+            )
+          )
+          return result
+        }
+      }
+      return []
+    } catch (e) {
+      return []
+    }
+  }
+
+  private static convertNavTree(
+    bucket: DocumentSymbol[],
+    item: Proto.NavigationTree,
+  ): boolean {
+    let shouldInclude = TypeScriptDocumentSymbolProvider.shouldInclueEntry(item)
+    const children = new Set(item.childItems || [])
+    for (const span of item.spans) {
+      const range = typeConverters.Range.fromTextSpan(span)
+      const symbolInfo = DocumentSymbol.create(
+        item.text,
+        '',
+        getSymbolKind(item.kind),
+        range,
+        range)
+      symbolInfo.children = children.size > 0 ? [] : null
+
+      for (const child of children) {
+        if (child.spans.some(span => !!containsRange(range, typeConverters.Range.fromTextSpan(span)))) {
+          const includedChild = TypeScriptDocumentSymbolProvider.convertNavTree(symbolInfo.children, child)
+          shouldInclude = shouldInclude || includedChild
+          children.delete(child)
+        }
+      }
+
+      if (shouldInclude) {
+        bucket.push(symbolInfo)
+      }
+    }
+
+    return shouldInclude
+  }
+
+  private static shouldInclueEntry(
+    item: Proto.NavigationTree | Proto.NavigationBarItem
+  ): boolean {
+    if (item.kind === PConst.Kind.alias) {
+      return false
+    }
+    return !!(
+      item.text &&
+      item.text !== '<function>' &&
+      item.text !== '<class>'
+    )
+  }
+}
+
+function containsRange(range: Range, otherRange: Range): boolean {
+  if (otherRange.start.line < range.start.line || otherRange.end.line < range.start.line) {
+    return false
+  }
+  if (otherRange.start.line > range.end.line || otherRange.end.line > range.end.line) {
+    return false
+  }
+  if (otherRange.start.line === range.start.line && otherRange.start.character < range.start.character) {
+    return false
+  }
+  if (otherRange.end.line === range.end.line && otherRange.end.character > range.end.character) {
+    return false
+  }
+  return true
+}
diff --git a/src/server/features/fileConfigurationManager.ts b/src/server/features/fileConfigurationManager.ts
new file mode 100644
index 0000000..dce4a4d
--- /dev/null
+++ b/src/server/features/fileConfigurationManager.ts
@@ -0,0 +1,175 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { TextDocument } from 'vscode-languageserver-protocol'
+import { WorkspaceConfiguration, workspace } from 'coc.nvim'
+import Proto from '../protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import API from '../utils/api'
+import * as languageIds from '../utils/languageModeIds'
+
+function objAreEqual<T>(a: T, b: T): boolean {
+  let keys = Object.keys(a)
+  for (let i = 0; i < keys.length; i++) { // tslint:disable-line
+    let key = keys[i]
+    if ((a as any)[key] !== (b as any)[key]) {
+      return false
+    }
+  }
+  return true
+}
+
+interface FormatOptions {
+  tabSize: number
+  insertSpaces: boolean
+}
+
+interface FileConfiguration {
+  formatOptions: Proto.FormatCodeSettings
+  preferences: Proto.UserPreferences
+}
+
+export interface CompletionOptions {
+  readonly commaAfterImport: boolean
+  readonly useCodeSnippetsOnMethodSuggest: boolean
+  readonly nameSuggestions: boolean
+  readonly autoImportSuggestions: boolean
+}
+
+export default class FileConfigurationManager {
+  private cachedOption = null
+  private requesting = false
+
+  public constructor(private readonly client: ITypeScriptServiceClient) {
+  }
+
+  public async ensureConfigurationOptions(languageId: string, insertSpaces: boolean, tabSize: number): Promise<void> {
+    let { requesting } = this
+    let options: FormatOptions = {
+      tabSize,
+      insertSpaces
+    }
+    if (requesting || (this.cachedOption && objAreEqual(this.cachedOption, options))) return
+    const currentOptions = this.getFileOptions(options, languageId)
+    this.requesting = true
+    const args = {
+      hostInfo: 'nvim-coc',
+      ...currentOptions
+    } as Proto.ConfigureRequestArguments
+    await this.client.execute('configure', args)
+    this.cachedOption = options
+    this.requesting = false
+  }
+
+  public async ensureConfigurationForDocument(document: TextDocument): Promise<void> {
+    let opts = await workspace.getFormatOptions(document.uri)
+    return this.ensureConfigurationOptions(document.languageId, opts.insertSpaces, opts.tabSize)
+  }
+
+  public reset(): void {
+    this.cachedOption = null
+  }
+
+  public getLanguageConfiguration(languageId: string): WorkspaceConfiguration {
+    return workspace.getConfiguration(languageId)
+  }
+
+  public isTypeScriptDocument(languageId: string): boolean {
+    return languageId === languageIds.typescript || languageId === languageIds.typescriptreact ||
+      languageId === languageIds.typescripttsx || languageId === languageIds.typescriptjsx
+  }
+
+  public enableJavascript(): boolean {
+    const config = workspace.getConfiguration('tsserver')
+    return !!config.get<boolean>('enableJavascript')
+  }
+
+  private getFileOptions(options: FormatOptions, languageId: string): FileConfiguration {
+    const lang = this.isTypeScriptDocument(languageId) ? 'typescript' : 'javascript'
+    return {
+      formatOptions: this.getFormatOptions(options, lang),
+      preferences: this.getPreferences(lang)
+    }
+  }
+
+  private getFormatOptions(options: FormatOptions, language: string): Proto.FormatCodeSettings {
+    const config = workspace.getConfiguration(`${language}.format`)
+
+    return {
+      tabSize: options.tabSize,
+      indentSize: options.tabSize,
+      convertTabsToSpaces: options.insertSpaces,
+      // We can use \n here since the editor normalizes later on to its line endings.
+      newLineCharacter: '\n',
+      insertSpaceAfterCommaDelimiter: config.get<boolean>('insertSpaceAfterCommaDelimiter'),
+      insertSpaceAfterConstructor: config.get<boolean>('insertSpaceAfterConstructor'),
+      insertSpaceAfterSemicolonInForStatements: config.get<boolean>('insertSpaceAfterSemicolonInForStatements'),
+      insertSpaceBeforeAndAfterBinaryOperators: config.get<boolean>('insertSpaceBeforeAndAfterBinaryOperators'),
+      insertSpaceAfterKeywordsInControlFlowStatements: config.get<boolean>('insertSpaceAfterKeywordsInControlFlowStatements'),
+      insertSpaceAfterFunctionKeywordForAnonymousFunctions: config.get<boolean>('insertSpaceAfterFunctionKeywordForAnonymousFunctions'),
+      insertSpaceBeforeFunctionParenthesis: config.get<boolean>('insertSpaceBeforeFunctionParenthesis'),
+      insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: config.get<boolean>('insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis'),
+      insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: config.get<boolean>('insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets'),
+      insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: config.get<boolean>('insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces'),
+      insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: config.get<boolean>('insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces'),
+      insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: config.get<boolean>('insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces'),
+      insertSpaceAfterTypeAssertion: config.get<boolean>('insertSpaceAfterTypeAssertion'),
+      placeOpenBraceOnNewLineForFunctions: config.get<boolean>('placeOpenBraceOnNewLineForFunctions'),
+      placeOpenBraceOnNewLineForControlBlocks: config.get<boolean>('placeOpenBraceOnNewLineForControlBlocks')
+    }
+  }
+
+  public getCompleteOptions(languageId: string): CompletionOptions {
+    const lang = this.isTypeScriptDocument(languageId) ? 'typescript' : 'javascript'
+    const config = workspace.getConfiguration(`${lang}.preferences.completion`)
+    return {
+      useCodeSnippetsOnMethodSuggest: config.get<boolean>('useCodeSnippetsOnMethodSuggest', true),
+      commaAfterImport: config.get<boolean>('commaAfterImport', true),
+      nameSuggestions: config.get<boolean>('nameSuggestions', true),
+      autoImportSuggestions: config.get<boolean>('autoImportSuggestions', true)
+    }
+  }
+
+  public getPreferences(language: string): Proto.UserPreferences {
+    if (!this.client.apiVersion.gte(API.v290)) {
+      return {}
+    }
+    const config = workspace.getConfiguration(`${language}.preferences`)
+    return {
+      importModuleSpecifierPreference: getImportModuleSpecifier(config) as any,
+      disableSuggestions: !config.get<boolean>('suggestionActions.enabled', true),
+      quotePreference: getQuoteType(config),
+      includeCompletionsForModuleExports: config.get<boolean>('completion.moduleExports', true),
+      includeCompletionsWithInsertText: true,
+      allowTextChangesInNewFiles: false,
+    }
+  }
+}
+
+type ModuleImportType = 'relative' | 'non-relative' | 'auto'
+type QuoteType = 'single' | 'double'
+
+function getImportModuleSpecifier(config): ModuleImportType {
+  let val = config.get('importModuleSpecifier')
+  switch (val) {
+    case 'relative':
+      return 'relative'
+    case 'non-relative':
+      return 'non-relative'
+    default:
+      return 'auto'
+  }
+}
+
+function getQuoteType(config): QuoteType {
+  let val = config.get('quoteStyle')
+  switch (val) {
+    case 'single':
+      return 'single'
+    case 'double':
+      return 'double'
+    default:
+      return 'single'
+  }
+}
diff --git a/src/server/features/folding.ts b/src/server/features/folding.ts
new file mode 100644
index 0000000..ae73ba6
--- /dev/null
+++ b/src/server/features/folding.ts
@@ -0,0 +1,70 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { CancellationToken } from 'vscode-jsonrpc'
+import { FoldingRange, TextDocument } from 'vscode-languageserver-types'
+import { FoldingContext, FoldingRangeProvider } from 'coc.nvim/lib/provider'
+import { workspace } from 'coc.nvim'
+import Proto from '../protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import * as typeConverters from '../utils/typeConverters'
+
+export default class TypeScriptFoldingProvider implements FoldingRangeProvider {
+  public constructor(private readonly client: ITypeScriptServiceClient) { }
+
+  public async provideFoldingRanges(
+    document: TextDocument,
+    _context: FoldingContext,
+    token: CancellationToken
+  ): Promise<FoldingRange[] | undefined> {
+    const file = this.client.toPath(document.uri)
+    if (!file) {
+      return
+    }
+
+    const args: Proto.FileRequestArgs = { file }
+    const { body } = await this.client.execute('getOutliningSpans', args, token)
+    if (!body) {
+      return
+    }
+
+    return body
+      .map(span => this.convertOutliningSpan(span, document))
+      .filter(foldingRange => !!foldingRange) as FoldingRange[]
+  }
+
+  private convertOutliningSpan(
+    span: Proto.OutliningSpan,
+    document: TextDocument
+  ): FoldingRange | undefined {
+    const range = typeConverters.Range.fromTextSpan(span.textSpan)
+    const kind = TypeScriptFoldingProvider.getFoldingRangeKind(span)
+
+    // Workaround for #49904
+    if (span.kind === 'comment') {
+      let doc = workspace.getDocument(document.uri)
+      const line = doc.getline(range.start.line)
+      if (line.match(/\/\/\s*#endregion/gi)) {
+        return undefined
+      }
+    }
+    let { start, end } = range
+    return FoldingRange.create(start.line, end.line, start.character, end.character, kind)
+  }
+
+  private static getFoldingRangeKind(
+    span: Proto.OutliningSpan
+  ): string {
+    switch (span.kind) {
+      case 'comment':
+      case 'region':
+      case 'imports':
+      case 'code':
+        return span.kind
+      default:
+        return undefined
+    }
+  }
+}
diff --git a/src/server/features/formatting.ts b/src/server/features/formatting.ts
new file mode 100644
index 0000000..48bd388
--- /dev/null
+++ b/src/server/features/formatting.ts
@@ -0,0 +1,158 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { CancellationToken, FormattingOptions, Position, Range, TextDocument, TextEdit } from 'vscode-languageserver-protocol'
+import { commands, workspace } from 'coc.nvim'
+import { DocumentFormattingEditProvider, DocumentRangeFormattingEditProvider } from 'coc.nvim/lib/provider'
+import * as Proto from '../protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import { languageIds } from '../utils/languageModeIds'
+import * as typeConverters from '../utils/typeConverters'
+import FileConfigurationManager from './fileConfigurationManager'
+
+export default class TypeScriptFormattingProvider
+  implements
+  DocumentRangeFormattingEditProvider,
+  DocumentFormattingEditProvider {
+  public constructor(
+    private readonly client: ITypeScriptServiceClient,
+    private readonly formattingOptionsManager: FileConfigurationManager
+  ) {
+    commands.register({
+      id: 'tsserver.format',
+      execute: async (): Promise<void> => {
+        let document = await workspace.document
+        if (!document) return
+        if (languageIds.indexOf(document.filetype) == -1) {
+          return
+        }
+        let options = await workspace.getFormatOptions()
+        let edit = await this.provideDocumentFormattingEdits(
+          document.textDocument,
+          options
+        )
+        if (!edit) return
+        await document.applyEdits(workspace.nvim, edit)
+      }
+    })
+  }
+
+  private async doFormat(
+    document: TextDocument,
+    options: FormattingOptions,
+    args: Proto.FormatRequestArgs,
+    token?: CancellationToken
+  ): Promise<TextEdit[]> {
+    await this.formattingOptionsManager.ensureConfigurationOptions(
+      document.languageId,
+      options.insertSpaces,
+      options.tabSize
+    )
+    try {
+      const response = await this.client.execute('format', args, token)
+      if (response.body) {
+        return response.body.map(typeConverters.TextEdit.fromCodeEdit)
+      }
+    } catch {
+      // noop
+    }
+    return []
+  }
+
+  public async provideDocumentRangeFormattingEdits(
+    document: TextDocument,
+    range: Range,
+    options: FormattingOptions,
+    token: CancellationToken
+  ): Promise<TextEdit[]> {
+    const filepath = this.client.toPath(document.uri)
+    if (!filepath) return []
+    const args: Proto.FormatRequestArgs = {
+      file: filepath,
+      line: range.start.line + 1,
+      offset: range.start.character + 1,
+      endLine: range.end.line + 1,
+      endOffset: range.end.character + 1
+    }
+    return this.doFormat(document, options, args, token)
+  }
+
+  public async provideDocumentFormattingEdits(
+    document: TextDocument,
+    options: FormattingOptions,
+    token?: CancellationToken
+  ): Promise<TextEdit[]> {
+    const filepath = this.client.toPath(document.uri)
+    if (!filepath) return []
+    const args: Proto.FormatRequestArgs = {
+      file: filepath,
+      line: 1,
+      offset: 1,
+      endLine: document.lineCount + 1,
+      endOffset: 1
+    }
+    return this.doFormat(document, options, args, token)
+  }
+
+  public async provideOnTypeFormattingEdits(
+    document: TextDocument,
+    position: Position,
+    ch: string,
+    options: FormattingOptions,
+    token: CancellationToken
+  ): Promise<TextEdit[]> {
+    if (!this.client.configuration.formatOnType) {
+      return
+    }
+    const file = this.client.toPath(document.uri)
+    if (!file) {
+      return []
+    }
+
+    await this.formattingOptionsManager.ensureConfigurationOptions(
+      document.languageId,
+      options.insertSpaces,
+      options.tabSize
+    )
+    const doc = workspace.getDocument(document.uri)
+
+    const args: Proto.FormatOnKeyRequestArgs = {
+      ...typeConverters.Position.toFileLocationRequestArgs(file, position),
+      key: ch
+    }
+    try {
+      const { body } = await this.client.execute('formatonkey', args, token)
+      const edits = body
+      const result: TextEdit[] = []
+      if (!edits) {
+        return result
+      }
+      for (const edit of edits) {
+        const textEdit = typeConverters.TextEdit.fromCodeEdit(edit)
+        const range = textEdit.range
+        // Work around for https://github.com/Microsoft/TypeScript/issues/6700.
+        // Check if we have an edit at the beginning of the line which only removes white spaces and leaves
+        // an empty line. Drop those edits
+        if (
+          range.start.character === 0 &&
+          range.start.line === range.end.line &&
+          textEdit.newText === ''
+        ) {
+          const lText = doc.getline(range.start.line)
+          // If the edit leaves something on the line keep the edit (note that the end character is exclusive).
+          // Keep it also if it removes something else than whitespace
+          if (lText.trim().length > 0 || lText.length > range.end.character) {
+            result.push(textEdit)
+          }
+        } else {
+          result.push(textEdit)
+        }
+      }
+      return result
+    } catch {
+      // noop
+    }
+    return []
+  }
+}
diff --git a/src/server/features/hover.ts b/src/server/features/hover.ts
new file mode 100644
index 0000000..09f3ea1
--- /dev/null
+++ b/src/server/features/hover.ts
@@ -0,0 +1,54 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { CancellationToken, Hover, MarkedString, Position, TextDocument } from 'vscode-languageserver-protocol'
+import { HoverProvider } from 'coc.nvim/lib/provider'
+import * as Proto from '../protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import { tagsMarkdownPreview } from '../utils/previewer'
+import * as typeConverters from '../utils/typeConverters'
+
+export default class TypeScriptHoverProvider implements HoverProvider {
+  public constructor(private readonly client: ITypeScriptServiceClient) { }
+
+  public async provideHover(
+    document: TextDocument,
+    position: Position,
+    token: CancellationToken
+  ): Promise<Hover | undefined> {
+    const filepath = this.client.toPath(document.uri)
+    if (!filepath) {
+      return undefined
+    }
+    const args = typeConverters.Position.toFileLocationRequestArgs(
+      filepath,
+      position
+    )
+    try {
+      const response = await this.client.execute('quickinfo', args, token)
+      if (response && response.body) {
+        const data = response.body
+        return {
+          contents: TypeScriptHoverProvider.getContents(data),
+          range: typeConverters.Range.fromTextSpan(data)
+        }
+      }
+    } catch (e) {
+      // noop
+    }
+    return undefined
+  }
+
+  private static getContents(data: Proto.QuickInfoResponseBody): MarkedString[] { // tslint:disable-line
+    const parts = []
+
+    if (data.displayString) {
+      parts.push({ language: 'typescript', value: data.displayString })
+    }
+
+    const tags = tagsMarkdownPreview(data.tags)
+    parts.push(data.documentation + (tags ? '\n\n' + tags : ''))
+    return parts
+  }
+}
diff --git a/src/server/features/implementationsCodeLens.ts b/src/server/features/implementationsCodeLens.ts
new file mode 100644
index 0000000..56398ef
--- /dev/null
+++ b/src/server/features/implementationsCodeLens.ts
@@ -0,0 +1,100 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { CancellationToken, CodeLens, Command, Location, Range, TextDocument } from 'vscode-languageserver-protocol'
+import * as Proto from '../protocol'
+import * as PConst from '../protocol.const'
+import * as typeConverters from '../utils/typeConverters'
+import { TypeScriptBaseCodeLensProvider } from './baseCodeLensProvider'
+
+export default class TypeScriptImplementationsCodeLensProvider extends TypeScriptBaseCodeLensProvider {
+  public async resolveCodeLens(
+    codeLens: CodeLens,
+    token: CancellationToken
+  ): Promise<CodeLens> {
+    let { uri } = codeLens.data
+    let filepath = this.client.toPath(uri)
+
+    const args = typeConverters.Position.toFileLocationRequestArgs(
+      filepath,
+      codeLens.range.start
+    )
+    try {
+      const response = await this.client.execute('implementation', args, token)
+      if (response && response.body) {
+        const locations = response.body
+          .map(reference => {
+            return {
+              uri: this.client.toResource(reference.file),
+              range: {
+                start: typeConverters.Position.fromLocation(reference.start),
+                end: {
+                  line: reference.start.line,
+                  character: 0
+                }
+              }
+            }
+          })
+          // Exclude original from implementations
+          .filter(
+            location => !(
+              location.uri.toString() === uri &&
+              location.range.start.line === codeLens.range.start.line &&
+              location.range.start.character ===
+              codeLens.range.start.character
+            )
+          )
+
+        codeLens.command = this.getCommand(locations, codeLens)
+        return codeLens
+      }
+    } catch {
+      // noop
+    }
+
+    codeLens.command = {
+      title: 'Could not determine implementations',
+      command: ''
+    }
+    return codeLens
+  }
+
+  private getCommand(
+    locations: Location[],
+    codeLens: CodeLens,
+  ): Command | undefined {
+    let { uri } = codeLens.data
+    return {
+      title: this.getTitle(locations),
+      command: locations.length ? 'editor.action.showReferences' : '',
+      arguments: [uri, codeLens.range.start, locations]
+    }
+  }
+
+  private getTitle(locations: Location[]): string {
+    return locations.length === 1 ? '1 implementation' : `${locations.length} implementations`
+  }
+
+  protected extractSymbol(
+    document: TextDocument,
+    item: Proto.NavigationTree,
+    _parent: Proto.NavigationTree | null
+  ): Range | null {
+    switch (item.kind) {
+      case PConst.Kind.interface:
+        return super.getSymbolRange(document, item)
+
+      case PConst.Kind.class:
+      case PConst.Kind.memberFunction:
+      case PConst.Kind.memberVariable:
+      case PConst.Kind.memberGetAccessor:
+      case PConst.Kind.memberSetAccessor:
+        if (item.kindModifiers.match(/\babstract\b/g)) {
+          return super.getSymbolRange(document, item)
+        }
+        break
+    }
+    return null
+  }
+}
diff --git a/src/server/features/organizeImports.ts b/src/server/features/organizeImports.ts
new file mode 100644
index 0000000..0e4568d
--- /dev/null
+++ b/src/server/features/organizeImports.ts
@@ -0,0 +1,101 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { disposeAll, TextDocumentWillSaveEvent, workspace } from 'coc.nvim'
+import { Command, CommandManager } from 'coc.nvim/lib/commands'
+import { Disposable, TextDocument, TextEdit, WorkspaceEdit } from 'vscode-languageserver-protocol'
+import Proto from '../protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import { standardLanguageDescriptions } from '../utils/languageDescription'
+import * as typeconverts from '../utils/typeConverters'
+import FileConfigurationManager from './fileConfigurationManager'
+
+class OrganizeImportsCommand implements Command {
+  public readonly id: string = 'tsserver.organizeImports'
+
+  constructor(
+    private readonly client: ITypeScriptServiceClient,
+    private commaAfterImport: boolean,
+    private modeIds: string[]
+  ) {
+    workspace.onWillSaveUntil(this.onWillSaveUntil, this, 'tsserver-organizeImports')
+  }
+
+  private onWillSaveUntil(event: TextDocumentWillSaveEvent): void {
+    let config = workspace.getConfiguration('tsserver')
+    let format = config.get('orgnizeImportOnSave', false)
+    if (!format) return
+    let { document } = event
+    if (this.modeIds.indexOf(document.languageId) == -1) return
+    let willSaveWaitUntil = async (): Promise<TextEdit[]> => {
+      let edit = await this.getTextEdits(document)
+      if (!edit) return []
+      return edit.changes ? edit.changes[document.uri] : []
+    }
+    event.waitUntil(willSaveWaitUntil())
+  }
+
+  private async getTextEdits(document: TextDocument): Promise<WorkspaceEdit | null> {
+    let file = this.client.toPath(document.uri)
+    const args: Proto.OrganizeImportsRequestArgs = {
+      scope: {
+        type: 'file',
+        args: {
+          file
+        }
+      }
+    }
+    const response = await this.client.execute('organizeImports', args)
+    if (!response || !response.success) {
+      return
+    }
+
+    const edit = typeconverts.WorkspaceEdit.fromFileCodeEdits(
+      this.client,
+      response.body
+    )
+    if (!this.commaAfterImport) {
+      let { changes } = edit
+      if (changes) {
+        for (let c of Object.keys(changes)) {
+          for (let textEdit of changes[c]) {
+            textEdit.newText = textEdit.newText.replace(/;/g, '')
+          }
+        }
+      }
+    }
+    return edit
+  }
+
+  public async execute(): Promise<void> {
+    let document = await workspace.document
+    if (this.modeIds.indexOf(document.filetype) == -1) return
+    let edit = await this.getTextEdits(document.textDocument)
+    if (edit) await workspace.applyEdit(edit)
+    return
+  }
+}
+
+export default class OrganizeImports {
+  private disposables: Disposable[] = []
+  public constructor(
+    client: ITypeScriptServiceClient,
+    commandManager: CommandManager,
+    fileConfigurationManager: FileConfigurationManager,
+    languageId: string
+  ) {
+    let description = standardLanguageDescriptions.find(o => o.id == languageId)
+    let modeIds = description ? description.modeIds : []
+    let option = fileConfigurationManager.getCompleteOptions(languageId)
+    let cmd = new OrganizeImportsCommand(client, option.commaAfterImport, modeIds)
+    commandManager.register(cmd)
+    this.disposables.push(Disposable.create(() => {
+      commandManager.unregister(cmd.id)
+    }))
+  }
+
+  public dispose(): void {
+    disposeAll(this.disposables)
+  }
+}
diff --git a/src/server/features/projectError.ts b/src/server/features/projectError.ts
new file mode 100644
index 0000000..c62132b
--- /dev/null
+++ b/src/server/features/projectError.ts
@@ -0,0 +1,49 @@
+import { disposeAll, workspace } from 'coc.nvim'
+import { Command, CommandManager } from 'coc.nvim/lib/commands'
+import { Disposable } from 'vscode-languageserver-protocol'
+import * as Proto from '../protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import * as languageIds from '../utils/languageModeIds'
+
+class ProjectErrorCommand implements Command {
+  public readonly id: string = 'tsserver.project_error'
+
+  constructor(
+    private readonly client: ITypeScriptServiceClient
+  ) {
+  }
+
+  public async execute(): Promise<void> {
+    let document = await workspace.document
+    if (languageIds[document.filetype] == null) return
+    let file = this.client.toPath(document.uri)
+    const args: Proto.GeterrForProjectRequestArgs = {
+      file,
+      delay: 20
+    }
+    const response = await this.client.execute('geterrForProject', args)
+    if (!response || !response.success) {
+      return
+    }
+
+    return
+  }
+}
+
+export default class ProjectErrors {
+  private disposables: Disposable[] = []
+  public constructor(
+    client: ITypeScriptServiceClient,
+    commandManager: CommandManager
+  ) {
+    let cmd = new ProjectErrorCommand(client)
+    commandManager.register(cmd)
+    this.disposables.push(Disposable.create(() => {
+      commandManager.unregister(cmd.id)
+    }))
+  }
+
+  public dispose(): void {
+    disposeAll(this.disposables)
+  }
+}
diff --git a/src/server/features/quickfix.ts b/src/server/features/quickfix.ts
new file mode 100644
index 0000000..85b6185
--- /dev/null
+++ b/src/server/features/quickfix.ts
@@ -0,0 +1,284 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { commands, workspace } from 'coc.nvim'
+import { Command } from 'coc.nvim/lib/commands'
+import { CodeActionProvider } from 'coc.nvim/lib/provider'
+import { CancellationToken, CodeAction, CodeActionContext, CodeActionKind, Diagnostic, Range, TextDocument } from 'vscode-languageserver-protocol'
+import * as Proto from '../protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import API from '../utils/api'
+import { applyCodeActionCommands, getEditForCodeAction } from '../utils/codeAction'
+import * as typeConverters from '../utils/typeConverters'
+import BufferSyncSupport from './bufferSyncSupport'
+import { DiagnosticsManager } from './diagnostics'
+
+class ApplyCodeActionCommand implements Command {
+  public static readonly ID = '_typescript.applyCodeActionCommand'
+  public readonly id = ApplyCodeActionCommand.ID
+
+  constructor(
+    private readonly client: ITypeScriptServiceClient,
+  ) { }
+
+  public async execute(action: Proto.CodeFixAction): Promise<boolean> {
+    return applyCodeActionCommands(this.client, action)
+  }
+}
+
+class ApplyFixAllCodeAction implements Command {
+  public static readonly ID = '_typescript.applyFixAllCodeAction'
+  public readonly id = ApplyFixAllCodeAction.ID
+
+  constructor(
+    private readonly client: ITypeScriptServiceClient,
+  ) { }
+
+  public async execute(
+    file: string,
+    tsAction: Proto.CodeFixAction
+  ): Promise<void> {
+    if (!tsAction.fixId) {
+      return
+    }
+
+    const args: Proto.GetCombinedCodeFixRequestArgs = {
+      scope: {
+        type: 'file',
+        args: { file }
+      },
+      fixId: tsAction.fixId
+    }
+
+    try {
+      const { body } = await this.client.execute('getCombinedCodeFix', args)
+      if (!body) {
+        return
+      }
+
+      const edit = typeConverters.WorkspaceEdit.fromFileCodeEdits(
+        this.client,
+        body.changes
+      )
+      await workspace.applyEdit(edit)
+      const token = CancellationToken.None
+
+      const { commands } = body
+      if (commands && commands.length) {
+        for (const command of commands) {
+          await this.client.execute('applyCodeActionCommand', { command }, token)
+        }
+      }
+    } catch {
+      // noop
+    }
+  }
+}
+
+/**
+ * Unique set of diagnostics keyed on diagnostic range and error code.
+ */
+class DiagnosticsSet {
+  public static from(diagnostics: Diagnostic[]): DiagnosticsSet {
+    const values = new Map<string, Diagnostic>()
+    for (const diagnostic of diagnostics) {
+      values.set(DiagnosticsSet.key(diagnostic), diagnostic)
+    }
+    return new DiagnosticsSet(values)
+  }
+
+  private static key(diagnostic: Diagnostic): string {
+    const { start, end } = diagnostic.range
+    return `${diagnostic.code}-${start.line},${start.character}-${end.line},${end.character}`
+  }
+
+  private constructor(
+    private readonly _values: Map<string, Diagnostic>
+  ) { }
+
+  public get values(): Iterable<Diagnostic> {
+    return this._values.values()
+  }
+}
+
+class SupportedCodeActionProvider {
+  private _supportedCodeActions?: Thenable<Set<number>>
+
+  public constructor(private readonly client: ITypeScriptServiceClient) { }
+
+  public async getFixableDiagnosticsForContext(
+    context: CodeActionContext
+  ): Promise<Diagnostic[]> {
+    const supportedActions = await this.supportedCodeActions
+    const fixableDiagnostics = DiagnosticsSet.from(
+      context.diagnostics.filter(diagnostic =>
+        supportedActions.has(+diagnostic.code!)
+      )
+    )
+    return Array.from(fixableDiagnostics.values)
+  }
+
+  private get supportedCodeActions(): Promise<Set<number>> {
+    if (!this._supportedCodeActions) {
+      this._supportedCodeActions = this.client
+        .execute('getSupportedCodeFixes', null, undefined)
+        .then(response => response.body || [])
+        .then(codes => codes.map(code => +code).filter(code => !isNaN(code)))
+        .then(codes => new Set(codes))
+    }
+    return Promise.resolve(this._supportedCodeActions)
+  }
+}
+
+export default class TypeScriptQuickFixProvider implements CodeActionProvider {
+  private readonly supportedCodeActionProvider: SupportedCodeActionProvider
+
+  constructor(
+    private readonly client: ITypeScriptServiceClient,
+    private readonly diagnosticsManager: DiagnosticsManager,
+    private readonly bufferSyncSupport: BufferSyncSupport,
+  ) {
+    commands.register(
+      new ApplyCodeActionCommand(client)
+    )
+    commands.register(
+      new ApplyFixAllCodeAction(client)
+    )
+
+    this.supportedCodeActionProvider = new SupportedCodeActionProvider(client)
+  }
+
+  public async provideCodeActions(
+    document: TextDocument,
+    _range: Range,
+    context: CodeActionContext,
+    token: CancellationToken
+  ): Promise<CodeAction[]> {
+    if (!this.client.apiVersion.gte(API.v213)) {
+      return []
+    }
+
+    const file = this.client.toPath(document.uri)
+    if (!file) {
+      return []
+    }
+
+    const fixableDiagnostics = await this.supportedCodeActionProvider.getFixableDiagnosticsForContext(
+      context
+    )
+    if (!fixableDiagnostics.length) {
+      return []
+    }
+
+    if (this.bufferSyncSupport.hasPendingDiagnostics(document.uri)) {
+      return []
+    }
+
+    const results: CodeAction[] = []
+    for (const diagnostic of fixableDiagnostics) {
+      results.push(
+        ...(await this.getFixesForDiagnostic(document, file, diagnostic, token))
+      )
+    }
+    return results
+  }
+
+  private async getFixesForDiagnostic(
+    document: TextDocument,
+    file: string,
+    diagnostic: Diagnostic,
+    token: CancellationToken
+  ): Promise<Iterable<CodeAction>> {
+    const args: Proto.CodeFixRequestArgs = {
+      ...typeConverters.Range.toFileRangeRequestArgs(file, diagnostic.range),
+      errorCodes: [+diagnostic.code!]
+    }
+    const codeFixesResponse = await this.client.execute(
+      'getCodeFixes',
+      args,
+      token
+    )
+    if (codeFixesResponse.body) {
+      const results: CodeAction[] = []
+      for (const tsCodeFix of codeFixesResponse.body) {
+        results.push(
+          ...(await this.getAllFixesForTsCodeAction(
+            document,
+            file,
+            diagnostic,
+            tsCodeFix
+          ))
+        )
+      }
+      return results
+    }
+    return []
+  }
+
+  private async getAllFixesForTsCodeAction(
+    document: TextDocument,
+    file: string,
+    diagnostic: Diagnostic,
+    tsAction: Proto.CodeAction
+  ): Promise<Iterable<CodeAction>> {
+    const singleFix = this.getSingleFixForTsCodeAction(diagnostic, tsAction)
+    const fixAll = await this.getFixAllForTsCodeAction(
+      document,
+      file,
+      diagnostic,
+      tsAction as Proto.CodeFixAction
+    )
+    return fixAll ? [singleFix, fixAll] : [singleFix]
+  }
+
+  private getSingleFixForTsCodeAction(
+    diagnostic: Diagnostic,
+    tsAction: Proto.CodeAction
+  ): CodeAction {
+    const codeAction: CodeAction = {
+      title: tsAction.description,
+      kind: CodeActionKind.QuickFix
+    }
+    codeAction.edit = getEditForCodeAction(this.client, tsAction)
+    codeAction.diagnostics = [diagnostic]
+    if (tsAction.commands) {
+      codeAction.command = {
+        command: ApplyCodeActionCommand.ID,
+        arguments: [tsAction],
+        title: tsAction.description
+      }
+    }
+    return codeAction
+  }
+
+  private async getFixAllForTsCodeAction(
+    document: TextDocument,
+    file: string,
+    diagnostic: Diagnostic,
+    tsAction: Proto.CodeFixAction
+  ): Promise<CodeAction | undefined> {
+    if (!tsAction.fixId || !this.client.apiVersion.gte(API.v270)) {
+      return undefined
+    }
+
+    // Make sure there are multiple diagnostics of the same type in the file
+    if (!this.diagnosticsManager
+      .getDiagnostics(document.uri)
+      .some(x => x.code === diagnostic.code && x !== diagnostic)) {
+      return
+    }
+
+    const action: CodeAction = {
+      title: tsAction.fixAllDescription || 'Fix all in file',
+      kind: CodeActionKind.QuickFix
+    }
+    action.diagnostics = [diagnostic]
+    action.command = {
+      command: ApplyFixAllCodeAction.ID,
+      arguments: [file, tsAction],
+      title: ''
+    }
+    return action
+  }
+}
diff --git a/src/server/features/refactor.ts b/src/server/features/refactor.ts
new file mode 100644
index 0000000..b452315
--- /dev/null
+++ b/src/server/features/refactor.ts
@@ -0,0 +1,221 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { CancellationToken, CodeAction, CodeActionContext, CodeActionKind, Range, TextDocument, WorkspaceEdit } from 'vscode-languageserver-protocol'
+import { Command } from 'coc.nvim/lib/commands'
+import { CodeActionProvider, CodeActionProviderMetadata } from 'coc.nvim/lib/provider'
+import { workspace, commands } from 'coc.nvim'
+import Proto from '../protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import * as typeConverters from '../utils/typeConverters'
+import FormattingOptionsManager from './fileConfigurationManager'
+
+class ApplyRefactoringCommand implements Command {
+  public static readonly ID = '_typescript.applyRefactoring'
+  public readonly id = ApplyRefactoringCommand.ID
+
+  constructor(private readonly client: ITypeScriptServiceClient) { }
+
+  public async execute(
+    document: TextDocument,
+    file: string,
+    refactor: string,
+    action: string,
+    range: Range
+  ): Promise<boolean> {
+    const args: Proto.GetEditsForRefactorRequestArgs = {
+      ...typeConverters.Range.toFileRangeRequestArgs(file, range),
+      refactor,
+      action
+    }
+    const response = await this.client.execute('getEditsForRefactor', args)
+    const body = response && response.body
+    if (!body || !body.edits.length) {
+      return false
+    }
+
+    const workspaceEdit = await this.toWorkspaceEdit(body)
+    if (!(await workspace.applyEdit(workspaceEdit))) {
+      return false
+    }
+    const renameLocation = body.renameLocation
+    if (renameLocation) {
+      commands.executeCommand('editor.action.rename',
+        document.uri,
+        typeConverters.Position.fromLocation(renameLocation)
+      )
+    }
+    return true
+  }
+
+  private async toWorkspaceEdit(body: Proto.RefactorEditInfo): Promise<WorkspaceEdit> {
+    for (const edit of body.edits) {
+      await workspace.createFile(edit.fileName, { ignoreIfExists: true })
+    }
+    let workspaceEdit = typeConverters.WorkspaceEdit.fromFileCodeEdits(
+      this.client,
+      body.edits
+    )
+    return workspaceEdit
+  }
+}
+
+class SelectRefactorCommand implements Command {
+  public static readonly ID = '_typescript.selectRefactoring'
+  public readonly id = SelectRefactorCommand.ID
+
+  constructor(private readonly doRefactoring: ApplyRefactoringCommand) { }
+
+  public async execute(
+    document: TextDocument,
+    file: string,
+    info: Proto.ApplicableRefactorInfo,
+    range: Range
+  ): Promise<boolean> {
+    let { actions } = info
+    const idx = actions.length == 1 ? 0 : await workspace.showQuickpick(
+      actions.map(action => action.description || action.name)
+    )
+    if (idx == -1) return false
+    let label = info.actions[idx].name
+    if (!label) return false
+    return this.doRefactoring.execute(
+      document,
+      file,
+      info.name,
+      label,
+      range
+    )
+  }
+}
+
+export default class TypeScriptRefactorProvider implements CodeActionProvider {
+  private static readonly extractFunctionKind = CodeActionKind.RefactorExtract + '.function'
+  private static readonly extractConstantKind = CodeActionKind.RefactorExtract + '.constant'
+  private static readonly moveKind = CodeActionKind.Refactor + '.move'
+
+  constructor(
+    private readonly client: ITypeScriptServiceClient,
+    private readonly formattingOptionsManager: FormattingOptionsManager,
+  ) {
+    const doRefactoringCommand = commands.register(
+      new ApplyRefactoringCommand(this.client)
+    )
+    commands.register(new SelectRefactorCommand(doRefactoringCommand))
+  }
+
+  public static readonly metadata: CodeActionProviderMetadata = {
+    providedCodeActionKinds: [CodeActionKind.Refactor]
+  }
+
+  public async provideCodeActions(
+    document: TextDocument,
+    range: Range,
+    context: CodeActionContext,
+    token: CancellationToken
+  ): Promise<CodeAction[] | undefined> {
+    if (!this.shouldTrigger(context)) {
+      return undefined
+    }
+    const file = this.client.toPath(document.uri)
+    if (!file) return undefined
+    await this.formattingOptionsManager.ensureConfigurationForDocument(document)
+    const args: Proto.GetApplicableRefactorsRequestArgs = typeConverters.Range.toFileRangeRequestArgs(
+      file,
+      range
+    )
+    let response: Proto.GetApplicableRefactorsResponse
+    try {
+      response = await this.client.execute('getApplicableRefactors', args, token)
+      if (!response || !response.body) {
+        return undefined
+      }
+    } catch {
+      return undefined
+    }
+
+    return this.convertApplicableRefactors(
+      response.body,
+      document,
+      file,
+      range
+    )
+  }
+
+  private convertApplicableRefactors(
+    body: Proto.ApplicableRefactorInfo[],
+    document: TextDocument,
+    file: string,
+    rangeOrSelection: Range
+  ): CodeAction[] {
+    const actions: CodeAction[] = []
+    for (const info of body) {
+      if (!info.inlineable) {
+        const codeAction: CodeAction = {
+          title: info.description,
+          kind: CodeActionKind.Refactor
+        }
+        codeAction.command = {
+          title: info.description,
+          command: SelectRefactorCommand.ID,
+          arguments: [document, file, info, rangeOrSelection]
+        }
+        actions.push(codeAction)
+      } else {
+        for (const action of info.actions) {
+          actions.push(
+            this.refactorActionToCodeAction(
+              action,
+              document,
+              file,
+              info,
+              rangeOrSelection
+            )
+          )
+        }
+      }
+    }
+    return actions
+  }
+
+  private refactorActionToCodeAction(
+    action: Proto.RefactorActionInfo,
+    document: TextDocument,
+    file: string,
+    info: Proto.ApplicableRefactorInfo,
+    rangeOrSelection: Range
+  ): CodeAction {
+    const codeAction: CodeAction = {
+      title: action.description,
+      kind: TypeScriptRefactorProvider.getKind(action)
+    }
+    codeAction.command = {
+      title: action.description,
+      command: ApplyRefactoringCommand.ID,
+      arguments: [document, file, info.name, action.name, rangeOrSelection]
+    }
+    return codeAction
+  }
+
+  private shouldTrigger(context: CodeActionContext): boolean {
+    if (
+      context.only &&
+      context.only.indexOf(CodeActionKind.Refactor) == -1
+    ) {
+      return false
+    }
+    return true
+  }
+
+  private static getKind(refactor: Proto.RefactorActionInfo): string {
+    if (refactor.name.startsWith('function_')) {
+      return TypeScriptRefactorProvider.extractFunctionKind
+    } else if (refactor.name.startsWith('constant_')) {
+      return TypeScriptRefactorProvider.extractConstantKind
+    } else if (refactor.name.startsWith('Move')) {
+      return TypeScriptRefactorProvider.moveKind
+    }
+    return CodeActionKind.Refactor
+  }
+}
diff --git a/src/server/features/references.ts b/src/server/features/references.ts
new file mode 100644
index 0000000..40200c1
--- /dev/null
+++ b/src/server/features/references.ts
@@ -0,0 +1,46 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { CancellationToken, Location, Position, TextDocument } from 'vscode-languageserver-protocol'
+import { ReferenceContext, ReferenceProvider } from 'coc.nvim/lib/provider'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import * as typeConverters from '../utils/typeConverters'
+
+export default class TypeScriptReferences implements ReferenceProvider {
+  public constructor(private readonly client: ITypeScriptServiceClient) {
+  }
+
+  public async provideReferences(
+    document: TextDocument,
+    position: Position,
+    context: ReferenceContext,
+    token: CancellationToken
+  ): Promise<Location[]> {
+    const filepath = this.client.toPath(document.uri)
+    if (!filepath) return []
+
+    const args = typeConverters.Position.toFileLocationRequestArgs(
+      filepath,
+      position
+    )
+    try {
+      const msg = await this.client.execute('references', args, token)
+      if (!msg.body) {
+        return []
+      }
+      const result: Location[] = []
+      for (const ref of msg.body.refs) {
+        if (!context.includeDeclaration && ref.isDefinition) {
+          continue
+        }
+        const url = this.client.toResource(ref.file)
+        const location = typeConverters.Location.fromTextSpan(url, ref)
+        result.push(location)
+      }
+      return result
+    } catch {
+      return []
+    }
+  }
+}
diff --git a/src/server/features/referencesCodeLens.ts b/src/server/features/referencesCodeLens.ts
new file mode 100644
index 0000000..e7549df
--- /dev/null
+++ b/src/server/features/referencesCodeLens.ts
@@ -0,0 +1,102 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { CancellationToken, CodeLens, Range, TextDocument } from 'vscode-languageserver-protocol'
+import * as Proto from '../protocol'
+import * as PConst from '../protocol.const'
+import * as typeConverters from '../utils/typeConverters'
+import { TypeScriptBaseCodeLensProvider } from './baseCodeLensProvider'
+
+export default class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLensProvider {
+  public resolveCodeLens(
+    codeLens: CodeLens,
+    token: CancellationToken
+  ): Promise<CodeLens> {
+    let { uri } = codeLens.data
+    let filepath = this.client.toPath(uri)
+    const args = typeConverters.Position.toFileLocationRequestArgs(
+      filepath,
+      codeLens.range.start
+    )
+    return this.client
+      .execute('references', args, token)
+      .then(response => {
+        if (!response || !response.body) {
+          throw codeLens
+        }
+
+        const locations = response.body.refs
+          .map(reference =>
+            typeConverters.Location.fromTextSpan(
+              this.client.toResource(reference.file),
+              reference
+            )
+          )
+          .filter(
+            location =>
+              // Exclude original definition from references
+              !(
+                location.uri.toString() === uri &&
+                location.range.start.line === codeLens.range.start.line &&
+                location.range.start.character ===
+                codeLens.range.start.character
+              )
+          )
+
+        codeLens.command = {
+          title: locations.length === 1 ? '1 reference' : `${locations.length} references`,
+          command: locations.length ? 'editor.action.showReferences' : '',
+          arguments: [uri, codeLens.range.start, locations]
+        }
+        return codeLens
+      })
+      .catch(() => {
+        codeLens.command = {
+          title: 'Could not determine references',
+          command: ''
+        }
+        return codeLens
+      })
+  }
+
+  protected extractSymbol(
+    document: TextDocument,
+    item: Proto.NavigationTree,
+    parent: Proto.NavigationTree | null
+  ): Range | null {
+    if (parent && parent.kind === PConst.Kind.enum) {
+      return super.getSymbolRange(document, item)
+    }
+
+    switch (item.kind) {
+      case PConst.Kind.const:
+      case PConst.Kind.let:
+      case PConst.Kind.variable:
+      case PConst.Kind.function:
+        // Only show references for exported variables
+        if (!item.kindModifiers.match(/\bexport\b/)) {
+          break
+        }
+      // fallthrough
+
+      case PConst.Kind.class:
+        if (item.text === '<class>') {
+          break
+        }
+      // fallthrough
+
+      case PConst.Kind.memberFunction:
+      case PConst.Kind.memberVariable:
+      case PConst.Kind.memberGetAccessor:
+      case PConst.Kind.memberSetAccessor:
+      case PConst.Kind.constructorImplementation:
+      case PConst.Kind.interface:
+      case PConst.Kind.type:
+      case PConst.Kind.enum:
+        return super.getSymbolRange(document, item)
+    }
+
+    return null
+  }
+}
diff --git a/src/server/features/rename.ts b/src/server/features/rename.ts
new file mode 100644
index 0000000..de51af2
--- /dev/null
+++ b/src/server/features/rename.ts
@@ -0,0 +1,70 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { CancellationToken, Position, TextDocument, TextEdit, WorkspaceEdit } from 'vscode-languageserver-protocol'
+import { RenameProvider } from 'coc.nvim/lib/provider'
+import * as Proto from '../protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import * as typeConverters from '../utils/typeConverters'
+
+export default class TypeScriptRenameProvider implements RenameProvider {
+  public constructor(private readonly client: ITypeScriptServiceClient) { }
+
+  public async provideRenameEdits(
+    document: TextDocument,
+    position: Position,
+    newName: string,
+    token: CancellationToken
+  ): Promise<WorkspaceEdit | null> {
+    const file = this.client.toPath(document.uri)
+    if (!file) {
+      return null
+    }
+
+    const args: Proto.RenameRequestArgs = {
+      ...typeConverters.Position.toFileLocationRequestArgs(file, position),
+      findInStrings: false,
+      findInComments: false
+    }
+
+    try {
+      const response = await this.client.execute('rename', args, token)
+      if (!response.body) {
+        return null
+      }
+
+      const renameInfo = response.body.info
+      if (!renameInfo.canRename) {
+        return Promise.reject<WorkspaceEdit>(
+          renameInfo.localizedErrorMessage
+        )
+      }
+
+      return this.toWorkspaceEdit(response.body.locs, newName)
+    } catch {
+      // noop
+    }
+    return null
+  }
+
+  private toWorkspaceEdit(
+    locations: ReadonlyArray<Proto.SpanGroup>,
+    newName: string
+  ): WorkspaceEdit {
+    let changes: { [uri: string]: TextEdit[] } = {}
+    for (const spanGroup of locations) {
+      const uri = this.client.toResource(spanGroup.file)
+      if (uri) {
+        changes[uri] = []
+        for (const textSpan of spanGroup.locs) {
+          changes[uri].push({
+            range: typeConverters.Range.fromTextSpan(textSpan),
+            newText: newName
+          })
+        }
+      }
+    }
+    return { changes }
+  }
+}
diff --git a/src/server/features/resourceMap.ts b/src/server/features/resourceMap.ts
new file mode 100644
index 0000000..ad71fb3
--- /dev/null
+++ b/src/server/features/resourceMap.ts
@@ -0,0 +1,81 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+/**
+ * Maps of file resources
+ *
+ * Attempts to handle correct mapping on both case sensitive and case in-sensitive
+ * file systems.
+ */
+export class ResourceMap<T> {
+  private readonly _map = new Map<string, T>()
+
+  constructor(
+    private readonly _normalizePath?: (resource: string) => string | null
+  ) { }
+
+  public has(resource: string): boolean {
+    const file = this.toKey(resource)
+    return !!file && this._map.has(file)
+  }
+
+  public get(resource: string): T | undefined {
+    const file = this.toKey(resource)
+    return file ? this._map.get(file) : undefined
+  }
+
+  public set(resource: string, value: T): void {
+    const file = this.toKey(resource)
+    if (file) {
+      this._map.set(file, value)
+    }
+  }
+
+  public delete(resource: string): void {
+    const file = this.toKey(resource)
+    if (file) {
+      this._map.delete(file)
+    }
+  }
+
+  public get values(): Iterable<T> {
+    return this._map.values()
+  }
+
+  public get keys(): Iterable<string> {
+    return this._map.keys()
+  }
+
+  private toKey(resource: string): string | null {
+    const key = this._normalizePath
+      ? this._normalizePath(resource)
+      : resource
+    if (!key) {
+      return key
+    }
+    return this.isCaseInsensitivePath(key) ? key.toLowerCase() : key
+  }
+
+  private isCaseInsensitivePath(path: string): boolean {
+    if (isWindowsPath(path)) {
+      return true
+    }
+    return path[0] === '/' && this.onIsCaseInsenitiveFileSystem
+  }
+
+  private get onIsCaseInsenitiveFileSystem(): boolean {
+    if (process.platform === 'win32') {
+      return true
+    }
+    if (process.platform === 'darwin') {
+      return true
+    }
+    return false
+  }
+}
+
+export function isWindowsPath(path: string): boolean {
+  return /^[a-zA-Z]:\\/.test(path)
+}
diff --git a/src/server/features/signatureHelp.ts b/src/server/features/signatureHelp.ts
new file mode 100644
index 0000000..2a31bf4
--- /dev/null
+++ b/src/server/features/signatureHelp.ts
@@ -0,0 +1,73 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { CancellationToken, Position, SignatureHelp, SignatureInformation, TextDocument } from 'vscode-languageserver-protocol'
+import { SignatureHelpProvider } from 'coc.nvim/lib/provider'
+import * as Proto from '../protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import * as Previewer from '../utils/previewer'
+import * as typeConverters from '../utils/typeConverters'
+
+export default class TypeScriptSignatureHelpProvider implements SignatureHelpProvider {
+  public static readonly triggerCharacters = ['(', ',', '<']
+
+  public constructor(private readonly client: ITypeScriptServiceClient) { }
+
+  public async provideSignatureHelp(
+    document: TextDocument,
+    position: Position,
+    token: CancellationToken
+  ): Promise<SignatureHelp | undefined> {
+    const filepath = this.client.toPath(document.uri)
+    if (!filepath) {
+      return undefined
+    }
+    const args: Proto.SignatureHelpRequestArgs = typeConverters.Position.toFileLocationRequestArgs(
+      filepath,
+      position
+    )
+
+    let info: Proto.SignatureHelpItems | undefined
+    try {
+      const response = await this.client.execute('signatureHelp', args, token)
+      info = response.body
+      if (!info) return undefined
+    } catch {
+      return undefined
+    }
+
+    const result: SignatureHelp = {
+      activeSignature: info.selectedItemIndex,
+      activeParameter: this.getActiveParmeter(info),
+      signatures: info.items.map(signature => {
+        return this.convertSignature(signature)
+      })
+    }
+    return result
+  }
+
+  private getActiveParmeter(info: Proto.SignatureHelpItems): number {
+    const activeSignature = info.items[info.selectedItemIndex]
+    if (activeSignature && activeSignature.isVariadic) {
+      return Math.min(info.argumentIndex, activeSignature.parameters.length - 1)
+    }
+    return info.argumentIndex
+  }
+
+  private convertSignature(item: Proto.SignatureHelpItem): SignatureInformation {
+    return {
+      label: Previewer.plain(item.prefixDisplayParts).replace(/\($/, ''),
+      documentation: Previewer.markdownDocumentation(
+        item.documentation,
+        item.tags.filter(x => x.name !== 'param')
+      ),
+      parameters: item.parameters.map(p => {
+        return {
+          label: Previewer.plain(p.displayParts),
+          documentation: Previewer.markdownDocumentation(p.documentation, [])
+        }
+      })
+    }
+  }
+}
diff --git a/src/server/features/tagCompletion.ts b/src/server/features/tagCompletion.ts
new file mode 100644
index 0000000..15eb50c
--- /dev/null
+++ b/src/server/features/tagCompletion.ts
@@ -0,0 +1,53 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { CancellationToken, CompletionContext, CompletionItem, Position, TextDocument } from 'vscode-languageserver-protocol'
+import { CompletionItemProvider } from 'coc.nvim/lib/provider'
+import * as Proto from '../protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import * as typeConverters from '../utils/typeConverters'
+
+export default class TypeScriptTagCompletion implements CompletionItemProvider {
+  constructor(
+    private readonly client: ITypeScriptServiceClient
+  ) { }
+
+  public async provideCompletionItems(
+    document: TextDocument,
+    position: Position,
+    token: CancellationToken,
+    context: CompletionContext
+  ): Promise<CompletionItem[] | undefined> {
+    const filepath = this.client.toPath(document.uri)
+    if (!filepath) return undefined
+    if (context.triggerCharacter != '>') {
+      return undefined
+    }
+
+    const args: Proto.JsxClosingTagRequestArgs = typeConverters.Position.toFileLocationRequestArgs(filepath, position)
+    let body: Proto.TextInsertion | undefined
+    try {
+      const response = await this.client.execute('jsxClosingTag', args, token)
+      body = response && response.body
+      if (!body) {
+        return undefined
+      }
+    } catch {
+      return undefined
+    }
+
+    return [this.getCompletion(body)]
+  }
+
+  private getCompletion(body: Proto.TextInsertion): CompletionItem {
+    const completion = CompletionItem.create(body.newText)
+    completion.insertText = this.getTagSnippet(body) // tslint:disable-line
+    return completion
+  }
+
+  private getTagSnippet(closingTag: Proto.TextInsertion): string {
+    let { newText, caretOffset } = closingTag
+    return newText.slice(0, caretOffset) + '$0' + newText.slice(caretOffset)
+  }
+}
diff --git a/src/server/features/updatePathOnRename.ts b/src/server/features/updatePathOnRename.ts
new file mode 100644
index 0000000..fff5ff8
--- /dev/null
+++ b/src/server/features/updatePathOnRename.ts
@@ -0,0 +1,105 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { Disposable, TextDocument, WorkspaceEdit } from 'vscode-languageserver-protocol'
+import Uri from 'vscode-uri'
+import { disposeAll, workspace } from 'coc.nvim'
+import * as Proto from '../protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import * as typeConverters from '../utils/typeConverters'
+import FileConfigurationManager from './fileConfigurationManager'
+
+function wait(ms: number): Promise<any> {
+  return new Promise(resolve => {
+    setTimeout(() => {
+      resolve()
+    }, ms)
+  })
+}
+
+export default class UpdateImportsOnFileRenameHandler {
+  private disposables: Disposable[] = []
+
+  public constructor(
+    private readonly client: ITypeScriptServiceClient,
+    private readonly fileConfigurationManager: FileConfigurationManager,
+    languageId: string
+  ) {
+    let glob = languageId == 'typescript' ? '**/*.ts' : '**/*.js'
+    const watcher = workspace.createFileSystemWatcher(glob)
+    this.disposables.push(watcher)
+    watcher.onDidRename(e => {
+      this.doRename(e.oldUri, e.newUri).catch(e => {
+        client.logger.error(e.message)
+      })
+    }, null, this.disposables)
+  }
+
+  public dispose(): void {
+    disposeAll(this.disposables)
+  }
+
+  private async doRename(
+    oldResource: Uri,
+    newResource: Uri
+  ): Promise<void> {
+    if (oldResource.scheme !== 'file' || newResource.scheme !== 'file') {
+      return
+    }
+    const targetFile = newResource.fsPath
+    const oldFile = oldResource.fsPath
+    await workspace.openResource(newResource.toString())
+    // Make sure TS knows about file
+    await wait(100)
+
+    let document = workspace.getDocument(newResource.toString())
+    if (!document) return
+
+    const edits = await this.getEditsForFileRename(
+      document.textDocument,
+      oldFile,
+      targetFile,
+    )
+    if (!edits) return
+
+    if (await this.promptUser(newResource)) {
+      await workspace.applyEdit(edits)
+    }
+  }
+
+  private async promptUser(newResource: Uri): Promise<boolean> {
+    const res = await workspace.nvim.call('coc#util#prompt_confirm', [`Update imports for moved file: ${newResource.fsPath} ?`])
+    return res == 1
+  }
+
+  private async getEditsForFileRename(document: TextDocument, oldFile: string, newFile: string): Promise<WorkspaceEdit> {
+    await this.fileConfigurationManager.ensureConfigurationForDocument(document)
+    const args: Proto.GetEditsForFileRenameRequestArgs = {
+      oldFilePath: oldFile,
+      newFilePath: newFile
+    }
+    const response = await this.client.execute('getEditsForFileRename', args)
+    if (!response || !response.body) {
+      return
+    }
+
+    const edits: Proto.FileCodeEdits[] = []
+    for (const edit of response.body) {
+      // Workaround for https://github.com/Microsoft/vscode/issues/52675
+      if ((edit as Proto.FileCodeEdits).fileName.match(
+        /[\/\\]node_modules[\/\\]/gi
+      )) {
+        continue
+      }
+      for (const change of (edit as Proto.FileCodeEdits).textChanges) {
+        if (change.newText.match(/\/node_modules\//gi)) {
+          continue
+        }
+      }
+
+      edits.push(edit)
+    }
+    return typeConverters.WorkspaceEdit.fromFileCodeEdits(this.client, edits)
+  }
+}
diff --git a/src/server/features/watchBuild.ts b/src/server/features/watchBuild.ts
new file mode 100644
index 0000000..e062b37
--- /dev/null
+++ b/src/server/features/watchBuild.ts
@@ -0,0 +1,172 @@
+import { DiagnosticCollection, disposeAll, Document, languages, workspace } from 'coc.nvim'
+import { Command, CommandManager } from 'coc.nvim/lib/commands'
+import { resolveRoot } from '../utils/fs'
+import fs from 'fs'
+import path from 'path'
+import { Diagnostic, DiagnosticSeverity, Disposable, Range } from 'vscode-languageserver-protocol'
+import Uri from 'vscode-uri'
+
+const TSC = './node_modules/.bin/tsc'
+const countRegex = /Found\s(\d+)\serror/
+const startRegex = /File\s+change\s+detected/
+const errorRegex = /^(.+):(\d+):(\d+)\s-\s(\w+)\s+[A-Za-z]+(\d+):\s+(.*)$/
+
+enum TscStatus {
+  INIT,
+  COMPILING,
+  RUNNING,
+  ERROR,
+}
+
+class WatchCommand implements Command {
+  public readonly id: string = 'tsserver.watchBuild'
+
+  constructor(
+    private collection: DiagnosticCollection
+  ) {
+  }
+
+  private setStatus(state: TscStatus): void {
+    let s = 'init'
+    switch (state) {
+      case TscStatus.COMPILING:
+        s = 'compiling'
+        break
+      case TscStatus.RUNNING:
+        s = 'running'
+        break
+      case TscStatus.ERROR:
+        s = 'error'
+        break
+    }
+    workspace.nvim.setVar('tsc_status', s, true)
+  }
+
+  public async execute(): Promise<void> {
+    let docs = workspace.documents
+    let idx = docs.findIndex(doc => doc.uri.indexOf(TSC) !== -1)
+    if (idx !== -1) return
+    let document = await workspace.document
+    let fsPath = Uri.parse(document.uri).fsPath
+    let cwd = path.dirname(fsPath)
+    let dir = resolveRoot(cwd, ['node_modules'])
+    if (dir) {
+      let file = path.join(dir, 'node_modules/.bin/tsc')
+      if (!fs.existsSync(file)) dir = null
+    }
+    if (!dir) {
+      workspace.showMessage('typescript module not found!', 'error')
+      return
+    }
+    let configRoot = resolveRoot(cwd, ['tsconfig.json'])
+    if (!configRoot) {
+      workspace.showMessage('tsconfig.json not found!', 'error')
+      return
+    }
+    let configPath = path.relative(dir, path.join(configRoot, 'tsconfig.json'))
+    let cmd = `${TSC} -p ${configPath} --watch true`
+    await workspace.nvim.call('coc#util#open_terminal', {
+      keepfocus: 1,
+      cwd: dir,
+      cmd
+    })
+  }
+
+  public async onTerminalCreated(doc: Document): Promise<void> {
+    let entries: Map<string, Diagnostic[]> = new Map()
+    let cwd = await doc.getcwd()
+    if (!cwd) return
+    let uris = new Set()
+    this.setStatus(TscStatus.RUNNING)
+    let parseLine = (line: string): void => {
+      if (startRegex.test(line)) {
+        this.setStatus(TscStatus.COMPILING)
+        entries = new Map()
+      } else if (errorRegex.test(line)) {
+        let ms = line.match(errorRegex)
+        let severity = /error/.test(ms[4]) ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning
+        let lnum = Number(ms[2]) - 1
+        let character = Number(ms[3]) - 1
+        let range = Range.create(lnum, character, lnum, character)
+        let uri = Uri.file(path.join(cwd, ms[1])).toString()
+        let diagnostics = entries.get(uri) || []
+        diagnostics.push(Diagnostic.create(range, ms[6], severity, ms[5], 'tsc'))
+        entries.set(uri, diagnostics)
+      } else if (countRegex.test(line)) {
+        let ms = line.match(countRegex)
+        if (ms[1] == '0') {
+          entries = new Map()
+          this.setStatus(TscStatus.RUNNING)
+          this.collection.clear()
+          uris = new Set()
+          return
+        }
+        this.setStatus(TscStatus.ERROR)
+        for (let [key, value] of entries.entries()) {
+          this.collection.set(key, value)
+        }
+        for (let uri of uris) {
+          if (!entries.has(uri)) {
+            this.collection.set(uri, [])
+          }
+        }
+        uris = new Set(entries.keys())
+      }
+    }
+    for (let line of doc.content.split('\n')) {
+      parseLine(line)
+    }
+    doc.onDocumentDetach(() => {
+      entries = new Map()
+      this.setStatus(TscStatus.INIT)
+      this.collection.clear()
+    })
+    doc.onDocumentChange(e => {
+      let { contentChanges } = e
+      for (let change of contentChanges) {
+        let lines = change.text.split('\n')
+        for (let line of lines) {
+          parseLine(line)
+        }
+      }
+    })
+  }
+}
+
+export default class WatchProject implements Disposable {
+  private disposables: Disposable[] = []
+  public constructor(
+    commandManager: CommandManager
+  ) {
+    let collection = languages.createDiagnosticCollection('tsc')
+    let cmd = new WatchCommand(collection)
+    commandManager.register(cmd)
+    this.disposables.push(Disposable.create(() => {
+      commandManager.unregister(cmd.id)
+    }))
+    workspace.documents.forEach(doc => {
+      let { uri } = doc
+      if (this.isTscBuffer(uri)) {
+        cmd.onTerminalCreated(doc).catch(_e => {
+          // noop
+        })
+      }
+    })
+    workspace.onDidOpenTextDocument(doc => {
+      let { uri } = doc
+      if (this.isTscBuffer(uri)) {
+        cmd.onTerminalCreated(workspace.getDocument(uri)).catch(_e => {
+          // noop
+        })
+      }
+    }, this, this.disposables)
+  }
+
+  private isTscBuffer(uri: string): boolean {
+    return uri.startsWith('term://') && uri.indexOf(TSC) !== -1
+  }
+
+  public dispose(): void {
+    disposeAll(this.disposables)
+  }
+}
diff --git a/src/server/features/workspaceSymbols.ts b/src/server/features/workspaceSymbols.ts
new file mode 100644
index 0000000..0c6c1f3
--- /dev/null
+++ b/src/server/features/workspaceSymbols.ts
@@ -0,0 +1,96 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { CancellationToken, Range, SymbolInformation, SymbolKind } from 'vscode-languageserver-protocol'
+import { WorkspaceSymbolProvider } from 'coc.nvim/lib/provider'
+import { workspace } from 'coc.nvim'
+import * as Proto from '../protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import * as typeConverters from '../utils/typeConverters'
+
+function getSymbolKind(item: Proto.NavtoItem): SymbolKind {
+  switch (item.kind) {
+    case 'method':
+      return SymbolKind.Method
+    case 'enum':
+      return SymbolKind.Enum
+    case 'function':
+      return SymbolKind.Function
+    case 'class':
+      return SymbolKind.Class
+    case 'interface':
+      return SymbolKind.Interface
+    case 'var':
+      return SymbolKind.Variable
+    default:
+      return SymbolKind.Variable
+  }
+}
+
+export default class TypeScriptWorkspaceSymbolProvider implements WorkspaceSymbolProvider {
+  public constructor(
+    private readonly client: ITypeScriptServiceClient,
+    private readonly languageIds: string[]
+  ) { }
+
+  public async provideWorkspaceSymbols(
+    search: string,
+    token: CancellationToken
+  ): Promise<SymbolInformation[]> {
+    const uri = this.getUri()
+    if (!uri) return []
+
+    const filepath = this.client.toPath(uri)
+    if (!filepath) return []
+
+    const args: Proto.NavtoRequestArgs = {
+      file: filepath,
+      searchValue: search
+    }
+
+    const response = await this.client.execute('navto', args, token)
+    if (!response.body) return []
+
+    const result: SymbolInformation[] = []
+    for (const item of response.body) {
+      if (!item.containerName && item.kind === 'alias') {
+        continue
+      }
+      const label = TypeScriptWorkspaceSymbolProvider.getLabel(item)
+      const range: Range = {
+        start: typeConverters.Position.fromLocation(item.start),
+        end: typeConverters.Position.fromLocation(item.end),
+      }
+      const symbolInfo = SymbolInformation.create(
+        label,
+        getSymbolKind(item),
+        range,
+        this.client.toResource(item.file))
+
+      result.push(symbolInfo)
+    }
+    return result
+  }
+
+  private static getLabel(item: Proto.NavtoItem): string {
+    let label = item.name
+    if (item.kind === 'method' || item.kind === 'function') {
+      label += '()'
+    }
+    return label
+  }
+
+  private getUri(): string {
+    // typescript wants to have a resource even when asking
+    // general questions so we check the active editor. If this
+    // doesn't match we take the first TS document.
+    const documents = workspace.textDocuments
+    for (const document of documents) {
+      if (this.languageIds.indexOf(document.languageId) >= 0) {
+        return document.uri
+      }
+    }
+    return undefined
+  }
+}
diff --git a/src/server/index.ts b/src/server/index.ts
new file mode 100644
index 0000000..75c4964
--- /dev/null
+++ b/src/server/index.ts
@@ -0,0 +1,94 @@
+import { disposeAll, IServiceProvider, ServiceStat, workspace, WorkspaceConfiguration } from 'coc.nvim'
+import { Disposable, DocumentSelector, Emitter, Event } from 'vscode-languageserver-protocol'
+import URI from 'vscode-uri'
+import TypeScriptServiceClientHost from './typescriptServiceClientHost'
+import { LanguageDescription, standardLanguageDescriptions } from './utils/languageDescription'
+
+function wait(ms: number): Promise<any> {
+  return new Promise(resolve => {
+    setTimeout(() => {
+      resolve()
+    }, ms)
+  })
+}
+
+export default class TsserverService implements IServiceProvider {
+  public id = 'tsserver'
+  public name = 'tsserver'
+  public enable: boolean
+  // supported language types
+  public selector: DocumentSelector
+  public state = ServiceStat.Initial
+  public clientHost: TypeScriptServiceClientHost
+  private _onDidServiceReady = new Emitter<void>()
+  public readonly onServiceReady: Event<void> = this._onDidServiceReady.event
+  private readonly disposables: Disposable[] = []
+  private descriptions: LanguageDescription[] = []
+
+  constructor() {
+    const config = workspace.getConfiguration('tsserver')
+    const enableJavascript = !!config.get<boolean>('enableJavascript')
+    this.enable = config.get<boolean>('enable')
+    this.descriptions = standardLanguageDescriptions.filter(o => {
+      return enableJavascript ? true : o.id != 'javascript'
+    })
+    this.selector = this.descriptions.reduce((arr, c) => {
+      return arr.concat(c.modeIds)
+    }, [])
+  }
+
+  public get config(): WorkspaceConfiguration {
+    return workspace.getConfiguration('tsserver')
+  }
+
+  public start(): Promise<void> {
+    this.clientHost = new TypeScriptServiceClientHost(this.descriptions)
+    this.disposables.push(this.clientHost)
+    Object.defineProperty(this, 'state', {
+      get: () => {
+        return this.clientHost.serviceClient.state
+      }
+    })
+    let client = this.clientHost.serviceClient
+    return new Promise(resolve => {
+      let started = false
+      client.onTsServerStarted(() => {
+        this._onDidServiceReady.fire(void 0)
+        this.ensureConfiguration() // tslint:disable-line
+        if (!started) {
+          started = true
+          resolve()
+        }
+      })
+    })
+  }
+
+  private async ensureConfiguration(): Promise<void> {
+    if (!this.clientHost) return
+    let document = await workspace.document
+    await wait(100)
+
+    let uri = URI.parse(document.uri)
+    let language = this.clientHost.findLanguage(uri)
+    if (!language) return
+    await language.fileConfigurationManager.ensureConfigurationForDocument(document.textDocument)
+  }
+
+  public dispose(): void {
+    disposeAll(this.disposables)
+  }
+
+  public async restart(): Promise<void> {
+    if (!this.clientHost) return
+    let client = this.clientHost.serviceClient
+    await client.restartTsServer()
+  }
+
+  public async stop(): Promise<void> {
+    if (!this.clientHost) return
+    this.clientHost.reset()
+    let client = this.clientHost.serviceClient
+    await client.stop()
+    return
+  }
+}
diff --git a/src/server/languageProvider.ts b/src/server/languageProvider.ts
new file mode 100644
index 0000000..d5a81b1
--- /dev/null
+++ b/src/server/languageProvider.ts
@@ -0,0 +1,349 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { Diagnostic, Disposable } from 'vscode-languageserver-protocol'
+import Uri from 'vscode-uri'
+import { workspace, commands, events, languages, DiagnosticKind, ServiceStat, disposeAll } from 'coc.nvim'
+import { CachedNavTreeResponse } from './features/baseCodeLensProvider'
+import BufferSyncSupport from './features/bufferSyncSupport'
+import CompletionItemProvider from './features/completionItemProvider'
+import DefinitionProvider from './features/definitionProvider'
+import { DiagnosticsManager } from './features/diagnostics'
+import DirectiveCommentCompletionProvider from './features/directiveCommentCompletions'
+import DocumentHighlight from './features/documentHighlight'
+import DocumentSymbolProvider from './features/documentSymbol'
+import FileConfigurationManager from './features/fileConfigurationManager'
+import Folding from './features/folding'
+import FormattingProvider from './features/formatting'
+import HoverProvider from './features/hover'
+import ImplementationsCodeLensProvider from './features/implementationsCodeLens'
+import OrganizeImportsProvider from './features/organizeImports'
+// import TagCompletionProvider from './features/tagCompletion'
+import QuickfixProvider from './features/quickfix'
+import RefactorProvider from './features/refactor'
+import ReferenceProvider from './features/references'
+import ReferencesCodeLensProvider from './features/referencesCodeLens'
+import RenameProvider from './features/rename'
+import SignatureHelpProvider from './features/signatureHelp'
+import UpdateImportsOnFileRenameHandler from './features/updatePathOnRename'
+import WatchBuild from './features/watchBuild'
+import WorkspaceSymbolProvider from './features/workspaceSymbols'
+import TypeScriptServiceClient from './typescriptServiceClient'
+import API from './utils/api'
+import { LanguageDescription } from './utils/languageDescription'
+import TypingsStatus from './utils/typingsStatus'
+
+const validateSetting = 'validate.enable'
+const suggestionSetting = 'suggestionActions.enabled'
+
+export default class LanguageProvider {
+  private readonly diagnosticsManager: DiagnosticsManager
+  private readonly bufferSyncSupport: BufferSyncSupport
+  public readonly fileConfigurationManager: FileConfigurationManager // tslint:disable-line
+  private _validate = true
+  private _enableSuggestionDiagnostics = true
+  private readonly disposables: Disposable[] = []
+
+  constructor(
+    public client: TypeScriptServiceClient,
+    private description: LanguageDescription,
+    typingsStatus: TypingsStatus
+  ) {
+    this.fileConfigurationManager = new FileConfigurationManager(client)
+    this.bufferSyncSupport = new BufferSyncSupport(
+      client,
+      description.modeIds,
+      this._validate
+    )
+    this.diagnosticsManager = new DiagnosticsManager()
+    this.disposables.push(this.diagnosticsManager)
+
+    client.onTsServerStarted(async () => {
+      let document = await workspace.document
+      if (description.modeIds.indexOf(document.filetype) !== -1) {
+        this.fileConfigurationManager.ensureConfigurationForDocument(document.textDocument) // tslint:disable-line
+      }
+    })
+
+    events.on('BufEnter', bufnr => {
+      let doc = workspace.getDocument(bufnr)
+      if (!doc) return
+      if (description.modeIds.indexOf(doc.filetype) == -1) return
+      if (client.state !== ServiceStat.Running) return
+      this.fileConfigurationManager.ensureConfigurationForDocument(doc.textDocument) // tslint:disable-line
+    }, this, this.disposables)
+
+    workspace.onDidChangeConfiguration(this.configurationChanged, this, this.disposables)
+
+    let initialized = false
+
+    client.onTsServerStarted(() => { // tslint:disable-line
+      if (!initialized) {
+        initialized = true
+        this.registerProviders(client, typingsStatus)
+        this.bufferSyncSupport.listen()
+      } else {
+        this.reInitialize()
+      }
+    })
+  }
+
+  public dispose(): void {
+    disposeAll(this.disposables)
+    this.bufferSyncSupport.dispose()
+  }
+
+  private configurationChanged(): void {
+    const config = workspace.getConfiguration(this.id)
+    this.updateValidate(config.get(validateSetting, true))
+    this.updateSuggestionDiagnostics(config.get(suggestionSetting, true))
+  }
+
+  private registerProviders(
+    client: TypeScriptServiceClient,
+    typingsStatus: TypingsStatus
+  ): void {
+    let languageIds = this.description.modeIds
+
+    this.disposables.push(
+      languages.registerCompletionItemProvider(
+        `tsserver-${this.description.id}`,
+        'TSC',
+        languageIds,
+        new CompletionItemProvider(
+          client,
+          typingsStatus,
+          this.fileConfigurationManager,
+          this.description.id
+        ),
+        CompletionItemProvider.triggerCharacters
+      )
+    )
+
+    if (this.client.apiVersion.gte(API.v230)) {
+      this.disposables.push(
+        languages.registerCompletionItemProvider(
+          `${this.description.id}-directive`,
+          'TSC',
+          languageIds,
+          new DirectiveCommentCompletionProvider(
+            client,
+          ),
+          ['@']
+        )
+      )
+    }
+    let definitionProvider = new DefinitionProvider(client)
+
+    this.disposables.push(
+      languages.registerDefinitionProvider(
+        languageIds,
+        definitionProvider
+      )
+    )
+
+    this.disposables.push(
+      languages.registerTypeDefinitionProvider(
+        languageIds,
+        definitionProvider
+      )
+    )
+
+    this.disposables.push(
+      languages.registerImplementationProvider(
+        languageIds,
+        definitionProvider
+      )
+    )
+
+    this.disposables.push(
+      languages.registerReferencesProvider(
+        languageIds,
+        new ReferenceProvider(client)
+      )
+    )
+
+    this.disposables.push(
+      languages.registerHoverProvider(
+        languageIds,
+        new HoverProvider(client))
+    )
+
+    this.disposables.push(
+      languages.registerDocumentHighlightProvider(languageIds, new DocumentHighlight(this.client))
+    )
+
+    this.disposables.push(
+      languages.registerSignatureHelpProvider(
+        languageIds,
+        new SignatureHelpProvider(client))
+    )
+
+    this.disposables.push(
+      languages.registerDocumentSymbolProvider(
+        languageIds,
+        new DocumentSymbolProvider(client))
+    )
+
+    this.disposables.push(
+      languages.registerWorkspaceSymbolProvider(
+        languageIds,
+        new WorkspaceSymbolProvider(client, languageIds))
+    )
+
+    this.disposables.push(
+      languages.registerRenameProvider(
+        languageIds,
+        new RenameProvider(client))
+    )
+    let formatProvider = new FormattingProvider(client, this.fileConfigurationManager)
+    this.disposables.push(
+      languages.registerDocumentFormatProvider(languageIds, formatProvider)
+    )
+    this.disposables.push(
+      languages.registerDocumentRangeFormatProvider(languageIds, formatProvider)
+    )
+    this.disposables.push(
+      languages.registerOnTypeFormattingEditProvider(languageIds, formatProvider, [';', '}', '\n'])
+    )
+
+    // this.disposables.push(
+    //   new ProjectError(client, commandManager)
+    // )
+
+    if (this.client.apiVersion.gte(API.v280)) {
+      this.disposables.push(
+        new OrganizeImportsProvider(client, commands, this.fileConfigurationManager, this.description.id)
+      )
+
+      this.disposables.push(
+        languages.registerFoldingRangeProvider(languageIds, new Folding(this.client))
+      )
+    }
+
+    let { fileConfigurationManager } = this
+    let conf = fileConfigurationManager.getLanguageConfiguration(this.id)
+
+    if (this.client.apiVersion.gte(API.v290)
+      && conf.get<boolean>('updateImportsOnFileMove.enable')) {
+      this.disposables.push(
+        new UpdateImportsOnFileRenameHandler(client, this.fileConfigurationManager, this.id)
+      )
+    }
+
+    if (this.client.apiVersion.gte(API.v240)) {
+      this.disposables.push(
+        languages.registerCodeActionProvider(
+          languageIds,
+          new RefactorProvider(client, this.fileConfigurationManager)))
+    }
+
+    this.disposables.push(
+      languages.registerCodeActionProvider(
+        languageIds,
+        new QuickfixProvider(client, this.diagnosticsManager, this.bufferSyncSupport)))
+    let cachedResponse = new CachedNavTreeResponse()
+    if (this.client.apiVersion.gte(API.v206)
+      && conf.get<boolean>('referencesCodeLens.enable')) {
+      this.disposables.push(
+        languages.registerCodeLensProvider(
+          languageIds,
+          new ReferencesCodeLensProvider(client, cachedResponse)))
+    }
+
+    if (this.client.apiVersion.gte(API.v220)
+      && conf.get<boolean>('implementationsCodeLens.enable')) {
+      this.disposables.push(
+        languages.registerCodeLensProvider(
+          languageIds,
+          new ImplementationsCodeLensProvider(client, cachedResponse)))
+    }
+
+    if (this.description.id == 'typescript') {
+      this.disposables.push(
+        new WatchBuild(commands)
+      )
+    }
+
+    // if (this.client.apiVersion.gte(API.v300)) {
+    //   this.disposables.push(
+    //     languages.registerCompletionItemProvider(
+    //       `tsserver-${this.description.id}-tag`,
+    //       'TSC',
+    //       languageIds,
+    //       new TagCompletionProvider(client),
+    //       ['>']
+    //     )
+    //   )
+    // }
+  }
+
+  public handles(resource: Uri): boolean {
+    let doc = workspace.getDocument(resource.toString())
+    let { modeIds } = this.description
+    if (doc && modeIds.indexOf(doc.filetype) !== -1) {
+      return true
+    }
+    let str = resource.toString()
+    if (this.id === 'typescript' && /\.ts(x)?$/.test(str)) {
+      return true
+    }
+    if (this.id === 'javascript' && /\.js(x)?$/.test(str)) {
+      return true
+    }
+    return false
+  }
+
+  private get id(): string { // tslint:disable-line
+    return this.description.id
+  }
+
+  public get diagnosticSource(): string {
+    return this.description.diagnosticSource
+  }
+
+  private updateValidate(value: boolean): void {
+    if (this._validate === value) {
+      return
+    }
+    this._validate = value
+    this.bufferSyncSupport.validate = value
+    this.diagnosticsManager.validate = value
+    if (value) {
+      this.triggerAllDiagnostics()
+    }
+  }
+
+  private updateSuggestionDiagnostics(value: boolean): void {
+    if (this._enableSuggestionDiagnostics === value) {
+      return
+    }
+    this._enableSuggestionDiagnostics = value
+    this.diagnosticsManager.enableSuggestions = value
+    if (value) {
+      this.triggerAllDiagnostics()
+    }
+  }
+
+  public reInitialize(): void {
+    this.diagnosticsManager.reInitialize()
+    this.bufferSyncSupport.reInitialize()
+  }
+
+  public triggerAllDiagnostics(): void {
+    this.bufferSyncSupport.requestAllDiagnostics()
+  }
+
+  public diagnosticsReceived(
+    diagnosticsKind: DiagnosticKind,
+    file: Uri,
+    diagnostics: Diagnostic[]
+  ): void {
+    this.diagnosticsManager.diagnosticsReceived(
+      diagnosticsKind,
+      file.toString(),
+      diagnostics
+    )
+  }
+}
diff --git a/src/server/protocol.const.ts b/src/server/protocol.const.ts
new file mode 100644
index 0000000..54010a6
--- /dev/null
+++ b/src/server/protocol.const.ts
@@ -0,0 +1,39 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+export class Kind {
+  public static readonly alias = 'alias'
+  public static readonly callSignature = 'call'
+  public static readonly class = 'class'
+  public static readonly const = 'const'
+  public static readonly constructorImplementation = 'constructor'
+  public static readonly constructSignature = 'construct'
+  public static readonly directory = 'directory'
+  public static readonly enum = 'enum'
+  public static readonly externalModuleName = 'external module name'
+  public static readonly file = 'file'
+  public static readonly function = 'function'
+  public static readonly indexSignature = 'index'
+  public static readonly interface = 'interface'
+  public static readonly keyword = 'keyword'
+  public static readonly let = 'let'
+  public static readonly localFunction = 'local function'
+  public static readonly localVariable = 'local var'
+  public static readonly memberFunction = 'method'
+  public static readonly memberGetAccessor = 'getter'
+  public static readonly memberSetAccessor = 'setter'
+  public static readonly memberVariable = 'property'
+  public static readonly module = 'module'
+  public static readonly primitiveType = 'primitive type'
+  public static readonly script = 'script'
+  public static readonly type = 'type'
+  public static readonly variable = 'var'
+  public static readonly warning = 'warning'
+}
+
+export class DiagnosticCategory {
+  public static readonly error = 'error'
+  public static readonly warning = 'warning'
+  public static readonly suggestion = 'suggestion'
+}
diff --git a/src/server/protocol.d.ts b/src/server/protocol.d.ts
new file mode 100644
index 0000000..dfaa3d9
--- /dev/null
+++ b/src/server/protocol.d.ts
@@ -0,0 +1,2475 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+declare namespace ts.server.protocol {
+  const enum CommandTypes {
+    JsxClosingTag = "jsxClosingTag",
+    Brace = "brace",
+    BraceCompletion = "braceCompletion",
+    GetSpanOfEnclosingComment = "getSpanOfEnclosingComment",
+    Change = "change",
+    Close = "close",
+    /** @deprecated Prefer CompletionInfo -- see comment on CompletionsResponse */
+    Completions = "completions",
+    CompletionInfo = "completionInfo",
+    CompletionDetails = "completionEntryDetails",
+    CompileOnSaveAffectedFileList = "compileOnSaveAffectedFileList",
+    CompileOnSaveEmitFile = "compileOnSaveEmitFile",
+    Configure = "configure",
+    Definition = "definition",
+    DefinitionAndBoundSpan = "definitionAndBoundSpan",
+    Implementation = "implementation",
+    Exit = "exit",
+    Format = "format",
+    Formatonkey = "formatonkey",
+    Geterr = "geterr",
+    GeterrForProject = "geterrForProject",
+    SemanticDiagnosticsSync = "semanticDiagnosticsSync",
+    SyntacticDiagnosticsSync = "syntacticDiagnosticsSync",
+    SuggestionDiagnosticsSync = "suggestionDiagnosticsSync",
+    NavBar = "navbar",
+    Navto = "navto",
+    NavTree = "navtree",
+    NavTreeFull = "navtree-full",
+    /** @deprecated */
+    Occurrences = "occurrences",
+    DocumentHighlights = "documentHighlights",
+    Open = "open",
+    Quickinfo = "quickinfo",
+    References = "references",
+    Reload = "reload",
+    Rename = "rename",
+    Saveto = "saveto",
+    SignatureHelp = "signatureHelp",
+    Status = "status",
+    TypeDefinition = "typeDefinition",
+    ProjectInfo = "projectInfo",
+    ReloadProjects = "reloadProjects",
+    Unknown = "unknown",
+    OpenExternalProject = "openExternalProject",
+    OpenExternalProjects = "openExternalProjects",
+    CloseExternalProject = "closeExternalProject",
+    GetOutliningSpans = "getOutliningSpans",
+    TodoComments = "todoComments",
+    Indentation = "indentation",
+    DocCommentTemplate = "docCommentTemplate",
+    CompilerOptionsForInferredProjects = "compilerOptionsForInferredProjects",
+    GetCodeFixes = "getCodeFixes",
+    GetCombinedCodeFix = "getCombinedCodeFix",
+    ApplyCodeActionCommand = "applyCodeActionCommand",
+    GetSupportedCodeFixes = "getSupportedCodeFixes",
+    GetApplicableRefactors = "getApplicableRefactors",
+    GetEditsForRefactor = "getEditsForRefactor",
+    OrganizeImports = "organizeImports",
+    GetEditsForFileRename = "getEditsForFileRename"
+  }
+  /**
+   * A TypeScript Server message
+   */
+  interface Message {
+    /**
+     * Sequence number of the message
+     */
+    seq: number;
+    /**
+     * One of "request", "response", or "event"
+     */
+    type: "request" | "response" | "event";
+  }
+  /**
+   * Client-initiated request message
+   */
+  interface Request extends Message {
+    type: "request";
+    /**
+     * The command to execute
+     */
+    command: string;
+    /**
+     * Object containing arguments for the command
+     */
+    arguments?: any;
+  }
+  /**
+   * Request to reload the project structure for all the opened files
+   */
+  interface ReloadProjectsRequest extends Message {
+    command: CommandTypes.ReloadProjects;
+  }
+  /**
+   * Server-initiated event message
+   */
+  interface Event extends Message {
+    type: "event";
+    /**
+     * Name of event
+     */
+    event: string;
+    /**
+     * Event-specific information
+     */
+    body?: any;
+  }
+  /**
+   * Response by server to client request message.
+   */
+  interface Response extends Message {
+    type: "response";
+    /**
+     * Sequence number of the request message.
+     */
+    request_seq: number;
+    /**
+     * Outcome of the request.
+     */
+    success: boolean;
+    /**
+     * The command requested.
+     */
+    command: string;
+    /**
+     * If success === false, this should always be provided.
+     * Otherwise, may (or may not) contain a success message.
+     */
+    message?: string;
+    /**
+     * Contains message body if success === true.
+     */
+    body?: any;
+  }
+  /**
+   * Arguments for FileRequest messages.
+   */
+  interface FileRequestArgs {
+    /**
+     * The file for the request (absolute pathname required).
+     */
+    file: string;
+    projectFileName?: string;
+  }
+  interface StatusRequest extends Request {
+    command: CommandTypes.Status;
+  }
+  interface StatusResponseBody {
+    /**
+     * The TypeScript version (`ts.version`).
+     */
+    version: string;
+  }
+  /**
+   * Response to StatusRequest
+   */
+  interface StatusResponse extends Response {
+    body: StatusResponseBody;
+  }
+  /**
+   * Requests a JS Doc comment template for a given position
+   */
+  interface DocCommentTemplateRequest extends FileLocationRequest {
+    command: CommandTypes.DocCommentTemplate;
+  }
+  /**
+   * Response to DocCommentTemplateRequest
+   */
+  interface DocCommandTemplateResponse extends Response {
+    body?: TextInsertion;
+  }
+  /**
+   * A request to get TODO comments from the file
+   */
+  interface TodoCommentRequest extends FileRequest {
+    command: CommandTypes.TodoComments;
+    arguments: TodoCommentRequestArgs;
+  }
+  /**
+   * Arguments for TodoCommentRequest request.
+   */
+  interface TodoCommentRequestArgs extends FileRequestArgs {
+    /**
+     * Array of target TodoCommentDescriptors that describes TODO comments to be found
+     */
+    descriptors: TodoCommentDescriptor[];
+  }
+  /**
+   * Response for TodoCommentRequest request.
+   */
+  interface TodoCommentsResponse extends Response {
+    body?: TodoComment[];
+  }
+  /**
+   * A request to determine if the caret is inside a comment.
+   */
+  interface SpanOfEnclosingCommentRequest extends FileLocationRequest {
+    command: CommandTypes.GetSpanOfEnclosingComment;
+    arguments: SpanOfEnclosingCommentRequestArgs;
+  }
+  interface SpanOfEnclosingCommentRequestArgs extends FileLocationRequestArgs {
+    /**
+     * Requires that the enclosing span be a multi-line comment, or else the request returns undefined.
+     */
+    onlyMultiLine: boolean;
+  }
+  /**
+   * Request to obtain outlining spans in file.
+   */
+  interface OutliningSpansRequest extends FileRequest {
+    command: CommandTypes.GetOutliningSpans;
+  }
+  interface OutliningSpan {
+    /** The span of the document to actually collapse. */
+    textSpan: TextSpan;
+    /** The span of the document to display when the user hovers over the collapsed span. */
+    hintSpan: TextSpan;
+    /** The text to display in the editor for the collapsed region. */
+    bannerText: string;
+    /**
+     * Whether or not this region should be automatically collapsed when
+     * the 'Collapse to Definitions' command is invoked.
+     */
+    autoCollapse: boolean;
+    /**
+     * Classification of the contents of the span
+     */
+    kind: OutliningSpanKind;
+  }
+  /**
+   * Response to OutliningSpansRequest request.
+   */
+  interface OutliningSpansResponse extends Response {
+    body?: OutliningSpan[];
+  }
+  /**
+   * A request to get indentation for a location in file
+   */
+  interface IndentationRequest extends FileLocationRequest {
+    command: CommandTypes.Indentation;
+    arguments: IndentationRequestArgs;
+  }
+  /**
+   * Response for IndentationRequest request.
+   */
+  interface IndentationResponse extends Response {
+    body?: IndentationResult;
+  }
+  /**
+   * Indentation result representing where indentation should be placed
+   */
+  interface IndentationResult {
+    /**
+     * The base position in the document that the indent should be relative to
+     */
+    position: number;
+    /**
+     * The number of columns the indent should be at relative to the position's column.
+     */
+    indentation: number;
+  }
+  /**
+   * Arguments for IndentationRequest request.
+   */
+  interface IndentationRequestArgs extends FileLocationRequestArgs {
+    /**
+     * An optional set of settings to be used when computing indentation.
+     * If argument is omitted - then it will use settings for file that were previously set via 'configure' request or global settings.
+     */
+    options?: EditorSettings;
+  }
+  /**
+   * Arguments for ProjectInfoRequest request.
+   */
+  interface ProjectInfoRequestArgs extends FileRequestArgs {
+    /**
+     * Indicate if the file name list of the project is needed
+     */
+    needFileNameList: boolean;
+  }
+  /**
+   * A request to get the project information of the current file.
+   */
+  interface ProjectInfoRequest extends Request {
+    command: CommandTypes.ProjectInfo;
+    arguments: ProjectInfoRequestArgs;
+  }
+  /**
+   * A request to retrieve compiler options diagnostics for a project
+   */
+  interface CompilerOptionsDiagnosticsRequest extends Request {
+    arguments: CompilerOptionsDiagnosticsRequestArgs;
+  }
+  /**
+   * Arguments for CompilerOptionsDiagnosticsRequest request.
+   */
+  interface CompilerOptionsDiagnosticsRequestArgs {
+    /**
+     * Name of the project to retrieve compiler options diagnostics.
+     */
+    projectFileName: string;
+  }
+  /**
+   * Response message body for "projectInfo" request
+   */
+  interface ProjectInfo {
+    /**
+     * For configured project, this is the normalized path of the 'tsconfig.json' file
+     * For inferred project, this is undefined
+     */
+    configFileName: string;
+    /**
+     * The list of normalized file name in the project, including 'lib.d.ts'
+     */
+    fileNames?: string[];
+    /**
+     * Indicates if the project has a active language service instance
+     */
+    languageServiceDisabled?: boolean;
+  }
+  /**
+   * Represents diagnostic info that includes location of diagnostic in two forms
+   * - start position and length of the error span
+   * - startLocation and endLocation - a pair of Location objects that store start/end line and offset of the error span.
+   */
+  interface DiagnosticWithLinePosition {
+    message: string;
+    start: number;
+    length: number;
+    startLocation: Location;
+    endLocation: Location;
+    category: string;
+    code: number;
+    /** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */
+    reportsUnnecessary?: {};
+    relatedInformation?: DiagnosticRelatedInformation[];
+  }
+  /**
+   * Response message for "projectInfo" request
+   */
+  interface ProjectInfoResponse extends Response {
+    body?: ProjectInfo;
+  }
+  /**
+   * Request whose sole parameter is a file name.
+   */
+  interface FileRequest extends Request {
+    arguments: FileRequestArgs;
+  }
+  /**
+   * Instances of this interface specify a location in a source file:
+   * (file, line, character offset), where line and character offset are 1-based.
+   */
+  interface FileLocationRequestArgs extends FileRequestArgs {
+    /**
+     * The line number for the request (1-based).
+     */
+    line: number;
+    /**
+     * The character offset (on the line) for the request (1-based).
+     */
+    offset: number;
+  }
+  type FileLocationOrRangeRequestArgs = FileLocationRequestArgs | FileRangeRequestArgs;
+  /**
+   * Request refactorings at a given position or selection area.
+   */
+  interface GetApplicableRefactorsRequest extends Request {
+    command: CommandTypes.GetApplicableRefactors;
+    arguments: GetApplicableRefactorsRequestArgs;
+  }
+  type GetApplicableRefactorsRequestArgs = FileLocationOrRangeRequestArgs;
+  /**
+   * Response is a list of available refactorings.
+   * Each refactoring exposes one or more "Actions"; a user selects one action to invoke a refactoring
+   */
+  interface GetApplicableRefactorsResponse extends Response {
+    body?: ApplicableRefactorInfo[];
+  }
+  /**
+   * A set of one or more available refactoring actions, grouped under a parent refactoring.
+   */
+  interface ApplicableRefactorInfo {
+    /**
+     * The programmatic name of the refactoring
+     */
+    name: string;
+    /**
+     * A description of this refactoring category to show to the user.
+     * If the refactoring gets inlined (see below), this text will not be visible.
+     */
+    description: string;
+    /**
+     * Inlineable refactorings can have their actions hoisted out to the top level
+     * of a context menu. Non-inlineanable refactorings should always be shown inside
+     * their parent grouping.
+     *
+     * If not specified, this value is assumed to be 'true'
+     */
+    inlineable?: boolean;
+    actions: RefactorActionInfo[];
+  }
+  /**
+   * Represents a single refactoring action - for example, the "Extract Method..." refactor might
+   * offer several actions, each corresponding to a surround class or closure to extract into.
+   */
+  interface RefactorActionInfo {
+    /**
+     * The programmatic name of the refactoring action
+     */
+    name: string;
+    /**
+     * A description of this refactoring action to show to the user.
+     * If the parent refactoring is inlined away, this will be the only text shown,
+     * so this description should make sense by itself if the parent is inlineable=true
+     */
+    description: string;
+  }
+  interface GetEditsForRefactorRequest extends Request {
+    command: CommandTypes.GetEditsForRefactor;
+    arguments: GetEditsForRefactorRequestArgs;
+  }
+  /**
+   * Request the edits that a particular refactoring action produces.
+   * Callers must specify the name of the refactor and the name of the action.
+   */
+  type GetEditsForRefactorRequestArgs = FileLocationOrRangeRequestArgs & {
+    refactor: string;
+    action: string;
+  };
+  interface GetEditsForRefactorResponse extends Response {
+    body?: RefactorEditInfo;
+  }
+  interface RefactorEditInfo {
+    edits: FileCodeEdits[];
+    /**
+     * An optional location where the editor should start a rename operation once
+     * the refactoring edits have been applied
+     */
+    renameLocation?: Location;
+    renameFilename?: string;
+  }
+  /**
+   * Organize imports by:
+   *   1) Removing unused imports
+   *   2) Coalescing imports from the same module
+   *   3) Sorting imports
+   */
+  interface OrganizeImportsRequest extends Request {
+    command: CommandTypes.OrganizeImports;
+    arguments: OrganizeImportsRequestArgs;
+  }
+  type OrganizeImportsScope = GetCombinedCodeFixScope;
+  interface OrganizeImportsRequestArgs {
+    scope: OrganizeImportsScope;
+  }
+  interface OrganizeImportsResponse extends Response {
+    body: ReadonlyArray<FileCodeEdits>;
+  }
+  interface GetEditsForFileRenameRequest extends Request {
+    command: CommandTypes.GetEditsForFileRename;
+    arguments: GetEditsForFileRenameRequestArgs;
+  }
+  /** Note: Paths may also be directories. */
+  interface GetEditsForFileRenameRequestArgs {
+    readonly oldFilePath: string;
+    readonly newFilePath: string;
+  }
+  interface GetEditsForFileRenameResponse extends Response {
+    body: ReadonlyArray<FileCodeEdits>;
+  }
+  /**
+   * Request for the available codefixes at a specific position.
+   */
+  interface CodeFixRequest extends Request {
+    command: CommandTypes.GetCodeFixes;
+    arguments: CodeFixRequestArgs;
+  }
+  interface GetCombinedCodeFixRequest extends Request {
+    command: CommandTypes.GetCombinedCodeFix;
+    arguments: GetCombinedCodeFixRequestArgs;
+  }
+  interface GetCombinedCodeFixResponse extends Response {
+    body: CombinedCodeActions;
+  }
+  interface ApplyCodeActionCommandRequest extends Request {
+    command: CommandTypes.ApplyCodeActionCommand;
+    arguments: ApplyCodeActionCommandRequestArgs;
+  }
+  interface ApplyCodeActionCommandResponse extends Response {
+  }
+  interface FileRangeRequestArgs extends FileRequestArgs {
+    /**
+     * The line number for the request (1-based).
+     */
+    startLine: number;
+    /**
+     * The character offset (on the line) for the request (1-based).
+     */
+    startOffset: number;
+    /**
+     * The line number for the request (1-based).
+     */
+    endLine: number;
+    /**
+     * The character offset (on the line) for the request (1-based).
+     */
+    endOffset: number;
+  }
+  /**
+   * Instances of this interface specify errorcodes on a specific location in a sourcefile.
+   */
+  interface CodeFixRequestArgs extends FileRangeRequestArgs {
+    /**
+     * Errorcodes we want to get the fixes for.
+     */
+    errorCodes?: ReadonlyArray<number>;
+  }
+  interface GetCombinedCodeFixRequestArgs {
+    scope: GetCombinedCodeFixScope;
+    fixId: {};
+  }
+  interface GetCombinedCodeFixScope {
+    type: "file";
+    args: FileRequestArgs;
+  }
+  interface ApplyCodeActionCommandRequestArgs {
+    /** May also be an array of commands. */
+    command: {};
+  }
+  /**
+   * Response for GetCodeFixes request.
+   */
+  interface GetCodeFixesResponse extends Response {
+    body?: CodeAction[];
+  }
+  /**
+   * A request whose arguments specify a file location (file, line, col).
+   */
+  interface FileLocationRequest extends FileRequest {
+    arguments: FileLocationRequestArgs;
+  }
+  /**
+   * A request to get codes of supported code fixes.
+   */
+  interface GetSupportedCodeFixesRequest extends Request {
+    command: CommandTypes.GetSupportedCodeFixes;
+  }
+  /**
+   * A response for GetSupportedCodeFixesRequest request.
+   */
+  interface GetSupportedCodeFixesResponse extends Response {
+    /**
+     * List of error codes supported by the server.
+     */
+    body?: string[];
+  }
+  /**
+   * Arguments for EncodedSemanticClassificationsRequest request.
+   */
+  interface EncodedSemanticClassificationsRequestArgs extends FileRequestArgs {
+    /**
+     * Start position of the span.
+     */
+    start: number;
+    /**
+     * Length of the span.
+     */
+    length: number;
+  }
+  /**
+   * Arguments in document highlight request; include: filesToSearch, file,
+   * line, offset.
+   */
+  interface DocumentHighlightsRequestArgs extends FileLocationRequestArgs {
+    /**
+     * List of files to search for document highlights.
+     */
+    filesToSearch: string[];
+  }
+  /**
+   * Go to definition request; value of command field is
+   * "definition". Return response giving the file locations that
+   * define the symbol found in file at location line, col.
+   */
+  interface DefinitionRequest extends FileLocationRequest {
+    command: CommandTypes.Definition;
+  }
+  interface DefinitionAndBoundSpanRequest extends FileLocationRequest {
+    readonly command: CommandTypes.DefinitionAndBoundSpan;
+  }
+  interface DefinitionAndBoundSpanResponse extends Response {
+    readonly body: DefinitionInfoAndBoundSpan;
+  }
+  /**
+   * Go to type request; value of command field is
+   * "typeDefinition". Return response giving the file locations that
+   * define the type for the symbol found in file at location line, col.
+   */
+  interface TypeDefinitionRequest extends FileLocationRequest {
+    command: CommandTypes.TypeDefinition;
+  }
+  /**
+   * Go to implementation request; value of command field is
+   * "implementation". Return response giving the file locations that
+   * implement the symbol found in file at location line, col.
+   */
+  interface ImplementationRequest extends FileLocationRequest {
+    command: CommandTypes.Implementation;
+  }
+  /**
+   * Location in source code expressed as (one-based) line and (one-based) column offset.
+   */
+  interface Location {
+    line: number;
+    offset: number;
+  }
+  /**
+   * Object found in response messages defining a span of text in source code.
+   */
+  interface TextSpan {
+    /**
+     * First character of the definition.
+     */
+    start: Location;
+    /**
+     * One character past last character of the definition.
+     */
+    end: Location;
+  }
+  /**
+   * Object found in response messages defining a span of text in a specific source file.
+   */
+  interface FileSpan extends TextSpan {
+    /**
+     * File containing text span.
+     */
+    file: string;
+  }
+  interface DefinitionInfoAndBoundSpan {
+    definitions: ReadonlyArray<FileSpan>;
+    textSpan: TextSpan;
+  }
+  /**
+   * Definition response message.  Gives text range for definition.
+   */
+  interface DefinitionResponse extends Response {
+    body?: FileSpan[];
+  }
+  interface DefinitionInfoAndBoundSpanReponse extends Response {
+    body?: DefinitionInfoAndBoundSpan;
+  }
+  /**
+   * Definition response message.  Gives text range for definition.
+   */
+  interface TypeDefinitionResponse extends Response {
+    body?: FileSpan[];
+  }
+  /**
+   * Implementation response message.  Gives text range for implementations.
+   */
+  interface ImplementationResponse extends Response {
+    body?: FileSpan[];
+  }
+  /**
+   * Request to get brace completion for a location in the file.
+   */
+  interface BraceCompletionRequest extends FileLocationRequest {
+    command: CommandTypes.BraceCompletion;
+    arguments: BraceCompletionRequestArgs;
+  }
+  /**
+   * Argument for BraceCompletionRequest request.
+   */
+  interface BraceCompletionRequestArgs extends FileLocationRequestArgs {
+    /**
+     * Kind of opening brace
+     */
+    openingBrace: string;
+  }
+  interface JsxClosingTagRequest extends FileLocationRequest {
+    readonly command: CommandTypes.JsxClosingTag;
+    readonly arguments: JsxClosingTagRequestArgs;
+  }
+  interface JsxClosingTagRequestArgs extends FileLocationRequestArgs {
+  }
+  interface JsxClosingTagResponse extends Response {
+    readonly body: TextInsertion;
+  }
+  /**
+   * @deprecated
+   * Get occurrences request; value of command field is
+   * "occurrences". Return response giving spans that are relevant
+   * in the file at a given line and column.
+   */
+  interface OccurrencesRequest extends FileLocationRequest {
+    command: CommandTypes.Occurrences;
+  }
+  /** @deprecated */
+  interface OccurrencesResponseItem extends FileSpan {
+    /**
+     * True if the occurrence is a write location, false otherwise.
+     */
+    isWriteAccess: boolean;
+    /**
+     * True if the occurrence is in a string, undefined otherwise;
+     */
+    isInString?: true;
+  }
+  /** @deprecated */
+  interface OccurrencesResponse extends Response {
+    body?: OccurrencesResponseItem[];
+  }
+  /**
+   * Get document highlights request; value of command field is
+   * "documentHighlights". Return response giving spans that are relevant
+   * in the file at a given line and column.
+   */
+  interface DocumentHighlightsRequest extends FileLocationRequest {
+    command: CommandTypes.DocumentHighlights;
+    arguments: DocumentHighlightsRequestArgs;
+  }
+  /**
+   * Span augmented with extra information that denotes the kind of the highlighting to be used for span.
+   */
+  interface HighlightSpan extends TextSpan {
+    kind: HighlightSpanKind;
+  }
+  /**
+   * Represents a set of highligh spans for a give name
+   */
+  interface DocumentHighlightsItem {
+    /**
+     * File containing highlight spans.
+     */
+    file: string;
+    /**
+     * Spans to highlight in file.
+     */
+    highlightSpans: HighlightSpan[];
+  }
+  /**
+   * Response for a DocumentHighlightsRequest request.
+   */
+  interface DocumentHighlightsResponse extends Response {
+    body?: DocumentHighlightsItem[];
+  }
+  /**
+   * Find references request; value of command field is
+   * "references". Return response giving the file locations that
+   * reference the symbol found in file at location line, col.
+   */
+  interface ReferencesRequest extends FileLocationRequest {
+    command: CommandTypes.References;
+  }
+  interface ReferencesResponseItem extends FileSpan {
+    /** Text of line containing the reference.  Including this
+     *  with the response avoids latency of editor loading files
+     * to show text of reference line (the server already has
+     * loaded the referencing files).
+     */
+    lineText: string;
+    /**
+     * True if reference is a write location, false otherwise.
+     */
+    isWriteAccess: boolean;
+    /**
+     * True if reference is a definition, false otherwise.
+     */
+    isDefinition: boolean;
+  }
+  /**
+   * The body of a "references" response message.
+   */
+  interface ReferencesResponseBody {
+    /**
+     * The file locations referencing the symbol.
+     */
+    refs: ReferencesResponseItem[];
+    /**
+     * The name of the symbol.
+     */
+    symbolName: string;
+    /**
+     * The start character offset of the symbol (on the line provided by the references request).
+     */
+    symbolStartOffset: number;
+    /**
+     * The full display name of the symbol.
+     */
+    symbolDisplayString: string;
+  }
+  /**
+   * Response to "references" request.
+   */
+  interface ReferencesResponse extends Response {
+    body?: ReferencesResponseBody;
+  }
+  /**
+   * Argument for RenameRequest request.
+   */
+  interface RenameRequestArgs extends FileLocationRequestArgs {
+    /**
+     * Should text at specified location be found/changed in comments?
+     */
+    findInComments?: boolean;
+    /**
+     * Should text at specified location be found/changed in strings?
+     */
+    findInStrings?: boolean;
+  }
+  /**
+   * Rename request; value of command field is "rename". Return
+   * response giving the file locations that reference the symbol
+   * found in file at location line, col. Also return full display
+   * name of the symbol so that client can print it unambiguously.
+   */
+  interface RenameRequest extends FileLocationRequest {
+    command: CommandTypes.Rename;
+    arguments: RenameRequestArgs;
+  }
+  /**
+   * Information about the item to be renamed.
+   */
+  interface RenameInfo {
+    /**
+     * True if item can be renamed.
+     */
+    canRename: boolean;
+    /**
+     * Error message if item can not be renamed.
+     */
+    localizedErrorMessage?: string;
+    /**
+     * Display name of the item to be renamed.
+     */
+    displayName: string;
+    /**
+     * Full display name of item to be renamed.
+     */
+    fullDisplayName: string;
+    /**
+     * The items's kind (such as 'className' or 'parameterName' or plain 'text').
+     */
+    kind: ScriptElementKind;
+    /**
+     * Optional modifiers for the kind (such as 'public').
+     */
+    kindModifiers: string;
+  }
+  /**
+   *  A group of text spans, all in 'file'.
+   */
+  interface SpanGroup {
+    /** The file to which the spans apply */
+    file: string;
+    /** The text spans in this group */
+    locs: TextSpan[];
+  }
+  interface RenameResponseBody {
+    /**
+     * Information about the item to be renamed.
+     */
+    info: RenameInfo;
+    /**
+     * An array of span groups (one per file) that refer to the item to be renamed.
+     */
+    locs: ReadonlyArray<SpanGroup>;
+  }
+  /**
+   * Rename response message.
+   */
+  interface RenameResponse extends Response {
+    body?: RenameResponseBody;
+  }
+  /**
+   * Represents a file in external project.
+   * External project is project whose set of files, compilation options and open\close state
+   * is maintained by the client (i.e. if all this data come from .csproj file in Visual Studio).
+   * External project will exist even if all files in it are closed and should be closed explicitly.
+   * If external project includes one or more tsconfig.json/jsconfig.json files then tsserver will
+   * create configured project for every config file but will maintain a link that these projects were created
+   * as a result of opening external project so they should be removed once external project is closed.
+   */
+  interface ExternalFile {
+    /**
+     * Name of file file
+     */
+    fileName: string;
+    /**
+     * Script kind of the file
+     */
+    scriptKind?: ScriptKindName | ts.ScriptKind;
+    /**
+     * Whether file has mixed content (i.e. .cshtml file that combines html markup with C#/JavaScript)
+     */
+    hasMixedContent?: boolean;
+    /**
+     * Content of the file
+     */
+    content?: string;
+  }
+  /**
+   * Represent an external project
+   */
+  interface ExternalProject {
+    /**
+     * Project name
+     */
+    projectFileName: string;
+    /**
+     * List of root files in project
+     */
+    rootFiles: ExternalFile[];
+    /**
+     * Compiler options for the project
+     */
+    options: ExternalProjectCompilerOptions;
+    /**
+     * @deprecated typingOptions. Use typeAcquisition instead
+     */
+    typingOptions?: TypeAcquisition;
+    /**
+     * Explicitly specified type acquisition for the project
+     */
+    typeAcquisition?: TypeAcquisition;
+  }
+  interface CompileOnSaveMixin {
+    /**
+     * If compile on save is enabled for the project
+     */
+    compileOnSave?: boolean;
+  }
+  /**
+   * For external projects, some of the project settings are sent together with
+   * compiler settings.
+   */
+  type ExternalProjectCompilerOptions = CompilerOptions & CompileOnSaveMixin;
+  /**
+   * Represents a set of changes that happen in project
+   */
+  interface ProjectChanges {
+    /**
+     * List of added files
+     */
+    added: string[];
+    /**
+     * List of removed files
+     */
+    removed: string[];
+    /**
+     * List of updated files
+     */
+    updated: string[];
+  }
+  /**
+   * Information found in a configure request.
+   */
+  interface ConfigureRequestArguments {
+    /**
+     * Information about the host, for example 'Emacs 24.4' or
+     * 'Sublime Text version 3075'
+     */
+    hostInfo?: string;
+    /**
+     * If present, tab settings apply only to this file.
+     */
+    file?: string;
+    /**
+     * The format options to use during formatting and other code editing features.
+     */
+    formatOptions?: FormatCodeSettings;
+    preferences?: UserPreferences;
+    /**
+     * The host's additional supported .js file extensions
+     */
+    extraFileExtensions?: FileExtensionInfo[];
+  }
+  /**
+   *  Configure request; value of command field is "configure".  Specifies
+   *  host information, such as host type, tab size, and indent size.
+   */
+  interface ConfigureRequest extends Request {
+    command: CommandTypes.Configure;
+    arguments: ConfigureRequestArguments;
+  }
+  /**
+   * Response to "configure" request.  This is just an acknowledgement, so
+   * no body field is required.
+   */
+  interface ConfigureResponse extends Response {
+  }
+  /**
+   *  Information found in an "open" request.
+   */
+  interface OpenRequestArgs extends FileRequestArgs {
+    /**
+     * Used when a version of the file content is known to be more up to date than the one on disk.
+     * Then the known content will be used upon opening instead of the disk copy
+     */
+    fileContent?: string;
+    /**
+     * Used to specify the script kind of the file explicitly. It could be one of the following:
+     *      "TS", "JS", "TSX", "JSX"
+     */
+    scriptKindName?: ScriptKindName;
+    /**
+     * Used to limit the searching for project config file. If given the searching will stop at this
+     * root path; otherwise it will go all the way up to the dist root path.
+     */
+    projectRootPath?: string;
+  }
+  type ScriptKindName = "TS" | "JS" | "TSX" | "JSX";
+  /**
+   * Open request; value of command field is "open". Notify the
+   * server that the client has file open.  The server will not
+   * monitor the filesystem for changes in this file and will assume
+   * that the client is updating the server (using the change and/or
+   * reload messages) when the file changes. Server does not currently
+   * send a response to an open request.
+   */
+  interface OpenRequest extends Request {
+    command: CommandTypes.Open;
+    arguments: OpenRequestArgs;
+  }
+  /**
+   * Request to open or update external project
+   */
+  interface OpenExternalProjectRequest extends Request {
+    command: CommandTypes.OpenExternalProject;
+    arguments: OpenExternalProjectArgs;
+  }
+  /**
+   * Arguments to OpenExternalProjectRequest request
+   */
+  type OpenExternalProjectArgs = ExternalProject;
+  /**
+   * Request to open multiple external projects
+   */
+  interface OpenExternalProjectsRequest extends Request {
+    command: CommandTypes.OpenExternalProjects;
+    arguments: OpenExternalProjectsArgs;
+  }
+  /**
+   * Arguments to OpenExternalProjectsRequest
+   */
+  interface OpenExternalProjectsArgs {
+    /**
+     * List of external projects to open or update
+     */
+    projects: ExternalProject[];
+  }
+  /**
+   * Response to OpenExternalProjectRequest request. This is just an acknowledgement, so
+   * no body field is required.
+   */
+  interface OpenExternalProjectResponse extends Response {
+  }
+  /**
+   * Response to OpenExternalProjectsRequest request. This is just an acknowledgement, so
+   * no body field is required.
+   */
+  interface OpenExternalProjectsResponse extends Response {
+  }
+  /**
+   * Request to close external project.
+   */
+  interface CloseExternalProjectRequest extends Request {
+    command: CommandTypes.CloseExternalProject;
+    arguments: CloseExternalProjectRequestArgs;
+  }
+  /**
+   * Arguments to CloseExternalProjectRequest request
+   */
+  interface CloseExternalProjectRequestArgs {
+    /**
+     * Name of the project to close
+     */
+    projectFileName: string;
+  }
+  /**
+   * Response to CloseExternalProjectRequest request. This is just an acknowledgement, so
+   * no body field is required.
+   */
+  interface CloseExternalProjectResponse extends Response {
+  }
+  /**
+   * Request to set compiler options for inferred projects.
+   * External projects are opened / closed explicitly.
+   * Configured projects are opened when user opens loose file that has 'tsconfig.json' or 'jsconfig.json' anywhere in one of containing folders.
+   * This configuration file will be used to obtain a list of files and configuration settings for the project.
+   * Inferred projects are created when user opens a loose file that is not the part of external project
+   * or configured project and will contain only open file and transitive closure of referenced files if 'useOneInferredProject' is false,
+   * or all open loose files and its transitive closure of referenced files if 'useOneInferredProject' is true.
+   */
+  interface SetCompilerOptionsForInferredProjectsRequest extends Request {
+    command: CommandTypes.CompilerOptionsForInferredProjects;
+    arguments: SetCompilerOptionsForInferredProjectsArgs;
+  }
+  /**
+   * Argument for SetCompilerOptionsForInferredProjectsRequest request.
+   */
+  interface SetCompilerOptionsForInferredProjectsArgs {
+    /**
+     * Compiler options to be used with inferred projects.
+     */
+    options: ExternalProjectCompilerOptions;
+    /**
+     * Specifies the project root path used to scope compiler options.
+     * It is an error to provide this property if the server has not been started with
+     * `useInferredProjectPerProjectRoot` enabled.
+     */
+    projectRootPath?: string;
+  }
+  /**
+   * Response to SetCompilerOptionsForInferredProjectsResponse request. This is just an acknowledgement, so
+   * no body field is required.
+   */
+  interface SetCompilerOptionsForInferredProjectsResponse extends Response {
+  }
+  /**
+   *  Exit request; value of command field is "exit".  Ask the server process
+   *  to exit.
+   */
+  interface ExitRequest extends Request {
+    command: CommandTypes.Exit;
+  }
+  /**
+   * Close request; value of command field is "close". Notify the
+   * server that the client has closed a previously open file.  If
+   * file is still referenced by open files, the server will resume
+   * monitoring the filesystem for changes to file.  Server does not
+   * currently send a response to a close request.
+   */
+  interface CloseRequest extends FileRequest {
+    command: CommandTypes.Close;
+  }
+  /**
+   * Request to obtain the list of files that should be regenerated if target file is recompiled.
+   * NOTE: this us query-only operation and does not generate any output on disk.
+   */
+  interface CompileOnSaveAffectedFileListRequest extends FileRequest {
+    command: CommandTypes.CompileOnSaveAffectedFileList;
+  }
+  /**
+   * Contains a list of files that should be regenerated in a project
+   */
+  interface CompileOnSaveAffectedFileListSingleProject {
+    /**
+     * Project name
+     */
+    projectFileName: string;
+    /**
+     * List of files names that should be recompiled
+     */
+    fileNames: string[];
+    /**
+     * true if project uses outFile or out compiler option
+     */
+    projectUsesOutFile: boolean;
+  }
+  /**
+   * Response for CompileOnSaveAffectedFileListRequest request;
+   */
+  interface CompileOnSaveAffectedFileListResponse extends Response {
+    body: CompileOnSaveAffectedFileListSingleProject[];
+  }
+  /**
+   * Request to recompile the file. All generated outputs (.js, .d.ts or .js.map files) is written on disk.
+   */
+  interface CompileOnSaveEmitFileRequest extends FileRequest {
+    command: CommandTypes.CompileOnSaveEmitFile;
+    arguments: CompileOnSaveEmitFileRequestArgs;
+  }
+  /**
+   * Arguments for CompileOnSaveEmitFileRequest
+   */
+  interface CompileOnSaveEmitFileRequestArgs extends FileRequestArgs {
+    /**
+     * if true - then file should be recompiled even if it does not have any changes.
+     */
+    forced?: boolean;
+  }
+  /**
+   * Quickinfo request; value of command field is
+   * "quickinfo". Return response giving a quick type and
+   * documentation string for the symbol found in file at location
+   * line, col.
+   */
+  interface QuickInfoRequest extends FileLocationRequest {
+    command: CommandTypes.Quickinfo;
+  }
+  /**
+   * Body of QuickInfoResponse.
+   */
+  interface QuickInfoResponseBody {
+    /**
+     * The symbol's kind (such as 'className' or 'parameterName' or plain 'text').
+     */
+    kind: ScriptElementKind;
+    /**
+     * Optional modifiers for the kind (such as 'public').
+     */
+    kindModifiers: string;
+    /**
+     * Starting file location of symbol.
+     */
+    start: Location;
+    /**
+     * One past last character of symbol.
+     */
+    end: Location;
+    /**
+     * Type and kind of symbol.
+     */
+    displayString: string;
+    /**
+     * Documentation associated with symbol.
+     */
+    documentation: string;
+    /**
+     * JSDoc tags associated with symbol.
+     */
+    tags: JSDocTagInfo[];
+  }
+  /**
+   * Quickinfo response message.
+   */
+  interface QuickInfoResponse extends Response {
+    body?: QuickInfoResponseBody;
+  }
+  /**
+   * Arguments for format messages.
+   */
+  interface FormatRequestArgs extends FileLocationRequestArgs {
+    /**
+     * Last line of range for which to format text in file.
+     */
+    endLine: number;
+    /**
+     * Character offset on last line of range for which to format text in file.
+     */
+    endOffset: number;
+    /**
+     * Format options to be used.
+     */
+    options?: FormatCodeSettings;
+  }
+  /**
+   * Format request; value of command field is "format".  Return
+   * response giving zero or more edit instructions.  The edit
+   * instructions will be sorted in file order.  Applying the edit
+   * instructions in reverse to file will result in correctly
+   * reformatted text.
+   */
+  interface FormatRequest extends FileLocationRequest {
+    command: CommandTypes.Format;
+    arguments: FormatRequestArgs;
+  }
+  /**
+   * Object found in response messages defining an editing
+   * instruction for a span of text in source code.  The effect of
+   * this instruction is to replace the text starting at start and
+   * ending one character before end with newText. For an insertion,
+   * the text span is empty.  For a deletion, newText is empty.
+   */
+  interface CodeEdit {
+    /**
+     * First character of the text span to edit.
+     */
+    start: Location;
+    /**
+     * One character past last character of the text span to edit.
+     */
+    end: Location;
+    /**
+     * Replace the span defined above with this string (may be
+     * the empty string).
+     */
+    newText: string;
+  }
+  interface FileCodeEdits {
+    fileName: string;
+    textChanges: CodeEdit[];
+  }
+  interface CodeFixResponse extends Response {
+    /** The code actions that are available */
+    body?: CodeFixAction[];
+  }
+  interface CodeAction {
+    /** Description of the code action to display in the UI of the editor */
+    description: string;
+    /** Text changes to apply to each file as part of the code action */
+    changes: FileCodeEdits[];
+    /** A command is an opaque object that should be passed to `ApplyCodeActionCommandRequestArgs` without modification.  */
+    commands?: {}[];
+  }
+  interface CombinedCodeActions {
+    changes: ReadonlyArray<FileCodeEdits>;
+    commands?: ReadonlyArray<{}>;
+  }
+  interface CodeFixAction extends CodeAction {
+    /** Short name to identify the fix, for use by telemetry. */
+    fixName: string;
+    /**
+     * If present, one may call 'getCombinedCodeFix' with this fixId.
+     * This may be omitted to indicate that the code fix can't be applied in a group.
+     */
+    fixId?: {};
+    /** Should be present if and only if 'fixId' is. */
+    fixAllDescription?: string;
+  }
+  /**
+   * Format and format on key response message.
+   */
+  interface FormatResponse extends Response {
+    body?: CodeEdit[];
+  }
+  /**
+   * Arguments for format on key messages.
+   */
+  interface FormatOnKeyRequestArgs extends FileLocationRequestArgs {
+    /**
+     * Key pressed (';', '\n', or '}').
+     */
+    key: string;
+    options?: FormatCodeSettings;
+  }
+  /**
+   * Format on key request; value of command field is
+   * "formatonkey". Given file location and key typed (as string),
+   * return response giving zero or more edit instructions.  The
+   * edit instructions will be sorted in file order.  Applying the
+   * edit instructions in reverse to file will result in correctly
+   * reformatted text.
+   */
+  interface FormatOnKeyRequest extends FileLocationRequest {
+    command: CommandTypes.Formatonkey;
+    arguments: FormatOnKeyRequestArgs;
+  }
+  type CompletionsTriggerCharacter = "." | '"' | "'" | "`" | "/" | "@" | "<";
+  /**
+   * Arguments for completions messages.
+   */
+  interface CompletionsRequestArgs extends FileLocationRequestArgs {
+    /**
+     * Optional prefix to apply to possible completions.
+     */
+    prefix?: string;
+    /**
+     * Character that was responsible for triggering completion.
+     * Should be `undefined` if a user manually requested completion.
+     */
+    triggerCharacter?: CompletionsTriggerCharacter;
+    /**
+     * @deprecated Use UserPreferences.includeCompletionsForModuleExports
+     */
+    includeExternalModuleExports?: boolean;
+    /**
+     * @deprecated Use UserPreferences.includeCompletionsWithInsertText
+     */
+    includeInsertTextCompletions?: boolean;
+  }
+  /**
+   * Completions request; value of command field is "completions".
+   * Given a file location (file, line, col) and a prefix (which may
+   * be the empty string), return the possible completions that
+   * begin with prefix.
+   */
+  interface CompletionsRequest extends FileLocationRequest {
+    command: CommandTypes.Completions;
+    arguments: CompletionsRequestArgs;
+  }
+  /**
+   * Arguments for completion details request.
+   */
+  interface CompletionDetailsRequestArgs extends FileLocationRequestArgs {
+    /**
+     * Names of one or more entries for which to obtain details.
+     */
+    entryNames: (string | CompletionEntryIdentifier)[];
+  }
+  interface CompletionEntryIdentifier {
+    name: string;
+    source?: string;
+  }
+  /**
+   * Completion entry details request; value of command field is
+   * "completionEntryDetails".  Given a file location (file, line,
+   * col) and an array of completion entry names return more
+   * detailed information for each completion entry.
+   */
+  interface CompletionDetailsRequest extends FileLocationRequest {
+    command: CommandTypes.CompletionDetails;
+    arguments: CompletionDetailsRequestArgs;
+  }
+  /**
+   * Part of a symbol description.
+   */
+  interface SymbolDisplayPart {
+    /**
+     * Text of an item describing the symbol.
+     */
+    text: string;
+    /**
+     * The symbol's kind (such as 'className' or 'parameterName' or plain 'text').
+     */
+    kind: string;
+  }
+  /**
+   * An item found in a completion response.
+   */
+  interface CompletionEntry {
+    /**
+     * The symbol's name.
+     */
+    name: string;
+    /**
+     * The symbol's kind (such as 'className' or 'parameterName').
+     */
+    kind: ScriptElementKind;
+    /**
+     * Optional modifiers for the kind (such as 'public').
+     */
+    kindModifiers?: string;
+    /**
+     * A string that is used for comparing completion items so that they can be ordered.  This
+     * is often the same as the name but may be different in certain circumstances.
+     */
+    sortText: string;
+    /**
+     * Text to insert instead of `name`.
+     * This is used to support bracketed completions; If `name` might be "a-b" but `insertText` would be `["a-b"]`,
+     * coupled with `replacementSpan` to replace a dotted access with a bracket access.
+     */
+    insertText?: string;
+    /**
+     * An optional span that indicates the text to be replaced by this completion item.
+     * If present, this span should be used instead of the default one.
+     * It will be set if the required span differs from the one generated by the default replacement behavior.
+     */
+    replacementSpan?: TextSpan;
+    /**
+     * Indicates whether commiting this completion entry will require additional code actions to be
+     * made to avoid errors. The CompletionEntryDetails will have these actions.
+     */
+    hasAction?: true;
+    /**
+     * Identifier (not necessarily human-readable) identifying where this completion came from.
+     */
+    source?: string;
+    /**
+     * If true, this completion should be highlighted as recommended. There will only be one of these.
+     * This will be set when we know the user should write an expression with a certain type and that type is an enum or constructable class.
+     * Then either that enum/class or a namespace containing it will be the recommended symbol.
+     */
+    isRecommended?: true;
+  }
+  /**
+   * Additional completion entry details, available on demand
+   */
+  interface CompletionEntryDetails {
+    /**
+     * The symbol's name.
+     */
+    name: string;
+    /**
+     * The symbol's kind (such as 'className' or 'parameterName').
+     */
+    kind: ScriptElementKind;
+    /**
+     * Optional modifiers for the kind (such as 'public').
+     */
+    kindModifiers: string;
+    /**
+     * Display parts of the symbol (similar to quick info).
+     */
+    displayParts: SymbolDisplayPart[];
+    /**
+     * Documentation strings for the symbol.
+     */
+    documentation?: SymbolDisplayPart[];
+    /**
+     * JSDoc tags for the symbol.
+     */
+    tags?: JSDocTagInfo[];
+    /**
+     * The associated code actions for this entry
+     */
+    codeActions?: CodeAction[];
+    /**
+     * Human-readable description of the `source` from the CompletionEntry.
+     */
+    source?: SymbolDisplayPart[];
+  }
+  /** @deprecated Prefer CompletionInfoResponse, which supports several top-level fields in addition to the array of entries. */
+  interface CompletionsResponse extends Response {
+    body?: CompletionEntry[];
+  }
+  interface CompletionInfoResponse extends Response {
+    body?: CompletionInfo;
+  }
+  interface CompletionInfo {
+    readonly isGlobalCompletion: boolean;
+    readonly isMemberCompletion: boolean;
+    readonly isNewIdentifierLocation: boolean;
+    readonly entries: ReadonlyArray<CompletionEntry>;
+  }
+  interface CompletionDetailsResponse extends Response {
+    body?: CompletionEntryDetails[];
+  }
+  /**
+   * Signature help information for a single parameter
+   */
+  interface SignatureHelpParameter {
+    /**
+     * The parameter's name
+     */
+    name: string;
+    /**
+     * Documentation of the parameter.
+     */
+    documentation: SymbolDisplayPart[];
+    /**
+     * Display parts of the parameter.
+     */
+    displayParts: SymbolDisplayPart[];
+    /**
+     * Whether the parameter is optional or not.
+     */
+    isOptional: boolean;
+  }
+  /**
+   * Represents a single signature to show in signature help.
+   */
+  interface SignatureHelpItem {
+    /**
+     * Whether the signature accepts a variable number of arguments.
+     */
+    isVariadic: boolean;
+    /**
+     * The prefix display parts.
+     */
+    prefixDisplayParts: SymbolDisplayPart[];
+    /**
+     * The suffix display parts.
+     */
+    suffixDisplayParts: SymbolDisplayPart[];
+    /**
+     * The separator display parts.
+     */
+    separatorDisplayParts: SymbolDisplayPart[];
+    /**
+     * The signature helps items for the parameters.
+     */
+    parameters: SignatureHelpParameter[];
+    /**
+     * The signature's documentation
+     */
+    documentation: SymbolDisplayPart[];
+    /**
+     * The signature's JSDoc tags
+     */
+    tags: JSDocTagInfo[];
+  }
+  /**
+   * Signature help items found in the response of a signature help request.
+   */
+  interface SignatureHelpItems {
+    /**
+     * The signature help items.
+     */
+    items: SignatureHelpItem[];
+    /**
+     * The span for which signature help should appear on a signature
+     */
+    applicableSpan: TextSpan;
+    /**
+     * The item selected in the set of available help items.
+     */
+    selectedItemIndex: number;
+    /**
+     * The argument selected in the set of parameters.
+     */
+    argumentIndex: number;
+    /**
+     * The argument count
+     */
+    argumentCount: number;
+  }
+  type SignatureHelpTriggerCharacter = "," | "(" | "<";
+  type SignatureHelpRetriggerCharacter = SignatureHelpTriggerCharacter | ")";
+  /**
+   * Arguments of a signature help request.
+   */
+  interface SignatureHelpRequestArgs extends FileLocationRequestArgs {
+    /**
+     * Reason why signature help was invoked.
+     * See each individual possible
+     */
+    triggerReason?: SignatureHelpTriggerReason;
+  }
+  type SignatureHelpTriggerReason = SignatureHelpInvokedReason | SignatureHelpCharacterTypedReason | SignatureHelpRetriggeredReason;
+  /**
+   * Signals that the user manually requested signature help.
+   * The language service will unconditionally attempt to provide a result.
+   */
+  interface SignatureHelpInvokedReason {
+    kind: "invoked";
+    triggerCharacter?: undefined;
+  }
+  /**
+   * Signals that the signature help request came from a user typing a character.
+   * Depending on the character and the syntactic context, the request may or may not be served a result.
+   */
+  interface SignatureHelpCharacterTypedReason {
+    kind: "characterTyped";
+    /**
+     * Character that was responsible for triggering signature help.
+     */
+    triggerCharacter: SignatureHelpTriggerCharacter;
+  }
+  /**
+   * Signals that this signature help request came from typing a character or moving the cursor.
+   * This should only occur if a signature help session was already active and the editor needs to see if it should adjust.
+   * The language service will unconditionally attempt to provide a result.
+   * `triggerCharacter` can be `undefined` for a retrigger caused by a cursor move.
+   */
+  interface SignatureHelpRetriggeredReason {
+    kind: "retrigger";
+    /**
+     * Character that was responsible for triggering signature help.
+     */
+    triggerCharacter?: SignatureHelpRetriggerCharacter;
+  }
+  /**
+   * Signature help request; value of command field is "signatureHelp".
+   * Given a file location (file, line, col), return the signature
+   * help.
+   */
+  interface SignatureHelpRequest extends FileLocationRequest {
+    command: CommandTypes.SignatureHelp;
+    arguments: SignatureHelpRequestArgs;
+  }
+  /**
+   * Response object for a SignatureHelpRequest.
+   */
+  interface SignatureHelpResponse extends Response {
+    body?: SignatureHelpItems;
+  }
+  /**
+   * Synchronous request for semantic diagnostics of one file.
+   */
+  interface SemanticDiagnosticsSyncRequest extends FileRequest {
+    command: CommandTypes.SemanticDiagnosticsSync;
+    arguments: SemanticDiagnosticsSyncRequestArgs;
+  }
+  interface SemanticDiagnosticsSyncRequestArgs extends FileRequestArgs {
+    includeLinePosition?: boolean;
+  }
+  /**
+   * Response object for synchronous sematic diagnostics request.
+   */
+  interface SemanticDiagnosticsSyncResponse extends Response {
+    body?: Diagnostic[] | DiagnosticWithLinePosition[];
+  }
+  interface SuggestionDiagnosticsSyncRequest extends FileRequest {
+    command: CommandTypes.SuggestionDiagnosticsSync;
+    arguments: SuggestionDiagnosticsSyncRequestArgs;
+  }
+  type SuggestionDiagnosticsSyncRequestArgs = SemanticDiagnosticsSyncRequestArgs;
+  type SuggestionDiagnosticsSyncResponse = SemanticDiagnosticsSyncResponse;
+  /**
+   * Synchronous request for syntactic diagnostics of one file.
+   */
+  interface SyntacticDiagnosticsSyncRequest extends FileRequest {
+    command: CommandTypes.SyntacticDiagnosticsSync;
+    arguments: SyntacticDiagnosticsSyncRequestArgs;
+  }
+  interface SyntacticDiagnosticsSyncRequestArgs extends FileRequestArgs {
+    includeLinePosition?: boolean;
+  }
+  /**
+   * Response object for synchronous syntactic diagnostics request.
+   */
+  interface SyntacticDiagnosticsSyncResponse extends Response {
+    body?: Diagnostic[] | DiagnosticWithLinePosition[];
+  }
+  /**
+   * Arguments for GeterrForProject request.
+   */
+  interface GeterrForProjectRequestArgs {
+    /**
+     * the file requesting project error list
+     */
+    file: string;
+    /**
+     * Delay in milliseconds to wait before starting to compute
+     * errors for the files in the file list
+     */
+    delay: number;
+  }
+  /**
+   * GeterrForProjectRequest request; value of command field is
+   * "geterrForProject". It works similarly with 'Geterr', only
+   * it request for every file in this project.
+   */
+  interface GeterrForProjectRequest extends Request {
+    command: CommandTypes.GeterrForProject;
+    arguments: GeterrForProjectRequestArgs;
+  }
+  /**
+   * Arguments for geterr messages.
+   */
+  interface GeterrRequestArgs {
+    /**
+     * List of file names for which to compute compiler errors.
+     * The files will be checked in list order.
+     */
+    files: string[];
+    /**
+     * Delay in milliseconds to wait before starting to compute
+     * errors for the files in the file list
+     */
+    delay: number;
+  }
+  /**
+   * Geterr request; value of command field is "geterr". Wait for
+   * delay milliseconds and then, if during the wait no change or
+   * reload messages have arrived for the first file in the files
+   * list, get the syntactic errors for the file, field requests,
+   * and then get the semantic errors for the file.  Repeat with a
+   * smaller delay for each subsequent file on the files list.  Best
+   * practice for an editor is to send a file list containing each
+   * file that is currently visible, in most-recently-used order.
+   */
+  interface GeterrRequest extends Request {
+    command: CommandTypes.Geterr;
+    arguments: GeterrRequestArgs;
+  }
+  type RequestCompletedEventName = "requestCompleted";
+  /**
+   * Event that is sent when server have finished processing request with specified id.
+   */
+  interface RequestCompletedEvent extends Event {
+    event: RequestCompletedEventName;
+    body: RequestCompletedEventBody;
+  }
+  interface RequestCompletedEventBody {
+    request_seq: number;
+  }
+  /**
+   * Item of diagnostic information found in a DiagnosticEvent message.
+   */
+  interface Diagnostic {
+    /**
+     * Starting file location at which text applies.
+     */
+    start: Location;
+    /**
+     * The last file location at which the text applies.
+     */
+    end: Location;
+    /**
+     * Text of diagnostic message.
+     */
+    text: string;
+    /**
+     * The category of the diagnostic message, e.g. "error", "warning", or "suggestion".
+     */
+    category: string;
+    reportsUnnecessary?: {};
+    /**
+     * Any related spans the diagnostic may have, such as other locations relevant to an error, such as declarartion sites
+     */
+    relatedInformation?: DiagnosticRelatedInformation[];
+    /**
+     * The error code of the diagnostic message.
+     */
+    code?: number;
+    /**
+     * The name of the plugin reporting the message.
+     */
+    source?: string;
+  }
+  interface DiagnosticWithFileName extends Diagnostic {
+    /**
+     * Name of the file the diagnostic is in
+     */
+    fileName: string;
+  }
+  /**
+   * Represents additional spans returned with a diagnostic which are relevant to it
+   */
+  interface DiagnosticRelatedInformation {
+    /**
+     * The category of the related information message, e.g. "error", "warning", or "suggestion".
+     */
+    category: string;
+    /**
+     * The code used ot identify the related information
+     */
+    code: number;
+    /**
+     * Text of related or additional information.
+     */
+    message: string;
+    /**
+     * Associated location
+     */
+    span?: FileSpan;
+  }
+  interface DiagnosticEventBody {
+    /**
+     * The file for which diagnostic information is reported.
+     */
+    file: string;
+    /**
+     * An array of diagnostic information items.
+     */
+    diagnostics: Diagnostic[];
+  }
+  type DiagnosticEventKind = "semanticDiag" | "syntaxDiag" | "suggestionDiag";
+  /**
+   * Event message for DiagnosticEventKind event types.
+   * These events provide syntactic and semantic errors for a file.
+   */
+  interface DiagnosticEvent extends Event {
+    body?: DiagnosticEventBody;
+  }
+  interface ConfigFileDiagnosticEventBody {
+    /**
+     * The file which trigged the searching and error-checking of the config file
+     */
+    triggerFile: string;
+    /**
+     * The name of the found config file.
+     */
+    configFile: string;
+    /**
+     * An arry of diagnostic information items for the found config file.
+     */
+    diagnostics: DiagnosticWithFileName[];
+  }
+  /**
+   * Event message for "configFileDiag" event type.
+   * This event provides errors for a found config file.
+   */
+  interface ConfigFileDiagnosticEvent extends Event {
+    body?: ConfigFileDiagnosticEventBody;
+    event: "configFileDiag";
+  }
+  type ProjectLanguageServiceStateEventName = "projectLanguageServiceState";
+  interface ProjectLanguageServiceStateEvent extends Event {
+    event: ProjectLanguageServiceStateEventName;
+    body?: ProjectLanguageServiceStateEventBody;
+  }
+  interface ProjectLanguageServiceStateEventBody {
+    /**
+     * Project name that has changes in the state of language service.
+     * For configured projects this will be the config file path.
+     * For external projects this will be the name of the projects specified when project was open.
+     * For inferred projects this event is not raised.
+     */
+    projectName: string;
+    /**
+     * True if language service state switched from disabled to enabled
+     * and false otherwise.
+     */
+    languageServiceEnabled: boolean;
+  }
+  type ProjectsUpdatedInBackgroundEventName = "projectsUpdatedInBackground";
+  interface ProjectsUpdatedInBackgroundEvent extends Event {
+    event: ProjectsUpdatedInBackgroundEventName;
+    body: ProjectsUpdatedInBackgroundEventBody;
+  }
+  interface ProjectsUpdatedInBackgroundEventBody {
+    /**
+     * Current set of open files
+     */
+    openFiles: string[];
+  }
+  /**
+   * Arguments for reload request.
+   */
+  interface ReloadRequestArgs extends FileRequestArgs {
+    /**
+     * Name of temporary file from which to reload file
+     * contents. May be same as file.
+     */
+    tmpfile: string;
+  }
+  /**
+   * Reload request message; value of command field is "reload".
+   * Reload contents of file with name given by the 'file' argument
+   * from temporary file with name given by the 'tmpfile' argument.
+   * The two names can be identical.
+   */
+  interface ReloadRequest extends FileRequest {
+    command: CommandTypes.Reload;
+    arguments: ReloadRequestArgs;
+  }
+  /**
+   * Response to "reload" request. This is just an acknowledgement, so
+   * no body field is required.
+   */
+  interface ReloadResponse extends Response {
+  }
+  /**
+   * Arguments for saveto request.
+   */
+  interface SavetoRequestArgs extends FileRequestArgs {
+    /**
+     * Name of temporary file into which to save server's view of
+     * file contents.
+     */
+    tmpfile: string;
+  }
+  /**
+   * Saveto request message; value of command field is "saveto".
+   * For debugging purposes, save to a temporaryfile (named by
+   * argument 'tmpfile') the contents of file named by argument
+   * 'file'.  The server does not currently send a response to a
+   * "saveto" request.
+   */
+  interface SavetoRequest extends FileRequest {
+    command: CommandTypes.Saveto;
+    arguments: SavetoRequestArgs;
+  }
+  /**
+   * Arguments for navto request message.
+   */
+  interface NavtoRequestArgs extends FileRequestArgs {
+    /**
+     * Search term to navigate to from current location; term can
+     * be '.*' or an identifier prefix.
+     */
+    searchValue: string;
+    /**
+     *  Optional limit on the number of items to return.
+     */
+    maxResultCount?: number;
+    /**
+     * Optional flag to indicate we want results for just the current file
+     * or the entire project.
+     */
+    currentFileOnly?: boolean;
+    projectFileName?: string;
+  }
+  /**
+   * Navto request message; value of command field is "navto".
+   * Return list of objects giving file locations and symbols that
+   * match the search term given in argument 'searchTerm'.  The
+   * context for the search is given by the named file.
+   */
+  interface NavtoRequest extends FileRequest {
+    command: CommandTypes.Navto;
+    arguments: NavtoRequestArgs;
+  }
+  /**
+   * An item found in a navto response.
+   */
+  interface NavtoItem extends FileSpan {
+    /**
+     * The symbol's name.
+     */
+    name: string;
+    /**
+     * The symbol's kind (such as 'className' or 'parameterName').
+     */
+    kind: ScriptElementKind;
+    /**
+     * exact, substring, or prefix.
+     */
+    matchKind: string;
+    /**
+     * If this was a case sensitive or insensitive match.
+     */
+    isCaseSensitive: boolean;
+    /**
+     * Optional modifiers for the kind (such as 'public').
+     */
+    kindModifiers?: string;
+    /**
+     * Name of symbol's container symbol (if any); for example,
+     * the class name if symbol is a class member.
+     */
+    containerName?: string;
+    /**
+     * Kind of symbol's container symbol (if any).
+     */
+    containerKind?: ScriptElementKind;
+  }
+  /**
+   * Navto response message. Body is an array of navto items.  Each
+   * item gives a symbol that matched the search term.
+   */
+  interface NavtoResponse extends Response {
+    body?: NavtoItem[];
+  }
+  /**
+   * Arguments for change request message.
+   */
+  interface ChangeRequestArgs extends FormatRequestArgs {
+    /**
+     * Optional string to insert at location (file, line, offset).
+     */
+    insertString?: string;
+  }
+  /**
+   * Change request message; value of command field is "change".
+   * Update the server's view of the file named by argument 'file'.
+   * Server does not currently send a response to a change request.
+   */
+  interface ChangeRequest extends FileLocationRequest {
+    command: CommandTypes.Change;
+    arguments: ChangeRequestArgs;
+  }
+  /**
+   * Response to "brace" request.
+   */
+  interface BraceResponse extends Response {
+    body?: TextSpan[];
+  }
+  /**
+   * Brace matching request; value of command field is "brace".
+   * Return response giving the file locations of matching braces
+   * found in file at location line, offset.
+   */
+  interface BraceRequest extends FileLocationRequest {
+    command: CommandTypes.Brace;
+  }
+  /**
+   * NavBar items request; value of command field is "navbar".
+   * Return response giving the list of navigation bar entries
+   * extracted from the requested file.
+   */
+  interface NavBarRequest extends FileRequest {
+    command: CommandTypes.NavBar;
+  }
+  /**
+   * NavTree request; value of command field is "navtree".
+   * Return response giving the navigation tree of the requested file.
+   */
+  interface NavTreeRequest extends FileRequest {
+    command: CommandTypes.NavTree;
+  }
+  interface NavigationBarItem {
+    /**
+     * The item's display text.
+     */
+    text: string;
+    /**
+     * The symbol's kind (such as 'className' or 'parameterName').
+     */
+    kind: ScriptElementKind;
+    /**
+     * Optional modifiers for the kind (such as 'public').
+     */
+    kindModifiers?: string;
+    /**
+     * The definition locations of the item.
+     */
+    spans: TextSpan[];
+    /**
+     * Optional children.
+     */
+    childItems?: NavigationBarItem[];
+    /**
+     * Number of levels deep this item should appear.
+     */
+    indent: number;
+  }
+  /** protocol.NavigationTree is identical to ts.NavigationTree, except using protocol.TextSpan instead of ts.TextSpan */
+  interface NavigationTree {
+    text: string;
+    kind: ScriptElementKind;
+    kindModifiers: string;
+    spans: TextSpan[];
+    nameSpan: TextSpan | undefined;
+    childItems?: NavigationTree[];
+  }
+  type TelemetryEventName = "telemetry";
+  interface TelemetryEvent extends Event {
+    event: TelemetryEventName;
+    body: TelemetryEventBody;
+  }
+  interface TelemetryEventBody {
+    telemetryEventName: string;
+    payload: any;
+  }
+  type TypesInstallerInitializationFailedEventName = "typesInstallerInitializationFailed";
+  interface TypesInstallerInitializationFailedEvent extends Event {
+    event: TypesInstallerInitializationFailedEventName;
+    body: TypesInstallerInitializationFailedEventBody;
+  }
+  interface TypesInstallerInitializationFailedEventBody {
+    message: string;
+  }
+  type TypingsInstalledTelemetryEventName = "typingsInstalled";
+  interface TypingsInstalledTelemetryEventBody extends TelemetryEventBody {
+    telemetryEventName: TypingsInstalledTelemetryEventName;
+    payload: TypingsInstalledTelemetryEventPayload;
+  }
+  interface TypingsInstalledTelemetryEventPayload {
+    /**
+     * Comma separated list of installed typing packages
+     */
+    installedPackages: string;
+    /**
+     * true if install request succeeded, otherwise - false
+     */
+    installSuccess: boolean;
+    /**
+     * version of typings installer
+     */
+    typingsInstallerVersion: string;
+  }
+  type BeginInstallTypesEventName = "beginInstallTypes";
+  type EndInstallTypesEventName = "endInstallTypes";
+  interface BeginInstallTypesEvent extends Event {
+    event: BeginInstallTypesEventName;
+    body: BeginInstallTypesEventBody;
+  }
+  interface EndInstallTypesEvent extends Event {
+    event: EndInstallTypesEventName;
+    body: EndInstallTypesEventBody;
+  }
+  interface InstallTypesEventBody {
+    /**
+     * correlation id to match begin and end events
+     */
+    eventId: number;
+    /**
+     * list of packages to install
+     */
+    packages: ReadonlyArray<string>;
+  }
+  interface BeginInstallTypesEventBody extends InstallTypesEventBody {
+  }
+  interface EndInstallTypesEventBody extends InstallTypesEventBody {
+    /**
+     * true if installation succeeded, otherwise false
+     */
+    success: boolean;
+  }
+  interface NavBarResponse extends Response {
+    body?: NavigationBarItem[];
+  }
+  interface NavTreeResponse extends Response {
+    body?: NavigationTree;
+  }
+  const enum IndentStyle {
+    None = "None",
+    Block = "Block",
+    Smart = "Smart"
+  }
+  interface EditorSettings {
+    baseIndentSize?: number;
+    indentSize?: number;
+    tabSize?: number;
+    newLineCharacter?: string;
+    convertTabsToSpaces?: boolean;
+    indentStyle?: IndentStyle | ts.IndentStyle;
+  }
+  interface FormatCodeSettings extends EditorSettings {
+    insertSpaceAfterCommaDelimiter?: boolean;
+    insertSpaceAfterSemicolonInForStatements?: boolean;
+    insertSpaceBeforeAndAfterBinaryOperators?: boolean;
+    insertSpaceAfterConstructor?: boolean;
+    insertSpaceAfterKeywordsInControlFlowStatements?: boolean;
+    insertSpaceAfterFunctionKeywordForAnonymousFunctions?: boolean;
+    insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis?: boolean;
+    insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets?: boolean;
+    insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces?: boolean;
+    insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces?: boolean;
+    insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces?: boolean;
+    insertSpaceAfterTypeAssertion?: boolean;
+    insertSpaceBeforeFunctionParenthesis?: boolean;
+    placeOpenBraceOnNewLineForFunctions?: boolean;
+    placeOpenBraceOnNewLineForControlBlocks?: boolean;
+    insertSpaceBeforeTypeAnnotation?: boolean;
+  }
+  interface UserPreferences {
+    readonly disableSuggestions?: boolean;
+    readonly quotePreference?: "double" | "single";
+    /**
+     * If enabled, TypeScript will search through all external modules' exports and add them to the completions list.
+     * This affects lone identifier completions but not completions on the right hand side of `obj.`.
+     */
+    readonly includeCompletionsForModuleExports?: boolean;
+    /**
+     * If enabled, the completion list will include completions with invalid identifier names.
+     * For those entries, The `insertText` and `replacementSpan` properties will be set to change from `.x` property access to `["x"]`.
+     */
+    readonly includeCompletionsWithInsertText?: boolean;
+    readonly importModuleSpecifierPreference?: "relative" | "non-relative";
+    readonly allowTextChangesInNewFiles?: boolean;
+  }
+  interface CompilerOptions {
+    allowJs?: boolean;
+    allowSyntheticDefaultImports?: boolean;
+    allowUnreachableCode?: boolean;
+    allowUnusedLabels?: boolean;
+    alwaysStrict?: boolean;
+    baseUrl?: string;
+    charset?: string;
+    checkJs?: boolean;
+    declaration?: boolean;
+    declarationDir?: string;
+    disableSizeLimit?: boolean;
+    downlevelIteration?: boolean;
+    emitBOM?: boolean;
+    emitDecoratorMetadata?: boolean;
+    experimentalDecorators?: boolean;
+    forceConsistentCasingInFileNames?: boolean;
+    importHelpers?: boolean;
+    inlineSourceMap?: boolean;
+    inlineSources?: boolean;
+    isolatedModules?: boolean;
+    jsx?: JsxEmit | ts.JsxEmit;
+    lib?: string[];
+    locale?: string;
+    mapRoot?: string;
+    maxNodeModuleJsDepth?: number;
+    module?: ModuleKind | ts.ModuleKind;
+    moduleResolution?: ModuleResolutionKind | ts.ModuleResolutionKind;
+    newLine?: NewLineKind | ts.NewLineKind;
+    noEmit?: boolean;
+    noEmitHelpers?: boolean;
+    noEmitOnError?: boolean;
+    noErrorTruncation?: boolean;
+    noFallthroughCasesInSwitch?: boolean;
+    noImplicitAny?: boolean;
+    noImplicitReturns?: boolean;
+    noImplicitThis?: boolean;
+    noUnusedLocals?: boolean;
+    noUnusedParameters?: boolean;
+    noImplicitUseStrict?: boolean;
+    noLib?: boolean;
+    noResolve?: boolean;
+    out?: string;
+    outDir?: string;
+    outFile?: string;
+    paths?: MapLike<string[]>;
+    plugins?: PluginImport[];
+    preserveConstEnums?: boolean;
+    preserveSymlinks?: boolean;
+    project?: string;
+    reactNamespace?: string;
+    removeComments?: boolean;
+    references?: ProjectReference[];
+    rootDir?: string;
+    rootDirs?: string[];
+    skipLibCheck?: boolean;
+    skipDefaultLibCheck?: boolean;
+    sourceMap?: boolean;
+    sourceRoot?: string;
+    strict?: boolean;
+    strictNullChecks?: boolean;
+    suppressExcessPropertyErrors?: boolean;
+    suppressImplicitAnyIndexErrors?: boolean;
+    target?: ScriptTarget | ts.ScriptTarget;
+    traceResolution?: boolean;
+    resolveJsonModule?: boolean;
+    types?: string[];
+    /** Paths used to used to compute primary types search locations */
+    typeRoots?: string[];
+    [option: string]: CompilerOptionsValue | undefined;
+  }
+  const enum JsxEmit {
+    None = "None",
+    Preserve = "Preserve",
+    ReactNative = "ReactNative",
+    React = "React"
+  }
+  const enum ModuleKind {
+    None = "None",
+    CommonJS = "CommonJS",
+    AMD = "AMD",
+    UMD = "UMD",
+    System = "System",
+    ES6 = "ES6",
+    ES2015 = "ES2015",
+    ESNext = "ESNext"
+  }
+  const enum ModuleResolutionKind {
+    Classic = "Classic",
+    Node = "Node"
+  }
+  const enum NewLineKind {
+    Crlf = "Crlf",
+    Lf = "Lf"
+  }
+  const enum ScriptTarget {
+    ES3 = "ES3",
+    ES5 = "ES5",
+    ES6 = "ES6",
+    ES2015 = "ES2015",
+    ES2016 = "ES2016",
+    ES2017 = "ES2017",
+    ESNext = "ESNext"
+  }
+}
+declare namespace ts.server.protocol {
+
+  interface TextInsertion {
+    newText: string;
+    /** The position in newText the caret should point to after the insertion. */
+    caretOffset: number;
+  }
+
+  interface TodoCommentDescriptor {
+    text: string;
+    priority: number;
+  }
+
+  interface TodoComment {
+    descriptor: TodoCommentDescriptor;
+    message: string;
+    position: number;
+  }
+
+  enum OutliningSpanKind {
+    /** Single or multi-line comments */
+    Comment = "comment",
+    /** Sections marked by '// #region' and '// #endregion' comments */
+    Region = "region",
+    /** Declarations and expressions */
+    Code = "code",
+    /** Contiguous blocks of import declarations */
+    Imports = "imports"
+  }
+
+  enum HighlightSpanKind {
+    none = "none",
+    definition = "definition",
+    reference = "reference",
+    writtenReference = "writtenReference"
+  }
+
+  enum ScriptElementKind {
+    unknown = "",
+    warning = "warning",
+    /** predefined type (void) or keyword (class) */
+    keyword = "keyword",
+    /** top level script node */
+    scriptElement = "script",
+    /** module foo {} */
+    moduleElement = "module",
+    /** class X {} */
+    classElement = "class",
+    /** var x = class X {} */
+    localClassElement = "local class",
+    /** interface Y {} */
+    interfaceElement = "interface",
+    /** type T = ... */
+    typeElement = "type",
+    /** enum E */
+    enumElement = "enum",
+    enumMemberElement = "enum member",
+    /**
+     * Inside module and script only
+     * const v = ..
+     */
+    variableElement = "var",
+    /** Inside function */
+    localVariableElement = "local var",
+    /**
+     * Inside module and script only
+     * function f() { }
+     */
+    functionElement = "function",
+    /** Inside function */
+    localFunctionElement = "local function",
+    /** class X { [public|private]* foo() {} } */
+    memberFunctionElement = "method",
+    /** class X { [public|private]* [get|set] foo:number; } */
+    memberGetAccessorElement = "getter",
+    memberSetAccessorElement = "setter",
+    /**
+     * class X { [public|private]* foo:number; }
+     * interface Y { foo:number; }
+     */
+    memberVariableElement = "property",
+    /** class X { constructor() { } } */
+    constructorImplementationElement = "constructor",
+    /** interface Y { ():number; } */
+    callSignatureElement = "call",
+    /** interface Y { []:number; } */
+    indexSignatureElement = "index",
+    /** interface Y { new():Y; } */
+    constructSignatureElement = "construct",
+    /** function foo(*Y*: string) */
+    parameterElement = "parameter",
+    typeParameterElement = "type parameter",
+    primitiveType = "primitive type",
+    label = "label",
+    alias = "alias",
+    constElement = "const",
+    letElement = "let",
+    directory = "directory",
+    externalModuleName = "external module name",
+    /**
+     * <JsxTagName attribute1 attribute2={0} />
+     */
+    jsxAttribute = "JSX attribute",
+    /** String literal */
+    string = "string"
+  }
+
+  interface TypeAcquisition {
+    enableAutoDiscovery?: boolean;
+    enable?: boolean;
+    include?: string[];
+    exclude?: string[];
+    [option: string]: string[] | boolean | undefined;
+  }
+
+  interface FileExtensionInfo {
+    extension: string;
+    isMixedContent: boolean;
+    scriptKind?: ScriptKind;
+  }
+
+  interface JSDocTagInfo {
+    name: string;
+    text?: string;
+  }
+
+  /**
+   * Type of objects whose values are all of the same type.
+   * The `in` and `for-in` operators can *not* be safely used,
+   * since `Object.prototype` may be modified by outside code.
+   */
+  interface MapLike<T> {
+    [index: string]: T;
+  }
+
+  interface PluginImport {
+    name: string;
+  }
+
+  interface ProjectReference {
+    /** A normalized path on disk */
+    path: string;
+    /** The path as the user originally wrote it */
+    originalPath?: string;
+    /** True if the output of this reference should be prepended to the output of this project. Only valid for --outFile compilations */
+    prepend?: boolean;
+    /** True if it is intended that this reference form a circularity */
+    circular?: boolean;
+  }
+
+  type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike<string[]> | PluginImport[] | ProjectReference[] | null | undefined;
+}
+declare namespace ts {
+  // these types are empty stubs for types from services and should not be used directly
+  export type ScriptKind = never;
+  export type IndentStyle = never;
+  export type JsxEmit = never;
+  export type ModuleKind = never;
+  export type ModuleResolutionKind = never;
+  export type NewLineKind = never;
+  export type ScriptTarget = never;
+}
+import protocol = ts.server.protocol;
+export = protocol;
+export as namespace protocol;
diff --git a/src/server/schema.json b/src/server/schema.json
new file mode 100644
index 0000000..fb28acd
--- /dev/null
+++ b/src/server/schema.json
@@ -0,0 +1,298 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema",
+  "properties": {
+    "tsserver.enable": {
+      "type": "boolean",
+      "default": true,
+      "description": "Enable tsserver extension"
+    },
+    "tsserver.locale": {
+      "type": "string",
+      "default": "",
+      "description": "Locale of tsserver"
+    },
+    "tsserver.typingsCacheLocation": {
+      "type": "string",
+      "default": "",
+      "description": "Folder path for cache typings"
+    },
+    "tsserver.formatOnSave": {
+      "type": "boolean",
+      "default": false,
+      "description": "Format document on buffer will save"
+    },
+    "tsserver.orgnizeImportOnSave": {
+      "type": "boolean",
+      "default": false,
+      "description": "Orgnize import on buffer will save"
+    },
+    "tsserver.formatOnType": {
+      "type": "boolean",
+      "default": true,
+      "description": "Run format on type special characters."
+    },
+    "tsserver.enableJavascript": {
+      "type": "boolean",
+      "default": true,
+      "description": "Use tsserver for javascript files"
+    },
+    "tsserver.tsdk": {
+      "type": "string",
+      "default": "",
+      "description": "Directory contains tsserver.js, works for workspace only"
+    },
+    "tsserver.npm": {
+      "type": "string",
+      "default": "",
+      "description": "Executable path of npm for download typings"
+    },
+    "tsserver.log": {
+      "type": "string",
+      "default": "off",
+      "enum": ["normal", "terse", "verbose", "off"],
+      "description": "Log level of tsserver"
+    },
+    "tsserver.trace.server": {
+      "type": "string",
+      "default": "off",
+      "enum": ["off", "messages", "verbose"],
+      "description": "Trace level of tsserver"
+    },
+    "tserver.pluginNames": {
+      "type": "array",
+      "default": [],
+      "items": {
+        "type": "string"
+      },
+      "description": "Module names of tsserver plugins"
+    },
+    "tsserver.pluginRoot": {
+      "type": "string",
+      "default": "",
+      "description": "Folder contains tsserver plugins"
+    },
+    "tsserver.debugPort": {
+      "type": "number",
+      "description": "Debug port number of tsserver"
+    },
+    "tsserver.reportStyleChecksAsWarnings": {
+      "type": "boolean",
+      "default": true
+    },
+    "tsserver.implicitProjectConfig.checkJs": {
+      "type": "boolean",
+      "default": false,
+      "description": "Enable checkJs for implicit project"
+    },
+    "tsserver.implicitProjectConfig.experimentalDecorators": {
+      "type": "boolean",
+      "default": false,
+      "description": "Enable experimentalDecorators for implicit project"
+    },
+    "tsserver.disableAutomaticTypeAcquisition": {
+      "type": "boolean",
+      "default": false,
+      "description": "Disable download of typings"
+    },
+    "typescript.updateImportsOnFileMove.enable": {
+      "type": "boolean",
+      "default": true,
+      "description": "Enable update imports on file move."
+    },
+    "typescript.implementationsCodeLens.enable": {
+      "type": "boolean",
+      "default": true,
+      "description": "Enable codeLens for implementations"
+    },
+    "typescript.referencesCodeLens.enable": {
+      "type": "boolean",
+      "default": true,
+      "description": "Enable codeLens for references"
+    },
+    "typescript.preferences.completion.useCodeSnippetsOnMethodSuggest": {
+      "type": "boolean",
+      "default": true,
+      "description": "Enable snippet for method suggestion"
+    },
+    "typescript.preferences.completion.nameSuggestions": {
+      "type": "boolean",
+      "default": true,
+      "description": "Complete for warning type of tsserver"
+    },
+    "typescript.preferences.completion.autoImportSuggestions": {
+      "type": "boolean",
+      "default": true,
+      "description": "Enable auto import suggestions for completion"
+    },
+    "typescript.preferences.completion.commaAfterImport": {
+      "type": "boolean",
+      "default": true,
+      "description": "Add comma after import"
+    },
+    "typescript.preferences.completion.moduleExports": {
+      "type": "boolean",
+      "default": true,
+      "description": "Include completion for module.exports"
+    },
+    "typescript.preferences.importModuleSpecifier": {
+      "type": "string",
+      "default": "non-relative",
+      "enum": ["non-relative", "relative"]
+    },
+    "typescript.preferences.suggestionActions.enabled": {
+      "type": "boolean",
+      "default": true
+    },
+    "typescript.preferences.quoteStyle": {
+      "type": "string",
+      "default": "single",
+      "enum": ["single", "double"]
+    },
+    "typescript.format.insertSpaceAfterCommaDelimiter": {
+      "type": "boolean",
+      "default": true
+    },
+    "typescript.format.insertSpaceAfterConstructor": {
+      "type": "boolean",
+      "default": false
+    },
+    "typescript.format.insertSpaceAfterSemicolonInForStatements": {
+      "type": "boolean",
+      "default": true
+    },
+    "typescript.format.insertSpaceBeforeAndAfterBinaryOperators": {
+      "type": "boolean",
+      "default": true
+    },
+    "typescript.format.insertSpaceAfterKeywordsInControlFlowStatements": {
+      "type": "boolean",
+      "default": true
+    },
+    "typescript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": {
+      "type": "boolean",
+      "default": true
+    },
+    "typescript.format.insertSpaceBeforeFunctionParenthesis": {
+      "type": "boolean",
+      "default": false
+    },
+    "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": {
+      "type": "boolean",
+      "default": false
+    },
+    "typescript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": {
+      "type": "boolean",
+      "default": false
+    },
+    "typescript.format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": {
+      "type": "boolean",
+      "default": false
+    },
+    "typescript.format.insertSpaceAfterTypeAssertion": {
+      "type": "boolean",
+      "default": false
+    },
+    "typescript.format.placeOpenBraceOnNewLineForFunctions": {
+      "type": "boolean",
+      "default": false
+    },
+    "typescript.format.placeOpenBraceOnNewLineForControlBlocks": {
+      "type": "boolean",
+      "default": false
+    },
+    "javascript.updateImportsOnFileMove.enable": {
+      "type": "boolean",
+      "default": true
+    },
+    "javascript.implementationsCodeLens.enable": {
+      "type": "boolean",
+      "default": true
+    },
+    "javascript.referencesCodeLens.enable": {
+      "type": "boolean",
+      "default": true
+    },
+    "javascript.preferences.completion.useCodeSnippetsOnMethodSuggest": {
+      "type": "boolean",
+      "default": true
+    },
+    "javascript.preferences.completion.nameSuggestions": {
+      "type": "boolean",
+      "default": true
+    },
+    "javascript.preferences.completion.autoImportSuggestions": {
+      "type": "boolean",
+      "default": true
+    },
+    "javascript.preferences.importModuleSpecifier": {
+      "type": "string",
+      "default": "non-relative",
+      "enum": ["non-relative", "relative"]
+    },
+    "javascript.preferences.suggestionActions.enabled": {
+      "type": "boolean",
+      "default": true
+    },
+    "javascript.preferences.completion.commaAfterImport": {
+      "type": "boolean",
+      "default": true
+    },
+    "javascript.preferences.quoteStyle": {
+      "type": "string",
+      "default": "single",
+      "enum": ["single", "double"]
+    },
+    "javascript.format.insertSpaceAfterCommaDelimiter": {
+      "type": "boolean",
+      "default": true
+    },
+    "javascript.format.insertSpaceAfterConstructor": {
+      "type": "boolean",
+      "default": false
+    },
+    "javascript.format.insertSpaceAfterSemicolonInForStatements": {
+      "type": "boolean",
+      "default": true
+    },
+    "javascript.format.insertSpaceBeforeAndAfterBinaryOperators": {
+      "type": "boolean",
+      "default": true
+    },
+    "javascript.format.insertSpaceAfterKeywordsInControlFlowStatements": {
+      "type": "boolean",
+      "default": true
+    },
+    "javascript.format.insertSpaceAfterFunctionKeywordForAnonymousFunctions": {
+      "type": "boolean",
+      "default": true
+    },
+    "javascript.format.insertSpaceBeforeFunctionParenthesis": {
+      "type": "boolean",
+      "default": false
+    },
+    "javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": {
+      "type": "boolean",
+      "default": false
+    },
+    "javascript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": {
+      "type": "boolean",
+      "default": false
+    },
+    "javascript.format.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": {
+      "type": "boolean",
+      "default": false
+    },
+    "javascript.format.insertSpaceAfterTypeAssertion": {
+      "type": "boolean",
+      "default": false
+    },
+    "javascript.format.placeOpenBraceOnNewLineForFunctions": {
+      "type": "boolean",
+      "default": false
+    },
+    "javascript.format.placeOpenBraceOnNewLineForControlBlocks": {
+      "type": "boolean",
+      "default": false
+    }
+  }
+}
diff --git a/src/server/typescriptService.ts b/src/server/typescriptService.ts
new file mode 100644
index 0000000..a400012
--- /dev/null
+++ b/src/server/typescriptService.ts
@@ -0,0 +1,215 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { CancellationToken, Event } from 'vscode-languageserver-protocol'
+import Uri from 'vscode-uri'
+import * as Proto from './protocol'
+import API from './utils/api'
+import { TypeScriptServiceConfiguration } from './utils/configuration'
+import Logger from './utils/logger'
+
+export interface TypeScriptServerPlugin {
+  readonly path: string
+  readonly name: string
+  readonly languages: string[]
+}
+
+export interface ITypeScriptServiceClient {
+  apiVersion: API
+  configuration: TypeScriptServiceConfiguration
+  onTsServerStarted: Event<API>
+  onProjectLanguageServiceStateChanged: Event<Proto.ProjectLanguageServiceStateEventBody>
+  onDidBeginInstallTypings: Event<Proto.BeginInstallTypesEventBody>
+  onDidEndInstallTypings: Event<Proto.EndInstallTypesEventBody>
+  onTypesInstallerInitializationFailed: Event<Proto.TypesInstallerInitializationFailedEventBody>
+  readonly logger: Logger
+
+  normalizePath(resource: Uri): string | null
+  asUrl(filepath: string): Uri
+  toPath(uri: string): string
+  toResource(path: string): string
+
+  execute(
+    command: 'configure',
+    args: Proto.ConfigureRequestArguments,
+    token?: CancellationToken
+  ): Promise<Proto.ConfigureResponse>
+  execute(
+    command: 'open',
+    args: Proto.OpenRequestArgs,
+    expectedResult: boolean,
+    token?: CancellationToken
+  ): Promise<any>
+  execute(
+    command: 'close',
+    args: Proto.FileRequestArgs,
+    expectedResult: boolean,
+    token?: CancellationToken
+  ): Promise<any>
+  execute(
+    command: 'change',
+    args: Proto.ChangeRequestArgs,
+    expectedResult: boolean,
+    token?: CancellationToken
+  ): Promise<any>
+  execute(
+    command: 'geterr',
+    args: Proto.GeterrRequestArgs,
+    expectedResult: boolean,
+    token?: CancellationToken
+  ): Promise<any>
+  execute(
+    command: 'geterrForProject',
+    args: Proto.GeterrForProjectRequestArgs,
+    token?: CancellationToken
+  ): Promise<any>
+  execute(
+    command: 'quickinfo',
+    args: Proto.FileLocationRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.QuickInfoResponse>
+  execute(
+    command: 'completions',
+    args: Proto.CompletionsRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.CompletionsResponse> // tslint:disable-line
+  execute(
+    command: 'completionEntryDetails',
+    args: Proto.CompletionDetailsRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.CompletionDetailsResponse>
+  execute(
+    command: 'signatureHelp',
+    args: Proto.SignatureHelpRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.SignatureHelpResponse>
+  execute(
+    command: 'definition',
+    args: Proto.FileLocationRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.DefinitionResponse>
+  execute(
+    command: 'implementation',
+    args: Proto.FileLocationRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.ImplementationResponse>
+  execute(
+    command: 'typeDefinition',
+    args: Proto.FileLocationRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.TypeDefinitionResponse>
+  execute(
+    command: 'references',
+    args: Proto.FileLocationRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.ReferencesResponse>
+  execute(
+    command: 'navto',
+    args: Proto.NavtoRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.NavtoResponse>
+  execute(
+    command: 'navbar',
+    args: Proto.FileRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.NavBarResponse>
+  execute(
+    command: 'format',
+    args: Proto.FormatRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.FormatResponse>
+  execute(
+    command: 'formatonkey',
+    args: Proto.FormatOnKeyRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.FormatResponse>
+  execute(
+    command: 'rename',
+    args: Proto.RenameRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.RenameResponse>
+  execute(
+    command: 'projectInfo',
+    args: Proto.ProjectInfoRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.ProjectInfoResponse>
+  execute(
+    command: 'reloadProjects',
+    args: any,
+    expectedResult: boolean,
+    token?: CancellationToken
+  ): Promise<any>
+  execute(
+    command: 'reload',
+    args: Proto.ReloadRequestArgs,
+    expectedResult: boolean,
+    token?: CancellationToken
+  ): Promise<any>
+  execute(
+    command: 'compilerOptionsForInferredProjects',
+    args: Proto.SetCompilerOptionsForInferredProjectsArgs,
+    token?: CancellationToken
+  ): Promise<any>
+  execute(
+    command: 'navtree',
+    args: Proto.FileRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.NavTreeResponse>
+  execute(
+    command: 'getCodeFixes',
+    args: Proto.CodeFixRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.GetCodeFixesResponse>
+  execute(
+    command: 'getSupportedCodeFixes',
+    args: null,
+    token?: CancellationToken
+  ): Promise<Proto.GetSupportedCodeFixesResponse>
+  execute(
+    command: 'getCombinedCodeFix',
+    args: Proto.GetCombinedCodeFixRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.GetCombinedCodeFixResponse>
+  execute(
+    command: 'docCommentTemplate',
+    args: Proto.FileLocationRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.DocCommandTemplateResponse>
+  execute(
+    command: 'getApplicableRefactors',
+    args: Proto.GetApplicableRefactorsRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.GetApplicableRefactorsResponse>
+  execute(
+    command: 'getEditsForRefactor',
+    args: Proto.GetEditsForRefactorRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.GetEditsForRefactorResponse>
+  execute(
+    command: 'getEditsForFileRename',
+    args: Proto.GetEditsForFileRenameRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.GetEditsForFileRenameResponse>
+  execute(
+    command: 'applyCodeActionCommand',
+    args: Proto.ApplyCodeActionCommandRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.ApplyCodeActionCommandResponse>
+  execute(
+    command: 'organizeImports',
+    args: Proto.OrganizeImportsRequestArgs,
+    token?: CancellationToken
+  ): Promise<Proto.OrganizeImportsResponse>
+  execute(
+    command: 'getOutliningSpans',
+    args: Proto.FileRequestArgs,
+    token: CancellationToken
+  ): Promise<Proto.OutliningSpansResponse>
+  execute(
+    command: string,
+    args: any,
+    expectedResult: boolean | CancellationToken,
+    token?: CancellationToken
+  ): Promise<any>
+}
diff --git a/src/server/typescriptServiceClient.ts b/src/server/typescriptServiceClient.ts
new file mode 100644
index 0000000..8717e7b
--- /dev/null
+++ b/src/server/typescriptServiceClient.ts
@@ -0,0 +1,834 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import cp from 'child_process'
+import fs from 'fs'
+import os from 'os'
+import path from 'path'
+import { CancellationToken, Disposable, Emitter, Event } from 'vscode-languageserver-protocol'
+import Uri from 'vscode-uri'
+import which from 'which'
+import { DiagnosticKind, ServiceStat, workspace, disposeAll } from 'coc.nvim'
+import FileConfigurationManager from './features/fileConfigurationManager'
+import * as Proto from './protocol'
+import { ITypeScriptServiceClient } from './typescriptService'
+import API from './utils/api'
+import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration'
+import Logger from './utils/logger'
+import { fork, getTempFile, IForkOptions, makeRandomHexString } from './utils/process'
+import Tracer from './utils/tracer'
+import { inferredProjectConfig } from './utils/tsconfig'
+import { TypeScriptVersion, TypeScriptVersionProvider } from './utils/versionProvider'
+import { ICallback, Reader } from './utils/wireProtocol'
+
+interface CallbackItem {
+  c: (value: any) => void
+  e: (err: any) => void
+  start: number
+}
+
+class CallbackMap {
+  private readonly callbacks: Map<number, CallbackItem> = new Map()
+  public pendingResponses = 0
+
+  public destroy(e: any): void {
+    for (const callback of this.callbacks.values()) {
+      callback.e(e)
+    }
+    this.callbacks.clear()
+    this.pendingResponses = 0
+  }
+
+  public add(seq: number, callback: CallbackItem): void {
+    this.callbacks.set(seq, callback)
+    ++this.pendingResponses
+  }
+
+  public fetch(seq: number): CallbackItem | undefined {
+    const callback = this.callbacks.get(seq)
+    this.delete(seq)
+    return callback
+  }
+
+  private delete(seq: number): void {
+    if (this.callbacks.delete(seq)) {
+      --this.pendingResponses
+    }
+  }
+}
+
+interface RequestItem {
+  request: Proto.Request
+  callbacks: CallbackItem | null
+}
+
+class RequestQueue {
+  private queue: RequestItem[] = []
+  private sequenceNumber = 0
+
+  public get length(): number {
+    return this.queue.length
+  }
+
+  public push(item: RequestItem): void {
+    this.queue.push(item)
+  }
+
+  public shift(): RequestItem | undefined {
+    return this.queue.shift()
+  }
+
+  public tryCancelPendingRequest(seq: number): boolean {
+    for (let i = 0; i < this.queue.length; i++) {
+      if (this.queue[i].request.seq === seq) {
+        this.queue.splice(i, 1)
+        return true
+      }
+    }
+    return false
+  }
+
+  public createRequest(command: string, args: any): Proto.Request {
+    return {
+      seq: this.sequenceNumber++,
+      type: 'request',
+      command,
+      arguments: args
+    }
+  }
+}
+
+class ForkedTsServerProcess {
+  constructor(private childProcess: cp.ChildProcess) { }
+
+  public onError(cb: (err: Error) => void): void {
+    this.childProcess.on('error', cb)
+  }
+
+  public onExit(cb: (err: any) => void): void {
+    this.childProcess.on('exit', cb)
+  }
+
+  public write(serverRequest: Proto.Request): void {
+    this.childProcess.stdin.write(
+      JSON.stringify(serverRequest) + '\r\n',
+      'utf8'
+    )
+  }
+
+  public createReader(
+    callback: ICallback<Proto.Response>,
+    onError: (error: any) => void
+  ): void {
+    // tslint:disable-next-line:no-unused-expression
+    new Reader<Proto.Response>(this.childProcess.stdout, callback, onError)
+  }
+
+  public kill(): void {
+    this.childProcess.kill()
+  }
+}
+
+export interface TsDiagnostics {
+  readonly kind: DiagnosticKind
+  readonly resource: Uri
+  readonly diagnostics: Proto.Diagnostic[]
+}
+
+export default class TypeScriptServiceClient implements ITypeScriptServiceClient {
+  public state = ServiceStat.Initial
+  public readonly logger: Logger = new Logger()
+  private fileConfigurationManager: FileConfigurationManager
+  private pathSeparator: string
+  private tracer: Tracer
+  private _configuration: TypeScriptServiceConfiguration
+  private versionProvider: TypeScriptVersionProvider
+  private tsServerLogFile: string | null = null
+  private servicePromise: Thenable<ForkedTsServerProcess> | null
+  private lastError: Error | null
+  private lastStart: number
+  private numberRestarts: number
+  private cancellationPipeName: string | null = null
+  private requestQueue: RequestQueue
+  private callbacks: CallbackMap
+  private readonly _onTsServerStarted = new Emitter<API>()
+  private readonly _onProjectLanguageServiceStateChanged = new Emitter<Proto.ProjectLanguageServiceStateEventBody>()
+  private readonly _onDidBeginInstallTypings = new Emitter<Proto.BeginInstallTypesEventBody>()
+  private readonly _onDidEndInstallTypings = new Emitter<Proto.EndInstallTypesEventBody>()
+  private readonly _onTypesInstallerInitializationFailed = new Emitter<
+    Proto.TypesInstallerInitializationFailedEventBody
+    >()
+  private _apiVersion: API
+  private readonly disposables: Disposable[] = []
+
+  constructor() {
+    this.pathSeparator = path.sep
+    this.lastStart = Date.now()
+    this.servicePromise = null
+    this.lastError = null
+    this.numberRestarts = 0
+    this.fileConfigurationManager = new FileConfigurationManager(this)
+    this.requestQueue = new RequestQueue()
+    this.callbacks = new CallbackMap()
+    this._configuration = TypeScriptServiceConfiguration.loadFromWorkspace()
+    this.versionProvider = new TypeScriptVersionProvider(this._configuration)
+    this._apiVersion = API.defaultVersion
+    this.tracer = new Tracer(this.logger)
+    const onInstalled = name => {
+      if (name == 'typescript') {
+        this.restartTsServer().catch(e => {
+          this.logger.error(e.stack)
+        })
+        workspace.terminal.removeListener('installed', onInstalled)
+      }
+    }
+    workspace.terminal.on('installed', onInstalled)
+    this.disposables.push(Disposable.create(() => {
+      workspace.terminal.removeListener('installed', onInstalled)
+    }))
+  }
+
+  private _onDiagnosticsReceived = new Emitter<TsDiagnostics>()
+  public get onDiagnosticsReceived(): Event<TsDiagnostics> {
+    return this._onDiagnosticsReceived.event
+  }
+
+  private _onConfigDiagnosticsReceived = new Emitter<Proto.ConfigFileDiagnosticEvent>()
+  public get onConfigDiagnosticsReceived(): Event<Proto.ConfigFileDiagnosticEvent> {
+    return this._onConfigDiagnosticsReceived.event
+  }
+
+  private _onResendModelsRequested = new Emitter<void>()
+  public get onResendModelsRequested(): Event<void> {
+    return this._onResendModelsRequested.event
+  }
+
+  public get configuration(): TypeScriptServiceConfiguration {
+    return this._configuration
+  }
+
+  public dispose(): void {
+    if (this.servicePromise) {
+      this.servicePromise
+        .then(childProcess => {
+          childProcess.kill()
+        })
+        .then(undefined, () => void 0)
+    }
+
+    disposeAll(this.disposables)
+    this.logger.dispose()
+    this._onTsServerStarted.dispose()
+    this._onResendModelsRequested.dispose()
+  }
+
+  private info(message: string, data?: any): void {
+    this.logger.info(message, data)
+  }
+
+  private error(message: string, data?: any): void {
+    this.logger.error(message, data)
+  }
+
+  public restartTsServer(): Promise<any> {
+    const start = () => {
+      this.servicePromise = this.startService(true)
+      return this.servicePromise
+    }
+
+    if (this.servicePromise) {
+      return Promise.resolve(this.servicePromise.then(childProcess => {
+        this.state = ServiceStat.Stopping
+        this.info('Killing TS Server')
+        childProcess.kill()
+        this.servicePromise = null
+      }).then(start))
+    } else {
+      return Promise.resolve(start())
+    }
+  }
+
+  public stop(): Promise<void> {
+    if (!this.servicePromise) return
+    return new Promise((resolve, reject) => {
+      this.servicePromise.then(childProcess => {
+        if (this.state == ServiceStat.Running) {
+          this.info('Killing TS Server')
+          childProcess.onExit(() => {
+            resolve()
+          })
+          childProcess.kill()
+          this.servicePromise = null
+        } else {
+          resolve()
+        }
+      }, reject)
+    })
+  }
+
+  public get onTsServerStarted(): Event<API> {
+    return this._onTsServerStarted.event
+  }
+
+  public get onProjectLanguageServiceStateChanged(): Event<
+    Proto.ProjectLanguageServiceStateEventBody
+    > {
+    return this._onProjectLanguageServiceStateChanged.event
+  }
+
+  public get onDidBeginInstallTypings(): Event<Proto.BeginInstallTypesEventBody> {
+    return this._onDidBeginInstallTypings.event
+  }
+
+  public get onDidEndInstallTypings(): Event<Proto.EndInstallTypesEventBody> {
+    return this._onDidEndInstallTypings.event
+  }
+
+  public get onTypesInstallerInitializationFailed(): Event<Proto.TypesInstallerInitializationFailedEventBody> {
+    return this._onTypesInstallerInitializationFailed.event
+  }
+
+  public get apiVersion(): API {
+    return this._apiVersion
+  }
+
+  private service(): Thenable<ForkedTsServerProcess> {
+    if (this.servicePromise) {
+      return this.servicePromise
+    }
+    if (this.lastError) {
+      return Promise.reject<ForkedTsServerProcess>(this.lastError)
+    }
+    return this.startService().then(() => {
+      if (this.servicePromise) {
+        return this.servicePromise
+      }
+    })
+  }
+
+  public ensureServiceStarted(): void {
+    if (!this.servicePromise) {
+      this.startService().catch(err => {
+        workspace.showMessage(`TSServer start failed: ${err.message}`, 'error')
+        this.error(`Service start failed: ${err.stack}`)
+      })
+    }
+  }
+
+  private async startService(resendModels = false): Promise<ForkedTsServerProcess> {
+    let currentVersion = this.versionProvider.getLocalVersion(workspace.root)
+    if (!currentVersion || !fs.existsSync(currentVersion.tsServerPath)) {
+      currentVersion = await this.versionProvider.getDefaultVersion()
+    }
+    if (!currentVersion || !currentVersion.isValid) {
+      workspace.showMessage('Can not find tsserver, try installing...', 'error')
+      await workspace.terminal.installModule('typescript', 'tsserver')
+      return
+    }
+    workspace.showMessage(`Using tsserver from: ${currentVersion.path}`) // tslint:disable-line
+    this._apiVersion = currentVersion.version
+    this.requestQueue = new RequestQueue()
+    this.callbacks = new CallbackMap()
+    this.lastError = null
+    const tsServerForkArgs = await this.getTsServerArgs()
+    const debugPort = this._configuration.debugPort
+    const options = {
+      execArgv: debugPort ? [`--inspect=${debugPort}`] : [], // [`--debug-brk=5859`]
+      cwd: workspace.root
+    }
+    this.servicePromise = this.startProcess(currentVersion, tsServerForkArgs, options, resendModels)
+    return this.servicePromise
+  }
+
+  private startProcess(currentVersion: TypeScriptVersion, args: string[], options: IForkOptions, resendModels: boolean): Promise<ForkedTsServerProcess> {
+    this.state = ServiceStat.Starting
+    return new Promise((resolve, reject) => {
+      try {
+        fork(
+          currentVersion.tsServerPath,
+          args,
+          options,
+          this.logger,
+          (err: any, childProcess: cp.ChildProcess | null) => {
+            if (err || !childProcess) {
+              this.state = ServiceStat.StartFailed
+              this.lastError = err
+              this.error('Starting TSServer failed with error.', err.stack)
+              return
+            }
+            this.state = ServiceStat.Running
+            this.info('Started TSServer', JSON.stringify(currentVersion, null, 2))
+            const handle = new ForkedTsServerProcess(childProcess)
+            this.lastStart = Date.now()
+
+            handle.onError((err: Error) => {
+              this.lastError = err
+              this.error('TSServer errored with error.', err)
+              this.error(`TSServer log file: ${this.tsServerLogFile || ''}`)
+              workspace.showMessage(`TSServer errored with error. ${err.message}`, 'error')
+              this.serviceExited(false)
+            })
+            handle.onExit((code: any) => {
+              if (code == null) {
+                this.info('TSServer normal exit')
+              } else {
+                this.error(`TSServer exited with code: ${code}`)
+              }
+              this.info(`TSServer log file: ${this.tsServerLogFile || ''}`)
+              this.serviceExited(code != null)
+            })
+
+            handle.createReader(
+              msg => {
+                this.dispatchMessage(msg)
+              },
+              error => {
+                this.error('ReaderError', error)
+              }
+            )
+            resolve(handle)
+            this._onTsServerStarted.fire(currentVersion.version)
+            this.serviceStarted(resendModels)
+          }
+        )
+      } catch (e) {
+        reject(e)
+      }
+    })
+  }
+
+  public async openTsServerLogFile(): Promise<boolean> {
+    const isRoot = process.getuid && process.getuid() == 0
+    let echoErr = (msg: string) => {
+      workspace.showMessage(msg, 'error')
+    }
+    if (isRoot) {
+      echoErr('Log disabled for root user.')
+      return false
+    }
+    if (!this.apiVersion.gte(API.v222)) {
+      echoErr('TS Server logging requires TS 2.2.2+')
+      return false
+    }
+    if (this._configuration.tsServerLogLevel === TsServerLogLevel.Off) {
+      echoErr(`TS Server logging is off. Change 'tsserver.log' in 'coc-settings.json' to enable`)
+      return false
+    }
+    if (!this.tsServerLogFile) {
+      echoErr('TS Server has not started logging.')
+      return false
+    }
+    try {
+      await workspace.nvim.command(`edit ${this.tsServerLogFile}`)
+      return true
+    } catch {
+      echoErr('Could not open TS Server log file')
+      return false
+    }
+  }
+
+  private serviceStarted(resendModels: boolean): void {
+    let document = workspace.getDocument(workspace.bufnr)
+    if (document) {
+      this.fileConfigurationManager.ensureConfigurationForDocument(document.textDocument) // tslint:disable-line
+    } else {
+      const configureOptions: Proto.ConfigureRequestArguments = {
+        hostInfo: 'nvim-coc'
+      }
+      this.execute('configure', configureOptions) // tslint:disable-line
+    }
+    this.setCompilerOptionsForInferredProjects(this._configuration)
+    if (resendModels) {
+      this._onResendModelsRequested.fire(void 0)
+    }
+  }
+
+  private setCompilerOptionsForInferredProjects(
+    configuration: TypeScriptServiceConfiguration
+  ): void {
+    if (!this.apiVersion.gte(API.v206)) return
+    const args: Proto.SetCompilerOptionsForInferredProjectsArgs = {
+      options: this.getCompilerOptionsForInferredProjects(configuration)
+    }
+    this.execute('compilerOptionsForInferredProjects', args, true) // tslint:disable-line
+  }
+
+  private getCompilerOptionsForInferredProjects(
+    configuration: TypeScriptServiceConfiguration
+  ): Proto.ExternalProjectCompilerOptions {
+    return {
+      ...inferredProjectConfig(configuration),
+      allowJs: true,
+      allowSyntheticDefaultImports: true,
+      allowNonTsExtensions: true
+    }
+  }
+
+  private serviceExited(restart: boolean): void {
+    this.state = ServiceStat.Stopped
+    this.servicePromise = null
+    this.tsServerLogFile = null
+    this.callbacks.destroy(new Error('Service died.'))
+    this.callbacks = new CallbackMap()
+    if (restart) {
+      const diff = Date.now() - this.lastStart
+      this.numberRestarts++
+      let startService = true
+      if (this.numberRestarts > 5) {
+        this.numberRestarts = 0
+        if (diff < 10 * 1000 /* 10 seconds */) {
+          this.lastStart = Date.now()
+          startService = false
+          workspace.showMessage('The TypeScript language service died 5 times right after it got started.', 'error') // tslint:disable-line
+        } else if (diff < 60 * 1000 /* 1 Minutes */) {
+          this.lastStart = Date.now()
+          workspace.showMessage('The TypeScript language service died unexpectedly 5 times in the last 5 Minutes.', 'error') // tslint:disable-line
+        }
+      }
+      if (startService) {
+        this.startService(true) // tslint:disable-line
+      }
+    }
+  }
+
+  public toPath(uri: string): string {
+    return this.normalizePath(Uri.parse(uri))
+  }
+
+  public toResource(filepath: string): string {
+    if (this._apiVersion.gte(API.v213)) {
+      if (filepath.startsWith('untitled:')) {
+        let resource = Uri.parse(filepath)
+        if (this.inMemoryResourcePrefix) {
+          const dirName = path.dirname(resource.path)
+          const fileName = path.basename(resource.path)
+          if (fileName.startsWith(this.inMemoryResourcePrefix)) {
+            resource = resource.with({ path: path.posix.join(dirName, fileName.slice(this.inMemoryResourcePrefix.length)) })
+          }
+        }
+        return resource.toString()
+      }
+    }
+    return Uri.file(filepath).toString()
+  }
+
+  public normalizePath(resource: Uri): string | null {
+    if (this._apiVersion.gte(API.v213)) {
+      if (resource.scheme !== 'file') {
+        const dirName = path.dirname(resource.path)
+        const fileName = this.inMemoryResourcePrefix + path.basename(resource.path)
+        return resource
+          .with({ path: path.posix.join(dirName, fileName) })
+          .toString(true)
+      }
+    }
+
+    const result = resource.fsPath
+    if (!result) return null
+
+    // Both \ and / must be escaped in regular expressions
+    return result.replace(new RegExp('\\' + this.pathSeparator, 'g'), '/')
+  }
+
+  private get inMemoryResourcePrefix(): string {
+    return this._apiVersion.gte(API.v270) ? '^' : ''
+  }
+
+  public asUrl(filepath: string): Uri {
+    if (this._apiVersion.gte(API.v213)) {
+      if (filepath.startsWith('untitled:')) {
+        let resource = Uri.parse(filepath)
+        if (this.inMemoryResourcePrefix) {
+          const dirName = path.dirname(resource.path)
+          const fileName = path.basename(resource.path)
+          if (fileName.startsWith(this.inMemoryResourcePrefix)) {
+            resource = resource.with({
+              path: path.posix.join(
+                dirName,
+                fileName.slice(this.inMemoryResourcePrefix.length)
+              )
+            })
+          }
+        }
+        return resource
+      }
+    }
+    return Uri.file(filepath)
+  }
+
+  public execute(
+    command: string,
+    args: any,
+    expectsResultOrToken?: boolean | CancellationToken
+  ): Promise<any> {
+    if (this.servicePromise == null) {
+      return Promise.resolve()
+    }
+    let token: CancellationToken | undefined
+    let expectsResult = true
+    if (typeof expectsResultOrToken === 'boolean') {
+      expectsResult = expectsResultOrToken
+    } else {
+      token = expectsResultOrToken
+    }
+
+    const request = this.requestQueue.createRequest(command, args)
+    const requestInfo: RequestItem = {
+      request,
+      callbacks: null
+    }
+    let result: Promise<any>
+    if (expectsResult) {
+      let wasCancelled = false
+      result = new Promise<any>((resolve, reject) => {
+        requestInfo.callbacks = { c: resolve, e: reject, start: Date.now() }
+        if (token) {
+          token.onCancellationRequested(() => {
+            wasCancelled = true
+            this.tryCancelRequest(request.seq)
+          })
+        }
+      }).catch((err: any) => {
+        if (!wasCancelled && command != 'signatureHelp') {
+          this.error(`'${command}' request failed with error.`, err)
+        }
+        throw err
+      })
+    } else {
+      result = Promise.resolve(null)
+    }
+    this.requestQueue.push(requestInfo)
+    this.sendNextRequests()
+
+    return result
+  }
+
+  private sendNextRequests(): void {
+    while (
+      this.callbacks.pendingResponses === 0 &&
+      this.requestQueue.length > 0
+    ) {
+      const item = this.requestQueue.shift()
+      if (item) {
+        this.sendRequest(item)
+      }
+    }
+  }
+
+  private sendRequest(requestItem: RequestItem): void {
+    const serverRequest = requestItem.request
+    this.tracer.traceRequest(
+      serverRequest,
+      !!requestItem.callbacks,
+      this.requestQueue.length
+    )
+    if (requestItem.callbacks) {
+      this.callbacks.add(serverRequest.seq, requestItem.callbacks)
+    }
+    this.service()
+      .then(childProcess => {
+        childProcess.write(serverRequest)
+      })
+      .then(undefined, err => {
+        const callback = this.callbacks.fetch(serverRequest.seq)
+        if (callback) {
+          callback.e(err)
+        }
+      })
+  }
+
+  private tryCancelRequest(seq: number): boolean {
+    try {
+      if (this.requestQueue.tryCancelPendingRequest(seq)) {
+        this.tracer.logTrace(`TypeScript Service: canceled request with sequence number ${seq}`)
+        return true
+      }
+
+      if (this.apiVersion.gte(API.v222) && this.cancellationPipeName) {
+        this.tracer.logTrace(`TypeScript Service: trying to cancel ongoing request with sequence number ${seq}`)
+        try {
+          fs.writeFileSync(this.cancellationPipeName + seq, '')
+        } catch {
+          // noop
+        }
+        return true
+      }
+
+      this.tracer.logTrace(
+        `TypeScript Service: tried to cancel request with sequence number ${seq}. But request got already delivered.`
+      )
+      return false
+    } finally {
+      const p = this.callbacks.fetch(seq)
+      if (p) {
+        p.e(new Error(`Cancelled Request ${seq}`))
+      }
+    }
+  }
+
+  private dispatchMessage(message: Proto.Message): void {
+    try {
+      if (message.type === 'response') {
+        const response: Proto.Response = message as Proto.Response
+        const p = this.callbacks.fetch(response.request_seq)
+        if (p) {
+          this.tracer.traceResponse(response, p.start)
+          if (response.success) {
+            p.c(response)
+          } else {
+            p.e(response)
+          }
+        }
+      } else if (message.type === 'event') {
+        const event: Proto.Event = message as Proto.Event
+        this.tracer.traceEvent(event)
+        this.dispatchEvent(event)
+      } else {
+        throw new Error('Unknown message type ' + message.type + ' received')
+      }
+    } finally {
+      this.sendNextRequests()
+    }
+  }
+
+  private dispatchEvent(event: Proto.Event): void {
+    switch (event.event) {
+      case 'syntaxDiag':
+      case 'semanticDiag':
+      case 'suggestionDiag':
+        const diagnosticEvent: Proto.DiagnosticEvent = event
+        if (diagnosticEvent.body && diagnosticEvent.body.diagnostics) {
+          this._onDiagnosticsReceived.fire({
+            kind: getDignosticsKind(event),
+            resource: this.asUrl(diagnosticEvent.body.file),
+            diagnostics: diagnosticEvent.body.diagnostics
+          })
+        }
+        break
+
+      case 'configFileDiag':
+        this._onConfigDiagnosticsReceived.fire(
+          event as Proto.ConfigFileDiagnosticEvent
+        )
+        break
+
+      case 'projectLanguageServiceState':
+        if (event.body) {
+          this._onProjectLanguageServiceStateChanged.fire(
+            (event as Proto.ProjectLanguageServiceStateEvent).body
+          )
+        }
+        break
+
+      case 'beginInstallTypes':
+        if (event.body) {
+          this._onDidBeginInstallTypings.fire(
+            (event as Proto.BeginInstallTypesEvent).body
+          )
+        }
+        break
+
+      case 'endInstallTypes':
+        if (event.body) {
+          this._onDidEndInstallTypings.fire(
+            (event as Proto.EndInstallTypesEvent).body
+          )
+        }
+        break
+
+      case 'typesInstallerInitializationFailed':
+        if (event.body) {
+          this._onTypesInstallerInitializationFailed.fire(
+            (event as Proto.TypesInstallerInitializationFailedEvent).body
+          )
+        }
+        break
+    }
+  }
+
+  private async getTsServerArgs(): Promise<string[]> {
+    const args: string[] = []
+    args.push('--allowLocalPluginLoads')
+
+    if (this.apiVersion.gte(API.v250)) {
+      args.push('--useInferredProjectPerProjectRoot')
+    } else {
+      args.push('--useSingleInferredProject')
+    }
+
+    if (this.apiVersion.gte(API.v206) && this._configuration.disableAutomaticTypeAcquisition) {
+      args.push('--disableAutomaticTypingAcquisition')
+    }
+
+    if (this.apiVersion.gte(API.v222)) {
+      this.cancellationPipeName = getTempFile(`tscancellation-${makeRandomHexString(20)}`)
+      args.push('--cancellationPipeName', this.cancellationPipeName + '*')
+    }
+
+    if (this.apiVersion.gte(API.v222)) {
+      const isRoot = process.getuid && process.getuid() == 0
+      if (this._configuration.tsServerLogLevel !== TsServerLogLevel.Off && !isRoot) {
+        const logDir = os.tmpdir()
+        if (logDir) {
+          this.tsServerLogFile = path.join(logDir, `coc-nvim-tsc.log`)
+          this.info('TSServer log file :', this.tsServerLogFile)
+        } else {
+          this.tsServerLogFile = null
+          this.error('Could not create TSServer log directory')
+        }
+
+        if (this.tsServerLogFile) {
+          args.push(
+            '--logVerbosity',
+            TsServerLogLevel.toString(this._configuration.tsServerLogLevel)
+          )
+          args.push('--logFile', this.tsServerLogFile)
+        }
+      }
+    }
+
+    if (this.apiVersion.gte(API.v230)) {
+      const plugins = this._configuration.tsServerPluginNames
+      const pluginRoot = this._configuration.tsServerPluginRoot
+      if (plugins.length) {
+        args.push('--globalPlugins', plugins.join(','))
+        if (pluginRoot) {
+          args.push('--pluginProbeLocations', pluginRoot)
+        }
+      }
+    }
+
+    if (this._configuration.typingsCacheLocation) {
+      args.push('--globalTypingsCacheLocation', `"${this._configuration.typingsCacheLocation}"`)
+    }
+
+    if (this.apiVersion.gte(API.v234)) {
+      if (this._configuration.npmLocation) {
+        args.push('--npmLocation', `"${this._configuration.npmLocation}"`)
+      } else {
+        try {
+          args.push('--npmLocation', `"${which.sync('npm')}"`)
+        } catch (e) { } // tslint:disable-line
+      }
+    }
+
+    if (this.apiVersion.gte(API.v291)) {
+      args.push('--noGetErrOnBackgroundUpdate')
+    }
+
+    return args
+  }
+}
+
+function getDignosticsKind(event: Proto.Event): DiagnosticKind {
+  switch (event.event) {
+    case 'syntaxDiag':
+      return DiagnosticKind.Syntax
+    case 'semanticDiag':
+      return DiagnosticKind.Semantic
+    case 'suggestionDiag':
+      return DiagnosticKind.Suggestion
+  }
+  throw new Error('Unknown dignostics kind')
+}
diff --git a/src/server/typescriptServiceClientHost.ts b/src/server/typescriptServiceClientHost.ts
new file mode 100644
index 0000000..775bced
--- /dev/null
+++ b/src/server/typescriptServiceClientHost.ts
@@ -0,0 +1,201 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { DiagnosticKind, disposeAll, workspace } from 'coc.nvim'
+import { Diagnostic, DiagnosticSeverity, Disposable } from 'vscode-languageserver-protocol'
+import Uri from 'vscode-uri'
+import LanguageProvider from './languageProvider'
+import * as Proto from './protocol'
+import * as PConst from './protocol.const'
+import TypeScriptServiceClient from './typescriptServiceClient'
+import { LanguageDescription } from './utils/languageDescription'
+import * as typeConverters from './utils/typeConverters'
+import TypingsStatus, { AtaProgressReporter } from './utils/typingsStatus'
+
+// Style check diagnostics that can be reported as warnings
+const styleCheckDiagnostics = [
+  6133, // variable is declared but never used
+  6138, // property is declared but its value is never read
+  7027, // unreachable code detected
+  7028, // unused label
+  7029, // fall through case in switch
+  7030 // not all code paths return a value
+]
+
+export default class TypeScriptServiceClientHost implements Disposable {
+  private readonly ataProgressReporter: AtaProgressReporter
+  private readonly typingsStatus: TypingsStatus
+  private readonly client: TypeScriptServiceClient
+  private readonly languages: LanguageProvider[] = []
+  private readonly languagePerId = new Map<string, LanguageProvider>()
+  private readonly disposables: Disposable[] = []
+  private reportStyleCheckAsWarnings = true
+
+  constructor(descriptions: LanguageDescription[]) {
+    const handleProjectChange = () => {
+      setTimeout(() => {
+        this.triggerAllDiagnostics()
+      }, 1500)
+    }
+
+    const configFileWatcher = workspace.createFileSystemWatcher('**/[tj]sconfig.json')
+    this.disposables.push(configFileWatcher)
+    configFileWatcher.onDidCreate(
+      this.reloadProjects,
+      this,
+      this.disposables
+    )
+    configFileWatcher.onDidDelete(
+      this.reloadProjects,
+      this,
+      this.disposables
+    )
+    configFileWatcher.onDidChange(handleProjectChange, this, this.disposables)
+
+    this.client = new TypeScriptServiceClient()
+    this.disposables.push(this.client)
+    this.client.onDiagnosticsReceived(({ kind, resource, diagnostics }) => {
+      this.diagnosticsReceived(kind, resource, diagnostics)
+    }, null, this.disposables)
+
+    this.client.onConfigDiagnosticsReceived(diag => {
+      let { body } = diag
+      if (body) {
+        let { configFile, diagnostics } = body
+        if (diagnostics.length) {
+          workspace.showMessage(`Invalid config file: ${configFile}`, 'error')
+        }
+      }
+    }, null, this.disposables)
+
+    this.typingsStatus = new TypingsStatus(this.client)
+    this.ataProgressReporter = new AtaProgressReporter(this.client)
+    for (const description of descriptions) { // tslint:disable-line
+      const manager = new LanguageProvider(
+        this.client,
+        description,
+        this.typingsStatus
+      )
+      this.languages.push(manager)
+      this.disposables.push(manager)
+      this.languagePerId.set(description.id, manager)
+    }
+
+    this.client.ensureServiceStarted()
+    this.client.onTsServerStarted(() => {
+      this.triggerAllDiagnostics()
+    })
+    this.configurationChanged()
+  }
+
+  public dispose(): void {
+    disposeAll(this.disposables)
+    this.typingsStatus.dispose()
+    this.ataProgressReporter.dispose()
+  }
+
+  public reset(): void {
+    for (let lang of this.languages) {
+      lang.fileConfigurationManager.reset()
+    }
+  }
+
+  public get serviceClient(): TypeScriptServiceClient {
+    return this.client
+  }
+
+  public reloadProjects(): void {
+    this.client.execute('reloadProjects', null, false) // tslint:disable-line
+    this.triggerAllDiagnostics()
+  }
+
+  // typescript or javascript
+  public getProvider(languageId: string): LanguageProvider {
+    return this.languagePerId.get(languageId)
+  }
+
+  private configurationChanged(): void {
+    const config = workspace.getConfiguration('tsserver')
+    this.reportStyleCheckAsWarnings = config.get('reportStyleChecksAsWarnings', true)
+  }
+
+  public findLanguage(resource: Uri): LanguageProvider | null {
+    try {
+      return this.languages.find(language => language.handles(resource))
+    } catch {
+      return null
+    }
+  }
+
+  public handles(uri: string): boolean {
+    return this.findLanguage(Uri.parse(uri)) != null
+  }
+
+  private triggerAllDiagnostics(): void {
+    for (const language of this.languagePerId.values()) {
+      language.triggerAllDiagnostics()
+    }
+  }
+
+  private diagnosticsReceived(
+    kind: DiagnosticKind,
+    resource: Uri,
+    diagnostics: Proto.Diagnostic[]
+  ): void {
+    const language = this.findLanguage(resource)
+    if (language) {
+      language.diagnosticsReceived(
+        kind,
+        resource,
+        this.createMarkerDatas(diagnostics))
+    }
+  }
+
+  private createMarkerDatas(diagnostics: Proto.Diagnostic[]): Diagnostic[] {
+    return diagnostics.map(tsDiag => this.tsDiagnosticToLspDiagnostic(tsDiag))
+  }
+
+  private tsDiagnosticToLspDiagnostic(diagnostic: Proto.Diagnostic): Diagnostic {
+    const { start, end, text } = diagnostic
+    const range = {
+      start: typeConverters.Position.fromLocation(start),
+      end: typeConverters.Position.fromLocation(end)
+    }
+    return {
+      range,
+      message: text,
+      code: diagnostic.code ? diagnostic.code : null,
+      severity: this.getDiagnosticSeverity(diagnostic),
+      source: diagnostic.source || 'tsserver',
+    }
+  }
+
+  private getDiagnosticSeverity(diagnostic: Proto.Diagnostic): DiagnosticSeverity {
+    if (
+      this.reportStyleCheckAsWarnings &&
+      this.isStyleCheckDiagnostic(diagnostic.code) &&
+      diagnostic.category === PConst.DiagnosticCategory.error
+    ) {
+      return DiagnosticSeverity.Warning
+    }
+
+    switch (diagnostic.category) {
+      case PConst.DiagnosticCategory.error:
+        return DiagnosticSeverity.Error
+
+      case PConst.DiagnosticCategory.warning:
+        return DiagnosticSeverity.Warning
+
+      case PConst.DiagnosticCategory.suggestion:
+        return DiagnosticSeverity.Information
+
+      default:
+        return DiagnosticSeverity.Error
+    }
+  }
+
+  private isStyleCheckDiagnostic(code: number | undefined): boolean {
+    return code ? styleCheckDiagnostics.indexOf(code) !== -1 : false
+  }
+}
diff --git a/src/server/utils/api.ts b/src/server/utils/api.ts
new file mode 100644
index 0000000..a5746ee
--- /dev/null
+++ b/src/server/utils/api.ts
@@ -0,0 +1,53 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import * as semver from 'semver'
+
+export default class API {
+  private static fromSimpleString(value: string): API {
+    return new API(value, value)
+  }
+
+  public static readonly defaultVersion = API.fromSimpleString('1.0.0')
+  public static readonly v203 = API.fromSimpleString('2.0.3')
+  public static readonly v206 = API.fromSimpleString('2.0.6')
+  public static readonly v208 = API.fromSimpleString('2.0.8')
+  public static readonly v213 = API.fromSimpleString('2.1.3')
+  public static readonly v220 = API.fromSimpleString('2.2.0')
+  public static readonly v222 = API.fromSimpleString('2.2.2')
+  public static readonly v230 = API.fromSimpleString('2.3.0')
+  public static readonly v234 = API.fromSimpleString('2.3.4')
+  public static readonly v240 = API.fromSimpleString('2.4.0')
+  public static readonly v250 = API.fromSimpleString('2.5.0')
+  public static readonly v260 = API.fromSimpleString('2.6.0')
+  public static readonly v270 = API.fromSimpleString('2.7.0')
+  public static readonly v280 = API.fromSimpleString('2.8.0')
+  public static readonly v290 = API.fromSimpleString('2.9.0')
+  public static readonly v291 = API.fromSimpleString('2.9.1')
+  public static readonly v292 = API.fromSimpleString('2.9.2')
+  public static readonly v300 = API.fromSimpleString('3.0.0')
+
+  public static fromVersionString(versionString: string): API {
+    let version = semver.valid(versionString)
+    if (!version) {
+      return new API('invalid version', '1.0.0')
+    }
+
+    // Cut off any prerelease tag since we sometimes consume those on purpose.
+    const index = versionString.indexOf('-')
+    if (index >= 0) {
+      version = version.substr(0, index)
+    }
+    return new API(versionString, version)
+  }
+
+  private constructor(
+    public readonly versionString: string,
+    private readonly version: string
+  ) { }
+
+  public gte(other: API): boolean {
+    return semver.gte(this.version, other.version)
+  }
+}
diff --git a/src/server/utils/async.ts b/src/server/utils/async.ts
new file mode 100644
index 0000000..9b7f6f5
--- /dev/null
+++ b/src/server/utils/async.ts
@@ -0,0 +1,61 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+export type ITask<T> = () => T
+
+export class Delayer<T> {
+  public defaultDelay: number
+  private timeout: any // Timer
+  private completionPromise: Promise<T | null> | null
+  private onSuccess: ((value?: T | Thenable<T>) => void) | null
+  private task: ITask<T> | null
+
+  constructor(defaultDelay: number) {
+    this.defaultDelay = defaultDelay
+    this.timeout = null
+    this.completionPromise = null
+    this.onSuccess = null
+    this.task = null
+  }
+
+  public trigger(
+    task: ITask<T>,
+    delay: number = this.defaultDelay
+  ): Promise<T | null> {
+    this.task = task
+    if (delay >= 0) {
+      this.cancelTimeout()
+    }
+
+    if (!this.completionPromise) {
+      this.completionPromise = new Promise<T>(resolve => {
+        this.onSuccess = resolve
+      }).then(() => {
+        this.completionPromise = null
+        this.onSuccess = null
+        let result = this.task && this.task()
+        this.task = null
+        return result
+      })
+    }
+
+    if (delay >= 0 || this.timeout === null) {
+      this.timeout = setTimeout(() => {
+        this.timeout = null
+        if (this.onSuccess) {
+          this.onSuccess(undefined)
+        }
+      }, delay >= 0 ? delay : this.defaultDelay)
+    }
+
+    return this.completionPromise
+  }
+
+  private cancelTimeout(): void {
+    if (this.timeout !== null) {
+      clearTimeout(this.timeout)
+      this.timeout = null
+    }
+  }
+}
diff --git a/src/server/utils/codeAction.ts b/src/server/utils/codeAction.ts
new file mode 100644
index 0000000..e0d3c42
--- /dev/null
+++ b/src/server/utils/codeAction.ts
@@ -0,0 +1,47 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { WorkspaceEdit } from 'vscode-languageserver-protocol'
+import { workspace } from 'coc.nvim'
+import * as Proto from '../protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import * as typeConverters from './typeConverters'
+
+export function getEditForCodeAction(
+  client: ITypeScriptServiceClient,
+  action: Proto.CodeAction
+): WorkspaceEdit | undefined {
+  return action.changes && action.changes.length
+    ? typeConverters.WorkspaceEdit.fromFileCodeEdits(client, action.changes)
+    : undefined
+}
+
+export async function applyCodeAction(
+  client: ITypeScriptServiceClient,
+  action: Proto.CodeAction
+): Promise<boolean> {
+  const workspaceEdit = getEditForCodeAction(client, action)
+  if (workspaceEdit) {
+    if (!(await workspace.applyEdit(workspaceEdit))) {
+      return false
+    }
+  }
+  return applyCodeActionCommands(client, action)
+}
+
+export async function applyCodeActionCommands(
+  client: ITypeScriptServiceClient,
+  action: Proto.CodeAction
+): Promise<boolean> {
+  // make sure there is command
+  if (action.commands && action.commands.length) {
+    for (const command of action.commands) {
+      const response = await client.execute('applyCodeActionCommand', { command })
+      if (!response || !response.body) {
+        return false
+      }
+    }
+  }
+  return true
+}
diff --git a/src/server/utils/completionItem.ts b/src/server/utils/completionItem.ts
new file mode 100644
index 0000000..ad923b5
--- /dev/null
+++ b/src/server/utils/completionItem.ts
@@ -0,0 +1,149 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { CompletionItem, CompletionItemKind, InsertTextFormat, Position, TextEdit } from 'vscode-languageserver-protocol'
+import { Document, workspace } from 'coc.nvim'
+import * as Proto from '../protocol'
+import * as PConst from '../protocol.const'
+
+export function resolveItem(
+  item: CompletionItem,
+  document: Document,
+): void {
+  let { textEdit, label } = item // tslint:disable-line
+  let { position } = item.data
+  if (textEdit) return
+  // try replace more characters after cursor
+  const wordRange = document.getWordRangeAtPosition(position)
+  let text = document.textDocument.getText({
+    start: {
+      line: position.line,
+      character: Math.max(0, position.character - label.length),
+    },
+    end: {
+      line: position.line,
+      character: position.character
+    }
+  })
+
+  text = text.toLowerCase()
+  const entryName = label.toLowerCase()
+
+  for (let i = entryName.length; i >= 0; --i) {
+    if (text.endsWith(entryName.substr(0, i)) &&
+      (!wordRange ||
+        wordRange.start.character > position.character - i)) {
+      item.textEdit = {
+        newText: label,
+        range: {
+          start: {
+            line: position.line,
+            character: Math.max(0, position.character - i)
+          },
+          end: {
+            line: position.line,
+            character: position.character
+          }
+        }
+      }
+      break
+    }
+  }
+}
+
+export function convertCompletionEntry(
+  tsEntry: Proto.CompletionEntry,
+  uri: string,
+  position: Position,
+  useCodeSnippetsOnMethodSuggest: boolean
+): CompletionItem {
+  let label = tsEntry.name
+  let sortText = tsEntry.sortText
+  if (tsEntry.isRecommended) {
+    // Make sure isRecommended property always comes first
+    // https://github.com/Microsoft/vscode/issues/40325
+    sortText = '\0' + sortText
+  } else if (tsEntry.source) {
+    // De-prioritze auto-imports
+    // https://github.com/Microsoft/vscode/issues/40311
+    sortText = '\uffff' + sortText
+  } else {
+    sortText = tsEntry.sortText
+  }
+  let kind = convertKind(tsEntry.kind)
+  let insertTextFormat = (
+    useCodeSnippetsOnMethodSuggest &&
+    (kind === CompletionItemKind.Function ||
+      kind === CompletionItemKind.Method)
+  ) ? InsertTextFormat.Snippet : InsertTextFormat.PlainText
+
+  let textEdit: TextEdit = null
+  let insertText = tsEntry.insertText
+  if (insertText) {
+    let document = workspace.getDocument(uri)
+    textEdit = {
+      range: document.getWordRangeAtPosition(position),
+      newText: insertText
+    }
+    insertText = null
+  }
+  let optional = tsEntry.kindModifiers && tsEntry.kindModifiers.match(/\boptional\b/)
+  return {
+    label,
+    insertText,
+    kind,
+    textEdit,
+    insertTextFormat,
+    sortText,
+    data: {
+      uri,
+      optional,
+      position,
+      source: tsEntry.source || ''
+    }
+  }
+}
+
+function convertKind(kind: string): CompletionItemKind {
+  switch (kind) {
+    case PConst.Kind.primitiveType:
+    case PConst.Kind.keyword:
+      return CompletionItemKind.Keyword
+    case PConst.Kind.const:
+      return CompletionItemKind.Constant
+    case PConst.Kind.let:
+    case PConst.Kind.variable:
+    case PConst.Kind.localVariable:
+    case PConst.Kind.alias:
+      return CompletionItemKind.Variable
+    case PConst.Kind.memberVariable:
+    case PConst.Kind.memberGetAccessor:
+    case PConst.Kind.memberSetAccessor:
+      return CompletionItemKind.Field
+    case PConst.Kind.function:
+      return CompletionItemKind.Function
+    case PConst.Kind.memberFunction:
+    case PConst.Kind.constructSignature:
+    case PConst.Kind.callSignature:
+    case PConst.Kind.indexSignature:
+      return CompletionItemKind.Method
+    case PConst.Kind.enum:
+      return CompletionItemKind.Enum
+    case PConst.Kind.module:
+    case PConst.Kind.externalModuleName:
+      return CompletionItemKind.Module
+    case PConst.Kind.class:
+    case PConst.Kind.type:
+      return CompletionItemKind.Class
+    case PConst.Kind.interface:
+      return CompletionItemKind.Interface
+    case PConst.Kind.warning:
+    case PConst.Kind.file:
+    case PConst.Kind.script:
+      return CompletionItemKind.File
+    case PConst.Kind.directory:
+      return CompletionItemKind.Folder
+  }
+  return CompletionItemKind.Property
+}
diff --git a/src/server/utils/configuration.ts b/src/server/utils/configuration.ts
new file mode 100644
index 0000000..a26c643
--- /dev/null
+++ b/src/server/utils/configuration.ts
@@ -0,0 +1,109 @@
+import { workspace, WorkspaceConfiguration } from 'coc.nvim'
+import which from 'which'
+
+export enum TsServerLogLevel {
+  Off,
+  Normal,
+  Terse,
+  Verbose
+}
+
+export namespace TsServerLogLevel {
+  export function fromString(value: string): TsServerLogLevel {
+    switch (value && value.toLowerCase()) {
+      case 'normal':
+        return TsServerLogLevel.Normal
+      case 'terse':
+        return TsServerLogLevel.Terse
+      case 'verbose':
+        return TsServerLogLevel.Verbose
+      case 'off':
+      default:
+        return TsServerLogLevel.Off
+    }
+  }
+
+  export function toString(value: TsServerLogLevel): string {
+    switch (value) {
+      case TsServerLogLevel.Normal:
+        return 'normal'
+      case TsServerLogLevel.Terse:
+        return 'terse'
+      case TsServerLogLevel.Verbose:
+        return 'verbose'
+      case TsServerLogLevel.Off:
+      default:
+        return 'off'
+    }
+  }
+}
+
+export class TypeScriptServiceConfiguration {
+  private _configuration: WorkspaceConfiguration
+  private constructor() {
+    this._configuration = workspace.getConfiguration('tsserver')
+
+    workspace.onDidChangeConfiguration(() => {
+      this._configuration = workspace.getConfiguration('tsserver')
+    })
+  }
+
+  public get locale(): string | null {
+    return this._configuration.get<string | null>('locale', null)
+  }
+
+  public get globalTsdk(): string | null {
+    return this._configuration.get<string | null>('tsdk', null)
+  }
+
+  public get tsServerLogLevel(): TsServerLogLevel {
+    return TsServerLogLevel.fromString(this._configuration.get<string | null>('log', null))
+  }
+
+  public get typingsCacheLocation(): string {
+    return this._configuration.get<string>('typingsCacheLocation', '')
+  }
+
+  public get tsServerPluginNames(): string[] {
+    return this._configuration.get<string[]>('pluginNames', [])
+  }
+
+  public get tsServerPluginRoot(): string | null {
+    return this._configuration.get<string | null>('tsServerPluginRoot', null)
+  }
+
+  public get checkJs(): boolean {
+    return this._configuration.get<boolean>('implicitProjectConfig.checkJs', false)
+  }
+
+  public get experimentalDecorators(): boolean {
+    return this._configuration.get<boolean>('implicitProjectConfig.experimentalDecorators', false)
+  }
+
+  public get disableAutomaticTypeAcquisition(): boolean {
+    return this._configuration.get<boolean>('disableAutomaticTypeAcquisition', false)
+  }
+
+  public get formatOnType(): boolean {
+    return this._configuration.get<boolean>('formatOnType', false)
+  }
+
+  public get debugPort(): number | null {
+    return this._configuration.get<number>('debugPort', parseInt(process.env['TSS_DEBUG'], 10))
+  }
+
+  public get npmLocation(): string | null {
+    let path = this._configuration.get<string>('npm', '')
+    if (path) return path
+    try {
+      path = which.sync('npm')
+    } catch (e) {
+      return null
+    }
+    return path
+  }
+
+  public static loadFromWorkspace(): TypeScriptServiceConfiguration {
+    return new TypeScriptServiceConfiguration()
+  }
+}
diff --git a/src/server/utils/fs.ts b/src/server/utils/fs.ts
new file mode 100644
index 0000000..18cf37f
--- /dev/null
+++ b/src/server/utils/fs.ts
@@ -0,0 +1,30 @@
+import path from 'path'
+import os from 'os'
+import fs from 'fs'
+
+export function getParentDirs(fullpath: string): string[] {
+  let obj = path.parse(fullpath)
+  if (!obj || !obj.root) return []
+  let res = []
+  let p = path.dirname(fullpath)
+  while (p && p !== obj.root) {
+    res.push(p)
+    p = path.dirname(p)
+  }
+  return res
+}
+
+export function resolveRoot(cwd: string, subs: string[], home?: string): string | null {
+  home = home || os.homedir()
+  let { root } = path.parse(cwd)
+  let paths = getParentDirs(cwd)
+  paths.unshift(cwd)
+  for (let p of paths) {
+    if (p == home || p == root) return null
+    for (let sub of subs) {
+      let d = path.join(p, sub)
+      if (fs.existsSync(d)) return path.dirname(d)
+    }
+  }
+  return root
+}
diff --git a/src/server/utils/is.ts b/src/server/utils/is.ts
new file mode 100644
index 0000000..b247111
--- /dev/null
+++ b/src/server/utils/is.ts
@@ -0,0 +1,18 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+const toString = Object.prototype.toString
+
+export function defined(value: any): boolean {
+  return typeof value !== 'undefined'
+}
+
+export function boolean(value: any): value is boolean {
+  return value === true || value === false
+}
+
+export function string(value: any): value is string {
+  return toString.call(value) === '[object String]'
+}
diff --git a/src/server/utils/languageDescription.ts b/src/server/utils/languageDescription.ts
new file mode 100644
index 0000000..e41154c
--- /dev/null
+++ b/src/server/utils/languageDescription.ts
@@ -0,0 +1,32 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import * as languageModeIds from './languageModeIds'
+
+export interface LanguageDescription {
+  readonly id: string
+  readonly diagnosticSource: string
+  readonly modeIds: string[]
+  readonly configFile?: string
+  readonly isExternal?: boolean
+  readonly diagnosticOwner: string
+}
+
+export const standardLanguageDescriptions: LanguageDescription[] = [
+  {
+    id: 'typescript',
+    diagnosticSource: 'ts',
+    diagnosticOwner: 'typescript',
+    modeIds: [languageModeIds.typescript, languageModeIds.typescriptreact,
+    languageModeIds.typescripttsx, languageModeIds.typescriptjsx],
+    configFile: 'tsconfig.json'
+  },
+  {
+    id: 'javascript',
+    diagnosticSource: 'ts',
+    diagnosticOwner: 'typescript',
+    modeIds: [languageModeIds.javascript, languageModeIds.javascriptreact],
+    configFile: 'jsconfig.json'
+  }
+]
diff --git a/src/server/utils/languageModeIds.ts b/src/server/utils/languageModeIds.ts
new file mode 100644
index 0000000..925fbd4
--- /dev/null
+++ b/src/server/utils/languageModeIds.ts
@@ -0,0 +1,14 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+export const typescript = 'typescript'
+export const typescriptreact = 'typescriptreact'
+export const typescripttsx = 'typescript.tsx'
+export const typescriptjsx = 'typescript.jsx'
+export const javascript = 'javascript'
+export const javascriptreact = 'javascript.jsx'
+export const jsxTags = 'jsx-tags'
+
+export const languageIds = [typescript, typescriptreact, javascript, javascriptreact, typescripttsx, jsxTags]
diff --git a/src/server/utils/logger.ts b/src/server/utils/logger.ts
new file mode 100644
index 0000000..5e7d6a8
--- /dev/null
+++ b/src/server/utils/logger.ts
@@ -0,0 +1,67 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { OutputChannel, workspace } from 'coc.nvim'
+import * as is from './is'
+
+export default class Logger {
+
+  private _channel: OutputChannel
+
+  private get output(): OutputChannel {
+    if (this._channel) {
+      return this._channel
+    }
+    this._channel = workspace.createOutputChannel('tsserver')
+    return this._channel
+  }
+
+  public dispose(): void {
+    if (this._channel) {
+      this._channel.dispose()
+    }
+  }
+
+  private data2String(data: any): string {
+    if (data instanceof Error) {
+      if (is.string(data.stack)) {
+        return data.stack
+      }
+      return (data as Error).message
+    }
+    if (is.boolean(data.success) && !data.success && is.string(data.message)) {
+      return data.message
+    }
+    if (is.string(data)) {
+      return data
+    }
+    return data.toString()
+  }
+
+  public info(message: string, data?: any): void {
+    this.logLevel('Info', message, data)
+  }
+
+  public warn(message: string, data?: any): void {
+    this.logLevel('Warn', message, data)
+  }
+
+  public error(message: string, data?: any): void {
+    // See https://github.com/Microsoft/TypeScript/issues/10496
+    if (data && data.message === 'No content available.') {
+      return
+    }
+    this.logLevel('Error', message, data)
+  }
+
+  public logLevel(level: string, message: string, data?: any): void {
+    this.output.appendLine(
+      `[${level}  - ${new Date().toLocaleTimeString()}] ${message}`
+    )
+    if (data) {
+      this.output.appendLine(this.data2String(data))
+    }
+  }
+}
diff --git a/src/server/utils/previewer.ts b/src/server/utils/previewer.ts
new file mode 100644
index 0000000..07d53b7
--- /dev/null
+++ b/src/server/utils/previewer.ts
@@ -0,0 +1,73 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { MarkupContent, MarkupKind } from 'vscode-languageserver-protocol'
+import * as Proto from '../protocol'
+
+function getTagBodyText(tag: Proto.JSDocTagInfo): string | undefined {
+  if (!tag.text) {
+    return undefined
+  }
+
+  switch (tag.name) {
+    case 'example':
+    case 'default':
+      // Convert to markdown code block if it not already one
+      if (tag.text.match(/^\s*[~`]{3}/g)) {
+        return tag.text
+      }
+      return '```\n' + tag.text + '\n```'
+  }
+
+  return tag.text
+}
+
+function getTagDocumentation(tag: Proto.JSDocTagInfo): string | undefined {
+  switch (tag.name) {
+    case 'param':
+      const body = (tag.text || '').split(/^([\w\.]+)\s*/)
+      if (body && body.length === 3) {
+        const param = body[1]
+        const doc = body[2]
+        const label = `*@${tag.name}* \`${param}\``
+        if (!doc) {
+          return label
+        }
+        return label + (doc.match(/\r\n|\n/g) ? '\n' + doc : ` — ${doc}`)
+      }
+  }
+
+  // Generic tag
+  const label = `*@${tag.name}*`
+  const text = getTagBodyText(tag)
+  if (!text) {
+    return label
+  }
+  return label + (text.match(/\r\n|\n/g) ? '\n' + text : ` — ${text}`)
+}
+
+export function plain(parts: Proto.SymbolDisplayPart[]): string {
+  if (!parts || !parts.length) return ''
+  return parts.map(part => part.text).join('')
+}
+
+export function tagsMarkdownPreview(tags: Proto.JSDocTagInfo[]): string {
+  return (tags || []).map(getTagDocumentation).join('  \n\n')
+}
+
+export function markdownDocumentation(
+  documentation: Proto.SymbolDisplayPart[],
+  tags: Proto.JSDocTagInfo[]
+): MarkupContent {
+  let out = plain(documentation)
+  const tagsPreview = tagsMarkdownPreview(tags)
+  if (tagsPreview) {
+    out = out + ('\n\n' + tagsPreview)
+  }
+  return {
+    kind: MarkupKind.Markdown,
+    value: out
+  }
+}
diff --git a/src/server/utils/process.ts b/src/server/utils/process.ts
new file mode 100644
index 0000000..7e62387
--- /dev/null
+++ b/src/server/utils/process.ts
@@ -0,0 +1,154 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import cp from 'child_process'
+import net from 'net'
+import os from 'os'
+import path from 'path'
+import { workspace } from 'coc.nvim'
+import Logger from './logger'
+
+export interface IForkOptions {
+  cwd?: string
+  execArgv?: string[]
+}
+
+export function makeRandomHexString(length: number): string {
+  let chars = ['0', '1', '2', '3', '4', '5', '6', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']
+  let result = ''
+  for (let i = 0; i < length; i++) {
+    const idx = Math.floor(chars.length * Math.random())
+    result += chars[idx]
+  }
+  return result
+}
+
+function generatePipeName(): string {
+  return getPipeName(makeRandomHexString(40))
+}
+
+function getPipeName(name: string): string {
+  const fullName = 'coc-tsc-' + name
+  if (process.platform === 'win32') {
+    return '\\\\.\\pipe\\' + fullName + '-sock'
+  }
+
+  // Mac/Unix: use socket file
+  return path.join(os.tmpdir(), fullName + '.sock')
+}
+
+export function getTempFile(name: string): string {
+  const fullName = 'coc-nvim-' + name
+  return path.join(os.tmpdir(), fullName + '.sock')
+}
+
+function generatePatchedEnv(
+  env: any,
+  stdInPipeName: string,
+  stdOutPipeName: string,
+  stdErrPipeName: string
+): any {
+  const newEnv = Object.assign({}, env)
+
+  // Set the two unique pipe names and the electron flag as process env
+  newEnv['STDIN_PIPE_NAME'] = stdInPipeName // tslint:disable-line
+  newEnv['STDOUT_PIPE_NAME'] = stdOutPipeName // tslint:disable-line
+  newEnv['STDERR_PIPE_NAME'] = stdErrPipeName // tslint:disable-line
+  newEnv['TSS_LOG'] = `-level verbose -file ${path.join(os.tmpdir(), 'coc-nvim-tsc.log')}` // tslint:disable-line
+
+  // Ensure we always have a PATH set
+  newEnv['PATH'] = newEnv['PATH'] || process.env.PATH // tslint:disable-line
+  return newEnv
+}
+
+export function fork(
+  modulePath: string,
+  args: string[],
+  options: IForkOptions,
+  logger: Logger,
+  callback: (error: any, cp: cp.ChildProcess | null) => void
+): void {
+  let callbackCalled = false
+  const resolve = (result: cp.ChildProcess) => {
+    if (callbackCalled) {
+      return
+    }
+    callbackCalled = true
+    callback(null, result)
+  }
+  const reject = (err: any) => {
+    if (callbackCalled) {
+      return
+    }
+    callbackCalled = true
+    callback(err, null)
+  }
+
+  // Generate three unique pipe names
+  const stdInPipeName = generatePipeName()
+  const stdOutPipeName = generatePipeName()
+  const stdErrPipeName = generatePipeName()
+
+  const newEnv = generatePatchedEnv(
+    process.env,
+    stdInPipeName,
+    stdOutPipeName,
+    stdErrPipeName
+  )
+  newEnv['NODE_PATH'] = path.join(modulePath, '..', '..', '..') // tslint:disable-line
+
+  let childProcess: cp.ChildProcess
+  // Begin listening to stderr pipe
+  let stdErrServer = net.createServer(stdErrStream => {
+    // From now on the childProcess.stderr is available for reading
+    childProcess.stderr = stdErrStream
+  })
+  stdErrServer.listen(stdErrPipeName)
+
+  // Begin listening to stdout pipe
+  let stdOutServer = net.createServer(stdOutStream => {
+    // The child process will write exactly one chunk with content `ready` when it has installed a listener to the stdin pipe
+
+    stdOutStream.once('data', (_chunk: Buffer) => {
+      // The child process is sending me the `ready` chunk, time to connect to the stdin pipe
+      childProcess.stdin = net.connect(stdInPipeName) as any
+
+      // From now on the childProcess.stdout is available for reading
+      childProcess.stdout = stdOutStream
+
+      resolve(childProcess)
+    })
+  })
+  stdOutServer.listen(stdOutPipeName)
+
+  let serverClosed = false
+  const closeServer = () => {
+    if (serverClosed) {
+      return
+    }
+    serverClosed = true
+    stdOutServer.close()
+    stdErrServer.close()
+  }
+
+  // Create the process
+  logger.info('Forking TSServer', `PATH: ${newEnv['PATH']} `)
+
+  const bootstrapperPath = path.join(workspace.pluginRoot, 'bin/tsserverForkStart')
+  childProcess = cp.fork(bootstrapperPath, [modulePath].concat(args), {
+    silent: true,
+    env: newEnv,
+    execArgv: options.execArgv
+  })
+
+  childProcess.once('error', (err: Error) => {
+    closeServer()
+    reject(err)
+  })
+
+  childProcess.once('exit', (err: Error) => {
+    closeServer()
+    reject(err)
+  })
+}
diff --git a/src/server/utils/regexp.ts b/src/server/utils/regexp.ts
new file mode 100644
index 0000000..274a4b2
--- /dev/null
+++ b/src/server/utils/regexp.ts
@@ -0,0 +1,8 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+export function escapeRegExp(text: string): string {
+  return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
+}
diff --git a/src/server/utils/tracer.ts b/src/server/utils/tracer.ts
new file mode 100644
index 0000000..4583be7
--- /dev/null
+++ b/src/server/utils/tracer.ts
@@ -0,0 +1,101 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { workspace } from 'coc.nvim'
+import * as Proto from '../protocol'
+import Logger from './logger'
+
+enum Trace {
+  Off,
+  Messages,
+  Verbose
+}
+
+namespace Trace {
+  export function fromString(value: string): Trace {
+    value = value || ''
+    value = value.toLowerCase()
+    switch (value) {
+      case 'off':
+        return Trace.Off
+      case 'messages':
+        return Trace.Messages
+      case 'verbose':
+        return Trace.Verbose
+      default:
+        return Trace.Off
+    }
+  }
+}
+
+export default class Tracer {
+  private trace?: Trace
+
+  constructor(private readonly logger: Logger) {
+    this.trace = Tracer.readTrace()
+  }
+
+  private static readTrace(): Trace {
+    let result: Trace = Trace.fromString(workspace.getConfiguration('tsserver').get<string>('trace.server', 'off'))
+    if (result === Trace.Off && !!process.env.TSS_TRACE) {
+      result = Trace.Messages
+    }
+    return result
+  }
+
+  public traceRequest(
+    request: Proto.Request,
+    responseExpected: boolean,
+    queueLength: number
+  ): void {
+    if (this.trace === Trace.Off) return
+    let data: string | undefined
+    if (this.trace === Trace.Verbose && request.arguments) {
+      data = `Arguments: ${JSON.stringify(request.arguments, null, 4)}`
+    }
+    this.logTrace(
+      `Sending request: ${request.command} (${
+      request.seq
+      }). Response expected: ${
+      responseExpected ? 'yes' : 'no'
+      }. Current queue length: ${queueLength}`,
+      data
+    )
+  }
+
+  public traceResponse(response: Proto.Response, startTime: number): void {
+    if (this.trace === Trace.Off) {
+      return
+    }
+    let data: string | undefined
+    if (this.trace === Trace.Verbose && response.body) {
+      data = `Result: ${JSON.stringify(response.body, null, 4)}`
+    }
+    this.logTrace(
+      `Response received: ${response.command} (${
+      response.request_seq
+      }). Request took ${Date.now() - startTime} ms. Success: ${
+      response.success
+      } ${!response.success ? '. Message: ' + response.message : ''}`,
+      data
+    )
+  }
+
+  public traceEvent(event: Proto.Event): void {
+    if (this.trace === Trace.Off) {
+      return
+    }
+    let data: string | undefined
+    if (this.trace === Trace.Verbose && event.body) {
+      data = `Data: ${JSON.stringify(event.body, null, 4)}`
+    }
+    this.logTrace(`Event received: ${event.event} (${event.seq}).`, data)
+  }
+
+  public logTrace(message: string, data?: any): void {
+    if (this.trace !== Trace.Off) {
+      this.logger.logLevel('Trace', message, data)
+    }
+  }
+}
diff --git a/src/server/utils/tsconfig.ts b/src/server/utils/tsconfig.ts
new file mode 100644
index 0000000..01e50f2
--- /dev/null
+++ b/src/server/utils/tsconfig.ts
@@ -0,0 +1,26 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import * as Proto from '../protocol'
+import { TypeScriptServiceConfiguration } from './configuration'
+
+export function inferredProjectConfig(
+  config: TypeScriptServiceConfiguration
+): Proto.ExternalProjectCompilerOptions {
+  const base: Proto.ExternalProjectCompilerOptions = {
+    module: 'commonjs' as Proto.ModuleKind,
+    target: 'es2016' as Proto.ScriptTarget,
+    jsx: 'preserve' as Proto.JsxEmit
+  }
+
+  if (config.checkJs) {
+    base.checkJs = true
+  }
+
+  if (config.experimentalDecorators) {
+    base.experimentalDecorators = true
+  }
+
+  return base
+}
diff --git a/src/server/utils/typeConverters.ts b/src/server/utils/typeConverters.ts
new file mode 100644
index 0000000..0906bf5
--- /dev/null
+++ b/src/server/utils/typeConverters.ts
@@ -0,0 +1,91 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+/**
+ * Helpers for converting FROM LanguageServer types language-server ts types
+ */
+import * as language from 'vscode-languageserver-protocol'
+import Proto from '../protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+
+export namespace Range {
+  export const fromTextSpan = (span: Proto.TextSpan): language.Range => {
+    return {
+      start: {
+        line: span.start.line - 1,
+        character: span.start.offset - 1
+      },
+      end: {
+        line: span.end.line - 1,
+        character: span.end.offset - 1
+      }
+    }
+  }
+
+  export const toFileRangeRequestArgs = (
+    file: string,
+    range: language.Range
+  ): Proto.FileRangeRequestArgs => ({
+    file,
+    startLine: range.start.line + 1,
+    startOffset: range.start.character + 1,
+    endLine: range.end.line + 1,
+    endOffset: range.end.character + 1
+  })
+}
+
+export namespace Position {
+  export const fromLocation = (tslocation: Proto.Location): language.Position => {
+    return {
+      line: tslocation.line - 1,
+      character: tslocation.offset - 1
+    }
+  }
+
+  export const toFileLocationRequestArgs = (
+    file: string,
+    position: language.Position
+  ): Proto.FileLocationRequestArgs => ({
+    file,
+    line: position.line + 1,
+    offset: position.character + 1
+  })
+}
+
+export namespace Location {
+  export const fromTextSpan = (
+    uri: string,
+    tsTextSpan: Proto.TextSpan
+  ): language.Location => {
+    return {
+      uri,
+      range: Range.fromTextSpan(tsTextSpan)
+    }
+  }
+}
+
+export namespace TextEdit {
+  export const fromCodeEdit = (edit: Proto.CodeEdit): language.TextEdit => {
+    return {
+      range: Range.fromTextSpan(edit),
+      newText: edit.newText
+    }
+  }
+}
+
+export namespace WorkspaceEdit {
+  export function fromFileCodeEdits(
+    client: ITypeScriptServiceClient,
+    edits: Iterable<Proto.FileCodeEdits>
+  ): language.WorkspaceEdit {
+    let changes = {}
+    for (const edit of edits) {
+      let uri = client.toResource(edit.fileName)
+      changes[uri] = edit.textChanges.map(change => {
+        return TextEdit.fromCodeEdit(change)
+      })
+    }
+    return { changes }
+  }
+}
diff --git a/src/server/utils/typingsStatus.ts b/src/server/utils/typingsStatus.ts
new file mode 100644
index 0000000..60418dd
--- /dev/null
+++ b/src/server/utils/typingsStatus.ts
@@ -0,0 +1,115 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import { Disposable } from 'vscode-languageserver-protocol'
+import { ITypeScriptServiceClient } from '../typescriptService'
+import { workspace } from 'coc.nvim'
+
+const typingsInstallTimeout = 30 * 1000
+
+export default class TypingsStatus implements Disposable {
+  private _acquiringTypings: { [eventId: string]: NodeJS.Timer } = Object.create(
+    {}
+  )
+  private _client: ITypeScriptServiceClient
+  private _subscriptions: Disposable[] = []
+
+  constructor(client: ITypeScriptServiceClient) {
+    this._client = client
+    this._subscriptions.push(
+      this._client.onDidBeginInstallTypings(event =>
+        this.onBeginInstallTypings(event.eventId)
+      )
+    )
+
+    this._subscriptions.push(
+      this._client.onDidEndInstallTypings(event =>
+        this.onEndInstallTypings(event.eventId)
+      )
+    )
+  }
+
+  public dispose(): void {
+    this._subscriptions.forEach(x => x.dispose())
+
+    for (const eventId of Object.keys(this._acquiringTypings)) {
+      clearTimeout(this._acquiringTypings[eventId])
+    }
+  }
+
+  public get isAcquiringTypings(): boolean {
+    return Object.keys(this._acquiringTypings).length > 0
+  }
+
+  private onBeginInstallTypings(eventId: number): void {
+    if (this._acquiringTypings[eventId]) {
+      return
+    }
+    this._acquiringTypings[eventId] = setTimeout(() => {
+      this.onEndInstallTypings(eventId)
+    }, typingsInstallTimeout)
+  }
+
+  private onEndInstallTypings(eventId: number): void {
+    const timer = this._acquiringTypings[eventId]
+    if (timer) {
+      clearTimeout(timer)
+    }
+    delete this._acquiringTypings[eventId]
+  }
+}
+
+export class AtaProgressReporter {
+  private _promises = new Map<number, Function>()
+  private _disposable: Disposable
+  private _invalid = false
+
+  constructor(client: ITypeScriptServiceClient) {
+    const disposables: Disposable[] = []
+    disposables.push(client.onDidBeginInstallTypings(e => this._onBegin(e.eventId)))
+    disposables.push(client.onDidEndInstallTypings(e => this._onEndOrTimeout(e.eventId)))
+    disposables.push(client.onTypesInstallerInitializationFailed(_ =>
+      this.onTypesInstallerInitializationFailed()
+    ))
+    this._disposable = Disposable.create(() => {
+      disposables.forEach(disposable => {
+        disposable.dispose()
+      })
+    })
+  }
+
+  public dispose(): void {
+    this._disposable.dispose()
+    this._promises.forEach(value => value())
+  }
+
+  private _onBegin(eventId: number): void {
+    const handle = setTimeout(
+      () => this._onEndOrTimeout(eventId),
+      typingsInstallTimeout
+    )
+    new Promise(resolve => { // tslint:disable-line
+      this._promises.set(eventId, () => {
+        clearTimeout(handle)
+        resolve()
+      })
+    })
+    workspace.showMessage('Fetching data for better TypeScript IntelliSense')
+  }
+
+  private _onEndOrTimeout(eventId: number): void {
+    const resolve = this._promises.get(eventId)
+    if (resolve) {
+      this._promises.delete(eventId)
+      resolve()
+    }
+  }
+
+  private onTypesInstallerInitializationFailed() { // tslint:disable-line
+    if (!this._invalid) {
+      workspace.showMessage('Could not install typings files for JavaScript language features. Please ensure that NPM is installed', 'error')
+    }
+    this._invalid = true
+  }
+}
diff --git a/src/server/utils/versionProvider.ts b/src/server/utils/versionProvider.ts
new file mode 100644
index 0000000..66911fd
--- /dev/null
+++ b/src/server/utils/versionProvider.ts
@@ -0,0 +1,137 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import fs from 'fs'
+import path from 'path'
+import { getParentDirs } from './fs'
+import { workspace } from 'coc.nvim'
+import API from './api'
+import { TypeScriptServiceConfiguration } from './configuration'
+
+export class TypeScriptVersion {
+  private _api: API | null | undefined
+  constructor(
+    public readonly path: string,
+    private readonly _pathLabel?: string
+  ) {
+    this._api = null
+  }
+
+  public get tsServerPath(): string {
+    return path.join(this.path, 'tsserver.js')
+  }
+
+  public get pathLabel(): string {
+    return typeof this._pathLabel === 'undefined' ? this.path : this._pathLabel
+  }
+
+  public get isValid(): boolean {
+    return this.version != null
+  }
+
+  public get version(): API | null {
+    if (this._api) return this._api
+    let api = this._api = this.getTypeScriptVersion(this.tsServerPath)
+    return api
+  }
+
+  public get versionString(): string | null {
+    const version = this.version
+    return version ? version.versionString : null
+
+  }
+
+  private getTypeScriptVersion(serverPath: string): API | undefined {
+    if (!fs.existsSync(serverPath)) {
+      return undefined
+    }
+
+    const p = serverPath.split(path.sep)
+    if (p.length <= 2) {
+      return undefined
+    }
+    const p2 = p.slice(0, -2)
+    const modulePath = p2.join(path.sep)
+    let fileName = path.join(modulePath, 'package.json')
+    if (!fs.existsSync(fileName)) {
+      // Special case for ts dev versions
+      if (path.basename(modulePath) === 'built') {
+        fileName = path.join(modulePath, '..', 'package.json')
+      }
+    }
+    if (!fs.existsSync(fileName)) {
+      return undefined
+    }
+
+    const contents = fs.readFileSync(fileName).toString()
+    let desc: any = null
+    try {
+      desc = JSON.parse(contents)
+    } catch (err) {
+      return undefined
+    }
+    if (!desc || !desc.version) {
+      return undefined
+    }
+    return desc.version ? API.fromVersionString(desc.version) : undefined
+  }
+}
+
+export class TypeScriptVersionProvider {
+
+  public constructor(private configuration: TypeScriptServiceConfiguration) { }
+
+  public updateConfiguration(
+    configuration: TypeScriptServiceConfiguration
+  ): void {
+    this.configuration = configuration
+  }
+
+  public async getDefaultVersion(): Promise<TypeScriptVersion> {
+    // tsdk from configuration
+    let { globalTsdk } = this.configuration
+    if (globalTsdk) return new TypeScriptVersion(globalTsdk)
+    // resolve global module
+    let modulePath = await workspace.resolveModule('typescript', 'tsserver')
+    if (modulePath) {
+      let p = path.join(modulePath, 'lib')
+      return new TypeScriptVersion(p)
+    }
+    // use bundled
+    return this.bundledVersion
+  }
+
+  public get globalVersion(): TypeScriptVersion | undefined {
+    let { globalTsdk } = this.configuration
+    if (globalTsdk) return new TypeScriptVersion(globalTsdk)
+    return undefined
+  }
+
+  public getLocalVersion(root): TypeScriptVersion | undefined {
+    let paths = getParentDirs(root)
+    paths.unshift(root)
+    for (let p of paths) {
+      if (fs.existsSync(path.join(p, 'node_modules'))) {
+        let lib = path.join(p, 'node_modules/typescript/lib')
+        return new TypeScriptVersion(lib)
+      }
+    }
+    return null
+  }
+
+  public get bundledVersion(): TypeScriptVersion | null {
+    let file = path.join(workspace.pluginRoot, 'node_modules/typescript/lib/tsserver.js')
+    if (!fs.existsSync(file)) return null
+    try {
+      const bundledVersion = new TypeScriptVersion(
+        path.dirname(file),
+        ''
+      )
+      return bundledVersion
+    } catch (e) {
+      // noop
+    }
+    return null
+  }
+}
diff --git a/src/server/utils/wireProtocol.ts b/src/server/utils/wireProtocol.ts
new file mode 100644
index 0000000..f71b11f
--- /dev/null
+++ b/src/server/utils/wireProtocol.ts
@@ -0,0 +1,138 @@
+/*---------------------------------------------------------------------------------------------
+ *  Copyright (c) Microsoft Corporation. All rights reserved.
+ *  Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import stream from 'stream'
+
+const DefaultSize = 8192
+const ContentLength = 'Content-Length: '
+const ContentLengthSize: number = Buffer.byteLength(ContentLength, 'utf8')
+const Blank: number = Buffer.from(' ', 'utf8')[0]
+const BackslashR: number = Buffer.from('\r', 'utf8')[0]
+const BackslashN: number = Buffer.from('\n', 'utf8')[0]
+
+class ProtocolBuffer {
+  private index = 0
+  private buffer: Buffer = Buffer.allocUnsafe(DefaultSize)
+
+  public append(data: string | Buffer): void {
+    let toAppend: Buffer | null = null
+    if (Buffer.isBuffer(data)) {
+      toAppend = data as Buffer
+    } else {
+      toAppend = Buffer.from(data, 'utf8')
+    }
+    if (this.buffer.length - this.index >= toAppend.length) {
+      toAppend.copy(this.buffer, this.index, 0, toAppend.length)
+    } else {
+      let newSize =
+        (Math.ceil((this.index + toAppend.length) / DefaultSize) + 1) *
+        DefaultSize
+      if (this.index === 0) {
+        this.buffer = Buffer.allocUnsafe(newSize)
+        toAppend.copy(this.buffer, 0, 0, toAppend.length)
+      } else {
+        this.buffer = Buffer.concat(
+          [this.buffer.slice(0, this.index), toAppend],
+          newSize
+        )
+      }
+    }
+    this.index += toAppend.length
+  }
+
+  public tryReadContentLength(): number {
+    let result = -1
+    let current = 0
+    // we are utf8 encoding...
+    while (
+      current < this.index &&
+      (this.buffer[current] === Blank ||
+        this.buffer[current] === BackslashR ||
+        this.buffer[current] === BackslashN)
+    ) {
+      current++
+    }
+    if (this.index < current + ContentLengthSize) {
+      return result
+    }
+    current += ContentLengthSize
+    let start = current
+    while (current < this.index && this.buffer[current] !== BackslashR) {
+      current++
+    }
+    if (
+      current + 3 >= this.index ||
+      this.buffer[current + 1] !== BackslashN ||
+      this.buffer[current + 2] !== BackslashR ||
+      this.buffer[current + 3] !== BackslashN
+    ) {
+      return result
+    }
+    let data = this.buffer.toString('utf8', start, current)
+    result = parseInt(data, 10)
+    this.buffer = this.buffer.slice(current + 4)
+    this.index = this.index - (current + 4)
+    return result
+  }
+
+  public tryReadContent(length: number): string | null {
+    if (this.index < length) {
+      return null
+    }
+    let result = this.buffer.toString('utf8', 0, length)
+    let sourceStart = length
+    while (
+      sourceStart < this.index &&
+      (this.buffer[sourceStart] === BackslashR ||
+        this.buffer[sourceStart] === BackslashN)
+    ) {
+      sourceStart++
+    }
+    this.buffer.copy(this.buffer, 0, sourceStart)
+    this.index = this.index - sourceStart
+    return result
+  }
+}
+
+export interface ICallback<T> {
+  (data: T): void // tslint:disable-line
+}
+
+export class Reader<T> {
+  private readonly buffer: ProtocolBuffer = new ProtocolBuffer()
+  private nextMessageLength = -1
+
+  public constructor(
+    private readonly readable: stream.Readable,
+    private readonly callback: ICallback<T>,
+    private readonly onError: (error: any) => void
+  ) {
+    this.readable.on('data', (data: Buffer) => {
+      this.onLengthData(data)
+    })
+  }
+
+  private onLengthData(data: Buffer): void {
+    try {
+      this.buffer.append(data)
+      while (true) {
+        if (this.nextMessageLength === -1) {
+          this.nextMessageLength = this.buffer.tryReadContentLength()
+          if (this.nextMessageLength === -1) {
+            return
+          }
+        }
+        const msg = this.buffer.tryReadContent(this.nextMessageLength)
+        if (msg === null) {
+          return
+        }
+        this.nextMessageLength = -1
+        const json = JSON.parse(msg)
+        this.callback(json)
+      }
+    } catch (e) {
+      this.onError(e)
+    }
+  }
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..4a9578e
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,18 @@
+{
+  "extends": "./node_modules/@chemzqm/tsconfig/tsconfig.json",
+  "compilerOptions": {
+    "outDir": "lib",
+    "target": "es2015",
+    "module": "commonjs",
+    "moduleResolution": "node",
+    "noImplicitThis": true,
+    "importHelpers": true,
+    "lib": ["es2018"],
+    "plugins": []
+  },
+  "include": [
+    "src"
+  ],
+  "exclude": [
+  ]
+}
diff --git a/tslint.json b/tslint.json
new file mode 100644
index 0000000..3bb26c0
--- /dev/null
+++ b/tslint.json
@@ -0,0 +1,10 @@
+{
+  "extends": "./node_modules/@chemzqm/tslint-config/tslint.json",
+  "rules": {
+  },
+  "linterOptions": {
+    "exclude": [
+    ]
+  }
+}
+
diff --git a/yarn.lock b/yarn.lock
new file mode 100644
index 0000000..c3c55c8
--- /dev/null
+++ b/yarn.lock
@@ -0,0 +1,644 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/code-frame@7.0.0-beta.44":
+  version "7.0.0-beta.44"
+  resolved "http://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz#2a02643368de80916162be70865c97774f3adbd9"
+  dependencies:
+    "@babel/highlight" "7.0.0-beta.44"
+
+"@babel/generator@7.0.0-beta.44":
+  version "7.0.0-beta.44"
+  resolved "http://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.44.tgz#c7e67b9b5284afcf69b309b50d7d37f3e5033d42"
+  dependencies:
+    "@babel/types" "7.0.0-beta.44"
+    jsesc "^2.5.1"
+    lodash "^4.2.0"
+    source-map "^0.5.0"
+    trim-right "^1.0.1"
+
+"@babel/helper-function-name@7.0.0-beta.44":
+  version "7.0.0-beta.44"
+  resolved "http://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.44.tgz#e18552aaae2231100a6e485e03854bc3532d44dd"
+  dependencies:
+    "@babel/helper-get-function-arity" "7.0.0-beta.44"
+    "@babel/template" "7.0.0-beta.44"
+    "@babel/types" "7.0.0-beta.44"
+
+"@babel/helper-get-function-arity@7.0.0-beta.44":
+  version "7.0.0-beta.44"
+  resolved "http://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.44.tgz#d03ca6dd2b9f7b0b1e6b32c56c72836140db3a15"
+  dependencies:
+    "@babel/types" "7.0.0-beta.44"
+
+"@babel/helper-split-export-declaration@7.0.0-beta.44":
+  version "7.0.0-beta.44"
+  resolved "http://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz#c0b351735e0fbcb3822c8ad8db4e583b05ebd9dc"
+  dependencies:
+    "@babel/types" "7.0.0-beta.44"
+
+"@babel/highlight@7.0.0-beta.44":
+  version "7.0.0-beta.44"
+  resolved "http://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.44.tgz#18c94ce543916a80553edcdcf681890b200747d5"
+  dependencies:
+    chalk "^2.0.0"
+    esutils "^2.0.2"
+    js-tokens "^3.0.0"
+
+"@babel/template@7.0.0-beta.44":
+  version "7.0.0-beta.44"
+  resolved "http://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f"
+  dependencies:
+    "@babel/code-frame" "7.0.0-beta.44"
+    "@babel/types" "7.0.0-beta.44"
+    babylon "7.0.0-beta.44"
+    lodash "^4.2.0"
+
+"@babel/traverse@7.0.0-beta.44":
+  version "7.0.0-beta.44"
+  resolved "http://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.44.tgz#a970a2c45477ad18017e2e465a0606feee0d2966"
+  dependencies:
+    "@babel/code-frame" "7.0.0-beta.44"
+    "@babel/generator" "7.0.0-beta.44"
+    "@babel/helper-function-name" "7.0.0-beta.44"
+    "@babel/helper-split-export-declaration" "7.0.0-beta.44"
+    "@babel/types" "7.0.0-beta.44"
+    babylon "7.0.0-beta.44"
+    debug "^3.1.0"
+    globals "^11.1.0"
+    invariant "^2.2.0"
+    lodash "^4.2.0"
+
+"@babel/types@7.0.0-beta.44":
+  version "7.0.0-beta.44"
+  resolved "http://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.44.tgz#6b1b164591f77dec0a0342aca995f2d046b3a757"
+  dependencies:
+    esutils "^2.0.2"
+    lodash "^4.2.0"
+    to-fast-properties "^2.0.0"
+
+"@chemzqm/neovim@4.3.23":
+  version "4.3.23"
+  resolved "https://registry.yarnpkg.com/@chemzqm/neovim/-/neovim-4.3.23.tgz#85385db1bedde01593be5f5ea7cbf7db10a868aa"
+  dependencies:
+    babel-eslint "^8.2.6"
+    msgpack-lite "^0.1.26"
+    traverse "^0.6.6"
+
+"@chemzqm/tsconfig@^0.0.3":
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/@chemzqm/tsconfig/-/tsconfig-0.0.3.tgz#ce3480d15d8cec46a315488caa07c9fca819aecc"
+
+"@chemzqm/tslint-config@^1.0.17":
+  version "1.0.17"
+  resolved "https://registry.yarnpkg.com/@chemzqm/tslint-config/-/tslint-config-1.0.17.tgz#9365dc9bdece0927fdfa6c068e1c70524c35a5fc"
+  dependencies:
+    tslint-config-prettier "^1.6.0"
+    tslint-react "^3.2.0"
+
+"@types/node@^10.9.4":
+  version "10.9.4"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-10.9.4.tgz#0f4cb2dc7c1de6096055357f70179043c33e9897"
+
+ansi-regex@^2.0.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+
+ansi-styles@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
+
+ansi-styles@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+  dependencies:
+    color-convert "^1.9.0"
+
+argparse@^1.0.7:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+  dependencies:
+    sprintf-js "~1.0.2"
+
+babel-code-frame@^6.22.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
+  dependencies:
+    chalk "^1.1.3"
+    esutils "^2.0.2"
+    js-tokens "^3.0.2"
+
+babel-eslint@^8.2.6:
+  version "8.2.6"
+  resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-8.2.6.tgz#6270d0c73205628067c0f7ae1693a9e797acefd9"
+  dependencies:
+    "@babel/code-frame" "7.0.0-beta.44"
+    "@babel/traverse" "7.0.0-beta.44"
+    "@babel/types" "7.0.0-beta.44"
+    babylon "7.0.0-beta.44"
+    eslint-scope "3.7.1"
+    eslint-visitor-keys "^1.0.0"
+
+babylon@7.0.0-beta.44:
+  version "7.0.0-beta.44"
+  resolved "http://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz#89159e15e6e30c5096e22d738d8c0af8a0e8ca1d"
+
+balanced-match@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+
+brace-expansion@^1.1.7:
+  version "1.1.11"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+  dependencies:
+    balanced-match "^1.0.0"
+    concat-map "0.0.1"
+
+bser@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719"
+  dependencies:
+    node-int64 "^0.4.0"
+
+builtin-modules@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
+
+chalk@^1.1.3:
+  version "1.1.3"
+  resolved "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
+  dependencies:
+    ansi-styles "^2.2.1"
+    escape-string-regexp "^1.0.2"
+    has-ansi "^2.0.0"
+    strip-ansi "^3.0.0"
+    supports-color "^2.0.0"
+
+chalk@^2.0.0, chalk@^2.3.0:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
+  dependencies:
+    ansi-styles "^3.2.1"
+    escape-string-regexp "^1.0.5"
+    supports-color "^5.3.0"
+
+circular-json@^0.5.5:
+  version "0.5.5"
+  resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.5.tgz#64182ef359042d37cd8e767fc9de878b1e9447d3"
+
+coc.nvim@^0.0.15:
+  version "0.0.15"
+  resolved "https://registry.yarnpkg.com/coc.nvim/-/coc.nvim-0.0.15.tgz#e28e42e9dae92bf372b16918bf68b2e8696e7e4d"
+  dependencies:
+    "@chemzqm/neovim" "4.3.23"
+    debounce "^1.2.0"
+    deep-equal "^1.0.1"
+    diff "^3.5.0"
+    fast-diff "^1.1.2"
+    fb-watchman "^2.0.0"
+    fuzzaldrin "^2.1.0"
+    glob "^7.1.3"
+    jsonc-parser "^2.0.2"
+    log4js "^3.0.5"
+    minimatch "^3.0.4"
+    node-serial "^0.1.1"
+    once "^1.4.0"
+    pify "^4.0.0"
+    semver "^5.5.1"
+    tslib "^1.9.3"
+    uuid "^3.3.2"
+    vscode-languageserver-protocol "^3.12.0"
+    vscode-languageserver-types "^3.12.0"
+    vscode-uri "^1.0.6"
+    which "^1.3.1"
+
+color-convert@^1.9.0:
+  version "1.9.3"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+  dependencies:
+    color-name "1.1.3"
+
+color-name@1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+
+commander@^2.12.1:
+  version "2.17.1"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
+
+concat-map@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+
+core-util-is@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+
+date-format@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/date-format/-/date-format-1.2.0.tgz#615e828e233dd1ab9bb9ae0950e0ceccfa6ecad8"
+
+debounce@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.0.tgz#44a540abc0ea9943018dc0eaa95cce87f65cd131"
+
+debug@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
+  dependencies:
+    ms "2.0.0"
+
+deep-equal@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
+
+diff@^3.2.0, diff@^3.5.0:
+  version "3.5.0"
+  resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
+
+escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+
+eslint-scope@3.7.1:
+  version "3.7.1"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
+  dependencies:
+    esrecurse "^4.1.0"
+    estraverse "^4.1.1"
+
+eslint-visitor-keys@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
+
+esprima@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+
+esrecurse@^4.1.0:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf"
+  dependencies:
+    estraverse "^4.1.0"
+
+estraverse@^4.1.0, estraverse@^4.1.1:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
+
+esutils@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
+
+event-lite@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/event-lite/-/event-lite-0.1.1.tgz#47cf08a8d37d0b694cdb7b3b17b51faac6576086"
+
+fast-diff@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.2.tgz#4b62c42b8e03de3f848460b639079920695d0154"
+
+fb-watchman@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58"
+  dependencies:
+    bser "^2.0.0"
+
+fs.realpath@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+
+fuzzaldrin@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/fuzzaldrin/-/fuzzaldrin-2.1.0.tgz#90204c3e2fdaa6941bb28d16645d418063a90e9b"
+
+glob@^7.0.5, glob@^7.1.1, glob@^7.1.3:
+  version "7.1.3"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.0.4"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
+globals@^11.1.0:
+  version "11.7.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673"
+
+has-ansi@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
+  dependencies:
+    ansi-regex "^2.0.0"
+
+has-flag@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+
+ieee754@^1.1.8:
+  version "1.1.12"
+  resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b"
+
+inflight@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+  dependencies:
+    once "^1.3.0"
+    wrappy "1"
+
+inherits@2, inherits@~2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+
+int64-buffer@^0.1.9:
+  version "0.1.10"
+  resolved "https://registry.yarnpkg.com/int64-buffer/-/int64-buffer-0.1.10.tgz#277b228a87d95ad777d07c13832022406a473423"
+
+invariant@^2.2.0:
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
+  dependencies:
+    loose-envify "^1.0.0"
+
+isarray@^1.0.0, isarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+
+isexe@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+
+js-tokens@^3.0.0, js-tokens@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
+
+"js-tokens@^3.0.0 || ^4.0.0":
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+
+js-yaml@^3.7.0:
+  version "3.12.0"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1"
+  dependencies:
+    argparse "^1.0.7"
+    esprima "^4.0.0"
+
+jsesc@^2.5.1:
+  version "2.5.1"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.1.tgz#e421a2a8e20d6b0819df28908f782526b96dd1fe"
+
+jsonc-parser@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.2.tgz#42fcf56d70852a043fadafde51ddb4a85649978d"
+
+lodash@^4.2.0:
+  version "4.17.10"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
+
+log4js@^3.0.5:
+  version "3.0.5"
+  resolved "https://registry.yarnpkg.com/log4js/-/log4js-3.0.5.tgz#b80146bfebad68b430d4f3569556d8a6edfef303"
+  dependencies:
+    circular-json "^0.5.5"
+    date-format "^1.2.0"
+    debug "^3.1.0"
+    rfdc "^1.1.2"
+    streamroller "0.7.0"
+
+loose-envify@^1.0.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+  dependencies:
+    js-tokens "^3.0.0 || ^4.0.0"
+
+minimatch@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+  dependencies:
+    brace-expansion "^1.1.7"
+
+minimist@0.0.8:
+  version "0.0.8"
+  resolved "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+
+mkdirp@^0.5.1:
+  version "0.5.1"
+  resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
+  dependencies:
+    minimist "0.0.8"
+
+ms@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+
+msgpack-lite@^0.1.26:
+  version "0.1.26"
+  resolved "https://registry.yarnpkg.com/msgpack-lite/-/msgpack-lite-0.1.26.tgz#dd3c50b26f059f25e7edee3644418358e2a9ad89"
+  dependencies:
+    event-lite "^0.1.1"
+    ieee754 "^1.1.8"
+    int64-buffer "^0.1.9"
+    isarray "^1.0.0"
+
+next-tick@~0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-0.1.0.tgz#1912cce8eb9b697d640fbba94f8f00dec3b94259"
+
+node-int64@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
+
+node-serial@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/node-serial/-/node-serial-0.1.1.tgz#3766fa9614c20c6f27d3713f9bd8c56747fbb8c9"
+  dependencies:
+    next-tick "~0.1.0"
+
+once@^1.3.0, once@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+  dependencies:
+    wrappy "1"
+
+path-is-absolute@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+
+path-parse@^1.0.5:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
+
+pify@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.0.tgz#db04c982b632fd0df9090d14aaf1c8413cadb695"
+
+process-nextick-args@~2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
+
+readable-stream@^2.3.0:
+  version "2.3.6"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
+  dependencies:
+    core-util-is "~1.0.0"
+    inherits "~2.0.3"
+    isarray "~1.0.0"
+    process-nextick-args "~2.0.0"
+    safe-buffer "~5.1.1"
+    string_decoder "~1.1.1"
+    util-deprecate "~1.0.1"
+
+resolve@^1.3.2:
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26"
+  dependencies:
+    path-parse "^1.0.5"
+
+rfdc@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.2.tgz#e6e72d74f5dc39de8f538f65e00c36c18018e349"
+
+rimraf@^2.6.2:
+  version "2.6.2"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
+  dependencies:
+    glob "^7.0.5"
+
+safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+
+semver@^5.3.0, semver@^5.5.1:
+  version "5.5.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477"
+
+source-map@^0.5.0:
+  version "0.5.7"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+
+sprintf-js@~1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+
+streamroller@0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-0.7.0.tgz#a1d1b7cf83d39afb0d63049a5acbf93493bdf64b"
+  dependencies:
+    date-format "^1.2.0"
+    debug "^3.1.0"
+    mkdirp "^0.5.1"
+    readable-stream "^2.3.0"
+
+string_decoder@~1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+  dependencies:
+    safe-buffer "~5.1.0"
+
+strip-ansi@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+  dependencies:
+    ansi-regex "^2.0.0"
+
+supports-color@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
+
+supports-color@^5.3.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+  dependencies:
+    has-flag "^3.0.0"
+
+to-fast-properties@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+
+traverse@^0.6.6:
+  version "0.6.6"
+  resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137"
+
+trim-right@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
+
+tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.3:
+  version "1.9.3"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
+
+tslint-config-prettier@^1.6.0:
+  version "1.15.0"
+  resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.15.0.tgz#76b9714399004ab6831fdcf76d89b73691c812cf"
+
+tslint-react@^3.2.0:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/tslint-react/-/tslint-react-3.6.0.tgz#7f462c95c4a0afaae82507f06517ff02942196a1"
+  dependencies:
+    tsutils "^2.13.1"
+
+tslint@^5.11.0:
+  version "5.11.0"
+  resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.11.0.tgz#98f30c02eae3cde7006201e4c33cb08b48581eed"
+  dependencies:
+    babel-code-frame "^6.22.0"
+    builtin-modules "^1.1.1"
+    chalk "^2.3.0"
+    commander "^2.12.1"
+    diff "^3.2.0"
+    glob "^7.1.1"
+    js-yaml "^3.7.0"
+    minimatch "^3.0.4"
+    resolve "^1.3.2"
+    semver "^5.3.0"
+    tslib "^1.8.0"
+    tsutils "^2.27.2"
+
+tsutils@^2.13.1, tsutils@^2.27.2:
+  version "2.29.0"
+  resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99"
+  dependencies:
+    tslib "^1.8.1"
+
+typescript@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.0.3.tgz#4853b3e275ecdaa27f78fda46dc273a7eb7fc1c8"
+
+util-deprecate@~1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+
+uuid@^3.3.2:
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
+
+vscode-jsonrpc@^3.6.2:
+  version "3.6.2"
+  resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.6.2.tgz#3b5eef691159a15556ecc500e9a8a0dd143470c8"
+
+vscode-languageserver-protocol@^3.12.0:
+  version "3.12.0"
+  resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.12.0.tgz#5b23501292abad88f0463b01e83ff98e64a37652"
+  dependencies:
+    vscode-jsonrpc "^3.6.2"
+    vscode-languageserver-types "^3.12.0"
+
+vscode-languageserver-types@^3.12.0:
+  version "3.12.0"
+  resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.12.0.tgz#f96051381b6a050b7175b37d6cb5d2f2eb64b944"
+
+vscode-uri@^1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.6.tgz#6b8f141b0bbc44ad7b07e94f82f168ac7608ad4d"
+
+which@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
+  dependencies:
+    isexe "^2.0.0"
+
+wrappy@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"