Home

Awesome

<h1 align="center">typescript-tools.nvim</h1> <p align="center"><sup>āš” TypeScript integration NeoVim deserves āš”</sup></p>

šŸš§ Warning šŸš§

Please note that the plugin is currently in the early beta version, which means you may encounter bugs.

ā‰ļø Why?

  1. Drop in, pure lua replacement for typescript-language-server
  2. If you work on a large TS/JS project, you probably understand why this plugin came into existence. The typescript-language-server can be extremely slow in such projects, and it often fails to provide accurate completions or just crash.

āœØ Features

code_action

šŸš€ How it works?

<details> <summary> If you're interested in learning more about the technical details of the plugin, you can click here. </summary> <p> <br> This plugin functions exactly like the bundled TypeScript support extension in Visual Studio Code. Thanks to the new (0.8.0) NeoVim API, it is now possible to pass a Lua function as the LSP start command. As a result, the plugin spawns a custom version of the I/O loop to communicate directly with Tsserver using its native protocol, without the need for any additional proxy. The Tsserver protocol, which is a JSON-based communication protocol, likely served as inspiration for the LSP. However, it is incompatible with the LSP. To address this, the I/O loop provided by this plugin features a translation layer that converts all messages to and from the Tsserver format. </p>

In summary, the architecture of this plugin can be visualized as shown in the diagram below:

 NeoVim                                                    Tsserver Instance
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”            ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
ā”‚                                            ā”‚            ā”‚                ā”‚
ā”‚  LSP Handlers          Tsserver LSP Loop   ā”‚            ā”‚                ā”‚
ā”‚ ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”           ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” ā”‚            ā”‚                ā”‚
ā”‚ ā”‚         ā”‚           ā”‚                  ā”‚ ā”‚            ā”‚                ā”‚
ā”‚ ā”‚         ā”‚ Request   ā”‚ ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” ā”‚ ā”‚            ā”‚                ā”‚
ā”‚ ā”‚         ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤ā–ŗā”‚ Translation  ā”‚ ā”‚ ā”‚            ā”‚                ā”‚
ā”‚ ā”‚         ā”‚ Response  ā”‚ ā”‚    Layer     ā”‚ ā”‚ ā”‚            ā”‚                ā”‚
ā”‚ ā”‚         ā—„ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”¤              ā”‚ ā”‚ ā”‚            ā”‚                ā”‚
ā”‚ ā”‚         ā”‚           ā”‚ ā””ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā–²ā”€ā”€ā”€ā”€ā”˜ ā”‚ ā”‚            ā”‚                ā”‚
ā”‚ ā”‚         ā”‚           ā”‚     ā”‚     ā”‚      ā”‚ ā”‚            ā”‚                ā”‚
ā”‚ ā”‚         ā”‚           ā”‚ ā”Œā”€ā”€ā”€ā–¼ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā” ā”‚ ā”‚ Request    ā”‚                ā”‚
ā”‚ ā”‚         ā”‚           ā”‚ ā”‚   I/O Loop   ā”œā”€ā”¼ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā–ŗ                ā”‚
ā”‚ ā”‚         ā”‚           ā”‚ ā”‚              ā”‚ ā”‚ ā”‚ Response   ā”‚                ā”‚
ā”‚ ā”‚         ā”‚           ā”‚ ā”‚              ā—„ā”€ā”¼ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤                ā”‚
ā”‚ ā”‚         ā”‚           ā”‚ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ ā”‚ ā”‚            ā”‚                ā”‚
ā”‚ ā”‚         ā”‚           ā”‚                  ā”‚ ā”‚            ā”‚                ā”‚
ā”‚ ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜           ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ ā”‚            ā”‚                ā”‚
ā”‚                                            ā”‚            ā”‚                ā”‚
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜            ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
</details>

šŸ“¦ Installation

ā—ļø IMPORTANT: As mentioned earlier, this plugin serves as a replacement for typescript-language-server, so you should remove the nvim-lspconfig setup for it.

āš”ļø Requirements

lazy.nvim

{
  "pmizio/typescript-tools.nvim",
  dependencies = { "nvim-lua/plenary.nvim", "neovim/nvim-lspconfig" },
  opts = {},
}

packer.nvim

use {
  "pmizio/typescript-tools.nvim",
  requires = { "nvim-lua/plenary.nvim", "neovim/nvim-lspconfig" },
  config = function()
    require("typescript-tools").setup {}
  end,
}

āš™ļø Configuration

The parameters passed into the setup function are also passed to the standard nvim-lspconfig server setup, allowing you to use the same settings here. But you can pass plugin-specific options through the settings parameter, which defaults to:

