Skip to content

Commit 5b19fb0

Browse files
committed
Copilot.vim 1.13.0
1 parent 2c31989 commit 5b19fb0

File tree

7 files changed

+468
-338
lines changed

7 files changed

+468
-338
lines changed

autoload/copilot.vim

+33-9
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,11 @@ function! s:Start() abort
6060
if s:Running()
6161
return
6262
endif
63-
let s:agent = copilot#agent#New({'notifications': {
63+
let s:agent = copilot#agent#New({'methods': {
6464
\ 'statusNotification': function('s:StatusNotification'),
6565
\ 'PanelSolution': function('copilot#panel#Solution'),
6666
\ 'PanelSolutionsDone': function('copilot#panel#SolutionsDone'),
67+
\ 'copilot/openURL': function('s:OpenURL'),
6768
\ },
6869
\ 'editorConfiguration' : s:EditorConfiguration()})
6970
endfunction
@@ -412,10 +413,11 @@ function! copilot#IsMapped() abort
412413
endfunction
413414

414415
function! copilot#Schedule(...) abort
415-
call copilot#Clear()
416416
if !s:has_ghost_text || !copilot#Enabled() || !copilot#IsMapped()
417+
call copilot#Clear()
417418
return
418419
endif
420+
call s:UpdatePreview()
419421
let delay = a:0 ? a:1 : get(g:, 'copilot_idle_delay', 15)
420422
let g:_copilot_timer = timer_start(delay, function('s:Trigger', [bufnr('')]))
421423
endfunction
@@ -502,18 +504,40 @@ endfunction
502504

503505
function! copilot#Browser() abort
504506
if type(get(g:, 'copilot_browser')) == v:t_list
505-
return copy(g:copilot_browser)
506-
elseif type(get(g:, 'browser_command')) == v:t_list
507-
return copy(g:browser_command)
508-
elseif has('win32') && executable('rundll32')
509-
return ['rundll32', 'url.dll,FileProtocolHandler']
510-
elseif isdirectory('/private') && executable('/usr/bin/open')
511-
return ['/usr/bin/open']
507+
let cmd = copy(g:copilot_browser)
508+
elseif type(get(g:, 'open_command')) == v:t_list
509+
let cmd = copy(g:open_command)
510+
elseif has('win32')
511+
let cmd = ['rundll32', 'url.dll,FileProtocolHandler']
512+
elseif has('mac')
513+
let cmd = ['open']
514+
elseif executable('wslview')
515+
return ['wslview']
512516
elseif executable('xdg-open')
513517
return ['xdg-open']
514518
else
515519
return []
516520
endif
521+
if executable(get(cmd, 0, ''))
522+
return cmd
523+
else
524+
return []
525+
endif
526+
endfunction
527+
528+
function! s:OpenURL(params) abort
529+
echo a:params.target
530+
let browser = copilot#Browser()
531+
if empty(browser)
532+
return v:false
533+
endif
534+
let status = {}
535+
call copilot#job#Stream(browser + [a:params.target], v:null, v:null, function('s:BrowserCallback', [status]))
536+
let time = reltime()
537+
while empty(status) && reltimefloat(reltime(time)) < 1
538+
sleep 10m
539+
endwhile
540+
return get(status, 'code') ? v:false : v:true
517541
endfunction
518542

519543
let s:commands = {}

autoload/copilot/agent.vim

+58-36
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ let g:autoloaded_copilot_agent = 1
55

66
scriptencoding utf-8
77

8-
let s:plugin_version = '1.12.1'
8+
let s:plugin_version = '1.13.0'
99

1010
let s:error_exit = -1
1111

@@ -44,14 +44,31 @@ function! s:LogSend(request, line) abort
4444
return '--> ' . a:line
4545
endfunction
4646

