Added the copilot.vim to sources_non_forked
This commit is contained in:
parent
4fa0cde32e
commit
e6f509e6e1
|
@ -0,0 +1,2 @@
|
|||
*.vim eol=lf
|
||||
/dist/** -whitespace -diff
|
|
@ -0,0 +1 @@
|
|||
At the moment we are not accepting contributions to the repository.
|
|
@ -0,0 +1 @@
|
|||
/doc/tags
|
|
@ -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.
|
|
@ -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).
|
|
@ -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.
|
|
@ -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('<abuf>'))
|
||||
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("\<Left>\<Del>", s.outdentSize) . repeat("\<Del>", s.deleteSize) .
|
||||
\ "\<C-R>\<C-O>=copilot#TextQueuedForInsertion()\<CR>" . (a:0 > 1 ? '' : "\<End>")
|
||||
endif
|
||||
let default = get(g:, 'copilot_tab_fallback', pumvisible() ? "\<C-N>" : "\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 '<Tab> map has been disabled or is claimed by another plugin'
|
||||
elseif !get(g:, 'copilot_enabled', 1)
|
||||
return 'Disabled globally by :Copilot disable'
|
||||
elseif buf_disabled is# 5
|
||||
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', '<unknown>')
|
||||
|
||||
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
|
|
@ -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('<sfile>:h:h:h')
|
||||
|
||||
if !exists('s:instances')
|
||||
let s:instances = {}
|
||||
endif
|
||||
|
||||
" allow sourcing this file to reload the Lua file too
|
||||
if has('nvim')
|
||||
lua package.loaded._copilot = nil
|
||||
endif
|
||||
|
||||
let s:jobstop = function(exists('*jobstop') ? 'jobstop' : 'job_stop')
|
||||
function! s:Kill(agent, ...) abort
|
||||
if has_key(a:agent, 'job')
|
||||
call s:jobstop(a:agent.job)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:AgentClose() dict abort
|
||||
if !has_key(self, 'job')
|
||||
return
|
||||
endif
|
||||
if exists('*chanclose')
|
||||
call chanclose(self.job, 'stdin')
|
||||
else
|
||||
call ch_close_in(self.job)
|
||||
endif
|
||||
call copilot#logger#Info('agent stopped')
|
||||
call timer_start(2000, function('s:Kill', [self]))
|
||||
endfunction
|
||||
|
||||
function! s:LogSend(request, line) abort
|
||||
return '--> ' . a:line
|
||||
endfunction
|
||||
|
||||
function! s:RejectRequest(request, error) abort
|
||||
if a:request.status ==# 'canceled'
|
||||
return
|
||||
endif
|
||||
let a:request.waiting = {}
|
||||
call remove(a:request, 'resolve')
|
||||
let reject = remove(a:request, 'reject')
|
||||
let a:request.status = 'error'
|
||||
let a:request.error = a:error
|
||||
for Cb in reject
|
||||
let a:request.waiting[timer_start(0, function('s:Callback', [a:request, 'error', Cb]))] = 1
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:Send(agent, request) abort
|
||||
try
|
||||
call ch_sendexpr(a:agent.job, a:request)
|
||||
return v:true
|
||||
catch /^Vim\%((\a\+)\)\=:E631:/
|
||||
return v:false
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:AgentNotify(method, params) dict abort
|
||||
return s:Send(self, {'method': a:method, 'params': a:params})
|
||||
endfunction
|
||||
|
||||
function! s:RequestWait() dict abort
|
||||
while self.status ==# 'running'
|
||||
sleep 1m
|
||||
endwhile
|
||||
while !empty(get(self, 'waiting', {}))
|
||||
sleep 1m
|
||||
endwhile
|
||||
return self
|
||||
endfunction
|
||||
|
||||
function! s:RequestAwait() dict abort
|
||||
call self.Wait()
|
||||
if has_key(self, 'result')
|
||||
return self.result
|
||||
endif
|
||||
throw 'copilot#agent(' . self.error.code . '): ' . self.error.message
|
||||
endfunction
|
||||
|
||||
function! s:RequestAgent() dict abort
|
||||
return get(s:instances, self.agent_id, v:null)
|
||||
endfunction
|
||||
|
||||
if !exists('s:id')
|
||||
let s:id = 0
|
||||
endif
|
||||
|
||||
function! s:SetUpRequest(agent, id, method, params, ...) abort
|
||||
let request = {
|
||||
\ 'agent_id': a:agent.id,
|
||||
\ 'id': a:id,
|
||||
\ 'method': a:method,
|
||||
\ 'params': a:params,
|
||||
\ 'Agent': function('s:RequestAgent'),
|
||||
\ 'Wait': function('s:RequestWait'),
|
||||
\ 'Await': function('s:RequestAwait'),
|
||||
\ 'Cancel': function('s:RequestCancel'),
|
||||
\ 'resolve': [],
|
||||
\ 'reject': [],
|
||||
\ 'status': 'running'}
|
||||
let a:agent.requests[a:id] = request
|
||||
let args = a:000[2:-1]
|
||||
if len(args)
|
||||
if !empty(a:1)
|
||||
call add(request.resolve, { v -> call(a:1, [v] + args)})
|
||||
endif
|
||||
if !empty(a:2)
|
||||
call add(request.reject, { v -> call(a:2, [v] + args)})
|
||||
endif
|
||||
return request
|
||||
endif
|
||||
if a:0 && !empty(a:1)
|
||||
call add(request.resolve, a:1)
|
||||
endif
|
||||
if a:0 > 1 && !empty(a:2)
|
||||
call add(request.reject, a:2)
|
||||
endif
|
||||
return request
|
||||
endfunction
|
||||
|
||||
function! s:UrlEncode(str) abort
|
||||
return substitute(iconv(a:str, 'latin1', 'utf-8'),'[^A-Za-z0-9._~!$&''()*+,;=:@/-]','\="%".printf("%02X",char2nr(submatch(0)))','g')
|
||||
endfunction
|
||||
|
||||
let s:slash = exists('+shellslash') ? '\' : '/'
|
||||
function! s:UriFromBufnr(bufnr) abort
|
||||
let absolute = tr(bufname(a:bufnr), s:slash, '/')
|
||||
if absolute !~# '^\a\+:\|^/\|^$' && getbufvar(a:bufnr, 'buftype') =~# '^\%(nowrite\)\=$'
|
||||
let absolute = substitute(tr(getcwd(), s:slash, '/'), '/\=$', '/', '') . absolute
|
||||
endif
|
||||
if has('win32') && absolute =~# '^\a://\@!'
|
||||
return 'file:///' . strpart(absolute, 0, 2) . s:UrlEncode(strpart(absolute, 2))
|
||||
elseif absolute =~# '^/'
|
||||
return 'file://' . s:UrlEncode(absolute)
|
||||
elseif absolute =~# '^\a[[:alnum:].+-]*:\|^$'
|
||||
return absolute
|
||||
else
|
||||
return ''
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:BufferText(bufnr) abort
|
||||
return join(getbufline(a:bufnr, 1, '$'), "\n") . "\n"
|
||||
endfunction
|
||||
|
||||
function! s:LogMessage(params) abort
|
||||
call copilot#logger#Raw(get(a:params, 'level', 3), get(a:params, 'message', ''))
|
||||
endfunction
|
||||
|
||||
function! s:ShowMessageRequest(params) abort
|
||||
let choice = inputlist([a:params.message . "\n\nRequest Actions:"] +
|
||||
\ map(copy(get(a:params, 'actions', [])), { i, v -> (i + 1) . '. ' . v.title}))
|
||||
return choice > 0 ? get(a:params.actions, choice - 1, v:null) : v:null
|
||||
endfunction
|
||||
|
||||
function! s:SendRequest(agent, request) abort
|
||||
if empty(s:Send(a:agent, a:request)) && has_key(a:agent.requests, a:request.id)
|
||||
call s:RejectRequest(remove(a:agent.requests, a:request.id), {'code': 257, 'message': 'Write failed'})
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:AgentRequest(method, params, ...) dict abort
|
||||
let s:id += 1
|
||||
let request = {'method': a:method, 'params': deepcopy(a:params), 'id': s:id}
|
||||
for doc in filter([get(request.params, 'doc', {}), get(request.params, 'textDocument',{})], 'type(get(v:val, "uri", "")) == v:t_number')
|
||||
let bufnr = doc.uri
|
||||
let doc.uri = s:UriFromBufnr(doc.uri)
|
||||
let uri = doc.uri
|
||||
let languageId = copilot#doc#LanguageForFileType(getbufvar(bufnr, '&filetype'))
|
||||
let doc_version = getbufvar(bufnr, 'changedtick')
|
||||
if has_key(self.open_buffers, bufnr) && (
|
||||
\ self.open_buffers[bufnr].uri !=# doc.uri ||
|
||||
\ self.open_buffers[bufnr].languageId !=# languageId)
|
||||
call remove(self.open_buffers, bufnr)
|
||||
sleep 1m
|
||||
endif
|
||||
if !has_key(self.open_buffers, bufnr)
|
||||
let td_item = {
|
||||
\ 'uri': doc.uri,
|
||||
\ 'version': doc_version,
|
||||
\ 'languageId': languageId,
|
||||
\ 'text': s:BufferText(bufnr)}
|
||||
call self.Notify('textDocument/didOpen', {'textDocument': td_item})
|
||||
let self.open_buffers[bufnr] = {
|
||||
\ 'uri': doc.uri,
|
||||
\ 'version': doc_version,
|
||||
\ 'languageId': languageId}
|
||||
else
|
||||
let vtd_id = {
|
||||
\ 'uri': doc.uri,
|
||||
\ 'version': doc_version}
|
||||
call self.Notify('textDocument/didChange', {
|
||||
\ 'textDocument': vtd_id,
|
||||
\ 'contentChanges': [{'text': s:BufferText(bufnr)}]})
|
||||
let self.open_buffers[bufnr].version = doc_version
|
||||
endif
|
||||
let doc.version = doc_version
|
||||
endfor
|
||||
call timer_start(0, { _ -> s:SendRequest(self, request) })
|
||||
return call('s:SetUpRequest', [self, s:id, a:method, a:params] + a:000)
|
||||
endfunction
|
||||
|
||||
function! s:AgentCall(method, params, ...) dict abort
|
||||
let request = call(self.Request, [a:method, a:params] + a:000)
|
||||
if a:0
|
||||
return request
|
||||
endif
|
||||
return request.Await()
|
||||
endfunction
|
||||
|
||||
function! s:AgentCancel(request) dict abort
|
||||
if has_key(self.requests, get(a:request, 'id', ''))
|
||||
call remove(self.requests, a:request.id)
|
||||
call self.Notify('$/cancelRequest', {'id': a:request.id})
|
||||
endif
|
||||
if get(a:request, 'status', '') ==# 'running'
|
||||
let a:request.status = 'canceled'
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:RequestCancel() dict abort
|
||||
let agent = self.Agent()
|
||||
if !empty(agent)
|
||||
call agent.Cancel(self)
|
||||
elseif get(self, 'status', '') ==# 'running'
|
||||
let self.status = 'canceled'
|
||||
endif
|
||||
return self
|
||||
endfunction
|
||||
|
||||
function! s:DispatchMessage(agent, handler, id, params, ...) abort
|
||||
try
|
||||
let response = {'result': call(a:handler, [a:params])}
|
||||
if response.result is# 0
|
||||
let response.result = v:null
|
||||
endif
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
let response = {'error': {'code': -32000, 'message': v:exception}}
|
||||
endtry
|
||||
if !empty(a:id)
|
||||
call s:Send(a:agent, extend({'id': a:id}, response))
|
||||
endif
|
||||
return response
|
||||
endfunction
|
||||
|
||||
function! s:OnMessage(agent, body, ...) abort
|
||||
if !has_key(a:body, 'method')
|
||||
return s:OnResponse(a:agent, a:body)
|
||||
endif
|
||||
let request = a:body
|
||||
let id = get(request, 'id', v:null)
|
||||
let params = get(request, 'params', v:null)
|
||||
if has_key(a:agent.methods, request.method)
|
||||
return s:DispatchMessage(a:agent, a:agent.methods[request.method], id, params)
|
||||
elseif !empty(id)
|
||||
call s:Send(a:agent, {"id": id, "error": {"code": -32700, "message": "Method not found: " . request.method}})
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:OnResponse(agent, response, ...) abort
|
||||
let response = a:response
|
||||
let id = get(a:response, 'id', v:null)
|
||||
if !has_key(a:agent.requests, id)
|
||||
return
|
||||
endif
|
||||
let request = remove(a:agent.requests, id)
|
||||
if request.status ==# 'canceled'
|
||||
return
|
||||
endif
|
||||
let request.waiting = {}
|
||||
let resolve = remove(request, 'resolve')
|
||||
let reject = remove(request, 'reject')
|
||||
if has_key(response, 'result')
|
||||
let request.status = 'success'
|
||||
let request.result = response.result
|
||||
for Cb in resolve
|
||||
let request.waiting[timer_start(0, function('s:Callback', [request, 'result', Cb]))] = 1
|
||||
endfor
|
||||
else
|
||||
let request.status = 'error'
|
||||
let request.error = response.error
|
||||
for Cb in reject
|
||||
let request.waiting[timer_start(0, function('s:Callback', [request, 'error', Cb]))] = 1
|
||||
endfor
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:OnErr(agent, line, ...) abort
|
||||
call copilot#logger#Debug('<-! ' . a:line)
|
||||
endfunction
|
||||
|
||||
function! s:OnExit(agent, code, ...) abort
|
||||
let a:agent.exit_status = a:code
|
||||
if has_key(a:agent, 'job')
|
||||
call remove(a:agent, 'job')
|
||||
endif
|
||||
if has_key(a:agent, 'client_id')
|
||||
call remove(a:agent, 'client_id')
|
||||
endif
|
||||
let code = a:code < 0 || a:code > 255 ? 256 : a:code
|
||||
for id in sort(keys(a:agent.requests), { a, b -> +a > +b })
|
||||
call s:RejectRequest(remove(a:agent.requests, id), {'code': code, 'message': 'Agent exited', 'data': {'status': a:code}})
|
||||
endfor
|
||||
call timer_start(0, { _ -> get(s:instances, a:agent.id) is# a:agent ? remove(s:instances, a:agent.id) : {} })
|
||||
call copilot#logger#Info('agent exited with status ' . a:code)
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#LspInit(agent_id, initialize_result) abort
|
||||
if !has_key(s:instances, a:agent_id)
|
||||
return
|
||||
endif
|
||||
let instance = s:instances[a:agent_id]
|
||||
call timer_start(0, { _ -> s:GetCapabilitiesResult(a:initialize_result, instance)})
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#LspExit(agent_id, code, signal) abort
|
||||
if !has_key(s:instances, a:agent_id)
|
||||
return
|
||||
endif
|
||||
let instance = remove(s:instances, a:agent_id)
|
||||
call s:OnExit(instance, a:code)
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#LspResponse(agent_id, opts, ...) abort
|
||||
if !has_key(s:instances, a:agent_id)
|
||||
return
|
||||
endif
|
||||
call s:OnResponse(s:instances[a:agent_id], a:opts)
|
||||
endfunction
|
||||
|
||||
function! s:LspRequest(method, params, ...) dict abort
|
||||
let id = v:lua.require'_copilot'.lsp_request(self.id, a:method, a:params)
|
||||
if id isnot# v:null
|
||||
return call('s:SetUpRequest', [self, id, a:method, a:params] + a:000)
|
||||
endif
|
||||
if has_key(self, 'client_id')
|
||||
call copilot#agent#LspExit(self.client_id, -1, -1)
|
||||
endif
|
||||
throw 'copilot#agent: LSP client not available'
|
||||
endfunction
|
||||
|
||||
function! s:LspClose() dict abort
|
||||
if !has_key(self, 'client_id')
|
||||
return
|
||||
endif
|
||||
return luaeval('vim.lsp.get_client_by_id(_A).stop()', self.client_id)
|
||||
endfunction
|
||||
|
||||
function! s:LspNotify(method, params) dict abort
|
||||
return v:lua.require'_copilot'.rpc_notify(self.id, a:method, a:params)
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#LspHandle(agent_id, request) abort
|
||||
if !has_key(s:instances, a:agent_id)
|
||||
return
|
||||
endif
|
||||
return s:OnMessage(s:instances[a:agent_id], a:request)
|
||||
endfunction
|
||||
|
||||
function! s:GetNodeVersion(command) abort
|
||||
let out = []
|
||||
let err = []
|
||||
let status = copilot#job#Stream(a:command + ['--version'], function('add', [out]), function('add', [err]))
|
||||
let string = matchstr(join(out, ''), '^v\zs\d\+\.[^[:space:]]*')
|
||||
if status != 0
|
||||
let string = ''
|
||||
endif
|
||||
let major = str2nr(string)
|
||||
let minor = str2nr(matchstr(string, '\.\zs\d\+'))
|
||||
return {'status': status, 'string': string, 'major': major, 'minor': minor}
|
||||
endfunction
|
||||
|
||||
function! s:Command() abort
|
||||
if !has('nvim-0.6') && v:version < 900
|
||||
return [v:null, '', 'Vim version too old']
|
||||
endif
|
||||
let node = get(g:, 'copilot_node_command', '')
|
||||
if empty(node)
|
||||
let node = ['node']
|
||||
elseif type(node) == type('')
|
||||
let node = [expand(node)]
|
||||
endif
|
||||
if !executable(get(node, 0, ''))
|
||||
if get(node, 0, '') ==# 'node'
|
||||
return [v:null, '', 'Node.js not found in PATH']
|
||||
else
|
||||
return [v:null, '', 'Node.js executable `' . get(node, 0, '') . "' not found"]
|
||||
endif
|
||||
endif
|
||||
let node_version = s:GetNodeVersion(node)
|
||||
let warning = ''
|
||||
if !get(g:, 'copilot_ignore_node_version') && node_version.major < 18 && get(node, 0, '') !=# 'node' && executable('node')
|
||||
let node_version_from_path = s:GetNodeVersion(['node'])
|
||||
if node_version_from_path.major >= 18
|
||||
let warning = 'Ignoring g:copilot_node_command: Node.js ' . node_version.string . ' is end-of-life'
|
||||
let node = ['node']
|
||||
let node_version = node_version_from_path
|
||||
endif
|
||||
endif
|
||||
if node_version.status != 0
|
||||
return [v:null, '', 'Node.js exited with status ' . node_version.status]
|
||||
endif
|
||||
if !get(g:, 'copilot_ignore_node_version')
|
||||
if node_version.major == 0
|
||||
return [v:null, node_version.string, 'Could not determine Node.js version']
|
||||
elseif node_version.major < 16 || node_version.major == 16 && node_version.minor < 14 || node_version.major == 17 && node_version.minor < 3
|
||||
" 16.14+ and 17.3+ still work for now, but are end-of-life
|
||||
return [v:null, node_version.string, 'Node.js version 18.x or newer required but found ' . node_version.string]
|
||||
endif
|
||||
endif
|
||||
let agent = get(g:, 'copilot_agent_command', '')
|
||||
if empty(agent) || !filereadable(agent)
|
||||
let agent = s:root . '/dist/agent.js'
|
||||
if !filereadable(agent)
|
||||
return [v:null, node_version.string, 'Could not find dist/agent.js (bad install?)']
|
||||
endif
|
||||
endif
|
||||
return [node + [agent, '--stdio'], node_version.string, warning]
|
||||
endfunction
|
||||
|
||||
function! s:UrlDecode(str) abort
|
||||
return substitute(a:str, '%\(\x\x\)', '\=iconv(nr2char("0x".submatch(1)), "utf-8", "latin1")', 'g')
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#EditorInfo() abort
|
||||
if !exists('s:editor_version')
|
||||
if has('nvim')
|
||||
let s:editor_version = matchstr(execute('version'), 'NVIM v\zs[^[:space:]]\+')
|
||||
else
|
||||
let s:editor_version = (v:version / 100) . '.' . (v:version % 100) . (exists('v:versionlong') ? printf('.%04d', v:versionlong % 1000) : '')
|
||||
endif
|
||||
endif
|
||||
let info = {
|
||||
\ 'editorInfo': {'name': has('nvim') ? 'Neovim': 'Vim', 'version': s:editor_version},
|
||||
\ 'editorPluginInfo': {'name': 'copilot.vim', 'version': s:plugin_version}}
|
||||
if type(get(g:, 'copilot_proxy')) == v:t_string
|
||||
let proxy = g:copilot_proxy
|
||||
else
|
||||
let proxy = ''
|
||||
endif
|
||||
let match = matchlist(proxy, '\c^\%([^:]\+://\)\=\%(\([^/#]\+@\)\)\=\%(\([^/:#]\+\)\|\[\([[:xdigit:]:]\+\)\]\)\%(:\(\d\+\)\)\=\%(/\|$\|?strict_\=ssl=\(.*\)\)')
|
||||
if !empty(match)
|
||||
let info.networkProxy = {'host': match[2] . match[3], 'port': empty(match[4]) ? 80 : +match[4]}
|
||||
if match[5] =~? '^[0f]'
|
||||
let info.networkProxy.rejectUnauthorized = v:false
|
||||
elseif match[5] =~? '^[1t]'
|
||||
let info.networkProxy.rejectUnauthorized = v:true
|
||||
elseif exists('g:copilot_proxy_strict_ssl')
|
||||
let info.networkProxy.rejectUnauthorized = empty(g:copilot_proxy_strict_ssl) ? v:false : v:true
|
||||
endif
|
||||
if !empty(match[1])
|
||||
let info.networkProxy.username = s:UrlDecode(matchstr(match[1], '^[^:@]*'))
|
||||
let info.networkProxy.password = s:UrlDecode(matchstr(match[1], ':\zs[^@]*'))
|
||||
endif
|
||||
endif
|
||||
return info
|
||||
endfunction
|
||||
|
||||
function! s:GetCapabilitiesResult(result, agent) abort
|
||||
let a:agent.capabilities = get(a:result, 'capabilities', {})
|
||||
let info = copilot#agent#EditorInfo()
|
||||
call a:agent.Request('setEditorInfo', extend({'editorConfiguration': a:agent.editorConfiguration}, info))
|
||||
endfunction
|
||||
|
||||
function! s:GetCapabilitiesError(error, agent) abort
|
||||
if a:error.code == s:error_exit
|
||||
let a:agent.startup_error = 'Agent exited with status ' . a:error.data.status
|
||||
else
|
||||
let a:agent.startup_error = 'Unexpected error ' . a:error.code . ' calling agent: ' . a:error.message
|
||||
call a:agent.Close()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:AgentStartupError() dict abort
|
||||
while (has_key(self, 'job') || has_key(self, 'client_id')) && !has_key(self, 'startup_error') && !has_key(self, 'capabilities')
|
||||
sleep 10m
|
||||
endwhile
|
||||
if has_key(self, 'capabilities')
|
||||
return ''
|
||||
else
|
||||
return get(self, 'startup_error', 'Something unexpected went wrong spawning the agent')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#New(...) abort
|
||||
let opts = a:0 ? a:1 : {}
|
||||
let instance = {'requests': {},
|
||||
\ 'editorConfiguration': get(opts, 'editorConfiguration', {}),
|
||||
\ 'Close': function('s:AgentClose'),
|
||||
\ 'Notify': function('s:AgentNotify'),
|
||||
\ 'Request': function('s:AgentRequest'),
|
||||
\ 'Call': function('s:AgentCall'),
|
||||
\ 'Cancel': function('s:AgentCancel'),
|
||||
\ 'StartupError': function('s:AgentStartupError'),
|
||||
\ }
|
||||
let instance.methods = extend({
|
||||
\ 'LogMessage': function('s:LogMessage'),
|
||||
\ 'window/logMessage': function('s:LogMessage'),
|
||||
\ }, get(opts, 'methods', {}))
|
||||
let [command, node_version, command_error] = s:Command()
|
||||
if len(command_error)
|
||||
if empty(command)
|
||||
let instance.id = -1
|
||||
let instance.startup_error = command_error
|
||||
return instance
|
||||
else
|
||||
let instance.node_version_warning = command_error
|
||||
endif
|
||||
endif
|
||||
let instance.node_version = node_version
|
||||
if has('nvim')
|
||||
call extend(instance, {
|
||||
\ 'Close': function('s:LspClose'),
|
||||
\ 'Notify': function('s:LspNotify'),
|
||||
\ 'Request': function('s:LspRequest')})
|
||||
let instance.client_id = v:lua.require'_copilot'.lsp_start_client(command, keys(instance.methods))
|
||||
let instance.id = instance.client_id
|
||||
else
|
||||
let state = {'headers': {}, 'mode': 'headers', 'buffer': ''}
|
||||
let instance.open_buffers = {}
|
||||
let instance.methods = extend({'window/showMessageRequest': function('s:ShowMessageRequest')}, instance.methods)
|
||||
let instance.job = job_start(command, {
|
||||
\ 'cwd': copilot#job#Cwd(),
|
||||
\ 'in_mode': 'lsp',
|
||||
\ 'out_mode': 'lsp',
|
||||
\ 'out_cb': { j, d -> timer_start(0, function('s:OnMessage', [instance, d])) },
|
||||
\ 'err_cb': { j, d -> timer_start(0, function('s:OnErr', [instance, d])) },
|
||||
\ 'exit_cb': { j, d -> timer_start(0, function('s:OnExit', [instance, d])) },
|
||||
\ })
|
||||
let instance.id = exists('*jobpid') ? jobpid(instance.job) : job_info(instance.job).process
|
||||
let capabilities = {'workspace': {'workspaceFolders': v:true}, 'copilot': {}}
|
||||
for name in keys(instance.methods)
|
||||
if name =~# '^copilot/'
|
||||
let capabilities.copilot[matchstr(name, '/\zs.*')] = v:true
|
||||
endif
|
||||
endfor
|
||||
let request = instance.Request('initialize', {'capabilities': capabilities}, function('s:GetCapabilitiesResult'), function('s:GetCapabilitiesError'), instance)
|
||||
endif
|
||||
let s:instances[instance.id] = instance
|
||||
return instance
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#Cancel(request) abort
|
||||
if type(a:request) == type({}) && has_key(a:request, 'Cancel')
|
||||
call a:request.Cancel()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:Callback(request, type, callback, timer) abort
|
||||
call remove(a:request.waiting, a:timer)
|
||||
if has_key(a:request, a:type)
|
||||
call a:callback(a:request[a:type])
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#Result(request, callback) abort
|
||||
if has_key(a:request, 'resolve')
|
||||
call add(a:request.resolve, a:callback)
|
||||
elseif has_key(a:request, 'result')
|
||||
let a:request.waiting[timer_start(0, function('s:Callback', [a:request, 'result', a:callback]))] = 1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#Error(request, callback) abort
|
||||
if has_key(a:request, 'reject')
|
||||
call add(a:request.reject, a:callback)
|
||||
elseif has_key(a:request, 'error')
|
||||
let a:request.waiting[timer_start(0, function('s:Callback', [a:request, 'error', a:callback]))] = 1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:CloseBuffer(bufnr) abort
|
||||
for instance in values(s:instances)
|
||||
try
|
||||
if has_key(instance, 'job') && has_key(instance.open_buffers, a:bufnr)
|
||||
let buffer = remove(instance.open_buffers, a:bufnr)
|
||||
call instance.Notify('textDocument/didClose', {'textDocument': {'uri': buffer.uri}})
|
||||
endif
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
endtry
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
augroup copilot_agent
|
||||
autocmd!
|
||||
if !has('nvim')
|
||||
autocmd BufUnload * call s:CloseBuffer(+expand('<abuf>'))
|
||||
endif
|
||||
augroup END
|
|
@ -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
|
|
@ -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
|
|
@ -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 ', '', ''), '\.\@<!\.\.\.\@!')
|
||||
if frame =~# '[\/]'
|
||||
call add(stacklines, '[redacted]')
|
||||
else
|
||||
call add(stacklines, substitute(frame, '^<SNR>\d\+_', '<SID>', ''))
|
||||
endif
|
||||
endfor
|
||||
call agent.Request('telemetry/exception', {
|
||||
\ 'origin': 'copilot.vim',
|
||||
\ 'stacktrace': join([v:exception] + stacklines, "\n")
|
||||
\ })
|
||||
endif
|
||||
endif
|
||||
endfunction
|
|
@ -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 <CR> 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 <buffer><script> <CR> <Cmd>exe copilot#panel#Accept()<CR>
|
||||
nmap <buffer><script> [[ <Cmd>call search('^─\{9,}\n.', 'bWe')<CR>
|
||||
nmap <buffer><script> ]] <Cmd>call search('^─\{9,}\n.', 'We')<CR>
|
||||
endfunction
|
||||
|
||||
function! s:BufReadCmd() abort
|
||||
setlocal bufhidden=wipe buftype=nofile nobuflisted readonly nomodifiable
|
||||
let state = get(b:, 'copilot_panel')
|
||||
if type(state) != v:t_dict
|
||||
return
|
||||
endif
|
||||
call s:Initialize(state)
|
||||
call s:Render(expand('<amatch>'))
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
function! copilot#panel#Open(opts) abort
|
||||
let s:panel_id += 1
|
||||
let state = {'solutions': {}, 'filetype': &filetype, 'line': getline('.'), 'bufnr': bufnr(''), 'tabstop': &tabstop}
|
||||
let bufname = 'copilot:///' . s:panel_id
|
||||
let params = copilot#doc#Params({'panelId': bufname})
|
||||
let state.was_insert = mode() =~# '^[iR]'
|
||||
if state.was_insert
|
||||
stopinsert
|
||||
else
|
||||
let params.doc.position.character = copilot#doc#UTF16Width(state.line)
|
||||
let params.position.character = params.doc.position.character
|
||||
endif
|
||||
let response = copilot#Request('getPanelCompletions', params).Wait()
|
||||
if response.status ==# 'error'
|
||||
return 'echoerr ' . string(response.error.message)
|
||||
endif
|
||||
let state.count_target = response.result.solutionCountTarget
|
||||
exe substitute(a:opts.mods, '\C\<tab\>', '-tab', 'g') 'keepalt split' bufname
|
||||
let b:copilot_panel = state
|
||||
call s:Initialize(state)
|
||||
call s:Render(@%)
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
augroup github_copilot_panel
|
||||
autocmd!
|
||||
autocmd BufReadCmd copilot:///* exe s:BufReadCmd()
|
||||
augroup END
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
100260
sources_non_forked/copilot.vim/dist/resources/cl100k/tokenizer_cushman002.json
vendored
Normal file
100260
sources_non_forked/copilot.vim/dist/resources/cl100k/tokenizer_cushman002.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
100001
sources_non_forked/copilot.vim/dist/resources/cl100k/vocab_cushman002.bpe
vendored
Normal file
100001
sources_non_forked/copilot.vim/dist/resources/cl100k/vocab_cushman002.bpe
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,167 @@
|
|||
*copilot.txt* GitHub Copilot - Your AI pair programmer
|
||||
|
||||
GETTING STARTED *copilot*
|
||||
|
||||
Invoke `:Copilot setup` to authenticate and enable GitHub Copilot.
|
||||
|
||||
Suggestions are displayed inline and can be accepted by pressing <Tab>. If
|
||||
inline suggestions do not appear to be working, invoke `:Copilot status` to
|
||||
verify Copilot is enabled and not experiencing any issues.
|
||||
|
||||
COMMANDS *:Copilot*
|
||||
|
||||
*:Copilot_disable*
|
||||
:Copilot disable Globally disable GitHub Copilot inline suggestions.
|
||||
|
||||
*:Copilot_enable*
|
||||
:Copilot enable Re-enable GitHub Copilot after :Copilot disable.
|
||||
|
||||
*:Copilot_setup*
|
||||
:Copilot setup Authenticate and enable GitHub Copilot.
|
||||
|
||||
*:Copilot_signout*
|
||||
:Copilot signout Sign out of GitHub Copilot.
|
||||
|
||||
*:Copilot_status*
|
||||
:Copilot status Check if GitHub Copilot is operational for the current
|
||||
buffer and report on any issues.
|
||||
|
||||
*:Copilot_panel*
|
||||
:Copilot panel Open a window with up to 10 completions for the
|
||||
current buffer. Use <CR> to accept a solution. Maps
|
||||
are also provided for [[ and ]] to jump from solution
|
||||
to solution. This is the default command if :Copilot
|
||||
is called without an argument.
|
||||
|
||||
*:Copilot_version*
|
||||
:Copilot version Show version information.
|
||||
|
||||
*:Copilot_feedback*
|
||||
:Copilot feedback Open the website for providing GitHub Copilot
|
||||
feedback. Be sure to include |:Copilot_version|
|
||||
output when reporting a bug.
|
||||
|
||||
OPTIONS *copilot-options*
|
||||
|
||||
*g:copilot_filetypes*
|
||||
g:copilot_filetypes A dictionary mapping file types to their enabled
|
||||
status. Most file types are enabled by default, so
|
||||
generally this is used for opting out.
|
||||
>
|
||||
let g:copilot_filetypes = {
|
||||
\ 'xml': v:false,
|
||||
\ }
|
||||
<
|
||||
Disabling all file types can be done by setting the
|
||||
special key "*". File types can then be turned back
|
||||
on individually.
|
||||
>
|
||||
let g:copilot_filetypes = {
|
||||
\ '*': v:false,
|
||||
\ 'python': v:true,
|
||||
\ }
|
||||
<
|
||||
*b:copilot_enabled*
|
||||
b:copilot_enabled Set to v:false to disable GitHub Copilot for the
|
||||
current buffer. Or set to v:true to force enabling
|
||||
it, overriding g:copilot_filetypes.
|
||||
|
||||
*g:copilot_node_command*
|
||||
g:copilot_node_command Tell Copilot what `node` binary to use with
|
||||
g:copilot_node_command. This is useful if the `node`
|
||||
in your PATH is an unsupported version.
|
||||
>
|
||||
let g:copilot_node_command =
|
||||
\ "~/.nodenv/versions/18.18.0/bin/node"
|
||||
<
|
||||
*g:copilot_proxy*
|
||||
g:copilot_proxy Tell Copilot what proxy server to use. This is a
|
||||
string in the format of `hostname:port` or
|
||||
`username:password@host:port`.
|
||||
>
|
||||
let g:copilot_proxy = 'localhost:3128'
|
||||
<
|
||||
*g:copilot_proxy_strict_ssl*
|
||||
g:copilot_proxy_strict_ssl
|
||||
Corporate proxies sometimes use a man-in-the-middle
|
||||
SSL certificate which is incompatible with GitHub
|
||||
Copilot. To work around this, SSL certificate
|
||||
verification can be disabled:
|
||||
>
|
||||
let g:copilot_proxy_strict_ssl = v:false
|
||||
<
|
||||
MAPS *copilot-maps*
|
||||
|
||||
*copilot-i_<Tab>*
|
||||
Copilot.vim uses <Tab> to accept the current suggestion. If you have an
|
||||
existing <Tab> map, that will be used as the fallback when no suggestion is
|
||||
displayed.
|
||||
|
||||
*copilot#Accept()*
|
||||
If you'd rather use a key that isn't <Tab>, define an <expr> map that calls
|
||||
copilot#Accept(). Here's an example with CTRL-J:
|
||||
>
|
||||
imap <silent><script><expr> <C-J> copilot#Accept("\<CR>")
|
||||
let g:copilot_no_tab_map = v:true
|
||||
<
|
||||
Lua version:
|
||||
>
|
||||
vim.keymap.set('i', '<C-J>', 'copilot#Accept("\<CR>")', {
|
||||
expr = true,
|
||||
replace_keycodes = false
|
||||
})
|
||||
vim.g.copilot_no_tab_map = true
|
||||
<
|
||||
The argument to copilot#Accept() is the fallback for when no suggestion is
|
||||
displayed. In this example, a regular carriage return is used. If no
|
||||
fallback is desired, use an argument of "" (an empty string).
|
||||
|
||||
Other Maps ~
|
||||
|
||||
Note that M- (a.k.a. meta or alt) maps are highly dependent on your terminal
|
||||
to function correctly and may be unsupported with your setup. As an
|
||||
alternative, you can create your own versions that invoke the <Plug> maps
|
||||
instead. Here's an example that maps CTRL-L to accept one word of the
|
||||
current suggestion:
|
||||
>
|
||||
imap <C-L> <Plug>(copilot-accept-word)
|
||||
<
|
||||
Lua version:
|
||||
>
|
||||
vim.keymap.set('i', '<C-L>', '<Plug>(copilot-accept-word)')
|
||||
<
|
||||
*copilot-i_CTRL-]*
|
||||
<C-]> Dismiss the current suggestion.
|
||||
<Plug>(copilot-dismiss)
|
||||
|
||||
*copilot-i_ALT-]*
|
||||
<M-]> Cycle to the next suggestion, if one is available.
|
||||
<Plug>(copilot-next)
|
||||
|
||||
*copilot-i_ALT-[*
|
||||
<M-[> Cycle to the previous suggestion.
|
||||
<Plug>(copilot-previous)
|
||||
|
||||
*copilot-i_ALT-\*
|
||||
<M-\> Explicitly request a suggestion, even if Copilot
|
||||
<Plug>(copilot-suggest) is disabled.
|
||||
|
||||
*copilot-i_ALT-Right*
|
||||
<M-Right> Accept the next word of the current suggestion.
|
||||
<Plug>(copilot-accept-word)
|
||||
|
||||
*copilot-i_ALT-CTRL-Right*
|
||||
|
||||
<M-C-Right> Accept the next line of the current suggestion.
|
||||
<Plug>(copilot-accept-line)
|
||||
|
||||
SYNTAX HIGHLIGHTING *copilot-highlighting*
|
||||
|
||||
Inline suggestions are highlighted using the CopilotSuggestion group,
|
||||
defaulting to a medium gray. The best place to override this is a file named
|
||||
after/colors/<colorschemename>.vim in your 'runtimepath' (e.g.,
|
||||
~/.config/nvim/after/colors/solarized.vim). Example declaration:
|
||||
>
|
||||
highlight CopilotSuggestion guifg=#555555 ctermfg=8
|
||||
<
|
||||
vim:tw=78:et:ft=help:norl:
|
|
@ -0,0 +1,75 @@
|
|||
local copilot = {}
|
||||
|
||||
copilot.lsp_start_client = function(cmd, handler_names)
|
||||
local capabilities = vim.lsp.protocol.make_client_capabilities()
|
||||
local handlers = {}
|
||||
local id
|
||||
for _, name in ipairs(handler_names) do
|
||||
handlers[name] = function(err, result)
|
||||
if result then
|
||||
local retval = vim.call('copilot#agent#LspHandle', id, {method = name, params = result})
|
||||
if type(retval) == 'table' then return retval.result, retval.error end
|
||||
end
|
||||
end
|
||||
if name:match('^copilot/') then
|
||||
capabilities.copilot = capabilities.copilot or {}
|
||||
capabilities.copilot[name:match('^copilot/(.*)$')] = true
|
||||
end
|
||||
end
|
||||
id = vim.lsp.start_client({
|
||||
cmd = cmd,
|
||||
cmd_cwd = vim.call('copilot#job#Cwd'),
|
||||
name = 'copilot',
|
||||
capabilities = capabilities,
|
||||
handlers = handlers,
|
||||
get_language_id = function(bufnr, filetype)
|
||||
return vim.call('copilot#doc#LanguageForFileType', filetype)
|
||||
end,
|
||||
on_init = function(client, initialize_result)
|
||||
vim.call('copilot#agent#LspInit', client.id, initialize_result)
|
||||
end,
|
||||
on_exit = function(code, signal, client_id)
|
||||
vim.schedule(function()
|
||||
vim.call('copilot#agent#LspExit', client_id, code, signal)
|
||||
end)
|
||||
end
|
||||
})
|
||||
return id
|
||||
end
|
||||
|
||||
copilot.lsp_request = function(client_id, method, params)
|
||||
local client = vim.lsp.get_client_by_id(client_id)
|
||||
if not client then return end
|
||||
vim.lsp.buf_attach_client(0, client_id)
|
||||
for _, doc in ipairs({params.doc, params.textDocument}) do
|
||||
if doc and type(doc.uri) == 'number' then
|
||||
local bufnr = doc.uri
|
||||
vim.lsp.buf_attach_client(bufnr, client_id)
|
||||
doc.uri = vim.uri_from_bufnr(bufnr)
|
||||
doc.version = vim.lsp.util.buf_versions[bufnr]
|
||||
end
|
||||
end
|
||||
local _, id
|
||||
_, id = client.request(method, params, function(err, result)
|
||||
vim.call('copilot#agent#LspResponse', client_id, {id = id, error = err, result = result})
|
||||
end)
|
||||
return id
|
||||
end
|
||||
|
||||
copilot.rpc_request = function(client_id, method, params)
|
||||
local client = vim.lsp.get_client_by_id(client_id)
|
||||
if not client then return end
|
||||
local _, id
|
||||
_, id = client.rpc.request(method, params, function(err, result)
|
||||
vim.call('copilot#agent#LspResponse', client_id, {id = id, error = err, result = result})
|
||||
end)
|
||||
return id
|
||||
end
|
||||
|
||||
copilot.rpc_notify = function(client_id, method, params)
|
||||
local client = vim.lsp.get_client_by_id(client_id)
|
||||
if not client then return end
|
||||
return client.rpc.notify(method, params)
|
||||
end
|
||||
|
||||
return copilot
|
|
@ -0,0 +1,113 @@
|
|||
if exists('g:loaded_copilot')
|
||||
finish
|
||||
endif
|
||||
let g:loaded_copilot = 1
|
||||
|
||||
scriptencoding utf-8
|
||||
|
||||
command! -bang -nargs=? -range=-1 -complete=customlist,copilot#CommandComplete Copilot exe copilot#Command(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)
|
||||
|
||||
if v:version < 800 || !exists('##CompleteChanged')
|
||||
finish
|
||||
endif
|
||||
|
||||
function! s:ColorScheme() abort
|
||||
if &t_Co == 256
|
||||
hi def CopilotSuggestion guifg=#808080 ctermfg=244
|
||||
else
|
||||
hi def CopilotSuggestion guifg=#808080 ctermfg=8
|
||||
endif
|
||||
hi def link CopilotAnnotation Normal
|
||||
endfunction
|
||||
|
||||
function! s:MapTab() abort
|
||||
if get(g:, 'copilot_no_tab_map') || get(g:, 'copilot_no_maps')
|
||||
return
|
||||
endif
|
||||
let tab_map = maparg('<Tab>', 'i', 0, 1)
|
||||
if !has_key(tab_map, 'rhs')
|
||||
imap <script><silent><nowait><expr> <Tab> copilot#Accept()
|
||||
elseif tab_map.rhs !~# 'copilot'
|
||||
if tab_map.expr
|
||||
let tab_fallback = '{ -> ' . tab_map.rhs . ' }'
|
||||
else
|
||||
let tab_fallback = substitute(json_encode(tab_map.rhs), '<', '\\<', 'g')
|
||||
endif
|
||||
let tab_fallback = substitute(tab_fallback, '<SID>', '<SNR>' . get(tab_map, 'sid') . '_', 'g')
|
||||
if get(tab_map, 'noremap') || get(tab_map, 'script') || mapcheck('<Left>', 'i') || mapcheck('<Del>', 'i')
|
||||
exe 'imap <script><silent><nowait><expr> <Tab> copilot#Accept(' . tab_fallback . ')'
|
||||
else
|
||||
exe 'imap <silent><nowait><expr> <Tab> copilot#Accept(' . tab_fallback . ')'
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:Event(type) abort
|
||||
try
|
||||
call call('copilot#On' . a:type, [])
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
augroup github_copilot
|
||||
autocmd!
|
||||
autocmd InsertLeave * call s:Event('InsertLeave')
|
||||
autocmd BufLeave * if mode() =~# '^[iR]'|call s:Event('InsertLeave')|endif
|
||||
autocmd InsertEnter * call s:Event('InsertEnter')
|
||||
autocmd BufEnter * if mode() =~# '^[iR]'|call s:Event('InsertEnter')|endif
|
||||
autocmd CursorMovedI * call s:Event('CursorMovedI')
|
||||
autocmd CompleteChanged * call s:Event('CompleteChanged')
|
||||
autocmd ColorScheme,VimEnter * call s:ColorScheme()
|
||||
autocmd VimEnter * call s:MapTab()
|
||||
autocmd BufUnload * call s:Event('BufUnload')
|
||||
autocmd VimLeavePre * call s:Event('VimLeavePre')
|
||||
autocmd BufReadCmd copilot://* setlocal buftype=nofile bufhidden=wipe nobuflisted readonly nomodifiable
|
||||
augroup END
|
||||
|
||||
call s:ColorScheme()
|
||||
call s:MapTab()
|
||||
if !get(g:, 'copilot_no_maps')
|
||||
imap <Plug>(copilot-dismiss) <Cmd>call copilot#Dismiss()<CR>
|
||||
if empty(mapcheck('<C-]>', 'i'))
|
||||
imap <silent><script><nowait><expr> <C-]> copilot#Dismiss() . "\<C-]>"
|
||||
endif
|
||||
imap <Plug>(copilot-next) <Cmd>call copilot#Next()<CR>
|
||||
imap <Plug>(copilot-previous) <Cmd>call copilot#Previous()<CR>
|
||||
imap <Plug>(copilot-suggest) <Cmd>call copilot#Suggest()<CR>
|
||||
imap <script><silent><nowait><expr> <Plug>(copilot-accept-word) copilot#AcceptWord()
|
||||
imap <script><silent><nowait><expr> <Plug>(copilot-accept-line) copilot#AcceptLine()
|
||||
try
|
||||
if !has('nvim') && &encoding ==# 'utf-8'
|
||||
" avoid 8-bit meta collision with UTF-8 characters
|
||||
let s:restore_encoding = 1
|
||||
set encoding=cp949
|
||||
endif
|
||||
if empty(mapcheck('<M-]>', 'i'))
|
||||
imap <M-]> <Plug>(copilot-next)
|
||||
endif
|
||||
if empty(mapcheck('<M-[>', 'i'))
|
||||
imap <M-[> <Plug>(copilot-previous)
|
||||
endif
|
||||
if empty(mapcheck('<M-Bslash>', 'i'))
|
||||
imap <M-Bslash> <Plug>(copilot-suggest)
|
||||
endif
|
||||
if empty(mapcheck('<M-Right>', 'i'))
|
||||
imap <M-Right> <Plug>(copilot-accept-word)
|
||||
endif
|
||||
if empty(mapcheck('<M-C-Right>', 'i'))
|
||||
imap <M-Down> <Plug>(copilot-accept-line)
|
||||
endif
|
||||
finally
|
||||
if exists('s:restore_encoding')
|
||||
set encoding=utf-8
|
||||
endif
|
||||
endtry
|
||||
endif
|
||||
|
||||
call copilot#Init()
|
||||
|
||||
let s:dir = expand('<sfile>:h:h')
|
||||
if getftime(s:dir . '/doc/copilot.txt') > getftime(s:dir . '/doc/tags')
|
||||
silent! execute 'helptags' fnameescape(s:dir . '/doc')
|
||||
endif
|
|
@ -0,0 +1,19 @@
|
|||
scriptencoding utf-8
|
||||
|
||||
if exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
|
||||
let s:subtype = matchstr(&l:filetype, '\<copilot\.\zs[[:alnum:]_-]\+')
|
||||
if !empty(s:subtype) && s:subtype !=# 'copilot'
|
||||
exe 'syn include @copilotLanguageTop syntax/' . s:subtype . '.vim'
|
||||
unlet! b:current_syntax
|
||||
endif
|
||||
|
||||
syn region copilotHeader start="\%^" end="^─\@="
|
||||
syn region copilotSolution matchgroup=copilotSeparator start="^─\{9,}$" end="\%(^─\{9,\}$\)\@=\|\%$" keepend contains=@copilotLanguageTop
|
||||
|
||||
hi def link copilotHeader PreProc
|
||||
hi def link copilotSeparator Comment
|
||||
|
||||
let b:current_syntax = "copilot"
|
Loading…
Reference in New Issue