gopls version
Build info
----------
golang.org/x/tools/gopls v0.16.1
golang.org/x/tools/gopls@v0.16.1 h1:1hO/dCeUvjEYx3V0rVvCtOkwnpEpqS29paE+Jw4dcAc=
github.com/BurntSushi/toml@v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/google/go-cmp@v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
golang.org/x/exp/typeparams@v0.0.0-20221212164502-fae10dda9338 h1:2O2DON6y3XMJiQRAS1UWU+54aec2uopH3x7MAiqGW6Y=
golang.org/x/mod@v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/sync@v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/telemetry@v0.0.0-20240607193123-221703e18637 h1:3Wt8mZlbFwG8llny+t18kh7AXxyWePFycXMuVdHxnyM=
golang.org/x/text@v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/tools@v0.22.1-0.20240628205440-9c895dd76b34 h1:Kd+Z5Pm6uwYx3T2KEkeHMHUMZxDPb/q6b1m+zEcy62c=
golang.org/x/vuln@v1.0.4 h1:SP0mPeg2PmGCu03V+61EcQiOjmpri2XijexKdzv8Z1I=
honnef.co/go/tools@v0.4.7 h1:9MDAWxMoSnB6QoSqiVr7P5mtkT9pOc1kSxchzPCnqJs=
mvdan.cc/gofumpt@v0.6.0 h1:G3QvahNDmpD+Aek/bNOLrFR2XC6ZAdo62dZu65gmwGo=
mvdan.cc/xurls/v2@v2.5.0 h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8=
go: go1.22.5
go env
GO111MODULE=''
GOARCH='arm64'
GOBIN=''
GOCACHE='/Users/aaron/Library/Caches/go-build'
GOENV='/Users/aaron/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/aaron/go/pkg/mod'
GOOS='darwin'
GOPATH='/Users/aaron/go'
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/Cellar/go/1.22.5/libexec'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.22.5/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.22.5'
GCCGO='gccgo'
AR='ar'
CC='cc'
CXX='c++'
CGO_ENABLED='1'
GOMOD='/dev/null'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/5p/fpck_9g5367f7lk3p50bvdbc0000gn/T/go-build356802509=/tmp/go-build -gno-record-gcc-switches -fno-common
What did you do?
- Upgrade
gopls
tov0.16.1
. - Open a Go file with
neovim
. - Attempt to use omnifunc completion with
<ctrl-x><ctrl-o>
neovim
throws an indexing error- Reverting back to
gopls
versionv0.15.3
works as expected.
What did you see happen?
Attempting to use omnifunc
completion with <ctrl-x><ctrl-o>
result in the following indexing error from neovim
.
Error executing vim.schedule lua callback: ...im/0.10.1/share/nvim/runtime/lua/vim/lsp/_completion.lua:154: attempt to index field 'range' (a nil value)
stack traceback:
...im/0.10.1/share/nvim/runtime/lua/vim/lsp/_completion.lua:154: in function 'adjust_start_col'
...im/0.10.1/share/nvim/runtime/lua/vim/lsp/_completion.lua:202: in function '_convert_results'
...im/0.10.1/share/nvim/runtime/lua/vim/lsp/_completion.lua:253: in function 'handler'
.../neovim/0.10.1/share/nvim/runtime/lua/vim/lsp/client.lua:687: in function ''
vim/_editor.lua: in function <vim/_editor.lua:0>
What did you expect to see?
Successful autocompletion recommendations without an error.
Editor and settings
Running neovim
version v0.10.1
.
-- Set leader
vim.g.mapleader = ","
-- Plug-Ins below
-- Bootstrap lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
local lazyrepo = "https://github.com/folke/lazy.nvim.git"
local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
if vim.v.shell_error ~= 0 then
vim.api.nvim_echo({
{ "Failed to clone lazy.nvim:\n", "ErrorMsg" },
{ out, "WarningMsg" },
{ "\nPress any key to exit..." },
}, true, {})
vim.fn.getchar()
os.exit(1)
end
end
vim.opt.rtp:prepend(lazypath)
local plugins = {
{
'catppuccin/nvim',
lazy = false,
priority = 1000,
config = function()
vim.cmd([[colorscheme catppuccin]])
end,
},
'neovim/nvim-lspconfig',
'windwp/nvim-autopairs',
{
'nvim-lualine/lualine.nvim',
dependencies = {'nvim-tree/nvim-web-devicons'},
},
'mattn/emmet-vim',
{
'nvim-treesitter/nvim-treesitter',
cmd = {"TSUpdateSync"},
},
'ray-x/lsp_signature.nvim',
{
'lukas-reineke/indent-blankline.nvim',
main = "ibl",
},
'glench/vim-jinja2-syntax',
'tsandall/vim-rego',
'lewis6991/gitsigns.nvim',
-- auto completion,
{
'hrsh7th/nvim-cmp',
dependencies = {
'hrsh7th/cmp-nvim-lsp',
'hrsh7th/cmp-buffer',
'hrsh7th/cmp-path',
'hrsh7th/cmp-cmdline',
}
},
'hrsh7th/cmp-vsnip',
'hrsh7th/vim-vsnip',
}
require("lazy").setup({
spec = plugins,
})
require('catppuccin').setup {
flavor = "mocha"
}
-- treesitter
require('nvim-treesitter.configs').setup {
ensure_installed = "all",
ignore_install = { "phpdoc" },
highlight = {
enable = true,
},
indent = {
enable = true
},
additional_vim_regex_highlighting = false,
}
-- completion
local cmp = require('cmp')
cmp.setup({
snippet = {
expand = function(args)
vim.fn["vsnip#anonymous"](args.body)
end
},
mapping = cmp.mapping.preset.insert({
['<C-b>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll_docs(4),
['<C-Space>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.abort(),
['<CR>'] = cmp.mapping.confirm({ select = true }),
}),
sources = cmp.config.sources({
{ name = "nvim_lsp" },
{ name = "vsnip" },
}, {
{ name = "buffer" },
})
})
-- indent guides
require('ibl').setup()
-- lualine
require('lualine').setup()
-- gitsigns
require('gitsigns').setup()
-- autopairs
require('nvim-autopairs').setup()
-- Use an on_attach function to only map the following keys
-- after the language server attaches to the current buffer
local on_attach = function(client, bufnr)
require "lsp_signature".on_attach({
bind = true,
handler_opts = {
border = "rounded"
}
}, bufnr)
vim.lsp.set_log_level("DEBUG")
local function buf_set_keymap(...) vim.api.nvim_buf_set_keymap(bufnr, ...) end
local function buf_set_option(...) vim.api.nvim_buf_set_option(bufnr, ...) end
-- Enable completion triggered by <c-x><c-o>
buf_set_option('omnifunc', 'v:lua.vim.lsp.omnifunc')
-- Mappings.
local opts = { noremap=true, silent=true }
-- See `:help vim.lsp.*` for documentation on any of the below functions
buf_set_keymap('n', 'gD', '<cmd>lua vim.lsp.buf.declaration()<CR>', opts)
buf_set_keymap('n', 'gd', '<cmd>lua vim.lsp.buf.definition()<CR>', opts)
buf_set_keymap('n', 'K', '<cmd>lua vim.lsp.buf.hover()<CR>', opts)
buf_set_keymap('n', 'gi', '<cmd>lua vim.lsp.buf.implementation()<CR>', opts)
buf_set_keymap('n', '<C-k>', '<cmd>lua vim.lsp.buf.signature_help()<CR>', opts)
buf_set_keymap('n', '<space>wa', '<cmd>lua vim.lsp.buf.add_workspace_folder()<CR>', opts)
buf_set_keymap('n', '<space>wr', '<cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>', opts)
buf_set_keymap('n', '<space>wl', '<cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>', opts)
buf_set_keymap('n', '<space>D', '<cmd>lua vim.lsp.buf.type_definition()<CR>', opts)
buf_set_keymap('n', '<space>rn', '<cmd>lua vim.lsp.buf.rename()<CR>', opts)
buf_set_keymap('n', '<space>ca', '<cmd>lua vim.lsp.buf.code_action()<CR>', opts)
buf_set_keymap('n', 'gr', '<cmd>lua vim.lsp.buf.references()<CR>', opts)
buf_set_keymap('n', '<space>e', '<cmd>lua vim.lsp.diagnostic.show_line_diagnostics()<CR>', opts)
buf_set_keymap('n', '[d', '<cmd>lua vim.lsp.diagnostic.goto_prev()<CR>', opts)
buf_set_keymap('n', ']d', '<cmd>lua vim.lsp.diagnostic.goto_next()<CR>', opts)
buf_set_keymap('n', '<space>q', '<cmd>lua vim.lsp.diagnostic.set_loclist()<CR>', opts)
buf_set_keymap('n', '<space>f', '<cmd>lua vim.lsp.buf.format()<CR>', opts)
end
-- Use a loop to conveniently call 'setup' on multiple servers and
-- map buffer local keybindings when the language server attaches
local nvim_lsp = require('lspconfig')
local capabilities = require('cmp_nvim_lsp').default_capabilities()
local shared = {
capabilities = capabilities,
on_attach=on_attach,
}
local servers = {
gopls = {
cmd = {"gopls", "--remote", "auto"}
},
pyright = {},
rust_analyzer = {
settings = {
["rust-analyzer"] = {
assist = {
importGranularity = "module",
importPrefix = "by_self",
},
cargo = {
loadOutDirsFromCheck = true
},
procMacro = {
enable = true
},
}
}
},
solargraph = {},
terraformls = {},
tsserver = {},
lua_ls = {}
}
for srv, srv_cfg in pairs(servers) do
cfg = vim.tbl_deep_extend("error", srv_cfg, shared)
nvim_lsp[srv].setup(cfg)
end
vim.opt.syntax = "ON"
vim.opt.laststatus = 2
vim.opt.shiftwidth = 4
vim.opt.tabstop = 4
vim.opt.softtabstop = 4
vim.opt.autoindent = true
vim.opt.expandtab = true
vim.opt.nu = true
vim.opt.showcmd = true
vim.opt.wildmenu = true
vim.opt.lazyredraw = true
vim.opt.showmatch = true
vim.opt.hlsearch = true
vim.g.editorconfig = false
vim.keymap.set('n', '<leader><space>', ':nohlsearch<CR>')
-- double spaces
vim.api.nvim_create_autocmd({"FileType"}, {
desc = "Some files are just double spaced",
pattern = {"terraform", "css", "html.handlebars", "html", "javascript", "json", "yaml", "lua"},
command = "setlocal shiftwidth=2 tabstop=2 softtabstop=2"
})
vim.api.nvim_create_autocmd({"BufWritePre"}, {
pattern = {"*.tfvars", "*.tf"},
command = "lua vim.lsp.buf.format()"
})
-- attempt to autoformat on save
vim.api.nvim_create_autocmd({"BufWritePre"}, {
pattern = {"go.mod", "go.sum", "*.go"},
command = "lua vim.lsp.buf.format()"
})
Logs
gopls
logs were empty. I can provide the lsp logs, but they're pretty brutal to read.
Comment From: gabyhelp
Related Issues and Documentation
- x/tools/gopls: Completion not working in modules with "vendor" as their name #54071 (closed)
- x/tools/gopls: nvim with go1.22 stopped working #66743 (closed)
- x/tools/gopls: panic: invalid Go version "go1.22.1" (should be something like "go1.12") #66553 (closed)
- Gopls is not installing on neovim editor. #64035 (closed)
- x/tools/gopls: gopls crashes neovim #51643 (closed)
- x/tools/gopls: missing complete entry when using gopls 0.12.0 #60835 (closed)
- x/tools/gopls: gopls stopped responding when gomod failed. #38818 (closed)
- x/tools/gopls: InvalidRangeExpr when using GOEXPERIMENT=rangefunc on iter.Seq #65667 (closed)
- x/tools/gopls: panic and crash in golang.org/x/telemetry/internal/counter #64577 (closed)
- x/tools/gopls: Error loading workspace folders (expected 1, got 0) #44122 (closed)
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
Comment From: abennett
What's odd is that autocompletion still works if triggered by entering a .
or the like. Only omnifunc completion with <ctrl-x><ctrl-o>
is failing.
Still working out how to debug this, and the problem could exist elsewhere. But given that I can revert to 0.15.3
to fix the problem makes me think there's a regression somewhere in 0.16.1
.
Comment From: hyangah
gopls v0.16.0+ started to use InsertReplaceEdits
if the client sets the capability. (https://github.com/golang/tools/blob/3057be8f634fdb03e1da1cad9fff3415299ad3ad/gopls/internal/protocol/tsprotocol.go#L1024). I think that is related.
@abennett Can you please share the LSP trace that includes the initialize message and the completion request/response messages (both failure case and successful case (.
-triggered)?
Comment From: abennett
I'll try to get whatever you ask for! 🙇
Comment From: abennett
From 0.16.1
(the "bad" one)
[DEBUG][2024-08-08 11:43:30] .../lua/vim/lsp.lua:1010 "omnifunc.findstart" {
base = "",
findstart = 1
}
[DEBUG][2024-08-08 11:43:30] ...m/lsp/client.lua:678 "LSP[gopls]" "client.request" 1 "textDocument/completion" {
position = {
character = 8,
line = 78
},
textDocument = {
uri = "file:///Users/aaron/mine/clickmon/main.go"
}
} <function 1> 1
[DEBUG][2024-08-08 11:43:30] .../vim/lsp/rpc.lua:286 "rpc.send" {
id = 11,
jsonrpc = "2.0",
method = "textDocument/completion",
params = {
position = {
character = 8,
line = 78
},
textDocument = {
uri = "file:///Users/aaron/mine/clickmon/main.go"
}
}
}
[DEBUG][2024-08-08 11:43:30] .../vim/lsp/rpc.lua:408 "rpc.receive" {
id = 11,
jsonrpc = "2.0",
result = {
isIncomplete = true,
items = { {
detail = "func(d time.Duration) time.Time",
documentation = {
kind = "markdown",
value = "Add returns the time t+d.\n"
},
filterText = "Add",
insertTextFormat = 2,
kind = 2,
label = "Add",
preselect = true,
sortText = "00000",
textEdit = {
insert = {
["end"] = {
character = 8,
line = 78
},
start = {
character = 8,
line = 78
}
},
newText = "Add(${1:})",
replace = {
["end"] = {
character = 8,
line = 78
},
start = {
character = 8,
line = 78
}
}
}
}
From 0.15.3
(the "good" one)
[DEBUG][2024-08-08 11:45:22] .../lua/vim/lsp.lua:1010 "omnifunc.findstart" {
base = "",
findstart = 1
}
[DEBUG][2024-08-08 11:45:22] ...m/lsp/client.lua:678 "LSP[gopls]" "client.request" 1 "textDocument/completion" {
position = {
character = 8,
line = 78
},
textDocument = {
uri = "file:///Users/aaron/mine/clickmon/main.go"
}
} <function 1> 1
[DEBUG][2024-08-08 11:45:22] .../vim/lsp/rpc.lua:286 "rpc.send" {
id = 10,
jsonrpc = "2.0",
method = "textDocument/completion",
params = {
position = {
character = 8,
line = 78
},
textDocument = {
uri = "file:///Users/aaron/mine/clickmon/main.go"
}
}
}
[DEBUG][2024-08-08 11:45:22] .../vim/lsp/rpc.lua:408 "rpc.receive" {
id = 10,
jsonrpc = "2.0",
result = {
isIncomplete = true,
items = { {
detail = "func(d time.Duration) time.Time",
documentation = {
kind = "markdown",
value = "Add returns the time t+d.\n"
},
filterText = "Add",
insertTextFormat = 2,
kind = 2,
label = "Add",
preselect = true,
sortText = "00000",
textEdit = {
newText = "Add(${1:})",
range = {
["end"] = {
character = 8,
line = 78
},
start = {
character = 8,
line = 78
}
}
}
}
I truncated items from each.
Comment From: abennett
Looks like the difference is the textEdit
using inserts like you said, and neovim is explicitly looking for textEdit.range
which no longer exists in gopls v0.16.1
. Hence the error.
https://github.com/neovim/neovim/blob/v0.10.1/runtime/lua/vim/lsp/_completion.lua#L154
if item.textEdit and item.textEdit.range.start.line == lnum then
Comment From: abennett
https://github.com/neovim/neovim/issues/14560#issuecomment-841749529
This is an upstream bug, we don't support InsertReplaceEdits (since we're using omnifunc which doesn't support that ofc), yet that is the response we are getting for completion results (even though we do not send this capability)
Comment From: abennett
So this is neovim's omnifunc not supporting InsertReplaceEdits
. Not sure how to proceed offhand. 🤔
Comment From: findleyr
So this is neovim's omnifunc not supporting InsertReplaceEdits.
and advertising the capability. I wonder if there's a minimally invasive way to suppress this capability in neovim.
Comment From: hyangah
https://github.com/neovim/neovim/issues/16909 and https://github.com/hrsh7th/cmp-nvim-lsp/issues/53#issuecomment-1509973282 may be related.
The gopls uses InsertReplaceEdits
only if the client advertises the capability (insertReplaceSupport
) following the LSP spec. So, this is a client-side issue. https://github.com/hrsh7th/cmp-nvim-lsp/issues/53#issuecomment-1509973282 has an example snippet to suppress the capability @abennett
Comment From: abennett
Agreee. vim-cmp
autocomplete is compatible, so this should work.
vim.keymap.set("i", "<C-x><C-o>", cmp.complete)
Comment From: abennett
Thank you fair bearing with me, @hyangah. 🙇♂️