47+
function! s:RejectRequest(request, error) abort
48+
if a:request.status ==# 'canceled'
49+
return
50+
endif
51+
let a:request.waiting = {}
52+
call remove(a:request, 'resolve')
53+
let reject = remove(a:request, 'reject')
54+
let a:request.status = 'error'
55+
let a:request.error = a:error
56+
for Cb in reject
57+
let a:request.waiting[timer_start(0, function('s:Callback', [a:request, 'error', Cb]))] = 1
58+
endfor
59+
endfunction
60+
4761
function! s:Send(agent, request) abort
48-
call ch_sendexpr(a:agent.job, a:request)
49-
return a:request
62+
try
63+
call ch_sendexpr(a:agent.job, a:request)
64+
return v:true
65+
catch /^Vim\%((\a\+)\)\=:E631:/
66+
return v:false
67+
endtry
5068
endfunction
5169

5270
function! s:AgentNotify(method, params) dict abort
53-
call s:Send(self, {'method': a:method, 'params': a:params})
54-
return v:true
71+
return s:Send(self, {'method': a:method, 'params': a:params})
5572
endfunction
5673

5774
function! s:RequestWait() dict abort
@@ -138,12 +155,22 @@ function! s:BufferText(bufnr) abort
138155
return join(getbufline(a:bufnr, 1, '$'), "\n") . "\n"
139156
endfunction
140157

158+
function! s:LogMessage(params) abort
159+
call copilot#logger#Raw(get(a:params, 'level', 3), get(a:params, 'message', ''))
160+
endfunction
161+
141162
function! s:ShowMessageRequest(params) abort
142163
let choice = inputlist([a:params.message . "\n\nRequest Actions:"] +
143164
\ map(copy(get(a:params, 'actions', [])), { i, v -> (i + 1) . '. ' . v.title}))
144165
return choice > 0 ? get(a:params.actions, choice - 1, v:null) : v:null
145166
endfunction
146167

168+
function! s:SendRequest(agent, request) abort
169+
if empty(s:Send(a:agent, a:request)) && has_key(a:agent.requests, a:request.id)
170+
call s:RejectRequest(remove(a:agent.requests, a:request.id), {'code': 257, 'message': 'Write failed'})
171+
endif
172+
endfunction
173+
147174
function! s:AgentRequest(method, params, ...) dict abort
148175
let s:id += 1
149176
let request = {'method': a:method, 'params': deepcopy(a:params), 'id': s:id}
@@ -173,15 +200,15 @@ function! s:AgentRequest(method, params, ...) dict abort
173200
else
174201
let vtd_id = {
175202
\ 'uri': doc.uri,
176-
\ 'version': getbufvar(bufnr, 'changedtick')}
203+
\ 'version': doc_version}
177204
call self.Notify('textDocument/didChange', {
178205
\ 'textDocument': vtd_id,
179206
\ 'contentChanges': [{'text': s:BufferText(bufnr)}]})
180-
let self.open_buffers[bufnr].version = version
207+
let self.open_buffers[bufnr].version = doc_version
181208
endif
182209
let doc.version = doc_version
183210
endfor
184-
call timer_start(0, { _ -> s:Send(self, request) })
211+
call timer_start(0, { _ -> s:SendRequest(self, request) })
185212
return call('s:SetUpRequest', [self, s:id, a:method, a:params] + a:000)
186213
endfunction
187214

@@ -216,13 +243,17 @@ endfunction
216243
function! s:DispatchMessage(agent, handler, id, params, ...) abort
217244
try
218245
let response = {'result': call(a:handler, [a:params])}
246+
if response.result is# 0
247+
let response.result = v:null
248+
endif
219249
catch
220250
call copilot#logger#Exception()
221251
let response = {'error': {'code': -32000, 'message': v:exception}}
222252
endtry
223253
if !empty(a:id)
224254
call s:Send(a:agent, extend({'id': a:id}, response))
225255
endif
256+
return response
226257
endfunction
227258

