Awesome
nvim-vtsls
Plugin to help utilize capabilities of vtsls.
NOTE: This plugin is not needed to work with vtsls
. It simply offers some extra helper commands and optional improvements. Any server related issue should go up to the upstream.
Usage
Setup server
Through nvim-lspconfig:
require("lspconfig.configs").vtsls = require("vtsls").lspconfig -- set default server config, optional but recommended
-- If the lsp setup is taken over by other plugin, it is the same to call the counterpart setup function
require("lspconfig").vtsls.setup({ --[[ your custom server config here ]] })
Execute commands
:VtsExec {command}
Rename file/folder and update import paths
:VtsRename {from} {to}
Config
This is OPTIONAL. All the fields are also optional.
require('vtsls').config({
-- customize handlers for commands
handlers = {
source_definition = function(err, locations) end,
file_references = function(err, locations) end,
code_action = function(err, actions) end,
},
-- automatically trigger renaming of extracted symbol
refactor_auto_rename = true,
refactor_move_to_file = {
-- If dressing.nvim is installed, telescope will be used for selection prompt. Use this to customize
-- the opts for telescope picker.
telescope_opts = function(items, default) end,
}
})
Commands
name | description |
---|---|
restart_tsserver | This not restart vtsls itself, but restart the underlying tsserver. |
open_tsserver_log | It will open prompt if logging has not been enabled. |
reload_projects | |
select_ts_version | Select version of ts either from workspace or global. |
goto_project_config | Open tsconfig.json . |
goto_source_definition | Go to the source definition instead of typings. |
file_references | Show references of the current file. |
rename_file | Rename the current file and update all the related paths in the project. |
organize_imports | |
sort_imports | |
remove_unused_imports | |
fix_all | |
remove_unused | |
add_missing_imports | |
source_actions | Pick applicable source actions (same as above) |
API
require('vtsls').commands[any_command_name](bufnr, on_resolve, on_reject)
require('vtsls').commands.goto_source_definition(winnr, on_resolve, on_reject) -- goto_source_definition requires winnr
require('vtsls').rename(old_name, new_name, on_resolve, on_reject) -- rename file or folder
-- These callbacks are useful if you want to promisify the command functions to write async code.
function on_resolve() end -- after handler called
function on_reject(msg_or_err) end -- in case any error happens
Other useful snippets
<details> <summary>Common settings to enable inlay hints</summary>{
settings = {
typescript = {
inlayHints = {
parameterNames = { enabled = "literals" },
parameterTypes = { enabled = true },
variableTypes = { enabled = true },
propertyDeclarationTypes = { enabled = true },
functionLikeReturnTypes = { enabled = true },
enumMemberValues = { enabled = true },
}
},
}
}
</details>
<details>
<summary>Handler for codelens command</summary>
vim.lsp.commands["editor.action.showReferences"] = function(command, ctx)
local locations = command.arguments[3]
local client = vim.lsp.get_client_by_id(ctx.client_id)
if locations and #locations > 0 then
local items = vim.lsp.util.locations_to_items(locations, client.offset_encoding)
vim.fn.setloclist(0, {}, " ", { title = "References", items = items, context = ctx })
vim.api.nvim_command("lopen")
end
end
Then executing vim.lsp.codelens.run()
will open up a quickfix window for references shown by the lens.
Excellent replacement for manually calling :VtsExec rename_file
or :VtsRename
.
You have two ways. You can use nvim-lsp-file-operations. It has integration with nvim and neo tree. Or you can use the following snippet. It also works for any server supporting workspace/didRenameFiles
notification.
local path_sep = package.config:sub(1, 1)
local function trim_sep(path)
return path:gsub(path_sep .. "$", "")
end
local function uri_from_path(path)
return vim.uri_from_fname(trim_sep(path))
end
local function is_sub_path(path, folder)
path = trim_sep(path)
folder = trim_sep(folder)
if path == folder then
return true
else
return path:sub(1, #folder + 1) == folder .. path_sep
end
end
local function check_folders_contains(folders, path)
for _, folder in pairs(folders) do
if is_sub_path(path, folder.name) then
return true
end
end
return false
end
local function match_file_operation_filter(filter, name, type)
if filter.scheme and filter.scheme ~= "file" then
-- we do not support uri scheme other than file
return false
end
local pattern = filter.pattern
local matches = pattern.matches
if type ~= matches then
return false
end
local regex_str = vim.fn.glob2regpat(pattern.glob)
if vim.tbl_get(pattern, "options", "ignoreCase") then
regex_str = "\\c" .. regex_str
end
return vim.regex(regex_str):match_str(name) ~= nil
end
local api = require("nvim-tree.api")
api.events.subscribe(api.events.Event.NodeRenamed, function(data)
local stat = vim.loop.fs_stat(data.new_name)
if not stat then
return
end
local type = ({ file = "file", directory = "folder" })[stat.type]
local clients = vim.lsp.get_clients({})
for _, client in ipairs(clients) do
if check_folders_contains(client.workspace_folders, data.old_name) then
local filters = vim.tbl_get(client.server_capabilities, "workspace", "fileOperations", "didRename", "filters")
or {}
for _, filter in pairs(filters) do
if
match_file_operation_filter(filter, data.old_name, type)
and match_file_operation_filter(filter, data.new_name, type)
then
client.notify(
"workspace/didRenameFiles",
{ files = { { oldUri = uri_from_path(data.old_name), newUri = uri_from_path(data.new_name) } } }
)
end
end
end
end
end)
</details>