DEV Community

Cover image for [SOLVED] Vue 3 + TypeScript + Inlay Hint support in NeoVim
Dan Walsh
Dan Walsh

Posted on

[SOLVED] Vue 3 + TypeScript + Inlay Hint support in NeoVim

It would be an understatement to say that getting stable Vue 3 language server support in NeoVim over the last 9-12 months has been smooth sailing for me, as evidenced by this lengthy GitHub issue: volar v2 no longer works.

I'd almost given up on finding "the right" solution. Everywhere I looked, others had "solved" the issue in different ways: they were using the vtsls TypeScript LSP wrapper instead of ts_ls, or they were using coc.nvim, or the now-archived null-ls.nvim. But nowhere could I simply find a reliable configuration for a Vue language server configured natively in NeoVim.

But today, I've finally got it working, and it looks like it should be for good. Massive thanks to Johnson Chu and GR3YH4TT3R93. đź‘Ź

Now I can finally say:

Frodo from Lord of the Rings saying,

TLDR, just gimme the config so I can write some sweet Vue code with that LSP-goodness

Since you asked so nicely, sure. Here are the volar and ts_ls server configs for nvim-lspconfig. If you're using something like kickstart.nvim, you'll want to add these entries to your servers table:

local servers = {
  -- Vue 3        
  volar = {},
  -- TypeScript
  ts_ls = {
    filetypes = { 'typescript', 'javascript', 'javascriptreact', 'typescriptreact', 'vue' },
    init_options = {
      plugins = {
        {
          name = '@vue/typescript-plugin',
          location = vim.fn.stdpath 'data' .. '/mason/packages/vue-language-server/node_modules/@vue/language-server',
          languages = { 'vue' },
        },
      },
    },
  },
  -- ...
}
Enter fullscreen mode Exit fullscreen mode

And for bonus points, here's the config if you want inlay hints. For this to work we need to disable hybrid mode:

local servers = {
  -- Vue 3        
  volar = {
    init_options = {
      vue = {
        hybridMode = false,
      },
    },
    settings = {
      typescript = {
        inlayHints = {
          enumMemberValues = {
            enabled = true,
          },
          functionLikeReturnTypes = {
            enabled = true,
          },
          propertyDeclarationTypes = {
            enabled = true,
          },
          parameterTypes = {
            enabled = true,
            suppressWhenArgumentMatchesName = true,
          },
          variableTypes = {
            enabled = true,
          },
        },
      },
    },
  },
  -- TypeScript
  ts_ls = {
    init_options = {
      plugins = {
        {
          name = '@vue/typescript-plugin',
          location = vim.fn.stdpath 'data' .. '/mason/packages/vue-language-server/node_modules/@vue/language-server',
          languages = { 'vue' },
        },
      },
    },
    settings = {
      typescript = {
        tsserver = {
          useSyntaxServer = false,
        },
        inlayHints = {
          includeInlayParameterNameHints = 'all',
          includeInlayParameterNameHintsWhenArgumentMatchesName = true,
          includeInlayFunctionParameterTypeHints = true,
          includeInlayVariableTypeHints = true,
          includeInlayVariableTypeHintsWhenTypeMatchesName = true,
          includeInlayPropertyDeclarationTypeHints = true,
          includeInlayFunctionLikeReturnTypeHints = true,
          includeInlayEnumMemberValueHints = true,
        },
      },
    },
  },
  -- ...
}
Enter fullscreen mode Exit fullscreen mode

Note: this assumes you are using mason.nvim to install and manage your LSPs.

With that in place, you should be able to do all the good things: code completion, go to definitions, rename symbols across files, and tell your co-workers you use NeoVim btw.

Winning

So how did we get here?

I'm glad you asked.

The previous Vue language server versions Vetur and Volar v1 were both troubled with memory duplication issues and the additional complexity of requiring the TypeScript Vue Plugin.

Both the TypeScript language server and the Vue language server were creating duplicate TypeScript abstract syntax trees (AST) in memory, which scaled with the size of your project.

While the TypeScript Vue Plugin had neat features like renaming symbols across .ts and .vue files, it added yet another layer of memory usage.

All of this lead to performance issues and tooling trade-offs, amounting to a sub-optimal development environment.

The solution at last

However, through much hard work and determination, Volar's author Johnson Chu delivered Volar v2 and a new "Hybrid mode" which solves these problems!

Johnson has a great write up on the history of these issues, including some useful visual aids. I would highly recommend you read it.

And it was GR3YH4TT3R93's support and patience in this GitHub issue thread, helping find the right configuration to support not just the newly released Volar v2, but inlay hint support as well.

Top comments (0)