pmizio / typescript-tools.nvim

⚑ TypeScript integration NeoVim deserves ⚑
MIT License
1.26k stars 36 forks source link

typescript-tools.nvim

⚑ TypeScript integration NeoVim deserves ⚑

🚧 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?

If you're interested in learning more about the technical details of the plugin, you can click here.


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.

In summary, the architecture of this plugin can be visualized as shown in the diagram below: ```lua NeoVim Tsserver Instance β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ LSP Handlers Tsserver LSP Loop β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Request β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚ Translation β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Response β”‚ β”‚ Layer β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ ◄───────────┼── β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β” β”‚ β”‚ Request β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ I/O Loop β”œβ”€β”Όβ”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ί β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Response β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ ◄─┼─┼───────────── β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ```

πŸ“¦ 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.

Example code here

```lua 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 }, } ```

The default values for preferences and format_options are in this file

πŸ’… styled-components support

Show more


To get IntelliSense for styled-components, you need to install the tsserver plugin globally, which enables support for it:

``` npm i -g @styled/typescript-styled-plugin typescript-styled-plugin ``` Now, you need to load the plugin by modifying the `settings` object as follows: ```lua require("typescript-tools").setup { settings = { ... tsserver_plugins = { -- for TypeScript v4.9+ "@styled/typescript-styled-plugin", -- or for older TypeScript versions -- "typescript-styled-plugin", }, }, } ```

Custom user commands

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

Supported LSP methods

Status Request
βœ… 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