Improves Volar language server goto definition functionality
When using vue with autoimports (unplugin-vue-components and unplugin-auto-import or using nuxt) vim.lsp.buf.definition will populate location list with the autoimport definition file and/or the typescript declaration for a symbol.
For example:
![]() |
|---|
| Default goto definition with unplugin auto-imports |
![]() |
|---|
| Using the default goto definition for a symbol in the same .vue file |
Another issue with Volar LSP is that since it uses typescript LSP, when
vim.lsp.buf.definition is called it requests definitions from both volar and
tsserver. Since the two LSP can return different results, the consequences can
be unpredictable.
vue-goto-definition waits a debounce period (configurable in opts) before
processing definitions. This allows Volar and tsserver time to return definitions.
vue-goto-definition overrides vim.lsp.buf.definition to attempt to filter the
location list and decide the best source for a symbol's definition.
Calling goto_definition directly can be useful if you have set opts.lsp.override
to false, disabling autocommand creation to override the default lsp definition.
require("vue-goto-definition").goto_definition(opts)opts is optional and will be passed into vim.lsp.buf.definition
{
filters = {
auto_imports = true, -- resolve definitions in auto-imports.d.ts
auto_components = true, -- resolve definitions in components.d.ts
import_same_file = true, -- filter location list entries referencing an
-- import in the current file. See below for details
declaration = true, -- filter declaration files unless the only location list
-- item is a declaration file
duplicate_filename = true, -- dedupe duplicate filenames
},
filetypes = { "vue", "typescript" }, -- enabled for filetypes
detection = { -- framework detection. Detection functions can be overridden here
nuxt = function() -- look for .nuxt directory
return vim.fn.glob(".nuxt/") ~= ""
end,
vue3 = function() -- look for vite.config.ts or App.vue
return vim.fn.filereadable("vite.config.ts") == 1 or vim.fn.filereadable("src/App.vue") == 1
end,
priority = { "nuxt", "vue3" }, -- order in which to detect framework
},
lsp = {
override_definition = true, -- override vim.lsp.buf.definition
},
debounce = 200
}filters.import_same_file stops the following from occuring:
import { useCounterStore } from "./stores/counter";
const store = useCounterStore();Calling goto_definition on useCounterStore() can target the import line as
the definition. Setting filters.import_same_file will filter those targets
in the same file.
If after filtering the locationlist items there are multiple items remaining they will be populated in a locationlist window.
neoconf could be used to detect the framework
local opts = {
filters = {
auto_imports = true,
auto_components = true,
import_same_file = true,
declaration = true,
duplicate_filename = true,
},
filetypes = { "vue", "typescript" },
detection = {
nuxt = function()
return vim.fn.glob(".nuxt/") ~= ""
end,
vue3 = function()
return vim.fn.filereadable("vite.config.ts") == 1 or vim.fn.filereadable("src/App.vue") == 1
end,
priority = { "nuxt", "vue3" },
},
lsp = {
override_definition = true, -- override vim.lsp.buf.definition
},
debounce = 200
}
return {
"catgoose/vue-goto-definition.nvim",
event = "BufReadPre",
opts = opts
})If you are using unplugin-auto-import and unplugin-vue-components make sure
to configure your tsconfig.app.json like this:
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": [
"env.d.ts",
"src/**/*",
"src/**/*.vue",
"auto-imports.d.ts",
"components.d.ts"
],
"exclude": [
"src/**/__tests__/*"
],
"compilerOptions": {
"composite": true,
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
}
}
}I don't use nuxt so you are on your own here. Please open an issue if definition resolution is not working.
If you are having trouble getting Volar configured correctly check my
lspconfig.lua

