From 3a510394e6befa5cdb4ebe6ab51758df34ee992a Mon Sep 17 00:00:00 2001 From: Wwww124-api Date: Thu, 6 Feb 2025 01:19:31 +0700 Subject: [PATCH] Delete autoload directory --- autoload/copilot.vim | 860 ---------------------------------- autoload/copilot/client.vim | 764 ------------------------------ autoload/copilot/handlers.vim | 31 -- autoload/copilot/job.vim | 106 ----- autoload/copilot/logger.vim | 105 ----- autoload/copilot/panel.vim | 167 ------- autoload/copilot/util.vim | 61 --- autoload/copilot/version.vim | 3 - 8 files changed, 2097 deletions(-) delete mode 100644 autoload/copilot.vim delete mode 100644 autoload/copilot/client.vim delete mode 100644 autoload/copilot/handlers.vim delete mode 100644 autoload/copilot/job.vim delete mode 100644 autoload/copilot/logger.vim delete mode 100644 autoload/copilot/panel.vim delete mode 100644 autoload/copilot/util.vim delete mode 100644 autoload/copilot/version.vim diff --git a/autoload/copilot.vim b/autoload/copilot.vim deleted file mode 100644 index 5016a039..00000000 --- a/autoload/copilot.vim +++ /dev/null @@ -1,860 +0,0 @@ -scriptencoding utf-8 - -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 - -let s:hlgroup = 'CopilotSuggestion' -let s:annot_hlgroup = 'CopilotAnnotation' - -if s:has_vim_ghost_text && empty(prop_type_get(s:hlgroup)) - call prop_type_add(s:hlgroup, {'highlight': s:hlgroup}) -endif -if s:has_vim_ghost_text && empty(prop_type_get(s:annot_hlgroup)) - call prop_type_add(s:annot_hlgroup, {'highlight': s:annot_hlgroup}) -endif - -function! s:Echo(msg) abort - if has('nvim') && &cmdheight == 0 - call v:lua.vim.notify(a:msg, v:null, {'title': 'GitHub Copilot'}) - else - echo a:msg - endif -endfunction - -function! s:EditorConfiguration() abort - let filetypes = copy(s:filetype_defaults) - if type(get(g:, 'copilot_filetypes')) == v:t_dict - call extend(filetypes, g:copilot_filetypes) - endif - return { - \ 'enableAutoCompletions': empty(get(g:, 'copilot_enabled', 1)) ? v:false : v:true, - \ 'disabledLanguages': map(sort(keys(filter(filetypes, { k, v -> empty(v) }))), { _, v -> {'languageId': v}}), - \ } -endfunction - -function! copilot#Init(...) abort - call copilot#util#Defer({ -> exists('s:client') || s:Start() }) -endfunction - -function! s:Running() abort - return exists('s:client.job') || exists('s:client.client_id') -endfunction - -function! s:Start() abort - if s:Running() || exists('s:client.startup_error') - return - endif - let s:client = copilot#client#New({'editorConfiguration' : s:EditorConfiguration()}) -endfunction - -function! s:Stop() abort - if exists('s:client') - let client = remove(s:, 'client') - call client.Close() - endif -endfunction - -function! copilot#Client() abort - call s:Start() - return s:client -endfunction - -function! copilot#RunningClient() abort - if s:Running() - return s:client - else - return v:null - endif -endfunction - -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: ' . s:editor_warning - echohl None - endif -endfunction - -function! copilot#Request(method, params, ...) abort - let client = copilot#Client() - return call(client.Request, [a:method, a:params] + a:000) -endfunction - -function! copilot#Call(method, params, ...) abort - let client = copilot#Client() - return call(client.Call, [a:method, a:params] + a:000) -endfunction - -function! copilot#Notify(method, params, ...) abort - let client = copilot#Client() - return call(client.Notify, [a:method, a:params] + a:000) -endfunction - -function! copilot#NvimNs() abort - return nvim_create_namespace('github-copilot') -endfunction - -function! copilot#Clear() abort - if exists('g:_copilot_timer') - call timer_stop(remove(g:, '_copilot_timer')) - endif - if exists('b:_copilot') - 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! copilot#Dismiss() abort - call copilot#Clear() - call s:UpdatePreview() - return '' -endfunction - -let s:filetype_defaults = { - \ 'gitcommit': 0, - \ 'gitrebase': 0, - \ 'hgcommit': 0, - \ 'svn': 0, - \ 'cvs': 0, - \ '.': 0} - -function! s:BufferDisabled() abort - if &buftype =~# '^\%(help\|prompt\|quickfix\|terminal\)$' - return 5 - endif - if exists('b:copilot_disabled') - return empty(b:copilot_disabled) ? 0 : 3 - endif - if exists('b:copilot_enabled') - return empty(b:copilot_enabled) ? 4 : 0 - endif - let short = empty(&l:filetype) ? '.' : split(&l:filetype, '\.', 1)[0] - let config = {} - if type(get(g:, 'copilot_filetypes')) == v:t_dict - let config = g:copilot_filetypes - endif - if has_key(config, &l:filetype) - return empty(config[&l:filetype]) - elseif has_key(config, short) - return empty(config[short]) - elseif has_key(config, '*') - return empty(config['*']) - else - return get(s:filetype_defaults, short, 1) == 0 ? 2 : 0 - endif -endfunction - -function! copilot#Enabled() abort - return get(g:, 'copilot_enabled', 1) - \ && empty(s:BufferDisabled()) -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 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#client#Result(completion, function(a:1, [b:_copilot])) - if a:0 > 1 - call copilot#client#Error(completion, function(a:2, [b:_copilot])) - endif - endif -endfunction - -function! s:HideDuringCompletion() abort - return get(g:, 'copilot_hide_during_completion', 1) -endfunction - -function! s:SuggestionTextWithAdjustments() abort - let empty = ['', 0, 0, {}] - try - if mode() !~# '^[iR]' || (s:HideDuringCompletion() && pumvisible()) || !exists('b:_copilot.suggestions') - 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.insertText) !=# v:t_string - return empty - endif - let line = getline('.') - let offset = col('.') - 1 - 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#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) - 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), choice] - endif - elseif typed ==# strpart(choice_text, 0, offset) - return [strpart(choice_text, offset), 0, strchars(delete), choice] - endif - catch - call copilot#logger#Exception() - endtry - return empty -endfunction - - -function! s:Advance(count, context, ...) abort - if a:context isnot# get(b:, '_copilot', {}) - return - endif - let a:context.choice += a:count - if a:context.choice < 0 - let a:context.choice += len(a:context.suggestions) - endif - let a:context.choice %= len(a:context.suggestions) - call s:UpdatePreview() -endfunction - -function! s:GetSuggestionsCyclingCallback(context, result) abort - let callbacks = remove(a:context, 'cycling_callbacks') - let seen = {} - for suggestion in a:context.suggestions - let seen[suggestion.insertText] = 1 - endfor - for suggestion in get(a:result, 'items', []) - if !has_key(seen, suggestion.insertText) - call add(a:context.suggestions, suggestion) - let seen[suggestion.insertText] = 1 - endif - endfor - for Callback in callbacks - call Callback(a:context) - endfor -endfunction - -function! s:GetSuggestionsCycling(callback) abort - if exists('b:_copilot.cycling_callbacks') - call add(b:_copilot.cycling_callbacks, a:callback) - 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('textDocument/inlineCompletion', - \ params, - \ function('s:GetSuggestionsCyclingCallback', [b:_copilot]), - \ function('s:GetSuggestionsCyclingCallback', [b:_copilot]), - \ ) - call s:UpdatePreview() - endif - return '' -endfunction - -function! copilot#Next() abort - return s:GetSuggestionsCycling(function('s:Advance', [1])) -endfunction - -function! copilot#Previous() abort - return s:GetSuggestionsCycling(function('s:Advance', [-1])) -endfunction - -function! copilot#GetDisplayedSuggestion() abort - let [text, outdent, delete, item] = s:SuggestionTextWithAdjustments() - - return { - \ 'item': item, - \ 'text': text, - \ 'outdentSize': outdent, - \ 'deleteSize': delete} -endfunction - -function! s:ClearPreview() abort - if s:has_nvim_ghost_text - call nvim_buf_del_extmark(0, copilot#NvimNs(), 1) - elseif s:has_vim_ghost_text - call prop_remove({'type': s:hlgroup, 'all': v:true}) - call prop_remove({'type': s:annot_hlgroup, 'all': v:true}) - endif -endfunction - -function! s:UpdatePreview() abort - try - let [text, outdent, delete, item] = s:SuggestionTextWithAdjustments() - let text = split(text, "\r\n\\=\\|\n", 1) - if empty(text[-1]) - call remove(text, -1) - endif - if empty(text) || !s:has_ghost_text - return s:ClearPreview() - endif - if exists('b:_copilot.cycling_callbacks') - let annot = '(1/…)' - elseif exists('b:_copilot.cycling') - let annot = '(' . (b:_copilot.choice + 1) . '/' . len(b:_copilot.suggestions) . ')' - else - let annot = '' - endif - call s:ClearPreview() - if s:has_nvim_ghost_text - let data = {'id': 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 - let data.virt_lines = map(text[1:-1], { _, l -> [[l, s:hlgroup]] }) - if !empty(annot) - let data.virt_lines[-1] += [[' '], [annot, s:annot_hlgroup]] - endif - elseif len(annot) - let data.virt_text += [[' '], [annot, s:annot_hlgroup]] - endif - let data.hl_mode = 'combine' - call nvim_buf_set_extmark(0, copilot#NvimNs(), line('.')-1, col('.')-1, data) - 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 - if !empty(annot) - call prop_add(line('.'), col('$'), {'type': s:annot_hlgroup, 'text': ' ' . annot}) - endif - endif - call copilot#Notify('textDocument/didShowCompletion', {'item': item}) - catch - return copilot#logger#Exception() - endtry -endfunction - -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 -endfunction - -function! copilot#Suggest() abort - if !s:Running() - return '' - endif - try - call copilot#Complete(function('s:HandleTriggerResult'), function('s:HandleTriggerError')) - catch - call copilot#logger#Exception() - endtry - return '' -endfunction - -function! s:Trigger(bufnr, timer) abort - let timer = get(g:, '_copilot_timer', -1) - if a:bufnr !=# bufnr('') || a:timer isnot# timer || mode() !=# 'i' - return - endif - unlet! g:_copilot_timer - return copilot#Suggest() -endfunction - -function! copilot#Schedule() abort - if !s:has_ghost_text || !s:Running() || !copilot#Enabled() - call copilot#Clear() - return - endif - call s:UpdatePreview() - 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! 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 - return copilot#Schedule() -endfunction - -function! copilot#OnCompleteChanged() abort - if s:HideDuringCompletion() - return copilot#Clear() - else - return copilot#Schedule() - endif -endfunction - -function! copilot#OnCursorMovedI() abort - return copilot#Schedule() -endfunction - -function! copilot#OnBufUnload() abort -endfunction - -function! copilot#OnVimLeavePre() abort -endfunction - -function! copilot#TextQueuedForInsertion() abort - try - return remove(s:, 'suggestion_text') - catch - return '' - endtry -endfunction - -function! copilot#Accept(...) abort - let s = copilot#GetDisplayedSuggestion() - if !empty(s.text) - unlet! b:_copilot - let text = '' - if a:0 > 1 - let text = substitute(matchstr(s.text, "\n*" . '\%(' . a:2 .'\)'), "\n*$", '', '') - endif - if empty(text) - let text = s.text - endif - 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" ? "\\=" : "\\=" - return repeat("\\", s.outdentSize) . repeat("\", s.deleteSize) . - \ recall . "copilot#TextQueuedForInsertion()\" . (a:0 > 1 ? '' : "\") - endif - let default = get(g:, 'copilot_tab_fallback', pumvisible() ? "\" : "\t") - if !a:0 - return default - elseif type(a:1) == v:t_string - return a:1 - elseif type(a:1) == v:t_func - try - return call(a:1, []) - catch - return default - endtry - else - return default - endif -endfunction - -function! copilot#AcceptWord(...) abort - return copilot#Accept(a:0 ? a:1 : '', '\%(\k\@!.\)*\k*') -endfunction - -function! copilot#AcceptLine(...) abort - return copilot#Accept(a:0 ? a:1 : "\r", "[^\n]\\+") -endfunction - -function! s:BrowserCallback(into, code) abort - let a:into.code = a:code -endfunction - -function! copilot#Browser() abort - if type(get(g:, 'copilot_browser')) == v:t_list - let cmd = copy(g:copilot_browser) - elseif type(get(g:, 'open_command')) == v:t_list - let cmd = copy(g:open_command) - elseif has('win32') - let cmd = ['rundll32', 'url.dll,FileProtocolHandler'] - elseif has('mac') - let cmd = ['open'] - elseif executable('wslview') - return ['wslview'] - elseif executable('xdg-open') - return ['xdg-open'] - else - return [] - endif - if executable(get(cmd, 0, '')) - return cmd - else - return [] - endif -endfunction - -let s:commands = {} - -function! s:EnabledStatusMessage() abort - let buf_disabled = s:BufferDisabled() - if !s:has_ghost_text - if has('nvim') - return "Neovim 0.6 required to support ghost text" - else - return "Vim " . s:vim_minimum_version . " required to support ghost text" - endif - elseif !get(g:, 'copilot_enabled', 1) - return 'Disabled globally by :Copilot disable' - elseif buf_disabled is# 5 - return 'Disabled for current buffer by buftype=' . &buftype - elseif buf_disabled is# 4 - return 'Disabled for current buffer by b:copilot_enabled' - elseif buf_disabled is# 3 - return 'Disabled for current buffer by b:copilot_disabled' - elseif buf_disabled is# 2 - return 'Disabled for filetype=' . &filetype . ' by internal default' - elseif buf_disabled - return 'Disabled for filetype=' . &filetype . ' by g:copilot_filetypes' - elseif !copilot#Enabled() - return 'BUG: Something is wrong with enabling/disabling' - else - return '' - endif -endfunction - -function! s:VerifySetup() abort - let error = copilot#Client().StartupError() - if !empty(error) - echo 'Copilot: ' . error - return - endif - - let status = copilot#Call('checkStatus', {}) - - if !has_key(status, 'user') - echo 'Copilot: Not authenticated. Invoke :Copilot setup' - return - endif - - if status.status ==# 'NoTelemetryConsent' - 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 - -function! s:commands.status(opts) abort - if !s:VerifySetup() - 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 - - echo 'Copilot: Ready' - call s:EditorVersionWarning() -endfunction - -function! s:commands.signout(opts) abort - let status = copilot#Call('checkStatus', {'options': {'localChecksOnly': v:true}}) - if has_key(status, 'user') - echo 'Copilot: Signed out as GitHub user ' . status.user - else - echo 'Copilot: Not signed in' - endif - call copilot#Call('signOut', {}) -endfunction - -function! s:commands.setup(opts) abort - let startup_error = copilot#Client().StartupError() - if !empty(startup_error) - echo 'Copilot: ' . startup_error - return - endif - - let browser = copilot#Browser() - - let status = copilot#Call('checkStatus', {}) - if has_key(status, 'user') - let data = {'status': 'AlreadySignedIn', 'user': status.user} - else - let data = copilot#Call('signInInitiate', {}) - endif - - if has_key(data, 'verificationUri') - let uri = data.verificationUri - if has('clipboard') - try - let @+ = data.userCode - catch - endtry - try - let @* = data.userCode - catch - endtry - endif - 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(codemsg . "In your browser, visit " . uri) - elseif len(browser) - 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() - while empty(status) && reltimefloat(reltime(time)) < 5 - sleep 10m - endwhile - if get(status, 'code', browser[0] !=# 'xdg-open') != 0 - call s:Echo("Failed to open browser. Visit " . uri) - else - call s:Echo("Opened " . uri) - endif - else - call s:Echo(codemsg . "Could not find browser. Visit " . uri) - endif - call s:Echo("Waiting (could take up to 10 seconds)") - let request = copilot#Request('signInConfirm', {'userCode': data.userCode}).Wait() - finally - if exists('mouse') - let &mouse = mouse - endif - endtry - if request.status ==# 'error' - return 'echoerr ' . string('Copilot: Authentication failure: ' . request.error.message) - 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', '') - - echo 'Copilot: Authenticated as GitHub user ' . user -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 - 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 '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 - try - if s:Running() - call copilot#Notify('notifyChangeConfiguration', {'settings': s:EditorConfiguration()}) - endif - catch - call copilot#logger#Exception() - endtry -endfunction - -let s:feedback_url = 'https://github.com/orgs/community/discussions/categories/copilot' -function! s:commands.feedback(opts) abort - echo s:feedback_url - let browser = copilot#Browser() - if len(browser) - call copilot#job#Stream(browser + [s:feedback_url], v:null, v:null, v:null) - endif -endfunction - -function! s:commands.restart(opts) abort - call s:Stop() - echo 'Copilot: Restarting language server' - call s:Start() -endfunction - -function! s:commands.disable(opts) abort - let g:copilot_enabled = 0 - call s:UpdateEditorConfiguration() -endfunction - -function! s:commands.enable(opts) abort - let g:copilot_enabled = 1 - call s:UpdateEditorConfiguration() -endfunction - -function! s:commands.panel(opts) abort - if s:VerifySetup() - return copilot#panel#Open(a:opts) - 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 !~# ' ' - return sort(filter(map(keys(s:commands), { k, v -> tr(v, '_', '-') }), - \ { k, v -> strpart(v, 0, len(a:arg)) ==# a:arg })) - else - return [] - endif -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 !empty(cmd) && !has_key(s:commands, tr(cmd, '-', '_')) - return 'echoerr ' . string('Copilot: unknown command ' . string(cmd)) - endif - try - if empty(cmd) - if !s:Running() - let cmd = 'restart' - else - 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 - 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 - else - return '' - endif - catch /^Copilot:/ - return 'echoerr ' . string(v:exception) - endtry -endfunction diff --git a/autoload/copilot/client.vim b/autoload/copilot/client.vim deleted file mode 100644 index c811062e..00000000 --- a/autoload/copilot/client.vim +++ /dev/null @@ -1,764 +0,0 @@ -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(':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('')) - endif -augroup END diff --git a/autoload/copilot/handlers.vim b/autoload/copilot/handlers.vim deleted file mode 100644 index a73186fe..00000000 --- a/autoload/copilot/handlers.vim +++ /dev/null @@ -1,31 +0,0 @@ -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 diff --git a/autoload/copilot/job.vim b/autoload/copilot/job.vim deleted file mode 100644 index 39904a8d..00000000 --- a/autoload/copilot/job.vim +++ /dev/null @@ -1,106 +0,0 @@ -scriptencoding utf-8 - -function! copilot#job#Nop(...) abort -endfunction - -function! s:Jobs(job_or_jobs) abort - let jobs = type(a:job_or_jobs) == v:t_list ? copy(a:job_or_jobs) : [a:job_or_jobs] - call map(jobs, { k, v -> type(v) == v:t_dict ? get(v, 'job', '') : v }) - call filter(jobs, { k, v -> type(v) !=# type('') }) - return jobs -endfunction - -let s:job_stop = exists('*job_stop') ? 'job_stop' : 'jobstop' -function! copilot#job#Stop(job) abort - for job in s:Jobs(a:job) - call call(s:job_stop, [job]) - endfor - return copilot#job#Wait(a:job) -endfunction - -let s:sleep = has('patch-8.2.2366') ? 'sleep! 1m' : 'sleep 1m' -function! copilot#job#Wait(jobs) abort - let jobs = s:Jobs(a:jobs) - if exists('*jobwait') - call jobwait(jobs) - else - for job in jobs - while ch_status(job) !=# 'closed' || job_status(job) ==# 'run' - exe s:sleep - endwhile - endfor - endif - return a:jobs -endfunction - -function! s:VimExitCallback(result, exit_cb, job, data) abort - let a:result.exit_status = a:data - if !has_key(a:result, 'closed') - return - endif - call remove(a:result, 'closed') - call a:exit_cb(a:result.exit_status) -endfunction - -function! s:VimCloseCallback(result, exit_cb, job) abort - if !has_key(a:result, 'exit_status') - let a:result.closed = v:true - return - endif - call a:exit_cb(a:result.exit_status) -endfunction - -function! s:NvimCallback(cb, job, data, type) dict abort - let self[a:type][0] .= remove(a:data, 0) - call extend(self[a:type], a:data) - while len(self[a:type]) > 1 - call a:cb(substitute(remove(self[a:type], 0), "\r$", '', '')) - endwhile -endfunction - -function! s:NvimExitCallback(out_cb, err_cb, exit_cb, job, data, type) dict abort - if len(self.stderr[0]) - call a:err_cb(substitute(self.stderr[0], "\r$", '', '')) - endif - call a:exit_cb(a:data) -endfunction - -function! copilot#job#Cwd() abort - let home = expand("~") - if !isdirectory(home) && isdirectory($VIM) - return $VIM - endif - return home -endfunction - -function! copilot#job#Stream(argv, out_cb, err_cb, ...) abort - let exit_status = [] - let ExitCb = function(a:0 && !empty(a:1) ? a:1 : { e -> add(exit_status, e) }, a:000[2:-1]) - let OutCb = function(empty(a:out_cb) ? 'copilot#job#Nop' : a:out_cb, a:000[2:-1]) - let ErrCb = function(empty(a:err_cb) ? 'copilot#job#Nop' : a:err_cb, a:000[2:-1]) - let state = {'headers': {}, 'mode': 'headers', 'buffer': ''} - if exists('*job_start') - let result = {} - let job = job_start(a:argv, { - \ 'cwd': copilot#job#Cwd(), - \ 'out_mode': 'raw', - \ 'out_cb': { j, d -> OutCb(d) }, - \ 'err_cb': { j, d -> ErrCb(d) }, - \ 'exit_cb': function('s:VimExitCallback', [result, ExitCb]), - \ 'close_cb': function('s:VimCloseCallback', [result, ExitCb]), - \ }) - else - let jopts = { - \ 'cwd': copilot#job#Cwd(), - \ 'stderr': [''], - \ 'on_stdout': { j, d, t -> OutCb(join(d, "\n")) }, - \ 'on_stderr': function('s:NvimCallback', [ErrCb]), - \ 'on_exit': function('s:NvimExitCallback', [OutCb, ErrCb, ExitCb])} - let job = jobstart(a:argv, jopts) - endif - if a:0 - return job - endif - call copilot#job#Wait(job) - return exit_status[0] -endfunction diff --git a/autoload/copilot/logger.vim b/autoload/copilot/logger.vim deleted file mode 100644 index 923a1c11..00000000 --- a/autoload/copilot/logger.vim +++ /dev/null @@ -1,105 +0,0 @@ -if !exists('s:log_file') - let s:log_file = tempname() . '-copilot.log' - try - call writefile([], s:log_file) - catch - endtry -endif - -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 - 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 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#Debug(...) abort - 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(3, a:000) -endfunction - -function! copilot#logger#Warn(...) abort - call copilot#logger#Raw(2, a:000) -endfunction - -function! copilot#logger#Error(...) abort - call copilot#logger#Raw(1, a:000) -endfunction - -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 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(v:throwpoint, ', \S\+ \(\d\+\)$', '[\1]', ''), '\.\@\d\+_', '', ''), '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, {'function': '[redacted]'}) - endif - endfor - 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 diff --git a/autoload/copilot/panel.vim b/autoload/copilot/panel.vim deleted file mode 100644 index 4e25237e..00000000 --- a/autoload/copilot/panel.vim +++ /dev/null @@ -1,167 +0,0 @@ -scriptencoding utf-8 - -if !exists('s:panel_id') - let s:panel_id = 0 -endif - -let s:separator = repeat('─', 72) - -function! s:Render(state) abort - let bufnr = bufnr('^' . a:state.panel . '$') - let state = a:state - if !bufloaded(bufnr) - return - endif - 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 lines = [substitute('Synthesizing ' . matchstr(get(a:state, 'message', ''), '\d\+\%(/\d\+\)\=') . ' completions', ' \+', ' ', 'g')] - endif - if len(sorted) - call add(lines, 'Press on a completion to accept') - endif - 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) - call setbufvar(bufnr, '&readonly', 0) - call setbufline(bufnr, 1, lines) - finally - call setbufvar(bufnr, '&modifiable', 0) - 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! 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! 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 -endfunction - -function! copilot#panel#Accept(...) abort - let state = get(b:, 'copilot_panel', {}) - 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 index = 0 - for lnum in range(1, at) - if getline(lnum) ==# s:separator - let index += 1 - endif - endfor - 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 completion"' - endif - 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 item.range.start.line + len(lines) - if state.was_insert - startinsert! - else - normal! $ - endif - endif - endif - return '' -endfunction - -function! s:Initialize(state) abort - let &l:filetype = 'copilot' . (empty(a:state.filetype) ? '' : '.' . a:state.filetype) - let &l:tabstop = a:state.tabstop - nmap