228259
function! s:OnMessage(agent, body, ...) abort
@@ -232,16 +263,10 @@ function! s:OnMessage(agent, body, ...) abort
232263
let request = a:body
233264
let id = get(request, 'id', v:null)
234265
let params = get(request, 'params', v:null)
235-
if empty(id)
236-
if has_key(a:agent.notifications, request.method)
237-
call timer_start(0, { _ -> a:agent.notifications[request.method](params) })
238-
elseif request.method ==# 'LogMessage'
239-
call copilot#logger#Raw(get(params, 'level', 3), get(params, 'message', ''))
240-
endif
241-
elseif has_key(a:agent.methods, request.method)
242-
call timer_start(0, function('s:DispatchMessage', [a:agent, a:agent.methods[request.method], id, params]))
243-
else
244-
return s:Send(a:agent, {"id": id, "error": {"code": -32700, "message": "Method not found: " . request.method}})
266+
if has_key(a:agent.methods, request.method)
267+
return s:DispatchMessage(a:agent, a:agent.methods[request.method], id, params)
268+
elseif !empty(id)
269+
call s:Send(a:agent, {"id": id, "error": {"code": -32700, "message": "Method not found: " . request.method}})
245270
endif
246271
endfunction
247272

@@ -285,20 +310,9 @@ function! s:OnExit(agent, code, ...) abort
285310
if has_key(a:agent, 'client_id')
286311
call remove(a:agent, 'client_id')
287312
endif
313+
let code = a:code < 0 || a:code > 255 ? 256 : a:code
288314
for id in sort(keys(a:agent.requests), { a, b -> +a > +b })
289-
let request = remove(a:agent.requests, id)
290-
if request.status ==# 'canceled'
291-
return
292-
endif
293-
let request.waiting = {}
294-
call remove(request, 'resolve')
295-
let reject = remove(request, 'reject')
296-
let request.status = 'error'
297-
let code = a:code < 0 || a:code > 255 ? 256 : a:code
298-
let request.error = {'code': code, 'message': 'Agent exited', 'data': {'status': a:code}}
299-
for Cb in reject
300-
let request.waiting[timer_start(0, function('s:Callback', [request, 'error', Cb]))] = 1
301-
endfor
315+
call s:RejectRequest(remove(a:agent.requests, id), {'code': code, 'message': 'Agent exited', 'data': {'status': a:code}})
302316
endfor
303317
call timer_start(0, { _ -> get(s:instances, a:agent.id) is# a:agent ? remove(s:instances, a:agent.id) : {} })
304318
call copilot#logger#Info('agent exited with status ' . a:code)
@@ -353,7 +367,7 @@ function! copilot#agent#LspHandle(agent_id, request) abort
353367
if !has_key(s:instances, a:agent_id)
354368
return
355369
endif
356-
call s:OnMessage(s:instances[a:agent_id], a:request)
370+
return s:OnMessage(s:instances[a:agent_id], a:request)
357371
endfunction
358372

359373
function! s:GetNodeVersion(command) abort
@@ -484,8 +498,6 @@ endfunction
484498
function! copilot#agent#New(...) abort
485499
let opts = a:0 ? a:1 : {}
486500
let instance = {'requests': {},
487-
\ 'methods': get(opts, 'methods', {}),
488-
\ 'notifications': get(opts, 'notifications', {}),
489501
\ 'editorConfiguration': get(opts, 'editorConfiguration', {}),
490502
\ 'Close': function('s:AgentClose'),
491503
\ 'Notify': function('s:AgentNotify'),
@@ -494,6 +506,10 @@ function! copilot#agent#New(...) abort
494506
\ 'Cancel': function('s:AgentCancel'),
495507
\ 'StartupError': function('s:AgentStartupError'),
496508
\ }
509+
let instance.methods = extend({
510+
\ 'LogMessage': function('s:LogMessage'),
511+
\ 'window/logMessage': function('s:LogMessage'),
512+
\ }, get(opts, 'methods', {}))
497513
let [command, node_version, command_error] = s:Command()
498514
if len(command_error)
499515
if empty(command)
@@ -510,7 +526,7 @@ function! copilot#agent#New(...) abort
510526
\ 'Close': function('s:LspClose'),
511527
\ 'Notify': function('s:LspNotify'),
512528
\ 'Request': function('s:LspRequest')})
513-
let instance.client_id = v:lua.require'_copilot'.lsp_start_client(command, keys(instance.notifications) + keys(instance.methods) + ['LogMessage'])
529+
let instance.client_id = v:lua.require'_copilot'.lsp_start_client(command, keys(instance.methods))
514530
let instance.id = instance.client_id
515531
else
516532
let state = {'headers': {}, 'mode': 'headers', 'buffer': ''}
@@ -525,7 +541,13 @@ function! copilot#agent#New(...) abort
525541
\ 'exit_cb': { j, d -> timer_start(0, function('s:OnExit', [instance, d])) },
526542
\ })
527543
let instance.id = exists('*jobpid') ? jobpid(instance.job) : job_info(instance.job).process
528-
let request = instance.Request('initialize', {'capabilities': {'workspace': {'workspaceFolders': v:true}}}, function('s:GetCapabilitiesResult'), function('s:GetCapabilitiesError'), instance)
544+
let capabilities = {'workspace': {'workspaceFolders': v:true}, 'copilot': {}}
545+
for name in keys(instance.methods)
546+
if name =~# '^copilot/'
547+
let capabilities.copilot[matchstr(name, '/\zs.*')] = v:true
548+
endif
549+
endfor
550+
let request = instance.Request('initialize', {'capabilities': capabilities}, function('s:GetCapabilitiesResult'), function('s:GetCapabilitiesError'), instance)
529551
endif
530552
let s:instances[instance.id] = instance
531553
return instance

