zbirenbaum / copilot-cmp

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

Move completions cycling to RPC [DRAFT] #19

Closed svilupp closed 1 year ago

svilupp commented 1 year ago

This fixes #18

This is an initial draft to move the getCompletionsCycling from a buffer call to an RPC

Draft:

Gaps:

Considerations:

This is probably a zero value-add! Please feel free to scrap it and do it yourself properly.

svilupp commented 1 year ago

Scratch my previous comments. I re-wrote it to take advantage of the existing methods and infrastructure.

Current status: Copilot seems to work and no Julia LS crashes yet.

Questions:

zbirenbaum commented 1 year ago

First off, sorry about the delay, I've been pretty busy, but I am really impressed that you were able to write this without having any lua experience :+1: PanelID is only required for getPanelCompletions, as it is a field the lsp checks for. The reason I have the dynamic registration for panel handlers is because unlike getCompletionsCycling, the response to the request for getPanelCompletions is not actually the completions. The real completions come from a notification, and a second notification (PanelCompletionsDone) comes in to let you know that there are no more expected. Cycling doesn't have such a complex system, and the completions are provided directly in the response callback.

That's not something that anyone could really be expected to know without a lot of knowledge of the copilot internals, so don't feel too bad about it. You actually got pretty close, and I'm sure given that info you could figure it out, but I figured I would help a bit.

Here's the full body for a functioning rpc version of the function:

methods.getCompletionsCycling = function (self, params, callback)
  local request = self.client.rpc.request
  local bufnr = params.context.bufnr
  local row = params.context.cursor.row

  methods.existing_matches[bufnr] = methods.existing_matches[bufnr] or {}
  methods.existing_matches[bufnr][row] = methods.existing_matches[bufnr][row] or {}

  local respond_callback = function(err, response)
    if err then return err end
    if not response or vim.tbl_isempty(response.completions) then return end --j
    methods.existing_matches[bufnr][row] = add_results(response.completions, params)
    local existing_matches = methods.existing_matches[bufnr][row]
    local completions = formatter.format_completions(vim.tbl_values(existing_matches or {}), params)
    callback(completions)
  end

  request("getCompletionsCycling", util.get_completion_params(), respond_callback)
  local completions = formatter.format_completions(vim.tbl_values(methods.existing_matches[bufnr][row] or {}), params)
  callback(completions)
end

You can open a new PR or do a hard reset and just paste that in over the current method, or you can manually remove the changes from outside that function and replace it. Since you put a good amount of work into this I'd like to give some credit

svilupp commented 1 year ago

Thank you. I've made the change.

Out of curiosity, what is the need for having the callback() inside of respond_callback() function? Isn't it called twice upon a successful request (once in the respond_callback() and once at the end of the method? methods.existing_matches seems non-local within respond_callback() so it would hold the results of completions in both of those callback calls, wouldn't it?

zbirenbaum commented 1 year ago

Thank you. I've made the change.

Out of curiosity, what is the need for having the callback() inside of respond_callback() function? Isn't it called twice upon a successful request (once in the respond_callback() and once at the end of the method? methods.existing_matches seems non-local within respond_callback() so it would hold the results of completions in both of those callback calls, wouldn't it?

Looks good!

And good question, callback is a function passed by nvim-cmp to the source's completion function, and takes a table with IsIncomplete and a table of the completions as the argument. respond_callback is the function triggered when a response is received from the LSP for the request submitted. Response callback receives the completion, which is then passed to cmp.

cmp will wait until it receives a response from the source (via callback), so I return the cached completions immediately to avoid freezing cmp (callback at the end), and then provide cmp the updated completions once they are received from copilot (callback inside response_callback).