Closed rockerBOO closed 2 years ago
This looks great! I think we'd have to get this integrated into typescript-language-server first, though. Optimistically, it seems like it would be pretty simple to wrap the tsserver
method, but that might just be ignorance.
I also don't know how long it would take to get this merged, since the repo hasn't seen a lot of activity (though it looks like there's been some changes in the past week or so) but I'd be happy to work on a PR once 4.4 is released, since it looks like a really useful feature.
@jose-elias-alvarez Should we open a ticket on typescript-language-server to expose the inlay hints?
@entropitor Sure, I think it's worth opening up an issue to show interest (even though progress is likely to be slow).
@jose-elias-alvarez I've implemented the inlay hints in the LSP (https://github.com/typescript-language-server/typescript-language-server/pull/259)
" To get all the inlay hints for the current buffer
lua print(vim.inspect(vim.lsp.buf_request_sync(0, 'typescript/inlayHints', {textDocument = vim.lsp.util.make_text_document_params()})))
" To get the inlay hints for the last selected region (if you pass a range, it will respect it)
lua print(vim.inspect(vim.lsp.buf_request_sync(0, 'typescript/inlayHints', vim.lsp.util.make_given_range_params())))
You need to pass the correct preferences to tsserver though, to maximally get hints for example:
init_options = {
hostInfo = "neovim",
preferences = {
includeInlayParameterNameHints = "all",
includeInlayParameterNameHintsWhenArgumentMatchesName = true,
includeInlayFunctionParameterTypeHints = true,
includeInlayVariableTypeHints = true,
includeInlayPropertyDeclarationTypeHints = true,
includeInlayFunctionLikeReturnTypeHints = true,
includeInlayEnumMemberValueHints = true
}
},
Using some code from https://github.com/nvim-lua/lsp_extensions.nvim/blob/master/lua/lsp_extensions/inlay_hints.lua:
local util = {}
function util.mk_handler(fn)
return function(...)
local config_or_client_id = select(4, ...)
local is_new = type(config_or_client_id) ~= "number"
if is_new then
fn(...)
else
local err = select(1, ...)
local method = select(2, ...)
local result = select(3, ...)
local client_id = select(4, ...)
local bufnr = select(5, ...)
local config = select(6, ...)
fn(err, result, {method = method, client_id = client_id, bufnr = bufnr}, config)
end
end
end
local inlay_hints = {}
local inlay_hints_ns = vim.api.nvim_create_namespace("lsp_extensions.inlay_hints")
local inlay_hints_callback = function()
opts = opts or {}
local highlight = opts.highlight or "Comment"
local prefix = opts.prefix or ""
local aligned = opts.aligned or false
local enabled = opts.enabled or {"Type", "Enum", "Parameter"}
local only_current_line = opts.only_current_line
if only_current_line == nil then
only_current_line = false
end
return util.mk_handler(
function(err, result, ctx, _)
if err then
return
end
if not result or vim.tbl_isempty(result) then
return
end
vim.api.nvim_buf_clear_namespace(ctx.bufnr, inlay_hints_ns, 0, -1)
local hint_store = {}
local longest_line = -1
-- Check if something is in the list
-- in_list({"ChainingHint"})("ChainingHint")
local in_list = function(list)
return function(item)
for _, f in ipairs(list) do
if f == item then
return true
end
end
return false
end
end
for _, hint in ipairs(result.inlayHints) do
local finish = hint.position.line
if not hint_store[finish] and in_list(enabled)(hint.kind) then
hint_store[finish] = hint
if aligned then
longest_line =
math.max(longest_line, #vim.api.nvim_buf_get_lines(ctx.bufnr, finish, finish + 1, false)[1])
end
end
end
local display_virt_text = function(hint)
local end_line = hint.position.line
-- Check for any existing / more important virtual text on the line.
-- TODO: Figure out how stackable virtual text works? What happens if there is more than one??
local existing_virt_text =
vim.api.nvim_buf_get_extmarks(ctx.bufnr, inlay_hints_ns, {end_line, 0}, {end_line, 0}, {})
if not vim.tbl_isempty(existing_virt_text) then
return
end
local text
if aligned then
local line_length = #vim.api.nvim_buf_get_lines(ctx.bufnr, end_line, end_line + 1, false)[1]
text = string.format("%s %s", (" "):rep(longest_line - line_length), prefix .. hint.text)
else
text = prefix .. hint.text
end
vim.api.nvim_buf_set_virtual_text(ctx.bufnr, inlay_hints_ns, end_line, {{text, highlight}}, {})
end
if only_current_line then
local hint = hint_store[vim.api.nvim_win_get_cursor(0)[1] - 1]
if not hint then
return
else
display_virt_text(hint)
end
else
for _, hint in pairs(hint_store) do
display_virt_text(hint)
end
end
end
)
end
function M.inlay_hints()
local params = {
textDocument = vim.lsp.util.make_text_document_params()
}
vim.lsp.buf_request(0, "typescript/inlayHints", params, inlay_hints_callback())
end
It seems to work just fine 👍
@entropitor Great! I hope they'll merge the PR soon. I'll (hopefully) find time to put together an implementation soon.
typescript-language-server 0.6.3 is out with experimental support for inlay hints :tada:
https://github.com/typescript-language-server/typescript-language-server/releases/tag/v0.6.3
Now implemented #79 Thanks @simrat39 @entropitor
Inlay hints just merged into typescript. https://github.com/microsoft/TypeScript/pull/42089
An example implementation: https://github.com/nvim-lua/lsp_extensions.nvim/blob/master/lua/lsp_extensions/inlay_hints.lua
I believe this will release in Typescript 4.4.
Thank you!