Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Selection cursor doesn't appear at the top of the autocompletion list for gopls #1809

Closed
2 tasks done
anthony-S93 opened this issue Jan 31, 2024 · 4 comments
Closed
2 tasks done
Labels
bug Something isn't working

Comments

@anthony-S93
Copy link

FAQ

  • I have checked the FAQ and it didn't resolve my problem.

Announcement

Minimal reproducible full config

-- minimal_init.lua
local root = vim.fn.fnamemodify("./.repro", ":p")

-- set stdpaths to use .repro
for _, name in ipairs({"config", "data", "state", "cache"}) do
    vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
end

--bootstrap lazy
local lazypath = root .. "/plugins/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
    vim.fn.system({
        "git",
        "clone",
        "--filter=blob:none",
        "--single-branch",
        "https://github.com/folke/lazy.nvim.git",
        lazypath,
    })
end
vim.opt.runtimepath:prepend(lazypath)


--install plugins
local plugins = {
    {
        "hrsh7th/nvim-cmp",
        dependencies = {
            "hrsh7th/cmp-nvim-lsp",
        }
    },

    {
        "saadparwaiz1/cmp_luasnip",
        dependencies = {
            "L3MON4D3/LuaSnip",
        }
    }
}

require("lazy").setup(plugins, { root = root .. "/plugins", })

-- Completion plugin
local cmp = require("cmp")
cmp.setup({
    snippet = {
        expand = function(args)
            require("luasnip").lsp_expand(args.body)
        end,
    },
    window = {
      -- completion = cmp.config.window.bordered(),
      -- documentation = cmp.config.window.bordered(),
    },
    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 }), -- Accept currently selected item. Set `select` to `false` to only confirm explicitly selected items.
    }),
    sources = cmp.config.sources({
      { name = 'nvim_lsp' },
      { name = 'luasnip' }, -- For luasnip users.
      -- { name = 'ultisnips' }, -- For ultisnips users.
      -- { name = 'snippy' }, -- For snippy users.
    }, {
      { name = 'buffer' },
    })
})

local capabilities = require("cmp_nvim_lsp").default_capabilities()

--- Lsp
vim.lsp.set_log_level("debug")
local pattern = 'go'
local cmd = {'/usr/bin/gopls'}
-- Add files/folders here that indicate the root of a project
local root_markers = {'go.mod'}
-- Change to table with settings if required
local settings = {
    gopls = {
        semanticTokens = true,
    }
}

vim.api.nvim_create_autocmd('FileType', {
  pattern = pattern,
  callback = function(args)
    local match = vim.fs.find(root_markers, { path = args.file, upward = true })[1]
    local root_dir = match and vim.fn.fnamemodify(match, ':p:h') or nil
    vim.lsp.start({
      name = 'gopls',
      cmd = cmd,
      root_dir = root_dir,
      settings = settings,
      capabilities = capabilities
    })
  end
})

Description

With gopls, the selection cursor will not appear at the top of the autocompletion list as it does with other language servers such as pyright, jdtls, tsserver, etc. This inconsistency bothers me because it often led to typing mistakes (since all other language servers ensure that the cursor always appears at the beginning of the list, with gopls, I have the tendency to press enter thinking that the top item is selected when in reality, the cursor is located at an item somewhere below the list).

I will use two examples to illustrate this issue.

Example 1: snippets

problematic_gopls_completion.snippets.mp4

As you can see, when the completion list pops up, the second item is selected instead of the top item. Here is the lsp log file for this example: snippets-completion-issue.log

Example 2: normal autocompletion

problematic_gopls_completion.normal.completion.mp4

In this example, it seems to me like the cursor pre-selects the item based on alphabetical order despite the fact that the autocompletion list isn't ordered alphabetically. Here is the lsp log file: normal-autocompletion-issue.log

Steps to reproduce

The environment used to demonstrate the issue can be set up via the following steps:

  1. Make sure you have a system-wide installation of go and gopls
  2. Create a test directory $ mkdir testing
  3. cd testing
  4. $ go mod init example/testing
  5. Create test file $touch test.go
  6. Open the test file nvim -u /path/to/minimal_init.lua test.go
  7. Type package on the first line of the file.

At this point, the behavior on your screen should mirror the behavior in the video I posted above.

Expected behavior

The cursor should select the top item in the autocompletion list.

Actual behavior

The cursor does not select the top item in the autocompletion list.

Additional context

Please note that this behavior occurs with gopls only and no other language server. I'm aware that this likely indicates that the issue is with gopls. But I have already opened an issue with gopls golang/go#61533. The developer's response is that the issue isn't with them.

@anthony-S93 anthony-S93 added the bug Something isn't working label Jan 31, 2024
@anthony-S93
Copy link
Author

anthony-S93 commented Feb 2, 2024

It turns out that gopls pre-selects an item each time an auto-completion list is generated. And gopls's pre-selection does not respect the ordering of the list generated by the completion source. As a workaround, you can prevent this behavior by setting preselect to cmp.PreselectMode.None. E.g:

local cmp = require("cmp")
cmp.setup({
    preselect = cmp.PreselectMode.None
})

@morhaham
Copy link

It turns out that gopls pre-selects an item each time an auto-completion list is generated. And gopls's pre-selection does not respect the ordering of the list generated by the completion source. This behavior can be prevented by setting preselect to cmp.PreselectMode.None. E.g:

local cmp = require("cmp")
cmp.setup({
    preselect = cmp.PreselectMode.None
})

With this setting there is no preselect at all. What if I want the first one to be selected always?

@anthony-S93
Copy link
Author

anthony-S93 commented Apr 27, 2024

The problem is that nvim-cmp reorders the items of the completion list dynamically. What this means is that the order of items in the completion list depends on the current state of the source file. For example, if the name of a function has already appeared in the file at least once, then that item will be placed higher up the list. I've already described the issue in detail here: golang/go#61533 (comment)

Unfortunatly, nvim-cmp's dynamic reordering does not take LSP preselection into account. By right, nvim-cmp should place the item that is preselected by the LSP at the top of the list. That is why I resorted to turn off the preselect feature altogether.

I can reopen this issue if you want. But at the moment, I'm not interested in any further investigation into this issue.

@powerman
Copy link

Another one issue with this: cmp.select_next_item { behavior = types.cmp.SelectBehavior.Select } (not tested with Insert behavior) ignores part of completions between first one and preselected one (actual for a110e12 - latest main branch). After completion menu opens with preselected item selected (ignoring noselect in default completeopt config!) cmp.select_next_item moves from preselected item to the end of a list, then switch to "no selected", but then, instead of selecting first list item it selects preselected one (and again move to the end of a list).

I'd love to see this fixed this way: "preselected" item is always first in a list, but not selected on menu open (because of noselect in completeopt).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants