1+ --- @class CopilotChat.ui.Diff.Diff
2+ --- @field change string
3+ --- @field reference string
4+ --- @field filename string
5+ --- @field filetype string
6+ --- @field start_line number
7+ --- @field end_line number
8+ --- @field bufnr number ?
9+
110local async = require (' plenary.async' )
211local copilot = require (' CopilotChat' )
312local utils = require (' CopilotChat.utils' )
103112--- @class CopilotChat.config.mapping
104113--- @field normal string ?
105114--- @field insert string ?
106- --- @field callback fun ( overlay : CopilotChat.ui.Overlay , diff : CopilotChat.ui.Diff , chat : CopilotChat.ui.Chat , source : CopilotChat.source )
115+ --- @field callback fun ( source : CopilotChat.source )
107116
108117--- @class CopilotChat.config.mapping.yank_diff : CopilotChat.config.mapping
109118--- @field register string ?
@@ -128,32 +137,32 @@ end
128137return {
129138 complete = {
130139 insert = ' <Tab>' ,
131- callback = function (overlay , diff , chat )
140+ callback = function ()
132141 copilot .trigger_complete (true )
133142 end ,
134143 },
135144
136145 close = {
137146 normal = ' q' ,
138147 insert = ' <C-c>' ,
139- callback = function (overlay , diff , chat )
148+ callback = function ()
140149 copilot .close ()
141150 end ,
142151 },
143152
144153 reset = {
145154 normal = ' <C-l>' ,
146155 insert = ' <C-l>' ,
147- callback = function (overlay , diff , chat )
156+ callback = function ()
148157 copilot .reset ()
149158 end ,
150159 },
151160
152161 submit_prompt = {
153162 normal = ' <CR>' ,
154163 insert = ' <C-s>' ,
155- callback = function (overlay , diff , chat )
156- local section = chat :get_closest_section ()
164+ callback = function ()
165+ local section = copilot . chat :get_closest_section ()
157166 if not section or section .answer then
158167 return
159168 end
@@ -164,8 +173,8 @@ return {
164173
165174 toggle_sticky = {
166175 normal = ' gr' ,
167- callback = function (overlay , diff , chat )
168- local section = chat :get_closest_section ()
176+ callback = function ()
177+ local section = copilot . chat :get_closest_section ()
169178 if not section or section .answer then
170179 return
171180 end
@@ -177,7 +186,7 @@ return {
177186
178187 local cursor = vim .api .nvim_win_get_cursor (0 )
179188 local cur_line = cursor [1 ]
180- vim .api .nvim_buf_set_lines (chat .bufnr , cur_line - 1 , cur_line , false , {})
189+ vim .api .nvim_buf_set_lines (copilot . chat .bufnr , cur_line - 1 , cur_line , false , {})
181190
182191 if vim .startswith (current_line , ' > ' ) then
183192 return
@@ -204,16 +213,22 @@ return {
204213
205214 insert_line = section .start_line + insert_line - 1
206215 local to_insert = first_one and { ' > ' .. current_line , ' ' } or { ' > ' .. current_line }
207- vim .api .nvim_buf_set_lines (chat .bufnr , insert_line - 1 , insert_line - 1 , false , to_insert )
216+ vim .api .nvim_buf_set_lines (
217+ copilot .chat .bufnr ,
218+ insert_line - 1 ,
219+ insert_line - 1 ,
220+ false ,
221+ to_insert
222+ )
208223 vim .api .nvim_win_set_cursor (0 , cursor )
209224 end ,
210225 },
211226
212227 accept_diff = {
213228 normal = ' <C-y>' ,
214229 insert = ' <C-y>' ,
215- callback = function (overlay , diff , chat , source )
216- local diff_data = get_diff (chat :get_closest_block ())
230+ callback = function (source )
231+ local diff_data = get_diff (copilot . chat :get_closest_block ())
217232 diff_data = prepare_diff_buffer (diff_data , source )
218233 if diff_data then
219234 local lines = vim .split (diff_data .change , ' \n ' , { trimempty = false })
@@ -235,8 +250,8 @@ return {
235250
236251 jump_to_diff = {
237252 normal = ' gj' ,
238- callback = function (overlay , diff , chat , source )
239- local diff_data = get_diff (chat :get_closest_block ())
253+ callback = function (source )
254+ local diff_data = get_diff (copilot . chat :get_closest_block ())
240255 diff_data = prepare_diff_buffer (diff_data , source )
241256 if diff_data then
242257 copilot .set_selection (diff_data .bufnr , diff_data .start_line , diff_data .end_line )
@@ -246,28 +261,18 @@ return {
246261
247262 quickfix_answers = {
248263 normal = ' gqa' ,
249- callback = function (overlay , diff , chat )
264+ callback = function ()
250265 local items = {}
251- for i , section in ipairs (chat .sections ) do
266+ for i , section in ipairs (copilot . chat .sections ) do
252267 if section .answer then
253- local prev_section = chat .sections [i - 1 ]
268+ local prev_section = copilot . chat .sections [i - 1 ]
254269 local text = ' '
255270 if prev_section then
256- text = vim .trim (
257- table.concat (
258- vim .api .nvim_buf_get_lines (
259- chat .bufnr ,
260- prev_section .start_line - 1 ,
261- prev_section .end_line ,
262- false
263- ),
264- ' '
265- )
266- )
271+ text = prev_section .content
267272 end
268273
269274 table.insert (items , {
270- bufnr = chat .bufnr ,
275+ bufnr = copilot . chat .bufnr ,
271276 lnum = section .start_line ,
272277 end_lnum = section .end_line ,
273278 text = text ,
@@ -282,11 +287,11 @@ return {
282287
283288 quickfix_diffs = {
284289 normal = ' gqd' ,
285- callback = function (overlay , diff , chat )
290+ callback = function ()
286291 local selection = copilot .get_selection ()
287292 local items = {}
288293
289- for _ , section in ipairs (chat .sections ) do
294+ for _ , section in ipairs (copilot . chat .sections ) do
290295 for _ , block in ipairs (section .blocks ) do
291296 local header = block .header
292297
@@ -302,7 +307,7 @@ return {
302307 end
303308
304309 table.insert (items , {
305- bufnr = chat .bufnr ,
310+ bufnr = copilot . chat .bufnr ,
306311 lnum = block .start_line ,
307312 end_lnum = block .end_line ,
308313 text = text ,
@@ -318,8 +323,8 @@ return {
318323 yank_diff = {
319324 normal = ' gy' ,
320325 register = ' "' , -- Default register to use for yanking
321- callback = function (overlay , diff , chat )
322- local block = chat :get_closest_block ()
326+ callback = function ()
327+ local block = copilot . chat :get_closest_block ()
323328 if not block then
324329 return
325330 end
@@ -331,20 +336,74 @@ return {
331336 show_diff = {
332337 normal = ' gd' ,
333338 full_diff = false , -- Show full diff instead of unified diff when showing diff window
334- callback = function (overlay , diff , chat )
335- local content = get_diff (chat :get_closest_block ())
336- if not content then
339+ callback = function ()
340+ local diff = get_diff (copilot . chat :get_closest_block ())
341+ if not diff then
337342 return
338343 end
339344
340- diff :show (content , chat .winnr , copilot .config .mappings .show_diff .full_diff )
345+ local opts = {
346+ filetype = diff .filetype ,
347+ syntax = ' diff' ,
348+ }
349+
350+ if copilot .config .mappings .show_diff .full_diff then
351+ -- Create modified version by applying the change
352+ local modified = {}
353+ if utils .buf_valid (diff .bufnr ) then
354+ modified = vim .api .nvim_buf_get_lines (diff .bufnr , 0 , - 1 , false )
355+ end
356+ local change_lines = vim .split (diff .change , ' \n ' )
357+
358+ -- Replace the lines in the modified content
359+ if # modified > 0 then
360+ local start_idx = diff .start_line - 1
361+ local end_idx = diff .end_line - 1
362+ for _ = start_idx , end_idx do
363+ table.remove (modified , start_idx )
364+ end
365+ for i , line in ipairs (change_lines ) do
366+ table.insert (modified , start_idx + i - 1 , line )
367+ end
368+ else
369+ modified = change_lines
370+ end
371+
372+ opts .text = table.concat (modified , ' \n ' )
373+
374+ opts .on_show = function ()
375+ vim .cmd (' diffthis' )
376+ vim .api .nvim_set_current_win (vim .fn .bufwinid (diff .bufnr ))
377+ vim .api .nvim_win_set_cursor (0 , { diff .start_line , 0 })
378+ vim .cmd (' diffthis' )
379+ vim .api .nvim_set_current_win (copilot .chat .winnr )
380+ vim .api .nvim_win_set_cursor (copilot .chat .winnr , { diff .start_line , 0 })
381+ end
382+
383+ opts .on_hide = function ()
384+ vim .cmd (' diffoff' )
385+ end
386+ else
387+ opts .text = tostring (vim .diff (diff .reference , diff .change , {
388+ result_type = ' unified' ,
389+ ignore_blank_lines = true ,
390+ ignore_whitespace = true ,
391+ ignore_whitespace_change = true ,
392+ ignore_whitespace_change_at_eol = true ,
393+ ignore_cr_at_eol = true ,
394+ algorithm = ' myers' ,
395+ ctxlen = # diff .reference ,
396+ }))
397+ end
398+
399+ copilot .chat :show_overlay (opts )
341400 end ,
342401 },
343402
344403 show_info = {
345404 normal = ' gi' ,
346- callback = function (overlay , diff , chat )
347- local section = chat :get_closest_section ()
405+ callback = function ()
406+ local section = copilot . chat :get_closest_section ()
348407 if not section or section .answer then
349408 return
350409 end
@@ -383,15 +442,17 @@ return {
383442 table.insert (lines , ' ' )
384443 end
385444
386- overlay :show (vim .trim (table.concat (lines , ' \n ' )) .. ' \n ' , chat .winnr , ' markdown' )
445+ copilot .chat :show_overlay ({
446+ text = vim .trim (table.concat (lines , ' \n ' )) .. ' \n ' ,
447+ })
387448 end )
388449 end ,
389450 },
390451
391452 show_context = {
392453 normal = ' gc' ,
393- callback = function (overlay , diff , chat )
394- local section = chat :get_closest_section ()
454+ callback = function ()
455+ local section = copilot . chat :get_closest_section ()
395456 if not section or section .answer then
396457 return
397458 end
@@ -430,14 +491,16 @@ return {
430491 end
431492
432493 utils .schedule_main ()
433- overlay :show (vim .trim (table.concat (lines , ' \n ' )) .. ' \n ' , chat .winnr , ' markdown' )
494+ copilot .chat :show_overlay ({
495+ text = vim .trim (table.concat (lines , ' \n ' )) .. ' \n ' ,
496+ })
434497 end )
435498 end ,
436499 },
437500
438501 show_help = {
439502 normal = ' gh' ,
440- callback = function (overlay , diff , chat )
503+ callback = function ()
441504 local chat_help = ' **`Special tokens`**\n '
442505 chat_help = chat_help .. ' `@<agent>` to select an agent\n '
443506 chat_help = chat_help .. ' `#<context>` to select a context\n '
@@ -463,7 +526,10 @@ return {
463526 end
464527 end
465528 end
466- overlay :show (chat_help , chat .winnr , ' markdown' )
529+
530+ copilot .chat :show_overlay ({
531+ text = chat_help ,
532+ })
467533 end ,
468534 },
469535}
0 commit comments