🐧
TypeScript 4.7のGoToSourceDefinitionをneovim builtin-lspから呼び出す
Go to Source Definitionとは
TypeScriptにおいて従来のGo to Definitionでは、型宣言ファイル(d.ts)がある場合はそちらにジャンプしていました。しかしTypeScript4.7でexperimentalとして登場したGo to Source Definitionでは、d.tsに隠蔽されている実装(.js or .ts)を探してその定義にジャンプすることができます。
Microsoft謹製エディタVisual Studio Codeには既にこのGo to Source Definitionが組み込まれています。しかしneovimユーザには悲しいことに、build-in LSPにはGo to Defenition(=vim.lsp.buf.definition)のような組み込み関数がGo to Source Definitionに対しては用意されていないようです。
したがってluaなりvimscriptを書いて自前でコマンドを定義してやる必要があります。
想定環境
- neovim
- lua
- neovim built-in LSP
- typescript-language-server
- Mason
実装
私の環境は init.lua 1ファイルにすべての設定を詰めこんでいるので、そこに書き込んでいきます。
function goto_source_definition関数以外は、適宜自分の環境と読み替えて使ってください。
-- go to source definition for TypeScript(~4.7)
function goto_source_definition()
-- 関数が呼ばれた時点でのファイルパスを取得する
local bufnr = 0
local uri = vim.uri_from_bufnr(bufnr)
-- 関数が呼ばれた時点でのファイル上での座標(row, col)を取得する
local pos = vim.api.nvim_win_get_cursor(0)
local row, col = pos[1] - 1, pos[2]
-- typsecript-language-server が要求するinterfaceに合わせる
-- SEE: https://github.com/typescript-language-server/typescript-language-server#go-to-source-definition
local position = { line = row, character = col }
local params = {
command = "_typescript.goToSourceDefinition",
arguments = {
uri,
position,
},
}
-- goToSourceDefinition実行後のcallback関数
local handle_response = function(err, result, ctx, config)
if result then
if #result == 1 then
vim.lsp.util.jump_to_location(result[1])
else
locations_to_items = vim.lsp.util.locations_to_items(result)
-- qflistにセットして、quickfix windowを開く
vim.fn.setqflist({}, " ", { title = "GoToSourceDefinition", items = locations_to_items })
vim.cmd("copen")
vim.cmd("wincmd p")
end
end
end
vim.lsp.buf_request_all(bufnr, "workspace/executeCommand", params, handle_response)
end
local on_attach = function(client, bufnr)
local set = vim.keymap.set
-- ...その他色々なコマンド設定...
set("n", "gs", "<cmd>lua goto_source_definition()<CR>")
-- ...
end
-- mason の設定
require("mason").setup()
require("mason-lspconfig").setup()
require("mason-lspconfig").setup_handlers({
function(server_name) -- default handler (optional)
require("lspconfig")[server_name].setup({
on_attach = on_attach,
})
end,
})
Discussion