@@ -6,6 +6,8 @@ local util = require("copilot.util")
66
77local mod = {}
88
9+ --- @alias copilot_suggestion_context { first ?: integer , cycling ?: integer , cycling_callbacks ?: (fun ( ctx : copilot_suggestion_context ): nil )[], params ?: table , suggestions ?: copilot_get_completions_data_completion[] , choice ?: integer }
10+
911local copilot = {
1012 setup_done = false ,
1113
@@ -14,15 +16,7 @@ local copilot = {
1416 extmark_id = 1 ,
1517
1618 _copilot_timer = nil ,
17- _copilot = {
18- first = nil ,
19- cycling = nil ,
20- cycling_callbacks = nil ,
21- params = nil ,
22- --- @type copilot_get_completions_data_completion[] | nil
23- suggestions = nil ,
24- choice = nil ,
25- },
19+ context = {},
2620
2721 auto_trigger = false ,
2822 debounce = 75 ,
@@ -46,15 +40,26 @@ local function should_auto_trigger()
4640 return vim .b .copilot_suggestion_auto_trigger
4741end
4842
49- local function reset_state ()
50- copilot ._copilot = {
51- first = nil ,
52- cycling = nil ,
53- cycling_callbacks = nil ,
54- params = nil ,
55- suggestions = nil ,
56- choice = nil ,
57- }
43+ --- @param bufnr ? integer
44+ --- @return copilot_suggestion_context
45+ local function get_ctx (bufnr )
46+ bufnr = bufnr or vim .api .nvim_get_current_buf ()
47+ local ctx = copilot .context [bufnr ]
48+ if not ctx then
49+ ctx = {}
50+ copilot .context [bufnr ] = ctx
51+ end
52+ return ctx
53+ end
54+
55+ --- @param ctx copilot_suggestion_context
56+ local function reset_ctx (ctx )
57+ ctx .first = nil
58+ ctx .cycling = nil
59+ ctx .cycling_callbacks = nil
60+ ctx .params = nil
61+ ctx .suggestions = nil
62+ ctx .choice = nil
5863end
5964
6065local function set_keymap (keymap )
@@ -153,15 +158,18 @@ local function reject(bufnr)
153158 end
154159end
155160
156- local function cancel_inflight_requests ()
161+ --- @param ctx ? copilot_suggestion_context
162+ local function cancel_inflight_requests (ctx )
163+ ctx = ctx or get_ctx ()
164+
157165 with_client (function (client )
158- if copilot . _copilot .first then
159- client .cancel_request (copilot . _copilot .first )
160- copilot . _copilot .first = nil
166+ if ctx .first then
167+ client .cancel_request (ctx .first )
168+ ctx .first = nil
161169 end
162- if copilot . _copilot .cycling then
163- client .cancel_request (copilot . _copilot .cycling )
164- copilot . _copilot .cycling = nil
170+ if ctx .cycling then
171+ client .cancel_request (ctx .cycling )
172+ ctx .cycling = nil
165173 end
166174 end )
167175end
@@ -170,20 +178,23 @@ local function clear_preview()
170178 vim .api .nvim_buf_del_extmark (0 , copilot .ns_id , copilot .extmark_id )
171179end
172180
181+ --- @param ctx ? copilot_suggestion_context
173182--- @return copilot_get_completions_data_completion | nil
174- local function get_current_suggestion ()
183+ local function get_current_suggestion (ctx )
184+ ctx = ctx or get_ctx ()
185+
175186 local ok , choice = pcall (function ()
176187 if
177188 not vim .fn .mode ():match (" ^[iR]" )
178189 or vim .fn .pumvisible () == 1
179190 or vim .b .copilot_suggestion_hidden
180- or not copilot . _copilot .suggestions
181- or # copilot . _copilot .suggestions == 0
191+ or not ctx .suggestions
192+ or # ctx .suggestions == 0
182193 then
183194 return nil
184195 end
185196
186- local choice = copilot . _copilot . suggestions [copilot . _copilot .choice ]
197+ local choice = ctx . suggestions [ctx .choice ]
187198 if not choice or not choice .range or choice .range .start .line ~= vim .fn .line (" ." ) - 1 then
188199 return nil
189200 end
@@ -203,8 +214,11 @@ local function get_current_suggestion()
203214 return nil
204215end
205216
206- local function update_preview ()
207- local suggestion = get_current_suggestion ()
217+ --- @param ctx ? copilot_suggestion_context
218+ local function update_preview (ctx )
219+ ctx = ctx or get_ctx ()
220+
221+ local suggestion = get_current_suggestion (ctx )
208222 local displayLines = suggestion and vim .split (suggestion .displayText , " \n " , { plain = true }) or {}
209223
210224 clear_preview ()
@@ -216,10 +230,10 @@ local function update_preview()
216230 --- @todo support popup preview
217231
218232 local annot = " "
219- if copilot . _copilot .cycling_callbacks then
233+ if ctx .cycling_callbacks then
220234 annot = " (1/…)"
221- elseif copilot . _copilot .cycling then
222- annot = " (" .. copilot . _copilot . choice .. " /" .. # copilot . _copilot .suggestions .. " )"
235+ elseif ctx .cycling then
236+ annot = " (" .. ctx . choice .. " /" .. # ctx .suggestions .. " )"
223237 end
224238
225239 local cursor_col = vim .fn .col (" ." )
@@ -259,23 +273,27 @@ local function update_preview()
259273 end
260274end
261275
262- local function clear ()
276+ --- @param ctx ? copilot_suggestion_context
277+ local function clear (ctx )
278+ ctx = ctx or get_ctx ()
263279 stop_timer ()
264- cancel_inflight_requests ()
265- update_preview ()
266- reset_state ( )
280+ cancel_inflight_requests (ctx )
281+ update_preview (ctx )
282+ reset_ctx ( ctx )
267283end
268284
269285--- @param callback fun ( err : any | nil , data : copilot_get_completions_data ): nil
270286local function complete (callback )
271287 stop_timer ()
272288
289+ local ctx = get_ctx ()
273290 local params = util .get_doc_params ()
274291
275- if not vim .deep_equal (copilot . _copilot .params , params ) then
292+ if not vim .deep_equal (ctx .params , params ) then
276293 with_client (function (client )
277294 local _ , id = api .get_completions (client , params , callback )
278- copilot ._copilot = { params = params , first = id }
295+ ctx .params = params
296+ ctx .first = id --[[ @as integer]]
279297 end )
280298 end
281299end
@@ -285,8 +303,9 @@ local function handle_trigger_request(err, data)
285303 if err then
286304 print (err )
287305 end
288- copilot ._copilot .suggestions = data and data .completions or {}
289- copilot ._copilot .choice = 1
306+ local ctx = get_ctx ()
307+ ctx .suggestions = data and data .completions or {}
308+ ctx .choice = 1
290309 update_preview ()
291310end
292311
@@ -301,71 +320,74 @@ local function trigger(bufnr, timer)
301320 complete (handle_trigger_request )
302321end
303322
304- local function get_suggestions_cycling_callback (state , err , data )
305- local callbacks = state .cycling_callbacks
306- state .cycling_callbacks = nil
323+ --- @param ctx copilot_suggestion_context
324+ local function get_suggestions_cycling_callback (ctx , err , data )
325+ local callbacks = ctx .cycling_callbacks or {}
326+ ctx .cycling_callbacks = nil
307327
308328 if err then
309329 print (err )
310330 return
311331 end
312332
313- if not state .suggestions then
333+ if not ctx .suggestions then
314334 return
315335 end
316336
317337 local seen = {}
318338
319- for _ , suggestion in ipairs (state .suggestions ) do
339+ for _ , suggestion in ipairs (ctx .suggestions ) do
320340 seen [suggestion .text ] = true
321341 end
322342
323343 for _ , suggestion in ipairs (data .completions or {}) do
324344 if not seen [suggestion .text ] then
325- table.insert (state .suggestions , suggestion )
345+ table.insert (ctx .suggestions , suggestion )
326346 seen [suggestion .text ] = true
327347 end
328348 end
329349
330350 for _ , callback in ipairs (callbacks ) do
331- callback (state )
351+ callback (ctx )
332352 end
333353end
334354
335- local function get_suggestions_cycling (callback )
336- if copilot ._copilot .cycling_callbacks then
337- table.insert (copilot ._copilot .cycling_callbacks , callback )
355+ --- @param callback fun ( ctx : copilot_suggestion_context ): nil
356+ --- @param ctx copilot_suggestion_context
357+ local function get_suggestions_cycling (callback , ctx )
358+ if ctx .cycling_callbacks then
359+ table.insert (ctx .cycling_callbacks , callback )
338360 return
339361 end
340362
341- if copilot . _copilot .cycling then
342- callback (copilot . _copilot )
363+ if ctx .cycling then
364+ callback (ctx )
343365 return
344366 end
345367
346- if copilot . _copilot .suggestions then
347- copilot . _copilot .cycling_callbacks = { callback }
368+ if ctx .suggestions then
369+ ctx .cycling_callbacks = { callback }
348370 with_client (function (client )
349- local _ , id = api .get_completions_cycling (client , copilot . _copilot .params , function (err , data )
350- get_suggestions_cycling_callback (copilot . _copilot , err , data )
371+ local _ , id = api .get_completions_cycling (client , ctx .params , function (err , data )
372+ get_suggestions_cycling_callback (ctx , err , data )
351373 end )
352- copilot . _copilot . cycling = id
353- update_preview ()
374+ ctx . cycling = id --[[ @as integer ]]
375+ update_preview (ctx )
354376 end )
355377 end
356378end
357379
358- local function advance (count , state )
359- if state ~= copilot . _copilot then
380+ local function advance (count , ctx )
381+ if ctx ~= get_ctx () then
360382 return
361383 end
362384
363- state .choice = (state .choice + count ) % # state .suggestions
364- if state .choice < 1 then
365- state .choice = # state .suggestions
385+ ctx .choice = (ctx .choice + count ) % # ctx .suggestions
386+ if ctx .choice < 1 then
387+ ctx .choice = # ctx .suggestions
366388 end
367389
368- update_preview ()
390+ update_preview (ctx )
369391end
370392
371393local function schedule ()
@@ -382,38 +404,44 @@ local function schedule()
382404end
383405
384406function mod .next ()
407+ local ctx = get_ctx ()
408+
385409 -- no suggestion request yet
386- if not copilot . _copilot .first then
410+ if not ctx .first then
387411 schedule ()
388412 return
389413 end
390414
391- get_suggestions_cycling (function (state )
392- advance (1 , state )
393- end )
415+ get_suggestions_cycling (function (context )
416+ advance (1 , context )
417+ end , ctx )
394418end
395419
396420function mod .prev ()
421+ local ctx = get_ctx ()
422+
397423 -- no suggestion request yet
398- if not copilot . _copilot .first then
424+ if not ctx .first then
399425 schedule ()
400426 return
401427 end
402428
403- get_suggestions_cycling (function (state )
404- advance (- 1 , state )
405- end )
429+ get_suggestions_cycling (function (context )
430+ advance (- 1 , context )
431+ end , ctx )
406432end
407433
408434--- @param modifier ? (fun ( suggestion : copilot_get_completions_data_completion ): copilot_get_completions_data_completion )
409435function mod .accept (modifier )
410- local suggestion = get_current_suggestion ()
436+ local ctx = get_ctx ()
437+
438+ local suggestion = get_current_suggestion (ctx )
411439 if not suggestion or vim .fn .empty (suggestion .text ) == 1 then
412440 return
413441 end
414442
415- cancel_inflight_requests ()
416- reset_state ( )
443+ cancel_inflight_requests (ctx )
444+ reset_ctx ( ctx )
417445
418446 vim .api .nvim_buf_set_var (0 , " _copilot_uuid" , " " )
419447 with_client (function (client )
@@ -477,9 +505,10 @@ function mod.accept_line()
477505end
478506
479507function mod .dismiss ()
508+ local ctx = get_ctx ()
480509 reject (0 )
481- clear ()
482- update_preview ()
510+ clear (ctx )
511+ update_preview (ctx )
483512end
484513
485514function mod .is_visible ()
@@ -514,7 +543,8 @@ local function on_buf_enter()
514543end
515544
516545local function on_cursor_moved_i ()
517- if copilot ._copilot_timer or copilot ._copilot .params or should_auto_trigger () then
546+ local ctx = get_ctx ()
547+ if copilot ._copilot_timer or ctx .params or should_auto_trigger () then
518548 schedule ()
519549 end
520550end
526556--- @param info { buf : integer }
527557local function on_buf_unload (info )
528558 reject (info .buf )
559+ copilot .context [info .buf ] = nil
529560end
530561
531562local function on_vim_leave_pre ()
0 commit comments