Skip to content

Commit c21fb9d

Browse files
authored
Add spinner and buffer view option (CopilotC-Nvim#5)
* chore: sync fork * feat: add option to change view mode * chore: sync the change from CopilotC-Nvim#9 * docs: add new usage to canary branch fix: typo on usage * chore: sync fork chore: sync the fork chore: sync fork chore: sync fork * feat: add spinner * feat: show spinner on processing * chore: sync fork
1 parent 8ebb0b7 commit c21fb9d

9 files changed

Lines changed: 448 additions & 77 deletions

File tree

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,20 @@ It will prompt you with instructions on your first start. If you already have `C
88

99
### Lazy.nvim
1010

11-
1. `pip install python-dotenv requests pynvim prompt-toolkit`
11+
1. `pip install python-dotenv requests pynvim==0.5.0 prompt-toolkit`
1212
2. Put it in your lazy setup
1313

1414
```lua
1515
require('lazy').setup({
1616
{
17-
"gptlang/CopilotChat.nvim",
17+
"jellydn/CopilotChat.nvim",
18+
branch = "canary",
1819
opts = {},
1920
build = function()
20-
vim.cmd("UpdateRemotePlugins")
21+
vim.defer_fn(function()
22+
vim.cmd("UpdateRemotePlugins")
23+
vim.notify("CopilotChat - Updated remote plugins. Please restart Neovim.")
24+
end, 3000)
2125
end,
2226
event = "VeryLazy",
2327
keys = {

lua/CopilotChat/init.lua

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@ local utils = require('CopilotChat.utils')
33
local M = {}
44

55
-- Set up the plugin
6-
M.setup = function()
6+
---@param options (table | nil)
7+
-- - mode: ('newbuffer' | 'split') default: newbuffer.
8+
M.setup = function(options)
9+
vim.g.copilot_chat_view_option = options and options.mode or 'newbuffer'
10+
711
-- Add new command to explain the selected text with CopilotChat
8-
utils.create_cmd('CopilotChatExplain', function(opts)
12+
utils.create_cmd('CopilotChatExplain', function()
913
vim.cmd('CopilotChat Explain how it works')
1014
end, { nargs = '*', range = true })
1115

lua/CopilotChat/spinner.lua

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
-- spinner.lua
2+
--
3+
-- This library is free software; you can redistribute it and/or modify it
4+
-- under the terms of the MIT license. See LICENSE for details.
5+
6+
local M = {}
7+
8+
-- User configuration section
9+
local config = {
10+
-- Show notification when done.
11+
-- Set to false to disable.
12+
show_notification = true,
13+
-- Name of the plugin.
14+
plugin = 'CopilotChat.nvim',
15+
-- Spinner frames.
16+
spinner_frames = {
17+
'',
18+
'',
19+
'',
20+
'',
21+
'',
22+
'',
23+
'',
24+
'',
25+
'',
26+
'',
27+
},
28+
}
29+
30+
-- {{{ NO NEED TO CHANGE
31+
32+
local spinner_index = 1
33+
local spinner_timer = nil
34+
local spinner_buf = nil
35+
local spinner_win = nil
36+
37+
--- Show a spinner at the specified position.
38+
---@param position? table
39+
function M.show(position)
40+
-- Default position: the top right corner
41+
local default_position = {
42+
relative = 'editor',
43+
width = 1,
44+
height = 1,
45+
col = vim.o.columns - 1,
46+
row = 0,
47+
}
48+
local options = position or default_position
49+
options.style = 'minimal'
50+
51+
-- Create buffer and window for the spinner
52+
spinner_buf = vim.api.nvim_create_buf(false, true)
53+
spinner_win = vim.api.nvim_open_win(spinner_buf, false, options)
54+
55+
-- Set up timer and update spinner
56+
spinner_timer = vim.loop.new_timer()
57+
spinner_timer:start(
58+
0,
59+
100,
60+
vim.schedule_wrap(function()
61+
vim.api.nvim_buf_set_lines(
62+
spinner_buf,
63+
0,
64+
-1,
65+
false,
66+
{ config.spinner_frames[spinner_index] }
67+
)
68+
spinner_index = spinner_index % #config.spinner_frames + 1
69+
end)
70+
)
71+
end
72+
73+
--- Hide the spinner.
74+
---@param show_msg? boolean
75+
function M.hide(show_msg)
76+
if spinner_timer then
77+
spinner_timer:stop()
78+
spinner_timer:close()
79+
spinner_timer = nil
80+
if spinner_win then
81+
vim.api.nvim_win_close(spinner_win, true)
82+
end
83+
if spinner_buf then
84+
vim.api.nvim_buf_delete(spinner_buf, { force = true })
85+
end
86+
87+
if config.show_notification or show_msg then
88+
vim.notify('Done!', vim.log.levels.INFO, { title = config.plugin })
89+
end
90+
end
91+
end
92+
93+
-- }}}
94+
95+
return M

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
python-dotenv
22
requests
3-
pynvim
3+
pynvim==0.5.0
44
prompt-toolkit

rplugin/python3/copilot-plugin.py

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import copilot
55
import dotenv
6+
import prompts
67
import pynvim
78

89
dotenv.load_dotenv()
@@ -34,41 +35,69 @@ def copilotChat(self, args: list[str]):
3435
if self.copilot.github_token is None:
3536
self.nvim.out_write("Please authenticate with Copilot first\n")
3637
return
38+
39+
# Start the spinner
40+
self.nvim.exec_lua('require("CopilotChat.spinner").show()')
41+
3742
prompt = " ".join(args)
43+
if prompt == "/fix":
44+
prompt = prompts.FIX_SHORTCUT
45+
elif prompt == "/test":
46+
prompt = prompts.TEST_SHORTCUT
47+
elif prompt == "/explain":
48+
prompt = prompts.EXPLAIN_SHORTCUT
3849

3950
# Get code from the unnamed register
4051
code = self.nvim.eval("getreg('\"')")
4152
file_type = self.nvim.eval("expand('%')").split(".")[-1]
53+
54+
# Get the view option from the command
55+
view_option = self.nvim.eval("g:copilot_chat_view_option")
56+
4257
# Check if we're already in a chat buffer
4358
if self.nvim.eval("getbufvar(bufnr(), '&buftype')") != "nofile":
4459
# Create a new scratch buffer to hold the chat
45-
self.nvim.command("enew")
60+
if view_option == "split":
61+
self.nvim.command("vnew")
62+
else:
63+
self.nvim.command("enew")
64+
# Set the buffer type to nofile and hide it when it's not active
4665
self.nvim.command("setlocal buftype=nofile bufhidden=hide noswapfile")
4766
# Set filetype as markdown and wrap with linebreaks
4867
self.nvim.command("setlocal filetype=markdown wrap linebreak")
4968

50-
if self.nvim.current.line != "":
51-
# Go to end of file and insert a new line
52-
self.nvim.command("normal Go")
53-
self.nvim.current.line += "### User"
54-
self.nvim.command("normal o")
55-
# TODO: How to handle the case with the large text in from neovim command
56-
self.nvim.current.line += prompt
57-
self.nvim.command("normal o")
58-
self.nvim.current.line += "### Copilot"
59-
self.nvim.command("normal o")
69+
# Get the current buffer
70+
buf = self.nvim.current.buffer
71+
self.nvim.api.buf_set_option(buf, "fileencoding", "utf-8")
72+
73+
# Add start separator
74+
start_separator = f"""### User
75+
{prompt}
76+
77+
### Copilot
6078
79+
"""
80+
buf.append(start_separator.split("\n"), -1)
81+
82+
# Add chat messages
6183
for token in self.copilot.ask(prompt, code, language=file_type):
62-
if "\n" not in token:
63-
self.nvim.current.line += token
64-
continue
65-
lines = token.split("\n")
66-
for i in range(len(lines)):
67-
self.nvim.current.line += lines[i]
68-
if i != len(lines) - 1:
69-
self.nvim.command("normal o")
70-
71-
self.nvim.command("normal o")
72-
self.nvim.current.line += ""
73-
self.nvim.command("normal o")
74-
self.nvim.current.line += "---"
84+
buffer_lines = self.nvim.api.buf_get_lines(buf, 0, -1, 0)
85+
last_line_row = len(buffer_lines) - 1
86+
last_line = buffer_lines[-1]
87+
last_line_col = len(last_line.encode("utf-8"))
88+
89+
self.nvim.api.buf_set_text(
90+
buf,
91+
last_line_row,
92+
last_line_col,
93+
last_line_row,
94+
last_line_col,
95+
token.split("\n"),
96+
)
97+
98+
# Stop the spinner
99+
self.nvim.exec_lua('require("CopilotChat.spinner").hide()')
100+
101+
# Add end separator
102+
end_separator = "\n---\n"
103+
buf.append(end_separator.split("\n"), -1)

0 commit comments

Comments
 (0)