Skip to content

Commit ce68b1f

Browse files
committed
Move help to separate window
Closes #361 Signed-off-by: Tomas Slusny <slusnucky@gmail.com>
1 parent b4e4adb commit ce68b1f

2 files changed

Lines changed: 157 additions & 139 deletions

File tree

lua/CopilotChat/config.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ local select = require('CopilotChat.select')
5151
---@field show_diff CopilotChat.config.mapping?
5252
---@field show_system_prompt CopilotChat.config.mapping?
5353
---@field show_user_selection CopilotChat.config.mapping?
54+
---@field show_help CopilotChat.config.mapping?
5455

5556
--- CopilotChat default configuration
5657
---@class CopilotChat.config
@@ -230,5 +231,8 @@ return {
230231
show_user_selection = {
231232
normal = 'gs',
232233
},
234+
show_help = {
235+
normal = 'gh',
236+
},
233237
},
234238
}

lua/CopilotChat/init.lua

Lines changed: 153 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ local plugin_name = 'CopilotChat.nvim'
2323
--- @field diff CopilotChat.Overlay?
2424
--- @field system_prompt CopilotChat.Overlay?
2525
--- @field user_selection CopilotChat.Overlay?
26+
--- @field help CopilotChat.Overlay?
2627
local state = {
2728
copilot = nil,
2829
chat = nil,
@@ -40,6 +41,7 @@ local state = {
4041
diff = nil,
4142
system_prompt = nil,
4243
user_selection = nil,
44+
help = nil,
4345
}
4446

4547
local function blend_color_with_neovim_bg(color_name, blend)
@@ -213,9 +215,13 @@ end
213215

214216
--- Get the info for a key.
215217
---@param name string
216-
---@param key CopilotChat.config.mapping
218+
---@param key CopilotChat.config.mapping?
217219
---@return string
218220
local function key_to_info(name, key)
221+
if not key then
222+
return ''
223+
end
224+
219225
local out = ''
220226
if key.normal and key.normal ~= '' then
221227
out = out .. "'" .. key.normal .. "' in normal mode"
@@ -625,14 +631,8 @@ function M.setup(config)
625631
{ link = '@punctuation.special.markdown', default = true }
626632
)
627633

628-
local overlay_help = ''
629-
if M.config.mappings.close then
630-
overlay_help = key_to_info('close', M.config.mappings.close)
631-
end
632-
local diff_help = ''
633-
if M.config.mappings.accept_diff then
634-
diff_help = key_to_info('accept_diff', M.config.mappings.accept_diff)
635-
end
634+
local overlay_help = key_to_info('close', M.config.mappings.close)
635+
local diff_help = key_to_info('accept_diff', M.config.mappings.accept_diff)
636636
if overlay_help ~= '' and diff_help ~= '' then
637637
diff_help = diff_help .. '\n' .. overlay_help
638638
end
@@ -688,160 +688,174 @@ function M.setup(config)
688688
end)
689689
end)
690690

691-
local chat_help = ''
692-
if M.config.show_help then
693-
local chat_keys = vim.tbl_keys(M.config.mappings)
694-
table.sort(chat_keys, function(a, b)
695-
a = M.config.mappings[a]
696-
a = a.normal or a.insert
697-
b = M.config.mappings[b]
698-
b = b.normal or b.insert
699-
return a < b
700-
end)
701-
702-
for _, name in ipairs(chat_keys) do
703-
local key = M.config.mappings[name]
704-
chat_help = chat_help .. key_to_info(name, key) .. '\n'
705-
end
691+
if state.help then
692+
state.help:delete()
706693
end
694+
state.help = Overlay('copilot-help', hl_ns, overlay_help, function(bufnr)
695+
map_key(M.config.mappings.close, bufnr, function()
696+
state.help:restore(state.chat.winnr, state.chat.bufnr)
697+
end)
698+
end)
707699