require("typescript-tools").setup {
  on_attach = function() ... end,
  handlers = { ... },
  ...
  settings = {
    -- spawn additional tsserver instance to calculate diagnostics on it
    separate_diagnostic_server = true,
    -- "change"|"insert_leave" determine when the client asks the server about diagnostic
    publish_diagnostic_on = "insert_leave",
    -- array of strings("fix_all"|"add_missing_imports"|"remove_unused"|
    -- "remove_unused_imports"|"organize_imports") -- or string "all"
    -- to include all supported code actions
    -- specify commands exposed as code_actions
    expose_as_code_action = {},
    -- string|nil - specify a custom path to `tsserver.js` file, if this is nil or file under path
    -- not exists then standard path resolution strategy is applied
    tsserver_path = nil,
    -- specify a list of plugins to load by tsserver, e.g., for support `styled-components`
    -- (see šŸ’… `styled-components` support section)
    tsserver_plugins = {},
    -- this value is passed to: https://nodejs.org/api/cli.html#--max-old-space-sizesize-in-megabytes
    -- memory limit in megabytes or "auto"(basically no limit)
    tsserver_max_memory = "auto",
    -- described below
    tsserver_format_options = {},
    tsserver_file_preferences = {},
    -- locale of all tsserver messages, supported locales you can find here:
    -- https://github.com/microsoft/TypeScript/blob/3c221fc086be52b19801f6e8d82596d04607ede6/src/compiler/utilitiesPublic.ts#L620
    tsserver_locale = "en",
    -- mirror of VSCode's `typescript.suggest.completeFunctionCalls`
    complete_function_calls = false,
    include_completions_with_insert_text = true,
    -- CodeLens
    -- WARNING: Experimental feature also in VSCode, because it might hit performance of server.
    -- possible values: ("off"|"all"|"implementations_only"|"references_only")
    code_lens = "off",
    -- by default code lenses are displayed on all referencable values and for some of you it can
    -- be too much this option reduce count of them by removing member references from lenses
    disable_member_code_lens = true,
    -- JSXCloseTag
    -- WARNING: it is disabled by default (maybe you configuration or distro already uses nvim-ts-autotag,
    -- that maybe have a conflict if enable this feature. )
    jsx_close_tag = {
        enable = false,
        filetypes = { "javascriptreact", "typescriptreact" },
    }
  },
}

Note that handlers can be used to override certain LSP methods. For example, you can use the filter_diagnostics helper to ignore specific errors:

local api = require("typescript-tools.api")
require("typescript-tools").setup {
  handlers = {
    ["textDocument/publishDiagnostics"] = api.filter_diagnostics(
      -- Ignore 'This may be converted to an async function' diagnostics.
      { 80006 }
    ),
  },
}

You can also pass custom configuration options that will be passed to tsserver instance. You can find available options in typescript repository (e.g. for version 5.0.4 of typescript):

To pass those options to plugin pass them to the plugin setup function:

require("typescript-tools").setup {
  settings = {
    ...
    tsserver_file_preferences = {
      includeInlayParameterNameHints = "all",
      includeCompletionsForModuleExports = true,
      quotePreference = "auto",
      ...
    },
    tsserver_format_options = {
      allowIncompleteCompletions = false,
      allowRenameOfImportPath = false,
      ...
    }
  },
}

If you want to make tsserver_format_options or tsserver_file_preferences filetype dependant you need to may set them as functions returning tables eg.

<details> <summary>Example code here</summary> <p>
require("typescript-tools").setup {
  settings = {
    ...
    tsserver_file_preferences = function(ft)
      -- Some "ifology" using `ft` of opened file
      return {
        includeInlayParameterNameHints = "all",
        includeCompletionsForModuleExports = true,
        quotePreference = "auto",
        ...
      }
    end,
    tsserver_format_options = function(ft)
      -- Some "ifology" using `ft` of opened file
      return {
        allowIncompleteCompletions = false,
        allowRenameOfImportPath = false,
        ...
      }
    end
  },
}
</p> </details>

The default values for preferences and format_options are in this file

šŸ’… styled-components support

<details> <summary>Show more</summary> <p> <br> To get IntelliSense for <code>styled-components</code>, you need to install the tsserver plugin globally, which enables support for it: </p>
npm i -g @styled/typescript-styled-plugin typescript-styled-plugin

Now, you need to load the plugin by modifying the settings object as follows:

require("typescript-tools").setup {
  settings = {
    ...
    tsserver_plugins = {
      -- for TypeScript v4.9+
      "@styled/typescript-styled-plugin",
      -- or for older TypeScript versions
      -- "typescript-styled-plugin",
    },
  },
}
</details>

Custom user commands

This plugin provides several custom user commands (they are only applied to current buffer):

Supported LSP methods

StatusRequest
āœ…textDocument/completion
āœ…textDocument/hover
āœ…textDocument/rename
āœ…textDocument/publishDiagnostics
āœ…textDocument/signatureHelp
āœ…textDocument/references
āœ…textDocument/definition
āœ…textDocument/typeDefinition
āœ…textDocument/implementation
āœ…textDocument/documentSymbol
āœ…textDocument/documentHighlight
āœ…textDocument/codeAction
āœ…textDocument/formatting
āœ…textDocument/rangeFormatting
āœ…textDocument/foldingRange
āœ…textDocument/semanticTokens/full (supported from TS v4.1)
āœ…textDocument/inlayHint (supported from TS v4.4)
āœ…callHierarchy/incomingCalls
āœ…callHierarchy/outgoingCalls
āœ…textDocument/codeLens
šŸš§textDocument/linkedEditingRange (planned)
āœ…workspace/symbol
āœ…workspace/willRenameFiles
āŒworkspace/applyEdit - N/A
āŒtextDocument/declaration - N/A
āŒwindow/logMessage - N/A
āŒwindow/showMessage - N/A
āŒwindow/showMessageRequest - N/A

šŸš¦ Roadmap

šŸ”Ø Development

Useful links:

šŸ› Run tests

The unit testing environment is automatically bootstrapped, just run:

make test

Or if you want to run a single test file:

make file=test_spec.lua test

šŸ’ Credits