inlay hints #172
This commit is contained in:
parent
f0732425f9
commit
c8d02a9d6a
38
README.md
38
README.md
@ -29,6 +29,7 @@ The plugin covers most features required for a gopher.
|
||||
- GoCheat get go cheatsheet from [cheat.sh](https://cheat.sh/).
|
||||
- Smart build tag detection when debug/run tests (e.g. `//go:build integration`)
|
||||
- Generate mocks with mockgen
|
||||
- Inlay hints: gopls (version 0.9.x or greater) inlay hints
|
||||
- luasnip: you might use friendly-snippets already, you still need to try pure lua snippets with go.nvim out,
|
||||
checkout [LuaSnip Tutorial](https://www.youtube.com/watch?v=ub0REXjhpmk) and [TJ's Introduction to LuaSnip](https://www.youtube.com/watch?v=Dn800rlPIho)
|
||||
|
||||
@ -153,7 +154,7 @@ Note: auto fill struct also supported by gopls lsp-action
|
||||
| GoIfErr | Add if err |
|
||||
| GoFixPlurals | change func foo(b int, a int, r int) -> func foo(b, a, r int) |
|
||||
|
||||
![GoFixPlurals Youtube video](https://www.youtube.com/watch?v=IP67Gkb5-qA)
|
||||
[GoFixPlurals Youtube video](https://www.youtube.com/watch?v=IP67Gkb5-qA)
|
||||
|
||||
|
||||
```go
|
||||
@ -503,6 +504,12 @@ for more info
|
||||
go.nvim support launch debuger from vscode-go .vscode/launch.json configurations
|
||||
If launch.json is valid, run `GoDebug` will launch from the launch.json configuration.
|
||||
|
||||
### Inlay hints
|
||||
|
||||
<img width="808" alt="image" src="https://user-images.githubusercontent.com/1681295/179863119-b7463072-015f-404c-b082-7bf6a01e3ab6.png">
|
||||
### Command
|
||||
* GoToggleInlay
|
||||
|
||||
#### Note:
|
||||
|
||||
Please use jsonls/null-ls check your launch.json is valid json file. Following syntax is not supported
|
||||
@ -593,6 +600,35 @@ require('go').setup({
|
||||
lsp_document_formatting = true,
|
||||
-- set to true: use gopls to format
|
||||
-- false if you want to use other formatter tool(e.g. efm, nulls)
|
||||
lsp_inlay_hints = {
|
||||
enable = true,
|
||||
-- Only show inlay hints for the current line
|
||||
only_current_line = false,
|
||||
-- Event which triggers a refersh of the inlay hints.
|
||||
-- You can make this "CursorMoved" or "CursorMoved,CursorMovedI" but
|
||||
-- not that this may cause higher CPU usage.
|
||||
-- This option is only respected when only_current_line and
|
||||
-- autoSetHints both are true.
|
||||
only_current_line_autocmd = "CursorHold",
|
||||
-- whether to show variable name before type hints with the inlay hints or not
|
||||
-- default: false
|
||||
show_variable_name = true,
|
||||
-- prefix for parameter hints
|
||||
parameter_hints_prefix = " ",
|
||||
show_parameter_hints = true,
|
||||
-- prefix for all the other hints (type, chaining)
|
||||
other_hints_prefix = "=> ",
|
||||
-- whether to align to the lenght of the longest line in the file
|
||||
max_len_align = false,
|
||||
-- padding from the left if max_len_align is true
|
||||
max_len_align_padding = 1,
|
||||
-- whether to align to the extreme right or not
|
||||
right_align = false,
|
||||
-- padding from the right if right_align is true
|
||||
right_align_padding = 6,
|
||||
-- The color of the hints
|
||||
highlight = "Comment",
|
||||
},
|
||||
gopls_cmd = nil, -- if you need to specify gopls path and cmd, e.g {"/home/user/lsp/gopls", "-logfile","/var/log/gopls.log" }
|
||||
gopls_remote_auto = true, -- add -remote=auto to gopls
|
||||
dap_debug = true, -- set to false to disable dap
|
||||
|
43
doc/go.txt
43
doc/go.txt
@ -294,7 +294,6 @@ COMMANDS *go-nvim-commands*
|
||||
:GoAlt *:GoAlt*
|
||||
Open alternative file (test/go), Also GoAltS/GoAltV
|
||||
|
||||
|
||||
:GoDoc {options} *:GoDoc*
|
||||
e.g. GoDoc fmt.Println
|
||||
|
||||
@ -319,6 +318,9 @@ COMMANDS *go-nvim-commands*
|
||||
f io.Reader", or "GoImpl MyType", "GoImpl mt MyType"
|
||||
you can use tab to complete the interface name.
|
||||
|
||||
:GoToggleInlay
|
||||
Toggle inlay hints for current buffer
|
||||
|
||||
:GoTermClose
|
||||
Closes the floating term.
|
||||
==============================================================================
|
||||
@ -348,6 +350,45 @@ You can setup go.nvim with following options:
|
||||
lsp_keymaps = true, -- true: apply default lsp keymaps
|
||||
lsp_codelens = true,
|
||||
lsp_diag_hdlr = true, -- hook lsp diag handler
|
||||
lsp_inlay_hints = {
|
||||
enable = true,
|
||||
|
||||
-- Only show inlay hints for the current line
|
||||
only_current_line = false,
|
||||
|
||||
-- Event which triggers a refersh of the inlay hints.
|
||||
-- You can make this "CursorMoved" or "CursorMoved,CursorMovedI" but
|
||||
-- not that this may cause higher CPU usage.
|
||||
-- This option is only respected when only_current_line and
|
||||
-- autoSetHints both are true.
|
||||
only_current_line_autocmd = "CursorHold",
|
||||
|
||||
-- whether to show variable name before type hints with the inlay hints or not
|
||||
-- default: false
|
||||
show_variable_name = true,
|
||||
|
||||
-- prefix for parameter hints
|
||||
parameter_hints_prefix = " ",
|
||||
show_parameter_hints = true,
|
||||
|
||||
-- prefix for all the other hints (type, chaining)
|
||||
other_hints_prefix = "=> ",
|
||||
|
||||
-- whether to align to the lenght of the longest line in the file
|
||||
max_len_align = false,
|
||||
|
||||
-- padding from the left if max_len_align is true
|
||||
max_len_align_padding = 1,
|
||||
|
||||
-- whether to align to the extreme right or not
|
||||
right_align = false,
|
||||
|
||||
-- padding from the right if right_align is true
|
||||
right_align_padding = 6,
|
||||
|
||||
-- The color of the hints
|
||||
highlight = "Comment",
|
||||
},
|
||||
gopls_remote_auto = true,
|
||||
gocoverage_sign = "█",
|
||||
gocoverage_sign_priority = 5,
|
||||
|
43
lua/go.lua
43
lua/go.lua
@ -44,6 +44,46 @@ _GO_NVIM_CFG = {
|
||||
-- virtual text setup
|
||||
lsp_diag_virtual_text = { space = 0, prefix = "" },
|
||||
lsp_diag_signs = true,
|
||||
lsp_inlay_hints = {
|
||||
enable = true,
|
||||
|
||||
-- Only show inlay hints for the current line
|
||||
only_current_line = false,
|
||||
|
||||
-- Event which triggers a refersh of the inlay hints.
|
||||
-- You can make this "CursorMoved" or "CursorMoved,CursorMovedI" but
|
||||
-- not that this may cause higher CPU usage.
|
||||
-- This option is only respected when only_current_line and
|
||||
-- autoSetHints both are true.
|
||||
only_current_line_autocmd = "CursorHold",
|
||||
|
||||
-- whether to show variable name before type hints with the inlay hints or not
|
||||
-- default: false
|
||||
show_variable_name = true,
|
||||
|
||||
-- prefix for parameter hints
|
||||
parameter_hints_prefix = " ",
|
||||
show_parameter_hints = true,
|
||||
|
||||
-- prefix for all the other hints (type, chaining)
|
||||
-- default: "=>"
|
||||
other_hints_prefix = "=> ",
|
||||
|
||||
-- whether to align to the lenght of the longest line in the file
|
||||
max_len_align = false,
|
||||
|
||||
-- padding from the left if max_len_align is true
|
||||
max_len_align_padding = 1,
|
||||
|
||||
-- whether to align to the extreme right or not
|
||||
right_align = false,
|
||||
|
||||
-- padding from the right if right_align is true
|
||||
right_align_padding = 6,
|
||||
|
||||
-- The color of the hints
|
||||
highlight = "Comment",
|
||||
},
|
||||
lsp_diag_update_in_insert = false,
|
||||
lsp_fmt_async = false, -- async lsp.buf.format
|
||||
go_boilplater_url = "https://github.com/thockin/go-build-template.git",
|
||||
@ -116,6 +156,9 @@ function go.setup(cfg)
|
||||
require("snips.all")
|
||||
end
|
||||
end
|
||||
if _GO_NVIM_CFG.lsp_inlay_hints.enable then
|
||||
require("go.inlay").setup()
|
||||
end
|
||||
go.doc_complete = require("go.godoc").doc_complete
|
||||
go.package_complete = require("go.package").complete
|
||||
go.dbg_complete = require("go.complete").dbg_complete
|
||||
|
@ -179,6 +179,18 @@ function M.version()
|
||||
return version
|
||||
end
|
||||
|
||||
local get_current_gomod = function()
|
||||
local file = io.open("go.mod", "r")
|
||||
if file == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
local first_line = file:read()
|
||||
local mod_name = first_line:gsub("module ", "")
|
||||
file:close()
|
||||
return mod_name
|
||||
end
|
||||
|
||||
local setups = {
|
||||
capabilities = {
|
||||
textDocument = {
|
||||
@ -221,26 +233,39 @@ local setups = {
|
||||
settings = {
|
||||
gopls = {
|
||||
-- more settings: https://github.com/golang/tools/blob/master/gopls/doc/settings.md
|
||||
-- flags = {allow_incremental_sync = true, debounce_text_changes = 500},
|
||||
-- not supported
|
||||
analyses = { unusedparams = true, unreachable = false },
|
||||
analyses = {
|
||||
unreachable = true,
|
||||
nilness = true,
|
||||
unusedparams = true,
|
||||
useany = true,
|
||||
unusedwrite = true,
|
||||
ST1003 = true,
|
||||
undeclaredname = true,
|
||||
fillreturns = true,
|
||||
nonewvars = true,
|
||||
fieldalignment = false,
|
||||
shadow = true,
|
||||
},
|
||||
codelenses = {
|
||||
generate = true, -- show the `go generate` lens.
|
||||
gc_details = true, -- // Show a code lens toggling the display of gc's choices.
|
||||
gc_details = true, -- Show a code lens toggling the display of gc's choices.
|
||||
test = true,
|
||||
tidy = true,
|
||||
vendor = true,
|
||||
regenerate_cgo = true,
|
||||
upgrade_dependency = true,
|
||||
},
|
||||
usePlaceholders = true,
|
||||
completeUnimported = true,
|
||||
staticcheck = true,
|
||||
matcher = "Fuzzy",
|
||||
diagnosticsDelay = "500ms",
|
||||
experimentalWatchedFileDelay = "100ms",
|
||||
experimentalWatchedFileDelay = "200ms",
|
||||
symbolMatcher = "fuzzy",
|
||||
["local"] = "",
|
||||
gofumpt = false, -- true, -- turn on for new repos, gofmpt is good but also create code turmoils
|
||||
["local"] = get_current_gomod(),
|
||||
gofumpt = true, -- true|false, -- turn on for new repos, gofmpt is good but also create code turmoils
|
||||
buildFlags = { "-tags", "integration" },
|
||||
-- buildFlags = {"-tags", "functional"}
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -251,12 +276,24 @@ M.setups = function()
|
||||
return
|
||||
end
|
||||
if v > "0.7" then
|
||||
setups.settings = vim.tbl_deep_extend("force", setups.settings, {
|
||||
experimentalPostfixCompletions = true,
|
||||
setups.settings.gopls = vim.tbl_deep_extend("force", setups.settings.gopls, {
|
||||
experimentalUseInvalidMetadata = true,
|
||||
hoverKind = "Structured",
|
||||
})
|
||||
end
|
||||
if v > "0.9" and _GO_NVIM_CFG.lsp_inlay_hints.enable then
|
||||
setups.settings.gopls = vim.tbl_deep_extend("force", setups.settings.gopls, {
|
||||
hints = {
|
||||
assignVariableTypes = true,
|
||||
compositeLiteralFields = true,
|
||||
compositeLiteralTypes = true,
|
||||
constantValues = true,
|
||||
functionTypeParameters = true,
|
||||
parameterNames = true,
|
||||
rangeVariableTypes = true,
|
||||
},
|
||||
})
|
||||
end
|
||||
return setups
|
||||
end
|
||||
|
||||
|
274
lua/go/inlay.lua
Normal file
274
lua/go/inlay.lua
Normal file
@ -0,0 +1,274 @@
|
||||
--part of code was from rust-tools inlay_hints.lua
|
||||
-- I was so jealous of rust-tools which provides inlay_hints until today gopls provides this feature
|
||||
local M = {}
|
||||
local vim = vim
|
||||
local api = vim.api
|
||||
local utils = require("go.utils")
|
||||
local log = utils.log
|
||||
local config
|
||||
|
||||
-- Update inlay hints when opening a new buffer and when writing a buffer to a
|
||||
-- file
|
||||
-- opts is a string representation of the table of options
|
||||
function M.setup()
|
||||
local events = { "BufEnter", "BufWinEnter", "TabEnter", "BufWritePost" }
|
||||
config = _GO_NVIM_CFG.lsp_inlay_hints
|
||||
if config.only_current_line then
|
||||
local user_events = vim.split(config.only_current_line_autocmd, ",")
|
||||
events = vim.tbl_extend("keep", events, user_events)
|
||||
end
|
||||
|
||||
local cmd_group = api.nvim_create_augroup("gopls_inlay", {})
|
||||
api.nvim_create_autocmd(events, {
|
||||
group = cmd_group,
|
||||
pattern = { "*.go", "*.mod" },
|
||||
callback = function()
|
||||
require("go.inlay").set_inlay_hints()
|
||||
end,
|
||||
})
|
||||
|
||||
api.nvim_create_user_command("GoToggleInlay", function(_)
|
||||
require("go.inlay").toggle_inlay_hints()
|
||||
end, { desc = "toggle gopls inlay hints" })
|
||||
vim.defer_fn(function()
|
||||
require("go.inlay").set_inlay_hints()
|
||||
end, 1000)
|
||||
end
|
||||
|
||||
local function get_params()
|
||||
local params = vim.lsp.util.make_given_range_params()
|
||||
params["range"]["start"]["line"] = 0
|
||||
params["range"]["end"]["line"] = vim.api.nvim_buf_line_count(0) - 1
|
||||
log(params)
|
||||
return params
|
||||
end
|
||||
|
||||
local namespace = vim.api.nvim_create_namespace("experimental/inlayHints")
|
||||
-- whether the hints are enabled or not
|
||||
local enabled = nil
|
||||
|
||||
-- parses the result into a easily parsable format
|
||||
-- input
|
||||
-- { {
|
||||
-- kind = 1,
|
||||
-- label = { {
|
||||
-- value = "[]int"
|
||||
-- } },
|
||||
-- paddingLeft = true,
|
||||
-- position = {
|
||||
-- character = 7,
|
||||
-- line = 8
|
||||
-- }
|
||||
-- }, {
|
||||
-- kind = 2,
|
||||
-- label = { {
|
||||
-- value = "stack:"
|
||||
-- } },
|
||||
-- paddingRight = true,
|
||||
-- position = {
|
||||
-- character = 29,
|
||||
-- line = 8
|
||||
-- }
|
||||
-- },
|
||||
|
||||
-- example:
|
||||
-- {
|
||||
-- ["12"] = { {
|
||||
-- kind = "TypeHint",
|
||||
-- label = "String"
|
||||
-- } },
|
||||
-- }
|
||||
|
||||
local function parseHints(result)
|
||||
local map = {}
|
||||
local only_current_line = config.only_current_line
|
||||
|
||||
if type(result) ~= "table" then
|
||||
return {}
|
||||
end
|
||||
for _, value in pairs(result) do
|
||||
local range = value.position
|
||||
local line = value.position.line
|
||||
local label = value.label
|
||||
local kind = value.kind
|
||||
local current_line = vim.api.nvim_win_get_cursor(0)[1]
|
||||
|
||||
local function add_line()
|
||||
if map[line] ~= nil then
|
||||
table.insert(map[line], { label = label, kind = kind, range = range })
|
||||
else
|
||||
map[line] = { { label = label, kind = kind, range = range } }
|
||||
end
|
||||
end
|
||||
|
||||
if only_current_line then
|
||||
if line == tostring(current_line - 1) then
|
||||
add_line()
|
||||
end
|
||||
else
|
||||
add_line()
|
||||
end
|
||||
end
|
||||
return map
|
||||
end
|
||||
|
||||
local function get_max_len(bufnr, parsed_data)
|
||||
local max_len = -1
|
||||
|
||||
for key, _ in pairs(parsed_data) do
|
||||
local line = tonumber(key)
|
||||
local current_line = vim.api.nvim_buf_get_lines(bufnr, line, line + 1, false)[1]
|
||||
if current_line then
|
||||
local current_line_len = string.len(current_line)
|
||||
max_len = math.max(max_len, current_line_len)
|
||||
end
|
||||
end
|
||||
|
||||
return max_len
|
||||
end
|
||||
|
||||
local function handler(err, result, ctx)
|
||||
log(result, ctx)
|
||||
if err then
|
||||
return
|
||||
end
|
||||
local bufnr = ctx.bufnr
|
||||
|
||||
if vim.api.nvim_get_current_buf() ~= bufnr then
|
||||
return
|
||||
end
|
||||
|
||||
local function unpack_label(label)
|
||||
local labels = ""
|
||||
for _, value in pairs(label) do
|
||||
labels = labels .. " " .. value.value
|
||||
end
|
||||
return labels
|
||||
end
|
||||
|
||||
-- clean it up at first
|
||||
M.disable_inlay_hints()
|
||||
|
||||
local parsed = parseHints(result)
|
||||
log(parsed)
|
||||
|
||||
for key, value in pairs(parsed) do
|
||||
local virt_text = ""
|
||||
local line = tonumber(key)
|
||||
|
||||
local current_line = vim.api.nvim_buf_get_lines(bufnr, line, line + 1, false)[1]
|
||||
|
||||
if current_line then
|
||||
local current_line_len = string.len(current_line)
|
||||
|
||||
local param_hints = {}
|
||||
local other_hints = {}
|
||||
|
||||
-- segregate paramter hints and other hints
|
||||
for _, value_inner in ipairs(value) do
|
||||
log(value_inner)
|
||||
if value_inner.kind == 2 then
|
||||
table.insert(param_hints, unpack_label(value_inner.label))
|
||||
end
|
||||
|
||||
if value_inner.kind == 1 then
|
||||
table.insert(other_hints, value_inner)
|
||||
end
|
||||
end
|
||||
log(config, param_hints)
|
||||
|
||||
-- show parameter hints inside brackets with commas and a thin arrow
|
||||
if not vim.tbl_isempty(param_hints) and config.show_parameter_hints then
|
||||
virt_text = virt_text .. config.parameter_hints_prefix .. "("
|
||||
for i, value_inner_inner in ipairs(param_hints) do
|
||||
virt_text = virt_text .. value_inner_inner:sub(1, -2)
|
||||
if i ~= #param_hints then
|
||||
virt_text = virt_text .. ", "
|
||||
end
|
||||
end
|
||||
virt_text = virt_text .. ") "
|
||||
log(virt_text)
|
||||
end
|
||||
|
||||
-- show other hints with commas and a thicc arrow
|
||||
if not vim.tbl_isempty(other_hints) then
|
||||
virt_text = virt_text .. config.other_hints_prefix
|
||||
for i, value_inner_inner in ipairs(other_hints) do
|
||||
if value_inner_inner.kind == 2 and config.show_variable_name then
|
||||
local char_start = value_inner_inner.range.start.character
|
||||
local char_end = value_inner_inner.range["end"].character
|
||||
log(current_line, char_start, char_end)
|
||||
local variable_name = string.sub(current_line, char_start + 1, char_end)
|
||||
virt_text = virt_text .. variable_name .. ": " .. value_inner_inner.label
|
||||
else
|
||||
log(value_inner_inner.label)
|
||||
local label = unpack_label(value_inner_inner.label)
|
||||
if string.sub(label, 1, 2) == ": " then
|
||||
virt_text = virt_text .. label:sub(3)
|
||||
else
|
||||
virt_text = virt_text .. label
|
||||
end
|
||||
end
|
||||
if i ~= #other_hints then
|
||||
virt_text = virt_text .. ", "
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if config.right_align then
|
||||
virt_text = virt_text .. string.rep(" ", config.right_align_padding)
|
||||
end
|
||||
|
||||
if config.max_len_align then
|
||||
local max_len = get_max_len(bufnr, parsed)
|
||||
virt_text = string.rep(" ", max_len - current_line_len + config.max_len_align_padding) .. virt_text
|
||||
end
|
||||
|
||||
-- set the virtual text if it is not empty
|
||||
if virt_text ~= "" then
|
||||
vim.api.nvim_buf_set_extmark(bufnr, namespace, line, 0, {
|
||||
virt_text_pos = config.right_align and "right_align" or "eol",
|
||||
virt_text = {
|
||||
{ virt_text, config.highlight },
|
||||
},
|
||||
hl_mode = "combine",
|
||||
})
|
||||
end
|
||||
|
||||
-- update state
|
||||
enabled = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function M.toggle_inlay_hints()
|
||||
if enabled then
|
||||
M.disable_inlay_hints()
|
||||
else
|
||||
M.set_inlay_hints()
|
||||
end
|
||||
enabled = not enabled
|
||||
end
|
||||
|
||||
function M.disable_inlay_hints()
|
||||
-- clear namespace which clears the virtual text as well
|
||||
vim.api.nvim_buf_clear_namespace(0, namespace, 0, -1)
|
||||
end
|
||||
|
||||
-- Sends the request to gopls to get the inlay hints and handle them
|
||||
function M.set_inlay_hints()
|
||||
-- check if lsp is ready
|
||||
local found = false
|
||||
for _, lsp in pairs(vim.lsp.buf_get_clients()) do
|
||||
if lsp.name == "gopls" then
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not found then
|
||||
return
|
||||
end
|
||||
vim.lsp.buf_request(0, "textDocument/inlayHint", get_params(), handler)
|
||||
end
|
||||
|
||||
return M
|
@ -154,6 +154,7 @@ function M.setup()
|
||||
if vim_version < 61 then
|
||||
vim.notify("LSP: go.nvim requires neovim 0.6.1 or later", vim.log.levels.WARN)
|
||||
end
|
||||
log(goplscfg)
|
||||
lspconfig.gopls.setup(goplscfg)
|
||||
end
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user