708700
if state.chat then
709701
state.chat:close(state.source and state.source.bufnr or nil)
710702
state.chat:delete()
711703
end
712-
state.chat = Chat(chat_help, M.config.auto_insert_mode, function(bufnr)
713-
map_key(M.config.mappings.complete, bufnr, complete)
714-
map_key(M.config.mappings.reset, bufnr, M.reset)
715-
map_key(M.config.mappings.close, bufnr, M.close)
716-
717-
map_key(M.config.mappings.submit_prompt, bufnr, function()
718-
local chat_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
719-
local lines, start_line, end_line, line_count =
720-
find_lines_between_separator(chat_lines, M.config.separator .. '$')
721-
local input = vim.trim(table.concat(lines, '\n'))
722-
if input ~= '' then
723-
-- If we are entering the input at the end, replace it
724-
if line_count == end_line then
725-
vim.api.nvim_buf_set_lines(bufnr, start_line, end_line, false, { '' })
704+
state.chat = Chat(
705+
M.config.show_help and key_to_info('show_help', M.config.mappings.show_help),
706+
M.config.auto_insert_mode,
707+
function(bufnr)
708+
map_key(M.config.mappings.show_help, bufnr, function()
709+
local chat_help = ''
710+
local chat_keys = vim.tbl_keys(M.config.mappings)
711+
table.sort(chat_keys, function(a, b)
712+
a = M.config.mappings[a]
713+
a = a.normal or a.insert
714+
b = M.config.mappings[b]
715+
b = b.normal or b.insert
716+
return a < b
717+
end)
718+
for _, name in ipairs(chat_keys) do
719+
local key = M.config.mappings[name]
720+
chat_help = chat_help .. key_to_info(name, key) .. '\n'
726721
end
727-
M.ask(input, state.config, state.source)
728-
end
729-
end)
730-
731-
map_key(M.config.mappings.accept_diff, bufnr, function()
732-
local selection = get_selection()
733-
if not selection.start_row or not selection.end_row then
734-
return
735-
end
736-
737-
local chat_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
738-
local section_lines =
739-
find_lines_between_separator(chat_lines, M.config.separator .. '$', true)
740-
local lines = find_lines_between_separator(section_lines, '^```%w*$', true)
741-
if #lines > 0 then
742-
vim.api.nvim_buf_set_text(
743-
state.source.bufnr,
744-
selection.start_row - 1,
745-
selection.start_col - 1,
746-
selection.end_row - 1,
747-
selection.end_col,
748-
lines
749-
)
750-
end
751-
end)
722+
chat_help = chat_help .. M.config.separator .. '\n'
723+
state.help:show(chat_help, 'markdown', 'markdown', state.chat.winnr)
724+
end)
752725

753-
map_key(M.config.mappings.yank_diff, bufnr, function()
754-
local selection = get_selection()
755-
if not selection.start_row or not selection.end_row then
756-
return
757-
end
726+
map_key(M.config.mappings.complete, bufnr, complete)
727+
map_key(M.config.mappings.reset, bufnr, M.reset)
728+
map_key(M.config.mappings.close, bufnr, M.close)
729+
730+
map_key(M.config.mappings.submit_prompt, bufnr, function()
731+
local chat_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
732+
local lines, start_line, end_line, line_count =
733+
find_lines_between_separator(chat_lines, M.config.separator .. '$')
734+
local input = vim.trim(table.concat(lines, '\n'))
735+
if input ~= '' then
736+
-- If we are entering the input at the end, replace it
737+
if line_count == end_line then
738+
vim.api.nvim_buf_set_lines(bufnr, start_line, end_line, false, { '' })
739+
end
740+
M.ask(input, state.config, state.source)
741+
end
742+
end)
758743

759-
local chat_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
760-
local section_lines =
761-
find_lines_between_separator(chat_lines, M.config.separator .. '$', true)
762-
local lines = find_lines_between_separator(section_lines, '^```%w*$', true)
763-
if #lines > 0 then
764-
local content = table.concat(lines, '\n')
765-
vim.fn.setreg(M.config.mappings.yank_diff.register, content)
766-
end
767-
end)
744+
map_key(M.config.mappings.accept_diff, bufnr, function()
745+
local selection = get_selection()
746+
if not selection.start_row or not selection.end_row then
747+
return
748+
end
768749

769-
map_key(M.config.mappings.show_diff, bufnr, function()
770-
local selection = get_selection()
771-
if not selection or not selection.start_row or not selection.end_row then
772-
return
773-
end
750+
local chat_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
751+
local section_lines =
752+
find_lines_between_separator(chat_lines, M.config.separator .. '$', true)
753+
local lines = find_lines_between_separator(section_lines, '^```%w*$', true)
754+
if #lines > 0 then
755+
vim.api.nvim_buf_set_text(
756+
state.source.bufnr,
757+
selection.start_row - 1,
758+
selection.start_col - 1,
759+
selection.end_row - 1,
760+
selection.end_col,
761+
lines
762+
)
763+
end
764+
end)
774765

775-
local chat_lines = vim.api.nvim_buf_get_lines(state.chat.bufnr, 0, -1, false)
776-
local section_lines =
777-
find_lines_between_separator(chat_lines, M.config.separator .. '$', true)
778-
local lines =
779-
table.concat(find_lines_between_separator(section_lines, '^```%w*$', true), '\n')
780-
if vim.trim(lines) ~= '' then
781-
state.last_code_output = lines
766+
map_key(M.config.mappings.yank_diff, bufnr, function()
767+
local selection = get_selection()
768+
if not selection.start_row or not selection.end_row then
769+
return
770+
end
782771

783-
local filetype = selection.filetype or vim.bo[state.source.bufnr].filetype
772+
local chat_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
773+
local section_lines =
774+
find_lines_between_separator(chat_lines, M.config.separator .. '$', true)
775+
local lines = find_lines_between_separator(section_lines, '^```%w*$', true)
776+
if #lines > 0 then
777+
local content = table.concat(lines, '\n')
778+
vim.fn.setreg(M.config.mappings.yank_diff.register, content)
779+
end
780+
end)
784781

785-
local diff = tostring(vim.diff(selection.lines, lines, {
786-
result_type = 'unified',
787-
ignore_blank_lines = true,
788-
ignore_whitespace = true,
789-
ignore_whitespace_change = true,
790-
ignore_whitespace_change_at_eol = true,
791-
ignore_cr_at_eol = true,
792-
algorithm = 'myers',
793-
ctxlen = #selection.lines,
794-
}))
795-
796-
state.diff:show(diff, filetype, 'diff', state.chat.winnr)
797-
end
798-
end)
782+
map_key(M.config.mappings.show_diff, bufnr, function()
783+
local selection = get_selection()
784+
if not selection or not selection.start_row or not selection.end_row then
785+
return
786+
end
799787

800-
map_key(M.config.mappings.show_system_prompt, bufnr, function()
801-
local prompt = state.last_system_prompt or M.config.system_prompt
802-
if not prompt then
803-
return
804-
end
788+
local chat_lines = vim.api.nvim_buf_get_lines(state.chat.bufnr, 0, -1, false)
789+
local section_lines =
790+
find_lines_between_separator(chat_lines, M.config.separator .. '$', true)
791+
local lines =
792+
table.concat(find_lines_between_separator(section_lines, '^```%w*$', true), '\n')
793+
if vim.trim(lines) ~= '' then
794+
state.last_code_output = lines
795+
796+
local filetype = selection.filetype or vim.bo[state.source.bufnr].filetype
797+
798+
local diff = tostring(vim.diff(selection.lines, lines, {
799+
result_type = 'unified',
800+
ignore_blank_lines = true,
801+
ignore_whitespace = true,
802+
ignore_whitespace_change = true,
803+
ignore_whitespace_change_at_eol = true,
804+
ignore_cr_at_eol = true,
805+
algorithm = 'myers',
806+
ctxlen = #selection.lines,
807+
}))
808+
809+
state.diff:show(diff, filetype, 'diff', state.chat.winnr)
810+
end
811+
end)
805812

806-
state.system_prompt:show(prompt, 'markdown', 'markdown', state.chat.winnr)
807-
end)
813+
map_key(M.config.mappings.show_system_prompt, bufnr, function()
814+
local prompt = state.last_system_prompt or M.config.system_prompt
815+
if not prompt then
816+
return
817+
end
808818

809-
map_key(M.config.mappings.show_user_selection, bufnr, function()
810-
local selection = get_selection()
811-
if not selection.start_row or not selection.end_row then
812-
return
813-
end
819+
state.system_prompt:show(prompt, 'markdown', 'markdown', state.chat.winnr)
820+
end)
814821

815-
local filetype = selection.filetype or vim.bo[state.source.bufnr].filetype
816-
local lines = selection.lines
817-
if vim.trim(lines) ~= '' then
818-
state.user_selection:show(lines, filetype, filetype, state.chat.winnr)
819-
end
820-
end)
822+
map_key(M.config.mappings.show_user_selection, bufnr, function()
823+
local selection = get_selection()
824+
if not selection.start_row or not selection.end_row then
825+
return
826+
end
821827

822-
vim.api.nvim_create_autocmd({ 'BufEnter', 'BufLeave' }, {
823-
buffer = state.chat.bufnr,
824-
callback = function(ev)
825-
if state.config.highlight_selection then
826-
M.highlight_selection(ev.event == 'BufLeave')
828+
local filetype = selection.filetype or vim.bo[state.source.bufnr].filetype
829+
local lines = selection.lines
830+
if vim.trim(lines) ~= '' then
831+
state.user_selection:show(lines, filetype, filetype, state.chat.winnr)
827832
end
828-
end,
829-
})
833+
end)
830834

831-
if M.config.insert_at_end then
832-
vim.api.nvim_create_autocmd({ 'InsertEnter' }, {
835+
vim.api.nvim_create_autocmd({ 'BufEnter', 'BufLeave' }, {
833836
buffer = state.chat.bufnr,
834-
callback = function()
835-
vim.cmd('normal! 0')
836-
vim.cmd('normal! G$')
837-
vim.v.char = 'x'
837+
callback = function(ev)
838+
if state.config.highlight_selection then
839+
M.highlight_selection(ev.event == 'BufLeave')
840+
end
838841
end,
839842
})
840-
end
841843

842-
append(M.config.question_header .. M.config.separator .. '\n\n')
843-
state.chat:finish()
844-
end)
844+
if M.config.insert_at_end then
845+
vim.api.nvim_create_autocmd({ 'InsertEnter' }, {
846+
buffer = state.chat.bufnr,
847+
callback = function()
848+
vim.cmd('normal! 0')
849+
vim.cmd('normal! G$')
850+
vim.v.char = 'x'
851+
end,
852+
})
853+
end
854+
855+
append(M.config.question_header .. M.config.separator .. '\n\n')
856+
state.chat:finish()
857+
end
858+
)
845859

846860
for name, prompt in pairs(M.prompts(true)) do
847861
vim.api.nvim_create_user_command('CopilotChat' .. name, function(args)

0 commit comments

Comments
 (0)