Skip to content

Commit 47eb231

Browse files
committed
Copilot.vim 1.1.0
1 parent c013148 commit 47eb231

File tree

7 files changed

+221
-74
lines changed

7 files changed

+221
-74
lines changed

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,4 @@ See `:help copilot` for more information.
4545

4646
## Limitations
4747

48-
Copilot.vim does not yet support cycling through alternate suggestions on
49-
Alt+[ and Alt+], or opening the GitHub Copilot panel on Ctrl+Enter.
48+
Copilot.vim does not yet support opening the GitHub Copilot panel on Ctrl+Enter.

autoload/copilot.vim

Lines changed: 126 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -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 ''
146151
endfunction
147152

148153
function! copilot#Dismiss() abort
149-
unlet! b:_copilot_suggestion b:_copilot_completion
154+
unlet! b:_copilot
150155
call copilot#Clear()
151156
return ''
152157
endfunction
@@ -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

224229
function! 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]))
253314
endfunction
254315

255316
function! 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

312375
function! 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
336413
endfunction
337414

338415
function! 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()
346422
endfunction
347423

@@ -374,7 +450,7 @@ function! copilot#Schedule(...) abort
374450
endfunction
375451

376452
function! copilot#OnInsertLeave() abort
377-
unlet! b:_copilot_suggestion b:_copilot_completion
453+
unlet! b:_copilot
378454
return copilot#Clear()
379455
endfunction
380456

@@ -406,7 +482,9 @@ endfunction
406482
function! 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
583661
function! 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
743821
function! 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
767849
endfunction

0 commit comments

Comments
 (0)