autoload/copilot/logger.vim

+9-6
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,17 @@ function! copilot#logger#Exception() abort
5555
call copilot#logger#Error('Exception: ' . v:exception . ' @ ' . v:throwpoint)
5656
let agent = copilot#RunningAgent()
5757
if !empty(agent)
58-
if v:throwpoint =~# '[\/]'
59-
let throwpoint = '[redacted]'
60-
else
61-
let throwpoint = v:throwpoint
62-
endif
58+
let stacklines = []
59+
for frame in split(substitute(substitute(v:throwpoint, ', \S\+ \(\d\+\)$', '[\1]', ''), '^function ', '', ''), '\.\@<!\.\.\.\@!')
60+
if frame =~# '[\/]'
61+
call add(stacklines, '[redacted]')
62+
else
63+
call add(stacklines, substitute(frame, '^<SNR>\d\+_', '<SID>', ''))
64+
endif
65+
endfor
6366
call agent.Request('telemetry/exception', {
6467
\ 'origin': 'copilot.vim',
65-
\ 'stacktrace': v:exception . ' @ ' . throwpoint,
68+
\ 'stacktrace': join([v:exception] + stacklines, "\n")
6669
\ })
6770
endif
6871
endif

autoload/copilot/panel.vim

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ if exists('g:autoloaded_copilot_panel')
33
endif
44
let g:autoloaded_copilot_panel = 1
55

6+
scriptencoding utf-8
7+
68
if !exists('s:panel_id')
79
let s:panel_id = 0
810
endif

dist/agent.js

+356-283
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/agent.js.map

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lua/_copilot.lua

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
local copilot = {}
22

33
copilot.lsp_start_client = function(cmd, handler_names)
4+
local capabilities = vim.lsp.protocol.make_client_capabilities()
45
local handlers = {}
56
local id
67
for _, name in ipairs(handler_names) do
78
handlers[name] = function(err, result)
89
if result then
910
local retval = vim.call('copilot#agent#LspHandle', id, {method = name, params = result})
10-
if retval ~= 0 then return retval end
11+
if type(retval) == 'table' then return retval.result, retval.error end
1112
end
1213
end
14+
if name:match('^copilot/') then
15+
capabilities.copilot = capabilities.copilot or {}
16+
capabilities.copilot[name:match('^copilot/(.*)$')] = true
17+
end
1318
end
1419
id = vim.lsp.start_client({
1520
cmd = cmd,
1621
cmd_cwd = vim.call('copilot#job#Cwd'),
1722
name = 'copilot',
23+
capabilities = capabilities,
1824
handlers = handlers,
1925
get_language_id = function(bufnr, filetype)
2026
return vim.call('copilot#doc#LanguageForFileType', filetype)

0 commit comments

Comments
 (0)