zbirenbaum / copilot.lua

Fully featured & enhanced replacement for copilot.vim complete with API for interacting with Github Copilot
MIT License
2.54k stars 72 forks source link

Feature: Loading indicator #121

Closed raine closed 1 year ago

raine commented 1 year ago

Hello,

Would it be possible to get some kind of indicator in for example statusbar, that copilot is fetching a suggestion?

Sometimes on hitting next suggestion I keep waiting and waiting and nothing ever comes up, and you can never be sure if it's just slow or not doing anything at all.

Thoughts? Thanks.

MunifTanjim commented 1 year ago

You can do something like this...

Set initial vim.g.copilot_status:

vim.api.nvim_set_var("copilot_status", "")

After calling require("copilot").setup():

local api = require("copilot.api")
api.register_status_notification_handler(function (data)
  vim.api.nvim_set_var("copilot_status", data.status)
end)

Then in your statusline you can use that value:


local copilot_status = vim.api.nvim_get_var("copilot_status")

if copilot_status == "Normal" then
  -- idle
elseif copilot_status == "InProgress" then
  -- processing
elseif copilot_status == "Warning" then
  -- something went wrong
else
  -- unknown
end
raine commented 1 year ago

Thanks @MunifTanjim.

I'm trying to make that work with lualine.nvim, but I think the issue left is that lualine only refreshes periodically and you won't get real time updates on copilot's status. I tried to work around it like so:

vim.keymap.set("i", "<M-]>", function ()
  require("copilot.suggestion").next()
  require("lualine").refresh()
end, {
  desc = "[copilot] next suggestion",
  silent = true,
})

It doesn't seem to work. I wonder if the issue is that call on next() blocks until suggestion is ready.

edit: Okay, I wasn't thinking. Of course I can just call require('lualine').refresh() on api.register_status_notification_handler callback.

brunobmello25 commented 1 year ago

@raine did you manage to get it working? I think mine is more or less working

on copilot.config I do:

  vim.api.nvim_set_var("copilot_status", "")

  api.register_status_notification_handler(function(data)
    vim.api.nvim_set_var("copilot_status", data.status)
    lualine.refresh()
  end)

And on my lualine module I have:

    lualine_x = {
      {
        function()
          local _, copilot_status = pcall(vim.api.nvim_get_var("copilot_status"))

          if copilot_status == "Normal" then
            return "O"
          elseif copilot_status == "InProgress" then
            return "!"
          elseif copilot_status == "Error" then
            return "X"
          else
            return ""
          end
        end
      }
    },

but it's not really showing anything

MunifTanjim commented 1 year ago

If you set vim.api.nvim_set_var("copilot_status", "") before that variable is ever read, you can get rid of the pcall (which is kinda slow).

but it's not really showing anything

I think your pcall syntax is not correct... it should be pcall(vim.api.nvim_get_var, "copilot_status").

@brunobmello25

brunobmello25 commented 1 year ago

hey @MunifTanjim , thanks for the help! indeed it was a problem in the pcall syntax, I'm not really good with lua hehehe

now it's working fine. I think it's best to maintain that pcall because I have no way of guaranteeing that there will be no call to that variable before it's initialized

thanks a lot! Now I just have to find better icons lol

brunobmello25 commented 1 year ago

Btw, I think that this might be a nice feature to integrate within this plugin. I'll see if I can work on this integration a bit better and open a PR at some time

MunifTanjim commented 1 year ago

Well the feature is already there, kinda. You subscribe to the status change using api.register_status_notification_handler and then use the value in your statusline plugin of choice.

Although, it would be nice if the virtual text could display a loading indicator when suggestions are loading.

brunobmello25 commented 1 year ago

I thought that maybe we could add an easier integration feature? like for example:

copilot.setup({
  integrations = {
    lualine = true
  }
})

and then maybe we could provide customization options, like:

copilot.setup({
  integrations = {
    lualine = {
      idle_icon = "...",
      loading_icon = "...",
    }
  }
})
MunifTanjim commented 1 year ago

There are a whole bunch of statusline plugins 😂 lualine / feline / galaxyline / expressline / neoline / windline / nougat and many more.

I wouldn't want to add integration with any specific statusline plugin, because that opens the door to other statusline plugins as well. It'll not be good for maintenance.

raine commented 1 year ago

Maybe it could provide building blocks for integrating it yourself:

By the way, yes, I got it working and it's definitely on improvement, but it still feels like something is missing. It's probably the case when you ask for next suggestion and nothing just happens, no change in status or anything. It leaves you a bit confused because you can't tell what the issue is; copilot just doesn't have any results or what. Maybe the virtual text way @MunifTanjim mentioned could also say something if no results were found or any other reason why nothing gets suggested.

zbirenbaum commented 1 year ago

There are a whole bunch of statusline plugins joy lualine / feline / galaxyline / expressline / neoline / windline / nougat and many more.

I wouldn't want to add integration with any specific statusline plugin, because that opens the door to other statusline plugins as well. It'll not be good for maintenance.

This is 100% correct. The purpose of copilot.lua has always been to provide the out of the box functionality provided by copilot.vim + a general api for other people to build on top of which copilot.vim lacks. That's why despite being the most popular use case, cmp integration is in its own repo.

For the record, this is already pretty easy to implement in every status line I know of. If your statusline doesn't allow you to update components by providing a function, I would recommend using something more modern. Lualine, Heirline, and Feline all support this.

It should be plenty for almost modern statuslines to do the following:

local M = { init = false }

local status = ''
local setup = function ()
  local api = require('copilot.api')
  api.register_status_notification_handler(function(data)
   -- customize your message however you want
    if data.status == 'Normal' then
      status = 'Ready'
    elseif data.status == 'InProgress' then
      status = 'Pending'
    else
      status = data.status or 'Offline' -- might never actually be nil but just in case
    end
    status = 'Copilot: ' .. status
  end)
end

M.get_status = function ()
  if not M.init then
    setup()
    M.init = true
  end
  return status
end

return M

Inside your statusline config: copilot_status = require('above_module').get_status For lualine: sections = { lualine_a = { copilot_status } }

For Feline:

copilot_component = {
  provider = copilot_status,
  hl = { fg = colors.vibrant_green, bg = 'NONE'},
  left_sep = {
    str = ' ',
    hl = { fg = 'NONE', bg = 'NONE', },
  },
  right_sep = {
    str = ' ',
    hl = { fg = 'NONE', bg = 'NONE', },
  }
}

`components.active[1] = {  copilot_component }

For the record, I pulled the feline example straight from my config, and it updates in realtime with virtually no delay regardless of if I'm using copilot-cmp, panel completions, etc.

Since this feature is out of the scope of the project, I am going to close this, but if the users here would like a wiki linked in the docs that people can PR with examples of integrating copilot.lua with various individual plugins, I would be open to that. If that's something you would like to see, please open a new issue titled 'Feature: User Integrations Wiki' or similar and just link this issue in the description.