@@ -138,15 +138,20 @@ function! copilot#Clear() abort
138138 if exists (' g:_copilot_timer' )
139139 call timer_stop (remove (g: , ' _copilot_timer' ))
140140 endif
141- if exists (' g:_copilot_completion' )
142- call copilot#agent#Cancel (remove (g: , ' _copilot_completion' ))
141+ if exists (' s:uuid' )
142+ call copilot#Request (' notifyRejected' , {' uuids' : [remove (s: , ' uuid' )]})
143+ endif
144+ if exists (' b:_copilot' )
145+ call copilot#agent#Cancel (get (b: _copilot , ' first' , {}))
146+ call copilot#agent#Cancel (get (b: _copilot , ' cycling' , {}))
147+ unlet b: _copilot
143148 endif
144149 call s: UpdatePreview ()
145150 return ' '
146151endfunction
147152
148153function ! copilot#Dismiss () abort
149- unlet ! b: _copilot_suggestion b: _copilot_completion
154+ unlet ! b: _copilot
150155 call copilot#Clear ()
151156 return ' '
152157endfunction
@@ -205,12 +210,12 @@ function! copilot#Complete(...) abort
205210 call timer_stop (remove (g: , ' _copilot_timer' ))
206211 endif
207212 let doc = copilot#doc#Get ()
208- if ! exists (' g:_copilot_completion.params. doc' ) || g: _copilot_completion .params .doc !=# doc
209- let g: _copilot_completion =
210- \ copilot#agent#Request (' getCompletions' , {' doc' : doc, ' options' : {}})
211- let g: _copilot_last_completion = g: _copilot_completion
213+ if ! exists (' b:_copilot. doc' ) || b: _copilot .doc !=# doc
214+ let b: _copilot = { ' doc ' : doc, ' first ' :
215+ \ copilot#agent#Request (' getCompletions' , {' doc' : doc, ' options' : {}})}
216+ let g: _copilot_last = b: _copilot
212217 endif
213- let completion = g: _copilot_completion
218+ let completion = b: _copilot . first
214219 if ! a: 0
215220 return completion.Await ()
216221 else
@@ -223,38 +228,96 @@ endfunction
223228
224229function ! s: SuggestionTextWithAdjustments () abort
225230 try
226- if mode () !~# ' ^[iR]' || pumvisible () || ! s: dest
227- return [' ' , 0 , 0 ]
231+ if mode () !~# ' ^[iR]' || pumvisible () || ! s: dest || ! exists ( ' b:_copilot.suggestions ' )
232+ return [' ' , 0 , 0 , ' ' ]
228233 endif
229- let choice = get (b: , ' _copilot_suggestion ' , {})
234+ let choice = get (b: _copilot .suggestions, b: _copilot .choice , {})
230235 if ! has_key (choice, ' range' ) || choice.range .start .line != line (' .' ) - 1
231- return [' ' , 0 , 0 ]
236+ return [' ' , 0 , 0 , ' ' ]
232237 endif
233238 let line = getline (' .' )
234239 let offset = col (' .' ) - 1
235240 if choice.range .start .character != 0
236241 call copilot#logger#Warn (' unexpected range ' . json_encode (choice.range ))
237- return [' ' , 0 , 0 ]
242+ return [' ' , 0 , 0 , ' ' ]
238243 endif
239244 let typed = strpart (line , 0 , offset)
240245 let delete = strchars (strpart (line , offset))
246+ let uuid = get (choice, ' uuid' , ' ' )
241247 if typed == # strpart (choice.text, 0 , offset)
242- return [strpart (choice.text, offset), 0 , delete ]
248+ return [strpart (choice.text, offset), 0 , delete , uuid ]
243249 elseif typed = ~# ' ^\s*$'
244250 let leading = matchstr (choice.text, ' ^\s\+' )
245251 if strpart (typed, 0 , len (leading)) == leading
246- return [strpart (choice.text, len (leading)), len (typed) - len (leading), delete ]
252+ return [strpart (choice.text, len (leading)), len (typed) - len (leading), delete , uuid ]
247253 endif
248254 endif
249255 catch
250256 call copilot#logger#Exception ()
251257 endtry
252- return [' ' , 0 , 0 ]
258+ return [' ' , 0 , 0 , ' ' ]
259+ endfunction
260+
261+
262+ function ! s: Advance (count , context, ... ) abort
263+ if a: context isnot # get (b: , ' _copilot' , {})
264+ return
265+ endif
266+ let a: context .choice += a: count
267+ if a: context .choice < 0
268+ let a: context .choice += len (a: context .suggestions)
269+ endif
270+ let a: context .choice % = len (a: context .suggestions)
271+ call s: UpdatePreview ()
272+ endfunction
273+
274+ function ! s: GetSuggestionsCyclingCallback (context, result) abort
275+ let callbacks = remove (a: context , ' cycling_callbacks' )
276+ let seen = {}
277+ for suggestion in a: context .suggestions
278+ let seen[suggestion.text] = 1
279+ endfor
280+ for suggestion in get (a: result , ' completions' , [])
281+ if ! has_key (seen, suggestion.text)
282+ call add (a: context .suggestions, suggestion)
283+ let seen[suggestion.text] = 1
284+ endif
285+ endfor
286+ for Callback in callbacks
287+ call Callback (a: context )
288+ endfor
289+ endfunction
290+
291+ function ! s: GetSuggestionsCycling (callback) abort
292+ if exists (' b:_copilot.cycling_callbacks' )
293+ call add (b: _copilot .cycling_callbacks, a: callback )
294+ elseif exists (' b:_copilot.cycling' )
295+ call a: callback (b: _copilot )
296+ elseif exists (' b:_copilot.suggestions' )
297+ let b: _copilot .cycling_callbacks = [a: callback ]
298+ let b: _copilot .cycling = copilot#agent#Request (' getCompletionsCycling' ,
299+ \ {' doc' : b: _copilot .first .params.doc, ' options' : {}},
300+ \ function (' s:GetSuggestionsCyclingCallback' , [b: _copilot ]),
301+ \ function (' s:GetSuggestionsCyclingCallback' , [b: _copilot ]),
302+ \ )
303+ call s: UpdatePreview ()
304+ endif
305+ return ' '
306+ endfunction
307+
308+ function ! copilot#Next () abort
309+ return s: GetSuggestionsCycling (function (' s:Advance' , [1 ]))
310+ endfunction
311+
312+ function ! copilot#Previous () abort
313+ return s: GetSuggestionsCycling (function (' s:Advance' , [-1 ]))
253314endfunction
254315
255316function ! copilot#GetDisplayedSuggestion () abort
256- let [text, outdent, delete ] = s: SuggestionTextWithAdjustments ()
317+ let [text, outdent, delete , uuid] = s: SuggestionTextWithAdjustments ()
318+
257319 return {
320+ \ ' uuid' : uuid,
258321 \ ' text' : text,
259322 \ ' outdentSize' : outdent,
260323 \ ' deleteSize' : delete }
@@ -311,7 +374,7 @@ endfunction
311374
312375function ! s: UpdatePreview () abort
313376 try
314- let [text, outdent, delete ] = s: SuggestionTextWithAdjustments ()
377+ let [text, outdent, delete , uuid ] = s: SuggestionTextWithAdjustments ()
315378 let text = split (text, " \n " , 1 )
316379 if empty (text[-1 ])
317380 call remove (text, -1 )
@@ -322,26 +385,39 @@ function! s:UpdatePreview() abort
322385 if empty (text) || s: dest >= 0
323386 return s: ClearPreview ()
324387 endif
388+ if exists (' b:_copilot.cycling_callbacks' )
389+ let annot = [[' ' ], [' (1/…)' , ' CopilotAnnotation' ]]
390+ elseif exists (' b:_copilot.cycling' )
391+ let annot = [[' ' ], [' (' . (b: _copilot .choice + 1 ) . ' /' . len (b: _copilot .suggestions) . ' )' , ' CopilotAnnotation' ]]
392+ else
393+ let annot = []
394+ endif
325395 let data = {' id' : 1 }
326396 let data.virt_text_win_col = virtcol (' .' ) - 1
327397 let data.virt_text = [[text[0 ] . repeat (' ' , delete - len (text[0 ])), s: hlgroup ]]
328398 if len (text) > 1
329399 let data.virt_lines = map (text[1 :-1 ], { _, l - > [[l , s: hlgroup ]] })
400+ let data.virt_lines[-1 ] += annot
401+ else
402+ let data.virt_text += annot
330403 endif
331404 call nvim_buf_del_extmark (0 , copilot#NvimNs (), 1 )
332405 call nvim_buf_set_extmark (0 , copilot#NvimNs (), line (' .' )-1 , col (' .' )-1 , data)
406+ if uuid !=# get (s: , ' uuid' , ' ' )
407+ let s: uuid = uuid
408+ call copilot#Request (' notifyShown' , {' uuid' : uuid})
409+ endif
333410 catch
334411 return copilot#logger#Exception ()
335412 endtry
336413endfunction
337414
338415function ! s: HandleTriggerResult (result) abort
339- if exists (' a:result.completions' )
340- let b: _copilot_suggestion = get (a: result .completions, 0 , {})
341- else
342- let b: _copilot_suggestion = {}
416+ if ! exists (' b:_copilot' )
417+ return
343418 endif
344- let b: _copilot_completion = b: _copilot_suggestion
419+ let b: _copilot .suggestions = get (a: result , ' completions' , [])
420+ let b: _copilot .choice = 0
345421 call s: UpdatePreview ()
346422endfunction
347423
@@ -374,7 +450,7 @@ function! copilot#Schedule(...) abort
374450endfunction
375451
376452function ! copilot#OnInsertLeave () abort
377- unlet ! b: _copilot_suggestion b: _copilot_completion
453+ unlet ! b: _copilot
378454 return copilot#Clear ()
379455endfunction
380456
@@ -406,7 +482,9 @@ endfunction
406482function ! copilot#Accept (... ) abort
407483 let s = copilot#GetDisplayedSuggestion ()
408484 if ! empty (s .text)
409- unlet ! b: _copilot_suggestion b: _copilot_completion
485+ unlet ! b: _copilot
486+ call copilot#Request (' notifyAccepted' , {' uuid' : s .uuid})
487+ unlet ! s: uuid
410488 call s: ClearPreview ()
411489 let s: suggestion_text = s .text
412490 return repeat (" \<Left>\<Del> " , s .outdentSize) . repeat (" \<Del> " , s .deleteSize) .
@@ -583,7 +661,7 @@ endfunction
583661function ! s: commands .setup (opts) abort
584662 let network_status = s: NetworkStatusMessage ()
585663 if ! empty (network_status)
586- return ' echoerr ' . string (' Copilot: ' . network_status)
664+ return ' echoerr ' . string (' Copilot: ' . network_status)
587665 endif
588666
589667 let browser = copilot#Browser ()
@@ -743,25 +821,29 @@ endfunction
743821function ! copilot#Command (line1, line2, range , bang , mods, arg) abort
744822 let cmd = matchstr (a: arg , ' ^\%(\\.\|\S\)\+' )
745823 let arg = matchstr (a: arg , ' \s\zs\S.*' )
746- if empty (cmd)
747- if empty (s: OAuthToken ()) || ! s: TermsAccepted (1 )
824+ try
825+ if empty (cmd)
826+ if empty (s: OAuthToken ()) || ! s: TermsAccepted (1 )
827+ let cmd = ' setup'
828+ else
829+ let cmd = ' status'
830+ endif
831+ elseif cmd == # ' auth'
748832 let cmd = ' setup'
833+ elseif cmd == # ' open'
834+ let cmd = ' split'
835+ endif
836+ if ! has_key (s: commands , tr (cmd, ' -' , ' _' ))
837+ return ' echoerr ' . string (' Copilot: unknown command ' . string (cmd))
838+ endif
839+ let opts = {' line1' : a: line1 , ' line2' : a: line2 , ' range' : a: range , ' bang' : a: bang , ' mods' : a: mods , ' arg' : arg}
840+ let retval = s: commands [tr (cmd, ' -' , ' _' )](opts)
841+ if type (retval) == v: t_string
842+ return retval
749843 else
750- let cmd = ' status '
844+ return ' '
751845 endif
752- elseif cmd == # ' auth'
753- let cmd = ' setup'
754- elseif cmd == # ' open'
755- let cmd = ' split'
756- endif
757- if ! has_key (s: commands , tr (cmd, ' -' , ' _' ))
758- return ' echoerr ' . string (' Copilot: unknown command ' . string (cmd))
759- endif
760- let opts = {' line1' : a: line1 , ' line2' : a: line2 , ' range' : a: range , ' bang' : a: bang , ' mods' : a: mods , ' arg' : arg}
761- let retval = s: commands [tr (cmd, ' -' , ' _' )](opts)
762- if type (retval) == v: t_string
763- return retval
764- else
765- return ' '
766- endif
846+ catch /^Copilot:/
847+ return ' echoerr ' . string (v: exception )
848+ endtry
767849endfunction
0 commit comments