zbirenbaum / copilot-cmp

Lua plugin to turn github copilot into a cmp source
MIT License
1.07k stars 38 forks source link

Copilot ignores source order on menu #88

Closed paulodiovani closed 9 months ago

paulodiovani commented 10 months ago

I have set copilot as my second to last source, because I want to always have LSP and Snippets first.

  sources = cmp.config.sources({
    { name = 'nvim_lsp' },
    { name = 'luasnip' }, -- For luasnip users.
    { name = 'copilot' },
    { name = 'buffer' },
    -- { name = 'vsnip' }, -- For vsnip users.
    -- { name = 'ultisnips' }, -- For ultisnips users.
    -- { name = 'snippy' }, -- For snippy users.
  })

But it just ignores my setting and injects its completion opinions at the top anyway, at the first or second lines.

image

zbirenbaum commented 10 months ago

This is not how ordering works. The order you list your sources in has no impact on how they appear. Comparator functions are how you control the ordering of completion. Please read the relevant cmp documentation on comparators

paulodiovani commented 10 months ago

The nvim-cmp docs say that the order we list the sources determines their order in the results list.

The order of the sources determines their order in the completion results.

Source: https://github.com/hrsh7th/nvim-cmp/blob/main/doc/cmp.txt#L578-L582

zbirenbaum commented 10 months ago

The nvim-cmp docs say that the order we list the sources determines their order in the results list.

The order of the sources determines their order in the completion results.

Source: https://github.com/hrsh7th/nvim-cmp/blob/main/doc/cmp.txt#L578-L582

Huh, I think that must be new, TIL. The standard way to do this though is through comparators. It's not perfect, as I just tested and it seems when the response first pops in it appears to be getting sent to the top, but once It's cached it sorts properly. I'll reopen this try to find a way to fix this, but it may require upstream changes. For reference though, here's the way you should go about sorting entries:

(fun(entry1: cmp.Entry, entry2: cmp.Entry): boolean | nil)[] The function to customize the sorting behavior. You can use built-in comparators via cmp.config.compare.*

Completions are a list of completion items, comparators are functions which determine how that list is sorted, which is what determines their order in your completion menu.

Source priority is for things like the buffer text source, where it is only really useful as a fallback if a better source like an LSP doesn't give any response at all.

You can view example comparator functions in comparators.lua

local comparators = {}
-- places completions with a higher score (if applicable) higher
comparators.score = function (entry1, entry2)
  if entry1.score and entry2.score then
    return entry1.score > entry2.score
  end
end

-- Places Copilot completions higher
comparators.prioritize = function (entry1, entry2)
  if entry1.copilot and not entry2.copilot then
    return true
  elseif entry2.copilot and not entry1.copilot then
    return false
  end
end

return comparators

If you wanted copilot completions to be sent to the very bottom, you could place this in your cmp config:

local copilot = require("copilot_cmp.comparators")

local reverse_prioritize = function(entry1, entry2)
  if entry1.copilot and not entry2.copilot then
    return false
  elseif entry2.copilot and not entry1.copilot then
    return true
  end
end

cmp.setup({
 ...
  sorting = {
    --keep priority weight at 2 for much closer matches to appear above copilot
    --set to 1 to make copilot always appear on top
    priority_weight = 1,
    comparators = {
      reverse_prioritize,
      cmp.config.compare.exact,
      cmp.config.compare.offset,
      cmp.config.compare.score,
      cmp.config.compare.recently_used,
      cmp.config.compare.locality,
      cmp.config.compare.kind,
      cmp.config.compare.sort_text,
      cmp.config.compare.length,
      cmp.config.compare.order,
    },
  },
paulodiovani commented 10 months ago

It worked, thanks. I just changed it a bit to (re)use default sorting:

-- Set up nvim-cmp.
local cmp = require 'cmp'
local cmp_config_default = require('cmp.config.default')()
local copilot_prioritize = require('copilot_cmp.comparators').prioritize

-- move copilot down
local copilot_reverse_prioritize = function(entry1, entry2)
  return not copilot_prioritize(entry1, entry2)
end

---@cast cmp -nil
cmp.setup({
  -- ...
  sorting = {
    comparators = table.insert(cmp_config_default.sorting.comparators, 1, copilot_reverse_prioritize),
  },
})
paulodiovani commented 9 months ago

After tweaking a bit with the comparators I managed to prioritize LSP first. Of course, that depends on the final score and Copilot can still show first in some cases.

In the end, my initial goal was not achieved, which was to prevent Copilot completions from showing "under the cursor", ofter making me select the wrong item. But since this issue's description was not clear on that point I'm closing it.

Thanks for the help.

endoze commented 4 months ago

In case anyone else finds this issue and still wants to de-prioritize copilot suggestions, this is what currently works for me.

local reverse_prioritize = function(entry1, entry2)
  if entry1.source.name == "copilot" and entry2.source.name ~= "copilot" then
    return false
  elseif entry2.copilot == "copilot" and entry1.source.name ~= "copilot" then
    return true
  end
end

I found that just checking either entry for .copilot never actually worked when using the comparators. I'm not sure if that is unique to my setup but the above function can be included in the list of nvim-cmp comparators and it will ensure that copilot suggestions are always at the bottom of nvim-cmp.

xzbdmw commented 3 months ago

reverse_prioritize

works good

paulodiovani commented 1 month ago

Worked on a first test. I'll check if it works on my daily workflow and report back later.