diff --git a/sources_non_forked/copilot.vim/.gitattributes b/sources_non_forked/copilot.vim/.gitattributes new file mode 100644 index 00000000..97909b66 --- /dev/null +++ b/sources_non_forked/copilot.vim/.gitattributes @@ -0,0 +1,2 @@ +*.vim eol=lf +/dist/** -whitespace -diff diff --git a/sources_non_forked/copilot.vim/.github/pull_request_template.md b/sources_non_forked/copilot.vim/.github/pull_request_template.md new file mode 100644 index 00000000..caec1d48 --- /dev/null +++ b/sources_non_forked/copilot.vim/.github/pull_request_template.md @@ -0,0 +1 @@ +At the moment we are not accepting contributions to the repository. diff --git a/sources_non_forked/copilot.vim/.gitignore b/sources_non_forked/copilot.vim/.gitignore new file mode 100644 index 00000000..0a56e3fc --- /dev/null +++ b/sources_non_forked/copilot.vim/.gitignore @@ -0,0 +1 @@ +/doc/tags diff --git a/sources_non_forked/copilot.vim/LICENSE.md b/sources_non_forked/copilot.vim/LICENSE.md new file mode 100644 index 00000000..f0e80c9a --- /dev/null +++ b/sources_non_forked/copilot.vim/LICENSE.md @@ -0,0 +1,4 @@ +GitHub Copilot is offered under the [GitHub Terms of +Service](https://docs.github.com/en/site-policy/github-terms/github-terms-for-additional-products-and-features#github-copilot). + +Copyright (C) 2023 GitHub, Inc. - All Rights Reserved. diff --git a/sources_non_forked/copilot.vim/README.md b/sources_non_forked/copilot.vim/README.md new file mode 100644 index 00000000..9f15e9a9 --- /dev/null +++ b/sources_non_forked/copilot.vim/README.md @@ -0,0 +1,64 @@ +# Copilot.vim + +GitHub Copilot uses OpenAI Codex to suggest code and entire functions in +real-time right from your editor. Trained on billions of lines of public +code, GitHub Copilot turns natural language prompts including comments and +method names into coding suggestions across dozens of languages. + +Copilot.vim is a Vim/Neovim plugin for GitHub Copilot. + +To learn more, visit +[https://github.com/features/copilot](https://github.com/features/copilot). + +## Subscription + +GitHub Copilot requires a subscription. It is free for verified students and +maintainers of popular open source projects on GitHub. + +GitHub Copilot is subject to the [GitHub Additional Product +Terms](https://docs.github.com/en/site-policy/github-terms/github-terms-for-additional-products-and-features). + +## Getting started + +1. Install [Neovim][] or the latest patch of [Vim][] (9.0.0185 or newer). + +2. Install [Node.js][]. + +3. Install `github/copilot.vim` using vim-plug, packer.nvim, or any other + plugin manager. Or to install manually, run one of the following + commands: + + * Vim, Linux/macOS: + + git clone https://github.com/github/copilot.vim.git \ + ~/.vim/pack/github/start/copilot.vim + + * Neovim, Linux/macOS: + + git clone https://github.com/github/copilot.vim.git \ + ~/.config/nvim/pack/github/start/copilot.vim + + * Vim, Windows (PowerShell command): + + git clone https://github.com/github/copilot.vim.git ` + $HOME/vimfiles/pack/github/start/copilot.vim + + * Neovim, Windows (PowerShell command): + + git clone https://github.com/github/copilot.vim.git ` + $HOME/AppData/Local/nvim/pack/github/start/copilot.vim + +4. Start Neovim and invoke `:Copilot setup`. + +[Node.js]: https://nodejs.org/en/download/ +[Neovim]: https://github.com/neovim/neovim/releases/latest +[Vim]: https://github.com/vim/vim + +Suggestions are displayed inline and can be accepted by pressing the tab key. +See `:help copilot` for more information. + +## Troubleshooting + +We’d love to get your help in making GitHub Copilot better! If you have +feedback or encounter any problems, please reach out on our [Feedback +forum](https://github.com/orgs/community/discussions/categories/copilot). diff --git a/sources_non_forked/copilot.vim/SECURITY.md b/sources_non_forked/copilot.vim/SECURITY.md new file mode 100644 index 00000000..7b59e863 --- /dev/null +++ b/sources_non_forked/copilot.vim/SECURITY.md @@ -0,0 +1,4 @@ +If you discover a security issue in this repo, please submit it through the +[GitHub Security Bug Bounty](https://hackerone.com/github). + +Thanks for helping make GitHub Copilot safe for everyone. diff --git a/sources_non_forked/copilot.vim/autoload/copilot.vim b/sources_non_forked/copilot.vim/autoload/copilot.vim new file mode 100644 index 00000000..920aa72f --- /dev/null +++ b/sources_non_forked/copilot.vim/autoload/copilot.vim @@ -0,0 +1,818 @@ +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: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! 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() }) +endfunction + +function! s:Running() abort + return exists('s:agent.job') || exists('s:agent.client_id') +endfunction + +function! s:Start() abort + if s:Running() + 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()}) +endfunction + +function! s:Stop() abort + if exists('s:agent') + let agent = remove(s:, 'agent') + call agent.Close() + endif +endfunction + +function! copilot#Agent() abort + call s:Start() + return s:agent +endfunction + +function! copilot#RunningAgent() abort + if s:Running() + return s:agent + else + return v:null + endif +endfunction + +function! s:NodeVersionWarning() abort + if exists('s:agent.node_version') && s:agent.node_version =~# '^16\.' + 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 + endif +endfunction + +function! copilot#Request(method, params, ...) abort + let agent = copilot#Agent() + return call(agent.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) +endfunction + +function! copilot#Notify(method, params, ...) abort + let agent = copilot#Agent() + return call(agent.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#agent#Cancel(get(b:_copilot, 'first', {})) + call copilot#agent#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, + \ '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()) + \ && empty(copilot#Agent().StartupError()) +endfunction + +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 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) + if a:0 > 1 + call copilot#agent#Error(completion, a:2) + endif + endif +endfunction + +function! s:HideDuringCompletion() abort + return get(g:, 'copilot_hide_during_completion', 1) +endfunction + +function! s:SuggestionTextWithAdjustments() abort + try + if mode() !~# '^[iR]' || (s:HideDuringCompletion() && pumvisible()) || !exists('b:_copilot.suggestions') + return ['', 0, 0, ''] + 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, ''] + 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 typed = strpart(line, 0, offset) + let end_offset = copilot#doc#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] + endif + elseif typed ==# strpart(choice_text, 0, offset) + return [strpart(choice_text, offset), 0, strchars(delete), uuid] + endif + catch + call copilot#logger#Exception() + endtry + return ['', 0, 0, ''] +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.text] = 1 + endfor + for suggestion in get(a:result, 'completions', []) + if !has_key(seen, suggestion.text) + call add(a:context.suggestions, suggestion) + let seen[suggestion.text] = 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 b:_copilot.cycling_callbacks = [a:callback] + let b:_copilot.cycling = copilot#Request('getCompletionsCycling', + \ b:_copilot.first.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, uuid] = s:SuggestionTextWithAdjustments() + + return { + \ 'uuid': uuid, + \ '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, uuid] = s:SuggestionTextWithAdjustments() + let text = split(text, "\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_win_col = virtcol('.') - 1 + 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) + else + call prop_add(line('.'), col('.'), {'type': s:hlgroup, 'text': text[0]}) + 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 + if !has_key(b:_copilot.shown_choices, uuid) + let b:_copilot.shown_choices[uuid] = v:true + call copilot#Request('notifyShown', {'uuid': uuid}) + endif + catch + return copilot#logger#Exception() + endtry +endfunction + +function! s:HandleTriggerResult(result) abort + if !exists('b:_copilot') + return + 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 + try + call copilot#Complete(function('s:HandleTriggerResult'), function('s:HandleTriggerResult')) + catch + call copilot#logger#Exception() + endtry + return '' +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 + 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() + call copilot#Clear() + return + endif + call s:UpdatePreview() + let delay = a:0 ? a:1 : get(g:, 'copilot_idle_delay', 15) + let g:_copilot_timer = timer_start(delay, function('s:Trigger', [bufnr('')])) +endfunction + +function! copilot#OnInsertLeave() abort + return copilot#Clear() +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 + call s:Reject(+expand('')) +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 + call copilot#Request('notifyAccepted', {'uuid': s.uuid, 'acceptedLength': copilot#doc#UTF16Width(text)}) + call s:ClearPreview() + let s:suggestion_text = text + return repeat("\\", s.outdentSize) . repeat("\", s.deleteSize) . + \ "\\=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 + +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 + 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 !copilot#IsMapped() + return ' 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 + 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#Agent().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 + return 1 +endfunction + +function! s:commands.status(opts) abort + if !s:VerifySetup() + 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() +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#Agent().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 = {} + else + let data = copilot#Call('signInInitiate', {}) + endif + + if has_key(data, 'verificationUri') + let uri = data.verificationUri + if has('clipboard') + let @+ = data.userCode + let @* = data.userCode + endif + call s:Echo("First copy your one-time code: " . data.userCode) + try + if len(&mouse) + let mouse = &mouse + set mouse= + endif + if get(a:opts, 'bang') + call s:Echo("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 + 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("Could not find browser. Visit " . uri) + endif + call s:Echo("Waiting (could take up to 5 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 + endif + + let user = get(status, 'user', '') + + echo 'Copilot: Authenticated as GitHub user ' . user +endfunction + +let s:commands.auth = 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() + else + echo 'dist/agent.js not running' + endif +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() + let err = copilot#Agent().StartupError() + if !empty(err) + return 'echoerr ' . string('Copilot: ' . err) + endif + echo 'Copilot: Restarting agent.' +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! 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 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' + else + let cmd = 'panel' + endif + endif + call extend(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/sources_non_forked/copilot.vim/autoload/copilot/agent.vim b/sources_non_forked/copilot.vim/autoload/copilot/agent.vim new file mode 100644 index 00000000..b53a8478 --- /dev/null +++ b/sources_non_forked/copilot.vim/autoload/copilot/agent.vim @@ -0,0 +1,603 @@ +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(':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('')) + endif +augroup END diff --git a/sources_non_forked/copilot.vim/autoload/copilot/doc.vim b/sources_non_forked/copilot.vim/autoload/copilot/doc.vim new file mode 100644 index 00000000..e442e530 --- /dev/null +++ b/sources_non_forked/copilot.vim/autoload/copilot/doc.vim @@ -0,0 +1,116 @@ +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 diff --git a/sources_non_forked/copilot.vim/autoload/copilot/job.vim b/sources_non_forked/copilot.vim/autoload/copilot/job.vim new file mode 100644 index 00000000..212e8e62 --- /dev/null +++ b/sources_non_forked/copilot.vim/autoload/copilot/job.vim @@ -0,0 +1,111 @@ +if exists('g:autoloaded_copilot_job') + finish +endif +let g:autoloaded_copilot_job = 1 + +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/sources_non_forked/copilot.vim/autoload/copilot/logger.vim b/sources_non_forked/copilot.vim/autoload/copilot/logger.vim new file mode 100644 index 00000000..105e3f9b --- /dev/null +++ b/sources_non_forked/copilot.vim/autoload/copilot/logger.vim @@ -0,0 +1,72 @@ +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 + call writefile([], s:log_file) + catch + endtry +endif + +function! copilot#logger#File() abort + return s:log_file +endfunction + +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) + 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') + 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) +endfunction + +function! copilot#logger#Info(...) abort + call copilot#logger#Raw(1, 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(3, 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 stacklines = [] + for frame in split(substitute(substitute(v:throwpoint, ', \S\+ \(\d\+\)$', '[\1]', ''), '^function ', '', ''), '\.\@\d\+_', '', '')) + endif + endfor + call agent.Request('telemetry/exception', { + \ 'origin': 'copilot.vim', + \ 'stacktrace': join([v:exception] + stacklines, "\n") + \ }) + endif + endif +endfunction diff --git a/sources_non_forked/copilot.vim/autoload/copilot/panel.vim b/sources_non_forked/copilot.vim/autoload/copilot/panel.vim new file mode 100644 index 00000000..072ef948 --- /dev/null +++ b/sources_non_forked/copilot.vim/autoload/copilot/panel.vim @@ -0,0 +1,160 @@ +if exists('g:autoloaded_copilot_panel') + finish +endif +let g:autoloaded_copilot_panel = 1 + +scriptencoding utf-8 + +if !exists('s:panel_id') + let s:panel_id = 0 +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 + return + endif + let sorted = s:Solutions(state) + if !empty(get(state, 'status', '')) + let lines = ['Error: ' . state.status] + 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)'] + endif + if len(sorted) + call add(lines, 'Press on a solution to accept') + endif + for solution in sorted + let lines += [s:separator] + split(solution.displayText, "\n", 1) + endfor + try + call setbufvar(bufnr, '&modifiable', 1) + call setbufvar(bufnr, '&readonly', 0) + call setbufline(bufnr, 1, lines) + finally + call setbufvar(bufnr, '&modifiable', 0) + call setbufvar(bufnr, '&readonly', 1) + endtry +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) +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 + 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) + 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 + for lnum in range(1, at) + if getline(lnum) ==# s:separator + let solution_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 getbufline(state.bufnr, lnum) !=# [state.line] + return 'echoerr "Buffer has changed since synthesizing solution"' + 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}) + bwipeout + let win = bufwinnr(state.bufnr) + if win > 0 + exe win . 'wincmd w' + exe solution.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 + call clearmatches() + call matchadd('CopilotSuggestion', '\C^' . s:separator . '\n\zs' . escape(a:state.line, '][^$.*\~'), 10, 4) + nmap