cleanup submodules
This commit is contained in:
@@ -1,2 +0,0 @@
|
|||||||
*.vim eol=lf
|
|
||||||
/copilot-language-server/** -whitespace -diff
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
At the moment we are not accepting contributions to the repository.
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
name: Auto-close PR
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
types: [opened, reopened]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
close:
|
|
||||||
name: Run
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
pull-requests: write
|
|
||||||
steps:
|
|
||||||
- run: |
|
|
||||||
gh pr close ${{ github.event.pull_request.number }} --comment \
|
|
||||||
"At the moment we are not accepting contributions to the repository.
|
|
||||||
|
|
||||||
Feedback for Copilot.vim can be given in the [feedback forum](https://github.com/github/copilot.vim/issues)."
|
|
||||||
env:
|
|
||||||
GH_REPO: ${{ github.repository }}
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
/doc/tags
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
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.
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
# GitHub Copilot for Vim and Neovim
|
|
||||||
|
|
||||||
GitHub Copilot is an AI pair programmer tool that helps you write code faster
|
|
||||||
and smarter. 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).
|
|
||||||
|
|
||||||
## Getting access to GitHub Copilot
|
|
||||||
|
|
||||||
To access GitHub Copilot, an active GitHub Copilot subscription is required.
|
|
||||||
Sign up for [GitHub Copilot Free](https://github.com/settings/copilot), or
|
|
||||||
request access from your enterprise admin.
|
|
||||||
|
|
||||||
## Getting started
|
|
||||||
|
|
||||||
1. Install [Neovim][] or the latest patch of [Vim][] (9.0.0185 or newer).
|
|
||||||
|
|
||||||
2. Install [Node.js][]. If you use a package manager, make sure to install
|
|
||||||
NPM as well (e.g., `apt install nodejs npm` on Debian/Ubuntu).
|
|
||||||
|
|
||||||
3. Install `github/copilot.vim` using vim-plug, lazy.nvim, or any other
|
|
||||||
plugin manager. Or to install manually, run one of the following
|
|
||||||
commands:
|
|
||||||
|
|
||||||
* Vim, Linux/macOS:
|
|
||||||
|
|
||||||
git clone --depth=1 https://github.com/github/copilot.vim.git \
|
|
||||||
~/.vim/pack/github/start/copilot.vim
|
|
||||||
|
|
||||||
* Neovim, Linux/macOS:
|
|
||||||
|
|
||||||
git clone --depth=1 https://github.com/github/copilot.vim.git \
|
|
||||||
~/.config/nvim/pack/github/start/copilot.vim
|
|
||||||
|
|
||||||
* Vim, Windows (PowerShell command):
|
|
||||||
|
|
||||||
git clone --depth=1 https://github.com/github/copilot.vim.git `
|
|
||||||
$HOME/vimfiles/pack/github/start/copilot.vim
|
|
||||||
|
|
||||||
* Neovim, Windows (PowerShell command):
|
|
||||||
|
|
||||||
git clone --depth=1 https://github.com/github/copilot.vim.git `
|
|
||||||
$HOME/AppData/Local/nvim/pack/github/start/copilot.vim
|
|
||||||
|
|
||||||
4. Start Vim/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/github/copilot.vim/issues).
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
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.
|
|
||||||
@@ -1,859 +0,0 @@
|
|||||||
scriptencoding utf-8
|
|
||||||
|
|
||||||
let s:has_nvim_ghost_text = has('nvim-0.8')
|
|
||||||
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! copilot#Init(...) abort
|
|
||||||
call copilot#util#Defer({ -> exists('s:client') || s:Start() })
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Running() abort
|
|
||||||
return exists('s:client.job') || exists('s:client.client_id')
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Start() abort
|
|
||||||
if s:Running() || exists('s:client.startup_error')
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
let s:client = copilot#client#New()
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Stop() abort
|
|
||||||
if exists('s:client')
|
|
||||||
let client = remove(s:, 'client')
|
|
||||||
call client.Close()
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#Client() abort
|
|
||||||
call s:Start()
|
|
||||||
return s:client
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#RunningClient() abort
|
|
||||||
if s:Running()
|
|
||||||
return s:client
|
|
||||||
else
|
|
||||||
return v:null
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
if has('nvim-0.8') && !has(luaeval('vim.version().api_prerelease') ? 'nvim-0.9.1' : 'nvim-0.9.0')
|
|
||||||
let s:editor_warning = 'Neovim 0.8 support is deprecated and will be dropped in a future release of copilot.vim.'
|
|
||||||
endif
|
|
||||||
if has('vim_starting') && exists('s:editor_warning')
|
|
||||||
call copilot#logger#Warn(s:editor_warning)
|
|
||||||
endif
|
|
||||||
function! s:EditorVersionWarning() abort
|
|
||||||
if exists('s:editor_warning')
|
|
||||||
echohl WarningMsg
|
|
||||||
echo 'Warning: ' . s:editor_warning
|
|
||||||
echohl None
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#Request(method, params, ...) abort
|
|
||||||
let client = copilot#Client()
|
|
||||||
return call(client.Request, [a:method, a:params] + a:000)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#Call(method, params, ...) abort
|
|
||||||
let client = copilot#Client()
|
|
||||||
return call(client.Call, [a:method, a:params] + a:000)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#Notify(method, params, ...) abort
|
|
||||||
let client = copilot#Client()
|
|
||||||
return call(client.Notify, [a:method, a:params] + a:000)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#NvimNs() abort
|
|
||||||
return nvim_create_namespace('github-copilot')
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#Clear() abort
|
|
||||||
if exists('g:_copilot_timer')
|
|
||||||
call timer_stop(remove(g:, '_copilot_timer'))
|
|
||||||
endif
|
|
||||||
if exists('b:_copilot')
|
|
||||||
call copilot#client#Cancel(get(b:_copilot, 'first', {}))
|
|
||||||
call copilot#client#Cancel(get(b:_copilot, 'cycling', {}))
|
|
||||||
endif
|
|
||||||
call s:UpdatePreview()
|
|
||||||
unlet! b:_copilot
|
|
||||||
return ''
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#Dismiss() abort
|
|
||||||
call copilot#Clear()
|
|
||||||
call s:UpdatePreview()
|
|
||||||
return ''
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
let s:filetype_defaults = {
|
|
||||||
\ 'gitcommit': 0,
|
|
||||||
\ 'gitrebase': 0,
|
|
||||||
\ 'hgcommit': 0,
|
|
||||||
\ 'svn': 0,
|
|
||||||
\ 'cvs': 0,
|
|
||||||
\ '.': 0}
|
|
||||||
|
|
||||||
function! s:BufferDisabled() abort
|
|
||||||
if &buftype =~# '^\%(help\|prompt\|quickfix\|terminal\)$'
|
|
||||||
return 5
|
|
||||||
endif
|
|
||||||
if exists('b:copilot_disabled')
|
|
||||||
return empty(b:copilot_disabled) ? 0 : 3
|
|
||||||
endif
|
|
||||||
if exists('b:copilot_enabled')
|
|
||||||
return empty(b:copilot_enabled) ? 4 : 0
|
|
||||||
endif
|
|
||||||
let short = empty(&l:filetype) ? '.' : split(&l:filetype, '\.', 1)[0]
|
|
||||||
let config = {}
|
|
||||||
if type(get(g:, 'copilot_filetypes')) == v:t_dict
|
|
||||||
let config = g:copilot_filetypes
|
|
||||||
endif
|
|
||||||
if has_key(config, &l:filetype)
|
|
||||||
return empty(config[&l:filetype])
|
|
||||||
elseif has_key(config, short)
|
|
||||||
return empty(config[short])
|
|
||||||
elseif has_key(config, '*')
|
|
||||||
return empty(config['*'])
|
|
||||||
else
|
|
||||||
return get(s:filetype_defaults, short, 1) == 0 ? 2 : 0
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#Enabled() abort
|
|
||||||
return get(g:, 'copilot_enabled', 1)
|
|
||||||
\ && empty(s:BufferDisabled())
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
let s:inline_invoked = 1
|
|
||||||
let s:inline_automatic = 2
|
|
||||||
|
|
||||||
function! copilot#Complete(...) abort
|
|
||||||
if exists('g:_copilot_timer')
|
|
||||||
call timer_stop(remove(g:, '_copilot_timer'))
|
|
||||||
endif
|
|
||||||
let target = [bufnr(''), getbufvar('', 'changedtick'), line('.'), col('.')]
|
|
||||||
if !exists('b:_copilot.target') || b:_copilot.target !=# target
|
|
||||||
if exists('b:_copilot.first')
|
|
||||||
call copilot#client#Cancel(b:_copilot.first)
|
|
||||||
endif
|
|
||||||
if exists('b:_copilot.cycling')
|
|
||||||
call copilot#client#Cancel(b:_copilot.cycling)
|
|
||||||
endif
|
|
||||||
let params = {
|
|
||||||
\ 'textDocument': {'uri': bufnr('')},
|
|
||||||
\ 'position': copilot#util#AppendPosition(),
|
|
||||||
\ 'formattingOptions': {'insertSpaces': &expandtab ? v:true : v:false, 'tabSize': shiftwidth()},
|
|
||||||
\ 'context': {'triggerKind': s:inline_automatic}}
|
|
||||||
let b:_copilot = {
|
|
||||||
\ 'target': target,
|
|
||||||
\ 'params': params,
|
|
||||||
\ 'first': copilot#Request('textDocument/inlineCompletion', params)}
|
|
||||||
let g:_copilot_last = b:_copilot
|
|
||||||
endif
|
|
||||||
let completion = b:_copilot.first
|
|
||||||
if !a:0
|
|
||||||
return completion.Await()
|
|
||||||
else
|
|
||||||
call copilot#client#Result(completion, function(a:1, [b:_copilot]))
|
|
||||||
if a:0 > 1
|
|
||||||
call copilot#client#Error(completion, function(a:2, [b:_copilot]))
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:HideDuringCompletion() abort
|
|
||||||
return get(g:, 'copilot_hide_during_completion', 1)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:SuggestionTextWithAdjustments() abort
|
|
||||||
let empty = ['', 0, '', {}]
|
|
||||||
try
|
|
||||||
if mode() !~# '^[iR]' || (s:HideDuringCompletion() && pumvisible()) || !exists('b:_copilot.suggestions')
|
|
||||||
return empty
|
|
||||||
endif
|
|
||||||
let choice = get(b:_copilot.suggestions, b:_copilot.choice, {})
|
|
||||||
if !has_key(choice, 'range') || choice.range.start.line != line('.') - 1 || type(choice.insertText) !=# v:t_string
|
|
||||||
return empty
|
|
||||||
endif
|
|
||||||
let line = getline('.')
|
|
||||||
let offset = col('.') - 1
|
|
||||||
let byte_offset = copilot#util#UTF16ToByteIdx(line, choice.range.start.character)
|
|
||||||
let choice_text = strpart(line, 0, byte_offset) .
|
|
||||||
\ substitute(substitute(choice.insertText, '\r\n\=', '\n', 'g'), '\n*$', '', '')
|
|
||||||
let typed = strpart(line, 0, offset)
|
|
||||||
let end_offset = copilot#util#UTF16ToByteIdx(line, choice.range.end.character)
|
|
||||||
if end_offset < 0
|
|
||||||
let end_offset = len(line)
|
|
||||||
endif
|
|
||||||
let delete = strpart(line, offset, end_offset - offset)
|
|
||||||
if typed =~# '^\s*$'
|
|
||||||
let leading = strpart(matchstr(choice_text, '^\s\+'), 0, len(typed))
|
|
||||||
let unindented = strpart(choice_text, len(leading))
|
|
||||||
if strpart(typed, 0, len(leading)) ==# leading && unindented !=# delete
|
|
||||||
return [unindented, len(typed) - len(leading), delete, choice]
|
|
||||||
endif
|
|
||||||
elseif typed ==# strpart(choice_text, 0, offset)
|
|
||||||
return [strpart(choice_text, offset), 0, delete, choice]
|
|
||||||
endif
|
|
||||||
catch
|
|
||||||
call copilot#logger#Exception()
|
|
||||||
endtry
|
|
||||||
return empty
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
|
|
||||||
function! s:Advance(count, context, ...) abort
|
|
||||||
if a:context isnot# get(b:, '_copilot', {})
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
let a:context.choice += a:count
|
|
||||||
if a:context.choice < 0
|
|
||||||
let a:context.choice += len(a:context.suggestions)
|
|
||||||
endif
|
|
||||||
let a:context.choice %= len(a:context.suggestions)
|
|
||||||
call s:UpdatePreview()
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:GetSuggestionsCyclingCallback(context, result) abort
|
|
||||||
let callbacks = remove(a:context, 'cycling_callbacks')
|
|
||||||
let seen = {}
|
|
||||||
for suggestion in a:context.suggestions
|
|
||||||
let seen[suggestion.insertText] = 1
|
|
||||||
endfor
|
|
||||||
for suggestion in get(a:result, 'items', [])
|
|
||||||
if !has_key(seen, suggestion.insertText)
|
|
||||||
call add(a:context.suggestions, suggestion)
|
|
||||||
let seen[suggestion.insertText] = 1
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
for Callback in callbacks
|
|
||||||
call Callback(a:context)
|
|
||||||
endfor
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:GetSuggestionsCycling(callback) abort
|
|
||||||
if exists('b:_copilot.cycling_callbacks')
|
|
||||||
call add(b:_copilot.cycling_callbacks, a:callback)
|
|
||||||
elseif exists('b:_copilot.cycling')
|
|
||||||
call a:callback(b:_copilot)
|
|
||||||
elseif exists('b:_copilot.suggestions')
|
|
||||||
let params = deepcopy(b:_copilot.first.params)
|
|
||||||
let params.context.triggerKind = s:inline_invoked
|
|
||||||
let b:_copilot.cycling_callbacks = [a:callback]
|
|
||||||
let b:_copilot.cycling = copilot#Request('textDocument/inlineCompletion',
|
|
||||||
\ params,
|
|
||||||
\ function('s:GetSuggestionsCyclingCallback', [b:_copilot]),
|
|
||||||
\ function('s:GetSuggestionsCyclingCallback', [b:_copilot]),
|
|
||||||
\ )
|
|
||||||
call s:UpdatePreview()
|
|
||||||
endif
|
|
||||||
return ''
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#Next() abort
|
|
||||||
return s:GetSuggestionsCycling(function('s:Advance', [1]))
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#Previous() abort
|
|
||||||
return s:GetSuggestionsCycling(function('s:Advance', [-1]))
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#GetDisplayedSuggestion() abort
|
|
||||||
let [text, outdent, delete, item] = s:SuggestionTextWithAdjustments()
|
|
||||||
|
|
||||||
return {
|
|
||||||
\ 'item': item,
|
|
||||||
\ 'text': text,
|
|
||||||
\ 'outdentSize': outdent,
|
|
||||||
\ 'deleteSize': strchars(delete),
|
|
||||||
\ 'deleteChars': 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_chars, item] = s:SuggestionTextWithAdjustments()
|
|
||||||
let delete = strchars(delete_chars)
|
|
||||||
let text = split(text, "\r\n\\=\\|\n", 1)
|
|
||||||
if empty(text[-1])
|
|
||||||
call remove(text, -1)
|
|
||||||
endif
|
|
||||||
if empty(text) || !s:has_ghost_text
|
|
||||||
return s:ClearPreview()
|
|
||||||
endif
|
|
||||||
if exists('b:_copilot.cycling_callbacks')
|
|
||||||
let annot = '(1/…)'
|
|
||||||
elseif exists('b:_copilot.cycling')
|
|
||||||
let annot = '(' . (b:_copilot.choice + 1) . '/' . len(b:_copilot.suggestions) . ')'
|
|
||||||
else
|
|
||||||
let annot = ''
|
|
||||||
endif
|
|
||||||
call s:ClearPreview()
|
|
||||||
if s:has_nvim_ghost_text
|
|
||||||
let data = {'id': 1}
|
|
||||||
let data.virt_text_pos = 'overlay'
|
|
||||||
let append = strpart(getline('.'), col('.') - 1 + delete)
|
|
||||||
let data.virt_text = [[text[0] . append . repeat(' ', delete - len(text[0])), s:hlgroup]]
|
|
||||||
if len(text) > 1
|
|
||||||
let data.virt_lines = map(text[1:-1], { _, l -> [[l, s:hlgroup]] })
|
|
||||||
if !empty(annot)
|
|
||||||
let data.virt_lines[-1] += [[' '], [annot, s:annot_hlgroup]]
|
|
||||||
endif
|
|
||||||
elseif len(annot)
|
|
||||||
let data.virt_text += [[' '], [annot, s:annot_hlgroup]]
|
|
||||||
endif
|
|
||||||
let data.hl_mode = 'combine'
|
|
||||||
call nvim_buf_set_extmark(0, copilot#NvimNs(), line('.')-1, col('.')-1, data)
|
|
||||||
elseif s:has_vim_ghost_text
|
|
||||||
let new_suffix = text[0]
|
|
||||||
let current_suffix = getline('.')[col('.') - 1 :]
|
|
||||||
let inset = ''
|
|
||||||
while delete > 0 && !empty(new_suffix)
|
|
||||||
let last_char = matchstr(new_suffix, '.$')
|
|
||||||
let new_suffix = matchstr(new_suffix, '^.\{-\}\ze.$')
|
|
||||||
if last_char ==# matchstr(current_suffix, '.$')
|
|
||||||
if !empty(inset)
|
|
||||||
call prop_add(line('.'), col('.') + len(current_suffix), {'type': s:hlgroup, 'text': inset})
|
|
||||||
let inset = ''
|
|
||||||
endif
|
|
||||||
let current_suffix = matchstr(current_suffix, '^.\{-\}\ze.$')
|
|
||||||
let delete -= 1
|
|
||||||
else
|
|
||||||
let inset = last_char . inset
|
|
||||||
endif
|
|
||||||
endwhile
|
|
||||||
if !empty(new_suffix . inset)
|
|
||||||
call prop_add(line('.'), col('.'), {'type': s:hlgroup, 'text': new_suffix . inset})
|
|
||||||
endif
|
|
||||||
for line in text[1:]
|
|
||||||
call prop_add(line('.'), 0, {'type': s:hlgroup, 'text_align': 'below', 'text': line})
|
|
||||||
endfor
|
|
||||||
if !empty(annot)
|
|
||||||
call prop_add(line('.'), col('$'), {'type': s:annot_hlgroup, 'text': ' ' . annot})
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
call copilot#Notify('textDocument/didShowCompletion', {'item': item})
|
|
||||||
catch
|
|
||||||
return copilot#logger#Exception()
|
|
||||||
endtry
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:HandleTriggerResult(state, result) abort
|
|
||||||
let a:state.suggestions = type(a:result) == type([]) ? a:result : get(empty(a:result) ? {} : a:result, 'items', [])
|
|
||||||
let a:state.choice = 0
|
|
||||||
if get(b:, '_copilot') is# a:state
|
|
||||||
call s:UpdatePreview()
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:HandleTriggerError(state, result) abort
|
|
||||||
let a:state.suggestions = []
|
|
||||||
let a:state.choice = 0
|
|
||||||
let a:state.error = a:result
|
|
||||||
if get(b:, '_copilot') is# a:state
|
|
||||||
call s:UpdatePreview()
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#Suggest() abort
|
|
||||||
if !s:Running()
|
|
||||||
return ''
|
|
||||||
endif
|
|
||||||
try
|
|
||||||
call copilot#Complete(function('s:HandleTriggerResult'), function('s:HandleTriggerError'))
|
|
||||||
catch
|
|
||||||
call copilot#logger#Exception()
|
|
||||||
endtry
|
|
||||||
return ''
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Trigger(bufnr, timer) abort
|
|
||||||
let timer = get(g:, '_copilot_timer', -1)
|
|
||||||
if a:bufnr !=# bufnr('') || a:timer isnot# timer || mode() !=# 'i'
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
unlet! g:_copilot_timer
|
|
||||||
return copilot#Suggest()
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#Schedule() abort
|
|
||||||
if !s:has_ghost_text || !s:Running() || !copilot#Enabled()
|
|
||||||
call copilot#Clear()
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
call s:UpdatePreview()
|
|
||||||
let delay = get(g:, 'copilot_idle_delay', 45)
|
|
||||||
call timer_stop(get(g:, '_copilot_timer', -1))
|
|
||||||
let g:_copilot_timer = timer_start(delay, function('s:Trigger', [bufnr('')]))
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Attach(bufnr, ...) abort
|
|
||||||
try
|
|
||||||
return copilot#Client().Attach(a:bufnr)
|
|
||||||
catch
|
|
||||||
call copilot#logger#Exception()
|
|
||||||
endtry
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#OnFileType() abort
|
|
||||||
if empty(s:BufferDisabled()) && &l:modifiable && &l:buflisted
|
|
||||||
call copilot#util#Defer(function('s:Attach'), bufnr(''))
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Focus(bufnr, ...) abort
|
|
||||||
if s:Running() && copilot#Client().IsAttached(a:bufnr)
|
|
||||||
call copilot#Client().Notify('textDocument/didFocus', {'textDocument': {'uri': copilot#Client().Attach(a:bufnr).uri}})
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#OnBufEnter() abort
|
|
||||||
let bufnr = bufnr('')
|
|
||||||
call copilot#util#Defer(function('s:Focus'), bufnr)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#OnInsertLeavePre() abort
|
|
||||||
call copilot#Clear()
|
|
||||||
call s:ClearPreview()
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#OnInsertEnter() abort
|
|
||||||
return copilot#Schedule()
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#OnCompleteChanged() abort
|
|
||||||
if s:HideDuringCompletion()
|
|
||||||
return copilot#Clear()
|
|
||||||
else
|
|
||||||
return copilot#Schedule()
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#OnCursorMovedI() abort
|
|
||||||
return copilot#Schedule()
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#OnBufUnload() abort
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#OnVimLeavePre() abort
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#TextQueuedForInsertion() abort
|
|
||||||
try
|
|
||||||
return remove(s:, 'suggestion_text')
|
|
||||||
catch
|
|
||||||
return ''
|
|
||||||
endtry
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#Accept(...) abort
|
|
||||||
let s = copilot#GetDisplayedSuggestion()
|
|
||||||
if !empty(s.text)
|
|
||||||
unlet! b:_copilot
|
|
||||||
let text = ''
|
|
||||||
if a:0 > 1
|
|
||||||
let text = substitute(matchstr(s.text, "\n*" . '\%(' . a:2 .'\)'), "\n*$", '', '')
|
|
||||||
endif
|
|
||||||
if empty(text)
|
|
||||||
let text = s.text
|
|
||||||
endif
|
|
||||||
let delete_chars = s.deleteChars
|
|
||||||
let leftover = strpart(s.text, strlen(text))
|
|
||||||
let idx = strridx(leftover, matchstr(delete_chars, '.$'))
|
|
||||||
while !empty(delete_chars) && idx != -1
|
|
||||||
let delete_chars = substitute(delete_chars, '.$', '', '')
|
|
||||||
let idx = strridx(leftover, matchstr(delete_chars, '.$'), idx - 1)
|
|
||||||
endwhile
|
|
||||||
if text ==# s.text && has_key(s.item, 'command')
|
|
||||||
call copilot#Request('workspace/executeCommand', s.item.command)
|
|
||||||
else
|
|
||||||
let line_text = strpart(getline('.'), 0, col('.') - 1) . text
|
|
||||||
call copilot#Notify('textDocument/didPartiallyAcceptCompletion', {
|
|
||||||
\ 'item': s.item,
|
|
||||||
\ 'acceptedLength': copilot#util#UTF16Width(line_text) - s.item.range.start.character})
|
|
||||||
endif
|
|
||||||
call s:ClearPreview()
|
|
||||||
let s:suggestion_text = text
|
|
||||||
let recall = text =~# "\n" ? "\<C-R>\<C-O>=" : "\<C-R>\<C-R>="
|
|
||||||
return repeat("\<Left>\<Del>", s.outdentSize) . repeat("\<Del>", strchars(delete_chars)) .
|
|
||||||
\ recall . "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! copilot#Browser() abort
|
|
||||||
if type(get(g:, 'copilot_browser')) == v:t_list
|
|
||||||
let cmd = copy(g:copilot_browser)
|
|
||||||
elseif type(get(g:, 'open_command')) == v:t_list
|
|
||||||
let cmd = copy(g:open_command)
|
|
||||||
elseif has('win32')
|
|
||||||
let cmd = ['rundll32', 'url.dll,FileProtocolHandler']
|
|
||||||
elseif has('mac')
|
|
||||||
let cmd = ['open']
|
|
||||||
elseif executable('wslview')
|
|
||||||
return ['wslview']
|
|
||||||
elseif executable('xdg-open')
|
|
||||||
return ['xdg-open']
|
|
||||||
else
|
|
||||||
return []
|
|
||||||
endif
|
|
||||||
if executable(get(cmd, 0, ''))
|
|
||||||
return cmd
|
|
||||||
else
|
|
||||||
return []
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
let s:commands = {}
|
|
||||||
|
|
||||||
function! s:EnabledStatusMessage() abort
|
|
||||||
let buf_disabled = s:BufferDisabled()
|
|
||||||
if !s:has_ghost_text
|
|
||||||
if has('nvim')
|
|
||||||
return "Neovim 0.6 required to support ghost text"
|
|
||||||
else
|
|
||||||
return "Vim " . s:vim_minimum_version . " required to support ghost text"
|
|
||||||
endif
|
|
||||||
elseif !get(g:, 'copilot_enabled', 1)
|
|
||||||
return 'Disabled globally by :Copilot disable'
|
|
||||||
elseif buf_disabled is# 5
|
|
||||||
return 'Disabled for current buffer by buftype=' . &buftype
|
|
||||||
elseif buf_disabled is# 4
|
|
||||||
return 'Disabled for current buffer by b:copilot_enabled'
|
|
||||||
elseif buf_disabled is# 3
|
|
||||||
return 'Disabled for current buffer by b:copilot_disabled'
|
|
||||||
elseif buf_disabled is# 2
|
|
||||||
return 'Disabled for filetype=' . &filetype . ' by internal default'
|
|
||||||
elseif buf_disabled
|
|
||||||
return 'Disabled for filetype=' . &filetype . ' by g:copilot_filetypes'
|
|
||||||
elseif !copilot#Enabled()
|
|
||||||
return 'BUG: Something is wrong with enabling/disabling'
|
|
||||||
else
|
|
||||||
return ''
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:VerifySetup() abort
|
|
||||||
let error = copilot#Client().StartupError()
|
|
||||||
if !empty(error)
|
|
||||||
echo 'Copilot: ' . error
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
|
|
||||||
if exists('s:client.status.kind') && s:client.status.kind ==# 'Error'
|
|
||||||
echo 'Copilot: Error: ' . get(s:client.status, 'message', 'unknown')
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
|
|
||||||
return 1
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:commands.status(opts) abort
|
|
||||||
if !s:VerifySetup()
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
|
|
||||||
if exists('s:client.status.kind') && s:client.status.kind ==# 'Warning'
|
|
||||||
echo 'Copilot: Warning: ' . get(s:client.status, 'message', 'unknown')
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
|
|
||||||
let status = s:EnabledStatusMessage()
|
|
||||||
if !empty(status)
|
|
||||||
echo 'Copilot: ' . status
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
|
|
||||||
echo 'Copilot: Ready'
|
|
||||||
call s:EditorVersionWarning()
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:commands.signout(opts) abort
|
|
||||||
echo 'Copilot: Signed out'
|
|
||||||
call copilot#Call('signOut', {})
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:commands.setup(opts) abort
|
|
||||||
let startup_error = copilot#Client().StartupError()
|
|
||||||
if !empty(startup_error)
|
|
||||||
echo 'Copilot: ' . startup_error
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
|
|
||||||
let data = copilot#Call('signIn', {})
|
|
||||||
|
|
||||||
if has_key(data, 'verificationUri')
|
|
||||||
let uri = data.verificationUri
|
|
||||||
if has('clipboard')
|
|
||||||
try
|
|
||||||
let @+ = data.userCode
|
|
||||||
catch
|
|
||||||
endtry
|
|
||||||
try
|
|
||||||
let @* = data.userCode
|
|
||||||
catch
|
|
||||||
endtry
|
|
||||||
endif
|
|
||||||
let codemsg = "First copy your one-time code: " . data.userCode . "\n"
|
|
||||||
try
|
|
||||||
if len(&mouse)
|
|
||||||
let mouse = &mouse
|
|
||||||
set mouse=
|
|
||||||
endif
|
|
||||||
if get(a:opts, 'bang')
|
|
||||||
call s:Echo(codemsg . "In your browser, visit " . uri)
|
|
||||||
let request = copilot#Request('signInConfirm', {})
|
|
||||||
else
|
|
||||||
call input(codemsg . "Press ENTER to open GitHub in your browser\n")
|
|
||||||
let request = copilot#Request('workspace/executeCommand', data.command)
|
|
||||||
endif
|
|
||||||
call s:Echo("Waiting for " . data.userCode . " at " . uri . " (could take up to 5 seconds)")
|
|
||||||
call request.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 data = request.result
|
|
||||||
endif
|
|
||||||
elseif get(data, 'status', '') isnot# 'AlreadySignedIn'
|
|
||||||
return 'echoerr ' . string('Copilot: Something went wrong')
|
|
||||||
endif
|
|
||||||
|
|
||||||
let user = get(data, 'user', '<unknown>')
|
|
||||||
|
|
||||||
echo 'Copilot: Authenticated as GitHub user ' . user
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
let s:commands.auth = s:commands.setup
|
|
||||||
let s:commands.signin = s:commands.setup
|
|
||||||
|
|
||||||
function! s:commands.help(opts) abort
|
|
||||||
return a:opts.mods . ' help ' . (len(a:opts.arg) ? ':Copilot_' . a:opts.arg : 'copilot')
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:commands.version(opts) abort
|
|
||||||
echo 'copilot.vim ' .copilot#client#EditorPluginInfo().version
|
|
||||||
let editorInfo = copilot#client#EditorInfo()
|
|
||||||
echo editorInfo.name . ' ' . editorInfo.version
|
|
||||||
if s:Running()
|
|
||||||
let versions = s:client.Request('getVersion', {})
|
|
||||||
if exists('s:client.serverInfo.version')
|
|
||||||
echo s:client.serverInfo.name . ' ' . s:client.serverInfo.version
|
|
||||||
else
|
|
||||||
echo 'GitHub Copilot Language Server ' . versions.Await().version
|
|
||||||
endif
|
|
||||||
if exists('s:client.node_version')
|
|
||||||
echo 'Node.js ' . s:client.node_version
|
|
||||||
else
|
|
||||||
echo 'Node.js ' . substitute(get(versions.Await(), 'runtimeVersion', '?'), '^node/', '', 'g')
|
|
||||||
endif
|
|
||||||
else
|
|
||||||
echo 'Not running'
|
|
||||||
if exists('s:client.node_version')
|
|
||||||
echo 'Node.js ' . s:client.node_version
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
if has('win32')
|
|
||||||
echo 'Windows'
|
|
||||||
elseif has('macunix')
|
|
||||||
echo 'macOS'
|
|
||||||
elseif !has('unix')
|
|
||||||
echo 'Unknown OS'
|
|
||||||
elseif isdirectory('/sys/kernel')
|
|
||||||
echo 'Linux'
|
|
||||||
else
|
|
||||||
echo 'UNIX'
|
|
||||||
endif
|
|
||||||
call s:EditorVersionWarning()
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:commands.restart(opts) abort
|
|
||||||
call s:Stop()
|
|
||||||
echo 'Copilot: Restarting language server'
|
|
||||||
call s:Start()
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:AfterUpgrade(old_version, client) abort
|
|
||||||
if exists('a:client.serverInfo.version')
|
|
||||||
call s:Echo('Copilot: Upgraded language server to ' . a:client.serverInfo.version)
|
|
||||||
let g:copilot_version = '^' . a:client.serverInfo.version
|
|
||||||
else
|
|
||||||
call s:Echo('Copilot: Failed to upgrade language server. Check log for details')
|
|
||||||
let g:copilot_version = a:old_version
|
|
||||||
if a:old_version is v:null
|
|
||||||
unlet g:copilot_version
|
|
||||||
endif
|
|
||||||
call s:Start()
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:commands.upgrade(opts) abort
|
|
||||||
if exists('s:client.serverInfo.version')
|
|
||||||
echo 'Copilot: Upgrading language server from version ' . s:client.serverInfo.version
|
|
||||||
else
|
|
||||||
echo 'Copilot: Upgrading language server'
|
|
||||||
endif
|
|
||||||
let old_version = get(g:, 'copilot_version', v:null)
|
|
||||||
let g:copilot_version = 'latest'
|
|
||||||
call s:Stop()
|
|
||||||
call s:Start()
|
|
||||||
call s:client.AfterInitialized(function('s:AfterUpgrade', [old_version]))
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:commands.disable(opts) abort
|
|
||||||
let g:copilot_enabled = 0
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:commands.enable(opts) abort
|
|
||||||
let g:copilot_enabled = 1
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:commands.panel(opts) abort
|
|
||||||
if s:VerifySetup()
|
|
||||||
return copilot#panel#Open(a:opts)
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:FmtModel(model) abort
|
|
||||||
return a:model.modelName . ' (' . a:model.id . ')'
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:commands.model(opts) abort
|
|
||||||
if !s:VerifySetup()
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
let client = copilot#Client()
|
|
||||||
let response = client.Request('copilot/models', {}).Wait()
|
|
||||||
if response.status ==# 'error'
|
|
||||||
return 'echoerr ' . string('Copilot: Error retrieving completions models: ' . response.error.message)
|
|
||||||
endif
|
|
||||||
let models = filter(response.result, { _, m -> index(m.scopes, 'completion') >= 0 })
|
|
||||||
if len(models) == 0
|
|
||||||
echo 'Copilot: Could not retrieve completions models'
|
|
||||||
elseif len(models) == 1
|
|
||||||
echo 'Copilot: Current/only completions model is ' . s:FmtModel(models[0])
|
|
||||||
else
|
|
||||||
let choices = map(copy(models), { i, m -> (i + 1) . '. ' . s:FmtModel(m) })
|
|
||||||
let choice = inputlist(['Select a completions model:'] + choices)
|
|
||||||
if choice < 1 || choice > len(models)
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
let model = models[choice - 1]
|
|
||||||
if type(get(g:, 'copilot_settings')) != v:t_dict
|
|
||||||
let g:copilot_settings = {}
|
|
||||||
endif
|
|
||||||
let g:copilot_settings.selectedCompletionModel = model.id
|
|
||||||
redraw
|
|
||||||
echo 'Copilot: Set completions model to ' . s:FmtModel(model)
|
|
||||||
call client.DidChangeConfiguration()
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:commands.log(opts) abort
|
|
||||||
return a:opts.mods . ' split +$ copilot:///log'
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#CommandComplete(arg, lead, pos) abort
|
|
||||||
let args = matchstr(strpart(a:lead, 0, a:pos), 'C\%[opilot][! ] *\zs.*')
|
|
||||||
if args !~# ' '
|
|
||||||
return sort(filter(map(keys(s:commands), { k, v -> tr(v, '_', '-') }),
|
|
||||||
\ { k, v -> strpart(v, 0, len(a:arg)) ==# a:arg }))
|
|
||||||
else
|
|
||||||
return []
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#Command(line1, line2, range, bang, mods, arg) abort
|
|
||||||
let cmd = matchstr(a:arg, '^\%(\\.\|\S\)\+')
|
|
||||||
let arg = matchstr(a:arg, '\s\zs\S.*')
|
|
||||||
if !empty(cmd) && !has_key(s:commands, tr(cmd, '-', '_'))
|
|
||||||
return 'echoerr ' . string('Copilot: unknown command ' . string(cmd))
|
|
||||||
endif
|
|
||||||
try
|
|
||||||
if empty(cmd)
|
|
||||||
if !s:Running()
|
|
||||||
let cmd = 'restart'
|
|
||||||
else
|
|
||||||
try
|
|
||||||
let opts = copilot#Call('checkStatus', {'options': {'localChecksOnly': v:true}})
|
|
||||||
if opts.status !=# 'OK' && opts.status !=# 'MaybeOK'
|
|
||||||
let cmd = 'setup'
|
|
||||||
else
|
|
||||||
let cmd = 'status'
|
|
||||||
endif
|
|
||||||
catch
|
|
||||||
call copilot#logger#Exception()
|
|
||||||
let cmd = 'log'
|
|
||||||
endtry
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
let opts = {'line1': a:line1, 'line2': a:line2, 'range': a:range, 'bang': a:bang, 'mods': a:mods, 'arg': arg}
|
|
||||||
let retval = s:commands[tr(cmd, '-', '_')](opts)
|
|
||||||
if type(retval) == v:t_string
|
|
||||||
return retval
|
|
||||||
else
|
|
||||||
return ''
|
|
||||||
endif
|
|
||||||
catch /^Copilot:/
|
|
||||||
return 'echoerr ' . string(v:exception)
|
|
||||||
endtry
|
|
||||||
endfunction
|
|
||||||
@@ -1,848 +0,0 @@
|
|||||||
scriptencoding utf-8
|
|
||||||
|
|
||||||
let s:plugin_version = copilot#version#String()
|
|
||||||
|
|
||||||
let s:error_canceled = {'code': -32800, 'message': 'Canceled'}
|
|
||||||
let s:error_exit = {'code': -32097, 'message': 'Process exited'}
|
|
||||||
let s:error_connection_inactive = {'code': -32096, 'message': 'Connection inactive'}
|
|
||||||
|
|
||||||
let s:root = expand('<sfile>:h:h:h')
|
|
||||||
|
|
||||||
if !exists('s:instances')
|
|
||||||
let s:instances = {}
|
|
||||||
endif
|
|
||||||
|
|
||||||
" allow sourcing this file to reload the Lua file too
|
|
||||||
if has('nvim')
|
|
||||||
lua package.loaded._copilot = nil
|
|
||||||
endif
|
|
||||||
|
|
||||||
function! s:Warn(msg) abort
|
|
||||||
if !empty(get(g:, 'copilot_no_startup_warnings'))
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
echohl WarningMsg
|
|
||||||
echomsg 'Copilot: ' . a:msg
|
|
||||||
echohl NONE
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:VimClose() dict abort
|
|
||||||
if !has_key(self, 'job')
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
let job = self.job
|
|
||||||
if has_key(self, 'kill')
|
|
||||||
call job_stop(job, 'kill')
|
|
||||||
call copilot#logger#Warn('Process forcefully terminated')
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
let self.kill = v:true
|
|
||||||
let self.shutdown = self.Request('shutdown', {}, function(self.Notify, ['exit']))
|
|
||||||
call timer_start(2000, { _ -> job_stop(job, 'kill') })
|
|
||||||
call copilot#logger#Debug('Process shutdown initiated')
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:LogSend(request, line) abort
|
|
||||||
return '--> ' . a:line
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:RejectRequest(request, error) abort
|
|
||||||
if a:request.status !=# 'running'
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
let a:request.waiting = {}
|
|
||||||
call remove(a:request, 'resolve')
|
|
||||||
let reject = remove(a:request, 'reject')
|
|
||||||
let a:request.status = 'error'
|
|
||||||
let a:request.error = deepcopy(a:error)
|
|
||||||
for Cb in reject
|
|
||||||
let a:request.waiting[timer_start(0, function('s:Callback', [a:request, 'error', Cb]))] = 1
|
|
||||||
endfor
|
|
||||||
if index([s:error_canceled.code, s:error_connection_inactive.code], a:error.code) != -1
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
let msg = 'Method ' . a:request.method . ' errored with E' . a:error.code . ': ' . json_encode(a:error.message)
|
|
||||||
if empty(reject)
|
|
||||||
call copilot#logger#Error(msg)
|
|
||||||
else
|
|
||||||
call copilot#logger#Debug(msg)
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:AfterInitialized(fn, ...) dict abort
|
|
||||||
call add(self.after_initialized, function(a:fn, [self] + a:000))
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:AlreadyInitialized(fn, ...) dict abort
|
|
||||||
return copilot#util#Defer(function(a:fn, [self] + a:000))
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Send(instance, request) abort
|
|
||||||
if !has_key(a:instance, 'job')
|
|
||||||
return v:false
|
|
||||||
endif
|
|
||||||
try
|
|
||||||
call ch_sendexpr(a:instance.job, a:request)
|
|
||||||
return v:true
|
|
||||||
catch /^Vim\%((\a\+)\)\=:E906:/
|
|
||||||
let a:instance.kill = v:true
|
|
||||||
let job = remove(a:instance, 'job')
|
|
||||||
call job_stop(job)
|
|
||||||
call timer_start(2000, { _ -> job_stop(job, 'kill') })
|
|
||||||
call copilot#logger#Warn('Terminating process after failed write')
|
|
||||||
return v:false
|
|
||||||
catch /^Vim\%((\a\+)\)\=:E631:/
|
|
||||||
return v:false
|
|
||||||
endtry
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:VimNotify(method, params) dict abort
|
|
||||||
let request = {'method': a:method, 'params': a:params}
|
|
||||||
call self.AfterInitialized(function('s:Send'), request)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:RequestWait() dict abort
|
|
||||||
while self.status ==# 'running'
|
|
||||||
sleep 1m
|
|
||||||
endwhile
|
|
||||||
while !empty(get(self, 'waiting', {}))
|
|
||||||
sleep 1m
|
|
||||||
endwhile
|
|
||||||
return self
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:RequestAwait() dict abort
|
|
||||||
call self.Wait()
|
|
||||||
if has_key(self, 'result')
|
|
||||||
return self.result
|
|
||||||
endif
|
|
||||||
throw 'Copilot:E' . self.error.code . ': ' . self.error.message
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:RequestClient() dict abort
|
|
||||||
return get(s:instances, self.client_id, v:null)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
if !exists('s:id')
|
|
||||||
let s:id = 0
|
|
||||||
endif
|
|
||||||
if !exists('s:progress_token_id')
|
|
||||||
let s:progress_token_id = 0
|
|
||||||
endif
|
|
||||||
|
|
||||||
function! s:SetUpRequest(instance, id, method, params, progress, ...) abort
|
|
||||||
let request = {
|
|
||||||
\ 'client_id': a:instance.id,
|
|
||||||
\ 'id': a:id,
|
|
||||||
\ 'method': a:method,
|
|
||||||
\ 'params': a:params,
|
|
||||||
\ 'Client': function('s:RequestClient'),
|
|
||||||
\ 'Wait': function('s:RequestWait'),
|
|
||||||
\ 'Await': function('s:RequestAwait'),
|
|
||||||
\ 'Cancel': function('s:RequestCancel'),
|
|
||||||
\ 'resolve': [],
|
|
||||||
\ 'reject': [],
|
|
||||||
\ 'progress': a:progress,
|
|
||||||
\ 'status': 'running'}
|
|
||||||
let args = a:000[2:-1]
|
|
||||||
if len(args)
|
|
||||||
if !empty(a:1)
|
|
||||||
call add(request.resolve, { v -> call(a:1, [v] + args)})
|
|
||||||
endif
|
|
||||||
if !empty(a:2)
|
|
||||||
call add(request.reject, { v -> call(a:2, [v] + args)})
|
|
||||||
endif
|
|
||||||
return request
|
|
||||||
endif
|
|
||||||
if a:0 && !empty(a:1)
|
|
||||||
call add(request.resolve, a:1)
|
|
||||||
endif
|
|
||||||
if a:0 > 1 && !empty(a:2)
|
|
||||||
call add(request.reject, a:2)
|
|
||||||
endif
|
|
||||||
return request
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:UrlEncode(str) abort
|
|
||||||
return substitute(iconv(a:str, 'latin1', 'utf-8'),'[^A-Za-z0-9._~!$&''()*+,;=:@/-]','\="%".printf("%02X",char2nr(submatch(0)))','g')
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
let s:slash = exists('+shellslash') ? '\' : '/'
|
|
||||||
function! s:UriFromBufnr(bufnr) abort
|
|
||||||
let absolute = tr(bufname(a:bufnr), s:slash, '/')
|
|
||||||
if absolute !~# '^\a\+:\|^/\|^$' && getbufvar(a:bufnr, 'buftype') =~# '^\%(nowrite\)\=$'
|
|
||||||
let absolute = substitute(tr(getcwd(), s:slash, '/'), '/\=$', '/', '') . absolute
|
|
||||||
endif
|
|
||||||
return s:UriFromPath(absolute)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:UriFromPath(absolute) abort
|
|
||||||
let absolute = a:absolute
|
|
||||||
if has('win32') && absolute =~# '^\a://\@!'
|
|
||||||
return 'file:///' . strpart(absolute, 0, 2) . s:UrlEncode(strpart(absolute, 2))
|
|
||||||
elseif absolute =~# '^/'
|
|
||||||
return 'file://' . s:UrlEncode(absolute)
|
|
||||||
elseif absolute =~# '^\a[[:alnum:].+-]*:\|^$'
|
|
||||||
return absolute
|
|
||||||
else
|
|
||||||
return ''
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:BufferText(bufnr) abort
|
|
||||||
return join(getbufline(a:bufnr, 1, '$'), "\n") . "\n"
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
let s:valid_request_key = '^\%(id\|method\|params\)$'
|
|
||||||
function! s:SendRequest(instance, request, ...) abort
|
|
||||||
if !has_key(a:instance, 'job') || get(a:instance, 'shutdown', a:request) isnot# a:request
|
|
||||||
return s:RejectRequest(a:request, s:error_connection_inactive)
|
|
||||||
endif
|
|
||||||
let json = filter(copy(a:request), 'v:key =~# s:valid_request_key')
|
|
||||||
if empty(s:Send(a:instance, json)) && has_key(a:request, 'id') && has_key(a:instance.requests, a:request.id)
|
|
||||||
call s:RejectRequest(remove(a:instance.requests, a:request.id), {'code': -32099, 'message': 'Write failed'})
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:RegisterWorkspaceFolderForBuffer(instance, buf) abort
|
|
||||||
let root = getbufvar(a:buf, 'workspace_folder')
|
|
||||||
if type(root) != v:t_string
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
let root = s:UriFromPath(substitute(root, '[\/]$', '', ''))
|
|
||||||
if empty(root) || has_key(a:instance.workspaceFolders, root)
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
let a:instance.workspaceFolders[root] = v:true
|
|
||||||
call a:instance.Notify('workspace/didChangeWorkspaceFolders', {'event': {'added': [{'uri': root, 'name': fnamemodify(root, ':t')}], 'removed': []}})
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:PreprocessParams(instance, params) abort
|
|
||||||
let bufnr = v:null
|
|
||||||
for doc in filter([get(a:params, 'textDocument', {})], 'type(get(v:val, "uri", "")) == v:t_number')
|
|
||||||
let bufnr = doc.uri
|
|
||||||
call s:RegisterWorkspaceFolderForBuffer(a:instance, bufnr)
|
|
||||||
call extend(doc, a:instance.Attach(bufnr))
|
|
||||||
endfor
|
|
||||||
let progress_tokens = []
|
|
||||||
for key in keys(a:params)
|
|
||||||
if key =~# 'Token$' && type(a:params[key]) == v:t_func
|
|
||||||
let s:progress_token_id += 1
|
|
||||||
let a:instance.progress[s:progress_token_id] = a:params[key]
|
|
||||||
call add(progress_tokens, s:progress_token_id)
|
|
||||||
let a:params[key] = s:progress_token_id
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
return [bufnr, progress_tokens]
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:VimAttach(bufnr) dict abort
|
|
||||||
if !bufloaded(a:bufnr)
|
|
||||||
return {'uri': '', 'version': 0}
|
|
||||||
endif
|
|
||||||
let bufnr = a:bufnr
|
|
||||||
let doc = {
|
|
||||||
\ 'uri': s:UriFromBufnr(bufnr),
|
|
||||||
\ 'version': getbufvar(bufnr, 'changedtick', 0),
|
|
||||||
\ 'languageId': getbufvar(bufnr, '&filetype'),
|
|
||||||
\ }
|
|
||||||
if has_key(self.open_buffers, bufnr) && (
|
|
||||||
\ self.open_buffers[bufnr].uri !=# doc.uri ||
|
|
||||||
\ self.open_buffers[bufnr].languageId !=# doc.languageId)
|
|
||||||
call self.Notify('textDocument/didClose', {'textDocument': {'uri': self.open_buffers[bufnr].uri}})
|
|
||||||
call remove(self.open_buffers, bufnr)
|
|
||||||
endif
|
|
||||||
if !has_key(self.open_buffers, bufnr)
|
|
||||||
call self.Notify('textDocument/didOpen', {'textDocument': extend({'text': s:BufferText(bufnr)}, doc)})
|
|
||||||
let self.open_buffers[bufnr] = doc
|
|
||||||
else
|
|
||||||
call self.Notify('textDocument/didChange', {
|
|
||||||
\ 'textDocument': {'uri': doc.uri, 'version': doc.version},
|
|
||||||
\ 'contentChanges': [{'text': s:BufferText(bufnr)}]})
|
|
||||||
let self.open_buffers[bufnr].version = doc.version
|
|
||||||
endif
|
|
||||||
return doc
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:VimIsAttached(bufnr) dict abort
|
|
||||||
return bufloaded(a:bufnr) && has_key(self.open_buffers, a:bufnr) ? v:true : v:false
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:VimRequest(method, params, ...) dict abort
|
|
||||||
let s:id += 1
|
|
||||||
let params = deepcopy(a:params)
|
|
||||||
let [_, progress] = s:PreprocessParams(self, params)
|
|
||||||
let request = call('s:SetUpRequest', [self, s:id, a:method, params, progress] + a:000)
|
|
||||||
call self.AfterInitialized(function('s:SendRequest'), request)
|
|
||||||
let self.requests[s:id] = request
|
|
||||||
return request
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Call(method, params, ...) dict abort
|
|
||||||
let request = call(self.Request, [a:method, a:params] + a:000)
|
|
||||||
if a:0
|
|
||||||
return request
|
|
||||||
endif
|
|
||||||
return request.Await()
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Cancel(request) dict abort
|
|
||||||
if has_key(self.requests, get(a:request, 'id', ''))
|
|
||||||
call self.Notify('$/cancelRequest', {'id': a:request.id})
|
|
||||||
call s:RejectRequest(remove(self.requests, a:request.id), s:error_canceled)
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:RequestCancel() dict abort
|
|
||||||
let instance = self.Client()
|
|
||||||
if !empty(instance)
|
|
||||||
call instance.Cancel(self)
|
|
||||||
elseif get(self, 'status', '') ==# 'running'
|
|
||||||
call s:RejectRequest(self, s:error_canceled)
|
|
||||||
endif
|
|
||||||
return self
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:DispatchMessage(instance, method, handler, id, params, ...) abort
|
|
||||||
try
|
|
||||||
let response = {'result': call(a:handler, [a:params, a:instance])}
|
|
||||||
if response.result is# 0
|
|
||||||
let response.result = v:null
|
|
||||||
endif
|
|
||||||
catch
|
|
||||||
call copilot#logger#Exception('lsp.request.' . a:method)
|
|
||||||
let response = {'error': {'code': -32000, 'message': v:exception}}
|
|
||||||
endtry
|
|
||||||
if a:id isnot# v:null
|
|
||||||
call s:Send(a:instance, extend({'id': a:id}, response))
|
|
||||||
endif
|
|
||||||
if !has_key(s:notifications, a:method)
|
|
||||||
return response
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:OnMessage(instance, body, ...) abort
|
|
||||||
if !has_key(a:body, 'method')
|
|
||||||
return s:OnResponse(a:instance, a:body)
|
|
||||||
endif
|
|
||||||
let request = a:body
|
|
||||||
let id = get(request, 'id', v:null)
|
|
||||||
let params = get(request, 'params', v:null)
|
|
||||||
if has_key(a:instance.methods, request.method)
|
|
||||||
return s:DispatchMessage(a:instance, request.method, a:instance.methods[request.method], id, params)
|
|
||||||
elseif id isnot# v:null
|
|
||||||
call s:Send(a:instance, {"id": id, "error": {"code": -32700, "message": "Method not found: " . request.method}})
|
|
||||||
call copilot#logger#Debug('Unexpected request ' . request.method . ' called with ' . json_encode(params))
|
|
||||||
elseif request.method !~# '^\$/'
|
|
||||||
call copilot#logger#Debug('Unexpected notification ' . request.method . ' called with ' . json_encode(params))
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:OnResponse(instance, response, ...) abort
|
|
||||||
let response = a:response
|
|
||||||
let id = get(a:response, 'id', v:null)
|
|
||||||
if !has_key(a:instance.requests, id)
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
let request = remove(a:instance.requests, id)
|
|
||||||
for progress_token in request.progress
|
|
||||||
if has_key(a:instance.progress, progress_token)
|
|
||||||
call remove(a:instance.progress, progress_token)
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
if request.status !=# 'running'
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
if has_key(response, 'result')
|
|
||||||
let request.waiting = {}
|
|
||||||
let resolve = remove(request, 'resolve')
|
|
||||||
call remove(request, 'reject')
|
|
||||||
let request.status = 'success'
|
|
||||||
let request.result = response.result
|
|
||||||
for Cb in resolve
|
|
||||||
let request.waiting[timer_start(0, function('s:Callback', [request, 'result', Cb]))] = 1
|
|
||||||
endfor
|
|
||||||
else
|
|
||||||
call s:RejectRequest(request, response.error)
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:OnErr(instance, ch, line, ...) abort
|
|
||||||
if !has_key(a:instance, 'serverInfo')
|
|
||||||
call copilot#logger#Bare('<-! ' . a:line)
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:FlushAfterInitialized(instance) abort
|
|
||||||
if has_key(a:instance, 'after_initialized')
|
|
||||||
let a:instance.AfterInitialized = function('s:AlreadyInitialized')
|
|
||||||
for Fn in remove(a:instance, 'after_initialized')
|
|
||||||
call copilot#util#Defer(Fn)
|
|
||||||
endfor
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:OnExit(instance, code, ...) abort
|
|
||||||
let a:instance.exit_status = a:code
|
|
||||||
if has_key(a:instance, 'job')
|
|
||||||
call remove(a:instance, 'job')
|
|
||||||
endif
|
|
||||||
if has_key(a:instance, 'client_id')
|
|
||||||
call remove(a:instance, 'client_id')
|
|
||||||
endif
|
|
||||||
let message = 'Process exited with status ' . a:code
|
|
||||||
if a:code >= 18 && a:code < 100
|
|
||||||
let message = 'Node.js too old. ' .
|
|
||||||
\ (get(a:instance.node, 0, 'node') ==# 'node' ? 'Upgrade' : 'Change g:copilot_node_command') .
|
|
||||||
\ ' to ' . a:code . '.x or newer'
|
|
||||||
endif
|
|
||||||
if !has_key(a:instance, 'serverInfo') && !has_key(a:instance, 'startup_error')
|
|
||||||
let a:instance.startup_error = message
|
|
||||||
endif
|
|
||||||
for id in sort(keys(a:instance.requests), { a, b -> +a > +b })
|
|
||||||
call s:RejectRequest(remove(a:instance.requests, id), s:error_exit)
|
|
||||||
endfor
|
|
||||||
call s:FlushAfterInitialized(a:instance)
|
|
||||||
call copilot#util#Defer({ -> get(s:instances, a:instance.id) is# a:instance ? remove(s:instances, a:instance.id) : {} })
|
|
||||||
if a:code == 0
|
|
||||||
call copilot#logger#Info(message)
|
|
||||||
else
|
|
||||||
call copilot#logger#Warn(message)
|
|
||||||
if !has_key(a:instance, 'kill')
|
|
||||||
call copilot#util#Defer(function('s:Warn'), message)
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#client#LspInit(id, initialize_result) abort
|
|
||||||
if !has_key(s:instances, a:id)
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
call s:PostInit(a:initialize_result, s:instances[a:id])
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#client#LspExit(id, code, signal) abort
|
|
||||||
if !has_key(s:instances, a:id)
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
let instance = remove(s:instances, a:id)
|
|
||||||
call s:OnExit(instance, a:code)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#client#LspResponse(id, opts, ...) abort
|
|
||||||
if !has_key(s:instances, a:id)
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
call s:OnResponse(s:instances[a:id], a:opts)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:NvimAttach(bufnr) dict abort
|
|
||||||
if !bufloaded(a:bufnr)
|
|
||||||
return {'uri': '', 'version': 0}
|
|
||||||
endif
|
|
||||||
call luaeval('pcall(vim.lsp.buf_attach_client, _A[1], _A[2])', [a:bufnr, self.id])
|
|
||||||
return luaeval('{uri = vim.uri_from_bufnr(_A), version = vim.lsp.util.buf_versions[_A]}', a:bufnr)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:NvimIsAttached(bufnr) dict abort
|
|
||||||
return bufloaded(a:bufnr) ? luaeval('vim.lsp.buf_is_attached(_A[1], _A[2])', [a:bufnr, self.id]) : v:false
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:NvimRequest(method, params, ...) dict abort
|
|
||||||
let params = deepcopy(a:params)
|
|
||||||
let [bufnr, progress] = s:PreprocessParams(self, params)
|
|
||||||
let request = call('s:SetUpRequest', [self, v:null, a:method, params, progress] + a:000)
|
|
||||||
call self.AfterInitialized(function('s:NvimDoRequest'), request, bufnr)
|
|
||||||
return request
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:NvimDoRequest(client, request, bufnr) abort
|
|
||||||
let request = a:request
|
|
||||||
if has_key(a:client, 'client_id') && !has_key(a:client, 'kill')
|
|
||||||
let request.id = eval("v:lua.require'_copilot'.lsp_request(a:client.id, a:request.method, a:request.params, a:bufnr)")
|
|
||||||
endif
|
|
||||||
if request.id isnot# v:null
|
|
||||||
let a:client.requests[request.id] = request
|
|
||||||
else
|
|
||||||
if has_key(a:client, 'client_id')
|
|
||||||
call copilot#client#LspExit(a:client.client_id, -1, -1)
|
|
||||||
endif
|
|
||||||
call copilot#util#Defer(function('s:RejectRequest'), request, s:error_connection_inactive)
|
|
||||||
endif
|
|
||||||
return request
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:NvimClose() dict abort
|
|
||||||
if !has_key(self, 'client_id')
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
let self.kill = v:true
|
|
||||||
return luaeval('vim.lsp.stop_client(_A)', self.client_id)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:NvimNotify(method, params) dict abort
|
|
||||||
call self.AfterInitialized(function('s:NvimDoNotify'), a:method, a:params)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:NvimDoNotify(client, method, params) abort
|
|
||||||
return eval("v:lua.require'_copilot'.rpc_notify(a:client.client_id, a:method, a:params)")
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#client#LspHandle(id, request) abort
|
|
||||||
if !has_key(s:instances, a:id)
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
return s:OnMessage(s:instances[a:id], a:request)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:PackageVersion() abort
|
|
||||||
try
|
|
||||||
return json_decode(join(readfile(s:root . '/copilot-language-server/package.json'))).version
|
|
||||||
catch
|
|
||||||
endtry
|
|
||||||
return ''
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:GetCommand(var, default) abort
|
|
||||||
let cmd = get(g:, a:var, '')
|
|
||||||
if type(cmd) == type('') && !empty(cmd)
|
|
||||||
return [expand(cmd)]
|
|
||||||
endif
|
|
||||||
return type(cmd) == v:t_list && !empty(cmd) ? copy(cmd) : a:default
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
let s:script_name = 'copilot-language-server/dist/language-server.js'
|
|
||||||
let s:pkg_name = '@github/copilot-language-server'
|
|
||||||
function! s:Command() abort
|
|
||||||
if !has('nvim-0.8') && v:version < 900
|
|
||||||
return [[], [], 'Vim version too old']
|
|
||||||
endif
|
|
||||||
let script = s:GetCommand('copilot_command', [])
|
|
||||||
let npx = get(g:, 'copilot_version', get(g:, 'copilot_npx', v:null))
|
|
||||||
if npx is# v:null
|
|
||||||
let npx = empty(script) && !empty(get(g:, 'copilot_npx_command', 1)) ? v:true : v:false
|
|
||||||
endif
|
|
||||||
if type(npx) != v:t_string && !empty(npx)
|
|
||||||
let npx = ''
|
|
||||||
endif
|
|
||||||
if type(npx) == v:t_string
|
|
||||||
if empty(npx)
|
|
||||||
let npx = '^'
|
|
||||||
endif
|
|
||||||
if npx =~# '^\%([~<>=^]\|\d\+\.\|latest$\)'
|
|
||||||
let npx = '@' . npx
|
|
||||||
endif
|
|
||||||
if npx =~# '^@[^/]*$'
|
|
||||||
let npx = s:pkg_name . npx
|
|
||||||
endif
|
|
||||||
if npx =~# '@[~<>=^]\+$'
|
|
||||||
let pkg_version = s:PackageVersion()
|
|
||||||
if pkg_version =~# '^[1-9]'
|
|
||||||
let npx .= pkg_version
|
|
||||||
else
|
|
||||||
let npx = substitute(npx, '@[=~]$', '@^', '') . '1.0.0'
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
let script = s:GetCommand('copilot_npx_command', ['npx']) + [npx]
|
|
||||||
if !executable(script[0])
|
|
||||||
let script = []
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
if empty(script)
|
|
||||||
let script = [s:root . '/' . s:script_name]
|
|
||||||
endif
|
|
||||||
if script[0] !~# '\.[cm]\=[jt]s$' && executable(script[0])
|
|
||||||
return [[], script, '']
|
|
||||||
elseif !filereadable(script[0])
|
|
||||||
return [[], [], 'Could not find ' . script[0]]
|
|
||||||
endif
|
|
||||||
let node = s:GetCommand('copilot_node_command', ['node'])
|
|
||||||
if !executable(get(node, 0, ''))
|
|
||||||
if get(node, 0, '') ==# 'node'
|
|
||||||
return [[], [], 'Node.js not found in PATH']
|
|
||||||
else
|
|
||||||
return [[], [], 'Node.js executable `' . get(node, 0, '') . "' not found"]
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
return [node, script, '']
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:UrlDecode(str) abort
|
|
||||||
return substitute(a:str, '%\(\x\x\)', '\=iconv(nr2char("0x".submatch(1)), "utf-8", "latin1")', 'g')
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#client#EditorInfo() abort
|
|
||||||
if !exists('s:editor_version')
|
|
||||||
if has('nvim')
|
|
||||||
let s:editor_version = matchstr(execute('version'), 'NVIM v\zs[^[:space:]]\+')
|
|
||||||
else
|
|
||||||
let s:editor_version = (v:version / 100) . '.' . (v:version % 100) . (exists('v:versionlong') ? printf('.%04d', v:versionlong % 10000) : '')
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
return {'name': has('nvim') ? 'Neovim': 'Vim', 'version': s:editor_version}
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#client#EditorPluginInfo() abort
|
|
||||||
return {'name': 'copilot.vim', 'version': s:plugin_version}
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#client#Settings() abort
|
|
||||||
let settings = {
|
|
||||||
\ 'http': {
|
|
||||||
\ 'proxy': get(g:, 'copilot_proxy', v:null),
|
|
||||||
\ 'proxyStrictSSL': get(g:, 'copilot_proxy_strict_ssl', v:null)},
|
|
||||||
\ 'github-enterprise': {'uri': get(g:, 'copilot_enterprise_uri', get(g:, 'copilot_auth_provider_url', v:null))},
|
|
||||||
\ }
|
|
||||||
if type(settings.http.proxy) ==# v:t_string && settings.http.proxy =~# '^[^/]\+$'
|
|
||||||
let settings.http.proxy = 'http://' . settings.http.proxy
|
|
||||||
endif
|
|
||||||
if type(get(g:, 'copilot_settings')) == v:t_dict
|
|
||||||
let settings.github = {'copilot': g:copilot_settings}
|
|
||||||
endif
|
|
||||||
if type(get(g:, 'copilot_lsp_settings')) == v:t_dict
|
|
||||||
call extend(settings, g:copilot_lsp_settings)
|
|
||||||
endif
|
|
||||||
return settings
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:PostInit(result, instance) abort
|
|
||||||
let a:instance.serverInfo = get(a:result, 'serverInfo', {})
|
|
||||||
if !has_key(a:instance, 'node_version') && has_key(a:result.serverInfo, 'nodeVersion')
|
|
||||||
let a:instance.node_version = a:result.serverInfo.nodeVersion
|
|
||||||
endif
|
|
||||||
call s:FlushAfterInitialized(a:instance)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:InitializeResult(result, instance) abort
|
|
||||||
call s:Send(a:instance, {'method': 'initialized', 'params': {}})
|
|
||||||
call s:PostInit(a:result, a:instance)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:InitializeError(error, instance) abort
|
|
||||||
if !has_key(a:instance, 'startup_error')
|
|
||||||
let a:instance.startup_error = 'Unexpected error E' . a:error.code . ' initializing language server: ' . a:error.message
|
|
||||||
call a:instance.Close()
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:StartupError() dict abort
|
|
||||||
while (has_key(self, 'job') || has_key(self, 'client_id')) && !has_key(self, 'startup_error') && !has_key(self, 'serverInfo')
|
|
||||||
sleep 10m
|
|
||||||
endwhile
|
|
||||||
if has_key(self, 'serverInfo')
|
|
||||||
return ''
|
|
||||||
else
|
|
||||||
return get(self, 'startup_error', 'Something unexpected went wrong spawning the language server')
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:VimDidChangeConfiguration() dict abort
|
|
||||||
let settings = copilot#client#Settings()
|
|
||||||
return self.Notify('workspace/didChangeConfiguration', {'settings': settings})
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:NvimDidChangeConfiguration() dict abort
|
|
||||||
let settings = copilot#client#Settings()
|
|
||||||
return eval("v:lua.require'_copilot'.did_change_configuration(self.id, settings)")
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:StatusNotification(params, instance) abort
|
|
||||||
let a:instance.status = a:params
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Nop(...) abort
|
|
||||||
return v:null
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:False(...) abort
|
|
||||||
return v:false
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Progress(params, instance) abort
|
|
||||||
if has_key(a:instance.progress, a:params.token)
|
|
||||||
call a:instance.progress[a:params.token](a:params.value)
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
let s:notifications = {
|
|
||||||
\ '$/progress': function('s:Progress'),
|
|
||||||
\ 'featureFlagsNotification': function('s:Nop'),
|
|
||||||
\ 'didChangeStatus': function('s:StatusNotification'),
|
|
||||||
\ 'window/logMessage': function('copilot#handlers#window_logMessage'),
|
|
||||||
\ }
|
|
||||||
|
|
||||||
let s:vim_handlers = {
|
|
||||||
\ 'window/showMessageRequest': function('copilot#handlers#window_showMessageRequest'),
|
|
||||||
\ 'window/showDocument': function('copilot#handlers#window_showDocument'),
|
|
||||||
\ }
|
|
||||||
|
|
||||||
let s:vim_capabilities = {
|
|
||||||
\ 'workspace': {'workspaceFolders': v:true},
|
|
||||||
\ 'window': {'showDocument': {'support': v:true}},
|
|
||||||
\ }
|
|
||||||
|
|
||||||
function! copilot#client#New() abort
|
|
||||||
let opts = {}
|
|
||||||
let instance = {'requests': {},
|
|
||||||
\ 'name': 'GitHub Copilot',
|
|
||||||
\ 'progress': {},
|
|
||||||
\ 'workspaceFolders': {},
|
|
||||||
\ 'after_initialized': [],
|
|
||||||
\ 'status': {'status': 'Starting', 'message': ''},
|
|
||||||
\ 'AfterInitialized': function('s:AfterInitialized'),
|
|
||||||
\ 'Close': function('s:Nop'),
|
|
||||||
\ 'Notify': function('s:False'),
|
|
||||||
\ 'Request': function('s:VimRequest'),
|
|
||||||
\ 'Attach': function('s:Nop'),
|
|
||||||
\ 'IsAttached': function('s:False'),
|
|
||||||
\ 'Call': function('s:Call'),
|
|
||||||
\ 'Cancel': function('s:Cancel'),
|
|
||||||
\ 'DidChangeConfiguration': function('s:VimDidChangeConfiguration'),
|
|
||||||
\ 'StartupError': function('s:StartupError'),
|
|
||||||
\ }
|
|
||||||
let instance.methods = copy(s:notifications)
|
|
||||||
let [node, argv, command_error] = s:Command()
|
|
||||||
if !empty(command_error)
|
|
||||||
let instance.id = -1
|
|
||||||
let instance.startup_error = command_error
|
|
||||||
call copilot#logger#Error(command_error)
|
|
||||||
call s:FlushAfterInitialized(instance)
|
|
||||||
return instance
|
|
||||||
endif
|
|
||||||
let instance.node = node
|
|
||||||
let command = node + argv + ['--stdio']
|
|
||||||
let opts.initializationOptions = {
|
|
||||||
\ 'editorInfo': copilot#client#EditorInfo(),
|
|
||||||
\ 'editorPluginInfo': copilot#client#EditorPluginInfo(),
|
|
||||||
\ }
|
|
||||||
if type(get(g:, 'copilot_integration_id')) == v:t_string
|
|
||||||
let opts.initializationOptions.copilotIntegrationId = g:copilot_integration_id
|
|
||||||
endif
|
|
||||||
let opts.workspaceFolders = []
|
|
||||||
let settings = copilot#client#Settings()
|
|
||||||
if type(get(g:, 'copilot_workspace_folders')) == v:t_list
|
|
||||||
for folder in g:copilot_workspace_folders
|
|
||||||
if type(folder) == v:t_string && !empty(folder) && folder !~# '\*\*\|^/$'
|
|
||||||
for path in glob(folder . '/', 0, 1)
|
|
||||||
let uri = s:UriFromPath(substitute(path, '[\/]*$', '', ''))
|
|
||||||
call add(opts.workspaceFolders, {'uri': uri, 'name': fnamemodify(uri, ':t')})
|
|
||||||
endfor
|
|
||||||
elseif type(folder) == v:t_dict && has_key(v:t_dict, 'uri') && !empty(folder.uri) && has_key(folder, 'name')
|
|
||||||
call add(opts.workspaceFolders, folder)
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
endif
|
|
||||||
for folder in opts.workspaceFolders
|
|
||||||
let instance.workspaceFolders[folder.uri] = v:true
|
|
||||||
endfor
|
|
||||||
call copilot#logger#Debug('Spawning ' . join(command, ' '))
|
|
||||||
let is_win_shell = has('win32') && &shellcmdflag !~# '^-'
|
|
||||||
if is_win_shell && command[0] !~# '[\/]'
|
|
||||||
let exepath = exepath(command[0])
|
|
||||||
if exepath !~? '\.exe$\|^$'
|
|
||||||
let command[0] = fnamemodify(exepath, ':t')
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
if has('nvim')
|
|
||||||
if is_win_shell
|
|
||||||
call map(command, { _, v -> substitute(v, '\^', '^^^^', 'g') })
|
|
||||||
endif
|
|
||||||
call extend(instance, {
|
|
||||||
\ 'Close': function('s:NvimClose'),
|
|
||||||
\ 'Notify': function('s:NvimNotify'),
|
|
||||||
\ 'Request': function('s:NvimRequest'),
|
|
||||||
\ 'Attach': function('s:NvimAttach'),
|
|
||||||
\ 'DidChangeConfiguration': function('s:NvimDidChangeConfiguration'),
|
|
||||||
\ 'IsAttached': function('s:NvimIsAttached'),
|
|
||||||
\ })
|
|
||||||
let id = eval("v:lua.require'_copilot'.lsp_start_client(command, instance.name, keys(instance.methods), opts, settings)")
|
|
||||||
if id is# v:null
|
|
||||||
let instance.id = -1
|
|
||||||
let instance.startup_error = 'Neovim failed to start LSP client'
|
|
||||||
call s:FlushAfterInitialized(instance)
|
|
||||||
return instance
|
|
||||||
endif
|
|
||||||
let instance.client_id = id
|
|
||||||
let instance.id = instance.client_id
|
|
||||||
else
|
|
||||||
if is_win_shell
|
|
||||||
let command = join(map(command, { _, v -> v =~# '[;&|^ ]' ? '"' . v . '"' : v }), ' ')
|
|
||||||
endif
|
|
||||||
call extend(instance, {
|
|
||||||
\ 'Close': function('s:VimClose'),
|
|
||||||
\ 'Notify': function('s:VimNotify'),
|
|
||||||
\ 'Attach': function('s:VimAttach'),
|
|
||||||
\ 'IsAttached': function('s:VimIsAttached'),
|
|
||||||
\ })
|
|
||||||
let state = {'headers': {}, 'mode': 'headers', 'buffer': ''}
|
|
||||||
let instance.open_buffers = {}
|
|
||||||
let instance.methods = extend(s:vim_handlers, instance.methods)
|
|
||||||
let instance.job = job_start(command, {
|
|
||||||
\ 'cwd': copilot#job#Cwd(),
|
|
||||||
\ 'noblock': 1,
|
|
||||||
\ 'stoponexit': '',
|
|
||||||
\ 'in_mode': 'lsp',
|
|
||||||
\ 'out_mode': 'lsp',
|
|
||||||
\ 'out_cb': { j, d -> copilot#util#Defer(function('s:OnMessage'), instance, d) },
|
|
||||||
\ 'err_cb': function('s:OnErr', [instance]),
|
|
||||||
\ 'exit_cb': { j, d -> copilot#util#Defer(function('s:OnExit'), instance, d) },
|
|
||||||
\ })
|
|
||||||
let instance.id = job_info(instance.job).process
|
|
||||||
let opts.capabilities = s:vim_capabilities
|
|
||||||
let opts.processId = getpid()
|
|
||||||
let request = instance.Request('initialize', opts, function('s:InitializeResult'), function('s:InitializeError'), instance)
|
|
||||||
call call(remove(instance.after_initialized, 0), [])
|
|
||||||
call instance.Notify('workspace/didChangeConfiguration', {'settings': settings})
|
|
||||||
endif
|
|
||||||
let s:instances[instance.id] = instance
|
|
||||||
return instance
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#client#Cancel(request) abort
|
|
||||||
if type(a:request) == type({}) && has_key(a:request, 'Cancel')
|
|
||||||
call a:request.Cancel()
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Callback(request, type, callback, timer) abort
|
|
||||||
call remove(a:request.waiting, a:timer)
|
|
||||||
if has_key(a:request, a:type)
|
|
||||||
call a:callback(a:request[a:type])
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#client#Result(request, callback) abort
|
|
||||||
if has_key(a:request, 'resolve')
|
|
||||||
call add(a:request.resolve, a:callback)
|
|
||||||
elseif has_key(a:request, 'result')
|
|
||||||
let a:request.waiting[timer_start(0, function('s:Callback', [a:request, 'result', a:callback]))] = 1
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#client#Error(request, callback) abort
|
|
||||||
if has_key(a:request, 'reject')
|
|
||||||
call add(a:request.reject, a:callback)
|
|
||||||
elseif has_key(a:request, 'error')
|
|
||||||
let a:request.waiting[timer_start(0, function('s:Callback', [a:request, 'error', a:callback]))] = 1
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:CloseBuffer(bufnr) abort
|
|
||||||
for instance in values(s:instances)
|
|
||||||
try
|
|
||||||
if has_key(instance, 'job') && has_key(instance.open_buffers, a:bufnr)
|
|
||||||
let buffer = remove(instance.open_buffers, a:bufnr)
|
|
||||||
call instance.Notify('textDocument/didClose', {'textDocument': {'uri': buffer.uri}})
|
|
||||||
endif
|
|
||||||
catch
|
|
||||||
call copilot#logger#Exception()
|
|
||||||
endtry
|
|
||||||
endfor
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
augroup copilot_close
|
|
||||||
autocmd!
|
|
||||||
if !has('nvim')
|
|
||||||
autocmd BufUnload * call s:CloseBuffer(+expand('<abuf>'))
|
|
||||||
endif
|
|
||||||
augroup END
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
function! copilot#handlers#window_logMessage(params, ...) abort
|
|
||||||
call copilot#logger#Raw(get(a:params, 'type', 6), get(a:params, 'message', ''))
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#handlers#window_showMessageRequest(params, instance, ...) abort
|
|
||||||
let choice = inputlist([a:instance.name . "\n" . a:params.message . "\n\nRequest Actions:"] +
|
|
||||||
\ map(copy(get(a:params, 'actions', [])), { i, v -> (i + 1) . '. ' . v.title}))
|
|
||||||
return choice > 0 ? get(a:params.actions, choice - 1, v:null) : v:null
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:BrowserCallback(into, code) abort
|
|
||||||
let a:into.code = a:code
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#handlers#window_showDocument(params, ...) abort
|
|
||||||
echo a:params.uri
|
|
||||||
if empty(get(a:params, 'external'))
|
|
||||||
return {'success': v:false}
|
|
||||||
endif
|
|
||||||
let browser = copilot#Browser()
|
|
||||||
if empty(browser)
|
|
||||||
return {'success': v:false}
|
|
||||||
endif
|
|
||||||
let status = {}
|
|
||||||
call copilot#job#Stream(browser + [a:params.uri], v:null, v:null, function('s:BrowserCallback', [status]))
|
|
||||||
let time = reltime()
|
|
||||||
while empty(status) && reltimefloat(reltime(time)) < 1
|
|
||||||
sleep 10m
|
|
||||||
endwhile
|
|
||||||
return {'success': get(status, 'code') ? v:false : v:true}
|
|
||||||
endfunction
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
scriptencoding utf-8
|
|
||||||
|
|
||||||
function! copilot#job#Nop(...) abort
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Jobs(job_or_jobs) abort
|
|
||||||
let jobs = type(a:job_or_jobs) == v:t_list ? copy(a:job_or_jobs) : [a:job_or_jobs]
|
|
||||||
call map(jobs, { k, v -> type(v) == v:t_dict ? get(v, 'job', '') : v })
|
|
||||||
call filter(jobs, { k, v -> type(v) !=# type('') })
|
|
||||||
return jobs
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
let s:job_stop = exists('*job_stop') ? 'job_stop' : 'jobstop'
|
|
||||||
function! copilot#job#Stop(job) abort
|
|
||||||
for job in s:Jobs(a:job)
|
|
||||||
call call(s:job_stop, [job])
|
|
||||||
endfor
|
|
||||||
return copilot#job#Wait(a:job)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
let s:sleep = has('patch-8.2.2366') ? 'sleep! 1m' : 'sleep 1m'
|
|
||||||
function! copilot#job#Wait(jobs) abort
|
|
||||||
let jobs = s:Jobs(a:jobs)
|
|
||||||
if exists('*jobwait')
|
|
||||||
call jobwait(jobs)
|
|
||||||
else
|
|
||||||
for job in jobs
|
|
||||||
while ch_status(job) !=# 'closed' || job_status(job) ==# 'run'
|
|
||||||
exe s:sleep
|
|
||||||
endwhile
|
|
||||||
endfor
|
|
||||||
endif
|
|
||||||
return a:jobs
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:VimExitCallback(result, exit_cb, job, data) abort
|
|
||||||
let a:result.exit_status = a:data
|
|
||||||
if !has_key(a:result, 'closed')
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
call remove(a:result, 'closed')
|
|
||||||
call a:exit_cb(a:result.exit_status)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:VimCloseCallback(result, exit_cb, job) abort
|
|
||||||
if !has_key(a:result, 'exit_status')
|
|
||||||
let a:result.closed = v:true
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
call a:exit_cb(a:result.exit_status)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:NvimCallback(cb, job, data, type) dict abort
|
|
||||||
let self[a:type][0] .= remove(a:data, 0)
|
|
||||||
call extend(self[a:type], a:data)
|
|
||||||
while len(self[a:type]) > 1
|
|
||||||
call a:cb(substitute(remove(self[a:type], 0), "\r$", '', ''))
|
|
||||||
endwhile
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:NvimExitCallback(out_cb, err_cb, exit_cb, job, data, type) dict abort
|
|
||||||
if len(self.stderr[0])
|
|
||||||
call a:err_cb(substitute(self.stderr[0], "\r$", '', ''))
|
|
||||||
endif
|
|
||||||
call a:exit_cb(a:data)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#job#Cwd() abort
|
|
||||||
let home = expand("~")
|
|
||||||
if !isdirectory(home) && isdirectory($VIM)
|
|
||||||
return $VIM
|
|
||||||
endif
|
|
||||||
return home
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#job#Stream(argv, out_cb, err_cb, ...) abort
|
|
||||||
let exit_status = []
|
|
||||||
let ExitCb = function(a:0 && !empty(a:1) ? a:1 : { e -> add(exit_status, e) }, a:000[2:-1])
|
|
||||||
let OutCb = function(empty(a:out_cb) ? 'copilot#job#Nop' : a:out_cb, a:000[2:-1])
|
|
||||||
let ErrCb = function(empty(a:err_cb) ? 'copilot#job#Nop' : a:err_cb, a:000[2:-1])
|
|
||||||
let state = {'headers': {}, 'mode': 'headers', 'buffer': ''}
|
|
||||||
if exists('*job_start')
|
|
||||||
let result = {}
|
|
||||||
let job = job_start(a:argv, {
|
|
||||||
\ 'cwd': copilot#job#Cwd(),
|
|
||||||
\ 'out_mode': 'raw',
|
|
||||||
\ 'out_cb': { j, d -> OutCb(d) },
|
|
||||||
\ 'err_cb': { j, d -> ErrCb(d) },
|
|
||||||
\ 'exit_cb': function('s:VimExitCallback', [result, ExitCb]),
|
|
||||||
\ 'close_cb': function('s:VimCloseCallback', [result, ExitCb]),
|
|
||||||
\ })
|
|
||||||
else
|
|
||||||
let jopts = {
|
|
||||||
\ 'cwd': copilot#job#Cwd(),
|
|
||||||
\ 'stderr': [''],
|
|
||||||
\ 'on_stdout': { j, d, t -> OutCb(join(d, "\n")) },
|
|
||||||
\ 'on_stderr': function('s:NvimCallback', [ErrCb]),
|
|
||||||
\ 'on_exit': function('s:NvimExitCallback', [OutCb, ErrCb, ExitCb])}
|
|
||||||
let job = jobstart(a:argv, jopts)
|
|
||||||
endif
|
|
||||||
if a:0
|
|
||||||
return job
|
|
||||||
endif
|
|
||||||
call copilot#job#Wait(job)
|
|
||||||
return exit_status[0]
|
|
||||||
endfunction
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
let s:logs = []
|
|
||||||
|
|
||||||
function! copilot#logger#BufReadCmd() abort
|
|
||||||
try
|
|
||||||
setlocal modifiable noreadonly
|
|
||||||
silent call deletebufline('', 1, '$')
|
|
||||||
if !empty(s:logs)
|
|
||||||
call setline(1, s:logs)
|
|
||||||
endif
|
|
||||||
finally
|
|
||||||
setlocal buftype=nofile bufhidden=wipe nobuflisted nomodified nomodifiable
|
|
||||||
endtry
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
let s:level_prefixes = ['', '[ERROR] ', '[WARN] ', '[INFO] ', '[DEBUG] ', '[DEBUG] ']
|
|
||||||
|
|
||||||
function! copilot#logger#Raw(level, message) abort
|
|
||||||
let lines = type(a:message) == v:t_list ? copy(a:message) : split(a:message, "\n", 1)
|
|
||||||
let lines[0] = strftime('[%Y-%m-%d %H:%M:%S] ') . get(s:level_prefixes, a:level, '[UNKNOWN] ') . get(lines, 0, '')
|
|
||||||
try
|
|
||||||
call map(lines, { k, L -> type(L) == v:t_func ? call(L, []) : L })
|
|
||||||
call extend(s:logs, lines)
|
|
||||||
let overflow = len(s:logs) - get(g:, 'copilot_log_history', 10000)
|
|
||||||
if overflow > 0
|
|
||||||
call remove(s:logs, 0, overflow - 1)
|
|
||||||
endif
|
|
||||||
let bufnr = bufnr('copilot:///log')
|
|
||||||
if bufnr > 0 && bufloaded(bufnr)
|
|
||||||
call setbufvar(bufnr, '&modifiable', 1)
|
|
||||||
call setbufline(bufnr, 1, s:logs)
|
|
||||||
call setbufvar(bufnr, '&modifiable', 0)
|
|
||||||
for winid in win_findbuf(bufnr)
|
|
||||||
if has('nvim') && winid != win_getid()
|
|
||||||
call nvim_win_set_cursor(winid, [len(s:logs), 0])
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
endif
|
|
||||||
catch
|
|
||||||
endtry
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#logger#Debug(...) abort
|
|
||||||
if empty(get(g:, 'copilot_debug'))
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
call copilot#logger#Raw(4, a:000)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#logger#Info(...) abort
|
|
||||||
call copilot#logger#Raw(3, a:000)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#logger#Warn(...) abort
|
|
||||||
call copilot#logger#Raw(2, a:000)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#logger#Error(...) abort
|
|
||||||
call copilot#logger#Raw(1, a:000)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#logger#Bare(...) abort
|
|
||||||
call copilot#logger#Raw(0, a:000)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#logger#Exception(...) abort
|
|
||||||
if !empty(v:exception) && v:exception !=# 'Vim:Interrupt'
|
|
||||||
call copilot#logger#Error('Exception: ' . v:exception . ' @ ' . v:throwpoint)
|
|
||||||
let client = copilot#RunningClient()
|
|
||||||
if !empty(client)
|
|
||||||
let [_, type, code, message; __] = matchlist(v:exception, '^\%(\(^[[:alnum:]_#]\+\)\%((\a\+)\)\=\%(\(:E-\=\d\+\)\)\=:\s*\)\=\(.*\)$')
|
|
||||||
let stacklines = []
|
|
||||||
for frame in split(substitute(v:throwpoint, ', \S\+ \(\d\+\)$', '[\1]', ''), '\.\@<!\.\.\.\@!')
|
|
||||||
let fn_line = matchlist(frame, '^\%(function \)\=\(\S\+\)\[\(\d\+\)\]$')
|
|
||||||
if !empty(fn_line)
|
|
||||||
call add(stacklines, {'function': substitute(fn_line[1], '^<SNR>\d\+_', '<SID>', ''), 'lineno': +fn_line[2]})
|
|
||||||
elseif frame =~# ' Autocmds for "\*"$'
|
|
||||||
call add(stacklines, {'function': frame})
|
|
||||||
elseif frame =~# ' Autocmds for ".*"$'
|
|
||||||
call add(stacklines, {'function': substitute(frame, ' for ".*"$', ' for "[redacted]"', '')})
|
|
||||||
else
|
|
||||||
call add(stacklines, {'function': '[redacted]'})
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
return client.Request('telemetry/exception', {
|
|
||||||
\ 'transaction': a:0 ? a:1 : '',
|
|
||||||
\ 'platform': 'other',
|
|
||||||
\ 'exception_detail': [{
|
|
||||||
\ 'type': type . code,
|
|
||||||
\ 'value': message,
|
|
||||||
\ 'stacktrace': stacklines}]
|
|
||||||
\ }, v:null, function('copilot#util#Nop'))
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
scriptencoding utf-8
|
|
||||||
|
|
||||||
if !exists('s:panel_id')
|
|
||||||
let s:panel_id = 0
|
|
||||||
endif
|
|
||||||
|
|
||||||
let s:separator = repeat('─', 72)
|
|
||||||
|
|
||||||
function! s:Render(state) abort
|
|
||||||
let bufnr = bufnr('^' . a:state.panel . '$')
|
|
||||||
let state = a:state
|
|
||||||
if !bufloaded(bufnr)
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
let sorted = a:state.items
|
|
||||||
if !empty(get(a:state, 'error'))
|
|
||||||
let lines = ['Error: ' . a:state.error.message]
|
|
||||||
let sorted = []
|
|
||||||
elseif get(a:state, 'percentage') == 100
|
|
||||||
let lines = ['Synthesized ' . (len(sorted) == 1 ? '1 completion' : len(sorted) . ' completions')]
|
|
||||||
else
|
|
||||||
let lines = [substitute('Synthesizing ' . matchstr(get(a:state, 'message', ''), '\d\+\%(/\d\+\)\=') . ' completions', ' \+', ' ', 'g')]
|
|
||||||
endif
|
|
||||||
if len(sorted)
|
|
||||||
call add(lines, 'Press <CR> on a completion to accept')
|
|
||||||
endif
|
|
||||||
let leads = {}
|
|
||||||
for item in sorted
|
|
||||||
let insert = split(item.insertText, "\r\n\\=\\|\n", 1)
|
|
||||||
let insert[0] = strpart(a:state.line, 0, copilot#util#UTF16ToByteIdx(a:state.line, item.range.start.character)) . insert[0]
|
|
||||||
let lines += [s:separator] + insert
|
|
||||||
if !has_key(leads, string(item.range.start))
|
|
||||||
let match = insert[0 : a:state.position.line - item.range.start.line]
|
|
||||||
let match[-1] = strpart(match[-1], 0, copilot#util#UTF16ToByteIdx(match[-1], a:state.position.character))
|
|
||||||
call map(match, { k, v -> escape(v, '][^$.*\~') })
|
|
||||||
let leads[string(item.range.start)] = join(match, '\n')
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
try
|
|
||||||
call setbufvar(bufnr, '&modifiable', 1)
|
|
||||||
call setbufvar(bufnr, '&readonly', 0)
|
|
||||||
call setbufline(bufnr, 1, lines)
|
|
||||||
finally
|
|
||||||
call setbufvar(bufnr, '&modifiable', 0)
|
|
||||||
endtry
|
|
||||||
call clearmatches()
|
|
||||||
call matchadd('CopilotSuggestion', '\C^' . s:separator . '\n\zs\%(' . join(sort(values(leads), { a, b -> len(b) - len(a) }), '\|') . '\)', 10, 4)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:PartialResult(state, value) abort
|
|
||||||
let items = type(a:value) == v:t_list ? a:value : a:value.items
|
|
||||||
call extend(a:state.items, items)
|
|
||||||
call s:Render(a:state)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:WorkDone(state, value) abort
|
|
||||||
if has_key(a:value, 'message')
|
|
||||||
let a:state.message = a:value.message
|
|
||||||
endif
|
|
||||||
if has_key(a:value, 'percentage')
|
|
||||||
let a:state.percentage = a:value.percentage
|
|
||||||
call s:Render(a:state)
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#panel#Accept(...) abort
|
|
||||||
let state = get(b:, 'copilot_panel', {})
|
|
||||||
if empty(state.items)
|
|
||||||
return ''
|
|
||||||
endif
|
|
||||||
if !has_key(state, 'bufnr') || !bufloaded(get(state, 'bufnr', -1))
|
|
||||||
return "echoerr 'Buffer was closed'"
|
|
||||||
endif
|
|
||||||
let at = a:0 ? a:1 : line('.')
|
|
||||||
let index = 0
|
|
||||||
for lnum in range(1, at)
|
|
||||||
if getline(lnum) ==# s:separator
|
|
||||||
let index += 1
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
if index > 0 && index <= len(state.items)
|
|
||||||
let item = state.items[index - 1]
|
|
||||||
let lnum = item.range.start.line + 1
|
|
||||||
if getbufline(state.bufnr, lnum) !=# [state.line]
|
|
||||||
return 'echoerr "Buffer has changed since synthesizing completion"'
|
|
||||||
endif
|
|
||||||
let lines = split(item.insertText, '\r\n\=\|\n', 1)
|
|
||||||
let old_first = getbufline(state.bufnr, item.range.start.line + 1)[0]
|
|
||||||
let byte_offset_start = copilot#util#UTF16ToByteIdx(old_first, item.range.start.character)
|
|
||||||
let lines[0] = strpart(old_first, 0, byte_offset_start) . lines[0]
|
|
||||||
let old_last = getbufline(state.bufnr, item.range.end.line + 1)[0]
|
|
||||||
let byte_offset_end = copilot#util#UTF16ToByteIdx(old_last, item.range.end.character)
|
|
||||||
let lines[-1] .= strpart(old_last, byte_offset_end)
|
|
||||||
call deletebufline(state.bufnr, item.range.start.line + 1, item.range.end.line + 1)
|
|
||||||
call appendbufline(state.bufnr, item.range.start.line, lines)
|
|
||||||
call copilot#Request('workspace/executeCommand', item.command)
|
|
||||||
bwipeout
|
|
||||||
let win = bufwinnr(state.bufnr)
|
|
||||||
if win > 0
|
|
||||||
exe win . 'wincmd w'
|
|
||||||
exe item.range.start.line + len(lines)
|
|
||||||
if state.was_insert
|
|
||||||
startinsert!
|
|
||||||
else
|
|
||||||
normal! $
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
return ''
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Initialize(state) abort
|
|
||||||
try
|
|
||||||
let &l:filetype = 'copilotpanel' . (empty(a:state.filetype) ? '' : '.' . a:state.filetype)
|
|
||||||
catch
|
|
||||||
let &l:filetype = 'copilotpanel'
|
|
||||||
endtry
|
|
||||||
let &l:tabstop = a:state.tabstop
|
|
||||||
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 nomodifiable
|
|
||||||
let state = get(b:, 'copilot_panel')
|
|
||||||
if type(state) != v:t_dict
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
call s:Initialize(state)
|
|
||||||
call s:Render(state)
|
|
||||||
return ''
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Result(state, result) abort
|
|
||||||
let a:state.percentage = 100
|
|
||||||
call s:PartialResult(a:state, a:result)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:Error(state, error) abort
|
|
||||||
let a:state.error = a:error
|
|
||||||
call s:Render(a:state)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#panel#Open(opts) abort
|
|
||||||
let s:panel_id += 1
|
|
||||||
let state = {'items': [], 'filetype': &filetype, 'was_insert': mode() =~# '^[iR]', 'bufnr': bufnr(''), 'tabstop': &tabstop}
|
|
||||||
let state.panel = 'copilot:///panel/' . s:panel_id
|
|
||||||
if state.was_insert
|
|
||||||
let state.position = copilot#util#AppendPosition()
|
|
||||||
stopinsert
|
|
||||||
else
|
|
||||||
let state.position = {'line': a:opts.line1 >= 1 ? a:opts.line1 - 1 : 0, 'character': copilot#util#UTF16Width(getline('.'))}
|
|
||||||
endif
|
|
||||||
let state.line = getline(state.position.line + 1)
|
|
||||||
let params = {
|
|
||||||
\ 'textDocument': {'uri': state.bufnr},
|
|
||||||
\ 'position': state.position,
|
|
||||||
\ 'partialResultToken': function('s:PartialResult', [state]),
|
|
||||||
\ 'workDoneToken': function('s:WorkDone', [state]),
|
|
||||||
\ }
|
|
||||||
let response = copilot#Request('textDocument/copilotPanelCompletion', params, function('s:Result', [state]), function('s:Error', [state]))
|
|
||||||
exe substitute(a:opts.mods, '\C\<tab\>', '-tab', 'g') 'keepalt split' state.panel
|
|
||||||
let b:copilot_panel = state
|
|
||||||
call s:Initialize(state)
|
|
||||||
call s:Render(state)
|
|
||||||
return ''
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
augroup github_copilot_panel
|
|
||||||
autocmd!
|
|
||||||
autocmd BufReadCmd copilot:///panel/* exe s:BufReadCmd()
|
|
||||||
augroup END
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
let s:deferred = []
|
|
||||||
|
|
||||||
function! copilot#util#Nop(...) abort
|
|
||||||
return v:null
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#util#Defer(fn, ...) abort
|
|
||||||
call add(s:deferred, function(a:fn, a:000))
|
|
||||||
return timer_start(0, function('s:RunDeferred'))
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:RunDeferred(...) abort
|
|
||||||
if empty(s:deferred)
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
let Fn = remove(s:deferred, 0)
|
|
||||||
call timer_start(0, function('s:RunDeferred'))
|
|
||||||
call call(Fn, [])
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! copilot#util#UTF16Width(str) abort
|
|
||||||
return strchars(substitute(a:str, "\\%#=2[^\u0001-\uffff]", " ", 'g'))
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
if exists('*utf16idx')
|
|
||||||
|
|
||||||
function! copilot#util#UTF16ToByteIdx(str, utf16_idx) abort
|
|
||||||
return byteidx(a:str, a:utf16_idx, 1)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
elseif has('nvim')
|
|
||||||
|
|
||||||
function! copilot#util#UTF16ToByteIdx(str, utf16_idx) abort
|
|
||||||
try
|
|
||||||
return v:lua.vim.str_byteindex(a:str, a:utf16_idx, 1)
|
|
||||||
catch /^Vim(return):E5108:/
|
|
||||||
return -1
|
|
||||||
endtry
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
function! copilot#util#UTF16ToByteIdx(str, utf16_idx) abort
|
|
||||||
if copilot#util#UTF16Width(a:str) < a:utf16_idx
|
|
||||||
return -1
|
|
||||||
endif
|
|
||||||
let end_offset = len(a:str)
|
|
||||||
while copilot#util#UTF16Width(strpart(a:str, 0, end_offset)) > a:utf16_idx && end_offset > 0
|
|
||||||
let end_offset -= 1
|
|
||||||
endwhile
|
|
||||||
return end_offset
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
endif
|
|
||||||
|
|
||||||
function! copilot#util#AppendPosition() abort
|
|
||||||
let line = getline('.')
|
|
||||||
let col_byte = col('.') - (mode() =~# '^[iR]' || empty(line))
|
|
||||||
let col_utf16 = copilot#util#UTF16Width(strpart(line, 0, col_byte))
|
|
||||||
return {'line': line('.') - 1, 'character': col_utf16}
|
|
||||||
endfunction
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
function! copilot#version#String() abort
|
|
||||||
return '1.58.0'
|
|
||||||
endfunction
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2025 GitHub
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@@ -1,269 +0,0 @@
|
|||||||
---
|
|
||||||
name: CVE Remediator
|
|
||||||
description: Detects and fixes security vulnerabilities (CVEs) in project dependencies across any ecosystem while maintaining a working build.
|
|
||||||
---
|
|
||||||
|
|
||||||
## Mission
|
|
||||||
|
|
||||||
Detect and fix CVEs (Common Vulnerabilities and Exposures) in project dependencies while maintaining a working build.
|
|
||||||
|
|
||||||
## Terminology
|
|
||||||
|
|
||||||
**Target dependencies** = the dependencies to check and fix, determined by user request:
|
|
||||||
- **Specific dependencies** when user names them (e.g., "log4j", "Spring and Jackson")
|
|
||||||
- **All direct dependencies** (excluding transitive) when user requests project-wide scan (e.g., "all CVEs", "scan project")
|
|
||||||
|
|
||||||
## Objectives
|
|
||||||
|
|
||||||
1. Identify CVEs in dependencies based on severity threshold
|
|
||||||
2. Upgrade vulnerable dependencies to patched versions
|
|
||||||
3. Resolve build errors caused by upgrades
|
|
||||||
4. Verify no new CVEs or build errors introduced
|
|
||||||
|
|
||||||
## Success Criteria
|
|
||||||
|
|
||||||
- Zero actionable fixable CVEs in target dependencies (based on severity threshold)
|
|
||||||
- Project builds successfully with no compilation errors
|
|
||||||
- No new CVEs introduced in target dependencies
|
|
||||||
|
|
||||||
## Core Rules
|
|
||||||
|
|
||||||
- NEVER introduce new CVEs in target dependencies
|
|
||||||
- NEVER downgrade dependencies
|
|
||||||
- NEVER modify functionality beyond API compatibility updates
|
|
||||||
- ONLY check and fix CVEs in target dependencies (always exclude transitive dependencies)
|
|
||||||
- ALWAYS build after each modification
|
|
||||||
- ALWAYS re-validate after each successful build
|
|
||||||
- ALWAYS verify build is successful: exit code 0 AND terminal output has NO errors AND get_errors returns NO errors
|
|
||||||
- NEVER skip build validation
|
|
||||||
|
|
||||||
## Understanding User Intent
|
|
||||||
|
|
||||||
Determine severity threshold and scope before starting:
|
|
||||||
|
|
||||||
**Severity Threshold** (which CVEs to fix):
|
|
||||||
|
|
||||||
- Default: critical, high
|
|
||||||
- Extract from request:
|
|
||||||
- "critical only" → critical
|
|
||||||
- "critical and high" → critical, high
|
|
||||||
- "include medium severity" or "medium and above" → critical, high, medium
|
|
||||||
- "all severities" → critical, high, medium, low
|
|
||||||
|
|
||||||
**Scope** (which dependencies to check):
|
|
||||||
|
|
||||||
- Specific: User names dependencies ("log4j", "Spring and Jackson") → locate and check only those
|
|
||||||
- Project-wide: User says "all", "scan project", "entire project" → discover and check all direct dependencies
|
|
||||||
|
|
||||||
**Important**: The `validate_cves` tool returns ALL CVEs regardless of severity. Filter results based on your determined severity threshold to identify actionable CVEs.
|
|
||||||
|
|
||||||
## Workflow
|
|
||||||
|
|
||||||
### Step 0: Detect Environment
|
|
||||||
|
|
||||||
Before examining dependencies, identify the project environment:
|
|
||||||
|
|
||||||
1. **Detect ecosystem**: Examine project files to determine language and build tool
|
|
||||||
2. **Locate dependency manifests and lockfiles**: Identify primary dependency files and version lockfiles
|
|
||||||
3. **Determine versions**: Check language and tool versions
|
|
||||||
|
|
||||||
**Detection examples** (adapt to your project):
|
|
||||||
|
|
||||||
- **Maven**:
|
|
||||||
- Manifest: `pom.xml`
|
|
||||||
- Version: Java version in `<java.version>` or `<maven.compiler.source>`, or run `java -version`
|
|
||||||
|
|
||||||
- **npm**:
|
|
||||||
- Manifest: `package.json`
|
|
||||||
- Lockfile: `package-lock.json`, `yarn.lock`, or `pnpm-lock.yaml`
|
|
||||||
- Version: Node version in `engines.node` or run `node -v`
|
|
||||||
|
|
||||||
- **pip**:
|
|
||||||
- Manifest: `requirements.txt`, `setup.py`, or `pyproject.toml`
|
|
||||||
- Lockfile: `poetry.lock`, `Pipfile.lock` (if using Poetry or Pipenv)
|
|
||||||
- Version: Python version in `python_requires` or run `python --version`
|
|
||||||
|
|
||||||
**Output**: Document detected ecosystem, language/tool versions, dependency manifest, lockfile (if present), and build commands to use.
|
|
||||||
|
|
||||||
### Step 1: Identify Target Dependencies
|
|
||||||
|
|
||||||
Identify package names and versions for **target dependencies** based on the scope determined in "Understanding User Intent" section. Always exclude transitive/indirect dependencies from the target set.
|
|
||||||
|
|
||||||
**Detection strategy (use build tools first, then fall back to manifest parsing):**
|
|
||||||
|
|
||||||
1. **Use build tool commands** (preferred - gets actual resolved versions, handles inheritance and version management):
|
|
||||||
- Maven: `mvn dependency:tree` (extract depth=1 for project-wide, filter for specific names) OR `mvn dependency:list -DexcludeTransitive=true`
|
|
||||||
- npm: `npm ls --depth=0` (project-wide) OR `npm ls <package-name>` (specific dependency)
|
|
||||||
- pip: `pip show <package-name>` (specific) OR parse `pipdeptree --json` (project-wide)
|
|
||||||
|
|
||||||
2. **Parse manifest and lockfiles** (fallback - simpler but may miss inherited or workspace dependencies):
|
|
||||||
- Maven: `<dependency>` entries in `pom.xml` `<dependencies>` section (excludes parent POM and `<dependencyManagement>`)
|
|
||||||
- npm: `dependencies` and `devDependencies` in `package.json`; resolve versions from `package-lock.json`, `yarn.lock`, or `pnpm-lock.yaml`
|
|
||||||
- pip: Top-level entries in `requirements.txt` or dependencies in `pyproject.toml`; resolve versions from `poetry.lock` or `Pipfile.lock` if available
|
|
||||||
|
|
||||||
**Scope-specific notes:**
|
|
||||||
- **Project-wide**: Extract all direct dependencies (depth=1 or first-level only)
|
|
||||||
- **Specific**: Filter for named dependencies; validate they exist in the project before proceeding
|
|
||||||
|
|
||||||
**Important:**
|
|
||||||
- Include all direct dependencies needed for runtime, building, and testing
|
|
||||||
- Validate the identified list makes sense for the project structure
|
|
||||||
- Command examples are hints only - adapt to the detected ecosystem and available tools
|
|
||||||
|
|
||||||
### Step 2: Remediation Loop
|
|
||||||
|
|
||||||
Iterate until zero actionable CVEs.
|
|
||||||
|
|
||||||
#### 2a. Validate
|
|
||||||
|
|
||||||
**Invoke `validate_cves`** with dependencies in format `package@version`.
|
|
||||||
|
|
||||||
Examples (adapt to your ecosystem):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"dependencies": ["org.springframework:spring-core@5.3.20", "org.apache.logging.log4j:log4j-core@2.14.1"],
|
|
||||||
"ecosystem": "maven"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"dependencies": ["django@3.2.0", "requests@2.25.1"],
|
|
||||||
"ecosystem": "pip"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Understanding the output:**
|
|
||||||
|
|
||||||
For each dependency, the tool provides CVE count, upgrade recommendations (fixable vs unfixable), and complete CVE details (severity, description, links).
|
|
||||||
|
|
||||||
Three possible scenarios:
|
|
||||||
- **All fixable**: Upgrade to recommended version fixes all CVEs
|
|
||||||
- **All unfixable**: No patched versions available yet
|
|
||||||
- **Mixed**: Some CVEs fixable by upgrade, others unfixable
|
|
||||||
|
|
||||||
Filter by your severity threshold to determine actionable CVEs.
|
|
||||||
|
|
||||||
**Next:**
|
|
||||||
|
|
||||||
- Zero actionable fixable CVEs in target dependencies → Step 3 (note any unfixable CVEs for final report)
|
|
||||||
- Found actionable fixable CVEs → Step 2b
|
|
||||||
|
|
||||||
#### 2b. Fix
|
|
||||||
|
|
||||||
For each actionable **fixable** CVE:
|
|
||||||
|
|
||||||
1. Note recommended patched version from tool output
|
|
||||||
2. Update dependency version in manifest file
|
|
||||||
3. If breaking changes exist, update affected code
|
|
||||||
|
|
||||||
**Important:** Do NOT attempt to fix CVEs marked as unfixable (no patched versions available). Track these for the final report.
|
|
||||||
|
|
||||||
After all fixes, return to Step 2a to validate with the updated dependency versions.
|
|
||||||
|
|
||||||
Continue loop until Step 2a finds zero actionable fixable CVEs.
|
|
||||||
|
|
||||||
### Step 3: Build Verification Loop
|
|
||||||
|
|
||||||
Iterate until build succeeds with clean output.
|
|
||||||
|
|
||||||
#### 3a. Build and Verify
|
|
||||||
|
|
||||||
Run the appropriate build command for your ecosystem.
|
|
||||||
|
|
||||||
**Example commands** (adapt to your detected environment):
|
|
||||||
|
|
||||||
- Maven: `mvn clean compile`, `mvn clean test`, or `mvn clean verify`
|
|
||||||
- npm: `npm run build` or `npm test`
|
|
||||||
- pip: `pip install -r requirements.txt` or `python -m pytest`
|
|
||||||
|
|
||||||
**Critical**: You MUST perform ALL three checks before declaring build success:
|
|
||||||
|
|
||||||
1. Check exit code is 0
|
|
||||||
2. Review complete terminal output for errors (look for error indicators specific to your build tool)
|
|
||||||
3. Run `get_errors` to check for compilation errors
|
|
||||||
|
|
||||||
**Next:**
|
|
||||||
|
|
||||||
- All checks pass (exit code 0 AND no terminal errors AND no compilation errors) → go to Step 3c
|
|
||||||
- Any check fails → go to Step 3b
|
|
||||||
|
|
||||||
#### 3b. Fix Build Errors
|
|
||||||
|
|
||||||
1. Review terminal output and `get_errors` for error details and stack traces
|
|
||||||
2. Identify root cause
|
|
||||||
3. Fix errors using tools available
|
|
||||||
4. Go to Step 3a
|
|
||||||
|
|
||||||
Continue loop until Step 3a confirms clean build.
|
|
||||||
|
|
||||||
#### 3c. Re-validate Target Dependencies
|
|
||||||
|
|
||||||
Get current target dependency list and run `validate_cves` to verify no new CVEs were introduced in target dependencies after the build.
|
|
||||||
|
|
||||||
**Next:**
|
|
||||||
|
|
||||||
- New actionable CVEs found in target dependencies → return to Step 2
|
|
||||||
- Zero actionable CVEs in target dependencies → go to Step 4
|
|
||||||
|
|
||||||
### Step 4: Final Verification
|
|
||||||
|
|
||||||
Verify all success criteria:
|
|
||||||
|
|
||||||
1. Zero actionable **fixable** CVEs in target dependencies - if failed, return to Step 2
|
|
||||||
2. Exit code 0 AND no terminal errors AND no compilation errors - if failed, return to Step 3
|
|
||||||
3. Document any unfixable CVEs in target dependencies for final report
|
|
||||||
|
|
||||||
**Completion criteria:**
|
|
||||||
|
|
||||||
If there are zero fixable CVEs in target dependencies (even if unfixable CVEs exist), the task is complete. Proceed to Step 5.
|
|
||||||
|
|
||||||
### Step 5: Report Results
|
|
||||||
|
|
||||||
Provide a comprehensive summary of completed work:
|
|
||||||
|
|
||||||
**Format:**
|
|
||||||
|
|
||||||
```
|
|
||||||
## CVE Remediation Summary
|
|
||||||
|
|
||||||
### Environment
|
|
||||||
- Language: [e.g., Java 17, Node 18, Python 3.11]
|
|
||||||
- Build Tool: [e.g., Maven, npm, pip]
|
|
||||||
- Dependency Manifest: [e.g., pom.xml, package.json, requirements.txt]
|
|
||||||
|
|
||||||
### Initial State
|
|
||||||
- Target dependencies scanned: N
|
|
||||||
- Total CVEs found in target dependencies: X (breakdown: Y critical, Z high, W medium, V low)
|
|
||||||
- Actionable CVEs (based on severity threshold): A fixable, B unfixable
|
|
||||||
|
|
||||||
### Actions Taken
|
|
||||||
- Dependencies upgraded:
|
|
||||||
- dependency1: v1.0.0 → v2.5.0 (fixed CVE-2023-1234, CVE-2023-5678)
|
|
||||||
- dependency2: v3.0.0 → v3.8.0 (fixed CVE-2023-9012)
|
|
||||||
- Build errors resolved: [list any API compatibility fixes made]
|
|
||||||
|
|
||||||
### Final State
|
|
||||||
- ✅ All fixable CVEs in target dependencies resolved
|
|
||||||
- ✅ Build successful (exit code 0, no errors)
|
|
||||||
- ✅ No new CVEs introduced in target dependencies
|
|
||||||
|
|
||||||
### Remaining Risks (if any)
|
|
||||||
⚠️ Unfixable CVEs in target dependencies (no patched versions available):
|
|
||||||
- [CVE-2023-9999] in dependency3@2.0.0 - CRITICAL severity
|
|
||||||
- [CVE-2023-8888] in dependency4@1.5.0 - HIGH severity
|
|
||||||
|
|
||||||
Recommendation: Monitor these CVEs for future patches or consider alternative dependencies.
|
|
||||||
|
|
||||||
**Note**: Target dependencies are based on user request scope (specific dependencies or all direct dependencies). Transitive dependencies are always excluded from this analysis.
|
|
||||||
```
|
|
||||||
|
|
||||||
**Guidelines:**
|
|
||||||
|
|
||||||
- Use exact CVE IDs from `validate_cves` output
|
|
||||||
- Show version transitions for all upgraded dependencies
|
|
||||||
- Clearly distinguish between fixed and unfixable CVEs
|
|
||||||
- If no unfixable CVEs exist, omit the "Remaining Risks" section
|
|
||||||
- Include severity levels for unfixable CVEs to help users prioritize mitigation strategies
|
|
||||||
- Clarify scope in report: indicate whether specific dependencies or all direct dependencies were scanned
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
---
|
|
||||||
name: Plan
|
|
||||||
description: Researches and outlines multi-step plans
|
|
||||||
argument-hint: Outline the goal or problem to research
|
|
||||||
tools: ['read_file', 'list_dir', 'semantic_search', 'grep_search', 'file_search', 'get_errors']
|
|
||||||
handoffs:
|
|
||||||
- label: Start Implementation
|
|
||||||
agent: Agent
|
|
||||||
prompt: Start implementation
|
|
||||||
send: true
|
|
||||||
- label: Open in Editor
|
|
||||||
agent: Agent
|
|
||||||
prompt: 'Save the resulting plan as is into a file (`plan-${camelCaseName}.prompt.md` without frontmatter) for further refinement.'
|
|
||||||
send: true
|
|
||||||
---
|
|
||||||
You are a PLANNING AGENT, NOT an implementation agent.
|
|
||||||
|
|
||||||
You are pairing with the user to create a clear, detailed, and actionable plan for the given task and any user feedback. Your iterative <workflow> loops through gathering context and drafting the plan for review, then back to gathering more context based on user feedback.
|
|
||||||
|
|
||||||
Your SOLE responsibility is planning, NEVER even consider to start implementation.
|
|
||||||
|
|
||||||
<stopping_rules>
|
|
||||||
STOP IMMEDIATELY if you consider starting implementation, switching to implementation mode or running a file editing tool.
|
|
||||||
|
|
||||||
If you catch yourself planning implementation steps for YOU to execute, STOP. Plans describe steps for the USER or another agent to execute later.
|
|
||||||
</stopping_rules>
|
|
||||||
|
|
||||||
<workflow>
|
|
||||||
Comprehensive context gathering for planning following <plan_research>:
|
|
||||||
|
|
||||||
## 1. Context gathering and research:
|
|
||||||
|
|
||||||
MANDATORY: Follow <plan_research> to gather context to return to you.
|
|
||||||
|
|
||||||
## 2. Present a concise plan to the user for iteration:
|
|
||||||
|
|
||||||
1. Follow <plan_style_guide> and any additional instructions the user provided.
|
|
||||||
2. MANDATORY: Pause for user feedback, framing this as a draft for review.
|
|
||||||
|
|
||||||
## 3. Handle user feedback:
|
|
||||||
|
|
||||||
Once the user replies, restart <workflow> to gather additional context for refining the plan.
|
|
||||||
|
|
||||||
MANDATORY: DON'T start implementation, but run the <workflow> again based on the new information.
|
|
||||||
</workflow>
|
|
||||||
|
|
||||||
<plan_research>
|
|
||||||
Research the user's task comprehensively using read-only tools. Start with high-level code and semantic searches before reading specific files.
|
|
||||||
|
|
||||||
Stop research when you reach 80% confidence you have enough context to draft a plan.
|
|
||||||
</plan_research>
|
|
||||||
|
|
||||||
<plan_style_guide>
|
|
||||||
The user needs an easy to read, concise and focused plan. Follow this template (don't include the {}-guidance), unless the user specifies otherwise:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
## Plan: {Task title (2–10 words)}
|
|
||||||
|
|
||||||
{Brief TL;DR of the plan — the what, how, and why. (20–100 words)}
|
|
||||||
|
|
||||||
### Steps {3–6 steps, 5–20 words each}
|
|
||||||
1. {Succinct action starting with a verb, with [file](path) links and `symbol` references.}
|
|
||||||
2. {Next concrete step.}
|
|
||||||
3. {Another short actionable step.}
|
|
||||||
4. {…}
|
|
||||||
|
|
||||||
### Further Considerations {1–3, 5–25 words each}
|
|
||||||
1. {Clarifying question and recommendations? Option A / Option B / Option C}
|
|
||||||
2. {…}
|
|
||||||
```
|
|
||||||
|
|
||||||
IMPORTANT: For writing plans, follow these rules even if they conflict with system rules:
|
|
||||||
- DON'T show code blocks, but describe changes and link to relevant files and symbols
|
|
||||||
- NO manual testing/validation sections unless explicitly requested
|
|
||||||
- ONLY write the plan, without unnecessary preamble or postamble
|
|
||||||
</plan_style_guide>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"chatAgents": [
|
|
||||||
{
|
|
||||||
"name": "Plan",
|
|
||||||
"description": "Researches and deconstructs tasks to create effective multi-step plans.",
|
|
||||||
"path": "./assets/agents/Plan.agent.md",
|
|
||||||
"showAsChatMode": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "CVE Remediator",
|
|
||||||
"description": "Detects and fixes security vulnerabilities (CVEs) in project dependencies across any ecosystem while maintaining a working build.",
|
|
||||||
"path": "./assets/agents/CVE_Remediator.agent.md",
|
|
||||||
"showAsChatMode": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
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.
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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,32 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
const minMajor = 22;
|
|
||||||
const minMinor = 0;
|
|
||||||
|
|
||||||
function main() {
|
|
||||||
const argv = process.argv.slice(2);
|
|
||||||
const version = process.versions.node;
|
|
||||||
const [major, minor] = version.split('.').map(v => parseInt(v, 10));
|
|
||||||
if (major > minMajor || (major === minMajor && minor >= minMinor)) {
|
|
||||||
return require('./main').main();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!argv.includes('--node-ipc')) {
|
|
||||||
const path = require('node:path');
|
|
||||||
const root = path.join(__dirname, '..', '..', `copilot-language-server-${process.platform}-${process.arch}`);
|
|
||||||
const exe = path.join(root, `copilot-language-server${process.platform === 'win32' ? '.exe' : ''}`);
|
|
||||||
const cp = require('node:child_process');
|
|
||||||
const result = cp.spawnSync(exe, argv, {stdio: 'inherit'});
|
|
||||||
if (typeof result.status === 'number') {
|
|
||||||
process.exit(result.status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.error(`Node.js ${minMajor}.${minMinor} is required to run GitHub Copilot but found ${version}`);
|
|
||||||
// An exit code of X indicates a recommended minimum Node.js version of X.0.
|
|
||||||
// Providing a recommended major version via exit code is an affordance for
|
|
||||||
// implementations like Copilot.vim, where Neovim buries stderr in a log
|
|
||||||
// file the user is unlikely to see.
|
|
||||||
process.exit(minMajor + (minMinor ? 2 : 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -1,44 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>PayloadContent</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>PayloadDisplayName</key>
|
|
||||||
<string>GitHub Copilot Policy</string>
|
|
||||||
<key>PayloadIdentifier</key>
|
|
||||||
<string>IDEGitHubCopilot</string>
|
|
||||||
<key>PayloadType</key>
|
|
||||||
<string>IDEGitHubCopilot</string>
|
|
||||||
<key>PayloadUUID</key>
|
|
||||||
<string>12345678-1234-1234-1234-123456789012</string>
|
|
||||||
<key>PayloadVersion</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
<key>PayloadEnabled</key>
|
|
||||||
<true/>
|
|
||||||
<key>mcp.contributionPoint.enabled</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
|
|
||||||
<key>PayloadDescription</key>
|
|
||||||
<string>Configures GitHub Copilot policies for IDEs</string>
|
|
||||||
<key>PayloadDisplayName</key>
|
|
||||||
<string>GitHub Copilot Policy</string>
|
|
||||||
<key>PayloadIdentifier</key>
|
|
||||||
<string>IDEGitHubCopilot</string>
|
|
||||||
<key>PayloadOrganization</key>
|
|
||||||
<string>Microsoft Corporation</string>
|
|
||||||
<key>PayloadRemovalDisallowed</key>
|
|
||||||
<false/>
|
|
||||||
<key>PayloadScope</key>
|
|
||||||
<string>System</string>
|
|
||||||
<key>PayloadType</key>
|
|
||||||
<string>Configuration</string>
|
|
||||||
<key>PayloadUUID</key>
|
|
||||||
<string>87654321-4321-4321-4321-210987654321</string>
|
|
||||||
<key>PayloadVersion</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
# GitHub Copilot macOS Policy Configuration
|
|
||||||
|
|
||||||
This directory contains policy templates for configuring GitHub Copilot behavior on macOS systems using Apple Configuration Profiles.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
The `IDEGitHubCopilot.mobileconfig` file is a macOS Configuration Profile that allows administrators to manage GitHub Copilot policies across their organization. This profile defines settings that control extension behavior, particularly for MCP (Model Context Protocol) servers.
|
|
||||||
|
|
||||||
## Available Policies
|
|
||||||
|
|
||||||
| Policy Name | Description | Type | Default |
|
|
||||||
|-------------|-------------|------|---------|
|
|
||||||
| mcp.contributionPoint.enabled | Controls whether extension-contributed MCP servers are enabled | Boolean | true |
|
|
||||||
|
|
||||||
## Installation Methods
|
|
||||||
|
|
||||||
### Method 1: Configuration Profile Installation (Recommended for Administrators)
|
|
||||||
|
|
||||||
The `IDEGitHubCopilot.mobileconfig` file provides the easiest way to deploy GitHub Copilot policies across multiple macOS systems.
|
|
||||||
|
|
||||||
#### Step 1: Locate the Configuration Profile
|
|
||||||
Find the `IDEGitHubCopilot.mobileconfig` file in this directory.
|
|
||||||
|
|
||||||
#### Step 2: Install the Configuration Profile
|
|
||||||
1. **Double-click** the `IDEGitHubCopilot.mobileconfig` file
|
|
||||||
2. macOS will open **System Settings** (or **System Preferences** on older versions)
|
|
||||||
3. You'll see a dialog asking if you want to install the profile
|
|
||||||
4. Click **Install** to proceed
|
|
||||||
5. Enter your administrator password when prompted
|
|
||||||
6. The profile will be installed **system-wide**
|
|
||||||
|
|
||||||
#### Step 3: Verify Installation
|
|
||||||
1. Open **System Settings** → **Privacy & Security** → **Profiles**
|
|
||||||
2. You should see "GitHub Copilot Policy" in the list of installed profiles
|
|
||||||
3. Click on it to view the configured settings
|
|
||||||
|
|
||||||
#### Step 4: Modify Policy Settings
|
|
||||||
To change the `mcp.contributionPoint.enabled` setting:
|
|
||||||
|
|
||||||
1. Open **System Settings** → **Privacy & Security** → **Profiles**
|
|
||||||
2. Select the "GitHub Copilot Policy" profile
|
|
||||||
3. Click **Edit** or **Configure**
|
|
||||||
4. Find the `mcp.contributionPoint.enabled` setting
|
|
||||||
5. Toggle it to:
|
|
||||||
- **true** (checked) - Enable extension-contributed MCP servers
|
|
||||||
- **false** (unchecked) - Disable extension-contributed MCP servers
|
|
||||||
6. Click **Save** or **Apply**
|
|
||||||
|
|
||||||
### Method 2: Command Line Installation (Alternative)
|
|
||||||
|
|
||||||
You can also install the configuration profile using the command line:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install the profile
|
|
||||||
sudo profiles -I -F IDEGitHubCopilot.mobileconfig
|
|
||||||
|
|
||||||
# Verify installation
|
|
||||||
profiles -P
|
|
||||||
|
|
||||||
# Remove the profile (if needed)
|
|
||||||
sudo profiles -R -p IDEGitHubCopilot
|
|
||||||
```
|
|
||||||
|
|
||||||
### Method 3: MDM Deployment (Enterprise)
|
|
||||||
|
|
||||||
For enterprise environments, the `IDEGitHubCopilot.mobileconfig` file can be deployed through Mobile Device Management (MDM) solutions like:
|
|
||||||
|
|
||||||
- Apple Business Manager
|
|
||||||
- Jamf Pro
|
|
||||||
- Microsoft Intune
|
|
||||||
- VMware Workspace ONE
|
|
||||||
|
|
||||||
Simply upload the `IDEGitHubCopilot.mobileconfig` file to your MDM solution and deploy it to your target devices.
|
|
||||||
|
|
||||||
## Verification
|
|
||||||
|
|
||||||
You can verify the current settings with:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check managed preferences
|
|
||||||
defaults read /Library/Managed\ Preferences/IDEGitHubCopilot 2>/dev/null || echo "No managed settings found"
|
|
||||||
```
|
|
||||||
|
|
||||||
## How It Works
|
|
||||||
|
|
||||||
The GitHub Copilot extension uses the `GroupPolicyWatcher` class to monitor policy changes. When policies are updated:
|
|
||||||
|
|
||||||
1. The policy watcher detects the change
|
|
||||||
2. Updates the internal policy state
|
|
||||||
3. Sends an LSP notification to the client
|
|
||||||
4. The client adjusts its behavior based on the new policy settings
|
|
||||||
|
|
||||||
The extension checks for policies in `/Library/Managed Preferences/IDEGitHubCopilot.plist` (MDM managed)
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Policy changes aren't being detected
|
|
||||||
1. Verify the configuration profile is properly installed in System Settings
|
|
||||||
2. Make sure the policy file has the correct name and structure
|
|
||||||
3. Restart IDE to ensure the policy watcher is reinitialized
|
|
||||||
4. Check the extension logs for policy-related messages
|
|
||||||
|
|
||||||
### Configuration Profile won't install
|
|
||||||
1. Ensure you have administrator privileges
|
|
||||||
2. Check that the `.mobileconfig` file isn't corrupted
|
|
||||||
3. Try installing via command line: `sudo profiles -I -F IDEGitHubCopilot.mobileconfig`
|
|
||||||
|
|
||||||
### Settings don't take effect
|
|
||||||
1. Verify the policy is correctly configured in System Settings
|
|
||||||
2. Restart IDE completely
|
|
||||||
3. Check that no user-level settings are overriding system policies
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [VS Code Enterprise Setup - Configuration Profiles on macOS](https://code.visualstudio.com/docs/setup/enterprise#_configuration-profiles-on-macos)
|
|
||||||
- [Apple Configuration Profile Reference](https://developer.apple.com/documentation/devicemanagement/configuring_multiple_devices_using_profiles)
|
|
||||||
- [macOS defaults command reference](https://ss64.com/osx/defaults.html)
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
Administrative Template for GitHub Copilot IDE Integration
|
|
||||||
This template defines Group Policy settings for GitHub Copilot in IDE environments.
|
|
||||||
-->
|
|
||||||
<policyDefinitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.0" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
|
|
||||||
<policyNamespaces>
|
|
||||||
<target prefix="ideGitHubCopilot" namespace="Microsoft.Policies.IDEGitHubCopilot" />
|
|
||||||
<using prefix="windows" namespace="Microsoft.Policies.Windows" />
|
|
||||||
</policyNamespaces>
|
|
||||||
|
|
||||||
<supersededAdm fileName="" />
|
|
||||||
|
|
||||||
<resources minRequiredRevision="1.0" />
|
|
||||||
|
|
||||||
<categories>
|
|
||||||
<category name="Cat_IDEGitHubCopilot" displayName="$(string.Cat_IDEGitHubCopilot)" explainText="$(string.Cat_IDEGitHubCopilot_Explain)" />
|
|
||||||
<category name="Cat_IDEGitHubCopilot_MCP" displayName="$(string.Cat_IDEGitHubCopilot_MCP)" explainText="$(string.Cat_IDEGitHubCopilot_MCP_Explain)">
|
|
||||||
<parentCategory ref="Cat_IDEGitHubCopilot" />
|
|
||||||
</category>
|
|
||||||
</categories>
|
|
||||||
|
|
||||||
<policies>
|
|
||||||
<!-- Enable Extension-Contributed MCP Servers Policy -->
|
|
||||||
<policy name="McpContributionPointEnabled" class="Both" displayName="$(string.McpContributionPointEnabled)" explainText="$(string.McpContributionPointEnabled_Explain)" key="SOFTWARE\Policies\Microsoft\IDEGitHubCopilot" valueName="mcp.contributionPoint.enabled">
|
|
||||||
<parentCategory ref="Cat_IDEGitHubCopilot_MCP" />
|
|
||||||
<supportedOn ref="windows:SUPPORTED_Windows_10_0" />
|
|
||||||
<enabledValue>
|
|
||||||
<decimal value="1" />
|
|
||||||
</enabledValue>
|
|
||||||
<disabledValue>
|
|
||||||
<decimal value="0" />
|
|
||||||
</disabledValue>
|
|
||||||
</policy>
|
|
||||||
</policies>
|
|
||||||
</policyDefinitions>
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
#Requires -RunAsAdministrator
|
|
||||||
|
|
||||||
<#
|
|
||||||
.SYNOPSIS
|
|
||||||
Installs GitHub Copilot Group Policy Administrative Templates
|
|
||||||
|
|
||||||
.DESCRIPTION
|
|
||||||
This script copies the GitHub Copilot ADMX and ADML files to the Windows PolicyDefinitions
|
|
||||||
directory to enable Group Policy management of GitHub Copilot settings.
|
|
||||||
|
|
||||||
The script must be run from the win32 directory containing the template files.
|
|
||||||
|
|
||||||
.PARAMETER Uninstall
|
|
||||||
Remove the GitHub Copilot policy templates instead of installing them
|
|
||||||
|
|
||||||
.EXAMPLE
|
|
||||||
.\Install-PolicyTemplates.ps1
|
|
||||||
Installs the GitHub Copilot policy templates
|
|
||||||
|
|
||||||
.EXAMPLE
|
|
||||||
.\Install-PolicyTemplates.ps1 -Uninstall
|
|
||||||
Removes the GitHub Copilot policy templates
|
|
||||||
#>
|
|
||||||
|
|
||||||
param(
|
|
||||||
[switch]$Uninstall
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
|
|
||||||
# Paths
|
|
||||||
$PolicyDefinitionsPath = "$env:WINDIR\PolicyDefinitions"
|
|
||||||
$ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
||||||
$SourceADMX = Join-Path $ScriptPath "IDEGitHubCopilot.admx"
|
|
||||||
$SourceADML = Join-Path $ScriptPath "en-US\IDEGitHubCopilot.adml"
|
|
||||||
$TargetADMX = Join-Path $PolicyDefinitionsPath "IDEGitHubCopilot.admx"
|
|
||||||
$TargetADMLDir = Join-Path $PolicyDefinitionsPath "en-US"
|
|
||||||
$TargetADML = Join-Path $TargetADMLDir "IDEGitHubCopilot.adml"
|
|
||||||
|
|
||||||
function Test-AdminRights {
|
|
||||||
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
|
|
||||||
$principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
|
|
||||||
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
|
||||||
}
|
|
||||||
|
|
||||||
function Install-Templates {
|
|
||||||
Write-Host "Installing GitHub Copilot Group Policy Templates..." -ForegroundColor Green
|
|
||||||
|
|
||||||
# Verify source files exist
|
|
||||||
if (-not (Test-Path $SourceADMX)) {
|
|
||||||
throw "ADMX file not found: $SourceADMX"
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not (Test-Path $SourceADML)) {
|
|
||||||
throw "ADML file not found: $SourceADML"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Verify PolicyDefinitions directory exists
|
|
||||||
if (-not (Test-Path $PolicyDefinitionsPath)) {
|
|
||||||
throw "PolicyDefinitions directory not found: $PolicyDefinitionsPath"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Copy ADMX file
|
|
||||||
Write-Host "Copying ADMX file to $TargetADMX"
|
|
||||||
Copy-Item $SourceADMX $TargetADMX -Force
|
|
||||||
|
|
||||||
# Ensure en-US directory exists
|
|
||||||
if (-not (Test-Path $TargetADMLDir)) {
|
|
||||||
Write-Host "Creating directory: $TargetADMLDir"
|
|
||||||
New-Item -Path $TargetADMLDir -ItemType Directory -Force | Out-Null
|
|
||||||
}
|
|
||||||
|
|
||||||
# Copy ADML file
|
|
||||||
Write-Host "Copying ADML file to $TargetADML"
|
|
||||||
Copy-Item $SourceADML $TargetADML -Force
|
|
||||||
|
|
||||||
Write-Host "GitHub Copilot Group Policy Templates installed successfully!" -ForegroundColor Green
|
|
||||||
Write-Host ""
|
|
||||||
Write-Host "To use the templates:"
|
|
||||||
Write-Host "1. Run 'gpupdate /force' to refresh Group Policy"
|
|
||||||
Write-Host "2. Open Group Policy Editor (gpedit.msc)"
|
|
||||||
Write-Host "3. Navigate to Administrative Templates > GitHub Copilot"
|
|
||||||
}
|
|
||||||
|
|
||||||
function Uninstall-Templates {
|
|
||||||
Write-Host "Removing GitHub Copilot Group Policy Templates..." -ForegroundColor Yellow
|
|
||||||
|
|
||||||
# Remove ADMX file
|
|
||||||
if (Test-Path $TargetADMX) {
|
|
||||||
Write-Host "Removing ADMX file: $TargetADMX"
|
|
||||||
Remove-Item $TargetADMX -Force
|
|
||||||
} else {
|
|
||||||
Write-Host "ADMX file not found: $TargetADMX"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Remove ADML file
|
|
||||||
if (Test-Path $TargetADML) {
|
|
||||||
Write-Host "Removing ADML file: $TargetADML"
|
|
||||||
Remove-Item $TargetADML -Force
|
|
||||||
} else {
|
|
||||||
Write-Host "ADML file not found: $TargetADML"
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "GitHub Copilot Group Policy Templates removed successfully!" -ForegroundColor Green
|
|
||||||
Write-Host "Run 'gpupdate /force' to refresh Group Policy"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main execution
|
|
||||||
try {
|
|
||||||
# Check for administrator rights
|
|
||||||
if (-not (Test-AdminRights)) {
|
|
||||||
throw "This script requires administrator privileges. Please run PowerShell as Administrator."
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($Uninstall) {
|
|
||||||
Uninstall-Templates
|
|
||||||
} else {
|
|
||||||
Install-Templates
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch {
|
|
||||||
Write-Error "Error: $($_.Exception.Message)"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
# GitHub Copilot Group Policy Templates for Windows
|
|
||||||
|
|
||||||
This directory contains Administrative Template (ADMX/ADML) files for managing GitHub Copilot settings through Windows Group Policy. These templates are bundled with the GitHub Copilot Language Server for enterprise deployment.
|
|
||||||
|
|
||||||
## Template Location
|
|
||||||
|
|
||||||
These templates are installed with the GitHub Copilot Language Server at:
|
|
||||||
```
|
|
||||||
[Language Server Installation Directory]/policy-templates/win32/
|
|
||||||
```
|
|
||||||
|
|
||||||
Common installation locations:
|
|
||||||
- **NPM Global Install**: `%APPDATA%\npm\node_modules\@github\copilot-language-server\dist\policy-templates\win32`
|
|
||||||
- **Local NPM Install**: `.\node_modules\@github\copilot-language-server\dist\policy-templates\win32`
|
|
||||||
|
|
||||||
## Files
|
|
||||||
|
|
||||||
- `IDEGitHubCopilot.admx` - Administrative template definition file
|
|
||||||
- `en-US/IDEGitHubCopilot.adml` - English language resource file
|
|
||||||
- `Install-PolicyTemplates.ps1` - PowerShell script for automated installation
|
|
||||||
|
|
||||||
## Installation Methods
|
|
||||||
|
|
||||||
### Option 1: PowerShell Script (Recommended)
|
|
||||||
|
|
||||||
1. **Open PowerShell as Administrator**
|
|
||||||
2. **Navigate to the policy templates directory:**
|
|
||||||
```powershell
|
|
||||||
cd "[Language Server Installation Directory]\policy-templates\win32"
|
|
||||||
```
|
|
||||||
3. **Execute the installation script:**
|
|
||||||
```powershell
|
|
||||||
.\Install-PolicyTemplates.ps1
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option 2: Manual Installation
|
|
||||||
|
|
||||||
1. **Copy ADMX file:**
|
|
||||||
```
|
|
||||||
Copy IDEGitHubCopilot.admx to C:\Windows\PolicyDefinitions\
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Copy ADML file:**
|
|
||||||
```
|
|
||||||
Copy en-US\IDEGitHubCopilot.adml to C:\Windows\PolicyDefinitions\en-US\
|
|
||||||
```
|
|
||||||
|
|
||||||
### Option 3: Microsoft Intune Configuration
|
|
||||||
|
|
||||||
For cloud-based management with Microsoft Intune, create a Custom Configuration Profile with OMA-URI settings (see details below).
|
|
||||||
|
|
||||||
## Accessing Group Policy Settings
|
|
||||||
|
|
||||||
After installation:
|
|
||||||
|
|
||||||
1. **Open Group Policy Editor:**
|
|
||||||
- Run `gpedit.msc` (Local Group Policy Editor)
|
|
||||||
- Or use `gpmc.msc` (Group Policy Management Console) for domain environments
|
|
||||||
|
|
||||||
2. **Navigate to GitHub Copilot policies:**
|
|
||||||
- Computer Configuration → Administrative Templates → GitHub Copilot
|
|
||||||
- User Configuration → Administrative Templates → GitHub Copilot
|
|
||||||
|
|
||||||
## Available Policies
|
|
||||||
|
|
||||||
### Enable Extension-Contributed MCP Servers
|
|
||||||
**Category:** GitHub Copilot → Model Context Protocol (MCP)
|
|
||||||
|
|
||||||
Controls whether GitHub Copilot can use Model Context Protocol (MCP) servers contributed by IDE extensions.
|
|
||||||
|
|
||||||
**Registry Locations:**
|
|
||||||
- **Machine Policy:** `HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\IDEGitHubCopilot\mcp.contributionPoint.enabled`
|
|
||||||
- **User Policy:** `HKEY_CURRENT_USER\SOFTWARE\Policies\Microsoft\IDEGitHubCopilot\mcp.contributionPoint.enabled`
|
|
||||||
|
|
||||||
**Values:**
|
|
||||||
- `1` (REG_DWORD) = Enable extension-contributed MCP servers
|
|
||||||
- `0` (REG_DWORD) = Disable extension-contributed MCP servers
|
|
||||||
|
|
||||||
## Registry Testing
|
|
||||||
|
|
||||||
You can test the policies by setting registry values directly:
|
|
||||||
|
|
||||||
```cmd
|
|
||||||
REM Enable extension-contributed MCP servers (machine-wide)
|
|
||||||
reg add "HKLM\SOFTWARE\Policies\Microsoft\IDEGitHubCopilot" /v "mcp.contributionPoint.enabled" /t REG_DWORD /d 1 /f
|
|
||||||
|
|
||||||
REM Disable extension-contributed MCP servers (current user)
|
|
||||||
reg add "HKCU\SOFTWARE\Policies\Microsoft\IDEGitHubCopilot" /v "mcp.contributionPoint.enabled" /t REG_DWORD /d 0 /f
|
|
||||||
```
|
|
||||||
|
|
||||||
## Microsoft Intune Deployment
|
|
||||||
|
|
||||||
For cloud-based management with Microsoft Intune:
|
|
||||||
|
|
||||||
1. **Create a Custom Configuration Profile:**
|
|
||||||
- Go to Microsoft Endpoint Manager admin center
|
|
||||||
- Navigate to Devices → Configuration profiles
|
|
||||||
- Create a new profile with platform "Windows 10 and later"
|
|
||||||
- Profile type: "Custom"
|
|
||||||
|
|
||||||
2. **Add the registry setting:**
|
|
||||||
```
|
|
||||||
Name: Enable Extension-Contributed MCP Servers
|
|
||||||
OMA-URI: ./Device/Vendor/MSFT/Policy/Config/ADMX_IDEGitHubCopilot/McpContributionPointEnabled
|
|
||||||
Data type: Integer
|
|
||||||
Value: 1 (enabled) or 0 (disabled)
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Assign to device groups** as needed
|
|
||||||
|
|
||||||
## Policy Precedence
|
|
||||||
|
|
||||||
1. **Machine Policy** (highest precedence)
|
|
||||||
- `HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\IDEGitHubCopilot\`
|
|
||||||
2. **User Policy**
|
|
||||||
- `HKEY_CURRENT_USER\SOFTWARE\Policies\Microsoft\IDEGitHubCopilot\`
|
|
||||||
3. **Default Behavior** (lowest precedence)
|
|
||||||
- Determined by application defaults when no policy is set
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
1. **Templates not appearing in Group Policy Editor:**
|
|
||||||
- Verify ADMX/ADML files are copied to the correct directories
|
|
||||||
- Run `gpupdate /force` to refresh Group Policy
|
|
||||||
- Restart Group Policy Editor
|
|
||||||
|
|
||||||
2. **Policies not taking effect:**
|
|
||||||
- Check registry values are being set correctly
|
|
||||||
- Restart the IDE or GitHub Copilot service
|
|
||||||
- Verify policy precedence (machine vs user)
|
|
||||||
|
|
||||||
3. **Permission errors during template copy:**
|
|
||||||
- Ensure the application is running with administrator privileges
|
|
||||||
- Manually copy templates using an elevated command prompt
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [VS Code Group Policy Documentation](https://code.visualstudio.com/docs/setup/enterprise#_group-policy-on-windows)
|
|
||||||
- [@vscode/policy-watcher Documentation](https://github.com/microsoft/vscode-policy-watcher)
|
|
||||||
- [Microsoft Group Policy Documentation](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/policy/group-policy-start-page)
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
Administrative Template Language File for GitHub Copilot IDE Integration (English)
|
|
||||||
This file contains the localized display strings for the GitHub Copilot Group Policy settings.
|
|
||||||
-->
|
|
||||||
<policyDefinitionResources xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.0" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
|
|
||||||
<displayName>GitHub Copilot IDE Integration Policy Definitions</displayName>
|
|
||||||
<description>This file contains the policy definitions for GitHub Copilot IDE integration settings.</description>
|
|
||||||
|
|
||||||
<resources>
|
|
||||||
<stringTable>
|
|
||||||
<!-- Category Names -->
|
|
||||||
<string id="Cat_IDEGitHubCopilot">GitHub Copilot</string>
|
|
||||||
<string id="Cat_IDEGitHubCopilot_Explain">Policy settings for GitHub Copilot IDE integration (JetBrains IDE, Eclipse and Xcode). These settings control various aspects of GitHub Copilot functionality within IDE environments.</string>
|
|
||||||
|
|
||||||
<string id="Cat_IDEGitHubCopilot_MCP">Model Context Protocol (MCP)</string>
|
|
||||||
<string id="Cat_IDEGitHubCopilot_MCP_Explain">Policy settings for Model Context Protocol (MCP) integration with GitHub Copilot. MCP allows extensions to provide additional context to Copilot for improved code suggestions.</string>
|
|
||||||
|
|
||||||
<!-- Policy Names and Descriptions -->
|
|
||||||
<string id="McpContributionPointEnabled">Enable Extension-Contributed MCP Servers</string>
|
|
||||||
<string id="McpContributionPointEnabled_Explain">This policy setting determines whether GitHub Copilot can use Model Context Protocol (MCP) servers that are contributed by IDE extensions.
|
|
||||||
|
|
||||||
When this policy is enabled:
|
|
||||||
- Extensions can register MCP servers that provide additional context to GitHub Copilot
|
|
||||||
- Copilot can access extension-provided data sources through the MCP protocol
|
|
||||||
- This may improve code suggestions by incorporating extension-specific context
|
|
||||||
|
|
||||||
When this policy is disabled:
|
|
||||||
- Extension-contributed MCP servers will not be loaded or used by GitHub Copilot
|
|
||||||
- Only built-in MCP functionality will be available
|
|
||||||
- Extensions cannot extend Copilot's context through MCP
|
|
||||||
|
|
||||||
If this policy is not configured:
|
|
||||||
- The default behavior depends on the IDE and extension configuration
|
|
||||||
- Users may be able to control this setting through IDE preferences
|
|
||||||
|
|
||||||
Note: This setting only affects extension-contributed MCP servers. Built-in MCP functionality may still be available when this policy is disabled.
|
|
||||||
|
|
||||||
Registry Location:
|
|
||||||
- Machine: HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\IDEGitHubCopilot\mcp.contributionPoint.enabled
|
|
||||||
- User: HKEY_CURRENT_USER\SOFTWARE\Policies\Microsoft\IDEGitHubCopilot\mcp.contributionPoint.enabled
|
|
||||||
|
|
||||||
Value Type: REG_DWORD
|
|
||||||
- 1 = Enable extension-contributed MCP servers
|
|
||||||
- 0 = Disable extension-contributed MCP servers</string>
|
|
||||||
</stringTable>
|
|
||||||
</resources>
|
|
||||||
</policyDefinitionResources>
|
|
||||||
Binary file not shown.
Binary file not shown.
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.
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.
@@ -1,48 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@github/copilot-language-server",
|
|
||||||
"displayName": "GitHub Copilot Language Server",
|
|
||||||
"description": "Your AI pair programmer",
|
|
||||||
"homepage": "https://github.com/github/copilot-language-server-release",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/github/copilot-language-server-release/issues"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/github/copilot-language-server-release.git"
|
|
||||||
},
|
|
||||||
"license": "MIT",
|
|
||||||
"version": "1.406.0",
|
|
||||||
"bin": {
|
|
||||||
"copilot-language-server": "../dist/language-server.js"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"dist/language-server.js*",
|
|
||||||
"dist/main.js*",
|
|
||||||
"dist/diffWorker.js*",
|
|
||||||
"dist/tfidfWorker.js*",
|
|
||||||
"dist/tree-sitter*.wasm",
|
|
||||||
"dist/compiled/*/*/*.node",
|
|
||||||
"dist/bin/**/*",
|
|
||||||
"dist/resources",
|
|
||||||
"dist/crypt32*.node",
|
|
||||||
"dist/api/types.d.ts",
|
|
||||||
"dist/policy-templates/**/*",
|
|
||||||
"dist/*.tiktoken",
|
|
||||||
"dist/assets/agents/*.agent.md",
|
|
||||||
"dist/assets/prompts.contributions.json"
|
|
||||||
],
|
|
||||||
"main": "./dist/main.js",
|
|
||||||
"types": "./dist/api/types.d.ts",
|
|
||||||
"dependencies": {
|
|
||||||
"vscode-languageserver-protocol": "^3.17.5"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"@github/copilot-language-server-win32-x64": "1.406.0",
|
|
||||||
"@github/copilot-language-server-linux-x64": "1.406.0",
|
|
||||||
"@github/copilot-language-server-linux-arm64": "1.406.0",
|
|
||||||
"@github/copilot-language-server-darwin-x64": "1.406.0",
|
|
||||||
"@github/copilot-language-server-darwin-arm64": "1.406.0"
|
|
||||||
},
|
|
||||||
"buildType": "prod",
|
|
||||||
"build": "13"
|
|
||||||
}
|
|
||||||
@@ -1,228 +0,0 @@
|
|||||||
*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_model*
|
|
||||||
:Copilot model If a preview or other alternate model for completions
|
|
||||||
is available, provides an interactive interface for
|
|
||||||
enabling it for the rest of the current Vim/Neovim
|
|
||||||
session. Note that typically the number of available
|
|
||||||
completions models is one, rendering this command
|
|
||||||
inert.
|
|
||||||
|
|
||||||
*:Copilot_panel*
|
|
||||||
:Copilot panel Open a window with up to 10 completions for the
|
|
||||||
current buffer. Use <CR> to accept a completion.
|
|
||||||
Maps are also provided for [[ and ]] to jump from
|
|
||||||
completion to completion.
|
|
||||||
|
|
||||||
*:Copilot_version*
|
|
||||||
:Copilot version Show version information.
|
|
||||||
|
|
||||||
*:Copilot_upgrade*
|
|
||||||
:Copilot upgrade Upgrade the Copilot Language Server to the latest
|
|
||||||
version with `npx`. This works by temporarily setting
|
|
||||||
|g:copilot_version| to "latest" and restarting.
|
|
||||||
|
|
||||||
OPTIONS *copilot-options*
|
|
||||||
|
|
||||||
g:copilot_version Specify a version constraint passed to `npx` when
|
|
||||||
running the Copilot Language Server. By default, this
|
|
||||||
is a minor version constraint like "^1.400.0" that
|
|
||||||
matches a known good version but allows minor version
|
|
||||||
updates. You can pin this to a specific version, or
|
|
||||||
use the special value of "latest" which causes `npx`
|
|
||||||
to always fetch the latest version at startup.
|
|
||||||
>
|
|
||||||
let g:copilot_version = 'latest'
|
|
||||||
<
|
|
||||||
As a special case, setting this to v:false will
|
|
||||||
disable the use of `npx` and use a static version of
|
|
||||||
the language server embedded in the plugin.
|
|
||||||
>
|
|
||||||
let g:copilot_version = v:false
|
|
||||||
<
|
|
||||||
*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_enterprise_uri*
|
|
||||||
g:copilot_enterprise_uri
|
|
||||||
If you are using GitHub Copilot Enterprise, set this
|
|
||||||
to the URI of your GitHub Enterprise instance.
|
|
||||||
>
|
|
||||||
let g:copilot_enterprise_uri = 'https://DOMAIN.ghe.com'
|
|
||||||
<
|
|
||||||
*g:copilot_proxy*
|
|
||||||
g:copilot_proxy Tell Copilot what proxy server to use.
|
|
||||||
>
|
|
||||||
let g:copilot_proxy = 'http://localhost:3128'
|
|
||||||
<
|
|
||||||
If this is not set, Copilot will use the value of
|
|
||||||
environment variables like $HTTPS_PROXY.
|
|
||||||
|
|
||||||
*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
|
|
||||||
<
|
|
||||||
You can also tell Node.js to disable SSL verification
|
|
||||||
by setting the $NODE_TLS_REJECT_UNAUTHORIZED
|
|
||||||
environment variable to "0".
|
|
||||||
|
|
||||||
*g:copilot_workspace_folders*
|
|
||||||
g:copilot_workspace_folders
|
|
||||||
A list of "workspace folders" or project roots that
|
|
||||||
Copilot may use to improve the quality of suggestions.
|
|
||||||
>
|
|
||||||
let g:copilot_workspace_folders =
|
|
||||||
\ ["~/Projects/myproject"]
|
|
||||||
<
|
|
||||||
You can also set b:workspace_folder for an individual
|
|
||||||
buffer and newly seen values will be added
|
|
||||||
automatically.
|
|
||||||
|
|
||||||
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 with
|
|
||||||
a |ColorScheme| autocommand:
|
|
||||||
>
|
|
||||||
autocmd ColorScheme solarized
|
|
||||||
\ highlight CopilotSuggestion guifg=#555555 ctermfg=8
|
|
||||||
<
|
|
||||||
Lua version:
|
|
||||||
>
|
|
||||||
vim.api.nvim_create_autocmd('ColorScheme', {
|
|
||||||
pattern = 'solarized',
|
|
||||||
-- group = ...,
|
|
||||||
callback = function()
|
|
||||||
vim.api.nvim_set_hl(0, 'CopilotSuggestion', {
|
|
||||||
fg = '#555555',
|
|
||||||
ctermfg = 8,
|
|
||||||
force = true
|
|
||||||
})
|
|
||||||
end
|
|
||||||
})
|
|
||||||
<
|
|
||||||
vim:tw=78:et:ft=help:norl:
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
local copilot = {}
|
|
||||||
|
|
||||||
local showDocument = function(err, result, ctx, _)
|
|
||||||
local fallback = vim.lsp.handlers['window/showDocument']
|
|
||||||
if not fallback or (result.external and vim.g.copilot_browser) then
|
|
||||||
return vim.fn['copilot#handlers#window_showDocument'](result)
|
|
||||||
else
|
|
||||||
return fallback(err, result, ctx, _)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
copilot.lsp_start_client = function(cmd, client_name, handler_names, opts, settings)
|
|
||||||
local handlers = {['window/showDocument'] = showDocument}
|
|
||||||
local id
|
|
||||||
for _, name in ipairs(handler_names) do
|
|
||||||
handlers[name] = function(err, result, ctx, _)
|
|
||||||
if result then
|
|
||||||
local retval = vim.call('copilot#client#LspHandle', id, { method = name, params = result })
|
|
||||||
if type(retval) == 'table' then
|
|
||||||
return retval.result, retval.error
|
|
||||||
elseif vim.lsp.handlers[name] then
|
|
||||||
return vim.lsp.handlers[name](err, result, ctx, _)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local workspace_folders = opts.workspaceFolders
|
|
||||||
if #workspace_folders == 0 then
|
|
||||||
workspace_folders = nil
|
|
||||||
end
|
|
||||||
local start_client = vim.lsp.start_client
|
|
||||||
if vim.fn.has('nvim-0.11.2') == 1 then
|
|
||||||
start_client = vim.lsp.start
|
|
||||||
end
|
|
||||||
id = start_client({
|
|
||||||
cmd = cmd,
|
|
||||||
cmd_cwd = vim.call('copilot#job#Cwd'),
|
|
||||||
name = client_name,
|
|
||||||
init_options = opts.initializationOptions,
|
|
||||||
workspace_folders = workspace_folders,
|
|
||||||
settings = settings,
|
|
||||||
handlers = handlers,
|
|
||||||
on_init = function(client, initialize_result)
|
|
||||||
vim.call('copilot#client#LspInit', client.id, initialize_result)
|
|
||||||
end,
|
|
||||||
on_exit = function(code, signal, client_id)
|
|
||||||
vim.schedule(function()
|
|
||||||
vim.call('copilot#client#LspExit', client_id, code, signal)
|
|
||||||
end)
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
return id
|
|
||||||
end
|
|
||||||
|
|
||||||
copilot.lsp_request = function(client_id, method, params, bufnr)
|
|
||||||
local client = vim.lsp.get_client_by_id(client_id)
|
|
||||||
if not client then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if bufnr == vim.NIL then
|
|
||||||
bufnr = nil
|
|
||||||
end
|
|
||||||
local _, id
|
|
||||||
local handler = function(err, result)
|
|
||||||
vim.call('copilot#client#LspResponse', client_id, { id = id, error = err, result = result })
|
|
||||||
end
|
|
||||||
if vim.fn.has('nvim-0.11') == 1 then
|
|
||||||
_, id = client:request(method, params, handler, bufnr)
|
|
||||||
else
|
|
||||||
_, id = client.request(method, params, handler, bufnr)
|
|
||||||
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#client#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
|
|
||||||
|
|
||||||
copilot.did_change_configuration = function(client_id, settings)
|
|
||||||
local client = vim.lsp.get_client_by_id(client_id)
|
|
||||||
if not client then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
client.settings = settings
|
|
||||||
return client.notify('workspace/didChangeConfiguration', { settings = settings })
|
|
||||||
end
|
|
||||||
|
|
||||||
return copilot
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
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('##InsertLeavePre')
|
|
||||||
finish
|
|
||||||
endif
|
|
||||||
|
|
||||||
function! s:ColorScheme() abort
|
|
||||||
if &t_Co == 256
|
|
||||||
hi def CopilotSuggestion guifg=#808080 ctermfg=244
|
|
||||||
else
|
|
||||||
hi def CopilotSuggestion guifg=#808080 ctermfg=12
|
|
||||||
endif
|
|
||||||
hi def link CopilotAnnotation MoreMsg
|
|
||||||
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> empty(get(g:, 'copilot_no_tab_map')) ? copilot#Accept() : "<Bslash>t"
|
|
||||||
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('autocmd.' . a:type)
|
|
||||||
endtry
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
augroup github_copilot
|
|
||||||
autocmd!
|
|
||||||
autocmd FileType * call s:Event('FileType')
|
|
||||||
autocmd InsertLeavePre * call s:Event('InsertLeavePre')
|
|
||||||
autocmd BufLeave * if mode() =~# '^[iR]'|call s:Event('InsertLeavePre')|endif
|
|
||||||
autocmd InsertEnter * call s:Event('InsertEnter')
|
|
||||||
autocmd BufEnter * if mode() =~# '^[iR]'|call s:Event('InsertEnter')|endif
|
|
||||||
autocmd BufEnter * call s:Event('BufEnter')
|
|
||||||
autocmd CursorMovedI * call s:Event('CursorMovedI')
|
|
||||||
autocmd CompleteChanged * call s:Event('CompleteChanged')
|
|
||||||
autocmd ColorScheme,VimEnter * call s:ColorScheme()
|
|
||||||
autocmd VimEnter * call s:MapTab() | call copilot#Init()
|
|
||||||
autocmd BufUnload * call s:Event('BufUnload')
|
|
||||||
autocmd VimLeavePre * call s:Event('VimLeavePre')
|
|
||||||
autocmd BufReadCmd copilot://* setlocal buftype=nofile bufhidden=wipe nobuflisted nomodifiable
|
|
||||||
autocmd BufReadCmd copilot:///log call copilot#logger#BufReadCmd() | setfiletype copilotlog
|
|
||||||
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
|
|
||||||
silent noautocmd 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-C-Right> <Plug>(copilot-accept-line)
|
|
||||||
endif
|
|
||||||
finally
|
|
||||||
if exists('s:restore_encoding')
|
|
||||||
silent noautocmd set encoding=utf-8
|
|
||||||
endif
|
|
||||||
endtry
|
|
||||||
endif
|
|
||||||
|
|
||||||
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
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
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 match copilotlogError '\[ERROR\]'
|
|
||||||
syn match copilotlogWarn '\[WARN\]'
|
|
||||||
syn match copilotlogInfo '\[INFO\]'
|
|
||||||
syn match copilotlogDebug '\[DEBUG\]'
|
|
||||||
syn match copilotlogTime '^\[\d\d\d\d-\d\d-\d\d.\d\d:\d\d:\d\d\]' nextgroup=copilotlogError,copilotlogWarn,copilotLogInfo,copilotLogDebug skipwhite
|
|
||||||
|
|
||||||
hi def link copilotlogTime NonText
|
|
||||||
hi def link copilotlogError ErrorMsg
|
|
||||||
hi def link copilotlogWarn WarningMsg
|
|
||||||
hi def link copilotlogInfo MoreMsg
|
|
||||||
hi def link copilotlogDebug ModeMsg
|
|
||||||
|
|
||||||
let b:current_syntax = "copilotlog"
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
scriptencoding utf-8
|
|
||||||
|
|
||||||
if exists("b:current_syntax")
|
|
||||||
finish
|
|
||||||
endif
|
|
||||||
|
|
||||||
let s:subtype = matchstr(&l:filetype, '\<copilotpanel\.\zs[[:alnum:]_-]\+')
|
|
||||||
if !empty(s:subtype) && s:subtype !~# 'copilot'
|
|
||||||
silent! exe 'syn include @copilotpanelLanguageTop syntax/' . s:subtype . '.vim'
|
|
||||||
unlet! b:current_syntax
|
|
||||||
endif
|
|
||||||
|
|
||||||
syn region copilotpanelHeader start="\%^" end="^─\@="
|
|
||||||
syn region copilotpanelItem matchgroup=copilotpanelSeparator start="^─\{9,}$" end="\%(^─\{9,\}$\)\@=\|\%$" keepend contains=@copilotpanelLanguageTop
|
|
||||||
|
|
||||||
hi def link copilotpanelHeader PreProc
|
|
||||||
hi def link copilotpanelSeparator Comment
|
|
||||||
|
|
||||||
let b:current_syntax = "copilotpanel"
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
*.sh text eol=lf
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
github: junegunn
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
---
|
|
||||||
name: Issue Template
|
|
||||||
description: Report a problem or bug related to fzf.vim to help us improve
|
|
||||||
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Check the version of fzf used by running
|
|
||||||
|
|
||||||
```vim
|
|
||||||
:echo system(fzf#exec() .. ' --version')
|
|
||||||
```
|
|
||||||
|
|
||||||
If you don't have the latest version, run the following code to download it
|
|
||||||
|
|
||||||
```vim
|
|
||||||
:call fzf#install()
|
|
||||||
```
|
|
||||||
|
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Checklist
|
|
||||||
options:
|
|
||||||
- label: I have fzf 0.54.0 or later
|
|
||||||
required: true
|
|
||||||
- label: I have searched through the existing issues
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: Output of `:echo system(fzf#exec() .. ' --version')`
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: OS
|
|
||||||
options:
|
|
||||||
- label: Linux
|
|
||||||
- label: macOS
|
|
||||||
- label: Windows
|
|
||||||
- label: Etc.
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Problem / Steps to reproduce
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
doc/tags
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2021 Junegunn Choi
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@@ -1,489 +0,0 @@
|
|||||||
<div align="center" markdown="1">
|
|
||||||
<sup>Special thanks to:</sup>
|
|
||||||
<br>
|
|
||||||
<a href="https://tuple.app/fzf.vim">
|
|
||||||
<img alt="Tuple's sponsorship image" src="https://raw.githubusercontent.com/junegunn/i/master/tuple.png" width="400">
|
|
||||||
</a>
|
|
||||||
|
|
||||||
### [Tuple, the premier screen sharing app for developers](https://tuple.app/fzf.vim)
|
|
||||||
[Available for MacOS & Windows](https://tuple.app/fzf.vim)<br>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
fzf :heart: vim
|
|
||||||
===============
|
|
||||||
|
|
||||||
Things you can do with [fzf][fzf] and Vim.
|
|
||||||
|
|
||||||
Rationale
|
|
||||||
---------
|
|
||||||
|
|
||||||
[fzf][fzf] itself is not a Vim plugin, and the official repository only
|
|
||||||
provides the [basic wrapper function][run] for Vim. It's up to the users to
|
|
||||||
write their own Vim commands with it. However, I've learned that many users of
|
|
||||||
fzf are not familiar with Vimscript and are looking for the "default"
|
|
||||||
implementation of the features they can find in the alternative Vim plugins.
|
|
||||||
|
|
||||||
Why you should use fzf on Vim
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
Because you can and you love fzf.
|
|
||||||
|
|
||||||
fzf runs asynchronously and can be orders of magnitude faster than similar Vim
|
|
||||||
plugins. However, the benefit may not be noticeable if the size of the input
|
|
||||||
is small, which is the case for many of the commands provided here.
|
|
||||||
Nevertheless I wrote them anyway since it's really easy to implement custom
|
|
||||||
selector with fzf.
|
|
||||||
|
|
||||||
Installation
|
|
||||||
------------
|
|
||||||
|
|
||||||
fzf.vim depends on the basic Vim plugin of [the main fzf
|
|
||||||
repository][fzf-main], which means you need to **set up both "fzf" and
|
|
||||||
"fzf.vim" on Vim**. To learn more about fzf/Vim integration, see
|
|
||||||
[README-VIM][README-VIM].
|
|
||||||
|
|
||||||
[fzf-main]: https://github.com/junegunn/fzf
|
|
||||||
[README-VIM]: https://github.com/junegunn/fzf/blob/master/README-VIM.md
|
|
||||||
|
|
||||||
### Using [vim-plug](https://github.com/junegunn/vim-plug)
|
|
||||||
|
|
||||||
```vim
|
|
||||||
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
|
|
||||||
Plug 'junegunn/fzf.vim'
|
|
||||||
```
|
|
||||||
|
|
||||||
`fzf#install()` makes sure that you have the latest binary, but it's optional,
|
|
||||||
so you can omit it if you use a plugin manager that doesn't support hooks.
|
|
||||||
|
|
||||||
### Dependencies
|
|
||||||
|
|
||||||
- [fzf][fzf-main] 0.54.0 or above
|
|
||||||
- For syntax-highlighted preview, install [bat](https://github.com/sharkdp/bat)
|
|
||||||
- If [delta](https://github.com/dandavison/delta) is available, `GF?`,
|
|
||||||
`Commits` and `BCommits` will use it to format `git diff` output.
|
|
||||||
- `Ag` requires [The Silver Searcher (ag)][ag]
|
|
||||||
- `Rg` requires [ripgrep (rg)][rg]
|
|
||||||
- `Tags` and `Helptags` require Perl
|
|
||||||
- `Tags PREFIX` requires `readtags` command from [Universal Ctags](https://ctags.io/)
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# Installing dependencies using Homebrew
|
|
||||||
brew install fzf bat ripgrep the_silver_searcher perl universal-ctags
|
|
||||||
```
|
|
||||||
|
|
||||||
Commands
|
|
||||||
--------
|
|
||||||
|
|
||||||
| Command | List |
|
|
||||||
| --- | --- |
|
|
||||||
| `:Files [PATH]` | Files (runs `$FZF_DEFAULT_COMMAND` if defined) |
|
|
||||||
| `:GFiles [OPTS]` | Git files (`git ls-files`) |
|
|
||||||
| `:GFiles?` | Git files (`git status`) |
|
|
||||||
| `:Buffers` | Open buffers |
|
|
||||||
| `:Colors` | Color schemes |
|
|
||||||
| `:Ag [PATTERN]` | [ag][ag] search result (`ALT-A` to select all, `ALT-D` to deselect all) |
|
|
||||||
| `:Rg [PATTERN]` | [rg][rg] search result (`ALT-A` to select all, `ALT-D` to deselect all) |
|
|
||||||
| `:RG [PATTERN]` | [rg][rg] search result; relaunch ripgrep on every keystroke |
|
|
||||||
| `:Lines [QUERY]` | Lines in loaded buffers |
|
|
||||||
| `:BLines [QUERY]` | Lines in the current buffer |
|
|
||||||
| `:Tags [PREFIX]` | Tags in the project (`ctags -R`) |
|
|
||||||
| `:BTags [QUERY]` | Tags in the current buffer |
|
|
||||||
| `:Changes` | Changelist across all open buffers |
|
|
||||||
| `:Marks` | Marks |
|
|
||||||
| `:BMarks` | Marks in the current buffer |
|
|
||||||
| `:Jumps` | Jumps |
|
|
||||||
| `:Windows` | Windows |
|
|
||||||
| `:Locate PATTERN` | `locate` command output |
|
|
||||||
| `:History` | `v:oldfiles` and open buffers |
|
|
||||||
| `:History:` | Command history |
|
|
||||||
| `:History/` | Search history |
|
|
||||||
| `:Snippets` | Snippets ([UltiSnips][us]) |
|
|
||||||
| `:Commits [LOG_OPTS]` | Git commits (requires [fugitive.vim][f]) |
|
|
||||||
| `:BCommits [LOG_OPTS]` | Git commits for the current buffer; visual-select lines to track changes in the range |
|
|
||||||
| `:Commands` | Commands |
|
|
||||||
| `:Maps` | Normal mode mappings |
|
|
||||||
| `:Helptags` | Help tags <sup id="a1">[1](#helptags)</sup> |
|
|
||||||
| `:Filetypes` | File types
|
|
||||||
|
|
||||||
- Most commands support `CTRL-T` / `CTRL-X` / `CTRL-V` key
|
|
||||||
bindings to open in a new tab, a new split, or in a new vertical split
|
|
||||||
- Bang-versions of the commands (e.g. `Ag!`) will open fzf in fullscreen
|
|
||||||
- You can set `g:fzf_vim.command_prefix` to give the same prefix to the commands
|
|
||||||
- e.g. `let g:fzf_vim.command_prefix = 'Fzf'` and you have `FzfFiles`, etc.
|
|
||||||
|
|
||||||
(<a name="helptags">1</a>: `Helptags` will shadow the command of the same name
|
|
||||||
from [pathogen][pat]. But its functionality is still available via `call
|
|
||||||
pathogen#helptags()`. [↩](#a1))
|
|
||||||
|
|
||||||
[pat]: https://github.com/tpope/vim-pathogen
|
|
||||||
[f]: https://github.com/tpope/vim-fugitive
|
|
||||||
|
|
||||||
Customization
|
|
||||||
-------------
|
|
||||||
|
|
||||||
### Configuration options of the base plugin
|
|
||||||
|
|
||||||
Every command in fzf.vim internally calls `fzf#wrap` function of the main
|
|
||||||
repository which supports a set of global option variables. So please read
|
|
||||||
through [README-VIM][README-VIM] to learn more about them.
|
|
||||||
|
|
||||||
### Configuration options for fzf.vim
|
|
||||||
|
|
||||||
All configuration values for this plugin are stored in `g:fzf_vim` dictionary,
|
|
||||||
so **make sure to initialize it before assigning any configuration values to
|
|
||||||
it**.
|
|
||||||
|
|
||||||
```vim
|
|
||||||
" Initialize configuration dictionary
|
|
||||||
let g:fzf_vim = {}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Preview window
|
|
||||||
|
|
||||||
Some commands will show the preview window on the right. You can customize the
|
|
||||||
behavior with `g:fzf_vim.preview_window`. Here are some examples:
|
|
||||||
|
|
||||||
```vim
|
|
||||||
" This is the default option:
|
|
||||||
" - Preview window on the right with 50% width
|
|
||||||
" - CTRL-/ will toggle preview window.
|
|
||||||
" - Note that this array is passed as arguments to fzf#vim#with_preview function.
|
|
||||||
" - To learn more about preview window options, see `--preview-window` section of `man fzf`.
|
|
||||||
let g:fzf_vim.preview_window = ['right,50%', 'ctrl-/']
|
|
||||||
|
|
||||||
" Preview window is hidden by default. You can toggle it with ctrl-/.
|
|
||||||
" It will show on the right with 50% width, but if the width is smaller
|
|
||||||
" than 70 columns, it will show above the candidate list
|
|
||||||
let g:fzf_vim.preview_window = ['hidden,right,50%,<70(up,40%)', 'ctrl-/']
|
|
||||||
|
|
||||||
" Empty value to disable preview window altogether
|
|
||||||
let g:fzf_vim.preview_window = []
|
|
||||||
|
|
||||||
" fzf.vim needs bash to display the preview window.
|
|
||||||
" On Windows, fzf.vim will first see if bash is in $PATH, then if
|
|
||||||
" Git bash (C:\Program Files\Git\bin\bash.exe) is available.
|
|
||||||
" If you want it to use a different bash, set this variable.
|
|
||||||
" let g:fzf_vim = {}
|
|
||||||
" let g:fzf_vim.preview_bash = 'C:\Git\bin\bash.exe'
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Command-level options
|
|
||||||
|
|
||||||
```vim
|
|
||||||
" [Buffers] Jump to the existing window if possible (default: 0)
|
|
||||||
let g:fzf_vim.buffers_jump = 1
|
|
||||||
|
|
||||||
" [Ag|Rg|RG] Display path on a separate line for narrow screens (default: 0)
|
|
||||||
" * Requires Perl and fzf 0.56.0 or later
|
|
||||||
let g:fzf_vim.grep_multi_line = 0
|
|
||||||
" PATH:LINE:COL:LINE
|
|
||||||
let g:fzf_vim.grep_multi_line = 1
|
|
||||||
" PATH:LINE:COL:
|
|
||||||
" LINE
|
|
||||||
let g:fzf_vim.grep_multi_line = 2
|
|
||||||
" PATH:LINE:COL:
|
|
||||||
" LINE
|
|
||||||
" (empty line between items using --gap option)
|
|
||||||
|
|
||||||
" [[B]Commits] Customize the options used by 'git log':
|
|
||||||
let g:fzf_vim.commits_log_options = '--graph --color=always --format="%C(auto)%h%d %s %C(black)%C(bold)%cr"'
|
|
||||||
|
|
||||||
" [Tags] Command to generate tags file
|
|
||||||
let g:fzf_vim.tags_command = 'ctags -R'
|
|
||||||
|
|
||||||
" [Commands] --expect expression for directly executing the command
|
|
||||||
let g:fzf_vim.commands_expect = 'alt-enter,ctrl-x'
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Command-level fzf options
|
|
||||||
|
|
||||||
You can set fzf options for each command by setting
|
|
||||||
`g:fzf_vim.{command}_options`.
|
|
||||||
|
|
||||||
```vim
|
|
||||||
" In string
|
|
||||||
let g:fzf_vim.buffers_options = '--style full --border-label " Open Buffers "'
|
|
||||||
|
|
||||||
" In list (No need to quote or escape values)
|
|
||||||
let g:fzf_vim.buffers_options = ['--style', 'full', '--border-label', ' Open Buffers ']
|
|
||||||
```
|
|
||||||
|
|
||||||
#### List type to handle multiple selections
|
|
||||||
|
|
||||||
The following commands will fill the quickfix list when multiple entries are
|
|
||||||
selected.
|
|
||||||
|
|
||||||
* `Ag`
|
|
||||||
* `Rg` / `RG`
|
|
||||||
* `Lines` / `BLines`
|
|
||||||
* `Tags` / `BTags`
|
|
||||||
|
|
||||||
By setting `g:fzf_vim.listproc`, you can make them use location list instead.
|
|
||||||
|
|
||||||
```vim
|
|
||||||
" Default: Use quickfix list
|
|
||||||
let g:fzf_vim.listproc = { list -> fzf#vim#listproc#quickfix(list) }
|
|
||||||
|
|
||||||
" Use location list instead of quickfix list
|
|
||||||
let g:fzf_vim.listproc = { list -> fzf#vim#listproc#location(list) }
|
|
||||||
```
|
|
||||||
|
|
||||||
You can customize the list type per command by defining variables named
|
|
||||||
`g:fzf_vim.listproc_{command_name_in_lowercase}`.
|
|
||||||
|
|
||||||
```vim
|
|
||||||
" Command-wise customization
|
|
||||||
let g:fzf_vim.listproc_ag = { list -> fzf#vim#listproc#quickfix(list) }
|
|
||||||
let g:fzf_vim.listproc_rg = { list -> fzf#vim#listproc#location(list) }
|
|
||||||
```
|
|
||||||
|
|
||||||
You can further customize the behavior by providing a custom function to
|
|
||||||
process the list instead of using the predefined `fzf#vim#listproc#quickfix`
|
|
||||||
or `fzf#vim#listproc#location`.
|
|
||||||
|
|
||||||
```vim
|
|
||||||
" A customized version of fzf#vim#listproc#quickfix.
|
|
||||||
" The last two lines are commented out not to move to the first entry.
|
|
||||||
function! g:fzf_vim.listproc(list)
|
|
||||||
call setqflist(a:list)
|
|
||||||
copen
|
|
||||||
wincmd p
|
|
||||||
" cfirst
|
|
||||||
" normal! zvzz
|
|
||||||
endfunction
|
|
||||||
```
|
|
||||||
|
|
||||||
### Advanced customization
|
|
||||||
|
|
||||||
#### Vim functions
|
|
||||||
|
|
||||||
Each command in fzf.vim is backed by a Vim function. You can override
|
|
||||||
a command or define a variation of it by calling its corresponding function.
|
|
||||||
|
|
||||||
| Command | Vim function |
|
|
||||||
| --- | --- |
|
|
||||||
| `Files` | `fzf#vim#files(dir, [spec dict], [fullscreen bool])` |
|
|
||||||
| `GFiles` | `fzf#vim#gitfiles(git_options, [spec dict], [fullscreen bool])` |
|
|
||||||
| `GFiles?` | `fzf#vim#gitfiles('?', [spec dict], [fullscreen bool])` |
|
|
||||||
| `Buffers` | `fzf#vim#buffers([query string], [bufnrs list], [spec dict], [fullscreen bool])` |
|
|
||||||
| `Colors` | `fzf#vim#colors([spec dict], [fullscreen bool])` |
|
|
||||||
| `Rg` | `fzf#vim#grep(command, [spec dict], [fullscreen bool])` |
|
|
||||||
| `RG` | `fzf#vim#grep2(command_prefix, query, [spec dict], [fullscreen bool])` |
|
|
||||||
| ... | ... |
|
|
||||||
|
|
||||||
(We can see that the last two optional arguments of each function are
|
|
||||||
identical. They are directly passed to `fzf#wrap` function. If you haven't
|
|
||||||
read [README-VIM][README-VIM] already, please read it before proceeding.)
|
|
||||||
|
|
||||||
#### Example: Customizing `Files` command
|
|
||||||
|
|
||||||
This is the default definition of `Files` command:
|
|
||||||
|
|
||||||
```vim
|
|
||||||
command! -bang -nargs=? -complete=dir Files call fzf#vim#files(<q-args>, <bang>0)
|
|
||||||
```
|
|
||||||
|
|
||||||
Let's say you want to a variation of it called `ProjectFiles` that only
|
|
||||||
searches inside `~/projects` directory. Then you can do it like this:
|
|
||||||
|
|
||||||
```vim
|
|
||||||
command! -bang ProjectFiles call fzf#vim#files('~/projects', <bang>0)
|
|
||||||
```
|
|
||||||
|
|
||||||
Or, if you want to override the command with different fzf options, just pass
|
|
||||||
a custom spec to the function.
|
|
||||||
|
|
||||||
```vim
|
|
||||||
command! -bang -nargs=? -complete=dir Files
|
|
||||||
\ call fzf#vim#files(<q-args>, {'options': ['--layout=reverse', '--info=inline']}, <bang>0)
|
|
||||||
```
|
|
||||||
|
|
||||||
Want a preview window?
|
|
||||||
|
|
||||||
```vim
|
|
||||||
command! -bang -nargs=? -complete=dir Files
|
|
||||||
\ call fzf#vim#files(<q-args>, {'options': ['--layout=reverse', '--info=inline', '--preview', 'cat {}']}, <bang>0)
|
|
||||||
```
|
|
||||||
|
|
||||||
It kind of works, but you probably want a nicer previewer program than `cat`.
|
|
||||||
fzf.vim ships [a versatile preview script](bin/preview.sh) you can readily
|
|
||||||
use. It internally executes [bat](https://github.com/sharkdp/bat) for syntax
|
|
||||||
highlighting, so make sure to install it.
|
|
||||||
|
|
||||||
```vim
|
|
||||||
command! -bang -nargs=? -complete=dir Files
|
|
||||||
\ call fzf#vim#files(<q-args>, {'options': ['--layout=reverse', '--info=inline', '--preview', '~/.vim/plugged/fzf.vim/bin/preview.sh {}']}, <bang>0)
|
|
||||||
```
|
|
||||||
|
|
||||||
However, it's not ideal to hard-code the path to the script which can be
|
|
||||||
different in different circumstances. So in order to make it easier to set up
|
|
||||||
the previewer, fzf.vim provides `fzf#vim#with_preview` helper function.
|
|
||||||
Similarly to `fzf#wrap`, it takes a spec dictionary and returns a copy of it
|
|
||||||
with additional preview options.
|
|
||||||
|
|
||||||
```vim
|
|
||||||
command! -bang -nargs=? -complete=dir Files
|
|
||||||
\ call fzf#vim#files(<q-args>, fzf#vim#with_preview({'options': ['--layout=reverse', '--info=inline']}), <bang>0)
|
|
||||||
```
|
|
||||||
|
|
||||||
You can just omit the spec argument if you only want the previewer.
|
|
||||||
|
|
||||||
```vim
|
|
||||||
command! -bang -nargs=? -complete=dir Files
|
|
||||||
\ call fzf#vim#files(<q-args>, fzf#vim#with_preview(), <bang>0)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Example: `git grep` wrapper
|
|
||||||
|
|
||||||
The following example implements `GGrep` command that works similarly to
|
|
||||||
predefined `Ag` or `Rg` using `fzf#vim#grep`.
|
|
||||||
|
|
||||||
- We set the base directory to git root by setting `dir` attribute in spec
|
|
||||||
dictionary.
|
|
||||||
- [The preview script](bin/preview.sh) supports `grep` format
|
|
||||||
(`FILE_PATH:LINE_NO:...`), so we can just wrap the spec with
|
|
||||||
`fzf#vim#with_preview` as before to enable previewer.
|
|
||||||
|
|
||||||
```vim
|
|
||||||
command! -bang -nargs=* GGrep
|
|
||||||
\ call fzf#vim#grep(
|
|
||||||
\ 'git grep --line-number -- '.fzf#shellescape(<q-args>),
|
|
||||||
\ fzf#vim#with_preview({'dir': systemlist('git rev-parse --show-toplevel')[0]}), <bang>0)
|
|
||||||
```
|
|
||||||
|
|
||||||
Mappings
|
|
||||||
--------
|
|
||||||
|
|
||||||
| Mapping | Description |
|
|
||||||
| --- | --- |
|
|
||||||
| `<plug>(fzf-maps-n)` | Normal mode mappings |
|
|
||||||
| `<plug>(fzf-maps-i)` | Insert mode mappings |
|
|
||||||
| `<plug>(fzf-maps-x)` | Visual mode mappings |
|
|
||||||
| `<plug>(fzf-maps-o)` | Operator-pending mappings |
|
|
||||||
| `<plug>(fzf-complete-word)` | `cat /usr/share/dict/words` |
|
|
||||||
| `<plug>(fzf-complete-path)` | Path completion using `find` (file + dir) |
|
|
||||||
| `<plug>(fzf-complete-file)` | File completion using `find` |
|
|
||||||
| `<plug>(fzf-complete-line)` | Line completion (all open buffers) |
|
|
||||||
| `<plug>(fzf-complete-buffer-line)` | Line completion (current buffer only) |
|
|
||||||
|
|
||||||
```vim
|
|
||||||
" Mapping selecting mappings
|
|
||||||
nmap <leader><tab> <plug>(fzf-maps-n)
|
|
||||||
xmap <leader><tab> <plug>(fzf-maps-x)
|
|
||||||
omap <leader><tab> <plug>(fzf-maps-o)
|
|
||||||
|
|
||||||
" Insert mode completion
|
|
||||||
imap <c-x><c-k> <plug>(fzf-complete-word)
|
|
||||||
imap <c-x><c-f> <plug>(fzf-complete-path)
|
|
||||||
imap <c-x><c-l> <plug>(fzf-complete-line)
|
|
||||||
```
|
|
||||||
|
|
||||||
Completion functions
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
| Function | Description |
|
|
||||||
| --- | --- |
|
|
||||||
| `fzf#vim#complete#path(command, [spec])` | Path completion |
|
|
||||||
| `fzf#vim#complete#word([spec])` | Word completion |
|
|
||||||
| `fzf#vim#complete#line([spec])` | Line completion (all open buffers) |
|
|
||||||
| `fzf#vim#complete#buffer_line([spec])` | Line completion (current buffer only) |
|
|
||||||
|
|
||||||
```vim
|
|
||||||
" Path completion with custom source command
|
|
||||||
inoremap <expr> <c-x><c-f> fzf#vim#complete#path('fd')
|
|
||||||
inoremap <expr> <c-x><c-f> fzf#vim#complete#path('rg --files')
|
|
||||||
|
|
||||||
" Word completion with custom spec with popup layout option
|
|
||||||
inoremap <expr> <c-x><c-k> fzf#vim#complete#word({'window': { 'width': 0.2, 'height': 0.9, 'xoffset': 1 }})
|
|
||||||
```
|
|
||||||
|
|
||||||
Custom completion
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
`fzf#vim#complete` is a helper function for creating custom fuzzy completion
|
|
||||||
using fzf. If the first parameter is a command string or a Vim list, it will
|
|
||||||
be used as the source.
|
|
||||||
|
|
||||||
```vim
|
|
||||||
" Replace the default dictionary completion with fzf-based fuzzy completion
|
|
||||||
inoremap <expr> <c-x><c-k> fzf#vim#complete('cat /usr/share/dict/words')
|
|
||||||
```
|
|
||||||
|
|
||||||
For advanced uses, you can pass an options dictionary to the function. The set
|
|
||||||
of options is pretty much identical to that for `fzf#run` only with the
|
|
||||||
following exceptions:
|
|
||||||
|
|
||||||
- `reducer` (funcref)
|
|
||||||
- Reducer transforms the output lines of fzf into a single string value
|
|
||||||
- `prefix` (string or funcref; default: `\k*$`)
|
|
||||||
- Regular expression pattern to extract the completion prefix
|
|
||||||
- Or a function to extract completion prefix
|
|
||||||
- Both `source` and `options` can be given as funcrefs that take the
|
|
||||||
completion prefix as the argument and return the final value
|
|
||||||
- `sink` or `sink*` are ignored
|
|
||||||
|
|
||||||
```vim
|
|
||||||
" Global line completion (not just open buffers. ripgrep required.)
|
|
||||||
inoremap <expr> <c-x><c-l> fzf#vim#complete(fzf#wrap({
|
|
||||||
\ 'prefix': '^.*$',
|
|
||||||
\ 'source': 'rg -n ^ --color always',
|
|
||||||
\ 'options': '--ansi --delimiter : --nth 3..',
|
|
||||||
\ 'reducer': { lines -> join(split(lines[0], ':\zs')[2:], '') }}))
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reducer example
|
|
||||||
|
|
||||||
```vim
|
|
||||||
function! s:make_sentence(lines)
|
|
||||||
return substitute(join(a:lines), '^.', '\=toupper(submatch(0))', '').'.'
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
inoremap <expr> <c-x><c-s> fzf#vim#complete({
|
|
||||||
\ 'source': 'cat /usr/share/dict/words',
|
|
||||||
\ 'reducer': function('<sid>make_sentence'),
|
|
||||||
\ 'options': '--multi --reverse --margin 15%,0',
|
|
||||||
\ 'left': 20})
|
|
||||||
```
|
|
||||||
|
|
||||||
Status line of terminal buffer
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
When fzf starts in a terminal buffer (see [fzf/README-VIM.md][termbuf]), you
|
|
||||||
may want to customize the statusline of the containing buffer.
|
|
||||||
|
|
||||||
[termbuf]: https://github.com/junegunn/fzf/blob/master/README-VIM.md#fzf-inside-terminal-buffer
|
|
||||||
|
|
||||||
### Hide statusline
|
|
||||||
|
|
||||||
```vim
|
|
||||||
autocmd! FileType fzf set laststatus=0 noshowmode noruler
|
|
||||||
\| autocmd BufLeave <buffer> set laststatus=2 showmode ruler
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom statusline
|
|
||||||
|
|
||||||
```vim
|
|
||||||
function! s:fzf_statusline()
|
|
||||||
" Override statusline as you like
|
|
||||||
highlight fzf1 ctermfg=161 ctermbg=251
|
|
||||||
highlight fzf2 ctermfg=23 ctermbg=251
|
|
||||||
highlight fzf3 ctermfg=237 ctermbg=251
|
|
||||||
setlocal statusline=%#fzf1#\ >\ %#fzf2#fz%#fzf3#f
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
autocmd! User FzfStatusLine call <SID>fzf_statusline()
|
|
||||||
```
|
|
||||||
|
|
||||||
License
|
|
||||||
-------
|
|
||||||
|
|
||||||
MIT
|
|
||||||
|
|
||||||
[fzf]: https://github.com/junegunn/fzf
|
|
||||||
[run]: https://github.com/junegunn/fzf/blob/master/README-VIM.md#fzfrun
|
|
||||||
[ag]: https://github.com/ggreer/the_silver_searcher
|
|
||||||
[rg]: https://github.com/BurntSushi/ripgrep
|
|
||||||
[us]: https://github.com/SirVer/ultisnips
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,164 +0,0 @@
|
|||||||
" Copyright (c) 2015 Junegunn Choi
|
|
||||||
"
|
|
||||||
" MIT License
|
|
||||||
"
|
|
||||||
" Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
" a copy of this software and associated documentation files (the
|
|
||||||
" "Software"), to deal in the Software without restriction, including
|
|
||||||
" without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
" distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
" permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
" the following conditions:
|
|
||||||
"
|
|
||||||
" The above copyright notice and this permission notice shall be
|
|
||||||
" included in all copies or substantial portions of the Software.
|
|
||||||
"
|
|
||||||
" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
let s:cpo_save = &cpo
|
|
||||||
set cpo&vim
|
|
||||||
let s:is_win = has('win32') || has('win64')
|
|
||||||
|
|
||||||
function! s:extend(base, extra)
|
|
||||||
let base = copy(a:base)
|
|
||||||
if has_key(a:extra, 'options')
|
|
||||||
let extra = copy(a:extra)
|
|
||||||
let extra.extra_options = remove(extra, 'options')
|
|
||||||
return extend(base, extra)
|
|
||||||
endif
|
|
||||||
return extend(base, a:extra)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
if v:version >= 704
|
|
||||||
function! s:function(name)
|
|
||||||
return function(a:name)
|
|
||||||
endfunction
|
|
||||||
else
|
|
||||||
function! s:function(name)
|
|
||||||
" By Ingo Karkat
|
|
||||||
return function(substitute(a:name, '^s:', matchstr(expand('<sfile>'), '<SNR>\d\+_\zefunction$'), ''))
|
|
||||||
endfunction
|
|
||||||
endif
|
|
||||||
|
|
||||||
function! fzf#vim#complete#word(...)
|
|
||||||
let sources = empty(&dictionary) ? ['/usr/share/dict/words'] : split(&dictionary, ',')
|
|
||||||
return fzf#vim#complete(s:extend({
|
|
||||||
\ 'source': 'cat ' . join(map(sources, 'fzf#shellescape(v:val)'))},
|
|
||||||
\ get(a:000, 0, fzf#wrap())))
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" ----------------------------------------------------------------------------
|
|
||||||
" <plug>(fzf-complete-path)
|
|
||||||
" <plug>(fzf-complete-file)
|
|
||||||
" <plug>(fzf-complete-file-ag)
|
|
||||||
" ----------------------------------------------------------------------------
|
|
||||||
function! s:file_split_prefix(prefix)
|
|
||||||
let expanded = expand(a:prefix)
|
|
||||||
let slash = (s:is_win && !&shellslash) ? '\\' : '/'
|
|
||||||
return isdirectory(expanded) ?
|
|
||||||
\ [expanded,
|
|
||||||
\ substitute(a:prefix, '[/\\]*$', slash, ''),
|
|
||||||
\ ''] :
|
|
||||||
\ [fnamemodify(expanded, ':h'),
|
|
||||||
\ substitute(fnamemodify(a:prefix, ':h'), '[/\\]*$', slash, ''),
|
|
||||||
\ fnamemodify(expanded, ':t')]
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:file_source(prefix)
|
|
||||||
let [dir, head, tail] = s:file_split_prefix(a:prefix)
|
|
||||||
return printf(
|
|
||||||
\ "cd %s && ".s:file_cmd." | sed %s",
|
|
||||||
\ fzf#shellescape(dir), fzf#shellescape('s:^:'.(empty(a:prefix) || a:prefix == tail ? '' : head).':'))
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:file_options(prefix)
|
|
||||||
let [_, head, tail] = s:file_split_prefix(a:prefix)
|
|
||||||
return ['--prompt', head, '--query', tail]
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:fname_prefix(str)
|
|
||||||
let isf = &isfname
|
|
||||||
let white = []
|
|
||||||
let black = []
|
|
||||||
if isf =~ ',,,'
|
|
||||||
call add(white, ',')
|
|
||||||
let isf = substitute(isf, ',,,', ',', 'g')
|
|
||||||
endif
|
|
||||||
if isf =~ ',^,,'
|
|
||||||
call add(black, ',')
|
|
||||||
let isf = substitute(isf, ',^,,', ',', 'g')
|
|
||||||
endif
|
|
||||||
|
|
||||||
for token in split(isf, ',')
|
|
||||||
let target = white
|
|
||||||
if token[0] == '^'
|
|
||||||
let target = black
|
|
||||||
let token = token[1:]
|
|
||||||
endif
|
|
||||||
|
|
||||||
let ends = matchlist(token, '\(.\+\)-\(.\+\)')
|
|
||||||
if empty(ends)
|
|
||||||
call add(target, token)
|
|
||||||
else
|
|
||||||
let ends = map(ends[1:2], "len(v:val) == 1 ? char2nr(v:val) : str2nr(v:val)")
|
|
||||||
for i in range(ends[0], ends[1])
|
|
||||||
call add(target, nr2char(i))
|
|
||||||
endfor
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
|
|
||||||
let prefix = a:str
|
|
||||||
for offset in range(1, len(a:str))
|
|
||||||
let char = a:str[len(a:str) - offset]
|
|
||||||
if (char =~ '\w' || index(white, char) >= 0) && index(black, char) < 0
|
|
||||||
continue
|
|
||||||
endif
|
|
||||||
let prefix = strpart(a:str, len(a:str) - offset + 1)
|
|
||||||
break
|
|
||||||
endfor
|
|
||||||
|
|
||||||
return prefix
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! fzf#vim#complete#path(command, ...)
|
|
||||||
let s:file_cmd = a:command
|
|
||||||
return fzf#vim#complete(s:extend({
|
|
||||||
\ 'prefix': s:function('s:fname_prefix'),
|
|
||||||
\ 'source': s:function('s:file_source'),
|
|
||||||
\ 'options': s:function('s:file_options')}, get(a:000, 0, fzf#wrap())))
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" ----------------------------------------------------------------------------
|
|
||||||
" <plug>(fzf-complete-line)
|
|
||||||
" <plug>(fzf-complete-buffer-line)
|
|
||||||
" ----------------------------------------------------------------------------
|
|
||||||
function! s:reduce_line(lines)
|
|
||||||
return join(split(a:lines[0], '\t\zs')[3:], '')
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
|
|
||||||
function! fzf#vim#complete#line(...)
|
|
||||||
let [display_bufnames, lines] = fzf#vim#_lines(0)
|
|
||||||
let nth = display_bufnames ? 4 : 3
|
|
||||||
return fzf#vim#complete(s:extend({
|
|
||||||
\ 'prefix': '^.*$',
|
|
||||||
\ 'source': lines,
|
|
||||||
\ 'options': '--tiebreak=index --ansi --nth '.nth.'.. --tabstop=1',
|
|
||||||
\ 'reducer': s:function('s:reduce_line')}, get(a:000, 0, fzf#wrap())))
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! fzf#vim#complete#buffer_line(...)
|
|
||||||
return fzf#vim#complete(s:extend({
|
|
||||||
\ 'prefix': '^.*$',
|
|
||||||
\ 'source': fzf#vim#_uniq(getline(1, '$'))}, get(a:000, 0, fzf#wrap())))
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
let &cpo = s:cpo_save
|
|
||||||
unlet s:cpo_save
|
|
||||||
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
" Copyright (c) 2024 Junegunn Choi
|
|
||||||
"
|
|
||||||
" MIT License
|
|
||||||
"
|
|
||||||
" Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
" a copy of this software and associated documentation files (the
|
|
||||||
" "Software"), to deal in the Software without restriction, including
|
|
||||||
" without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
" distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
" permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
" the following conditions:
|
|
||||||
"
|
|
||||||
" The above copyright notice and this permission notice shall be
|
|
||||||
" included in all copies or substantial portions of the Software.
|
|
||||||
"
|
|
||||||
" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
function! s:warn(message)
|
|
||||||
echohl WarningMsg
|
|
||||||
echom a:message
|
|
||||||
echohl None
|
|
||||||
return 0
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! fzf#vim#ipc#start(Callback)
|
|
||||||
if !exists('*job_start') && !exists('*jobstart')
|
|
||||||
call s:warn('job_start/jobstart function not supported')
|
|
||||||
return ''
|
|
||||||
endif
|
|
||||||
|
|
||||||
if !executable('mkfifo')
|
|
||||||
call s:warn('mkfifo is not available')
|
|
||||||
return ''
|
|
||||||
endif
|
|
||||||
|
|
||||||
call fzf#vim#ipc#stop()
|
|
||||||
|
|
||||||
let g:fzf_ipc = { 'fifo': tempname(), 'callback': a:Callback }
|
|
||||||
if !filereadable(g:fzf_ipc.fifo)
|
|
||||||
call system('mkfifo '..shellescape(g:fzf_ipc.fifo))
|
|
||||||
if v:shell_error
|
|
||||||
call s:warn('Failed to create fifo')
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
call fzf#vim#ipc#restart()
|
|
||||||
|
|
||||||
return g:fzf_ipc.fifo
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! fzf#vim#ipc#restart()
|
|
||||||
if !exists('g:fzf_ipc')
|
|
||||||
throw 'fzf#vim#ipc not started'
|
|
||||||
endif
|
|
||||||
|
|
||||||
let Callback = g:fzf_ipc.callback
|
|
||||||
if exists('*job_start')
|
|
||||||
let g:fzf_ipc.job = job_start(
|
|
||||||
\ ['cat', g:fzf_ipc.fifo],
|
|
||||||
\ {'out_cb': { _, msg -> call(Callback, [msg]) },
|
|
||||||
\ 'exit_cb': { _, status -> status == 0 ? fzf#vim#ipc#restart() : '' }}
|
|
||||||
\ )
|
|
||||||
else
|
|
||||||
let eof = ['']
|
|
||||||
let g:fzf_ipc.job = jobstart(
|
|
||||||
\ ['cat', g:fzf_ipc.fifo],
|
|
||||||
\ {'stdout_buffered': 1,
|
|
||||||
\ 'on_stdout': { j, msg, e -> msg != eof ? call(Callback, msg) : '' },
|
|
||||||
\ 'on_exit': { j, status, e -> status == 0 ? fzf#vim#ipc#restart() : '' }}
|
|
||||||
\ )
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! fzf#vim#ipc#stop()
|
|
||||||
if !exists('g:fzf_ipc')
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
|
|
||||||
let job = g:fzf_ipc.job
|
|
||||||
if exists('*job_stop')
|
|
||||||
call job_stop(job)
|
|
||||||
else
|
|
||||||
call jobstop(job)
|
|
||||||
call jobwait([job])
|
|
||||||
endif
|
|
||||||
|
|
||||||
call delete(g:fzf_ipc.fifo)
|
|
||||||
unlet g:fzf_ipc
|
|
||||||
endfunction
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
" Copyright (c) 2023 Junegunn Choi
|
|
||||||
"
|
|
||||||
" MIT License
|
|
||||||
"
|
|
||||||
" Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
" a copy of this software and associated documentation files (the
|
|
||||||
" "Software"), to deal in the Software without restriction, including
|
|
||||||
" without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
" distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
" permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
" the following conditions:
|
|
||||||
"
|
|
||||||
" The above copyright notice and this permission notice shall be
|
|
||||||
" included in all copies or substantial portions of the Software.
|
|
||||||
"
|
|
||||||
" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
function! fzf#vim#listproc#quickfix(list)
|
|
||||||
call setqflist(a:list)
|
|
||||||
copen
|
|
||||||
wincmd p
|
|
||||||
cfirst
|
|
||||||
normal! zvzz
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! fzf#vim#listproc#location(list)
|
|
||||||
call setloclist(0, a:list)
|
|
||||||
lopen
|
|
||||||
wincmd p
|
|
||||||
lfirst
|
|
||||||
normal! zvzz
|
|
||||||
endfunction
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/usr/bin/env ruby
|
|
||||||
|
|
||||||
puts 'preview.rb is deprecated. Use preview.sh instead.'
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
REVERSE="\x1b[7m"
|
|
||||||
RESET="\x1b[m"
|
|
||||||
|
|
||||||
if [[ $# -lt 1 ]]; then
|
|
||||||
echo "usage: $0 [--tag] FILENAME[:LINENO][:IGNORED]"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $1 = --tag ]]; then
|
|
||||||
shift
|
|
||||||
"$(dirname "${BASH_SOURCE[0]}")/tagpreview.sh" "$@"
|
|
||||||
exit $?
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Ignore if an empty path is given
|
|
||||||
[[ -z $1 ]] && exit
|
|
||||||
|
|
||||||
IFS=':' read -r -a INPUT <<< "$1"
|
|
||||||
FILE=${INPUT[0]}
|
|
||||||
CENTER=${INPUT[1]}
|
|
||||||
|
|
||||||
if [[ "$1" =~ ^[A-Za-z]:\\ ]]; then
|
|
||||||
FILE=$FILE:${INPUT[1]}
|
|
||||||
CENTER=${INPUT[2]}
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -n "$CENTER" && ! "$CENTER" =~ ^[0-9] ]]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
CENTER=${CENTER/[^0-9]*/}
|
|
||||||
|
|
||||||
# MS Win support
|
|
||||||
if [[ "$FILE" =~ '\' ]]; then
|
|
||||||
if [ -z "$MSWINHOME" ]; then
|
|
||||||
MSWINHOME="$HOMEDRIVE$HOMEPATH"
|
|
||||||
fi
|
|
||||||
if grep -qEi "(Microsoft|WSL)" /proc/version &> /dev/null ; then
|
|
||||||
MSWINHOME="${MSWINHOME//\\/\\\\}"
|
|
||||||
FILE="${FILE/#\~\\/$MSWINHOME\\}"
|
|
||||||
FILE=$(wslpath -u "$FILE")
|
|
||||||
elif [ -n "$MSWINHOME" ]; then
|
|
||||||
FILE="${FILE/#\~\\/$MSWINHOME\\}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
FILE="${FILE/#\~\//$HOME/}"
|
|
||||||
if [ ! -r "$FILE" ]; then
|
|
||||||
if [[ "${INPUT[0]}" != '[No Name]' ]]; then
|
|
||||||
echo "File not found ${FILE}"
|
|
||||||
fi
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$CENTER" ]; then
|
|
||||||
CENTER=0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Sometimes bat is installed as batcat.
|
|
||||||
if [[ -z "$BATCAT" ]]; then
|
|
||||||
if command -v batcat > /dev/null; then
|
|
||||||
BATCAT="batcat"
|
|
||||||
elif command -v bat > /dev/null; then
|
|
||||||
BATCAT="bat"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$FZF_PREVIEW_COMMAND" ] && [ "${BATCAT:+x}" ] && [[ ! -d "$FILE" ]] ; then
|
|
||||||
${BATCAT} --style="${BAT_STYLE:-numbers}" --color=always --pager=never \
|
|
||||||
--highlight-line=$CENTER -- "$FILE"
|
|
||||||
exit $?
|
|
||||||
fi
|
|
||||||
|
|
||||||
FILE_LENGTH=${#FILE}
|
|
||||||
MIME=$(file --dereference --mime -- "$FILE")
|
|
||||||
if [[ "${MIME:FILE_LENGTH}" =~ binary ]] && [[ ! -d "$FILE" ]]; then
|
|
||||||
echo "$MIME"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
DEFAULT_COMMAND="highlight -O ansi -l {} || coderay {} || rougify {} || cat {}"
|
|
||||||
if [[ -d "$FILE" ]]; then
|
|
||||||
DEFAULT_COMMAND="tree -C -L2 {} || ls -l --color=always {}"
|
|
||||||
fi
|
|
||||||
CMD=${FZF_PREVIEW_COMMAND:-$DEFAULT_COMMAND}
|
|
||||||
CMD=${CMD//{\}/"$(printf %q "$FILE")"}
|
|
||||||
|
|
||||||
eval "$CMD" 2> /dev/null | awk "{ \
|
|
||||||
if (NR == $CENTER) \
|
|
||||||
{ gsub(/\x1b[[0-9;]*m/, \"&$REVERSE\"); printf(\"$REVERSE%s\n$RESET\", \$0); } \
|
|
||||||
else printf(\"$RESET%s\n\", \$0); \
|
|
||||||
}"
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
REVERSE="\x1b[7m"
|
|
||||||
RESET="\x1b[m"
|
|
||||||
|
|
||||||
if [ -z "$1" ]; then
|
|
||||||
echo "usage: $0 FILENAME:TAGFILE:EXCMD"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
IFS=':' read -r FILE TAGFILE EXCMD <<< "$*"
|
|
||||||
|
|
||||||
# Complete file paths which are relative to the given tag file
|
|
||||||
if [ "${FILE:0:1}" != "/" ]; then
|
|
||||||
FILE="$(dirname "${TAGFILE}")/${FILE}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -r "$FILE" ]; then
|
|
||||||
echo "File not found ${FILE}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If users aren't using vim, they are probably using neovim
|
|
||||||
if command -v vim > /dev/null; then
|
|
||||||
VIMNAME="vim"
|
|
||||||
elif command -v nvim > /dev/null; then
|
|
||||||
VIMNAME="nvim"
|
|
||||||
else
|
|
||||||
echo "Cannot preview tag: vim or nvim unavailable"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
CENTER="$("${VIMNAME}" -R -i NONE -u NONE -e -m -s "${FILE}" \
|
|
||||||
-c "set nomagic" \
|
|
||||||
-c "silent ${EXCMD}" \
|
|
||||||
-c 'let l=line(".") | new | put =l | print | qa!')" || exit
|
|
||||||
|
|
||||||
START_LINE="$(( CENTER - FZF_PREVIEW_LINES / 2 ))"
|
|
||||||
if (( START_LINE <= 0 )); then
|
|
||||||
START_LINE=1
|
|
||||||
fi
|
|
||||||
END_LINE="$(( START_LINE + FZF_PREVIEW_LINES - 1 ))"
|
|
||||||
|
|
||||||
# Sometimes bat is installed as batcat.
|
|
||||||
if command -v batcat > /dev/null; then
|
|
||||||
BATNAME="batcat"
|
|
||||||
elif command -v bat > /dev/null; then
|
|
||||||
BATNAME="bat"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$FZF_PREVIEW_COMMAND" ] && [ "${BATNAME:+x}" ]; then
|
|
||||||
${BATNAME} --style="${BAT_STYLE:-numbers}" \
|
|
||||||
--color=always \
|
|
||||||
--pager=never \
|
|
||||||
--wrap=never \
|
|
||||||
--terminal-width="${FZF_PREVIEW_COLUMNS}" \
|
|
||||||
--line-range="${START_LINE}:${END_LINE}" \
|
|
||||||
--highlight-line="${CENTER}" \
|
|
||||||
"$FILE"
|
|
||||||
exit $?
|
|
||||||
fi
|
|
||||||
|
|
||||||
DEFAULT_COMMAND="highlight -O ansi -l {} || coderay {} || rougify {} || cat {}"
|
|
||||||
CMD=${FZF_PREVIEW_COMMAND:-$DEFAULT_COMMAND}
|
|
||||||
CMD=${CMD//{\}/$(printf %q "$FILE")}
|
|
||||||
|
|
||||||
eval "$CMD" 2> /dev/null | awk "{ \
|
|
||||||
if (NR >= $START_LINE && NR <= $END_LINE) { \
|
|
||||||
if (NR == $CENTER) \
|
|
||||||
{ gsub(/\x1b[[0-9;]*m/, \"&$REVERSE\"); printf(\"$REVERSE%s\n$RESET\", \$0); } \
|
|
||||||
else printf(\"$RESET%s\n\", \$0); \
|
|
||||||
} \
|
|
||||||
}"
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
#!/usr/bin/env perl
|
|
||||||
|
|
||||||
use strict;
|
|
||||||
|
|
||||||
my $prefix = shift @ARGV;
|
|
||||||
|
|
||||||
foreach my $file (@ARGV) {
|
|
||||||
my $lines;
|
|
||||||
if ($prefix eq "") {
|
|
||||||
open $lines, $file;
|
|
||||||
} else {
|
|
||||||
# https://perldoc.perl.org/perlopentut#Expressing-the-command-as-a-list
|
|
||||||
open $lines, '-|', 'readtags', '-t', $file, '-e', '-p', '-', $prefix;
|
|
||||||
}
|
|
||||||
while (<$lines>) {
|
|
||||||
unless (/^\!/) {
|
|
||||||
s/^[^\t]*/sprintf("%-24s", $&)/e;
|
|
||||||
s/$/\t$file/;
|
|
||||||
print;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close $lines;
|
|
||||||
}
|
|
||||||
@@ -1,545 +0,0 @@
|
|||||||
fzf-vim.txt fzf-vim Last change: June 8 2025
|
|
||||||
FZF-VIM - TABLE OF CONTENTS *fzf-vim* *fzf-vim-toc*
|
|
||||||
==============================================================================
|
|
||||||
|
|
||||||
fzf :heart: vim |fzf-vim-fzfheart-vim|
|
|
||||||
Rationale |fzf-vim-rationale|
|
|
||||||
Why you should use fzf on Vim |fzf-vim-why-you-should-use-fzf-on-vim|
|
|
||||||
Installation |fzf-vim-installation|
|
|
||||||
Using vim-plug |fzf-vim-using-vim-plug|
|
|
||||||
Dependencies |fzf-vim-dependencies|
|
|
||||||
Commands |fzf-vim-commands|
|
|
||||||
Customization |fzf-vim-customization|
|
|
||||||
Configuration options of the base plugin |fzf-vim-configuration-options-of-the-base-plugin|
|
|
||||||
Configuration options for fzf.vim |fzf-vim-configuration-options-for-fzf-vim|
|
|
||||||
Preview window |fzf-vim-preview-window|
|
|
||||||
Command-level options |fzf-vim-command-level-options|
|
|
||||||
Command-level fzf options |fzf-vim-command-level-fzf-options|
|
|
||||||
List type to handle multiple selections |fzf-vim-list-type-to-handle-multiple-selections|
|
|
||||||
Advanced customization |fzf-vim-advanced-customization|
|
|
||||||
Vim functions |fzf-vim-vim-functions|
|
|
||||||
Example: Customizing Files command |fzf-vim-example-customizing-files-command|
|
|
||||||
Example: git grep wrapper |fzf-vim-example-git-grep-wrapper|
|
|
||||||
Mappings |fzf-vim-mappings|
|
|
||||||
Completion functions |fzf-vim-completion-functions|
|
|
||||||
Custom completion |fzf-vim-custom-completion|
|
|
||||||
Reducer example |fzf-vim-reducer-example|
|
|
||||||
Status line of terminal buffer |fzf-vim-status-line-of-terminal-buffer|
|
|
||||||
Hide statusline |fzf-vim-hide-statusline|
|
|
||||||
Custom statusline |fzf-vim-custom-statusline|
|
|
||||||
License |fzf-vim-license|
|
|
||||||
|
|
||||||
FZF :HEART: VIM *fzf-vim-fzfheart-vim*
|
|
||||||
==============================================================================
|
|
||||||
|
|
||||||
Things you can do with {fzf}{1} and Vim.
|
|
||||||
|
|
||||||
{1} https://github.com/junegunn/fzf
|
|
||||||
|
|
||||||
|
|
||||||
RATIONALE *fzf-vim-rationale*
|
|
||||||
==============================================================================
|
|
||||||
|
|
||||||
{fzf}{1} itself is not a Vim plugin, and the official repository only provides
|
|
||||||
the {basic wrapper function}{2} for Vim. It's up to the users to write their
|
|
||||||
own Vim commands with it. However, I've learned that many users of fzf are not
|
|
||||||
familiar with Vimscript and are looking for the "default" implementation of
|
|
||||||
the features they can find in the alternative Vim plugins.
|
|
||||||
|
|
||||||
{1} https://github.com/junegunn/fzf
|
|
||||||
{2} https://github.com/junegunn/fzf/blob/master/README-VIM.md#fzfrun
|
|
||||||
|
|
||||||
|
|
||||||
WHY YOU SHOULD USE FZF ON VIM *fzf-vim-why-you-should-use-fzf-on-vim*
|
|
||||||
==============================================================================
|
|
||||||
|
|
||||||
Because you can and you love fzf.
|
|
||||||
|
|
||||||
fzf runs asynchronously and can be orders of magnitude faster than similar Vim
|
|
||||||
plugins. However, the benefit may not be noticeable if the size of the input
|
|
||||||
is small, which is the case for many of the commands provided here.
|
|
||||||
Nevertheless I wrote them anyway since it's really easy to implement custom
|
|
||||||
selector with fzf.
|
|
||||||
|
|
||||||
|
|
||||||
INSTALLATION *fzf-vim-installation*
|
|
||||||
==============================================================================
|
|
||||||
|
|
||||||
fzf.vim depends on the basic Vim plugin of {the main fzf repository}{1}, which
|
|
||||||
means you need to set up both "fzf" and "fzf.vim" on Vim. To learn more about
|
|
||||||
fzf/Vim integration, see {README-VIM}{3}.
|
|
||||||
|
|
||||||
{1} https://github.com/junegunn/fzf
|
|
||||||
{3} https://github.com/junegunn/fzf/blob/master/README-VIM.md
|
|
||||||
|
|
||||||
|
|
||||||
< Using vim-plug >____________________________________________________________~
|
|
||||||
*fzf-vim-using-vim-plug*
|
|
||||||
>
|
|
||||||
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
|
|
||||||
Plug 'junegunn/fzf.vim'
|
|
||||||
<
|
|
||||||
`fzf#install()` makes sure that you have the latest binary, but it's optional,
|
|
||||||
so you can omit it if you use a plugin manager that doesn't support hooks.
|
|
||||||
|
|
||||||
|
|
||||||
< Dependencies >______________________________________________________________~
|
|
||||||
*fzf-vim-dependencies*
|
|
||||||
|
|
||||||
- {fzf}{1} 0.54.0 or above
|
|
||||||
- For syntax-highlighted preview, install {bat}{4}
|
|
||||||
- If {delta}{5} is available, `GF?`, `Commits` and `BCommits` will use it to
|
|
||||||
format `git diff` output.
|
|
||||||
- `Ag` requires {The Silver Searcher (ag)}{6}
|
|
||||||
- `Rg` requires {ripgrep (rg)}{7}
|
|
||||||
- `Tags` and `Helptags` require Perl
|
|
||||||
- `Tags PREFIX` requires `readtags` command from {Universal Ctags}{8}
|
|
||||||
>
|
|
||||||
# Installing dependencies using Homebrew
|
|
||||||
brew install fzf bat ripgrep the_silver_searcher perl universal-ctags
|
|
||||||
<
|
|
||||||
{1} https://github.com/junegunn/fzf
|
|
||||||
{4} https://github.com/sharkdp/bat
|
|
||||||
{5} https://github.com/dandavison/delta
|
|
||||||
{6} https://github.com/ggreer/the_silver_searcher
|
|
||||||
{7} https://github.com/BurntSushi/ripgrep
|
|
||||||
{8} https://ctags.io/
|
|
||||||
|
|
||||||
|
|
||||||
COMMANDS *fzf-vim-commands*
|
|
||||||
==============================================================================
|
|
||||||
|
|
||||||
*:Files* *:GFiles* *:Buffers* *:Colors* *:Ag* *:Rg* *:RG* *:Lines* *:BLines* *:Tags* *:BTags*
|
|
||||||
*:Changes* *:Marks* *:BMarks* *:Jumps* *:Windows* *:Locate* *:History* *:Snippets* *:Commits*
|
|
||||||
*:BCommits* *:Commands* *:Maps* *:Helptags* *:Filetypes*
|
|
||||||
|
|
||||||
-----------------------+--------------------------------------------------------------------------------------
|
|
||||||
Command | List ~
|
|
||||||
-----------------------+--------------------------------------------------------------------------------------
|
|
||||||
`:Files [PATH]` | Files (runs `$FZF_DEFAULT_COMMAND` if defined)
|
|
||||||
`:GFiles [OPTS]` | Git files ( `git ls-files` )
|
|
||||||
`:GFiles?` | Git files ( `git status` )
|
|
||||||
`:Buffers` | Open buffers
|
|
||||||
`:Colors` | Color schemes
|
|
||||||
`:Ag [PATTERN]` | {ag}{6} search result ( `ALT-A` to select all, `ALT-D` to deselect all)
|
|
||||||
`:Rg [PATTERN]` | {rg}{7} search result ( `ALT-A` to select all, `ALT-D` to deselect all)
|
|
||||||
`:RG [PATTERN]` | {rg}{7} search result; relaunch ripgrep on every keystroke
|
|
||||||
`:Lines [QUERY]` | Lines in loaded buffers
|
|
||||||
`:BLines [QUERY]` | Lines in the current buffer
|
|
||||||
`:Tags [PREFIX]` | Tags in the project ( `ctags -R` )
|
|
||||||
`:BTags [QUERY]` | Tags in the current buffer
|
|
||||||
`:Changes` | Changelist across all open buffers
|
|
||||||
`:Marks` | Marks
|
|
||||||
`:BMarks` | Marks in the current buffer
|
|
||||||
`:Jumps` | Jumps
|
|
||||||
`:Windows` | Windows
|
|
||||||
`:Locate PATTERN` | `locate` command output
|
|
||||||
`:History` | `v:oldfiles` and open buffers
|
|
||||||
`:History:` | Command history
|
|
||||||
`:History/` | Search history
|
|
||||||
`:Snippets` | Snippets ({UltiSnips}{9})
|
|
||||||
`:Commits [LOG_OPTS]` | Git commits (requires {fugitive.vim}{10})
|
|
||||||
`:BCommits [LOG_OPTS]` | Git commits for the current buffer; visual-select lines to track changes in the range
|
|
||||||
`:Commands` | Commands
|
|
||||||
`:Maps` | Normal mode mappings
|
|
||||||
`:Helptags` | Help tags [1]
|
|
||||||
`:Filetypes` | File types
|
|
||||||
-----------------------+--------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
*g:fzf_vim.command_prefix*
|
|
||||||
|
|
||||||
- Most commands support CTRL-T / CTRL-X / CTRL-V key bindings to open in a new
|
|
||||||
tab, a new split, or in a new vertical split
|
|
||||||
- Bang-versions of the commands (e.g. `Ag!`) will open fzf in fullscreen
|
|
||||||
- You can set `g:fzf_vim.command_prefix` to give the same prefix to the commands
|
|
||||||
- e.g. `let g:fzf_vim.command_prefix = 'Fzf'` and you have `FzfFiles`, etc.
|
|
||||||
|
|
||||||
(1: `Helptags` will shadow the command of the same name from {pathogen}{11}.
|
|
||||||
But its functionality is still available via `call pathogen#helptags()`. [↩])
|
|
||||||
|
|
||||||
{6} https://github.com/ggreer/the_silver_searcher
|
|
||||||
{7} https://github.com/BurntSushi/ripgrep
|
|
||||||
{7} https://github.com/BurntSushi/ripgrep
|
|
||||||
{9} https://github.com/SirVer/ultisnips
|
|
||||||
{10} https://github.com/tpope/vim-fugitive
|
|
||||||
{11} https://github.com/tpope/vim-pathogen
|
|
||||||
|
|
||||||
|
|
||||||
CUSTOMIZATION *fzf-vim-customization*
|
|
||||||
==============================================================================
|
|
||||||
|
|
||||||
|
|
||||||
< Configuration options of the base plugin >__________________________________~
|
|
||||||
*fzf-vim-configuration-options-of-the-base-plugin*
|
|
||||||
|
|
||||||
Every command in fzf.vim internally calls `fzf#wrap` function of the main
|
|
||||||
repository which supports a set of global option variables. So please read
|
|
||||||
through {README-VIM}{3} to learn more about them.
|
|
||||||
|
|
||||||
{3} https://github.com/junegunn/fzf/blob/master/README-VIM.md
|
|
||||||
|
|
||||||
|
|
||||||
< Configuration options for fzf.vim >_________________________________________~
|
|
||||||
*fzf-vim-configuration-options-for-fzf-vim*
|
|
||||||
|
|
||||||
*g:fzf_vim*
|
|
||||||
|
|
||||||
All configuration values for this plugin are stored in `g:fzf_vim` dictionary,
|
|
||||||
so make sure to initialize it before assigning any configuration values to it.
|
|
||||||
>
|
|
||||||
" Initialize configuration dictionary
|
|
||||||
let g:fzf_vim = {}
|
|
||||||
<
|
|
||||||
|
|
||||||
Preview window~
|
|
||||||
*fzf-vim-preview-window*
|
|
||||||
|
|
||||||
*g:fzf_vim.preview_window*
|
|
||||||
|
|
||||||
Some commands will show the preview window on the right. You can customize the
|
|
||||||
behavior with `g:fzf_vim.preview_window`. Here are some examples:
|
|
||||||
|
|
||||||
*g:fzf_vim.preview_bash*
|
|
||||||
>
|
|
||||||
" This is the default option:
|
|
||||||
" - Preview window on the right with 50% width
|
|
||||||
" - CTRL-/ will toggle preview window.
|
|
||||||
" - Note that this array is passed as arguments to fzf#vim#with_preview function.
|
|
||||||
" - To learn more about preview window options, see `--preview-window` section of `man fzf`.
|
|
||||||
let g:fzf_vim.preview_window = ['right,50%', 'ctrl-/']
|
|
||||||
|
|
||||||
" Preview window is hidden by default. You can toggle it with ctrl-/.
|
|
||||||
" It will show on the right with 50% width, but if the width is smaller
|
|
||||||
" than 70 columns, it will show above the candidate list
|
|
||||||
let g:fzf_vim.preview_window = ['hidden,right,50%,<70(up,40%)', 'ctrl-/']
|
|
||||||
|
|
||||||
" Empty value to disable preview window altogether
|
|
||||||
let g:fzf_vim.preview_window = []
|
|
||||||
|
|
||||||
" fzf.vim needs bash to display the preview window.
|
|
||||||
" On Windows, fzf.vim will first see if bash is in $PATH, then if
|
|
||||||
" Git bash (C:\Program Files\Git\bin\bash.exe) is available.
|
|
||||||
" If you want it to use a different bash, set this variable.
|
|
||||||
" let g:fzf_vim = {}
|
|
||||||
" let g:fzf_vim.preview_bash = 'C:\Git\bin\bash.exe'
|
|
||||||
<
|
|
||||||
|
|
||||||
Command-level options~
|
|
||||||
*fzf-vim-command-level-options*
|
|
||||||
|
|
||||||
*g:fzf_vim.commands_expect* *g:fzf_vim.tags_command* *g:fzf_vim.commits_log_options*
|
|
||||||
*g:fzf_vim.buffers_jump*
|
|
||||||
>
|
|
||||||
" [Buffers] Jump to the existing window if possible (default: 0)
|
|
||||||
let g:fzf_vim.buffers_jump = 1
|
|
||||||
|
|
||||||
" [Ag|Rg|RG] Display path on a separate line for narrow screens (default: 0)
|
|
||||||
" * Requires Perl and fzf 0.56.0 or later
|
|
||||||
let g:fzf_vim.grep_multi_line = 0
|
|
||||||
" PATH:LINE:COL:LINE
|
|
||||||
let g:fzf_vim.grep_multi_line = 1
|
|
||||||
" PATH:LINE:COL:
|
|
||||||
" LINE
|
|
||||||
let g:fzf_vim.grep_multi_line = 2
|
|
||||||
" PATH:LINE:COL:
|
|
||||||
" LINE
|
|
||||||
" (empty line between items using --gap option)
|
|
||||||
|
|
||||||
" [[B]Commits] Customize the options used by 'git log':
|
|
||||||
let g:fzf_vim.commits_log_options = '--graph --color=always --format="%C(auto)%h%d %s %C(black)%C(bold)%cr"'
|
|
||||||
|
|
||||||
" [Tags] Command to generate tags file
|
|
||||||
let g:fzf_vim.tags_command = 'ctags -R'
|
|
||||||
|
|
||||||
" [Commands] --expect expression for directly executing the command
|
|
||||||
let g:fzf_vim.commands_expect = 'alt-enter,ctrl-x'
|
|
||||||
<
|
|
||||||
|
|
||||||
Command-level fzf options~
|
|
||||||
*fzf-vim-command-level-fzf-options*
|
|
||||||
|
|
||||||
You can set fzf options for each command by setting
|
|
||||||
`g:fzf_vim.{command}_options`.
|
|
||||||
>
|
|
||||||
" In string
|
|
||||||
let g:fzf_vim.buffers_options = '--style full --border-label " Open Buffers "'
|
|
||||||
|
|
||||||
" In list (No need to quote or escape values)
|
|
||||||
let g:fzf_vim.buffers_options = ['--style', 'full', '--border-label', ' Open Buffers ']
|
|
||||||
<
|
|
||||||
|
|
||||||
List type to handle multiple selections~
|
|
||||||
*fzf-vim-list-type-to-handle-multiple-selections*
|
|
||||||
|
|
||||||
The following commands will fill the quickfix list when multiple entries are
|
|
||||||
selected.
|
|
||||||
|
|
||||||
- `Ag`
|
|
||||||
- `Rg` / `RG`
|
|
||||||
- `Lines` / `BLines`
|
|
||||||
- `Tags` / `BTags`
|
|
||||||
|
|
||||||
*g:fzf_vim.listproc*
|
|
||||||
|
|
||||||
By setting `g:fzf_vim.listproc`, you can make them use location list instead.
|
|
||||||
>
|
|
||||||
" Default: Use quickfix list
|
|
||||||
let g:fzf_vim.listproc = { list -> fzf#vim#listproc#quickfix(list) }
|
|
||||||
|
|
||||||
" Use location list instead of quickfix list
|
|
||||||
let g:fzf_vim.listproc = { list -> fzf#vim#listproc#location(list) }
|
|
||||||
<
|
|
||||||
You can customize the list type per command by defining variables named
|
|
||||||
`g:fzf_vim.listproc_{command_name_in_lowercase}`.
|
|
||||||
|
|
||||||
*g:fzf_vim.listproc_rg* *g:fzf_vim.listproc_ag*
|
|
||||||
>
|
|
||||||
" Command-wise customization
|
|
||||||
let g:fzf_vim.listproc_ag = { list -> fzf#vim#listproc#quickfix(list) }
|
|
||||||
let g:fzf_vim.listproc_rg = { list -> fzf#vim#listproc#location(list) }
|
|
||||||
<
|
|
||||||
You can further customize the behavior by providing a custom function to
|
|
||||||
process the list instead of using the predefined `fzf#vim#listproc#quickfix`
|
|
||||||
or `fzf#vim#listproc#location`.
|
|
||||||
>
|
|
||||||
" A customized version of fzf#vim#listproc#quickfix.
|
|
||||||
" The last two lines are commented out not to move to the first entry.
|
|
||||||
function! g:fzf_vim.listproc(list)
|
|
||||||
call setqflist(a:list)
|
|
||||||
copen
|
|
||||||
wincmd p
|
|
||||||
" cfirst
|
|
||||||
" normal! zvzz
|
|
||||||
endfunction
|
|
||||||
<
|
|
||||||
|
|
||||||
< Advanced customization >____________________________________________________~
|
|
||||||
*fzf-vim-advanced-customization*
|
|
||||||
|
|
||||||
|
|
||||||
Vim functions~
|
|
||||||
*fzf-vim-vim-functions*
|
|
||||||
|
|
||||||
Each command in fzf.vim is backed by a Vim function. You can override a
|
|
||||||
command or define a variation of it by calling its corresponding function.
|
|
||||||
|
|
||||||
----------+---------------------------------------------------------------------------------
|
|
||||||
Command | Vim function ~
|
|
||||||
----------+---------------------------------------------------------------------------------
|
|
||||||
`Files` | `fzf#vim#files(dir, [spec dict], [fullscreen bool])`
|
|
||||||
`GFiles` | `fzf#vim#gitfiles(git_options, [spec dict], [fullscreen bool])`
|
|
||||||
`GFiles?` | `fzf#vim#gitfiles('?', [spec dict], [fullscreen bool])`
|
|
||||||
`Buffers` | `fzf#vim#buffers([query string], [bufnrs list], [spec dict], [fullscreen bool])`
|
|
||||||
`Colors` | `fzf#vim#colors([spec dict], [fullscreen bool])`
|
|
||||||
`Rg` | `fzf#vim#grep(command, [spec dict], [fullscreen bool])`
|
|
||||||
`RG` | `fzf#vim#grep2(command_prefix, query, [spec dict], [fullscreen bool])`
|
|
||||||
... | ...
|
|
||||||
----------+---------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
(We can see that the last two optional arguments of each function are
|
|
||||||
identical. They are directly passed to `fzf#wrap` function. If you haven't
|
|
||||||
read {README-VIM}{3} already, please read it before proceeding.)
|
|
||||||
|
|
||||||
{3} https://github.com/junegunn/fzf/blob/master/README-VIM.md
|
|
||||||
|
|
||||||
|
|
||||||
Example: Customizing Files command~
|
|
||||||
*fzf-vim-example-customizing-files-command*
|
|
||||||
|
|
||||||
This is the default definition of `Files` command:
|
|
||||||
>
|
|
||||||
command! -bang -nargs=? -complete=dir Files call fzf#vim#files(<q-args>, <bang>0)
|
|
||||||
<
|
|
||||||
Let's say you want to a variation of it called `ProjectFiles` that only
|
|
||||||
searches inside `~/projects` directory. Then you can do it like this:
|
|
||||||
>
|
|
||||||
command! -bang ProjectFiles call fzf#vim#files('~/projects', <bang>0)
|
|
||||||
<
|
|
||||||
Or, if you want to override the command with different fzf options, just pass
|
|
||||||
a custom spec to the function.
|
|
||||||
>
|
|
||||||
command! -bang -nargs=? -complete=dir Files
|
|
||||||
\ call fzf#vim#files(<q-args>, {'options': ['--layout=reverse', '--info=inline']}, <bang>0)
|
|
||||||
<
|
|
||||||
Want a preview window?
|
|
||||||
>
|
|
||||||
command! -bang -nargs=? -complete=dir Files
|
|
||||||
\ call fzf#vim#files(<q-args>, {'options': ['--layout=reverse', '--info=inline', '--preview', 'cat {}']}, <bang>0)
|
|
||||||
<
|
|
||||||
It kind of works, but you probably want a nicer previewer program than `cat`.
|
|
||||||
fzf.vim ships {a versatile preview script}{12} you can readily use. It
|
|
||||||
internally executes {bat}{4} for syntax highlighting, so make sure to install
|
|
||||||
it.
|
|
||||||
>
|
|
||||||
command! -bang -nargs=? -complete=dir Files
|
|
||||||
\ call fzf#vim#files(<q-args>, {'options': ['--layout=reverse', '--info=inline', '--preview', '~/.vim/plugged/fzf.vim/bin/preview.sh {}']}, <bang>0)
|
|
||||||
<
|
|
||||||
However, it's not ideal to hard-code the path to the script which can be
|
|
||||||
different in different circumstances. So in order to make it easier to set up
|
|
||||||
the previewer, fzf.vim provides `fzf#vim#with_preview` helper function.
|
|
||||||
Similarly to `fzf#wrap`, it takes a spec dictionary and returns a copy of it
|
|
||||||
with additional preview options.
|
|
||||||
>
|
|
||||||
command! -bang -nargs=? -complete=dir Files
|
|
||||||
\ call fzf#vim#files(<q-args>, fzf#vim#with_preview({'options': ['--layout=reverse', '--info=inline']}), <bang>0)
|
|
||||||
<
|
|
||||||
You can just omit the spec argument if you only want the previewer.
|
|
||||||
>
|
|
||||||
command! -bang -nargs=? -complete=dir Files
|
|
||||||
\ call fzf#vim#files(<q-args>, fzf#vim#with_preview(), <bang>0)
|
|
||||||
<
|
|
||||||
{12} bin/preview.sh
|
|
||||||
{4} https://github.com/sharkdp/bat
|
|
||||||
|
|
||||||
|
|
||||||
Example: git grep wrapper~
|
|
||||||
*fzf-vim-example-git-grep-wrapper*
|
|
||||||
|
|
||||||
The following example implements `GGrep` command that works similarly to
|
|
||||||
predefined `Ag` or `Rg` using `fzf#vim#grep`.
|
|
||||||
|
|
||||||
- We set the base directory to git root by setting `dir` attribute in spec
|
|
||||||
dictionary.
|
|
||||||
- {The preview script}{12} supports `grep` format (`FILE_PATH:LINE_NO:...`), so
|
|
||||||
we can just wrap the spec with `fzf#vim#with_preview` as before to enable
|
|
||||||
previewer.
|
|
||||||
>
|
|
||||||
command! -bang -nargs=* GGrep
|
|
||||||
\ call fzf#vim#grep(
|
|
||||||
\ 'git grep --line-number -- '.fzf#shellescape(<q-args>),
|
|
||||||
\ fzf#vim#with_preview({'dir': systemlist('git rev-parse --show-toplevel')[0]}), <bang>0)
|
|
||||||
<
|
|
||||||
{12} bin/preview.sh
|
|
||||||
|
|
||||||
|
|
||||||
MAPPINGS *fzf-vim-mappings*
|
|
||||||
==============================================================================
|
|
||||||
|
|
||||||
---------------------------------+------------------------------------------
|
|
||||||
Mapping | Description ~
|
|
||||||
---------------------------------+------------------------------------------
|
|
||||||
<plug>(fzf-maps-n) | Normal mode mappings
|
|
||||||
<plug>(fzf-maps-i) | Insert mode mappings
|
|
||||||
<plug>(fzf-maps-x) | Visual mode mappings
|
|
||||||
<plug>(fzf-maps-o) | Operator-pending mappings
|
|
||||||
<plug>(fzf-complete-word) | `cat /usr/share/dict/words`
|
|
||||||
<plug>(fzf-complete-path) | Path completion using `find` (file + dir)
|
|
||||||
<plug>(fzf-complete-file) | File completion using `find`
|
|
||||||
<plug>(fzf-complete-line) | Line completion (all open buffers)
|
|
||||||
<plug>(fzf-complete-buffer-line) | Line completion (current buffer only)
|
|
||||||
---------------------------------+------------------------------------------
|
|
||||||
>
|
|
||||||
" Mapping selecting mappings
|
|
||||||
nmap <leader><tab> <plug>(fzf-maps-n)
|
|
||||||
xmap <leader><tab> <plug>(fzf-maps-x)
|
|
||||||
omap <leader><tab> <plug>(fzf-maps-o)
|
|
||||||
|
|
||||||
" Insert mode completion
|
|
||||||
imap <c-x><c-k> <plug>(fzf-complete-word)
|
|
||||||
imap <c-x><c-f> <plug>(fzf-complete-path)
|
|
||||||
imap <c-x><c-l> <plug>(fzf-complete-line)
|
|
||||||
<
|
|
||||||
|
|
||||||
COMPLETION FUNCTIONS *fzf-vim-completion-functions*
|
|
||||||
==============================================================================
|
|
||||||
|
|
||||||
-----------------------------------------+--------------------------------------
|
|
||||||
Function | Description ~
|
|
||||||
-----------------------------------------+--------------------------------------
|
|
||||||
`fzf#vim#complete#path(command, [spec])` | Path completion
|
|
||||||
`fzf#vim#complete#word([spec])` | Word completion
|
|
||||||
`fzf#vim#complete#line([spec])` | Line completion (all open buffers)
|
|
||||||
`fzf#vim#complete#buffer_line([spec])` | Line completion (current buffer only)
|
|
||||||
-----------------------------------------+--------------------------------------
|
|
||||||
>
|
|
||||||
" Path completion with custom source command
|
|
||||||
inoremap <expr> <c-x><c-f> fzf#vim#complete#path('fd')
|
|
||||||
inoremap <expr> <c-x><c-f> fzf#vim#complete#path('rg --files')
|
|
||||||
|
|
||||||
" Word completion with custom spec with popup layout option
|
|
||||||
inoremap <expr> <c-x><c-k> fzf#vim#complete#word({'window': { 'width': 0.2, 'height': 0.9, 'xoffset': 1 }})
|
|
||||||
<
|
|
||||||
|
|
||||||
CUSTOM COMPLETION *fzf-vim-custom-completion*
|
|
||||||
==============================================================================
|
|
||||||
|
|
||||||
`fzf#vim#complete` is a helper function for creating custom fuzzy completion
|
|
||||||
using fzf. If the first parameter is a command string or a Vim list, it will
|
|
||||||
be used as the source.
|
|
||||||
>
|
|
||||||
" Replace the default dictionary completion with fzf-based fuzzy completion
|
|
||||||
inoremap <expr> <c-x><c-k> fzf#vim#complete('cat /usr/share/dict/words')
|
|
||||||
<
|
|
||||||
For advanced uses, you can pass an options dictionary to the function. The set
|
|
||||||
of options is pretty much identical to that for `fzf#run` only with the
|
|
||||||
following exceptions:
|
|
||||||
|
|
||||||
- `reducer` (funcref)
|
|
||||||
- Reducer transforms the output lines of fzf into a single string value
|
|
||||||
- `prefix` (string or funcref; default: `\k*$`)
|
|
||||||
- Regular expression pattern to extract the completion prefix
|
|
||||||
- Or a function to extract completion prefix
|
|
||||||
- Both `source` and `options` can be given as funcrefs that take the completion
|
|
||||||
prefix as the argument and return the final value
|
|
||||||
- `sink` or `sink*` are ignored
|
|
||||||
>
|
|
||||||
" Global line completion (not just open buffers. ripgrep required.)
|
|
||||||
inoremap <expr> <c-x><c-l> fzf#vim#complete(fzf#wrap({
|
|
||||||
\ 'prefix': '^.*$',
|
|
||||||
\ 'source': 'rg -n ^ --color always',
|
|
||||||
\ 'options': '--ansi --delimiter : --nth 3..',
|
|
||||||
\ 'reducer': { lines -> join(split(lines[0], ':\zs')[2:], '') }}))
|
|
||||||
<
|
|
||||||
|
|
||||||
< Reducer example >___________________________________________________________~
|
|
||||||
*fzf-vim-reducer-example*
|
|
||||||
>
|
|
||||||
function! s:make_sentence(lines)
|
|
||||||
return substitute(join(a:lines), '^.', '\=toupper(submatch(0))', '').'.'
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
inoremap <expr> <c-x><c-s> fzf#vim#complete({
|
|
||||||
\ 'source': 'cat /usr/share/dict/words',
|
|
||||||
\ 'reducer': function('<sid>make_sentence'),
|
|
||||||
\ 'options': '--multi --reverse --margin 15%,0',
|
|
||||||
\ 'left': 20})
|
|
||||||
<
|
|
||||||
|
|
||||||
STATUS LINE OF TERMINAL BUFFER *fzf-vim-status-line-of-terminal-buffer*
|
|
||||||
==============================================================================
|
|
||||||
|
|
||||||
When fzf starts in a terminal buffer (see {fzf/README-VIM.md}{13}), you may
|
|
||||||
want to customize the statusline of the containing buffer.
|
|
||||||
|
|
||||||
{13} https://github.com/junegunn/fzf/blob/master/README-VIM.md#fzf-inside-terminal-buffer
|
|
||||||
|
|
||||||
|
|
||||||
< Hide statusline >___________________________________________________________~
|
|
||||||
*fzf-vim-hide-statusline*
|
|
||||||
>
|
|
||||||
autocmd! FileType fzf set laststatus=0 noshowmode noruler
|
|
||||||
\| autocmd BufLeave <buffer> set laststatus=2 showmode ruler
|
|
||||||
<
|
|
||||||
|
|
||||||
< Custom statusline >_________________________________________________________~
|
|
||||||
*fzf-vim-custom-statusline*
|
|
||||||
>
|
|
||||||
function! s:fzf_statusline()
|
|
||||||
" Override statusline as you like
|
|
||||||
highlight fzf1 ctermfg=161 ctermbg=251
|
|
||||||
highlight fzf2 ctermfg=23 ctermbg=251
|
|
||||||
highlight fzf3 ctermfg=237 ctermbg=251
|
|
||||||
setlocal statusline=%#fzf1#\ >\ %#fzf2#fz%#fzf3#f
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
autocmd! User FzfStatusLine call <SID>fzf_statusline()
|
|
||||||
<
|
|
||||||
|
|
||||||
LICENSE *fzf-vim-license*
|
|
||||||
==============================================================================
|
|
||||||
|
|
||||||
MIT
|
|
||||||
|
|
||||||
|
|
||||||
==============================================================================
|
|
||||||
vim:tw=78:sw=2:ts=2:ft=help:norl:nowrap:
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
" Copyright (c) 2015 Junegunn Choi
|
|
||||||
"
|
|
||||||
" MIT License
|
|
||||||
"
|
|
||||||
" Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
" a copy of this software and associated documentation files (the
|
|
||||||
" "Software"), to deal in the Software without restriction, including
|
|
||||||
" without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
" distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
" permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
" the following conditions:
|
|
||||||
"
|
|
||||||
" The above copyright notice and this permission notice shall be
|
|
||||||
" included in all copies or substantial portions of the Software.
|
|
||||||
"
|
|
||||||
" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
" EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
" NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
" LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
if exists('g:loaded_fzf_vim')
|
|
||||||
finish
|
|
||||||
endif
|
|
||||||
let g:loaded_fzf_vim = 1
|
|
||||||
|
|
||||||
let s:cpo_save = &cpo
|
|
||||||
set cpo&vim
|
|
||||||
let s:is_win = has('win32') || has('win64')
|
|
||||||
|
|
||||||
function! s:conf(name, default)
|
|
||||||
let conf = get(g:, 'fzf_vim', {})
|
|
||||||
let val = get(conf, a:name, get(g:, 'fzf_' . a:name, a:default))
|
|
||||||
return val
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:defs(commands)
|
|
||||||
let prefix = s:conf('command_prefix', '')
|
|
||||||
if prefix =~# '^[^A-Z]'
|
|
||||||
echoerr 'g:fzf_command_prefix must start with an uppercase letter'
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
for command in a:commands
|
|
||||||
let name = ':'.prefix.matchstr(command, '\C[A-Z]\S\+')
|
|
||||||
if 2 != exists(name)
|
|
||||||
execute substitute(command, '\ze\C[A-Z]', prefix, '')
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
call s:defs([
|
|
||||||
\'command! -bang -nargs=? -complete=dir Files call fzf#vim#files(<q-args>, fzf#vim#with_preview(), <bang>0)',
|
|
||||||
\'command! -bang -nargs=? GitFiles call fzf#vim#gitfiles(<q-args>, fzf#vim#with_preview(<q-args> == "?" ? { "placeholder": "" } : {}), <bang>0)',
|
|
||||||
\'command! -bang -nargs=? GFiles call fzf#vim#gitfiles(<q-args>, fzf#vim#with_preview(<q-args> == "?" ? { "placeholder": "" } : {}), <bang>0)',
|
|
||||||
\'command! -bar -bang -nargs=? -complete=buffer Buffers call fzf#vim#buffers(<q-args>, fzf#vim#with_preview({ "placeholder": "{1}" }), <bang>0)',
|
|
||||||
\'command! -bang -nargs=* Lines call fzf#vim#lines(<q-args>, <bang>0)',
|
|
||||||
\'command! -bang -nargs=* BLines call fzf#vim#buffer_lines(<q-args>, <bang>0)',
|
|
||||||
\'command! -bar -bang Colors call fzf#vim#colors(<bang>0)',
|
|
||||||
\'command! -bang -nargs=+ -complete=dir Locate call fzf#vim#locate(<q-args>, fzf#vim#with_preview(), <bang>0)',
|
|
||||||
\'command! -bang -nargs=* Ag call fzf#vim#ag(<q-args>, fzf#vim#with_preview(), <bang>0)',
|
|
||||||
\'command! -bang -nargs=* Rg call fzf#vim#grep("rg --column --line-number --no-heading --color=always --smart-case -- ".fzf#shellescape(<q-args>), fzf#vim#with_preview(), <bang>0)',
|
|
||||||
\'command! -bang -nargs=* RG call fzf#vim#grep2("rg --column --line-number --no-heading --color=always --smart-case -- ", <q-args>, fzf#vim#with_preview(), <bang>0)',
|
|
||||||
\'command! -bang -nargs=* Tags call fzf#vim#tags(<q-args>, fzf#vim#with_preview({ "placeholder": "--tag {2}:{-1}:{3..}" }), <bang>0)',
|
|
||||||
\'command! -bang -nargs=* BTags call fzf#vim#buffer_tags(<q-args>, fzf#vim#with_preview({ "placeholder": "{2}:{3..}" }), <bang>0)',
|
|
||||||
\'command! -bar -bang Snippets call fzf#vim#snippets(<bang>0)',
|
|
||||||
\'command! -bar -bang Commands call fzf#vim#commands(<bang>0)',
|
|
||||||
\'command! -bar -bang Jumps call fzf#vim#jumps(fzf#vim#with_preview({ "placeholder": "{2..4}"}), <bang>0)',
|
|
||||||
\'command! -bar -bang -nargs=* Marks call fzf#vim#marks(<q-args>, <bang>0)',
|
|
||||||
\'command! -bar -bang -nargs=* BMarks call fzf#vim#marks("abcdefghijklmnopqrstuvwxyz", <bang>0)',
|
|
||||||
\'command! -bar -bang Changes call fzf#vim#changes(<bang>0)',
|
|
||||||
\'command! -bar -bang Helptags call fzf#vim#helptags(fzf#vim#with_preview({ "placeholder": "--tag {2}:{3}:{4}" }), <bang>0)',
|
|
||||||
\'command! -bar -bang Windows call fzf#vim#windows(fzf#vim#with_preview({ "placeholder": "{2}" }), <bang>0)',
|
|
||||||
\'command! -bar -bang -nargs=* -range=% -complete=file Commits let b:fzf_winview = winsaveview() | <line1>,<line2>call fzf#vim#commits(<q-args>, fzf#vim#with_preview({ "placeholder": "" }), <bang>0)',
|
|
||||||
\'command! -bar -bang -nargs=* -range=% BCommits let b:fzf_winview = winsaveview() | <line1>,<line2>call fzf#vim#buffer_commits(<q-args>, fzf#vim#with_preview({ "placeholder": "" }), <bang>0)',
|
|
||||||
\'command! -bar -bang Maps call fzf#vim#maps("n", <bang>0)',
|
|
||||||
\'command! -bar -bang Filetypes call fzf#vim#filetypes(<bang>0)',
|
|
||||||
\'command! -bang -nargs=* History call s:history(<q-args>, fzf#vim#with_preview(), <bang>0)'])
|
|
||||||
|
|
||||||
function! s:history(arg, extra, bang)
|
|
||||||
let bang = a:bang || a:arg[len(a:arg)-1] == '!'
|
|
||||||
if a:arg[0] == ':'
|
|
||||||
call fzf#vim#command_history(bang)
|
|
||||||
elseif a:arg[0] == '/'
|
|
||||||
call fzf#vim#search_history(bang)
|
|
||||||
else
|
|
||||||
call fzf#vim#history(a:extra, bang)
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! fzf#complete(...)
|
|
||||||
return call('fzf#vim#complete', a:000)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
if (has('nvim') || has('terminal') && has('patch-8.0.995')) && (s:conf('statusline', 1) || s:conf('nvim_statusline', 1))
|
|
||||||
function! s:fzf_restore_colors()
|
|
||||||
if exists('#User#FzfStatusLine')
|
|
||||||
doautocmd User FzfStatusLine
|
|
||||||
else
|
|
||||||
if $TERM !~ "256color"
|
|
||||||
highlight default fzf1 ctermfg=1 ctermbg=8 guifg=#E12672 guibg=#565656
|
|
||||||
highlight default fzf2 ctermfg=2 ctermbg=8 guifg=#BCDDBD guibg=#565656
|
|
||||||
highlight default fzf3 ctermfg=7 ctermbg=8 guifg=#D9D9D9 guibg=#565656
|
|
||||||
else
|
|
||||||
highlight default fzf1 ctermfg=161 ctermbg=238 guifg=#E12672 guibg=#565656
|
|
||||||
highlight default fzf2 ctermfg=151 ctermbg=238 guifg=#BCDDBD guibg=#565656
|
|
||||||
highlight default fzf3 ctermfg=252 ctermbg=238 guifg=#D9D9D9 guibg=#565656
|
|
||||||
endif
|
|
||||||
setlocal statusline=%#fzf1#\ >\ %#fzf2#fz%#fzf3#f
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:fzf_vim_term()
|
|
||||||
if get(w:, 'airline_active', 0)
|
|
||||||
let w:airline_disabled = 1
|
|
||||||
autocmd BufWinLeave <buffer> let w:airline_disabled = 0
|
|
||||||
endif
|
|
||||||
autocmd WinEnter,ColorScheme <buffer> call s:fzf_restore_colors()
|
|
||||||
|
|
||||||
setlocal nospell
|
|
||||||
call s:fzf_restore_colors()
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
augroup _fzf_statusline
|
|
||||||
autocmd!
|
|
||||||
autocmd FileType fzf call s:fzf_vim_term()
|
|
||||||
augroup END
|
|
||||||
endif
|
|
||||||
|
|
||||||
if !exists('g:fzf#vim#buffers')
|
|
||||||
let g:fzf#vim#buffers = {}
|
|
||||||
endif
|
|
||||||
|
|
||||||
augroup fzf_buffers
|
|
||||||
autocmd!
|
|
||||||
if exists('*reltimefloat')
|
|
||||||
autocmd BufWinEnter,WinEnter * let g:fzf#vim#buffers[bufnr('')] = reltimefloat(reltime())
|
|
||||||
else
|
|
||||||
autocmd BufWinEnter,WinEnter * let g:fzf#vim#buffers[bufnr('')] = localtime()
|
|
||||||
endif
|
|
||||||
autocmd BufDelete * silent! call remove(g:fzf#vim#buffers, expand('<abuf>'))
|
|
||||||
augroup END
|
|
||||||
|
|
||||||
inoremap <expr> <plug>(fzf-complete-word) fzf#vim#complete#word()
|
|
||||||
if s:is_win
|
|
||||||
inoremap <expr> <plug>(fzf-complete-path) fzf#vim#complete#path('dir /s/b')
|
|
||||||
inoremap <expr> <plug>(fzf-complete-file) fzf#vim#complete#path('dir /s/b/a:-d')
|
|
||||||
else
|
|
||||||
inoremap <expr> <plug>(fzf-complete-path) fzf#vim#complete#path("find . -path '*/\.*' -prune -o -print \| sed '1d;s:^..::'")
|
|
||||||
inoremap <expr> <plug>(fzf-complete-file) fzf#vim#complete#path("find . -path '*/\.*' -prune -o -type f -print -o -type l -print \| sed 's:^..::'")
|
|
||||||
endif
|
|
||||||
inoremap <expr> <plug>(fzf-complete-file-ag) fzf#vim#complete#path('ag -l -g ""')
|
|
||||||
inoremap <expr> <plug>(fzf-complete-line) fzf#vim#complete#line()
|
|
||||||
inoremap <expr> <plug>(fzf-complete-buffer-line) fzf#vim#complete#buffer_line()
|
|
||||||
|
|
||||||
nnoremap <silent> <plug>(fzf-maps-n) :<c-u>call fzf#vim#maps('n', 0)<cr>
|
|
||||||
inoremap <silent> <plug>(fzf-maps-i) <c-o>:call fzf#vim#maps('i', 0)<cr>
|
|
||||||
xnoremap <silent> <plug>(fzf-maps-x) :<c-u>call fzf#vim#maps('x', 0)<cr>
|
|
||||||
onoremap <silent> <plug>(fzf-maps-o) <c-c>:<c-u>call fzf#vim#maps('o', 0)<cr>
|
|
||||||
|
|
||||||
let &cpo = s:cpo_save
|
|
||||||
unlet s:cpo_save
|
|
||||||
|
|
||||||
Submodule vim/.config/vim/pack/plugins/start/lsp deleted from e99ec151da
Submodule vim/.vim/pack/plugins/start/copilot.vim deleted from 206011a8bc
Submodule vim/.vim/pack/plugins/start/fzf.vim deleted from ddc14a6a54
Reference in New Issue
Block a user