mirror of
https://github.com/amix/vimrc
synced 2025-07-07 08:45:00 +08:00
Updated plugins
This commit is contained in:
@ -1,11 +1,6 @@
|
||||
if exists('g:autoloaded_copilot')
|
||||
finish
|
||||
endif
|
||||
let g:autoloaded_copilot = 1
|
||||
|
||||
scriptencoding utf-8
|
||||
|
||||
let s:has_nvim_ghost_text = has('nvim-0.6') && exists('*nvim_buf_get_mark')
|
||||
let s:has_nvim_ghost_text = has('nvim-0.7') && exists('*nvim_buf_get_mark')
|
||||
let s:vim_minimum_version = '9.0.0185'
|
||||
let s:has_vim_ghost_text = has('patch-' . s:vim_minimum_version) && has('textprop')
|
||||
let s:has_ghost_text = s:has_nvim_ghost_text || s:has_vim_ghost_text
|
||||
@ -39,81 +34,68 @@ function! s:EditorConfiguration() abort
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! s:StatusNotification(params, ...) abort
|
||||
let status = get(a:params, 'status', '')
|
||||
if status ==? 'error'
|
||||
let s:agent_error = a:params.message
|
||||
else
|
||||
unlet! s:agent_error
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#Init(...) abort
|
||||
call timer_start(0, { _ -> s:Start() })
|
||||
call copilot#util#Defer({ -> exists('s:client') || s:Start() })
|
||||
endfunction
|
||||
|
||||
function! s:Running() abort
|
||||
return exists('s:agent.job') || exists('s:agent.client_id')
|
||||
return exists('s:client.job') || exists('s:client.client_id')
|
||||
endfunction
|
||||
|
||||
function! s:Start() abort
|
||||
if s:Running()
|
||||
if s:Running() || exists('s:client.startup_error')
|
||||
return
|
||||
endif
|
||||
let s:agent = copilot#agent#New({'methods': {
|
||||
\ 'statusNotification': function('s:StatusNotification'),
|
||||
\ 'PanelSolution': function('copilot#panel#Solution'),
|
||||
\ 'PanelSolutionsDone': function('copilot#panel#SolutionsDone'),
|
||||
\ 'copilot/openURL': function('s:OpenURL'),
|
||||
\ },
|
||||
\ 'editorConfiguration' : s:EditorConfiguration()})
|
||||
let s:client = copilot#client#New({'editorConfiguration' : s:EditorConfiguration()})
|
||||
endfunction
|
||||
|
||||
function! s:Stop() abort
|
||||
if exists('s:agent')
|
||||
let agent = remove(s:, 'agent')
|
||||
call agent.Close()
|
||||
if exists('s:client')
|
||||
let client = remove(s:, 'client')
|
||||
call client.Close()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#Agent() abort
|
||||
function! copilot#Client() abort
|
||||
call s:Start()
|
||||
return s:agent
|
||||
return s:client
|
||||
endfunction
|
||||
|
||||
function! copilot#RunningAgent() abort
|
||||
function! copilot#RunningClient() abort
|
||||
if s:Running()
|
||||
return s:agent
|
||||
return s:client
|
||||
else
|
||||
return v:null
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:NodeVersionWarning() abort
|
||||
if exists('s:agent.node_version') && s:agent.node_version =~# '^16\.'
|
||||
if has('nvim-0.7') && !has(luaeval('vim.version().api_prerelease') ? 'nvim-0.8.1' : 'nvim-0.8.0')
|
||||
let s:editor_warning = 'Neovim 0.7 support is deprecated and will be dropped in a future release of copilot.vim.'
|
||||
endif
|
||||
if has('vim_starting') && exists('s:editor_warning')
|
||||
call copilot#logger#Warn(s:editor_warning)
|
||||
endif
|
||||
function! s:EditorVersionWarning() abort
|
||||
if exists('s:editor_warning')
|
||||
echohl WarningMsg
|
||||
echo "Warning: Node.js 16 is approaching end of life and support will be dropped in a future release of copilot.vim."
|
||||
echohl NONE
|
||||
elseif exists('s:agent.node_version_warning')
|
||||
echohl WarningMsg
|
||||
echo 'Warning:' s:agent.node_version_warning
|
||||
echohl NONE
|
||||
echo 'Warning: ' . s:editor_warning
|
||||
echohl None
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#Request(method, params, ...) abort
|
||||
let agent = copilot#Agent()
|
||||
return call(agent.Request, [a:method, a:params] + a:000)
|
||||
let client = copilot#Client()
|
||||
return call(client.Request, [a:method, a:params] + a:000)
|
||||
endfunction
|
||||
|
||||
function! copilot#Call(method, params, ...) abort
|
||||
let agent = copilot#Agent()
|
||||
return call(agent.Call, [a:method, a:params] + a:000)
|
||||
let client = copilot#Client()
|
||||
return call(client.Call, [a:method, a:params] + a:000)
|
||||
endfunction
|
||||
|
||||
function! copilot#Notify(method, params, ...) abort
|
||||
let agent = copilot#Agent()
|
||||
return call(agent.Notify, [a:method, a:params] + a:000)
|
||||
let client = copilot#Client()
|
||||
return call(client.Notify, [a:method, a:params] + a:000)
|
||||
endfunction
|
||||
|
||||
function! copilot#NvimNs() abort
|
||||
@ -125,37 +107,21 @@ function! copilot#Clear() abort
|
||||
call timer_stop(remove(g:, '_copilot_timer'))
|
||||
endif
|
||||
if exists('b:_copilot')
|
||||
call copilot#agent#Cancel(get(b:_copilot, 'first', {}))
|
||||
call copilot#agent#Cancel(get(b:_copilot, 'cycling', {}))
|
||||
call copilot#client#Cancel(get(b:_copilot, 'first', {}))
|
||||
call copilot#client#Cancel(get(b:_copilot, 'cycling', {}))
|
||||
endif
|
||||
call s:UpdatePreview()
|
||||
unlet! b:_copilot
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
function! s:Reject(bufnr) abort
|
||||
try
|
||||
let dict = getbufvar(a:bufnr, '_copilot')
|
||||
if type(dict) == v:t_dict && !empty(get(dict, 'shown_choices', {}))
|
||||
call copilot#Request('notifyRejected', {'uuids': keys(dict.shown_choices)})
|
||||
let dict.shown_choices = {}
|
||||
endif
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! copilot#Dismiss() abort
|
||||
call s:Reject('%')
|
||||
call copilot#Clear()
|
||||
call s:UpdatePreview()
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
let s:filetype_defaults = {
|
||||
\ 'yaml': 0,
|
||||
\ 'markdown': 0,
|
||||
\ 'help': 0,
|
||||
\ 'gitcommit': 0,
|
||||
\ 'gitrebase': 0,
|
||||
\ 'hgcommit': 0,
|
||||
@ -192,26 +158,41 @@ endfunction
|
||||
function! copilot#Enabled() abort
|
||||
return get(g:, 'copilot_enabled', 1)
|
||||
\ && empty(s:BufferDisabled())
|
||||
\ && empty(copilot#Agent().StartupError())
|
||||
endfunction
|
||||
|
||||
let s:inline_invoked = 1
|
||||
let s:inline_automatic = 2
|
||||
|
||||
function! copilot#Complete(...) abort
|
||||
if exists('g:_copilot_timer')
|
||||
call timer_stop(remove(g:, '_copilot_timer'))
|
||||
endif
|
||||
let params = copilot#doc#Params()
|
||||
if !exists('b:_copilot.params') || b:_copilot.params !=# params
|
||||
let b:_copilot = {'params': params, 'first':
|
||||
\ copilot#Request('getCompletions', params)}
|
||||
let target = [bufnr(''), getbufvar('', 'changedtick'), line('.'), col('.')]
|
||||
if !exists('b:_copilot.target') || b:_copilot.target !=# target
|
||||
if exists('b:_copilot.first')
|
||||
call copilot#client#Cancel(b:_copilot.first)
|
||||
endif
|
||||
if exists('b:_copilot.cycling')
|
||||
call copilot#client#Cancel(b:_copilot.cycling)
|
||||
endif
|
||||
let params = {
|
||||
\ 'textDocument': {'uri': bufnr('')},
|
||||
\ 'position': copilot#util#AppendPosition(),
|
||||
\ 'formattingOptions': {'insertSpaces': &expandtab ? v:true : v:false, 'tabSize': shiftwidth()},
|
||||
\ 'context': {'triggerKind': s:inline_automatic}}
|
||||
let b:_copilot = {
|
||||
\ 'target': target,
|
||||
\ 'params': params,
|
||||
\ 'first': copilot#Request('textDocument/inlineCompletion', params)}
|
||||
let g:_copilot_last = b:_copilot
|
||||
endif
|
||||
let completion = b:_copilot.first
|
||||
if !a:0
|
||||
return completion.Await()
|
||||
else
|
||||
call copilot#agent#Result(completion, a:1)
|
||||
call copilot#client#Result(completion, function(a:1, [b:_copilot]))
|
||||
if a:0 > 1
|
||||
call copilot#agent#Error(completion, a:2)
|
||||
call copilot#client#Error(completion, function(a:2, [b:_copilot]))
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
@ -221,37 +202,37 @@ function! s:HideDuringCompletion() abort
|
||||
endfunction
|
||||
|
||||
function! s:SuggestionTextWithAdjustments() abort
|
||||
let empty = ['', 0, 0, {}]
|
||||
try
|
||||
if mode() !~# '^[iR]' || (s:HideDuringCompletion() && pumvisible()) || !exists('b:_copilot.suggestions')
|
||||
return ['', 0, 0, '']
|
||||
return empty
|
||||
endif
|
||||
let choice = get(b:_copilot.suggestions, b:_copilot.choice, {})
|
||||
if !has_key(choice, 'range') || choice.range.start.line != line('.') - 1 || type(choice.text) !=# v:t_string
|
||||
return ['', 0, 0, '']
|
||||
if !has_key(choice, 'range') || choice.range.start.line != line('.') - 1 || type(choice.insertText) !=# v:t_string
|
||||
return empty
|
||||
endif
|
||||
let line = getline('.')
|
||||
let offset = col('.') - 1
|
||||
let choice_text = strpart(line, 0, copilot#doc#UTF16ToByteIdx(line, choice.range.start.character)) . choice.text
|
||||
let choice_text = strpart(line, 0, copilot#util#UTF16ToByteIdx(line, choice.range.start.character)) . substitute(choice.insertText, "\n*$", '', '')
|
||||
let typed = strpart(line, 0, offset)
|
||||
let end_offset = copilot#doc#UTF16ToByteIdx(line, choice.range.end.character)
|
||||
let end_offset = copilot#util#UTF16ToByteIdx(line, choice.range.end.character)
|
||||
if end_offset < 0
|
||||
let end_offset = len(line)
|
||||
endif
|
||||
let delete = strpart(line, offset, end_offset - offset)
|
||||
let uuid = get(choice, 'uuid', '')
|
||||
if typed =~# '^\s*$'
|
||||
let leading = matchstr(choice_text, '^\s\+')
|
||||
let unindented = strpart(choice_text, len(leading))
|
||||
if strpart(typed, 0, len(leading)) == leading && unindented !=# delete
|
||||
return [unindented, len(typed) - len(leading), strchars(delete), uuid]
|
||||
return [unindented, len(typed) - len(leading), strchars(delete), choice]
|
||||
endif
|
||||
elseif typed ==# strpart(choice_text, 0, offset)
|
||||
return [strpart(choice_text, offset), 0, strchars(delete), uuid]
|
||||
return [strpart(choice_text, offset), 0, strchars(delete), choice]
|
||||
endif
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
endtry
|
||||
return ['', 0, 0, '']
|
||||
return empty
|
||||
endfunction
|
||||
|
||||
|
||||
@ -271,12 +252,12 @@ function! s:GetSuggestionsCyclingCallback(context, result) abort
|
||||
let callbacks = remove(a:context, 'cycling_callbacks')
|
||||
let seen = {}
|
||||
for suggestion in a:context.suggestions
|
||||
let seen[suggestion.text] = 1
|
||||
let seen[suggestion.insertText] = 1
|
||||
endfor
|
||||
for suggestion in get(a:result, 'completions', [])
|
||||
if !has_key(seen, suggestion.text)
|
||||
for suggestion in get(a:result, 'items', [])
|
||||
if !has_key(seen, suggestion.insertText)
|
||||
call add(a:context.suggestions, suggestion)
|
||||
let seen[suggestion.text] = 1
|
||||
let seen[suggestion.insertText] = 1
|
||||
endif
|
||||
endfor
|
||||
for Callback in callbacks
|
||||
@ -290,9 +271,11 @@ function! s:GetSuggestionsCycling(callback) abort
|
||||
elseif exists('b:_copilot.cycling')
|
||||
call a:callback(b:_copilot)
|
||||
elseif exists('b:_copilot.suggestions')
|
||||
let params = deepcopy(b:_copilot.first.params)
|
||||
let params.context.triggerKind = s:inline_invoked
|
||||
let b:_copilot.cycling_callbacks = [a:callback]
|
||||
let b:_copilot.cycling = copilot#Request('getCompletionsCycling',
|
||||
\ b:_copilot.first.params,
|
||||
let b:_copilot.cycling = copilot#Request('textDocument/inlineCompletion',
|
||||
\ params,
|
||||
\ function('s:GetSuggestionsCyclingCallback', [b:_copilot]),
|
||||
\ function('s:GetSuggestionsCyclingCallback', [b:_copilot]),
|
||||
\ )
|
||||
@ -310,10 +293,10 @@ function! copilot#Previous() abort
|
||||
endfunction
|
||||
|
||||
function! copilot#GetDisplayedSuggestion() abort
|
||||
let [text, outdent, delete, uuid] = s:SuggestionTextWithAdjustments()
|
||||
let [text, outdent, delete, item] = s:SuggestionTextWithAdjustments()
|
||||
|
||||
return {
|
||||
\ 'uuid': uuid,
|
||||
\ 'item': item,
|
||||
\ 'text': text,
|
||||
\ 'outdentSize': outdent,
|
||||
\ 'deleteSize': delete}
|
||||
@ -330,8 +313,8 @@ endfunction
|
||||
|
||||
function! s:UpdatePreview() abort
|
||||
try
|
||||
let [text, outdent, delete, uuid] = s:SuggestionTextWithAdjustments()
|
||||
let text = split(text, "\n", 1)
|
||||
let [text, outdent, delete, item] = s:SuggestionTextWithAdjustments()
|
||||
let text = split(text, "\r\n\\=\\|\n", 1)
|
||||
if empty(text[-1])
|
||||
call remove(text, -1)
|
||||
endif
|
||||
@ -348,7 +331,7 @@ function! s:UpdatePreview() abort
|
||||
call s:ClearPreview()
|
||||
if s:has_nvim_ghost_text
|
||||
let data = {'id': 1}
|
||||
let data.virt_text_win_col = virtcol('.') - 1
|
||||
let data.virt_text_pos = 'overlay'
|
||||
let append = strpart(getline('.'), col('.') - 1 + delete)
|
||||
let data.virt_text = [[text[0] . append . repeat(' ', delete - len(text[0])), s:hlgroup]]
|
||||
if len(text) > 1
|
||||
@ -361,8 +344,27 @@ function! s:UpdatePreview() abort
|
||||
endif
|
||||
let data.hl_mode = 'combine'
|
||||
call nvim_buf_set_extmark(0, copilot#NvimNs(), line('.')-1, col('.')-1, data)
|
||||
else
|
||||
call prop_add(line('.'), col('.'), {'type': s:hlgroup, 'text': text[0]})
|
||||
elseif s:has_vim_ghost_text
|
||||
let new_suffix = text[0]
|
||||
let current_suffix = getline('.')[col('.') - 1 :]
|
||||
let inset = ''
|
||||
while delete > 0 && !empty(new_suffix)
|
||||
let last_char = matchstr(new_suffix, '.$')
|
||||
let new_suffix = matchstr(new_suffix, '^.\{-\}\ze.$')
|
||||
if last_char ==# matchstr(current_suffix, '.$')
|
||||
if !empty(inset)
|
||||
call prop_add(line('.'), col('.') + len(current_suffix), {'type': s:hlgroup, 'text': inset})
|
||||
let inset = ''
|
||||
endif
|
||||
let current_suffix = matchstr(current_suffix, '^.\{-\}\ze.$')
|
||||
let delete -= 1
|
||||
else
|
||||
let inset = last_char . inset
|
||||
endif
|
||||
endwhile
|
||||
if !empty(new_suffix . inset)
|
||||
call prop_add(line('.'), col('.'), {'type': s:hlgroup, 'text': new_suffix . inset})
|
||||
endif
|
||||
for line in text[1:]
|
||||
call prop_add(line('.'), 0, {'type': s:hlgroup, 'text_align': 'below', 'text': line})
|
||||
endfor
|
||||
@ -370,28 +372,35 @@ function! s:UpdatePreview() abort
|
||||
call prop_add(line('.'), col('$'), {'type': s:annot_hlgroup, 'text': ' ' . annot})
|
||||
endif
|
||||
endif
|
||||
if !has_key(b:_copilot.shown_choices, uuid)
|
||||
let b:_copilot.shown_choices[uuid] = v:true
|
||||
call copilot#Request('notifyShown', {'uuid': uuid})
|
||||
endif
|
||||
call copilot#Notify('textDocument/didShowCompletion', {'item': item})
|
||||
catch
|
||||
return copilot#logger#Exception()
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:HandleTriggerResult(result) abort
|
||||
if !exists('b:_copilot')
|
||||
return
|
||||
function! s:HandleTriggerResult(state, result) abort
|
||||
let a:state.suggestions = type(a:result) == type([]) ? a:result : get(empty(a:result) ? {} : a:result, 'items', [])
|
||||
let a:state.choice = 0
|
||||
if get(b:, '_copilot') is# a:state
|
||||
call s:UpdatePreview()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:HandleTriggerError(state, result) abort
|
||||
let a:state.suggestions = []
|
||||
let a:state.choice = 0
|
||||
let a:state.error = a:result
|
||||
if get(b:, '_copilot') is# a:state
|
||||
call s:UpdatePreview()
|
||||
endif
|
||||
let b:_copilot.suggestions = get(a:result, 'completions', [])
|
||||
let b:_copilot.choice = 0
|
||||
let b:_copilot.shown_choices = {}
|
||||
call s:UpdatePreview()
|
||||
endfunction
|
||||
|
||||
function! copilot#Suggest() abort
|
||||
if !s:Running()
|
||||
return ''
|
||||
endif
|
||||
try
|
||||
call copilot#Complete(function('s:HandleTriggerResult'), function('s:HandleTriggerResult'))
|
||||
call copilot#Complete(function('s:HandleTriggerResult'), function('s:HandleTriggerError'))
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
endtry
|
||||
@ -400,30 +409,52 @@ endfunction
|
||||
|
||||
function! s:Trigger(bufnr, timer) abort
|
||||
let timer = get(g:, '_copilot_timer', -1)
|
||||
unlet! g:_copilot_timer
|
||||
if a:bufnr !=# bufnr('') || a:timer isnot# timer || mode() !=# 'i'
|
||||
return
|
||||
endif
|
||||
unlet! g:_copilot_timer
|
||||
return copilot#Suggest()
|
||||
endfunction
|
||||
|
||||
function! copilot#IsMapped() abort
|
||||
return get(g:, 'copilot_assume_mapped') ||
|
||||
\ hasmapto('copilot#Accept(', 'i')
|
||||
endfunction
|
||||
|
||||
function! copilot#Schedule(...) abort
|
||||
if !s:has_ghost_text || !copilot#Enabled() || !copilot#IsMapped()
|
||||
function! copilot#Schedule() abort
|
||||
if !s:has_ghost_text || !s:Running() || !copilot#Enabled()
|
||||
call copilot#Clear()
|
||||
return
|
||||
endif
|
||||
call s:UpdatePreview()
|
||||
let delay = a:0 ? a:1 : get(g:, 'copilot_idle_delay', 15)
|
||||
let delay = get(g:, 'copilot_idle_delay', 45)
|
||||
call timer_stop(get(g:, '_copilot_timer', -1))
|
||||
let g:_copilot_timer = timer_start(delay, function('s:Trigger', [bufnr('')]))
|
||||
endfunction
|
||||
|
||||
function! copilot#OnInsertLeave() abort
|
||||
return copilot#Clear()
|
||||
function! s:Attach(bufnr, ...) abort
|
||||
try
|
||||
return copilot#Client().Attach(a:bufnr)
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! copilot#OnFileType() abort
|
||||
if empty(s:BufferDisabled()) && &l:modifiable && &l:buflisted
|
||||
call copilot#util#Defer(function('s:Attach'), bufnr(''))
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:Focus(bufnr, ...) abort
|
||||
if s:Running() && copilot#Client().IsAttached(a:bufnr)
|
||||
call copilot#Client().Notify('textDocument/didFocus', {'textDocument': {'uri': copilot#Client().Attach(a:bufnr).uri}})
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#OnBufEnter() abort
|
||||
let bufnr = bufnr('')
|
||||
call copilot#util#Defer(function('s:Focus'), bufnr)
|
||||
endfunction
|
||||
|
||||
function! copilot#OnInsertLeavePre() abort
|
||||
call copilot#Clear()
|
||||
call s:ClearPreview()
|
||||
endfunction
|
||||
|
||||
function! copilot#OnInsertEnter() abort
|
||||
@ -443,7 +474,6 @@ function! copilot#OnCursorMovedI() abort
|
||||
endfunction
|
||||
|
||||
function! copilot#OnBufUnload() abort
|
||||
call s:Reject(+expand('<abuf>'))
|
||||
endfunction
|
||||
|
||||
function! copilot#OnVimLeavePre() abort
|
||||
@ -468,11 +498,19 @@ function! copilot#Accept(...) abort
|
||||
if empty(text)
|
||||
let text = s.text
|
||||
endif
|
||||
call copilot#Request('notifyAccepted', {'uuid': s.uuid, 'acceptedLength': copilot#doc#UTF16Width(text)})
|
||||
if text ==# s.text && has_key(s.item, 'command')
|
||||
call copilot#Request('workspace/executeCommand', s.item.command)
|
||||
else
|
||||
let line_text = strpart(getline('.'), 0, col('.') - 1) . text
|
||||
call copilot#Notify('textDocument/didPartiallyAcceptCompletion', {
|
||||
\ 'item': s.item,
|
||||
\ 'acceptedLength': copilot#util#UTF16Width(line_text) - s.item.range.start.character})
|
||||
endif
|
||||
call s:ClearPreview()
|
||||
let s:suggestion_text = text
|
||||
let recall = text =~# "\n" ? "\<C-R>\<C-O>=" : "\<C-R>\<C-R>="
|
||||
return repeat("\<Left>\<Del>", s.outdentSize) . repeat("\<Del>", s.deleteSize) .
|
||||
\ "\<C-R>\<C-O>=copilot#TextQueuedForInsertion()\<CR>" . (a:0 > 1 ? '' : "\<End>")
|
||||
\ recall . "copilot#TextQueuedForInsertion()\<CR>" . (a:0 > 1 ? '' : "\<End>")
|
||||
endif
|
||||
let default = get(g:, 'copilot_tab_fallback', pumvisible() ? "\<C-N>" : "\t")
|
||||
if !a:0
|
||||
@ -525,21 +563,6 @@ function! copilot#Browser() abort
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:OpenURL(params) abort
|
||||
echo a:params.target
|
||||
let browser = copilot#Browser()
|
||||
if empty(browser)
|
||||
return v:false
|
||||
endif
|
||||
let status = {}
|
||||
call copilot#job#Stream(browser + [a:params.target], v:null, v:null, function('s:BrowserCallback', [status]))
|
||||
let time = reltime()
|
||||
while empty(status) && reltimefloat(reltime(time)) < 1
|
||||
sleep 10m
|
||||
endwhile
|
||||
return get(status, 'code') ? v:false : v:true
|
||||
endfunction
|
||||
|
||||
let s:commands = {}
|
||||
|
||||
function! s:EnabledStatusMessage() abort
|
||||
@ -550,8 +573,6 @@ function! s:EnabledStatusMessage() abort
|
||||
else
|
||||
return "Vim " . s:vim_minimum_version . " required to support ghost text"
|
||||
endif
|
||||
elseif !copilot#IsMapped()
|
||||
return '<Tab> map has been disabled or is claimed by another plugin'
|
||||
elseif !get(g:, 'copilot_enabled', 1)
|
||||
return 'Disabled globally by :Copilot disable'
|
||||
elseif buf_disabled is# 5
|
||||
@ -572,7 +593,7 @@ function! s:EnabledStatusMessage() abort
|
||||
endfunction
|
||||
|
||||
function! s:VerifySetup() abort
|
||||
let error = copilot#Agent().StartupError()
|
||||
let error = copilot#Client().StartupError()
|
||||
if !empty(error)
|
||||
echo 'Copilot: ' . error
|
||||
return
|
||||
@ -589,6 +610,12 @@ function! s:VerifySetup() abort
|
||||
echo 'Copilot: Telemetry terms not accepted. Invoke :Copilot setup'
|
||||
return
|
||||
endif
|
||||
|
||||
if status.status ==# 'NotAuthorized'
|
||||
echo "Copilot: You don't have access to GitHub Copilot. Sign up by visiting https://github.com/settings/copilot"
|
||||
return
|
||||
endif
|
||||
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
@ -597,31 +624,22 @@ function! s:commands.status(opts) abort
|
||||
return
|
||||
endif
|
||||
|
||||
if exists('s:client.status.status') && s:client.status.status =~# 'Warning\|Error'
|
||||
echo 'Copilot: ' . s:client.status.status
|
||||
if !empty(get(s:client.status, 'message', ''))
|
||||
echon ': ' . s:client.status.message
|
||||
endif
|
||||
return
|
||||
endif
|
||||
|
||||
let status = s:EnabledStatusMessage()
|
||||
if !empty(status)
|
||||
echo 'Copilot: ' . status
|
||||
return
|
||||
endif
|
||||
|
||||
let startup_error = copilot#Agent().StartupError()
|
||||
if !empty(startup_error)
|
||||
echo 'Copilot: ' . startup_error
|
||||
return
|
||||
endif
|
||||
|
||||
if exists('s:agent_error')
|
||||
echo 'Copilot: ' . s:agent_error
|
||||
return
|
||||
endif
|
||||
|
||||
let status = copilot#Call('checkStatus', {})
|
||||
if status.status ==# 'NotAuthorized'
|
||||
echo 'Copilot: Not authorized'
|
||||
return
|
||||
endif
|
||||
|
||||
echo 'Copilot: Enabled and online'
|
||||
call s:NodeVersionWarning()
|
||||
echo 'Copilot: Ready'
|
||||
call s:EditorVersionWarning()
|
||||
endfunction
|
||||
|
||||
function! s:commands.signout(opts) abort
|
||||
@ -635,7 +653,7 @@ function! s:commands.signout(opts) abort
|
||||
endfunction
|
||||
|
||||
function! s:commands.setup(opts) abort
|
||||
let startup_error = copilot#Agent().StartupError()
|
||||
let startup_error = copilot#Client().StartupError()
|
||||
if !empty(startup_error)
|
||||
echo 'Copilot: ' . startup_error
|
||||
return
|
||||
@ -645,7 +663,7 @@ function! s:commands.setup(opts) abort
|
||||
|
||||
let status = copilot#Call('checkStatus', {})
|
||||
if has_key(status, 'user')
|
||||
let data = {}
|
||||
let data = {'status': 'AlreadySignedIn', 'user': status.user}
|
||||
else
|
||||
let data = copilot#Call('signInInitiate', {})
|
||||
endif
|
||||
@ -653,23 +671,25 @@ function! s:commands.setup(opts) abort
|
||||
if has_key(data, 'verificationUri')
|
||||
let uri = data.verificationUri
|
||||
if has('clipboard')
|
||||
let @+ = data.userCode
|
||||
let @* = data.userCode
|
||||
try
|
||||
let @+ = data.userCode
|
||||
catch
|
||||
endtry
|
||||
try
|
||||
let @* = data.userCode
|
||||
catch
|
||||
endtry
|
||||
endif
|
||||
call s:Echo("First copy your one-time code: " . data.userCode)
|
||||
let codemsg = "First copy your one-time code: " . data.userCode . "\n"
|
||||
try
|
||||
if len(&mouse)
|
||||
let mouse = &mouse
|
||||
set mouse=
|
||||
endif
|
||||
if get(a:opts, 'bang')
|
||||
call s:Echo("In your browser, visit " . uri)
|
||||
call s:Echo(codemsg . "In your browser, visit " . uri)
|
||||
elseif len(browser)
|
||||
call s:Echo("Press ENTER to open GitHub in your browser")
|
||||
let c = getchar()
|
||||
while c isnot# 13 && c isnot# 10 && c isnot# 0
|
||||
let c = getchar()
|
||||
endwhile
|
||||
call input(codemsg . "Press ENTER to open GitHub in your browser\n")
|
||||
let status = {}
|
||||
call copilot#job#Stream(browser + [uri], v:null, v:null, function('s:BrowserCallback', [status]))
|
||||
let time = reltime()
|
||||
@ -682,9 +702,9 @@ function! s:commands.setup(opts) abort
|
||||
call s:Echo("Opened " . uri)
|
||||
endif
|
||||
else
|
||||
call s:Echo("Could not find browser. Visit " . uri)
|
||||
call s:Echo(codemsg . "Could not find browser. Visit " . uri)
|
||||
endif
|
||||
call s:Echo("Waiting (could take up to 5 seconds)")
|
||||
call s:Echo("Waiting (could take up to 10 seconds)")
|
||||
let request = copilot#Request('signInConfirm', {'userCode': data.userCode}).Wait()
|
||||
finally
|
||||
if exists('mouse')
|
||||
@ -696,6 +716,8 @@ function! s:commands.setup(opts) abort
|
||||
else
|
||||
let status = request.result
|
||||
endif
|
||||
elseif get(data, 'status', '') isnot# 'AlreadySignedIn'
|
||||
return 'echoerr ' . string('Copilot: Something went wrong')
|
||||
endif
|
||||
|
||||
let user = get(status, 'user', '<unknown>')
|
||||
@ -704,22 +726,46 @@ function! s:commands.setup(opts) abort
|
||||
endfunction
|
||||
|
||||
let s:commands.auth = s:commands.setup
|
||||
let s:commands.signin = s:commands.setup
|
||||
|
||||
function! s:commands.help(opts) abort
|
||||
return a:opts.mods . ' help ' . (len(a:opts.arg) ? ':Copilot_' . a:opts.arg : 'copilot')
|
||||
endfunction
|
||||
|
||||
function! s:commands.version(opts) abort
|
||||
let info = copilot#agent#EditorInfo()
|
||||
echo 'copilot.vim ' .info.editorPluginInfo.version
|
||||
echo info.editorInfo.name . ' ' . info.editorInfo.version
|
||||
if exists('s:agent.node_version')
|
||||
echo 'dist/agent.js ' . s:agent.Call('getVersion', {}).version
|
||||
echo 'Node.js ' . s:agent.node_version
|
||||
call s:NodeVersionWarning()
|
||||
echo 'copilot.vim ' .copilot#client#EditorPluginInfo().version
|
||||
let editorInfo = copilot#client#EditorInfo()
|
||||
echo editorInfo.name . ' ' . editorInfo.version
|
||||
if s:Running()
|
||||
let versions = s:client.Request('getVersion', {})
|
||||
if exists('s:client.serverInfo.version')
|
||||
echo s:client.serverInfo.name . ' ' . s:client.serverInfo.version
|
||||
else
|
||||
echo 'GitHub Copilot Language Server ' . versions.Await().version
|
||||
endif
|
||||
if exists('s:client.node_version')
|
||||
echo 'Node.js ' . s:client.node_version
|
||||
else
|
||||
echo 'Node.js ' . substitute(get(versions.Await(), 'runtimeVersion', '?'), '^node/', '', 'g')
|
||||
endif
|
||||
else
|
||||
echo 'dist/agent.js not running'
|
||||
echo 'Not running'
|
||||
if exists('s:client.node_version')
|
||||
echo 'Node.js ' . s:client.node_version
|
||||
endif
|
||||
endif
|
||||
if has('win32')
|
||||
echo 'Windows'
|
||||
elseif has('macunix')
|
||||
echo 'macOS'
|
||||
elseif !has('unix')
|
||||
echo 'Unknown OS'
|
||||
elseif isdirectory('/sys/kernel')
|
||||
echo 'Linux'
|
||||
else
|
||||
echo 'UNIX'
|
||||
endif
|
||||
call s:EditorVersionWarning()
|
||||
endfunction
|
||||
|
||||
function! s:UpdateEditorConfiguration() abort
|
||||
@ -743,11 +789,8 @@ endfunction
|
||||
|
||||
function! s:commands.restart(opts) abort
|
||||
call s:Stop()
|
||||
let err = copilot#Agent().StartupError()
|
||||
if !empty(err)
|
||||
return 'echoerr ' . string('Copilot: ' . err)
|
||||
endif
|
||||
echo 'Copilot: Restarting agent.'
|
||||
echo 'Copilot: Restarting language server'
|
||||
call s:Start()
|
||||
endfunction
|
||||
|
||||
function! s:commands.disable(opts) abort
|
||||
@ -766,6 +809,10 @@ function! s:commands.panel(opts) abort
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:commands.log(opts) abort
|
||||
return a:opts.mods . ' split +$ copilot:///log'
|
||||
endfunction
|
||||
|
||||
function! copilot#CommandComplete(arg, lead, pos) abort
|
||||
let args = matchstr(strpart(a:lead, 0, a:pos), 'C\%[opilot][! ] *\zs.*')
|
||||
if args !~# ' '
|
||||
@ -779,33 +826,28 @@ endfunction
|
||||
function! copilot#Command(line1, line2, range, bang, mods, arg) abort
|
||||
let cmd = matchstr(a:arg, '^\%(\\.\|\S\)\+')
|
||||
let arg = matchstr(a:arg, '\s\zs\S.*')
|
||||
if cmd ==# 'log'
|
||||
return a:mods . ' split +$ ' . fnameescape(copilot#logger#File())
|
||||
endif
|
||||
if !empty(cmd) && !has_key(s:commands, tr(cmd, '-', '_'))
|
||||
return 'echoerr ' . string('Copilot: unknown command ' . string(cmd))
|
||||
endif
|
||||
try
|
||||
let err = copilot#Agent().StartupError()
|
||||
if !empty(err)
|
||||
return 'echo ' . string('Copilot: ' . err)
|
||||
endif
|
||||
try
|
||||
let opts = copilot#Call('checkStatus', {'options': {'localChecksOnly': v:true}})
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
let opts = {'status': 'VimException'}
|
||||
endtry
|
||||
if empty(cmd)
|
||||
if opts.status ==# 'VimException'
|
||||
return a:mods . ' split +$ ' . fnameescape(copilot#logger#File())
|
||||
elseif opts.status !=# 'OK' && opts.status !=# 'MaybeOK'
|
||||
let cmd = 'setup'
|
||||
if !s:Running()
|
||||
let cmd = 'restart'
|
||||
else
|
||||
let cmd = 'panel'
|
||||
try
|
||||
let opts = copilot#Call('checkStatus', {'options': {'localChecksOnly': v:true}})
|
||||
if opts.status !=# 'OK' && opts.status !=# 'MaybeOK'
|
||||
let cmd = 'setup'
|
||||
else
|
||||
let cmd = 'panel'
|
||||
endif
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
let cmd = 'log'
|
||||
endtry
|
||||
endif
|
||||
endif
|
||||
call extend(opts, {'line1': a:line1, 'line2': a:line2, 'range': a:range, 'bang': a:bang, 'mods': a:mods, 'arg': arg})
|
||||
let opts = {'line1': a:line1, 'line2': a:line2, 'range': a:range, 'bang': a:bang, 'mods': a:mods, 'arg': arg}
|
||||
let retval = s:commands[tr(cmd, '-', '_')](opts)
|
||||
if type(retval) == v:t_string
|
||||
return retval
|
||||
|
@ -1,603 +0,0 @@
|
||||
if exists('g:autoloaded_copilot_agent')
|
||||
finish
|
||||
endif
|
||||
let g:autoloaded_copilot_agent = 1
|
||||
|
||||
scriptencoding utf-8
|
||||
|
||||
let s:plugin_version = '1.13.0'
|
||||
|
||||
let s:error_exit = -1
|
||||
|
||||
let s:root = expand('<sfile>:h:h:h')
|
||||
|
||||
if !exists('s:instances')
|
||||
let s:instances = {}
|
||||
endif
|
||||
|
||||
" allow sourcing this file to reload the Lua file too
|
||||
if has('nvim')
|
||||
lua package.loaded._copilot = nil
|
||||
endif
|
||||
|
||||
let s:jobstop = function(exists('*jobstop') ? 'jobstop' : 'job_stop')
|
||||
function! s:Kill(agent, ...) abort
|
||||
if has_key(a:agent, 'job')
|
||||
call s:jobstop(a:agent.job)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:AgentClose() dict abort
|
||||
if !has_key(self, 'job')
|
||||
return
|
||||
endif
|
||||
if exists('*chanclose')
|
||||
call chanclose(self.job, 'stdin')
|
||||
else
|
||||
call ch_close_in(self.job)
|
||||
endif
|
||||
call copilot#logger#Info('agent stopped')
|
||||
call timer_start(2000, function('s:Kill', [self]))
|
||||
endfunction
|
||||
|
||||
function! s:LogSend(request, line) abort
|
||||
return '--> ' . a:line
|
||||
endfunction
|
||||
|
||||
function! s:RejectRequest(request, error) abort
|
||||
if a:request.status ==# 'canceled'
|
||||
return
|
||||
endif
|
||||
let a:request.waiting = {}
|
||||
call remove(a:request, 'resolve')
|
||||
let reject = remove(a:request, 'reject')
|
||||
let a:request.status = 'error'
|
||||
let a:request.error = a:error
|
||||
for Cb in reject
|
||||
let a:request.waiting[timer_start(0, function('s:Callback', [a:request, 'error', Cb]))] = 1
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:Send(agent, request) abort
|
||||
try
|
||||
call ch_sendexpr(a:agent.job, a:request)
|
||||
return v:true
|
||||
catch /^Vim\%((\a\+)\)\=:E631:/
|
||||
return v:false
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:AgentNotify(method, params) dict abort
|
||||
return s:Send(self, {'method': a:method, 'params': a:params})
|
||||
endfunction
|
||||
|
||||
function! s:RequestWait() dict abort
|
||||
while self.status ==# 'running'
|
||||
sleep 1m
|
||||
endwhile
|
||||
while !empty(get(self, 'waiting', {}))
|
||||
sleep 1m
|
||||
endwhile
|
||||
return self
|
||||
endfunction
|
||||
|
||||
function! s:RequestAwait() dict abort
|
||||
call self.Wait()
|
||||
if has_key(self, 'result')
|
||||
return self.result
|
||||
endif
|
||||
throw 'copilot#agent(' . self.error.code . '): ' . self.error.message
|
||||
endfunction
|
||||
|
||||
function! s:RequestAgent() dict abort
|
||||
return get(s:instances, self.agent_id, v:null)
|
||||
endfunction
|
||||
|
||||
if !exists('s:id')
|
||||
let s:id = 0
|
||||
endif
|
||||
|
||||
function! s:SetUpRequest(agent, id, method, params, ...) abort
|
||||
let request = {
|
||||
\ 'agent_id': a:agent.id,
|
||||
\ 'id': a:id,
|
||||
\ 'method': a:method,
|
||||
\ 'params': a:params,
|
||||
\ 'Agent': function('s:RequestAgent'),
|
||||
\ 'Wait': function('s:RequestWait'),
|
||||
\ 'Await': function('s:RequestAwait'),
|
||||
\ 'Cancel': function('s:RequestCancel'),
|
||||
\ 'resolve': [],
|
||||
\ 'reject': [],
|
||||
\ 'status': 'running'}
|
||||
let a:agent.requests[a:id] = request
|
||||
let args = a:000[2:-1]
|
||||
if len(args)
|
||||
if !empty(a:1)
|
||||
call add(request.resolve, { v -> call(a:1, [v] + args)})
|
||||
endif
|
||||
if !empty(a:2)
|
||||
call add(request.reject, { v -> call(a:2, [v] + args)})
|
||||
endif
|
||||
return request
|
||||
endif
|
||||
if a:0 && !empty(a:1)
|
||||
call add(request.resolve, a:1)
|
||||
endif
|
||||
if a:0 > 1 && !empty(a:2)
|
||||
call add(request.reject, a:2)
|
||||
endif
|
||||
return request
|
||||
endfunction
|
||||
|
||||
function! s:UrlEncode(str) abort
|
||||
return substitute(iconv(a:str, 'latin1', 'utf-8'),'[^A-Za-z0-9._~!$&''()*+,;=:@/-]','\="%".printf("%02X",char2nr(submatch(0)))','g')
|
||||
endfunction
|
||||
|
||||
let s:slash = exists('+shellslash') ? '\' : '/'
|
||||
function! s:UriFromBufnr(bufnr) abort
|
||||
let absolute = tr(bufname(a:bufnr), s:slash, '/')
|
||||
if absolute !~# '^\a\+:\|^/\|^$' && getbufvar(a:bufnr, 'buftype') =~# '^\%(nowrite\)\=$'
|
||||
let absolute = substitute(tr(getcwd(), s:slash, '/'), '/\=$', '/', '') . absolute
|
||||
endif
|
||||
if has('win32') && absolute =~# '^\a://\@!'
|
||||
return 'file:///' . strpart(absolute, 0, 2) . s:UrlEncode(strpart(absolute, 2))
|
||||
elseif absolute =~# '^/'
|
||||
return 'file://' . s:UrlEncode(absolute)
|
||||
elseif absolute =~# '^\a[[:alnum:].+-]*:\|^$'
|
||||
return absolute
|
||||
else
|
||||
return ''
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:BufferText(bufnr) abort
|
||||
return join(getbufline(a:bufnr, 1, '$'), "\n") . "\n"
|
||||
endfunction
|
||||
|
||||
function! s:LogMessage(params) abort
|
||||
call copilot#logger#Raw(get(a:params, 'level', 3), get(a:params, 'message', ''))
|
||||
endfunction
|
||||
|
||||
function! s:ShowMessageRequest(params) abort
|
||||
let choice = inputlist([a:params.message . "\n\nRequest Actions:"] +
|
||||
\ map(copy(get(a:params, 'actions', [])), { i, v -> (i + 1) . '. ' . v.title}))
|
||||
return choice > 0 ? get(a:params.actions, choice - 1, v:null) : v:null
|
||||
endfunction
|
||||
|
||||
function! s:SendRequest(agent, request) abort
|
||||
if empty(s:Send(a:agent, a:request)) && has_key(a:agent.requests, a:request.id)
|
||||
call s:RejectRequest(remove(a:agent.requests, a:request.id), {'code': 257, 'message': 'Write failed'})
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:AgentRequest(method, params, ...) dict abort
|
||||
let s:id += 1
|
||||
let request = {'method': a:method, 'params': deepcopy(a:params), 'id': s:id}
|
||||
for doc in filter([get(request.params, 'doc', {}), get(request.params, 'textDocument',{})], 'type(get(v:val, "uri", "")) == v:t_number')
|
||||
let bufnr = doc.uri
|
||||
let doc.uri = s:UriFromBufnr(doc.uri)
|
||||
let uri = doc.uri
|
||||
let languageId = copilot#doc#LanguageForFileType(getbufvar(bufnr, '&filetype'))
|
||||
let doc_version = getbufvar(bufnr, 'changedtick')
|
||||
if has_key(self.open_buffers, bufnr) && (
|
||||
\ self.open_buffers[bufnr].uri !=# doc.uri ||
|
||||
\ self.open_buffers[bufnr].languageId !=# languageId)
|
||||
call remove(self.open_buffers, bufnr)
|
||||
sleep 1m
|
||||
endif
|
||||
if !has_key(self.open_buffers, bufnr)
|
||||
let td_item = {
|
||||
\ 'uri': doc.uri,
|
||||
\ 'version': doc_version,
|
||||
\ 'languageId': languageId,
|
||||
\ 'text': s:BufferText(bufnr)}
|
||||
call self.Notify('textDocument/didOpen', {'textDocument': td_item})
|
||||
let self.open_buffers[bufnr] = {
|
||||
\ 'uri': doc.uri,
|
||||
\ 'version': doc_version,
|
||||
\ 'languageId': languageId}
|
||||
else
|
||||
let vtd_id = {
|
||||
\ 'uri': doc.uri,
|
||||
\ 'version': doc_version}
|
||||
call self.Notify('textDocument/didChange', {
|
||||
\ 'textDocument': vtd_id,
|
||||
\ 'contentChanges': [{'text': s:BufferText(bufnr)}]})
|
||||
let self.open_buffers[bufnr].version = doc_version
|
||||
endif
|
||||
let doc.version = doc_version
|
||||
endfor
|
||||
call timer_start(0, { _ -> s:SendRequest(self, request) })
|
||||
return call('s:SetUpRequest', [self, s:id, a:method, a:params] + a:000)
|
||||
endfunction
|
||||
|
||||
function! s:AgentCall(method, params, ...) dict abort
|
||||
let request = call(self.Request, [a:method, a:params] + a:000)
|
||||
if a:0
|
||||
return request
|
||||
endif
|
||||
return request.Await()
|
||||
endfunction
|
||||
|
||||
function! s:AgentCancel(request) dict abort
|
||||
if has_key(self.requests, get(a:request, 'id', ''))
|
||||
call remove(self.requests, a:request.id)
|
||||
call self.Notify('$/cancelRequest', {'id': a:request.id})
|
||||
endif
|
||||
if get(a:request, 'status', '') ==# 'running'
|
||||
let a:request.status = 'canceled'
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:RequestCancel() dict abort
|
||||
let agent = self.Agent()
|
||||
if !empty(agent)
|
||||
call agent.Cancel(self)
|
||||
elseif get(self, 'status', '') ==# 'running'
|
||||
let self.status = 'canceled'
|
||||
endif
|
||||
return self
|
||||
endfunction
|
||||
|
||||
function! s:DispatchMessage(agent, handler, id, params, ...) abort
|
||||
try
|
||||
let response = {'result': call(a:handler, [a:params])}
|
||||
if response.result is# 0
|
||||
let response.result = v:null
|
||||
endif
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
let response = {'error': {'code': -32000, 'message': v:exception}}
|
||||
endtry
|
||||
if !empty(a:id)
|
||||
call s:Send(a:agent, extend({'id': a:id}, response))
|
||||
endif
|
||||
return response
|
||||
endfunction
|
||||
|
||||
function! s:OnMessage(agent, body, ...) abort
|
||||
if !has_key(a:body, 'method')
|
||||
return s:OnResponse(a:agent, a:body)
|
||||
endif
|
||||
let request = a:body
|
||||
let id = get(request, 'id', v:null)
|
||||
let params = get(request, 'params', v:null)
|
||||
if has_key(a:agent.methods, request.method)
|
||||
return s:DispatchMessage(a:agent, a:agent.methods[request.method], id, params)
|
||||
elseif !empty(id)
|
||||
call s:Send(a:agent, {"id": id, "error": {"code": -32700, "message": "Method not found: " . request.method}})
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:OnResponse(agent, response, ...) abort
|
||||
let response = a:response
|
||||
let id = get(a:response, 'id', v:null)
|
||||
if !has_key(a:agent.requests, id)
|
||||
return
|
||||
endif
|
||||
let request = remove(a:agent.requests, id)
|
||||
if request.status ==# 'canceled'
|
||||
return
|
||||
endif
|
||||
let request.waiting = {}
|
||||
let resolve = remove(request, 'resolve')
|
||||
let reject = remove(request, 'reject')
|
||||
if has_key(response, 'result')
|
||||
let request.status = 'success'
|
||||
let request.result = response.result
|
||||
for Cb in resolve
|
||||
let request.waiting[timer_start(0, function('s:Callback', [request, 'result', Cb]))] = 1
|
||||
endfor
|
||||
else
|
||||
let request.status = 'error'
|
||||
let request.error = response.error
|
||||
for Cb in reject
|
||||
let request.waiting[timer_start(0, function('s:Callback', [request, 'error', Cb]))] = 1
|
||||
endfor
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:OnErr(agent, line, ...) abort
|
||||
call copilot#logger#Debug('<-! ' . a:line)
|
||||
endfunction
|
||||
|
||||
function! s:OnExit(agent, code, ...) abort
|
||||
let a:agent.exit_status = a:code
|
||||
if has_key(a:agent, 'job')
|
||||
call remove(a:agent, 'job')
|
||||
endif
|
||||
if has_key(a:agent, 'client_id')
|
||||
call remove(a:agent, 'client_id')
|
||||
endif
|
||||
let code = a:code < 0 || a:code > 255 ? 256 : a:code
|
||||
for id in sort(keys(a:agent.requests), { a, b -> +a > +b })
|
||||
call s:RejectRequest(remove(a:agent.requests, id), {'code': code, 'message': 'Agent exited', 'data': {'status': a:code}})
|
||||
endfor
|
||||
call timer_start(0, { _ -> get(s:instances, a:agent.id) is# a:agent ? remove(s:instances, a:agent.id) : {} })
|
||||
call copilot#logger#Info('agent exited with status ' . a:code)
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#LspInit(agent_id, initialize_result) abort
|
||||
if !has_key(s:instances, a:agent_id)
|
||||
return
|
||||
endif
|
||||
let instance = s:instances[a:agent_id]
|
||||
call timer_start(0, { _ -> s:GetCapabilitiesResult(a:initialize_result, instance)})
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#LspExit(agent_id, code, signal) abort
|
||||
if !has_key(s:instances, a:agent_id)
|
||||
return
|
||||
endif
|
||||
let instance = remove(s:instances, a:agent_id)
|
||||
call s:OnExit(instance, a:code)
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#LspResponse(agent_id, opts, ...) abort
|
||||
if !has_key(s:instances, a:agent_id)
|
||||
return
|
||||
endif
|
||||
call s:OnResponse(s:instances[a:agent_id], a:opts)
|
||||
endfunction
|
||||
|
||||
function! s:LspRequest(method, params, ...) dict abort
|
||||
let id = v:lua.require'_copilot'.lsp_request(self.id, a:method, a:params)
|
||||
if id isnot# v:null
|
||||
return call('s:SetUpRequest', [self, id, a:method, a:params] + a:000)
|
||||
endif
|
||||
if has_key(self, 'client_id')
|
||||
call copilot#agent#LspExit(self.client_id, -1, -1)
|
||||
endif
|
||||
throw 'copilot#agent: LSP client not available'
|
||||
endfunction
|
||||
|
||||
function! s:LspClose() dict abort
|
||||
if !has_key(self, 'client_id')
|
||||
return
|
||||
endif
|
||||
return luaeval('vim.lsp.get_client_by_id(_A).stop()', self.client_id)
|
||||
endfunction
|
||||
|
||||
function! s:LspNotify(method, params) dict abort
|
||||
return v:lua.require'_copilot'.rpc_notify(self.id, a:method, a:params)
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#LspHandle(agent_id, request) abort
|
||||
if !has_key(s:instances, a:agent_id)
|
||||
return
|
||||
endif
|
||||
return s:OnMessage(s:instances[a:agent_id], a:request)
|
||||
endfunction
|
||||
|
||||
function! s:GetNodeVersion(command) abort
|
||||
let out = []
|
||||
let err = []
|
||||
let status = copilot#job#Stream(a:command + ['--version'], function('add', [out]), function('add', [err]))
|
||||
let string = matchstr(join(out, ''), '^v\zs\d\+\.[^[:space:]]*')
|
||||
if status != 0
|
||||
let string = ''
|
||||
endif
|
||||
let major = str2nr(string)
|
||||
let minor = str2nr(matchstr(string, '\.\zs\d\+'))
|
||||
return {'status': status, 'string': string, 'major': major, 'minor': minor}
|
||||
endfunction
|
||||
|
||||
function! s:Command() abort
|
||||
if !has('nvim-0.6') && v:version < 900
|
||||
return [v:null, '', 'Vim version too old']
|
||||
endif
|
||||
let node = get(g:, 'copilot_node_command', '')
|
||||
if empty(node)
|
||||
let node = ['node']
|
||||
elseif type(node) == type('')
|
||||
let node = [expand(node)]
|
||||
endif
|
||||
if !executable(get(node, 0, ''))
|
||||
if get(node, 0, '') ==# 'node'
|
||||
return [v:null, '', 'Node.js not found in PATH']
|
||||
else
|
||||
return [v:null, '', 'Node.js executable `' . get(node, 0, '') . "' not found"]
|
||||
endif
|
||||
endif
|
||||
let node_version = s:GetNodeVersion(node)
|
||||
let warning = ''
|
||||
if !get(g:, 'copilot_ignore_node_version') && node_version.major < 18 && get(node, 0, '') !=# 'node' && executable('node')
|
||||
let node_version_from_path = s:GetNodeVersion(['node'])
|
||||
if node_version_from_path.major >= 18
|
||||
let warning = 'Ignoring g:copilot_node_command: Node.js ' . node_version.string . ' is end-of-life'
|
||||
let node = ['node']
|
||||
let node_version = node_version_from_path
|
||||
endif
|
||||
endif
|
||||
if node_version.status != 0
|
||||
return [v:null, '', 'Node.js exited with status ' . node_version.status]
|
||||
endif
|
||||
if !get(g:, 'copilot_ignore_node_version')
|
||||
if node_version.major == 0
|
||||
return [v:null, node_version.string, 'Could not determine Node.js version']
|
||||
elseif node_version.major < 16 || node_version.major == 16 && node_version.minor < 14 || node_version.major == 17 && node_version.minor < 3
|
||||
" 16.14+ and 17.3+ still work for now, but are end-of-life
|
||||
return [v:null, node_version.string, 'Node.js version 18.x or newer required but found ' . node_version.string]
|
||||
endif
|
||||
endif
|
||||
let agent = get(g:, 'copilot_agent_command', '')
|
||||
if empty(agent) || !filereadable(agent)
|
||||
let agent = s:root . '/dist/agent.js'
|
||||
if !filereadable(agent)
|
||||
return [v:null, node_version.string, 'Could not find dist/agent.js (bad install?)']
|
||||
endif
|
||||
endif
|
||||
return [node + [agent, '--stdio'], node_version.string, warning]
|
||||
endfunction
|
||||
|
||||
function! s:UrlDecode(str) abort
|
||||
return substitute(a:str, '%\(\x\x\)', '\=iconv(nr2char("0x".submatch(1)), "utf-8", "latin1")', 'g')
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#EditorInfo() abort
|
||||
if !exists('s:editor_version')
|
||||
if has('nvim')
|
||||
let s:editor_version = matchstr(execute('version'), 'NVIM v\zs[^[:space:]]\+')
|
||||
else
|
||||
let s:editor_version = (v:version / 100) . '.' . (v:version % 100) . (exists('v:versionlong') ? printf('.%04d', v:versionlong % 1000) : '')
|
||||
endif
|
||||
endif
|
||||
let info = {
|
||||
\ 'editorInfo': {'name': has('nvim') ? 'Neovim': 'Vim', 'version': s:editor_version},
|
||||
\ 'editorPluginInfo': {'name': 'copilot.vim', 'version': s:plugin_version}}
|
||||
if type(get(g:, 'copilot_proxy')) == v:t_string
|
||||
let proxy = g:copilot_proxy
|
||||
else
|
||||
let proxy = ''
|
||||
endif
|
||||
let match = matchlist(proxy, '\c^\%([^:]\+://\)\=\%(\([^/#]\+@\)\)\=\%(\([^/:#]\+\)\|\[\([[:xdigit:]:]\+\)\]\)\%(:\(\d\+\)\)\=\%(/\|$\|?strict_\=ssl=\(.*\)\)')
|
||||
if !empty(match)
|
||||
let info.networkProxy = {'host': match[2] . match[3], 'port': empty(match[4]) ? 80 : +match[4]}
|
||||
if match[5] =~? '^[0f]'
|
||||
let info.networkProxy.rejectUnauthorized = v:false
|
||||
elseif match[5] =~? '^[1t]'
|
||||
let info.networkProxy.rejectUnauthorized = v:true
|
||||
elseif exists('g:copilot_proxy_strict_ssl')
|
||||
let info.networkProxy.rejectUnauthorized = empty(g:copilot_proxy_strict_ssl) ? v:false : v:true
|
||||
endif
|
||||
if !empty(match[1])
|
||||
let info.networkProxy.username = s:UrlDecode(matchstr(match[1], '^[^:@]*'))
|
||||
let info.networkProxy.password = s:UrlDecode(matchstr(match[1], ':\zs[^@]*'))
|
||||
endif
|
||||
endif
|
||||
return info
|
||||
endfunction
|
||||
|
||||
function! s:GetCapabilitiesResult(result, agent) abort
|
||||
let a:agent.capabilities = get(a:result, 'capabilities', {})
|
||||
let info = copilot#agent#EditorInfo()
|
||||
call a:agent.Request('setEditorInfo', extend({'editorConfiguration': a:agent.editorConfiguration}, info))
|
||||
endfunction
|
||||
|
||||
function! s:GetCapabilitiesError(error, agent) abort
|
||||
if a:error.code == s:error_exit
|
||||
let a:agent.startup_error = 'Agent exited with status ' . a:error.data.status
|
||||
else
|
||||
let a:agent.startup_error = 'Unexpected error ' . a:error.code . ' calling agent: ' . a:error.message
|
||||
call a:agent.Close()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:AgentStartupError() dict abort
|
||||
while (has_key(self, 'job') || has_key(self, 'client_id')) && !has_key(self, 'startup_error') && !has_key(self, 'capabilities')
|
||||
sleep 10m
|
||||
endwhile
|
||||
if has_key(self, 'capabilities')
|
||||
return ''
|
||||
else
|
||||
return get(self, 'startup_error', 'Something unexpected went wrong spawning the agent')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#New(...) abort
|
||||
let opts = a:0 ? a:1 : {}
|
||||
let instance = {'requests': {},
|
||||
\ 'editorConfiguration': get(opts, 'editorConfiguration', {}),
|
||||
\ 'Close': function('s:AgentClose'),
|
||||
\ 'Notify': function('s:AgentNotify'),
|
||||
\ 'Request': function('s:AgentRequest'),
|
||||
\ 'Call': function('s:AgentCall'),
|
||||
\ 'Cancel': function('s:AgentCancel'),
|
||||
\ 'StartupError': function('s:AgentStartupError'),
|
||||
\ }
|
||||
let instance.methods = extend({
|
||||
\ 'LogMessage': function('s:LogMessage'),
|
||||
\ 'window/logMessage': function('s:LogMessage'),
|
||||
\ }, get(opts, 'methods', {}))
|
||||
let [command, node_version, command_error] = s:Command()
|
||||
if len(command_error)
|
||||
if empty(command)
|
||||
let instance.id = -1
|
||||
let instance.startup_error = command_error
|
||||
return instance
|
||||
else
|
||||
let instance.node_version_warning = command_error
|
||||
endif
|
||||
endif
|
||||
let instance.node_version = node_version
|
||||
if has('nvim')
|
||||
call extend(instance, {
|
||||
\ 'Close': function('s:LspClose'),
|
||||
\ 'Notify': function('s:LspNotify'),
|
||||
\ 'Request': function('s:LspRequest')})
|
||||
let instance.client_id = v:lua.require'_copilot'.lsp_start_client(command, keys(instance.methods))
|
||||
let instance.id = instance.client_id
|
||||
else
|
||||
let state = {'headers': {}, 'mode': 'headers', 'buffer': ''}
|
||||
let instance.open_buffers = {}
|
||||
let instance.methods = extend({'window/showMessageRequest': function('s:ShowMessageRequest')}, instance.methods)
|
||||
let instance.job = job_start(command, {
|
||||
\ 'cwd': copilot#job#Cwd(),
|
||||
\ 'in_mode': 'lsp',
|
||||
\ 'out_mode': 'lsp',
|
||||
\ 'out_cb': { j, d -> timer_start(0, function('s:OnMessage', [instance, d])) },
|
||||
\ 'err_cb': { j, d -> timer_start(0, function('s:OnErr', [instance, d])) },
|
||||
\ 'exit_cb': { j, d -> timer_start(0, function('s:OnExit', [instance, d])) },
|
||||
\ })
|
||||
let instance.id = exists('*jobpid') ? jobpid(instance.job) : job_info(instance.job).process
|
||||
let capabilities = {'workspace': {'workspaceFolders': v:true}, 'copilot': {}}
|
||||
for name in keys(instance.methods)
|
||||
if name =~# '^copilot/'
|
||||
let capabilities.copilot[matchstr(name, '/\zs.*')] = v:true
|
||||
endif
|
||||
endfor
|
||||
let request = instance.Request('initialize', {'capabilities': capabilities}, function('s:GetCapabilitiesResult'), function('s:GetCapabilitiesError'), instance)
|
||||
endif
|
||||
let s:instances[instance.id] = instance
|
||||
return instance
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#Cancel(request) abort
|
||||
if type(a:request) == type({}) && has_key(a:request, 'Cancel')
|
||||
call a:request.Cancel()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:Callback(request, type, callback, timer) abort
|
||||
call remove(a:request.waiting, a:timer)
|
||||
if has_key(a:request, a:type)
|
||||
call a:callback(a:request[a:type])
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#Result(request, callback) abort
|
||||
if has_key(a:request, 'resolve')
|
||||
call add(a:request.resolve, a:callback)
|
||||
elseif has_key(a:request, 'result')
|
||||
let a:request.waiting[timer_start(0, function('s:Callback', [a:request, 'result', a:callback]))] = 1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#Error(request, callback) abort
|
||||
if has_key(a:request, 'reject')
|
||||
call add(a:request.reject, a:callback)
|
||||
elseif has_key(a:request, 'error')
|
||||
let a:request.waiting[timer_start(0, function('s:Callback', [a:request, 'error', a:callback]))] = 1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:CloseBuffer(bufnr) abort
|
||||
for instance in values(s:instances)
|
||||
try
|
||||
if has_key(instance, 'job') && has_key(instance.open_buffers, a:bufnr)
|
||||
let buffer = remove(instance.open_buffers, a:bufnr)
|
||||
call instance.Notify('textDocument/didClose', {'textDocument': {'uri': buffer.uri}})
|
||||
endif
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
endtry
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
augroup copilot_agent
|
||||
autocmd!
|
||||
if !has('nvim')
|
||||
autocmd BufUnload * call s:CloseBuffer(+expand('<abuf>'))
|
||||
endif
|
||||
augroup END
|
764
sources_non_forked/copilot.vim/autoload/copilot/client.vim
Normal file
764
sources_non_forked/copilot.vim/autoload/copilot/client.vim
Normal file
@ -0,0 +1,764 @@
|
||||
scriptencoding utf-8
|
||||
|
||||
let s:plugin_version = copilot#version#String()
|
||||
|
||||
let s:error_canceled = {'code': -32800, 'message': 'Canceled'}
|
||||
let s:error_exit = {'code': -32097, 'message': 'Process exited'}
|
||||
let s:error_connection_inactive = {'code': -32096, 'message': 'Connection inactive'}
|
||||
|
||||
let s:root = expand('<sfile>:h:h:h')
|
||||
|
||||
if !exists('s:instances')
|
||||
let s:instances = {}
|
||||
endif
|
||||
|
||||
" allow sourcing this file to reload the Lua file too
|
||||
if has('nvim')
|
||||
lua package.loaded._copilot = nil
|
||||
endif
|
||||
|
||||
function! s:Warn(msg) abort
|
||||
if !empty(get(g:, 'copilot_no_startup_warnings'))
|
||||
return
|
||||
endif
|
||||
echohl WarningMsg
|
||||
echomsg 'Copilot: ' . a:msg
|
||||
echohl NONE
|
||||
endfunction
|
||||
|
||||
function! s:VimClose() dict abort
|
||||
if !has_key(self, 'job')
|
||||
return
|
||||
endif
|
||||
let job = self.job
|
||||
if has_key(self, 'kill')
|
||||
call job_stop(job, 'kill')
|
||||
call copilot#logger#Warn('Process forcefully terminated')
|
||||
return
|
||||
endif
|
||||
let self.kill = v:true
|
||||
let self.shutdown = self.Request('shutdown', {}, function(self.Notify, ['exit']))
|
||||
call timer_start(2000, { _ -> job_stop(job, 'kill') })
|
||||
call copilot#logger#Debug('Process shutdown initiated')
|
||||
endfunction
|
||||
|
||||
function! s:LogSend(request, line) abort
|
||||
return '--> ' . a:line
|
||||
endfunction
|
||||
|
||||
function! s:RejectRequest(request, error) abort
|
||||
if a:request.status !=# 'running'
|
||||
return
|
||||
endif
|
||||
let a:request.waiting = {}
|
||||
call remove(a:request, 'resolve')
|
||||
let reject = remove(a:request, 'reject')
|
||||
let a:request.status = 'error'
|
||||
let a:request.error = deepcopy(a:error)
|
||||
for Cb in reject
|
||||
let a:request.waiting[timer_start(0, function('s:Callback', [a:request, 'error', Cb]))] = 1
|
||||
endfor
|
||||
if index([s:error_canceled.code, s:error_connection_inactive.code], a:error.code) != -1
|
||||
return
|
||||
endif
|
||||
let msg = 'Method ' . a:request.method . ' errored with E' . a:error.code . ': ' . json_encode(a:error.message)
|
||||
if empty(reject)
|
||||
call copilot#logger#Error(msg)
|
||||
else
|
||||
call copilot#logger#Debug(msg)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:AfterInitialized(fn, ...) dict abort
|
||||
call add(self.after_initialized, function(a:fn, a:000))
|
||||
endfunction
|
||||
|
||||
function! s:Send(instance, request) abort
|
||||
if !has_key(a:instance, 'job')
|
||||
return v:false
|
||||
endif
|
||||
try
|
||||
call ch_sendexpr(a:instance.job, a:request)
|
||||
return v:true
|
||||
catch /^Vim\%((\a\+)\)\=:E906:/
|
||||
let a:instance.kill = v:true
|
||||
let job = remove(a:instance, 'job')
|
||||
call job_stop(job)
|
||||
call timer_start(2000, { _ -> job_stop(job, 'kill') })
|
||||
call copilot#logger#Warn('Terminating process after failed write')
|
||||
return v:false
|
||||
catch /^Vim\%((\a\+)\)\=:E631:/
|
||||
return v:false
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:VimNotify(method, params) dict abort
|
||||
let request = {'method': a:method, 'params': a:params}
|
||||
call self.AfterInitialized(function('s:Send', [self, request]))
|
||||
endfunction
|
||||
|
||||
function! s:RequestWait() dict abort
|
||||
while self.status ==# 'running'
|
||||
sleep 1m
|
||||
endwhile
|
||||
while !empty(get(self, 'waiting', {}))
|
||||
sleep 1m
|
||||
endwhile
|
||||
return self
|
||||
endfunction
|
||||
|
||||
function! s:RequestAwait() dict abort
|
||||
call self.Wait()
|
||||
if has_key(self, 'result')
|
||||
return self.result
|
||||
endif
|
||||
throw 'Copilot:E' . self.error.code . ': ' . self.error.message
|
||||
endfunction
|
||||
|
||||
function! s:RequestClient() dict abort
|
||||
return get(s:instances, self.client_id, v:null)
|
||||
endfunction
|
||||
|
||||
if !exists('s:id')
|
||||
let s:id = 0
|
||||
endif
|
||||
if !exists('s:progress_token_id')
|
||||
let s:progress_token_id = 0
|
||||
endif
|
||||
|
||||
function! s:SetUpRequest(instance, id, method, params, progress, ...) abort
|
||||
let request = {
|
||||
\ 'client_id': a:instance.id,
|
||||
\ 'id': a:id,
|
||||
\ 'method': a:method,
|
||||
\ 'params': a:params,
|
||||
\ 'Client': function('s:RequestClient'),
|
||||
\ 'Wait': function('s:RequestWait'),
|
||||
\ 'Await': function('s:RequestAwait'),
|
||||
\ 'Cancel': function('s:RequestCancel'),
|
||||
\ 'resolve': [],
|
||||
\ 'reject': [],
|
||||
\ 'progress': a:progress,
|
||||
\ 'status': 'running'}
|
||||
let args = a:000[2:-1]
|
||||
if len(args)
|
||||
if !empty(a:1)
|
||||
call add(request.resolve, { v -> call(a:1, [v] + args)})
|
||||
endif
|
||||
if !empty(a:2)
|
||||
call add(request.reject, { v -> call(a:2, [v] + args)})
|
||||
endif
|
||||
return request
|
||||
endif
|
||||
if a:0 && !empty(a:1)
|
||||
call add(request.resolve, a:1)
|
||||
endif
|
||||
if a:0 > 1 && !empty(a:2)
|
||||
call add(request.reject, a:2)
|
||||
endif
|
||||
return request
|
||||
endfunction
|
||||
|
||||
function! s:UrlEncode(str) abort
|
||||
return substitute(iconv(a:str, 'latin1', 'utf-8'),'[^A-Za-z0-9._~!$&''()*+,;=:@/-]','\="%".printf("%02X",char2nr(submatch(0)))','g')
|
||||
endfunction
|
||||
|
||||
let s:slash = exists('+shellslash') ? '\' : '/'
|
||||
function! s:UriFromBufnr(bufnr) abort
|
||||
let absolute = tr(bufname(a:bufnr), s:slash, '/')
|
||||
if absolute !~# '^\a\+:\|^/\|^$' && getbufvar(a:bufnr, 'buftype') =~# '^\%(nowrite\)\=$'
|
||||
let absolute = substitute(tr(getcwd(), s:slash, '/'), '/\=$', '/', '') . absolute
|
||||
endif
|
||||
return s:UriFromPath(absolute)
|
||||
endfunction
|
||||
|
||||
function! s:UriFromPath(absolute) abort
|
||||
let absolute = a:absolute
|
||||
if has('win32') && absolute =~# '^\a://\@!'
|
||||
return 'file:///' . strpart(absolute, 0, 2) . s:UrlEncode(strpart(absolute, 2))
|
||||
elseif absolute =~# '^/'
|
||||
return 'file://' . s:UrlEncode(absolute)
|
||||
elseif absolute =~# '^\a[[:alnum:].+-]*:\|^$'
|
||||
return absolute
|
||||
else
|
||||
return ''
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:BufferText(bufnr) abort
|
||||
return join(getbufline(a:bufnr, 1, '$'), "\n") . "\n"
|
||||
endfunction
|
||||
|
||||
let s:valid_request_key = '^\%(id\|method\|params\)$'
|
||||
function! s:SendRequest(instance, request, ...) abort
|
||||
if !has_key(a:instance, 'job') || get(a:instance, 'shutdown', a:request) isnot# a:request
|
||||
return s:RejectRequest(a:request, s:error_connection_inactive)
|
||||
endif
|
||||
let json = filter(copy(a:request), 'v:key =~# s:valid_request_key')
|
||||
if empty(s:Send(a:instance, json)) && has_key(a:request, 'id') && has_key(a:instance.requests, a:request.id)
|
||||
call s:RejectRequest(remove(a:instance.requests, a:request.id), {'code': -32099, 'message': 'Write failed'})
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:RegisterWorkspaceFolderForBuffer(instance, buf) abort
|
||||
let root = getbufvar(a:buf, 'workspace_folder')
|
||||
if type(root) != v:t_string
|
||||
return
|
||||
endif
|
||||
let root = s:UriFromPath(substitute(root, '[\/]$', '', ''))
|
||||
if empty(root) || has_key(a:instance.workspaceFolders, root)
|
||||
return
|
||||
endif
|
||||
let a:instance.workspaceFolders[root] = v:true
|
||||
call a:instance.Notify('workspace/didChangeWorkspaceFolders', {'event': {'added': [{'uri': root, 'name': fnamemodify(root, ':t')}], 'removed': []}})
|
||||
endfunction
|
||||
|
||||
function! s:PreprocessParams(instance, params) abort
|
||||
let bufnr = v:null
|
||||
for doc in filter([get(a:params, 'textDocument', {})], 'type(get(v:val, "uri", "")) == v:t_number')
|
||||
let bufnr = doc.uri
|
||||
call s:RegisterWorkspaceFolderForBuffer(a:instance, bufnr)
|
||||
call extend(doc, a:instance.Attach(bufnr))
|
||||
endfor
|
||||
let progress_tokens = []
|
||||
for key in keys(a:params)
|
||||
if key =~# 'Token$' && type(a:params[key]) == v:t_func
|
||||
let s:progress_token_id += 1
|
||||
let a:instance.progress[s:progress_token_id] = a:params[key]
|
||||
call add(progress_tokens, s:progress_token_id)
|
||||
let a:params[key] = s:progress_token_id
|
||||
endif
|
||||
endfor
|
||||
return [bufnr, progress_tokens]
|
||||
endfunction
|
||||
|
||||
function! s:VimAttach(bufnr) dict abort
|
||||
if !bufloaded(a:bufnr)
|
||||
return {'uri': '', 'version': 0}
|
||||
endif
|
||||
let bufnr = a:bufnr
|
||||
let doc = {
|
||||
\ 'uri': s:UriFromBufnr(bufnr),
|
||||
\ 'version': getbufvar(bufnr, 'changedtick', 0),
|
||||
\ 'languageId': getbufvar(bufnr, '&filetype'),
|
||||
\ }
|
||||
if has_key(self.open_buffers, bufnr) && (
|
||||
\ self.open_buffers[bufnr].uri !=# doc.uri ||
|
||||
\ self.open_buffers[bufnr].languageId !=# doc.languageId)
|
||||
call self.Notify('textDocument/didClose', {'textDocument': {'uri': self.open_buffers[bufnr].uri}})
|
||||
call remove(self.open_buffers, bufnr)
|
||||
endif
|
||||
if !has_key(self.open_buffers, bufnr)
|
||||
call self.Notify('textDocument/didOpen', {'textDocument': extend({'text': s:BufferText(bufnr)}, doc)})
|
||||
let self.open_buffers[bufnr] = doc
|
||||
else
|
||||
call self.Notify('textDocument/didChange', {
|
||||
\ 'textDocument': {'uri': doc.uri, 'version': doc.version},
|
||||
\ 'contentChanges': [{'text': s:BufferText(bufnr)}]})
|
||||
let self.open_buffers[bufnr].version = doc.version
|
||||
endif
|
||||
return doc
|
||||
endfunction
|
||||
|
||||
function! s:VimIsAttached(bufnr) dict abort
|
||||
return bufloaded(a:bufnr) && has_key(self.open_buffers, a:bufnr) ? v:true : v:false
|
||||
endfunction
|
||||
|
||||
function! s:VimRequest(method, params, ...) dict abort
|
||||
let s:id += 1
|
||||
let params = deepcopy(a:params)
|
||||
let [_, progress] = s:PreprocessParams(self, params)
|
||||
let request = call('s:SetUpRequest', [self, s:id, a:method, params, progress] + a:000)
|
||||
call self.AfterInitialized(function('s:SendRequest', [self, request]))
|
||||
let self.requests[s:id] = request
|
||||
return request
|
||||
endfunction
|
||||
|
||||
function! s:Call(method, params, ...) dict abort
|
||||
let request = call(self.Request, [a:method, a:params] + a:000)
|
||||
if a:0
|
||||
return request
|
||||
endif
|
||||
return request.Await()
|
||||
endfunction
|
||||
|
||||
function! s:Cancel(request) dict abort
|
||||
if has_key(self.requests, get(a:request, 'id', ''))
|
||||
call self.Notify('$/cancelRequest', {'id': a:request.id})
|
||||
call s:RejectRequest(remove(self.requests, a:request.id), s:error_canceled)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:RequestCancel() dict abort
|
||||
let instance = self.Client()
|
||||
if !empty(instance)
|
||||
call instance.Cancel(self)
|
||||
elseif get(self, 'status', '') ==# 'running'
|
||||
call s:RejectRequest(self, s:error_canceled)
|
||||
endif
|
||||
return self
|
||||
endfunction
|
||||
|
||||
function! s:DispatchMessage(instance, method, handler, id, params, ...) abort
|
||||
try
|
||||
let response = {'result': call(a:handler, [a:params, a:instance])}
|
||||
if response.result is# 0
|
||||
let response.result = v:null
|
||||
endif
|
||||
catch
|
||||
call copilot#logger#Exception('lsp.request.' . a:method)
|
||||
let response = {'error': {'code': -32000, 'message': v:exception}}
|
||||
endtry
|
||||
if a:id isnot# v:null
|
||||
call s:Send(a:instance, extend({'id': a:id}, response))
|
||||
endif
|
||||
if !has_key(s:notifications, a:method)
|
||||
return response
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:OnMessage(instance, body, ...) abort
|
||||
if !has_key(a:body, 'method')
|
||||
return s:OnResponse(a:instance, a:body)
|
||||
endif
|
||||
let request = a:body
|
||||
let id = get(request, 'id', v:null)
|
||||
let params = get(request, 'params', v:null)
|
||||
if has_key(a:instance.methods, request.method)
|
||||
return s:DispatchMessage(a:instance, request.method, a:instance.methods[request.method], id, params)
|
||||
elseif id isnot# v:null
|
||||
call s:Send(a:instance, {"id": id, "error": {"code": -32700, "message": "Method not found: " . request.method}})
|
||||
call copilot#logger#Debug('Unexpected request ' . request.method . ' called with ' . json_encode(params))
|
||||
elseif request.method !~# '^\$/'
|
||||
call copilot#logger#Debug('Unexpected notification ' . request.method . ' called with ' . json_encode(params))
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:OnResponse(instance, response, ...) abort
|
||||
let response = a:response
|
||||
let id = get(a:response, 'id', v:null)
|
||||
if !has_key(a:instance.requests, id)
|
||||
return
|
||||
endif
|
||||
let request = remove(a:instance.requests, id)
|
||||
for progress_token in request.progress
|
||||
if has_key(a:instance.progress, progress_token)
|
||||
call remove(a:instance.progress, progress_token)
|
||||
endif
|
||||
endfor
|
||||
if request.status !=# 'running'
|
||||
return
|
||||
endif
|
||||
if has_key(response, 'result')
|
||||
let request.waiting = {}
|
||||
let resolve = remove(request, 'resolve')
|
||||
call remove(request, 'reject')
|
||||
let request.status = 'success'
|
||||
let request.result = response.result
|
||||
for Cb in resolve
|
||||
let request.waiting[timer_start(0, function('s:Callback', [request, 'result', Cb]))] = 1
|
||||
endfor
|
||||
else
|
||||
call s:RejectRequest(request, response.error)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:OnErr(instance, ch, line, ...) abort
|
||||
if !has_key(a:instance, 'serverInfo')
|
||||
call copilot#logger#Bare('<-! ' . a:line)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:OnExit(instance, code, ...) abort
|
||||
let a:instance.exit_status = a:code
|
||||
if has_key(a:instance, 'job')
|
||||
call remove(a:instance, 'job')
|
||||
endif
|
||||
if has_key(a:instance, 'client_id')
|
||||
call remove(a:instance, 'client_id')
|
||||
endif
|
||||
let message = 'Process exited with status ' . a:code
|
||||
if a:code >= 18 && a:code < 100
|
||||
let message = 'Node.js too old. ' .
|
||||
\ (get(a:instance.node, 0, 'node') ==# 'node' ? 'Upgrade' : 'Change g:copilot_node_command') .
|
||||
\ ' to ' . a:code . '.x or newer'
|
||||
endif
|
||||
if !has_key(a:instance, 'serverInfo') && !has_key(a:instance, 'startup_error')
|
||||
let a:instance.startup_error = message
|
||||
endif
|
||||
for id in sort(keys(a:instance.requests), { a, b -> +a > +b })
|
||||
call s:RejectRequest(remove(a:instance.requests, id), s:error_exit)
|
||||
endfor
|
||||
if has_key(a:instance, 'after_initialized')
|
||||
let a:instance.AfterInitialized = function('copilot#util#Defer')
|
||||
for Fn in remove(a:instance, 'after_initialized')
|
||||
call copilot#util#Defer(Fn)
|
||||
endfor
|
||||
endif
|
||||
call copilot#util#Defer({ -> get(s:instances, a:instance.id) is# a:instance ? remove(s:instances, a:instance.id) : {} })
|
||||
if a:code == 0
|
||||
call copilot#logger#Info(message)
|
||||
else
|
||||
call copilot#logger#Warn(message)
|
||||
if !has_key(a:instance, 'kill')
|
||||
call copilot#util#Defer(function('s:Warn'), message)
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#client#LspInit(id, initialize_result) abort
|
||||
if !has_key(s:instances, a:id)
|
||||
return
|
||||
endif
|
||||
call s:PostInit(a:initialize_result, s:instances[a:id])
|
||||
endfunction
|
||||
|
||||
function! copilot#client#LspExit(id, code, signal) abort
|
||||
if !has_key(s:instances, a:id)
|
||||
return
|
||||
endif
|
||||
let instance = remove(s:instances, a:id)
|
||||
call s:OnExit(instance, a:code)
|
||||
endfunction
|
||||
|
||||
function! copilot#client#LspResponse(id, opts, ...) abort
|
||||
if !has_key(s:instances, a:id)
|
||||
return
|
||||
endif
|
||||
call s:OnResponse(s:instances[a:id], a:opts)
|
||||
endfunction
|
||||
|
||||
function! s:NvimAttach(bufnr) dict abort
|
||||
if !bufloaded(a:bufnr)
|
||||
return {'uri': '', 'version': 0}
|
||||
endif
|
||||
call luaeval('pcall(vim.lsp.buf_attach_client, _A[1], _A[2])', [a:bufnr, self.id])
|
||||
return luaeval('{uri = vim.uri_from_bufnr(_A), version = vim.lsp.util.buf_versions[_A]}', a:bufnr)
|
||||
endfunction
|
||||
|
||||
function! s:NvimIsAttached(bufnr) dict abort
|
||||
return bufloaded(a:bufnr) ? luaeval('vim.lsp.buf_is_attached(_A[1], _A[2])', [a:bufnr, self.id]) : v:false
|
||||
endfunction
|
||||
|
||||
function! s:NvimRequest(method, params, ...) dict abort
|
||||
let params = deepcopy(a:params)
|
||||
let [bufnr, progress] = s:PreprocessParams(self, params)
|
||||
let request = call('s:SetUpRequest', [self, v:null, a:method, params, progress] + a:000)
|
||||
call self.AfterInitialized(function('s:NvimDoRequest', [self, request, bufnr]))
|
||||
return request
|
||||
endfunction
|
||||
|
||||
function! s:NvimDoRequest(client, request, bufnr) abort
|
||||
let request = a:request
|
||||
if has_key(a:client, 'client_id') && !has_key(a:client, 'kill')
|
||||
let request.id = eval("v:lua.require'_copilot'.lsp_request(a:client.id, a:request.method, a:request.params, a:bufnr)")
|
||||
endif
|
||||
if request.id isnot# v:null
|
||||
let a:client.requests[request.id] = request
|
||||
else
|
||||
if has_key(a:client, 'client_id')
|
||||
call copilot#client#LspExit(a:client.client_id, -1, -1)
|
||||
endif
|
||||
call copilot#util#Defer(function('s:RejectRequest'), request, s:error_connection_inactive)
|
||||
endif
|
||||
return request
|
||||
endfunction
|
||||
|
||||
function! s:NvimClose() dict abort
|
||||
if !has_key(self, 'client_id')
|
||||
return
|
||||
endif
|
||||
let self.kill = v:true
|
||||
return luaeval('vim.lsp.get_client_by_id(_A).stop()', self.client_id)
|
||||
endfunction
|
||||
|
||||
function! s:NvimNotify(method, params) dict abort
|
||||
call self.AfterInitialized(function('s:NvimDoNotify', [self.client_id, a:method, a:params]))
|
||||
endfunction
|
||||
|
||||
function! s:NvimDoNotify(client_id, method, params) abort
|
||||
return eval("v:lua.require'_copilot'.rpc_notify(a:client_id, a:method, a:params)")
|
||||
endfunction
|
||||
|
||||
function! copilot#client#LspHandle(id, request) abort
|
||||
if !has_key(s:instances, a:id)
|
||||
return
|
||||
endif
|
||||
return s:OnMessage(s:instances[a:id], a:request)
|
||||
endfunction
|
||||
|
||||
let s:script_name = 'dist/language-server.js'
|
||||
function! s:Command() abort
|
||||
if !has('nvim-0.7') && v:version < 900
|
||||
return [[], [], 'Vim version too old']
|
||||
endif
|
||||
let script = get(g:, 'copilot_command', '')
|
||||
if type(script) == type('')
|
||||
let script = [expand(script)]
|
||||
endif
|
||||
if empty(script) || !filereadable(script[0])
|
||||
let script = [s:root . '/' . s:script_name]
|
||||
if !filereadable(script[0])
|
||||
return [[], [], 'Could not find ' . s:script_name . ' (bad install?)']
|
||||
endif
|
||||
elseif script[0] !~# '\.js$'
|
||||
return [[], script + ['--stdio'], '']
|
||||
endif
|
||||
let node = get(g:, 'copilot_node_command', '')
|
||||
if empty(node)
|
||||
let node = ['node']
|
||||
elseif type(node) == type('')
|
||||
let node = [expand(node)]
|
||||
endif
|
||||
if !executable(get(node, 0, ''))
|
||||
if get(node, 0, '') ==# 'node'
|
||||
return [[], [], 'Node.js not found in PATH']
|
||||
else
|
||||
return [[], [], 'Node.js executable `' . get(node, 0, '') . "' not found"]
|
||||
endif
|
||||
endif
|
||||
return [node, script + ['--stdio'], '']
|
||||
endfunction
|
||||
|
||||
function! s:UrlDecode(str) abort
|
||||
return substitute(a:str, '%\(\x\x\)', '\=iconv(nr2char("0x".submatch(1)), "utf-8", "latin1")', 'g')
|
||||
endfunction
|
||||
|
||||
function! copilot#client#EditorInfo() abort
|
||||
if !exists('s:editor_version')
|
||||
if has('nvim')
|
||||
let s:editor_version = matchstr(execute('version'), 'NVIM v\zs[^[:space:]]\+')
|
||||
else
|
||||
let s:editor_version = (v:version / 100) . '.' . (v:version % 100) . (exists('v:versionlong') ? printf('.%04d', v:versionlong % 10000) : '')
|
||||
endif
|
||||
endif
|
||||
return {'name': has('nvim') ? 'Neovim': 'Vim', 'version': s:editor_version}
|
||||
endfunction
|
||||
|
||||
function! copilot#client#EditorPluginInfo() abort
|
||||
return {'name': 'copilot.vim', 'version': s:plugin_version}
|
||||
endfunction
|
||||
|
||||
function! copilot#client#Settings() abort
|
||||
let settings = {
|
||||
\ 'http': {
|
||||
\ 'proxy': get(g:, 'copilot_proxy', v:null),
|
||||
\ 'proxyStrictSSL': get(g:, 'copilot_proxy_strict_ssl', v:null)},
|
||||
\ 'github-enterprise': {'uri': get(g:, 'copilot_auth_provider_url', v:null)},
|
||||
\ }
|
||||
if type(settings.http.proxy) ==# v:t_string && settings.http.proxy =~# '^[^/]\+$'
|
||||
let settings.http.proxy = 'http://' . settings.http.proxy
|
||||
endif
|
||||
if type(get(g:, 'copilot_settings')) == v:t_dict
|
||||
call extend(settings, g:copilot_settings)
|
||||
endif
|
||||
return settings
|
||||
endfunction
|
||||
|
||||
function! s:PostInit(result, instance) abort
|
||||
let a:instance.serverInfo = get(a:result, 'serverInfo', {})
|
||||
if !has_key(a:instance, 'node_version') && has_key(a:result.serverInfo, 'nodeVersion')
|
||||
let a:instance.node_version = a:result.serverInfo.nodeVersion
|
||||
endif
|
||||
let a:instance.AfterInitialized = function('copilot#util#Defer')
|
||||
for Fn in remove(a:instance, 'after_initialized')
|
||||
call copilot#util#Defer(Fn)
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:InitializeResult(result, instance) abort
|
||||
call s:Send(a:instance, {'method': 'initialized', 'params': {}})
|
||||
call s:PostInit(a:result, a:instance)
|
||||
endfunction
|
||||
|
||||
function! s:InitializeError(error, instance) abort
|
||||
if !has_key(a:instance, 'startup_error')
|
||||
let a:instance.startup_error = 'Unexpected error E' . a:error.code . ' initializing language server: ' . a:error.message
|
||||
call a:instance.Close()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:StartupError() dict abort
|
||||
while (has_key(self, 'job') || has_key(self, 'client_id')) && !has_key(self, 'startup_error') && !has_key(self, 'serverInfo')
|
||||
sleep 10m
|
||||
endwhile
|
||||
if has_key(self, 'serverInfo')
|
||||
return ''
|
||||
else
|
||||
return get(self, 'startup_error', 'Something unexpected went wrong spawning the language server')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:StatusNotification(params, instance) abort
|
||||
let a:instance.status = a:params
|
||||
endfunction
|
||||
|
||||
function! s:Nop(...) abort
|
||||
return v:null
|
||||
endfunction
|
||||
|
||||
function! s:False(...) abort
|
||||
return v:false
|
||||
endfunction
|
||||
|
||||
function! s:Progress(params, instance) abort
|
||||
if has_key(a:instance.progress, a:params.token)
|
||||
call a:instance.progress[a:params.token](a:params.value)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
let s:notifications = {
|
||||
\ '$/progress': function('s:Progress'),
|
||||
\ 'featureFlagsNotification': function('s:Nop'),
|
||||
\ 'statusNotification': function('s:StatusNotification'),
|
||||
\ 'window/logMessage': function('copilot#handlers#window_logMessage'),
|
||||
\ }
|
||||
|
||||
let s:vim_handlers = {
|
||||
\ 'window/showMessageRequest': function('copilot#handlers#window_showMessageRequest'),
|
||||
\ 'window/showDocument': function('copilot#handlers#window_showDocument'),
|
||||
\ }
|
||||
|
||||
let s:vim_capabilities = {
|
||||
\ 'workspace': {'workspaceFolders': v:true},
|
||||
\ 'window': {'showDocument': {'support': v:true}},
|
||||
\ }
|
||||
|
||||
function! copilot#client#New(...) abort
|
||||
let opts = a:0 ? a:1 : {}
|
||||
let instance = {'requests': {},
|
||||
\ 'progress': {},
|
||||
\ 'workspaceFolders': {},
|
||||
\ 'after_initialized': [],
|
||||
\ 'status': {'status': 'Starting', 'message': ''},
|
||||
\ 'AfterInitialized': function('s:AfterInitialized'),
|
||||
\ 'Close': function('s:Nop'),
|
||||
\ 'Notify': function('s:False'),
|
||||
\ 'Request': function('s:VimRequest'),
|
||||
\ 'Attach': function('s:Nop'),
|
||||
\ 'IsAttached': function('s:False'),
|
||||
\ 'Call': function('s:Call'),
|
||||
\ 'Cancel': function('s:Cancel'),
|
||||
\ 'StartupError': function('s:StartupError'),
|
||||
\ }
|
||||
let instance.methods = copy(s:notifications)
|
||||
let [node, argv, command_error] = s:Command()
|
||||
if !empty(command_error)
|
||||
let instance.id = -1
|
||||
let instance.startup_error = command_error
|
||||
call copilot#logger#Error(command_error)
|
||||
return instance
|
||||
endif
|
||||
let instance.node = node
|
||||
let command = node + argv
|
||||
let opts = {}
|
||||
let opts.initializationOptions = {
|
||||
\ 'editorInfo': copilot#client#EditorInfo(),
|
||||
\ 'editorPluginInfo': copilot#client#EditorPluginInfo(),
|
||||
\ }
|
||||
let opts.workspaceFolders = []
|
||||
let settings = extend(copilot#client#Settings(), get(opts, 'editorConfiguration', {}))
|
||||
if type(get(g:, 'copilot_workspace_folders')) == v:t_list
|
||||
for folder in g:copilot_workspace_folders
|
||||
if type(folder) == v:t_string && !empty(folder) && folder !~# '\*\*\|^/$'
|
||||
for path in glob(folder . '/', 0, 1)
|
||||
let uri = s:UriFromPath(substitute(path, '[\/]*$', '', ''))
|
||||
call add(opts.workspaceFolders, {'uri': uri, 'name': fnamemodify(uri, ':t')})
|
||||
endfor
|
||||
elseif type(folder) == v:t_dict && has_key(v:t_dict, 'uri') && !empty(folder.uri) && has_key(folder, 'name')
|
||||
call add(opts.workspaceFolders, folder)
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
for folder in opts.workspaceFolders
|
||||
let instance.workspaceFolders[folder.uri] = v:true
|
||||
endfor
|
||||
if has('nvim')
|
||||
call extend(instance, {
|
||||
\ 'Close': function('s:NvimClose'),
|
||||
\ 'Notify': function('s:NvimNotify'),
|
||||
\ 'Request': function('s:NvimRequest'),
|
||||
\ 'Attach': function('s:NvimAttach'),
|
||||
\ 'IsAttached': function('s:NvimIsAttached'),
|
||||
\ })
|
||||
let instance.client_id = eval("v:lua.require'_copilot'.lsp_start_client(command, keys(instance.methods), opts, settings)")
|
||||
let instance.id = instance.client_id
|
||||
else
|
||||
call extend(instance, {
|
||||
\ 'Close': function('s:VimClose'),
|
||||
\ 'Notify': function('s:VimNotify'),
|
||||
\ 'Attach': function('s:VimAttach'),
|
||||
\ 'IsAttached': function('s:VimIsAttached'),
|
||||
\ })
|
||||
let state = {'headers': {}, 'mode': 'headers', 'buffer': ''}
|
||||
let instance.open_buffers = {}
|
||||
let instance.methods = extend(s:vim_handlers, instance.methods)
|
||||
let instance.job = job_start(command, {
|
||||
\ 'cwd': copilot#job#Cwd(),
|
||||
\ 'noblock': 1,
|
||||
\ 'stoponexit': '',
|
||||
\ 'in_mode': 'lsp',
|
||||
\ 'out_mode': 'lsp',
|
||||
\ 'out_cb': { j, d -> copilot#util#Defer(function('s:OnMessage'), instance, d) },
|
||||
\ 'err_cb': function('s:OnErr', [instance]),
|
||||
\ 'exit_cb': { j, d -> copilot#util#Defer(function('s:OnExit'), instance, d) },
|
||||
\ })
|
||||
let instance.id = job_info(instance.job).process
|
||||
let opts.capabilities = s:vim_capabilities
|
||||
let opts.processId = getpid()
|
||||
let request = instance.Request('initialize', opts, function('s:InitializeResult'), function('s:InitializeError'), instance)
|
||||
call call(remove(instance.after_initialized, 0), [])
|
||||
call instance.Notify('workspace/didChangeConfiguration', {'settings': settings})
|
||||
endif
|
||||
let s:instances[instance.id] = instance
|
||||
return instance
|
||||
endfunction
|
||||
|
||||
function! copilot#client#Cancel(request) abort
|
||||
if type(a:request) == type({}) && has_key(a:request, 'Cancel')
|
||||
call a:request.Cancel()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:Callback(request, type, callback, timer) abort
|
||||
call remove(a:request.waiting, a:timer)
|
||||
if has_key(a:request, a:type)
|
||||
call a:callback(a:request[a:type])
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#client#Result(request, callback) abort
|
||||
if has_key(a:request, 'resolve')
|
||||
call add(a:request.resolve, a:callback)
|
||||
elseif has_key(a:request, 'result')
|
||||
let a:request.waiting[timer_start(0, function('s:Callback', [a:request, 'result', a:callback]))] = 1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#client#Error(request, callback) abort
|
||||
if has_key(a:request, 'reject')
|
||||
call add(a:request.reject, a:callback)
|
||||
elseif has_key(a:request, 'error')
|
||||
let a:request.waiting[timer_start(0, function('s:Callback', [a:request, 'error', a:callback]))] = 1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:CloseBuffer(bufnr) abort
|
||||
for instance in values(s:instances)
|
||||
try
|
||||
if has_key(instance, 'job') && has_key(instance.open_buffers, a:bufnr)
|
||||
let buffer = remove(instance.open_buffers, a:bufnr)
|
||||
call instance.Notify('textDocument/didClose', {'textDocument': {'uri': buffer.uri}})
|
||||
endif
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
endtry
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
augroup copilot_close
|
||||
autocmd!
|
||||
if !has('nvim')
|
||||
autocmd BufUnload * call s:CloseBuffer(+expand('<abuf>'))
|
||||
endif
|
||||
augroup END
|
@ -1,116 +0,0 @@
|
||||
if exists('g:autoloaded_copilot_prompt')
|
||||
finish
|
||||
endif
|
||||
let g:autoloaded_copilot_prompt = 1
|
||||
|
||||
scriptencoding utf-8
|
||||
|
||||
let s:slash = exists('+shellslash') ? '\' : '/'
|
||||
|
||||
function copilot#doc#UTF16Width(str) abort
|
||||
return strchars(substitute(a:str, "\\%#=2[^\u0001-\uffff]", " ", 'g'))
|
||||
endfunction
|
||||
|
||||
if exists('*utf16idx')
|
||||
|
||||
function! copilot#doc#UTF16ToByteIdx(str, utf16_idx) abort
|
||||
return byteidx(a:str, a:utf16_idx, 1)
|
||||
endfunction
|
||||
|
||||
elseif has('nvim')
|
||||
|
||||
function! copilot#doc#UTF16ToByteIdx(str, utf16_idx) abort
|
||||
try
|
||||
return v:lua.vim.str_byteindex(a:str, a:utf16_idx, 1)
|
||||
catch /^Vim(return):E5108:/
|
||||
return -1
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
else
|
||||
|
||||
function! copilot#doc#UTF16ToByteIdx(str, utf16_idx) abort
|
||||
if copilot#doc#UTF16Width(a:str) < a:utf16_idx
|
||||
return -1
|
||||
endif
|
||||
let end_offset = len(a:str)
|
||||
while copilot#doc#UTF16Width(strpart(a:str, 0, end_offset)) > a:utf16_idx && end_offset > 0
|
||||
let end_offset -= 1
|
||||
endwhile
|
||||
return end_offset
|
||||
endfunction
|
||||
|
||||
endif
|
||||
|
||||
|
||||
let s:language_normalization_map = {
|
||||
\ "bash": "shellscript",
|
||||
\ "bst": "bibtex",
|
||||
\ "cs": "csharp",
|
||||
\ "cuda": "cuda-cpp",
|
||||
\ "dosbatch": "bat",
|
||||
\ "dosini": "ini",
|
||||
\ "gitcommit": "git-commit",
|
||||
\ "gitrebase": "git-rebase",
|
||||
\ "make": "makefile",
|
||||
\ "objc": "objective-c",
|
||||
\ "objcpp": "objective-cpp",
|
||||
\ "ps1": "powershell",
|
||||
\ "raku": "perl6",
|
||||
\ "sh": "shellscript",
|
||||
\ "text": "plaintext",
|
||||
\ }
|
||||
function copilot#doc#LanguageForFileType(filetype) abort
|
||||
let filetype = substitute(a:filetype, '\..*', '', '')
|
||||
return get(s:language_normalization_map, empty(filetype) ? "text" : filetype, filetype)
|
||||
endfunction
|
||||
|
||||
function! s:RelativePath(absolute) abort
|
||||
if exists('b:copilot_relative_path')
|
||||
return b:copilot_relative_path
|
||||
elseif exists('b:copilot_root')
|
||||
let root = b:copilot_root
|
||||
elseif len(get(b:, 'projectionist', {}))
|
||||
let root = sort(keys(b:projectionist), { a, b -> a < b })[0]
|
||||
else
|
||||
let root = getcwd()
|
||||
endif
|
||||
let root = tr(root, s:slash, '/') . '/'
|
||||
if strpart(tr(a:absolute, 'A-Z', 'a-z'), 0, len(root)) ==# tr(root, 'A-Z', 'a-z')
|
||||
return strpart(a:absolute, len(root))
|
||||
else
|
||||
return fnamemodify(a:absolute, ':t')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#doc#Get() abort
|
||||
let absolute = tr(@%, s:slash, '/')
|
||||
if absolute !~# '^\a\+:\|^/\|^$' && &buftype =~# '^\%(nowrite\)\=$'
|
||||
let absolute = substitute(tr(getcwd(), s:slash, '/'), '/\=$', '/', '') . absolute
|
||||
endif
|
||||
let doc = {
|
||||
\ 'uri': bufnr(''),
|
||||
\ 'version': getbufvar('', 'changedtick'),
|
||||
\ 'relativePath': s:RelativePath(absolute),
|
||||
\ 'insertSpaces': &expandtab ? v:true : v:false,
|
||||
\ 'tabSize': shiftwidth(),
|
||||
\ 'indentSize': shiftwidth(),
|
||||
\ }
|
||||
let line = getline('.')
|
||||
let col_byte = col('.') - (mode() =~# '^[iR]' || empty(line))
|
||||
let col_utf16 = copilot#doc#UTF16Width(strpart(line, 0, col_byte))
|
||||
let doc.position = {'line': line('.') - 1, 'character': col_utf16}
|
||||
return doc
|
||||
endfunction
|
||||
|
||||
function! copilot#doc#Params(...) abort
|
||||
let extra = a:0 ? a:1 : {}
|
||||
let params = extend({'doc': extend(copilot#doc#Get(), get(extra, 'doc', {}))}, extra, 'keep')
|
||||
let params.textDocument = {
|
||||
\ 'uri': params.doc.uri,
|
||||
\ 'version': params.doc.version,
|
||||
\ 'relativePath': params.doc.relativePath,
|
||||
\ }
|
||||
let params.position = params.doc.position
|
||||
return params
|
||||
endfunction
|
31
sources_non_forked/copilot.vim/autoload/copilot/handlers.vim
Normal file
31
sources_non_forked/copilot.vim/autoload/copilot/handlers.vim
Normal file
@ -0,0 +1,31 @@
|
||||
function! copilot#handlers#window_logMessage(params, ...) abort
|
||||
call copilot#logger#Raw(get(a:params, 'type', 6), get(a:params, 'message', ''))
|
||||
endfunction
|
||||
|
||||
function! copilot#handlers#window_showMessageRequest(params, ...) abort
|
||||
let choice = inputlist([a:params.message . "\n\nRequest Actions:"] +
|
||||
\ map(copy(get(a:params, 'actions', [])), { i, v -> (i + 1) . '. ' . v.title}))
|
||||
return choice > 0 ? get(a:params.actions, choice - 1, v:null) : v:null
|
||||
endfunction
|
||||
|
||||
function! s:BrowserCallback(into, code) abort
|
||||
let a:into.code = a:code
|
||||
endfunction
|
||||
|
||||
function! copilot#handlers#window_showDocument(params, ...) abort
|
||||
echo a:params.uri
|
||||
if empty(get(a:params, 'external'))
|
||||
return {'success': v:false}
|
||||
endif
|
||||
let browser = copilot#Browser()
|
||||
if empty(browser)
|
||||
return {'success': v:false}
|
||||
endif
|
||||
let status = {}
|
||||
call copilot#job#Stream(browser + [a:params.uri], v:null, v:null, function('s:BrowserCallback', [status]))
|
||||
let time = reltime()
|
||||
while empty(status) && reltimefloat(reltime(time)) < 1
|
||||
sleep 10m
|
||||
endwhile
|
||||
return {'success': get(status, 'code') ? v:false : v:true}
|
||||
endfunction
|
@ -1,11 +1,6 @@
|
||||
if exists('g:autoloaded_copilot_job')
|
||||
finish
|
||||
endif
|
||||
let g:autoloaded_copilot_job = 1
|
||||
|
||||
scriptencoding utf-8
|
||||
|
||||
function copilot#job#Nop(...) abort
|
||||
function! copilot#job#Nop(...) abort
|
||||
endfunction
|
||||
|
||||
function! s:Jobs(job_or_jobs) abort
|
||||
|
@ -1,8 +1,3 @@
|
||||
if exists('g:autoloaded_copilot_log')
|
||||
finish
|
||||
endif
|
||||
let g:autoloaded_copilot_log = 1
|
||||
|
||||
if !exists('s:log_file')
|
||||
let s:log_file = tempname() . '-copilot.log'
|
||||
try
|
||||
@ -11,35 +6,59 @@ if !exists('s:log_file')
|
||||
endtry
|
||||
endif
|
||||
|
||||
function! copilot#logger#File() abort
|
||||
return s:log_file
|
||||
let s:logs = []
|
||||
|
||||
function! copilot#logger#BufReadCmd() abort
|
||||
try
|
||||
setlocal modifiable noreadonly
|
||||
silent call deletebufline('', 1, '$')
|
||||
if !empty(s:logs)
|
||||
call setline(1, s:logs)
|
||||
endif
|
||||
finally
|
||||
setlocal buftype=nofile bufhidden=wipe nobuflisted nomodified nomodifiable
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
let s:level_prefixes = ['', '[ERROR] ', '[WARN] ', '[INFO] ', '[DEBUG] ', '[DEBUG] ']
|
||||
|
||||
function! copilot#logger#Raw(level, message) abort
|
||||
if $COPILOT_AGENT_VERBOSE !~# '^\%(1\|true\)$' && a:level < 1
|
||||
return
|
||||
endif
|
||||
let lines = type(a:message) == v:t_list ? copy(a:message) : split(a:message, "\n", 1)
|
||||
let lines[0] = strftime('[%Y-%m-%d %H:%M:%S] ') . get(s:level_prefixes, a:level, '[UNKNOWN] ') . get(lines, 0, '')
|
||||
try
|
||||
if !filewritable(s:log_file)
|
||||
return
|
||||
endif
|
||||
call map(lines, { k, L -> type(L) == v:t_func ? call(L, []) : L })
|
||||
call writefile(lines, s:log_file, 'a')
|
||||
call extend(s:logs, lines)
|
||||
let overflow = len(s:logs) - get(g:, 'copilot_log_history', 10000)
|
||||
if overflow > 0
|
||||
call remove(s:logs, 0, overflow - 1)
|
||||
endif
|
||||
let bufnr = bufnr('copilot:///log')
|
||||
if bufnr > 0 && bufloaded(bufnr)
|
||||
call setbufvar(bufnr, '&modifiable', 1)
|
||||
call setbufline(bufnr, 1, s:logs)
|
||||
call setbufvar(bufnr, '&modifiable', 0)
|
||||
for winid in win_findbuf(bufnr)
|
||||
if has('nvim') && winid != win_getid()
|
||||
call nvim_win_set_cursor(winid, [len(s:logs), 0])
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
catch
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! copilot#logger#Trace(...) abort
|
||||
call copilot#logger#Raw(-1, a:000)
|
||||
endfunction
|
||||
|
||||
function! copilot#logger#Debug(...) abort
|
||||
call copilot#logger#Raw(0, a:000)
|
||||
if empty(get(g:, 'copilot_debug'))
|
||||
return
|
||||
endif
|
||||
call copilot#logger#Raw(4, a:000)
|
||||
endfunction
|
||||
|
||||
function! copilot#logger#Info(...) abort
|
||||
call copilot#logger#Raw(1, a:000)
|
||||
call copilot#logger#Raw(3, a:000)
|
||||
endfunction
|
||||
|
||||
function! copilot#logger#Warn(...) abort
|
||||
@ -47,26 +66,40 @@ function! copilot#logger#Warn(...) abort
|
||||
endfunction
|
||||
|
||||
function! copilot#logger#Error(...) abort
|
||||
call copilot#logger#Raw(3, a:000)
|
||||
call copilot#logger#Raw(1, a:000)
|
||||
endfunction
|
||||
|
||||
function! copilot#logger#Exception() abort
|
||||
function! copilot#logger#Bare(...) abort
|
||||
call copilot#logger#Raw(0, a:000)
|
||||
endfunction
|
||||
|
||||
function! copilot#logger#Exception(...) abort
|
||||
if !empty(v:exception) && v:exception !=# 'Vim:Interrupt'
|
||||
call copilot#logger#Error('Exception: ' . v:exception . ' @ ' . v:throwpoint)
|
||||
let agent = copilot#RunningAgent()
|
||||
if !empty(agent)
|
||||
let client = copilot#RunningClient()
|
||||
if !empty(client)
|
||||
let [_, type, code, message; __] = matchlist(v:exception, '^\%(\(^[[:alnum:]_#]\+\)\%((\a\+)\)\=\%(\(:E-\=\d\+\)\)\=:\s*\)\=\(.*\)$')
|
||||
let stacklines = []
|
||||
for frame in split(substitute(substitute(v:throwpoint, ', \S\+ \(\d\+\)$', '[\1]', ''), '^function ', '', ''), '\.\@<!\.\.\.\@!')
|
||||
if frame =~# '[\/]'
|
||||
call add(stacklines, '[redacted]')
|
||||
for frame in split(substitute(v:throwpoint, ', \S\+ \(\d\+\)$', '[\1]', ''), '\.\@<!\.\.\.\@!')
|
||||
let fn_line = matchlist(frame, '^\%(function \)\=\(\S\+\)\[\(\d\+\)\]$')
|
||||
if !empty(fn_line)
|
||||
call add(stacklines, {'function': substitute(fn_line[1], '^<SNR>\d\+_', '<SID>', ''), 'lineno': +fn_line[2]})
|
||||
elseif frame =~# ' Autocmds for "\*"$'
|
||||
call add(stacklines, {'function': frame})
|
||||
elseif frame =~# ' Autocmds for ".*"$'
|
||||
call add(stacklines, {'function': substitute(frame, ' for ".*"$', ' for "[redacted]"', '')})
|
||||
else
|
||||
call add(stacklines, substitute(frame, '^<SNR>\d\+_', '<SID>', ''))
|
||||
call add(stacklines, {'function': '[redacted]'})
|
||||
endif
|
||||
endfor
|
||||
call agent.Request('telemetry/exception', {
|
||||
\ 'origin': 'copilot.vim',
|
||||
\ 'stacktrace': join([v:exception] + stacklines, "\n")
|
||||
\ })
|
||||
return client.Request('telemetry/exception', {
|
||||
\ 'transaction': a:0 ? a:1 : '',
|
||||
\ 'platform': 'other',
|
||||
\ 'exception_detail': [{
|
||||
\ 'type': type . code,
|
||||
\ 'value': message,
|
||||
\ 'stacktrace': stacklines}]
|
||||
\ }, v:null, function('copilot#util#Nop'))
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
@ -1,8 +1,3 @@
|
||||
if exists('g:autoloaded_copilot_panel')
|
||||
finish
|
||||
endif
|
||||
let g:autoloaded_copilot_panel = 1
|
||||
|
||||
scriptencoding utf-8
|
||||
|
||||
if !exists('s:panel_id')
|
||||
@ -11,29 +6,35 @@ endif
|
||||
|
||||
let s:separator = repeat('─', 72)
|
||||
|
||||
function! s:Solutions(state) abort
|
||||
return sort(values(get(a:state, 'solutions', {})), { a, b -> a.score < b.score })
|
||||
endfunction
|
||||
|
||||
function! s:Render(panel_id) abort
|
||||
let bufnr = bufnr('^' . a:panel_id . '$')
|
||||
let state = getbufvar(bufnr, 'copilot_panel')
|
||||
if !bufloaded(bufnr) || type(state) != v:t_dict
|
||||
function! s:Render(state) abort
|
||||
let bufnr = bufnr('^' . a:state.panel . '$')
|
||||
let state = a:state
|
||||
if !bufloaded(bufnr)
|
||||
return
|
||||
endif
|
||||
let sorted = s:Solutions(state)
|
||||
if !empty(get(state, 'status', ''))
|
||||
let lines = ['Error: ' . state.status]
|
||||
let sorted = a:state.items
|
||||
if !empty(get(a:state, 'error'))
|
||||
let lines = ['Error: ' . a:state.error.message]
|
||||
let sorted = []
|
||||
elseif get(a:state, 'percentage') == 100
|
||||
let lines = ['Synthesized ' . (len(sorted) == 1 ? '1 completion' : len(sorted) . ' completions')]
|
||||
else
|
||||
let target = get(state, 'count_target', '?')
|
||||
let received = has_key(state, 'status') ? target : len(sorted)
|
||||
let lines = ['Synthesiz' . (has_key(state, 'status') ? 'ed ' : 'ing ') . received . '/' . target . ' solutions (Duplicates hidden)']
|
||||
let lines = [substitute('Synthesizing ' . matchstr(get(a:state, 'message', ''), '\d\+\%(/\d\+\)\=') . ' completions', ' \+', ' ', 'g')]
|
||||
endif
|
||||
if len(sorted)
|
||||
call add(lines, 'Press <CR> on a solution to accept')
|
||||
call add(lines, 'Press <CR> on a completion to accept')
|
||||
endif
|
||||
for solution in sorted
|
||||
let lines += [s:separator] + split(solution.displayText, "\n", 1)
|
||||
let leads = {}
|
||||
for item in sorted
|
||||
let insert = split(item.insertText, "\r\n\\=\\|\n", 1)
|
||||
let insert[0] = strpart(a:state.line, 0, copilot#util#UTF16ToByteIdx(a:state.line, item.range.start.character)) . insert[0]
|
||||
let lines += [s:separator] + insert
|
||||
if !has_key(leads, string(item.range.start))
|
||||
let match = insert[0 : a:state.position.line - item.range.start.line]
|
||||
let match[-1] = strpart(match[-1], 0, copilot#util#UTF16ToByteIdx(match[-1], a:state.position.character))
|
||||
call map(match, { k, v -> escape(v, '][^$.*\~') })
|
||||
let leads[string(item.range.start)] = join(match, '\n')
|
||||
endif
|
||||
endfor
|
||||
try
|
||||
call setbufvar(bufnr, '&modifiable', 1)
|
||||
@ -41,64 +42,61 @@ function! s:Render(panel_id) abort
|
||||
call setbufline(bufnr, 1, lines)
|
||||
finally
|
||||
call setbufvar(bufnr, '&modifiable', 0)
|
||||
call setbufvar(bufnr, '&readonly', 1)
|
||||
endtry
|
||||
call clearmatches()
|
||||
call matchadd('CopilotSuggestion', '\C^' . s:separator . '\n\zs\%(' . join(sort(values(leads), { a, b -> len(b) - len(a) }), '\|') . '\)', 10, 4)
|
||||
endfunction
|
||||
|
||||
function! copilot#panel#Solution(params, ...) abort
|
||||
let state = getbufvar('^' . a:params.panelId . '$', 'copilot_panel')
|
||||
if !bufloaded(a:params.panelId) || type(state) != v:t_dict
|
||||
return
|
||||
endif
|
||||
let state.solutions[a:params.solutionId] = a:params
|
||||
call s:Render(a:params.panelId)
|
||||
function! s:PartialResult(state, value) abort
|
||||
let items = type(a:value) == v:t_list ? a:value : a:value.items
|
||||
call extend(a:state.items, items)
|
||||
call s:Render(a:state)
|
||||
endfunction
|
||||
|
||||
function! copilot#panel#SolutionsDone(params, ...) abort
|
||||
let state = getbufvar('^' . a:params.panelId . '$', 'copilot_panel')
|
||||
if !bufloaded(a:params.panelId) || type(state) != v:t_dict
|
||||
call copilot#logger#Debug('SolutionsDone: ' . a:params.panelId)
|
||||
return
|
||||
function! s:WorkDone(state, value) abort
|
||||
if has_key(a:value, 'message')
|
||||
let a:state.message = a:value.message
|
||||
endif
|
||||
if has_key(a:value, 'percentage')
|
||||
let a:state.percentage = a:value.percentage
|
||||
call s:Render(a:state)
|
||||
endif
|
||||
let state.status = get(a:params, 'message', '')
|
||||
call s:Render(a:params.panelId)
|
||||
endfunction
|
||||
|
||||
function! copilot#panel#Accept(...) abort
|
||||
let state = get(b:, 'copilot_panel', {})
|
||||
let solutions = s:Solutions(state)
|
||||
if empty(solutions)
|
||||
if empty(state.items)
|
||||
return ''
|
||||
endif
|
||||
if !has_key(state, 'bufnr') || !bufloaded(get(state, 'bufnr', -1))
|
||||
return "echoerr 'Buffer was closed'"
|
||||
endif
|
||||
let at = a:0 ? a:1 : line('.')
|
||||
let solution_index = 0
|
||||
let index = 0
|
||||
for lnum in range(1, at)
|
||||
if getline(lnum) ==# s:separator
|
||||
let solution_index += 1
|
||||
let index += 1
|
||||
endif
|
||||
endfor
|
||||
if solution_index > 0 && solution_index <= len(solutions)
|
||||
let solution = solutions[solution_index - 1]
|
||||
let lnum = solution.range.start.line + 1
|
||||
if index > 0 && index <= len(state.items)
|
||||
let item = state.items[index - 1]
|
||||
let lnum = item.range.start.line + 1
|
||||
if getbufline(state.bufnr, lnum) !=# [state.line]
|
||||
return 'echoerr "Buffer has changed since synthesizing solution"'
|
||||
return 'echoerr "Buffer has changed since synthesizing completion"'
|
||||
endif
|
||||
let lines = split(solution.displayText, "\n", 1)
|
||||
let old_first = getline(solution.range.start.line + 1)
|
||||
let lines[0] = strpart(old_first, 0, copilot#doc#UTF16ToByteIdx(old_first, solution.range.start.character)) . lines[0]
|
||||
let old_last = getline(solution.range.end.line + 1)
|
||||
let lines[-1] .= strpart(old_last, copilot#doc#UTF16ToByteIdx(old_last, solution.range.start.character))
|
||||
call setbufline(state.bufnr, solution.range.start.line + 1, lines[0])
|
||||
call appendbufline(state.bufnr, solution.range.start.line + 1, lines[1:-1])
|
||||
call copilot#Request('notifyAccepted', {'uuid': solution.solutionId})
|
||||
let lines = split(item.insertText, "\n", 1)
|
||||
let old_first = getbufline(state.bufnr, item.range.start.line + 1)[0]
|
||||
let lines[0] = strpart(old_first, 0, copilot#util#UTF16ToByteIdx(old_first, item.range.start.character)) . lines[0]
|
||||
let old_last = getbufline(state.bufnr, item.range.end.line + 1)[0]
|
||||
let lines[-1] .= strpart(old_last, copilot#util#UTF16ToByteIdx(old_last, item.range.end.character))
|
||||
call deletebufline(state.bufnr, item.range.start.line + 1, item.range.end.line + 1)
|
||||
call appendbufline(state.bufnr, item.range.start.line, lines)
|
||||
call copilot#Request('workspace/executeCommand', item.command)
|
||||
bwipeout
|
||||
let win = bufwinnr(state.bufnr)
|
||||
if win > 0
|
||||
exe win . 'wincmd w'
|
||||
exe solution.range.start.line + len(lines)
|
||||
exe item.range.start.line + len(lines)
|
||||
if state.was_insert
|
||||
startinsert!
|
||||
else
|
||||
@ -112,49 +110,58 @@ endfunction
|
||||
function! s:Initialize(state) abort
|
||||
let &l:filetype = 'copilot' . (empty(a:state.filetype) ? '' : '.' . a:state.filetype)
|
||||
let &l:tabstop = a:state.tabstop
|
||||
call clearmatches()
|
||||
call matchadd('CopilotSuggestion', '\C^' . s:separator . '\n\zs' . escape(a:state.line, '][^$.*\~'), 10, 4)
|
||||
nmap <buffer><script> <CR> <Cmd>exe copilot#panel#Accept()<CR>
|
||||
nmap <buffer><script> [[ <Cmd>call search('^─\{9,}\n.', 'bWe')<CR>
|
||||
nmap <buffer><script> ]] <Cmd>call search('^─\{9,}\n.', 'We')<CR>
|
||||
endfunction
|
||||
|
||||
function! s:BufReadCmd() abort
|
||||
setlocal bufhidden=wipe buftype=nofile nobuflisted readonly nomodifiable
|
||||
setlocal bufhidden=wipe buftype=nofile nobuflisted nomodifiable
|
||||
let state = get(b:, 'copilot_panel')
|
||||
if type(state) != v:t_dict
|
||||
return
|
||||
endif
|
||||
call s:Initialize(state)
|
||||
call s:Render(expand('<amatch>'))
|
||||
call s:Render(state)
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
function! s:Result(state, result) abort
|
||||
let a:state.percentage = 100
|
||||
call s:PartialResult(a:state, a:result)
|
||||
endfunction
|
||||
|
||||
function! s:Error(state, error) abort
|
||||
let a:state.error = a:error
|
||||
call s:Render(a:state)
|
||||
endfunction
|
||||
|
||||
function! copilot#panel#Open(opts) abort
|
||||
let s:panel_id += 1
|
||||
let state = {'solutions': {}, 'filetype': &filetype, 'line': getline('.'), 'bufnr': bufnr(''), 'tabstop': &tabstop}
|
||||
let bufname = 'copilot:///' . s:panel_id
|
||||
let params = copilot#doc#Params({'panelId': bufname})
|
||||
let state.was_insert = mode() =~# '^[iR]'
|
||||
let state = {'items': [], 'filetype': &filetype, 'was_insert': mode() =~# '^[iR]', 'bufnr': bufnr(''), 'tabstop': &tabstop}
|
||||
let state.panel = 'copilot:///panel/' . s:panel_id
|
||||
if state.was_insert
|
||||
let state.position = copilot#util#AppendPosition()
|
||||
stopinsert
|
||||
else
|
||||
let params.doc.position.character = copilot#doc#UTF16Width(state.line)
|
||||
let params.position.character = params.doc.position.character
|
||||
let state.position = {'line': a:opts.line1 >= 1 ? a:opts.line1 - 1 : 0, 'character': copilot#util#UTF16Width(getline('.'))}
|
||||
endif
|
||||
let response = copilot#Request('getPanelCompletions', params).Wait()
|
||||
if response.status ==# 'error'
|
||||
return 'echoerr ' . string(response.error.message)
|
||||
endif
|
||||
let state.count_target = response.result.solutionCountTarget
|
||||
exe substitute(a:opts.mods, '\C\<tab\>', '-tab', 'g') 'keepalt split' bufname
|
||||
let state.line = getline(state.position.line + 1)
|
||||
let params = {
|
||||
\ 'textDocument': {'uri': state.bufnr},
|
||||
\ 'position': state.position,
|
||||
\ 'partialResultToken': function('s:PartialResult', [state]),
|
||||
\ 'workDoneToken': function('s:WorkDone', [state]),
|
||||
\ }
|
||||
let response = copilot#Request('textDocument/copilotPanelCompletion', params, function('s:Result', [state]), function('s:Error', [state]))
|
||||
exe substitute(a:opts.mods, '\C\<tab\>', '-tab', 'g') 'keepalt split' state.panel
|
||||
let b:copilot_panel = state
|
||||
call s:Initialize(state)
|
||||
call s:Render(@%)
|
||||
call s:Render(state)
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
augroup github_copilot_panel
|
||||
autocmd!
|
||||
autocmd BufReadCmd copilot:///* exe s:BufReadCmd()
|
||||
autocmd BufReadCmd copilot:///panel/* exe s:BufReadCmd()
|
||||
augroup END
|
||||
|
61
sources_non_forked/copilot.vim/autoload/copilot/util.vim
Normal file
61
sources_non_forked/copilot.vim/autoload/copilot/util.vim
Normal file
@ -0,0 +1,61 @@
|
||||
let s:deferred = []
|
||||
|
||||
function! copilot#util#Nop(...) abort
|
||||
return v:null
|
||||
endfunction
|
||||
|
||||
function! copilot#util#Defer(fn, ...) abort
|
||||
call add(s:deferred, function(a:fn, a:000))
|
||||
return timer_start(0, function('s:RunDeferred'))
|
||||
endfunction
|
||||
|
||||
function! s:RunDeferred(...) abort
|
||||
if empty(s:deferred)
|
||||
return
|
||||
endif
|
||||
let Fn = remove(s:deferred, 0)
|
||||
call timer_start(0, function('s:RunDeferred'))
|
||||
call call(Fn, [])
|
||||
endfunction
|
||||
|
||||
function! copilot#util#UTF16Width(str) abort
|
||||
return strchars(substitute(a:str, "\\%#=2[^\u0001-\uffff]", " ", 'g'))
|
||||
endfunction
|
||||
|
||||
if exists('*utf16idx')
|
||||
|
||||
function! copilot#util#UTF16ToByteIdx(str, utf16_idx) abort
|
||||
return byteidx(a:str, a:utf16_idx, 1)
|
||||
endfunction
|
||||
|
||||
elseif has('nvim')
|
||||
|
||||
function! copilot#util#UTF16ToByteIdx(str, utf16_idx) abort
|
||||
try
|
||||
return v:lua.vim.str_byteindex(a:str, a:utf16_idx, 1)
|
||||
catch /^Vim(return):E5108:/
|
||||
return -1
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
else
|
||||
|
||||
function! copilot#util#UTF16ToByteIdx(str, utf16_idx) abort
|
||||
if copilot#util#UTF16Width(a:str) < a:utf16_idx
|
||||
return -1
|
||||
endif
|
||||
let end_offset = len(a:str)
|
||||
while copilot#util#UTF16Width(strpart(a:str, 0, end_offset)) > a:utf16_idx && end_offset > 0
|
||||
let end_offset -= 1
|
||||
endwhile
|
||||
return end_offset
|
||||
endfunction
|
||||
|
||||
endif
|
||||
|
||||
function! copilot#util#AppendPosition() abort
|
||||
let line = getline('.')
|
||||
let col_byte = col('.') - (mode() =~# '^[iR]' || empty(line))
|
||||
let col_utf16 = copilot#util#UTF16Width(strpart(line, 0, col_byte))
|
||||
return {'line': line('.') - 1, 'character': col_utf16}
|
||||
endfunction
|
@ -1,3 +1,3 @@
|
||||
function! copilot#version#String() abort
|
||||
return '1.18.0'
|
||||
return '1.40.0'
|
||||
endfunction
|
||||
|
Reference in New Issue
Block a user