Update 2024-11-23 16:01 OpenBSD/amd64-t14
This commit is contained in:
parent
381dec3be2
commit
ed33e478fd
@ -1,4 +1,4 @@
|
||||
# Copilot.vim
|
||||
# GitHub Copilot for Vim and Neovim
|
||||
|
||||
GitHub Copilot uses OpenAI Codex to suggest code and entire functions in
|
||||
real-time right from your editor. Trained on billions of lines of public
|
||||
@ -48,7 +48,7 @@ Terms](https://docs.github.com/en/site-policy/github-terms/github-terms-for-addi
|
||||
git clone https://github.com/github/copilot.vim.git `
|
||||
$HOME/AppData/Local/nvim/pack/github/start/copilot.vim
|
||||
|
||||
4. Start Neovim and invoke `:Copilot setup`.
|
||||
4. Start Vim/Neovim and invoke `:Copilot setup`.
|
||||
|
||||
[Node.js]: https://nodejs.org/en/download/
|
||||
[Neovim]: https://github.com/neovim/neovim/releases/latest
|
||||
|
@ -1,6 +1,6 @@
|
||||
scriptencoding utf-8
|
||||
|
||||
let s:has_nvim_ghost_text = has('nvim-0.6') && exists('*nvim_buf_get_mark')
|
||||
let s:has_nvim_ghost_text = has('nvim-0.7') && exists('*nvim_buf_get_mark')
|
||||
let s:vim_minimum_version = '9.0.0185'
|
||||
let s:has_vim_ghost_text = has('patch-' . s:vim_minimum_version) && has('textprop')
|
||||
let s:has_ghost_text = s:has_nvim_ghost_text || s:has_vim_ghost_text
|
||||
@ -34,81 +34,68 @@ function! s:EditorConfiguration() abort
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! s:StatusNotification(params, ...) abort
|
||||
let status = get(a:params, 'status', '')
|
||||
if status ==? 'error'
|
||||
let s:agent_error = a:params.message
|
||||
else
|
||||
unlet! s:agent_error
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#Init(...) abort
|
||||
call timer_start(0, { _ -> s:Start() })
|
||||
call copilot#util#Defer({ -> exists('s:client') || s:Start() })
|
||||
endfunction
|
||||
|
||||
function! s:Running() abort
|
||||
return exists('s:agent.job') || exists('s:agent.client_id')
|
||||
return exists('s:client.job') || exists('s:client.client_id')
|
||||
endfunction
|
||||
|
||||
function! s:Start() abort
|
||||
if s:Running()
|
||||
if s:Running() || exists('s:client.startup_error')
|
||||
return
|
||||
endif
|
||||
let s:agent = copilot#agent#New({'methods': {
|
||||
\ 'statusNotification': function('s:StatusNotification'),
|
||||
\ 'PanelSolution': function('copilot#panel#Solution'),
|
||||
\ 'PanelSolutionsDone': function('copilot#panel#SolutionsDone'),
|
||||
\ 'copilot/openURL': function('s:OpenURL'),
|
||||
\ },
|
||||
\ 'editorConfiguration' : s:EditorConfiguration()})
|
||||
let s:client = copilot#client#New({'editorConfiguration' : s:EditorConfiguration()})
|
||||
endfunction
|
||||
|
||||
function! s:Stop() abort
|
||||
if exists('s:agent')
|
||||
let agent = remove(s:, 'agent')
|
||||
call agent.Close()
|
||||
if exists('s:client')
|
||||
let client = remove(s:, 'client')
|
||||
call client.Close()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#Agent() abort
|
||||
function! copilot#Client() abort
|
||||
call s:Start()
|
||||
return s:agent
|
||||
return s:client
|
||||
endfunction
|
||||
|
||||
function! copilot#RunningAgent() abort
|
||||
function! copilot#RunningClient() abort
|
||||
if s:Running()
|
||||
return s:agent
|
||||
return s:client
|
||||
else
|
||||
return v:null
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:NodeVersionWarning() abort
|
||||
if exists('s:agent.node_version') && s:agent.node_version =~# '^16\.'
|
||||
if has('nvim-0.7') && !has(luaeval('vim.version().api_prerelease') ? 'nvim-0.8.1' : 'nvim-0.8.0')
|
||||
let s:editor_warning = 'Neovim 0.7 support is deprecated and will be dropped in a future release of copilot.vim.'
|
||||
endif
|
||||
if has('vim_starting') && exists('s:editor_warning')
|
||||
call copilot#logger#Warn(s:editor_warning)
|
||||
endif
|
||||
function! s:EditorVersionWarning() abort
|
||||
if exists('s:editor_warning')
|
||||
echohl WarningMsg
|
||||
echo "Warning: Node.js 16 is approaching end of life and support will be dropped in a future release of copilot.vim."
|
||||
echohl NONE
|
||||
elseif exists('s:agent.node_version_warning')
|
||||
echohl WarningMsg
|
||||
echo 'Warning:' s:agent.node_version_warning
|
||||
echohl NONE
|
||||
echo 'Warning: ' . s:editor_warning
|
||||
echohl None
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#Request(method, params, ...) abort
|
||||
let agent = copilot#Agent()
|
||||
return call(agent.Request, [a:method, a:params] + a:000)
|
||||
let client = copilot#Client()
|
||||
return call(client.Request, [a:method, a:params] + a:000)
|
||||
endfunction
|
||||
|
||||
function! copilot#Call(method, params, ...) abort
|
||||
let agent = copilot#Agent()
|
||||
return call(agent.Call, [a:method, a:params] + a:000)
|
||||
let client = copilot#Client()
|
||||
return call(client.Call, [a:method, a:params] + a:000)
|
||||
endfunction
|
||||
|
||||
function! copilot#Notify(method, params, ...) abort
|
||||
let agent = copilot#Agent()
|
||||
return call(agent.Notify, [a:method, a:params] + a:000)
|
||||
let client = copilot#Client()
|
||||
return call(client.Notify, [a:method, a:params] + a:000)
|
||||
endfunction
|
||||
|
||||
function! copilot#NvimNs() abort
|
||||
@ -120,37 +107,21 @@ function! copilot#Clear() abort
|
||||
call timer_stop(remove(g:, '_copilot_timer'))
|
||||
endif
|
||||
if exists('b:_copilot')
|
||||
call copilot#agent#Cancel(get(b:_copilot, 'first', {}))
|
||||
call copilot#agent#Cancel(get(b:_copilot, 'cycling', {}))
|
||||
call copilot#client#Cancel(get(b:_copilot, 'first', {}))
|
||||
call copilot#client#Cancel(get(b:_copilot, 'cycling', {}))
|
||||
endif
|
||||
call s:UpdatePreview()
|
||||
unlet! b:_copilot
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
function! s:Reject(bufnr) abort
|
||||
try
|
||||
let dict = getbufvar(a:bufnr, '_copilot')
|
||||
if type(dict) == v:t_dict && !empty(get(dict, 'shown_choices', {}))
|
||||
call copilot#Request('notifyRejected', {'uuids': keys(dict.shown_choices)})
|
||||
let dict.shown_choices = {}
|
||||
endif
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! copilot#Dismiss() abort
|
||||
call s:Reject('%')
|
||||
call copilot#Clear()
|
||||
call s:UpdatePreview()
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
let s:filetype_defaults = {
|
||||
\ 'yaml': 0,
|
||||
\ 'markdown': 0,
|
||||
\ 'help': 0,
|
||||
\ 'gitcommit': 0,
|
||||
\ 'gitrebase': 0,
|
||||
\ 'hgcommit': 0,
|
||||
@ -187,26 +158,41 @@ endfunction
|
||||
function! copilot#Enabled() abort
|
||||
return get(g:, 'copilot_enabled', 1)
|
||||
\ && empty(s:BufferDisabled())
|
||||
\ && empty(copilot#Agent().StartupError())
|
||||
endfunction
|
||||
|
||||
let s:inline_invoked = 1
|
||||
let s:inline_automatic = 2
|
||||
|
||||
function! copilot#Complete(...) abort
|
||||
if exists('g:_copilot_timer')
|
||||
call timer_stop(remove(g:, '_copilot_timer'))
|
||||
endif
|
||||
let params = copilot#doc#Params()
|
||||
if !exists('b:_copilot.params') || b:_copilot.params !=# params
|
||||
let b:_copilot = {'params': params, 'first':
|
||||
\ copilot#Request('getCompletions', params)}
|
||||
let target = [bufnr(''), getbufvar('', 'changedtick'), line('.'), col('.')]
|
||||
if !exists('b:_copilot.target') || b:_copilot.target !=# target
|
||||
if exists('b:_copilot.first')
|
||||
call copilot#client#Cancel(b:_copilot.first)
|
||||
endif
|
||||
if exists('b:_copilot.cycling')
|
||||
call copilot#client#Cancel(b:_copilot.cycling)
|
||||
endif
|
||||
let params = {
|
||||
\ 'textDocument': {'uri': bufnr('')},
|
||||
\ 'position': copilot#util#AppendPosition(),
|
||||
\ 'formattingOptions': {'insertSpaces': &expandtab ? v:true : v:false, 'tabSize': shiftwidth()},
|
||||
\ 'context': {'triggerKind': s:inline_automatic}}
|
||||
let b:_copilot = {
|
||||
\ 'target': target,
|
||||
\ 'params': params,
|
||||
\ 'first': copilot#Request('textDocument/inlineCompletion', params)}
|
||||
let g:_copilot_last = b:_copilot
|
||||
endif
|
||||
let completion = b:_copilot.first
|
||||
if !a:0
|
||||
return completion.Await()
|
||||
else
|
||||
call copilot#agent#Result(completion, a:1)
|
||||
call copilot#client#Result(completion, function(a:1, [b:_copilot]))
|
||||
if a:0 > 1
|
||||
call copilot#agent#Error(completion, a:2)
|
||||
call copilot#client#Error(completion, function(a:2, [b:_copilot]))
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
@ -216,37 +202,37 @@ function! s:HideDuringCompletion() abort
|
||||
endfunction
|
||||
|
||||
function! s:SuggestionTextWithAdjustments() abort
|
||||
let empty = ['', 0, 0, {}]
|
||||
try
|
||||
if mode() !~# '^[iR]' || (s:HideDuringCompletion() && pumvisible()) || !exists('b:_copilot.suggestions')
|
||||
return ['', 0, 0, '']
|
||||
return empty
|
||||
endif
|
||||
let choice = get(b:_copilot.suggestions, b:_copilot.choice, {})
|
||||
if !has_key(choice, 'range') || choice.range.start.line != line('.') - 1 || type(choice.text) !=# v:t_string
|
||||
return ['', 0, 0, '']
|
||||
if !has_key(choice, 'range') || choice.range.start.line != line('.') - 1 || type(choice.insertText) !=# v:t_string
|
||||
return empty
|
||||
endif
|
||||
let line = getline('.')
|
||||
let offset = col('.') - 1
|
||||
let choice_text = strpart(line, 0, copilot#doc#UTF16ToByteIdx(line, choice.range.start.character)) . choice.text
|
||||
let choice_text = strpart(line, 0, copilot#util#UTF16ToByteIdx(line, choice.range.start.character)) . substitute(choice.insertText, "\n*$", '', '')
|
||||
let typed = strpart(line, 0, offset)
|
||||
let end_offset = copilot#doc#UTF16ToByteIdx(line, choice.range.end.character)
|
||||
let end_offset = copilot#util#UTF16ToByteIdx(line, choice.range.end.character)
|
||||
if end_offset < 0
|
||||
let end_offset = len(line)
|
||||
endif
|
||||
let delete = strpart(line, offset, end_offset - offset)
|
||||
let uuid = get(choice, 'uuid', '')
|
||||
if typed =~# '^\s*$'
|
||||
let leading = matchstr(choice_text, '^\s\+')
|
||||
let unindented = strpart(choice_text, len(leading))
|
||||
if strpart(typed, 0, len(leading)) == leading && unindented !=# delete
|
||||
return [unindented, len(typed) - len(leading), strchars(delete), uuid]
|
||||
return [unindented, len(typed) - len(leading), strchars(delete), choice]
|
||||
endif
|
||||
elseif typed ==# strpart(choice_text, 0, offset)
|
||||
return [strpart(choice_text, offset), 0, strchars(delete), uuid]
|
||||
return [strpart(choice_text, offset), 0, strchars(delete), choice]
|
||||
endif
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
endtry
|
||||
return ['', 0, 0, '']
|
||||
return empty
|
||||
endfunction
|
||||
|
||||
|
||||
@ -266,12 +252,12 @@ function! s:GetSuggestionsCyclingCallback(context, result) abort
|
||||
let callbacks = remove(a:context, 'cycling_callbacks')
|
||||
let seen = {}
|
||||
for suggestion in a:context.suggestions
|
||||
let seen[suggestion.text] = 1
|
||||
let seen[suggestion.insertText] = 1
|
||||
endfor
|
||||
for suggestion in get(a:result, 'completions', [])
|
||||
if !has_key(seen, suggestion.text)
|
||||
for suggestion in get(a:result, 'items', [])
|
||||
if !has_key(seen, suggestion.insertText)
|
||||
call add(a:context.suggestions, suggestion)
|
||||
let seen[suggestion.text] = 1
|
||||
let seen[suggestion.insertText] = 1
|
||||
endif
|
||||
endfor
|
||||
for Callback in callbacks
|
||||
@ -285,9 +271,11 @@ function! s:GetSuggestionsCycling(callback) abort
|
||||
elseif exists('b:_copilot.cycling')
|
||||
call a:callback(b:_copilot)
|
||||
elseif exists('b:_copilot.suggestions')
|
||||
let params = deepcopy(b:_copilot.first.params)
|
||||
let params.context.triggerKind = s:inline_invoked
|
||||
let b:_copilot.cycling_callbacks = [a:callback]
|
||||
let b:_copilot.cycling = copilot#Request('getCompletionsCycling',
|
||||
\ b:_copilot.first.params,
|
||||
let b:_copilot.cycling = copilot#Request('textDocument/inlineCompletion',
|
||||
\ params,
|
||||
\ function('s:GetSuggestionsCyclingCallback', [b:_copilot]),
|
||||
\ function('s:GetSuggestionsCyclingCallback', [b:_copilot]),
|
||||
\ )
|
||||
@ -305,10 +293,10 @@ function! copilot#Previous() abort
|
||||
endfunction
|
||||
|
||||
function! copilot#GetDisplayedSuggestion() abort
|
||||
let [text, outdent, delete, uuid] = s:SuggestionTextWithAdjustments()
|
||||
let [text, outdent, delete, item] = s:SuggestionTextWithAdjustments()
|
||||
|
||||
return {
|
||||
\ 'uuid': uuid,
|
||||
\ 'item': item,
|
||||
\ 'text': text,
|
||||
\ 'outdentSize': outdent,
|
||||
\ 'deleteSize': delete}
|
||||
@ -325,8 +313,8 @@ endfunction
|
||||
|
||||
function! s:UpdatePreview() abort
|
||||
try
|
||||
let [text, outdent, delete, uuid] = s:SuggestionTextWithAdjustments()
|
||||
let text = split(text, "\n", 1)
|
||||
let [text, outdent, delete, item] = s:SuggestionTextWithAdjustments()
|
||||
let text = split(text, "\r\n\\=\\|\n", 1)
|
||||
if empty(text[-1])
|
||||
call remove(text, -1)
|
||||
endif
|
||||
@ -343,7 +331,7 @@ function! s:UpdatePreview() abort
|
||||
call s:ClearPreview()
|
||||
if s:has_nvim_ghost_text
|
||||
let data = {'id': 1}
|
||||
let data.virt_text_win_col = virtcol('.') - 1
|
||||
let data.virt_text_pos = 'overlay'
|
||||
let append = strpart(getline('.'), col('.') - 1 + delete)
|
||||
let data.virt_text = [[text[0] . append . repeat(' ', delete - len(text[0])), s:hlgroup]]
|
||||
if len(text) > 1
|
||||
@ -356,8 +344,27 @@ function! s:UpdatePreview() abort
|
||||
endif
|
||||
let data.hl_mode = 'combine'
|
||||
call nvim_buf_set_extmark(0, copilot#NvimNs(), line('.')-1, col('.')-1, data)
|
||||
else
|
||||
call prop_add(line('.'), col('.'), {'type': s:hlgroup, 'text': text[0]})
|
||||
elseif s:has_vim_ghost_text
|
||||
let new_suffix = text[0]
|
||||
let current_suffix = getline('.')[col('.') - 1 :]
|
||||
let inset = ''
|
||||
while delete > 0 && !empty(new_suffix)
|
||||
let last_char = matchstr(new_suffix, '.$')
|
||||
let new_suffix = matchstr(new_suffix, '^.\{-\}\ze.$')
|
||||
if last_char ==# matchstr(current_suffix, '.$')
|
||||
if !empty(inset)
|
||||
call prop_add(line('.'), col('.') + len(current_suffix), {'type': s:hlgroup, 'text': inset})
|
||||
let inset = ''
|
||||
endif
|
||||
let current_suffix = matchstr(current_suffix, '^.\{-\}\ze.$')
|
||||
let delete -= 1
|
||||
else
|
||||
let inset = last_char . inset
|
||||
endif
|
||||
endwhile
|
||||
if !empty(new_suffix . inset)
|
||||
call prop_add(line('.'), col('.'), {'type': s:hlgroup, 'text': new_suffix . inset})
|
||||
endif
|
||||
for line in text[1:]
|
||||
call prop_add(line('.'), 0, {'type': s:hlgroup, 'text_align': 'below', 'text': line})
|
||||
endfor
|
||||
@ -365,28 +372,35 @@ function! s:UpdatePreview() abort
|
||||
call prop_add(line('.'), col('$'), {'type': s:annot_hlgroup, 'text': ' ' . annot})
|
||||
endif
|
||||
endif
|
||||
if !has_key(b:_copilot.shown_choices, uuid)
|
||||
let b:_copilot.shown_choices[uuid] = v:true
|
||||
call copilot#Request('notifyShown', {'uuid': uuid})
|
||||
endif
|
||||
call copilot#Notify('textDocument/didShowCompletion', {'item': item})
|
||||
catch
|
||||
return copilot#logger#Exception()
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:HandleTriggerResult(result) abort
|
||||
if !exists('b:_copilot')
|
||||
return
|
||||
function! s:HandleTriggerResult(state, result) abort
|
||||
let a:state.suggestions = type(a:result) == type([]) ? a:result : get(empty(a:result) ? {} : a:result, 'items', [])
|
||||
let a:state.choice = 0
|
||||
if get(b:, '_copilot') is# a:state
|
||||
call s:UpdatePreview()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:HandleTriggerError(state, result) abort
|
||||
let a:state.suggestions = []
|
||||
let a:state.choice = 0
|
||||
let a:state.error = a:result
|
||||
if get(b:, '_copilot') is# a:state
|
||||
call s:UpdatePreview()
|
||||
endif
|
||||
let b:_copilot.suggestions = get(a:result, 'completions', [])
|
||||
let b:_copilot.choice = 0
|
||||
let b:_copilot.shown_choices = {}
|
||||
call s:UpdatePreview()
|
||||
endfunction
|
||||
|
||||
function! copilot#Suggest() abort
|
||||
if !s:Running()
|
||||
return ''
|
||||
endif
|
||||
try
|
||||
call copilot#Complete(function('s:HandleTriggerResult'), function('s:HandleTriggerResult'))
|
||||
call copilot#Complete(function('s:HandleTriggerResult'), function('s:HandleTriggerError'))
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
endtry
|
||||
@ -402,24 +416,45 @@ function! s:Trigger(bufnr, timer) abort
|
||||
return copilot#Suggest()
|
||||
endfunction
|
||||
|
||||
function! copilot#IsMapped() abort
|
||||
return get(g:, 'copilot_assume_mapped') ||
|
||||
\ hasmapto('copilot#Accept(', 'i')
|
||||
endfunction
|
||||
|
||||
function! copilot#Schedule(...) abort
|
||||
if !s:has_ghost_text || !copilot#Enabled() || !copilot#IsMapped()
|
||||
function! copilot#Schedule() abort
|
||||
if !s:has_ghost_text || !s:Running() || !copilot#Enabled()
|
||||
call copilot#Clear()
|
||||
return
|
||||
endif
|
||||
call s:UpdatePreview()
|
||||
let delay = a:0 ? a:1 : get(g:, 'copilot_idle_delay', 15)
|
||||
let delay = get(g:, 'copilot_idle_delay', 45)
|
||||
call timer_stop(get(g:, '_copilot_timer', -1))
|
||||
let g:_copilot_timer = timer_start(delay, function('s:Trigger', [bufnr('')]))
|
||||
endfunction
|
||||
|
||||
function! copilot#OnInsertLeave() abort
|
||||
return copilot#Clear()
|
||||
function! s:Attach(bufnr, ...) abort
|
||||
try
|
||||
return copilot#Client().Attach(a:bufnr)
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! copilot#OnFileType() abort
|
||||
if empty(s:BufferDisabled()) && &l:modifiable && &l:buflisted
|
||||
call copilot#util#Defer(function('s:Attach'), bufnr(''))
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:Focus(bufnr, ...) abort
|
||||
if s:Running() && copilot#Client().IsAttached(a:bufnr)
|
||||
call copilot#Client().Notify('textDocument/didFocus', {'textDocument': {'uri': copilot#Client().Attach(a:bufnr).uri}})
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#OnBufEnter() abort
|
||||
let bufnr = bufnr('')
|
||||
call copilot#util#Defer(function('s:Focus'), bufnr)
|
||||
endfunction
|
||||
|
||||
function! copilot#OnInsertLeavePre() abort
|
||||
call copilot#Clear()
|
||||
call s:ClearPreview()
|
||||
endfunction
|
||||
|
||||
function! copilot#OnInsertEnter() abort
|
||||
@ -439,7 +474,6 @@ function! copilot#OnCursorMovedI() abort
|
||||
endfunction
|
||||
|
||||
function! copilot#OnBufUnload() abort
|
||||
call s:Reject(+expand('<abuf>'))
|
||||
endfunction
|
||||
|
||||
function! copilot#OnVimLeavePre() abort
|
||||
@ -464,11 +498,19 @@ function! copilot#Accept(...) abort
|
||||
if empty(text)
|
||||
let text = s.text
|
||||
endif
|
||||
call copilot#Request('notifyAccepted', {'uuid': s.uuid, 'acceptedLength': copilot#doc#UTF16Width(text)})
|
||||
if text ==# s.text && has_key(s.item, 'command')
|
||||
call copilot#Request('workspace/executeCommand', s.item.command)
|
||||
else
|
||||
let line_text = strpart(getline('.'), 0, col('.') - 1) . text
|
||||
call copilot#Notify('textDocument/didPartiallyAcceptCompletion', {
|
||||
\ 'item': s.item,
|
||||
\ 'acceptedLength': copilot#util#UTF16Width(line_text) - s.item.range.start.character})
|
||||
endif
|
||||
call s:ClearPreview()
|
||||
let s:suggestion_text = text
|
||||
let recall = text =~# "\n" ? "\<C-R>\<C-O>=" : "\<C-R>\<C-R>="
|
||||
return repeat("\<Left>\<Del>", s.outdentSize) . repeat("\<Del>", s.deleteSize) .
|
||||
\ "\<C-R>\<C-O>=copilot#TextQueuedForInsertion()\<CR>" . (a:0 > 1 ? '' : "\<End>")
|
||||
\ recall . "copilot#TextQueuedForInsertion()\<CR>" . (a:0 > 1 ? '' : "\<End>")
|
||||
endif
|
||||
let default = get(g:, 'copilot_tab_fallback', pumvisible() ? "\<C-N>" : "\t")
|
||||
if !a:0
|
||||
@ -521,21 +563,6 @@ function! copilot#Browser() abort
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:OpenURL(params) abort
|
||||
echo a:params.target
|
||||
let browser = copilot#Browser()
|
||||
if empty(browser)
|
||||
return v:false
|
||||
endif
|
||||
let status = {}
|
||||
call copilot#job#Stream(browser + [a:params.target], v:null, v:null, function('s:BrowserCallback', [status]))
|
||||
let time = reltime()
|
||||
while empty(status) && reltimefloat(reltime(time)) < 1
|
||||
sleep 10m
|
||||
endwhile
|
||||
return get(status, 'code') ? v:false : v:true
|
||||
endfunction
|
||||
|
||||
let s:commands = {}
|
||||
|
||||
function! s:EnabledStatusMessage() abort
|
||||
@ -546,8 +573,6 @@ function! s:EnabledStatusMessage() abort
|
||||
else
|
||||
return "Vim " . s:vim_minimum_version . " required to support ghost text"
|
||||
endif
|
||||
elseif !copilot#IsMapped()
|
||||
return '<Tab> map has been disabled or is claimed by another plugin'
|
||||
elseif !get(g:, 'copilot_enabled', 1)
|
||||
return 'Disabled globally by :Copilot disable'
|
||||
elseif buf_disabled is# 5
|
||||
@ -568,7 +593,7 @@ function! s:EnabledStatusMessage() abort
|
||||
endfunction
|
||||
|
||||
function! s:VerifySetup() abort
|
||||
let error = copilot#Agent().StartupError()
|
||||
let error = copilot#Client().StartupError()
|
||||
if !empty(error)
|
||||
echo 'Copilot: ' . error
|
||||
return
|
||||
@ -585,6 +610,12 @@ function! s:VerifySetup() abort
|
||||
echo 'Copilot: Telemetry terms not accepted. Invoke :Copilot setup'
|
||||
return
|
||||
endif
|
||||
|
||||
if status.status ==# 'NotAuthorized'
|
||||
echo "Copilot: You don't have access to GitHub Copilot. Sign up by visiting https://github.com/settings/copilot"
|
||||
return
|
||||
endif
|
||||
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
@ -593,31 +624,22 @@ function! s:commands.status(opts) abort
|
||||
return
|
||||
endif
|
||||
|
||||
if exists('s:client.status.status') && s:client.status.status =~# 'Warning\|Error'
|
||||
echo 'Copilot: ' . s:client.status.status
|
||||
if !empty(get(s:client.status, 'message', ''))
|
||||
echon ': ' . s:client.status.message
|
||||
endif
|
||||
return
|
||||
endif
|
||||
|
||||
let status = s:EnabledStatusMessage()
|
||||
if !empty(status)
|
||||
echo 'Copilot: ' . status
|
||||
return
|
||||
endif
|
||||
|
||||
let startup_error = copilot#Agent().StartupError()
|
||||
if !empty(startup_error)
|
||||
echo 'Copilot: ' . startup_error
|
||||
return
|
||||
endif
|
||||
|
||||
if exists('s:agent_error')
|
||||
echo 'Copilot: ' . s:agent_error
|
||||
return
|
||||
endif
|
||||
|
||||
let status = copilot#Call('checkStatus', {})
|
||||
if status.status ==# 'NotAuthorized'
|
||||
echo 'Copilot: Not authorized'
|
||||
return
|
||||
endif
|
||||
|
||||
echo 'Copilot: Enabled and online'
|
||||
call s:NodeVersionWarning()
|
||||
echo 'Copilot: Ready'
|
||||
call s:EditorVersionWarning()
|
||||
endfunction
|
||||
|
||||
function! s:commands.signout(opts) abort
|
||||
@ -631,7 +653,7 @@ function! s:commands.signout(opts) abort
|
||||
endfunction
|
||||
|
||||
function! s:commands.setup(opts) abort
|
||||
let startup_error = copilot#Agent().StartupError()
|
||||
let startup_error = copilot#Client().StartupError()
|
||||
if !empty(startup_error)
|
||||
echo 'Copilot: ' . startup_error
|
||||
return
|
||||
@ -641,7 +663,7 @@ function! s:commands.setup(opts) abort
|
||||
|
||||
let status = copilot#Call('checkStatus', {})
|
||||
if has_key(status, 'user')
|
||||
let data = {}
|
||||
let data = {'status': 'AlreadySignedIn', 'user': status.user}
|
||||
else
|
||||
let data = copilot#Call('signInInitiate', {})
|
||||
endif
|
||||
@ -649,23 +671,25 @@ function! s:commands.setup(opts) abort
|
||||
if has_key(data, 'verificationUri')
|
||||
let uri = data.verificationUri
|
||||
if has('clipboard')
|
||||
let @+ = data.userCode
|
||||
let @* = data.userCode
|
||||
try
|
||||
let @+ = data.userCode
|
||||
catch
|
||||
endtry
|
||||
try
|
||||
let @* = data.userCode
|
||||
catch
|
||||
endtry
|
||||
endif
|
||||
call s:Echo("First copy your one-time code: " . data.userCode)
|
||||
let codemsg = "First copy your one-time code: " . data.userCode . "\n"
|
||||
try
|
||||
if len(&mouse)
|
||||
let mouse = &mouse
|
||||
set mouse=
|
||||
endif
|
||||
if get(a:opts, 'bang')
|
||||
call s:Echo("In your browser, visit " . uri)
|
||||
call s:Echo(codemsg . "In your browser, visit " . uri)
|
||||
elseif len(browser)
|
||||
call s:Echo("Press ENTER to open GitHub in your browser")
|
||||
let c = getchar()
|
||||
while c isnot# 13 && c isnot# 10 && c isnot# 0
|
||||
let c = getchar()
|
||||
endwhile
|
||||
call input(codemsg . "Press ENTER to open GitHub in your browser\n")
|
||||
let status = {}
|
||||
call copilot#job#Stream(browser + [uri], v:null, v:null, function('s:BrowserCallback', [status]))
|
||||
let time = reltime()
|
||||
@ -678,9 +702,9 @@ function! s:commands.setup(opts) abort
|
||||
call s:Echo("Opened " . uri)
|
||||
endif
|
||||
else
|
||||
call s:Echo("Could not find browser. Visit " . uri)
|
||||
call s:Echo(codemsg . "Could not find browser. Visit " . uri)
|
||||
endif
|
||||
call s:Echo("Waiting (could take up to 5 seconds)")
|
||||
call s:Echo("Waiting (could take up to 10 seconds)")
|
||||
let request = copilot#Request('signInConfirm', {'userCode': data.userCode}).Wait()
|
||||
finally
|
||||
if exists('mouse')
|
||||
@ -692,6 +716,8 @@ function! s:commands.setup(opts) abort
|
||||
else
|
||||
let status = request.result
|
||||
endif
|
||||
elseif get(data, 'status', '') isnot# 'AlreadySignedIn'
|
||||
return 'echoerr ' . string('Copilot: Something went wrong')
|
||||
endif
|
||||
|
||||
let user = get(status, 'user', '<unknown>')
|
||||
@ -700,23 +726,46 @@ function! s:commands.setup(opts) abort
|
||||
endfunction
|
||||
|
||||
let s:commands.auth = s:commands.setup
|
||||
let s:commands.signin = s:commands.setup
|
||||
|
||||
function! s:commands.help(opts) abort
|
||||
return a:opts.mods . ' help ' . (len(a:opts.arg) ? ':Copilot_' . a:opts.arg : 'copilot')
|
||||
endfunction
|
||||
|
||||
function! s:commands.version(opts) abort
|
||||
let info = copilot#agent#EditorInfo()
|
||||
echo 'copilot.vim ' .info.editorPluginInfo.version
|
||||
echo info.editorInfo.name . ' ' . info.editorInfo.version
|
||||
echo 'copilot.vim ' .copilot#client#EditorPluginInfo().version
|
||||
let editorInfo = copilot#client#EditorInfo()
|
||||
echo editorInfo.name . ' ' . editorInfo.version
|
||||
if s:Running()
|
||||
let versions = s:agent.Call('getVersion', {})
|
||||
echo 'dist/agent.js ' . versions.version
|
||||
echo 'Node.js ' . get(s:agent, 'node_version', substitute(get(versions, 'runtimeVersion', '?'), '^node/', '', 'g'))
|
||||
call s:NodeVersionWarning()
|
||||
let versions = s:client.Request('getVersion', {})
|
||||
if exists('s:client.serverInfo.version')
|
||||
echo s:client.serverInfo.name . ' ' . s:client.serverInfo.version
|
||||
else
|
||||
echo 'GitHub Copilot Language Server ' . versions.Await().version
|
||||
endif
|
||||
if exists('s:client.node_version')
|
||||
echo 'Node.js ' . s:client.node_version
|
||||
else
|
||||
echo 'Node.js ' . substitute(get(versions.Await(), 'runtimeVersion', '?'), '^node/', '', 'g')
|
||||
endif
|
||||
else
|
||||
echo 'dist/agent.js not running'
|
||||
echo 'Not running'
|
||||
if exists('s:client.node_version')
|
||||
echo 'Node.js ' . s:client.node_version
|
||||
endif
|
||||
endif
|
||||
if has('win32')
|
||||
echo 'Windows'
|
||||
elseif has('macunix')
|
||||
echo 'macOS'
|
||||
elseif !has('unix')
|
||||
echo 'Unknown OS'
|
||||
elseif isdirectory('/sys/kernel')
|
||||
echo 'Linux'
|
||||
else
|
||||
echo 'UNIX'
|
||||
endif
|
||||
call s:EditorVersionWarning()
|
||||
endfunction
|
||||
|
||||
function! s:UpdateEditorConfiguration() abort
|
||||
@ -740,11 +789,8 @@ endfunction
|
||||
|
||||
function! s:commands.restart(opts) abort
|
||||
call s:Stop()
|
||||
let err = copilot#Agent().StartupError()
|
||||
if !empty(err)
|
||||
return 'echoerr ' . string('Copilot: ' . err)
|
||||
endif
|
||||
echo 'Copilot: Restarting agent.'
|
||||
echo 'Copilot: Restarting language server'
|
||||
call s:Start()
|
||||
endfunction
|
||||
|
||||
function! s:commands.disable(opts) abort
|
||||
@ -763,6 +809,10 @@ function! s:commands.panel(opts) abort
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:commands.log(opts) abort
|
||||
return a:opts.mods . ' split +$ copilot:///log'
|
||||
endfunction
|
||||
|
||||
function! copilot#CommandComplete(arg, lead, pos) abort
|
||||
let args = matchstr(strpart(a:lead, 0, a:pos), 'C\%[opilot][! ] *\zs.*')
|
||||
if args !~# ' '
|
||||
@ -776,33 +826,28 @@ endfunction
|
||||
function! copilot#Command(line1, line2, range, bang, mods, arg) abort
|
||||
let cmd = matchstr(a:arg, '^\%(\\.\|\S\)\+')
|
||||
let arg = matchstr(a:arg, '\s\zs\S.*')
|
||||
if cmd ==# 'log'
|
||||
return a:mods . ' split +$ ' . fnameescape(copilot#logger#File())
|
||||
endif
|
||||
if !empty(cmd) && !has_key(s:commands, tr(cmd, '-', '_'))
|
||||
return 'echoerr ' . string('Copilot: unknown command ' . string(cmd))
|
||||
endif
|
||||
try
|
||||
let err = copilot#Agent().StartupError()
|
||||
if !empty(err)
|
||||
return 'echo ' . string('Copilot: ' . err)
|
||||
endif
|
||||
try
|
||||
let opts = copilot#Call('checkStatus', {'options': {'localChecksOnly': v:true}})
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
let opts = {'status': 'VimException'}
|
||||
endtry
|
||||
if empty(cmd)
|
||||
if opts.status ==# 'VimException'
|
||||
return a:mods . ' split +$ ' . fnameescape(copilot#logger#File())
|
||||
elseif opts.status !=# 'OK' && opts.status !=# 'MaybeOK'
|
||||
let cmd = 'setup'
|
||||
if !s:Running()
|
||||
let cmd = 'restart'
|
||||
else
|
||||
let cmd = 'panel'
|
||||
try
|
||||
let opts = copilot#Call('checkStatus', {'options': {'localChecksOnly': v:true}})
|
||||
if opts.status !=# 'OK' && opts.status !=# 'MaybeOK'
|
||||
let cmd = 'setup'
|
||||
else
|
||||
let cmd = 'panel'
|
||||
endif
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
let cmd = 'log'
|
||||
endtry
|
||||
endif
|
||||
endif
|
||||
call extend(opts, {'line1': a:line1, 'line2': a:line2, 'range': a:range, 'bang': a:bang, 'mods': a:mods, 'arg': arg})
|
||||
let opts = {'line1': a:line1, 'line2': a:line2, 'range': a:range, 'bang': a:bang, 'mods': a:mods, 'arg': arg}
|
||||
let retval = s:commands[tr(cmd, '-', '_')](opts)
|
||||
if type(retval) == v:t_string
|
||||
return retval
|
||||
|
@ -1,603 +0,0 @@
|
||||
scriptencoding utf-8
|
||||
|
||||
let s:plugin_version = copilot#version#String()
|
||||
|
||||
let s:error_exit = -1
|
||||
|
||||
let s:root = expand('<sfile>:h:h:h')
|
||||
|
||||
if !exists('s:instances')
|
||||
let s:instances = {}
|
||||
endif
|
||||
|
||||
" allow sourcing this file to reload the Lua file too
|
||||
if has('nvim')
|
||||
lua package.loaded._copilot = nil
|
||||
endif
|
||||
|
||||
let s:jobstop = function(exists('*jobstop') ? 'jobstop' : 'job_stop')
|
||||
function! s:Kill(agent, ...) abort
|
||||
if has_key(a:agent, 'job')
|
||||
call s:jobstop(a:agent.job)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:AgentClose() dict abort
|
||||
if !has_key(self, 'job')
|
||||
return
|
||||
endif
|
||||
if exists('*chanclose')
|
||||
call chanclose(self.job, 'stdin')
|
||||
else
|
||||
call ch_close_in(self.job)
|
||||
endif
|
||||
call copilot#logger#Info('agent stopped')
|
||||
call timer_start(2000, function('s:Kill', [self]))
|
||||
endfunction
|
||||
|
||||
function! s:LogSend(request, line) abort
|
||||
return '--> ' . a:line
|
||||
endfunction
|
||||
|
||||
function! s:RejectRequest(request, error) abort
|
||||
if a:request.status ==# 'canceled'
|
||||
return
|
||||
endif
|
||||
let a:request.waiting = {}
|
||||
call remove(a:request, 'resolve')
|
||||
let reject = remove(a:request, 'reject')
|
||||
let a:request.status = 'error'
|
||||
let a:request.error = a:error
|
||||
for Cb in reject
|
||||
let a:request.waiting[timer_start(0, function('s:Callback', [a:request, 'error', Cb]))] = 1
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:Send(agent, request) abort
|
||||
try
|
||||
call ch_sendexpr(a:agent.job, a:request)
|
||||
return v:true
|
||||
catch /^Vim\%((\a\+)\)\=:E631:/
|
||||
return v:false
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:AgentNotify(method, params) dict abort
|
||||
return s:Send(self, {'method': a:method, 'params': a:params})
|
||||
endfunction
|
||||
|
||||
function! s:RequestWait() dict abort
|
||||
while self.status ==# 'running'
|
||||
sleep 1m
|
||||
endwhile
|
||||
while !empty(get(self, 'waiting', {}))
|
||||
sleep 1m
|
||||
endwhile
|
||||
return self
|
||||
endfunction
|
||||
|
||||
function! s:RequestAwait() dict abort
|
||||
call self.Wait()
|
||||
if has_key(self, 'result')
|
||||
return self.result
|
||||
endif
|
||||
throw 'copilot#agent:E' . self.error.code . ': ' . self.error.message
|
||||
endfunction
|
||||
|
||||
function! s:RequestAgent() dict abort
|
||||
return get(s:instances, self.agent_id, v:null)
|
||||
endfunction
|
||||
|
||||
if !exists('s:id')
|
||||
let s:id = 0
|
||||
endif
|
||||
|
||||
function! s:SetUpRequest(agent, id, method, params, ...) abort
|
||||
let request = {
|
||||
\ 'agent_id': a:agent.id,
|
||||
\ 'id': a:id,
|
||||
\ 'method': a:method,
|
||||
\ 'params': a:params,
|
||||
\ 'Agent': function('s:RequestAgent'),
|
||||
\ 'Wait': function('s:RequestWait'),
|
||||
\ 'Await': function('s:RequestAwait'),
|
||||
\ 'Cancel': function('s:RequestCancel'),
|
||||
\ 'resolve': [],
|
||||
\ 'reject': [],
|
||||
\ 'status': 'running'}
|
||||
let a:agent.requests[a:id] = request
|
||||
let args = a:000[2:-1]
|
||||
if len(args)
|
||||
if !empty(a:1)
|
||||
call add(request.resolve, { v -> call(a:1, [v] + args)})
|
||||
endif
|
||||
if !empty(a:2)
|
||||
call add(request.reject, { v -> call(a:2, [v] + args)})
|
||||
endif
|
||||
return request
|
||||
endif
|
||||
if a:0 && !empty(a:1)
|
||||
call add(request.resolve, a:1)
|
||||
endif
|
||||
if a:0 > 1 && !empty(a:2)
|
||||
call add(request.reject, a:2)
|
||||
endif
|
||||
return request
|
||||
endfunction
|
||||
|
||||
function! s:UrlEncode(str) abort
|
||||
return substitute(iconv(a:str, 'latin1', 'utf-8'),'[^A-Za-z0-9._~!$&''()*+,;=:@/-]','\="%".printf("%02X",char2nr(submatch(0)))','g')
|
||||
endfunction
|
||||
|
||||
let s:slash = exists('+shellslash') ? '\' : '/'
|
||||
function! s:UriFromBufnr(bufnr) abort
|
||||
let absolute = tr(bufname(a:bufnr), s:slash, '/')
|
||||
if absolute !~# '^\a\+:\|^/\|^$' && getbufvar(a:bufnr, 'buftype') =~# '^\%(nowrite\)\=$'
|
||||
let absolute = substitute(tr(getcwd(), s:slash, '/'), '/\=$', '/', '') . absolute
|
||||
endif
|
||||
if has('win32') && absolute =~# '^\a://\@!'
|
||||
return 'file:///' . strpart(absolute, 0, 2) . s:UrlEncode(strpart(absolute, 2))
|
||||
elseif absolute =~# '^/'
|
||||
return 'file://' . s:UrlEncode(absolute)
|
||||
elseif absolute =~# '^\a[[:alnum:].+-]*:\|^$'
|
||||
return absolute
|
||||
else
|
||||
return ''
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:BufferText(bufnr) abort
|
||||
return join(getbufline(a:bufnr, 1, '$'), "\n") . "\n"
|
||||
endfunction
|
||||
|
||||
function! s:LogMessage(params) abort
|
||||
call copilot#logger#Raw(get(a:params, 'level', 3), get(a:params, 'message', ''))
|
||||
endfunction
|
||||
|
||||
function! s:ShowMessageRequest(params) abort
|
||||
let choice = inputlist([a:params.message . "\n\nRequest Actions:"] +
|
||||
\ map(copy(get(a:params, 'actions', [])), { i, v -> (i + 1) . '. ' . v.title}))
|
||||
return choice > 0 ? get(a:params.actions, choice - 1, v:null) : v:null
|
||||
endfunction
|
||||
|
||||
function! s:SendRequest(agent, request) abort
|
||||
if empty(s:Send(a:agent, a:request)) && has_key(a:agent.requests, a:request.id)
|
||||
call s:RejectRequest(remove(a:agent.requests, a:request.id), {'code': 257, 'message': 'Write failed'})
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:AgentRequest(method, params, ...) dict abort
|
||||
let s:id += 1
|
||||
let request = {'method': a:method, 'params': deepcopy(a:params), 'id': s:id}
|
||||
for doc in filter([get(request.params, 'doc', {}), get(request.params, 'textDocument',{})], 'type(get(v:val, "uri", "")) == v:t_number')
|
||||
let bufnr = doc.uri
|
||||
let doc.uri = s:UriFromBufnr(doc.uri)
|
||||
let uri = doc.uri
|
||||
let languageId = copilot#doc#LanguageForFileType(getbufvar(bufnr, '&filetype'))
|
||||
let doc_version = getbufvar(bufnr, 'changedtick')
|
||||
if has_key(self.open_buffers, bufnr) && (
|
||||
\ self.open_buffers[bufnr].uri !=# doc.uri ||
|
||||
\ self.open_buffers[bufnr].languageId !=# languageId)
|
||||
call remove(self.open_buffers, bufnr)
|
||||
sleep 1m
|
||||
endif
|
||||
if !has_key(self.open_buffers, bufnr)
|
||||
let td_item = {
|
||||
\ 'uri': doc.uri,
|
||||
\ 'version': doc_version,
|
||||
\ 'languageId': languageId,
|
||||
\ 'text': s:BufferText(bufnr)}
|
||||
call self.Notify('textDocument/didOpen', {'textDocument': td_item})
|
||||
let self.open_buffers[bufnr] = {
|
||||
\ 'uri': doc.uri,
|
||||
\ 'version': doc_version,
|
||||
\ 'languageId': languageId}
|
||||
else
|
||||
let vtd_id = {
|
||||
\ 'uri': doc.uri,
|
||||
\ 'version': doc_version}
|
||||
call self.Notify('textDocument/didChange', {
|
||||
\ 'textDocument': vtd_id,
|
||||
\ 'contentChanges': [{'text': s:BufferText(bufnr)}]})
|
||||
let self.open_buffers[bufnr].version = doc_version
|
||||
endif
|
||||
let doc.version = doc_version
|
||||
endfor
|
||||
call timer_start(0, { _ -> s:SendRequest(self, request) })
|
||||
return call('s:SetUpRequest', [self, s:id, a:method, a:params] + a:000)
|
||||
endfunction
|
||||
|
||||
function! s:AgentCall(method, params, ...) dict abort
|
||||
let request = call(self.Request, [a:method, a:params] + a:000)
|
||||
if a:0
|
||||
return request
|
||||
endif
|
||||
return request.Await()
|
||||
endfunction
|
||||
|
||||
function! s:AgentCancel(request) dict abort
|
||||
if has_key(self.requests, get(a:request, 'id', ''))
|
||||
call remove(self.requests, a:request.id)
|
||||
call self.Notify('$/cancelRequest', {'id': a:request.id})
|
||||
endif
|
||||
if get(a:request, 'status', '') ==# 'running'
|
||||
let a:request.status = 'canceled'
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:RequestCancel() dict abort
|
||||
let agent = self.Agent()
|
||||
if !empty(agent)
|
||||
call agent.Cancel(self)
|
||||
elseif get(self, 'status', '') ==# 'running'
|
||||
let self.status = 'canceled'
|
||||
endif
|
||||
return self
|
||||
endfunction
|
||||
|
||||
function! s:DispatchMessage(agent, method, handler, id, params, ...) abort
|
||||
try
|
||||
let response = {'result': call(a:handler, [a:params])}
|
||||
if response.result is# 0
|
||||
let response.result = v:null
|
||||
endif
|
||||
catch
|
||||
call copilot#logger#Exception('lsp.request.' . a:method)
|
||||
let response = {'error': {'code': -32000, 'message': v:exception}}
|
||||
endtry
|
||||
if !empty(a:id)
|
||||
call s:Send(a:agent, extend({'id': a:id}, response))
|
||||
endif
|
||||
return response
|
||||
endfunction
|
||||
|
||||
function! s:OnMessage(agent, body, ...) abort
|
||||
if !has_key(a:body, 'method')
|
||||
return s:OnResponse(a:agent, a:body)
|
||||
endif
|
||||
let request = a:body
|
||||
let id = get(request, 'id', v:null)
|
||||
let params = get(request, 'params', v:null)
|
||||
if has_key(a:agent.methods, request.method)
|
||||
return s:DispatchMessage(a:agent, request.method, a:agent.methods[request.method], id, params)
|
||||
elseif !empty(id)
|
||||
call s:Send(a:agent, {"id": id, "error": {"code": -32700, "message": "Method not found: " . request.method}})
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:OnResponse(agent, response, ...) abort
|
||||
let response = a:response
|
||||
let id = get(a:response, 'id', v:null)
|
||||
if !has_key(a:agent.requests, id)
|
||||
return
|
||||
endif
|
||||
let request = remove(a:agent.requests, id)
|
||||
if request.status ==# 'canceled'
|
||||
return
|
||||
endif
|
||||
let request.waiting = {}
|
||||
let resolve = remove(request, 'resolve')
|
||||
let reject = remove(request, 'reject')
|
||||
if has_key(response, 'result')
|
||||
let request.status = 'success'
|
||||
let request.result = response.result
|
||||
for Cb in resolve
|
||||
let request.waiting[timer_start(0, function('s:Callback', [request, 'result', Cb]))] = 1
|
||||
endfor
|
||||
else
|
||||
let request.status = 'error'
|
||||
let request.error = response.error
|
||||
for Cb in reject
|
||||
let request.waiting[timer_start(0, function('s:Callback', [request, 'error', Cb]))] = 1
|
||||
endfor
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:OnErr(agent, line, ...) abort
|
||||
call copilot#logger#Debug('<-! ' . a:line)
|
||||
endfunction
|
||||
|
||||
function! s:OnExit(agent, code, ...) abort
|
||||
let a:agent.exit_status = a:code
|
||||
if has_key(a:agent, 'job')
|
||||
call remove(a:agent, 'job')
|
||||
endif
|
||||
if has_key(a:agent, 'client_id')
|
||||
call remove(a:agent, 'client_id')
|
||||
endif
|
||||
let code = a:code < 0 || a:code > 255 ? 256 : a:code
|
||||
for id in sort(keys(a:agent.requests), { a, b -> +a > +b })
|
||||
call s:RejectRequest(remove(a:agent.requests, id), {'code': code, 'message': 'Agent exited', 'data': {'status': a:code}})
|
||||
endfor
|
||||
call timer_start(0, { _ -> get(s:instances, a:agent.id) is# a:agent ? remove(s:instances, a:agent.id) : {} })
|
||||
call copilot#logger#Info('agent exited with status ' . a:code)
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#LspInit(agent_id, initialize_result) abort
|
||||
if !has_key(s:instances, a:agent_id)
|
||||
return
|
||||
endif
|
||||
let instance = s:instances[a:agent_id]
|
||||
call timer_start(0, { _ -> s:GetCapabilitiesResult(a:initialize_result, instance)})
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#LspExit(agent_id, code, signal) abort
|
||||
if !has_key(s:instances, a:agent_id)
|
||||
return
|
||||
endif
|
||||
let instance = remove(s:instances, a:agent_id)
|
||||
call s:OnExit(instance, a:code)
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#LspResponse(agent_id, opts, ...) abort
|
||||
if !has_key(s:instances, a:agent_id)
|
||||
return
|
||||
endif
|
||||
call s:OnResponse(s:instances[a:agent_id], a:opts)
|
||||
endfunction
|
||||
|
||||
function! s:LspRequest(method, params, ...) dict abort
|
||||
let id = v:lua.require'_copilot'.lsp_request(self.id, a:method, a:params)
|
||||
if id isnot# v:null
|
||||
return call('s:SetUpRequest', [self, id, a:method, a:params] + a:000)
|
||||
endif
|
||||
if has_key(self, 'client_id')
|
||||
call copilot#agent#LspExit(self.client_id, -1, -1)
|
||||
endif
|
||||
throw 'copilot#agent: LSP client not available'
|
||||
endfunction
|
||||
|
||||
function! s:LspClose() dict abort
|
||||
if !has_key(self, 'client_id')
|
||||
return
|
||||
endif
|
||||
return luaeval('vim.lsp.get_client_by_id(_A).stop()', self.client_id)
|
||||
endfunction
|
||||
|
||||
function! s:LspNotify(method, params) dict abort
|
||||
return v:lua.require'_copilot'.rpc_notify(self.id, a:method, a:params)
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#LspHandle(agent_id, request) abort
|
||||
if !has_key(s:instances, a:agent_id)
|
||||
return
|
||||
endif
|
||||
return s:OnMessage(s:instances[a:agent_id], a:request)
|
||||
endfunction
|
||||
|
||||
function! s:GetNodeVersion(command) abort
|
||||
let out = []
|
||||
let err = []
|
||||
let status = copilot#job#Stream(a:command + ['--version'], function('add', [out]), function('add', [err]))
|
||||
let string = matchstr(join(out, ''), '^v\zs\d\+\.[^[:space:]]*')
|
||||
if status != 0
|
||||
let string = ''
|
||||
endif
|
||||
let major = str2nr(string)
|
||||
let minor = str2nr(matchstr(string, '\.\zs\d\+'))
|
||||
return {'status': status, 'string': string, 'major': major, 'minor': minor}
|
||||
endfunction
|
||||
|
||||
function! s:Command() abort
|
||||
if !has('nvim-0.6') && v:version < 900
|
||||
return [v:null, '', 'Vim version too old']
|
||||
endif
|
||||
let agent = get(g:, 'copilot_agent_command', '')
|
||||
if empty(agent) || !filereadable(agent)
|
||||
let agent = s:root . '/dist/agent.js'
|
||||
if !filereadable(agent)
|
||||
return [v:null, '', 'Could not find dist/agent.js (bad install?)']
|
||||
endif
|
||||
endif
|
||||
let node = get(g:, 'copilot_node_command', '')
|
||||
if empty(node)
|
||||
let node = ['node']
|
||||
elseif type(node) == type('')
|
||||
let node = [expand(node)]
|
||||
endif
|
||||
if !executable(get(node, 0, ''))
|
||||
if get(node, 0, '') ==# 'node'
|
||||
return [v:null, '', 'Node.js not found in PATH']
|
||||
else
|
||||
return [v:null, '', 'Node.js executable `' . get(node, 0, '') . "' not found"]
|
||||
endif
|
||||
endif
|
||||
if get(g:, 'copilot_ignore_node_version')
|
||||
return [node + [agent, '--stdio'], '', '']
|
||||
endif
|
||||
let node_version = s:GetNodeVersion(node)
|
||||
let warning = ''
|
||||
if node_version.major < 18 && get(node, 0, '') !=# 'node' && executable('node')
|
||||
let node_version_from_path = s:GetNodeVersion(['node'])
|
||||
if node_version_from_path.major >= 18
|
||||
let warning = 'Ignoring g:copilot_node_command: Node.js ' . node_version.string . ' is end-of-life'
|
||||
let node = ['node']
|
||||
let node_version = node_version_from_path
|
||||
endif
|
||||
endif
|
||||
if node_version.status != 0
|
||||
return [v:null, '', 'Node.js exited with status ' . node_version.status]
|
||||
endif
|
||||
if !get(g:, 'copilot_ignore_node_version')
|
||||
if node_version.major == 0
|
||||
return [v:null, node_version.string, 'Could not determine Node.js version']
|
||||
elseif node_version.major < 16 || node_version.major == 16 && node_version.minor < 14 || node_version.major == 17 && node_version.minor < 3
|
||||
" 16.14+ and 17.3+ still work for now, but are end-of-life
|
||||
return [v:null, node_version.string, 'Node.js version 18.x or newer required but found ' . node_version.string]
|
||||
endif
|
||||
endif
|
||||
return [node + [agent, '--stdio'], node_version.string, warning]
|
||||
endfunction
|
||||
|
||||
function! s:UrlDecode(str) abort
|
||||
return substitute(a:str, '%\(\x\x\)', '\=iconv(nr2char("0x".submatch(1)), "utf-8", "latin1")', 'g')
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#EditorInfo() abort
|
||||
if !exists('s:editor_version')
|
||||
if has('nvim')
|
||||
let s:editor_version = matchstr(execute('version'), 'NVIM v\zs[^[:space:]]\+')
|
||||
else
|
||||
let s:editor_version = (v:version / 100) . '.' . (v:version % 100) . (exists('v:versionlong') ? printf('.%04d', v:versionlong % 1000) : '')
|
||||
endif
|
||||
endif
|
||||
let info = {
|
||||
\ 'editorInfo': {'name': has('nvim') ? 'Neovim': 'Vim', 'version': s:editor_version},
|
||||
\ 'editorPluginInfo': {'name': 'copilot.vim', 'version': s:plugin_version}}
|
||||
if type(get(g:, 'copilot_proxy')) == v:t_string
|
||||
let proxy = g:copilot_proxy
|
||||
else
|
||||
let proxy = ''
|
||||
endif
|
||||
let match = matchlist(proxy, '\c^\%([^:]\+://\)\=\%(\([^/#]\+@\)\)\=\%(\([^/:#]\+\)\|\[\([[:xdigit:]:]\+\)\]\)\%(:\(\d\+\)\)\=\%(/\|$\|?strict_\=ssl=\(.*\)\)')
|
||||
if !empty(match)
|
||||
let info.networkProxy = {'host': match[2] . match[3], 'port': empty(match[4]) ? 80 : +match[4]}
|
||||
if match[5] =~? '^[0f]'
|
||||
let info.networkProxy.rejectUnauthorized = v:false
|
||||
elseif match[5] =~? '^[1t]'
|
||||
let info.networkProxy.rejectUnauthorized = v:true
|
||||
elseif exists('g:copilot_proxy_strict_ssl')
|
||||
let info.networkProxy.rejectUnauthorized = empty(g:copilot_proxy_strict_ssl) ? v:false : v:true
|
||||
endif
|
||||
if !empty(match[1])
|
||||
let info.networkProxy.username = s:UrlDecode(matchstr(match[1], '^[^:@]*'))
|
||||
let info.networkProxy.password = s:UrlDecode(matchstr(match[1], ':\zs[^@]*'))
|
||||
endif
|
||||
endif
|
||||
return info
|
||||
endfunction
|
||||
|
||||
function! s:GetCapabilitiesResult(result, agent) abort
|
||||
let a:agent.capabilities = get(a:result, 'capabilities', {})
|
||||
let info = copilot#agent#EditorInfo()
|
||||
call a:agent.Request('setEditorInfo', extend({'editorConfiguration': a:agent.editorConfiguration}, info))
|
||||
endfunction
|
||||
|
||||
function! s:GetCapabilitiesError(error, agent) abort
|
||||
if a:error.code == s:error_exit
|
||||
let a:agent.startup_error = 'Agent exited with status ' . a:error.data.status
|
||||
else
|
||||
let a:agent.startup_error = 'Unexpected error ' . a:error.code . ' calling agent: ' . a:error.message
|
||||
call a:agent.Close()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:AgentStartupError() dict abort
|
||||
while (has_key(self, 'job') || has_key(self, 'client_id')) && !has_key(self, 'startup_error') && !has_key(self, 'capabilities')
|
||||
sleep 10m
|
||||
endwhile
|
||||
if has_key(self, 'capabilities')
|
||||
return ''
|
||||
else
|
||||
return get(self, 'startup_error', 'Something unexpected went wrong spawning the agent')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#New(...) abort
|
||||
let opts = a:0 ? a:1 : {}
|
||||
let instance = {'requests': {},
|
||||
\ 'editorConfiguration': get(opts, 'editorConfiguration', {}),
|
||||
\ 'Close': function('s:AgentClose'),
|
||||
\ 'Notify': function('s:AgentNotify'),
|
||||
\ 'Request': function('s:AgentRequest'),
|
||||
\ 'Call': function('s:AgentCall'),
|
||||
\ 'Cancel': function('s:AgentCancel'),
|
||||
\ 'StartupError': function('s:AgentStartupError'),
|
||||
\ }
|
||||
let instance.methods = extend({
|
||||
\ 'LogMessage': function('s:LogMessage'),
|
||||
\ 'window/logMessage': function('s:LogMessage'),
|
||||
\ }, get(opts, 'methods', {}))
|
||||
let [command, node_version, command_error] = s:Command()
|
||||
if len(command_error)
|
||||
if empty(command)
|
||||
let instance.id = -1
|
||||
let instance.startup_error = command_error
|
||||
return instance
|
||||
else
|
||||
let instance.node_version_warning = command_error
|
||||
endif
|
||||
endif
|
||||
if !empty(node_version)
|
||||
let instance.node_version = node_version
|
||||
endif
|
||||
if has('nvim')
|
||||
call extend(instance, {
|
||||
\ 'Close': function('s:LspClose'),
|
||||
\ 'Notify': function('s:LspNotify'),
|
||||
\ 'Request': function('s:LspRequest')})
|
||||
let instance.client_id = v:lua.require'_copilot'.lsp_start_client(command, keys(instance.methods))
|
||||
let instance.id = instance.client_id
|
||||
else
|
||||
let state = {'headers': {}, 'mode': 'headers', 'buffer': ''}
|
||||
let instance.open_buffers = {}
|
||||
let instance.methods = extend({'window/showMessageRequest': function('s:ShowMessageRequest')}, instance.methods)
|
||||
let instance.job = job_start(command, {
|
||||
\ 'cwd': copilot#job#Cwd(),
|
||||
\ 'in_mode': 'lsp',
|
||||
\ 'out_mode': 'lsp',
|
||||
\ 'out_cb': { j, d -> timer_start(0, function('s:OnMessage', [instance, d])) },
|
||||
\ 'err_cb': { j, d -> timer_start(0, function('s:OnErr', [instance, d])) },
|
||||
\ 'exit_cb': { j, d -> timer_start(0, function('s:OnExit', [instance, d])) },
|
||||
\ })
|
||||
let instance.id = exists('*jobpid') ? jobpid(instance.job) : job_info(instance.job).process
|
||||
let capabilities = {'workspace': {'workspaceFolders': v:true}, 'copilot': {}}
|
||||
for name in keys(instance.methods)
|
||||
if name =~# '^copilot/'
|
||||
let capabilities.copilot[matchstr(name, '/\zs.*')] = v:true
|
||||
endif
|
||||
endfor
|
||||
let request = instance.Request('initialize', {'capabilities': capabilities}, function('s:GetCapabilitiesResult'), function('s:GetCapabilitiesError'), instance)
|
||||
endif
|
||||
let s:instances[instance.id] = instance
|
||||
return instance
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#Cancel(request) abort
|
||||
if type(a:request) == type({}) && has_key(a:request, 'Cancel')
|
||||
call a:request.Cancel()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:Callback(request, type, callback, timer) abort
|
||||
call remove(a:request.waiting, a:timer)
|
||||
if has_key(a:request, a:type)
|
||||
call a:callback(a:request[a:type])
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#Result(request, callback) abort
|
||||
if has_key(a:request, 'resolve')
|
||||
call add(a:request.resolve, a:callback)
|
||||
elseif has_key(a:request, 'result')
|
||||
let a:request.waiting[timer_start(0, function('s:Callback', [a:request, 'result', a:callback]))] = 1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#agent#Error(request, callback) abort
|
||||
if has_key(a:request, 'reject')
|
||||
call add(a:request.reject, a:callback)
|
||||
elseif has_key(a:request, 'error')
|
||||
let a:request.waiting[timer_start(0, function('s:Callback', [a:request, 'error', a:callback]))] = 1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:CloseBuffer(bufnr) abort
|
||||
for instance in values(s:instances)
|
||||
try
|
||||
if has_key(instance, 'job') && has_key(instance.open_buffers, a:bufnr)
|
||||
let buffer = remove(instance.open_buffers, a:bufnr)
|
||||
call instance.Notify('textDocument/didClose', {'textDocument': {'uri': buffer.uri}})
|
||||
endif
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
endtry
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
augroup copilot_agent
|
||||
autocmd!
|
||||
if !has('nvim')
|
||||
autocmd BufUnload * call s:CloseBuffer(+expand('<abuf>'))
|
||||
endif
|
||||
augroup END
|
764
.vim/pack/plugins/opt/vim-copilot/autoload/copilot/client.vim
Normal file
764
.vim/pack/plugins/opt/vim-copilot/autoload/copilot/client.vim
Normal file
@ -0,0 +1,764 @@
|
||||
scriptencoding utf-8
|
||||
|
||||
let s:plugin_version = copilot#version#String()
|
||||
|
||||
let s:error_canceled = {'code': -32800, 'message': 'Canceled'}
|
||||
let s:error_exit = {'code': -32097, 'message': 'Process exited'}
|
||||
let s:error_connection_inactive = {'code': -32096, 'message': 'Connection inactive'}
|
||||
|
||||
let s:root = expand('<sfile>:h:h:h')
|
||||
|
||||
if !exists('s:instances')
|
||||
let s:instances = {}
|
||||
endif
|
||||
|
||||
" allow sourcing this file to reload the Lua file too
|
||||
if has('nvim')
|
||||
lua package.loaded._copilot = nil
|
||||
endif
|
||||
|
||||
function! s:Warn(msg) abort
|
||||
if !empty(get(g:, 'copilot_no_startup_warnings'))
|
||||
return
|
||||
endif
|
||||
echohl WarningMsg
|
||||
echomsg 'Copilot: ' . a:msg
|
||||
echohl NONE
|
||||
endfunction
|
||||
|
||||
function! s:VimClose() dict abort
|
||||
if !has_key(self, 'job')
|
||||
return
|
||||
endif
|
||||
let job = self.job
|
||||
if has_key(self, 'kill')
|
||||
call job_stop(job, 'kill')
|
||||
call copilot#logger#Warn('Process forcefully terminated')
|
||||
return
|
||||
endif
|
||||
let self.kill = v:true
|
||||
let self.shutdown = self.Request('shutdown', {}, function(self.Notify, ['exit']))
|
||||
call timer_start(2000, { _ -> job_stop(job, 'kill') })
|
||||
call copilot#logger#Debug('Process shutdown initiated')
|
||||
endfunction
|
||||
|
||||
function! s:LogSend(request, line) abort
|
||||
return '--> ' . a:line
|
||||
endfunction
|
||||
|
||||
function! s:RejectRequest(request, error) abort
|
||||
if a:request.status !=# 'running'
|
||||
return
|
||||
endif
|
||||
let a:request.waiting = {}
|
||||
call remove(a:request, 'resolve')
|
||||
let reject = remove(a:request, 'reject')
|
||||
let a:request.status = 'error'
|
||||
let a:request.error = deepcopy(a:error)
|
||||
for Cb in reject
|
||||
let a:request.waiting[timer_start(0, function('s:Callback', [a:request, 'error', Cb]))] = 1
|
||||
endfor
|
||||
if index([s:error_canceled.code, s:error_connection_inactive.code], a:error.code) != -1
|
||||
return
|
||||
endif
|
||||
let msg = 'Method ' . a:request.method . ' errored with E' . a:error.code . ': ' . json_encode(a:error.message)
|
||||
if empty(reject)
|
||||
call copilot#logger#Error(msg)
|
||||
else
|
||||
call copilot#logger#Debug(msg)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:AfterInitialized(fn, ...) dict abort
|
||||
call add(self.after_initialized, function(a:fn, a:000))
|
||||
endfunction
|
||||
|
||||
function! s:Send(instance, request) abort
|
||||
if !has_key(a:instance, 'job')
|
||||
return v:false
|
||||
endif
|
||||
try
|
||||
call ch_sendexpr(a:instance.job, a:request)
|
||||
return v:true
|
||||
catch /^Vim\%((\a\+)\)\=:E906:/
|
||||
let a:instance.kill = v:true
|
||||
let job = remove(a:instance, 'job')
|
||||
call job_stop(job)
|
||||
call timer_start(2000, { _ -> job_stop(job, 'kill') })
|
||||
call copilot#logger#Warn('Terminating process after failed write')
|
||||
return v:false
|
||||
catch /^Vim\%((\a\+)\)\=:E631:/
|
||||
return v:false
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:VimNotify(method, params) dict abort
|
||||
let request = {'method': a:method, 'params': a:params}
|
||||
call self.AfterInitialized(function('s:Send', [self, request]))
|
||||
endfunction
|
||||
|
||||
function! s:RequestWait() dict abort
|
||||
while self.status ==# 'running'
|
||||
sleep 1m
|
||||
endwhile
|
||||
while !empty(get(self, 'waiting', {}))
|
||||
sleep 1m
|
||||
endwhile
|
||||
return self
|
||||
endfunction
|
||||
|
||||
function! s:RequestAwait() dict abort
|
||||
call self.Wait()
|
||||
if has_key(self, 'result')
|
||||
return self.result
|
||||
endif
|
||||
throw 'Copilot:E' . self.error.code . ': ' . self.error.message
|
||||
endfunction
|
||||
|
||||
function! s:RequestClient() dict abort
|
||||
return get(s:instances, self.client_id, v:null)
|
||||
endfunction
|
||||
|
||||
if !exists('s:id')
|
||||
let s:id = 0
|
||||
endif
|
||||
if !exists('s:progress_token_id')
|
||||
let s:progress_token_id = 0
|
||||
endif
|
||||
|
||||
function! s:SetUpRequest(instance, id, method, params, progress, ...) abort
|
||||
let request = {
|
||||
\ 'client_id': a:instance.id,
|
||||
\ 'id': a:id,
|
||||
\ 'method': a:method,
|
||||
\ 'params': a:params,
|
||||
\ 'Client': function('s:RequestClient'),
|
||||
\ 'Wait': function('s:RequestWait'),
|
||||
\ 'Await': function('s:RequestAwait'),
|
||||
\ 'Cancel': function('s:RequestCancel'),
|
||||
\ 'resolve': [],
|
||||
\ 'reject': [],
|
||||
\ 'progress': a:progress,
|
||||
\ 'status': 'running'}
|
||||
let args = a:000[2:-1]
|
||||
if len(args)
|
||||
if !empty(a:1)
|
||||
call add(request.resolve, { v -> call(a:1, [v] + args)})
|
||||
endif
|
||||
if !empty(a:2)
|
||||
call add(request.reject, { v -> call(a:2, [v] + args)})
|
||||
endif
|
||||
return request
|
||||
endif
|
||||
if a:0 && !empty(a:1)
|
||||
call add(request.resolve, a:1)
|
||||
endif
|
||||
if a:0 > 1 && !empty(a:2)
|
||||
call add(request.reject, a:2)
|
||||
endif
|
||||
return request
|
||||
endfunction
|
||||
|
||||
function! s:UrlEncode(str) abort
|
||||
return substitute(iconv(a:str, 'latin1', 'utf-8'),'[^A-Za-z0-9._~!$&''()*+,;=:@/-]','\="%".printf("%02X",char2nr(submatch(0)))','g')
|
||||
endfunction
|
||||
|
||||
let s:slash = exists('+shellslash') ? '\' : '/'
|
||||
function! s:UriFromBufnr(bufnr) abort
|
||||
let absolute = tr(bufname(a:bufnr), s:slash, '/')
|
||||
if absolute !~# '^\a\+:\|^/\|^$' && getbufvar(a:bufnr, 'buftype') =~# '^\%(nowrite\)\=$'
|
||||
let absolute = substitute(tr(getcwd(), s:slash, '/'), '/\=$', '/', '') . absolute
|
||||
endif
|
||||
return s:UriFromPath(absolute)
|
||||
endfunction
|
||||
|
||||
function! s:UriFromPath(absolute) abort
|
||||
let absolute = a:absolute
|
||||
if has('win32') && absolute =~# '^\a://\@!'
|
||||
return 'file:///' . strpart(absolute, 0, 2) . s:UrlEncode(strpart(absolute, 2))
|
||||
elseif absolute =~# '^/'
|
||||
return 'file://' . s:UrlEncode(absolute)
|
||||
elseif absolute =~# '^\a[[:alnum:].+-]*:\|^$'
|
||||
return absolute
|
||||
else
|
||||
return ''
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:BufferText(bufnr) abort
|
||||
return join(getbufline(a:bufnr, 1, '$'), "\n") . "\n"
|
||||
endfunction
|
||||
|
||||
let s:valid_request_key = '^\%(id\|method\|params\)$'
|
||||
function! s:SendRequest(instance, request, ...) abort
|
||||
if !has_key(a:instance, 'job') || get(a:instance, 'shutdown', a:request) isnot# a:request
|
||||
return s:RejectRequest(a:request, s:error_connection_inactive)
|
||||
endif
|
||||
let json = filter(copy(a:request), 'v:key =~# s:valid_request_key')
|
||||
if empty(s:Send(a:instance, json)) && has_key(a:request, 'id') && has_key(a:instance.requests, a:request.id)
|
||||
call s:RejectRequest(remove(a:instance.requests, a:request.id), {'code': -32099, 'message': 'Write failed'})
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:RegisterWorkspaceFolderForBuffer(instance, buf) abort
|
||||
let root = getbufvar(a:buf, 'workspace_folder')
|
||||
if type(root) != v:t_string
|
||||
return
|
||||
endif
|
||||
let root = s:UriFromPath(substitute(root, '[\/]$', '', ''))
|
||||
if empty(root) || has_key(a:instance.workspaceFolders, root)
|
||||
return
|
||||
endif
|
||||
let a:instance.workspaceFolders[root] = v:true
|
||||
call a:instance.Notify('workspace/didChangeWorkspaceFolders', {'event': {'added': [{'uri': root, 'name': fnamemodify(root, ':t')}], 'removed': []}})
|
||||
endfunction
|
||||
|
||||
function! s:PreprocessParams(instance, params) abort
|
||||
let bufnr = v:null
|
||||
for doc in filter([get(a:params, 'textDocument', {})], 'type(get(v:val, "uri", "")) == v:t_number')
|
||||
let bufnr = doc.uri
|
||||
call s:RegisterWorkspaceFolderForBuffer(a:instance, bufnr)
|
||||
call extend(doc, a:instance.Attach(bufnr))
|
||||
endfor
|
||||
let progress_tokens = []
|
||||
for key in keys(a:params)
|
||||
if key =~# 'Token$' && type(a:params[key]) == v:t_func
|
||||
let s:progress_token_id += 1
|
||||
let a:instance.progress[s:progress_token_id] = a:params[key]
|
||||
call add(progress_tokens, s:progress_token_id)
|
||||
let a:params[key] = s:progress_token_id
|
||||
endif
|
||||
endfor
|
||||
return [bufnr, progress_tokens]
|
||||
endfunction
|
||||
|
||||
function! s:VimAttach(bufnr) dict abort
|
||||
if !bufloaded(a:bufnr)
|
||||
return {'uri': '', 'version': 0}
|
||||
endif
|
||||
let bufnr = a:bufnr
|
||||
let doc = {
|
||||
\ 'uri': s:UriFromBufnr(bufnr),
|
||||
\ 'version': getbufvar(bufnr, 'changedtick', 0),
|
||||
\ 'languageId': getbufvar(bufnr, '&filetype'),
|
||||
\ }
|
||||
if has_key(self.open_buffers, bufnr) && (
|
||||
\ self.open_buffers[bufnr].uri !=# doc.uri ||
|
||||
\ self.open_buffers[bufnr].languageId !=# doc.languageId)
|
||||
call self.Notify('textDocument/didClose', {'textDocument': {'uri': self.open_buffers[bufnr].uri}})
|
||||
call remove(self.open_buffers, bufnr)
|
||||
endif
|
||||
if !has_key(self.open_buffers, bufnr)
|
||||
call self.Notify('textDocument/didOpen', {'textDocument': extend({'text': s:BufferText(bufnr)}, doc)})
|
||||
let self.open_buffers[bufnr] = doc
|
||||
else
|
||||
call self.Notify('textDocument/didChange', {
|
||||
\ 'textDocument': {'uri': doc.uri, 'version': doc.version},
|
||||
\ 'contentChanges': [{'text': s:BufferText(bufnr)}]})
|
||||
let self.open_buffers[bufnr].version = doc.version
|
||||
endif
|
||||
return doc
|
||||
endfunction
|
||||
|
||||
function! s:VimIsAttached(bufnr) dict abort
|
||||
return bufloaded(a:bufnr) && has_key(self.open_buffers, a:bufnr) ? v:true : v:false
|
||||
endfunction
|
||||
|
||||
function! s:VimRequest(method, params, ...) dict abort
|
||||
let s:id += 1
|
||||
let params = deepcopy(a:params)
|
||||
let [_, progress] = s:PreprocessParams(self, params)
|
||||
let request = call('s:SetUpRequest', [self, s:id, a:method, params, progress] + a:000)
|
||||
call self.AfterInitialized(function('s:SendRequest', [self, request]))
|
||||
let self.requests[s:id] = request
|
||||
return request
|
||||
endfunction
|
||||
|
||||
function! s:Call(method, params, ...) dict abort
|
||||
let request = call(self.Request, [a:method, a:params] + a:000)
|
||||
if a:0
|
||||
return request
|
||||
endif
|
||||
return request.Await()
|
||||
endfunction
|
||||
|
||||
function! s:Cancel(request) dict abort
|
||||
if has_key(self.requests, get(a:request, 'id', ''))
|
||||
call self.Notify('$/cancelRequest', {'id': a:request.id})
|
||||
call s:RejectRequest(remove(self.requests, a:request.id), s:error_canceled)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:RequestCancel() dict abort
|
||||
let instance = self.Client()
|
||||
if !empty(instance)
|
||||
call instance.Cancel(self)
|
||||
elseif get(self, 'status', '') ==# 'running'
|
||||
call s:RejectRequest(self, s:error_canceled)
|
||||
endif
|
||||
return self
|
||||
endfunction
|
||||
|
||||
function! s:DispatchMessage(instance, method, handler, id, params, ...) abort
|
||||
try
|
||||
let response = {'result': call(a:handler, [a:params, a:instance])}
|
||||
if response.result is# 0
|
||||
let response.result = v:null
|
||||
endif
|
||||
catch
|
||||
call copilot#logger#Exception('lsp.request.' . a:method)
|
||||
let response = {'error': {'code': -32000, 'message': v:exception}}
|
||||
endtry
|
||||
if a:id isnot# v:null
|
||||
call s:Send(a:instance, extend({'id': a:id}, response))
|
||||
endif
|
||||
if !has_key(s:notifications, a:method)
|
||||
return response
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:OnMessage(instance, body, ...) abort
|
||||
if !has_key(a:body, 'method')
|
||||
return s:OnResponse(a:instance, a:body)
|
||||
endif
|
||||
let request = a:body
|
||||
let id = get(request, 'id', v:null)
|
||||
let params = get(request, 'params', v:null)
|
||||
if has_key(a:instance.methods, request.method)
|
||||
return s:DispatchMessage(a:instance, request.method, a:instance.methods[request.method], id, params)
|
||||
elseif id isnot# v:null
|
||||
call s:Send(a:instance, {"id": id, "error": {"code": -32700, "message": "Method not found: " . request.method}})
|
||||
call copilot#logger#Debug('Unexpected request ' . request.method . ' called with ' . json_encode(params))
|
||||
elseif request.method !~# '^\$/'
|
||||
call copilot#logger#Debug('Unexpected notification ' . request.method . ' called with ' . json_encode(params))
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:OnResponse(instance, response, ...) abort
|
||||
let response = a:response
|
||||
let id = get(a:response, 'id', v:null)
|
||||
if !has_key(a:instance.requests, id)
|
||||
return
|
||||
endif
|
||||
let request = remove(a:instance.requests, id)
|
||||
for progress_token in request.progress
|
||||
if has_key(a:instance.progress, progress_token)
|
||||
call remove(a:instance.progress, progress_token)
|
||||
endif
|
||||
endfor
|
||||
if request.status !=# 'running'
|
||||
return
|
||||
endif
|
||||
if has_key(response, 'result')
|
||||
let request.waiting = {}
|
||||
let resolve = remove(request, 'resolve')
|
||||
call remove(request, 'reject')
|
||||
let request.status = 'success'
|
||||
let request.result = response.result
|
||||
for Cb in resolve
|
||||
let request.waiting[timer_start(0, function('s:Callback', [request, 'result', Cb]))] = 1
|
||||
endfor
|
||||
else
|
||||
call s:RejectRequest(request, response.error)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:OnErr(instance, ch, line, ...) abort
|
||||
if !has_key(a:instance, 'serverInfo')
|
||||
call copilot#logger#Bare('<-! ' . a:line)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:OnExit(instance, code, ...) abort
|
||||
let a:instance.exit_status = a:code
|
||||
if has_key(a:instance, 'job')
|
||||
call remove(a:instance, 'job')
|
||||
endif
|
||||
if has_key(a:instance, 'client_id')
|
||||
call remove(a:instance, 'client_id')
|
||||
endif
|
||||
let message = 'Process exited with status ' . a:code
|
||||
if a:code >= 18 && a:code < 100
|
||||
let message = 'Node.js too old. ' .
|
||||
\ (get(a:instance.node, 0, 'node') ==# 'node' ? 'Upgrade' : 'Change g:copilot_node_command') .
|
||||
\ ' to ' . a:code . '.x or newer'
|
||||
endif
|
||||
if !has_key(a:instance, 'serverInfo') && !has_key(a:instance, 'startup_error')
|
||||
let a:instance.startup_error = message
|
||||
endif
|
||||
for id in sort(keys(a:instance.requests), { a, b -> +a > +b })
|
||||
call s:RejectRequest(remove(a:instance.requests, id), s:error_exit)
|
||||
endfor
|
||||
if has_key(a:instance, 'after_initialized')
|
||||
let a:instance.AfterInitialized = function('copilot#util#Defer')
|
||||
for Fn in remove(a:instance, 'after_initialized')
|
||||
call copilot#util#Defer(Fn)
|
||||
endfor
|
||||
endif
|
||||
call copilot#util#Defer({ -> get(s:instances, a:instance.id) is# a:instance ? remove(s:instances, a:instance.id) : {} })
|
||||
if a:code == 0
|
||||
call copilot#logger#Info(message)
|
||||
else
|
||||
call copilot#logger#Warn(message)
|
||||
if !has_key(a:instance, 'kill')
|
||||
call copilot#util#Defer(function('s:Warn'), message)
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#client#LspInit(id, initialize_result) abort
|
||||
if !has_key(s:instances, a:id)
|
||||
return
|
||||
endif
|
||||
call s:PostInit(a:initialize_result, s:instances[a:id])
|
||||
endfunction
|
||||
|
||||
function! copilot#client#LspExit(id, code, signal) abort
|
||||
if !has_key(s:instances, a:id)
|
||||
return
|
||||
endif
|
||||
let instance = remove(s:instances, a:id)
|
||||
call s:OnExit(instance, a:code)
|
||||
endfunction
|
||||
|
||||
function! copilot#client#LspResponse(id, opts, ...) abort
|
||||
if !has_key(s:instances, a:id)
|
||||
return
|
||||
endif
|
||||
call s:OnResponse(s:instances[a:id], a:opts)
|
||||
endfunction
|
||||
|
||||
function! s:NvimAttach(bufnr) dict abort
|
||||
if !bufloaded(a:bufnr)
|
||||
return {'uri': '', 'version': 0}
|
||||
endif
|
||||
call luaeval('pcall(vim.lsp.buf_attach_client, _A[1], _A[2])', [a:bufnr, self.id])
|
||||
return luaeval('{uri = vim.uri_from_bufnr(_A), version = vim.lsp.util.buf_versions[_A]}', a:bufnr)
|
||||
endfunction
|
||||
|
||||
function! s:NvimIsAttached(bufnr) dict abort
|
||||
return bufloaded(a:bufnr) ? luaeval('vim.lsp.buf_is_attached(_A[1], _A[2])', [a:bufnr, self.id]) : v:false
|
||||
endfunction
|
||||
|
||||
function! s:NvimRequest(method, params, ...) dict abort
|
||||
let params = deepcopy(a:params)
|
||||
let [bufnr, progress] = s:PreprocessParams(self, params)
|
||||
let request = call('s:SetUpRequest', [self, v:null, a:method, params, progress] + a:000)
|
||||
call self.AfterInitialized(function('s:NvimDoRequest', [self, request, bufnr]))
|
||||
return request
|
||||
endfunction
|
||||
|
||||
function! s:NvimDoRequest(client, request, bufnr) abort
|
||||
let request = a:request
|
||||
if has_key(a:client, 'client_id') && !has_key(a:client, 'kill')
|
||||
let request.id = eval("v:lua.require'_copilot'.lsp_request(a:client.id, a:request.method, a:request.params, a:bufnr)")
|
||||
endif
|
||||
if request.id isnot# v:null
|
||||
let a:client.requests[request.id] = request
|
||||
else
|
||||
if has_key(a:client, 'client_id')
|
||||
call copilot#client#LspExit(a:client.client_id, -1, -1)
|
||||
endif
|
||||
call copilot#util#Defer(function('s:RejectRequest'), request, s:error_connection_inactive)
|
||||
endif
|
||||
return request
|
||||
endfunction
|
||||
|
||||
function! s:NvimClose() dict abort
|
||||
if !has_key(self, 'client_id')
|
||||
return
|
||||
endif
|
||||
let self.kill = v:true
|
||||
return luaeval('vim.lsp.get_client_by_id(_A).stop()', self.client_id)
|
||||
endfunction
|
||||
|
||||
function! s:NvimNotify(method, params) dict abort
|
||||
call self.AfterInitialized(function('s:NvimDoNotify', [self.client_id, a:method, a:params]))
|
||||
endfunction
|
||||
|
||||
function! s:NvimDoNotify(client_id, method, params) abort
|
||||
return eval("v:lua.require'_copilot'.rpc_notify(a:client_id, a:method, a:params)")
|
||||
endfunction
|
||||
|
||||
function! copilot#client#LspHandle(id, request) abort
|
||||
if !has_key(s:instances, a:id)
|
||||
return
|
||||
endif
|
||||
return s:OnMessage(s:instances[a:id], a:request)
|
||||
endfunction
|
||||
|
||||
let s:script_name = 'dist/language-server.js'
|
||||
function! s:Command() abort
|
||||
if !has('nvim-0.7') && v:version < 900
|
||||
return [[], [], 'Vim version too old']
|
||||
endif
|
||||
let script = get(g:, 'copilot_command', '')
|
||||
if type(script) == type('')
|
||||
let script = [expand(script)]
|
||||
endif
|
||||
if empty(script) || !filereadable(script[0])
|
||||
let script = [s:root . '/' . s:script_name]
|
||||
if !filereadable(script[0])
|
||||
return [[], [], 'Could not find ' . s:script_name . ' (bad install?)']
|
||||
endif
|
||||
elseif script[0] !~# '\.js$'
|
||||
return [[], script + ['--stdio'], '']
|
||||
endif
|
||||
let node = get(g:, 'copilot_node_command', '')
|
||||
if empty(node)
|
||||
let node = ['node']
|
||||
elseif type(node) == type('')
|
||||
let node = [expand(node)]
|
||||
endif
|
||||
if !executable(get(node, 0, ''))
|
||||
if get(node, 0, '') ==# 'node'
|
||||
return [[], [], 'Node.js not found in PATH']
|
||||
else
|
||||
return [[], [], 'Node.js executable `' . get(node, 0, '') . "' not found"]
|
||||
endif
|
||||
endif
|
||||
return [node, script + ['--stdio'], '']
|
||||
endfunction
|
||||
|
||||
function! s:UrlDecode(str) abort
|
||||
return substitute(a:str, '%\(\x\x\)', '\=iconv(nr2char("0x".submatch(1)), "utf-8", "latin1")', 'g')
|
||||
endfunction
|
||||
|
||||
function! copilot#client#EditorInfo() abort
|
||||
if !exists('s:editor_version')
|
||||
if has('nvim')
|
||||
let s:editor_version = matchstr(execute('version'), 'NVIM v\zs[^[:space:]]\+')
|
||||
else
|
||||
let s:editor_version = (v:version / 100) . '.' . (v:version % 100) . (exists('v:versionlong') ? printf('.%04d', v:versionlong % 10000) : '')
|
||||
endif
|
||||
endif
|
||||
return {'name': has('nvim') ? 'Neovim': 'Vim', 'version': s:editor_version}
|
||||
endfunction
|
||||
|
||||
function! copilot#client#EditorPluginInfo() abort
|
||||
return {'name': 'copilot.vim', 'version': s:plugin_version}
|
||||
endfunction
|
||||
|
||||
function! copilot#client#Settings() abort
|
||||
let settings = {
|
||||
\ 'http': {
|
||||
\ 'proxy': get(g:, 'copilot_proxy', v:null),
|
||||
\ 'proxyStrictSSL': get(g:, 'copilot_proxy_strict_ssl', v:null)},
|
||||
\ 'github-enterprise': {'uri': get(g:, 'copilot_auth_provider_url', v:null)},
|
||||
\ }
|
||||
if type(settings.http.proxy) ==# v:t_string && settings.http.proxy =~# '^[^/]\+$'
|
||||
let settings.http.proxy = 'http://' . settings.http.proxy
|
||||
endif
|
||||
if type(get(g:, 'copilot_settings')) == v:t_dict
|
||||
call extend(settings, g:copilot_settings)
|
||||
endif
|
||||
return settings
|
||||
endfunction
|
||||
|
||||
function! s:PostInit(result, instance) abort
|
||||
let a:instance.serverInfo = get(a:result, 'serverInfo', {})
|
||||
if !has_key(a:instance, 'node_version') && has_key(a:result.serverInfo, 'nodeVersion')
|
||||
let a:instance.node_version = a:result.serverInfo.nodeVersion
|
||||
endif
|
||||
let a:instance.AfterInitialized = function('copilot#util#Defer')
|
||||
for Fn in remove(a:instance, 'after_initialized')
|
||||
call copilot#util#Defer(Fn)
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! s:InitializeResult(result, instance) abort
|
||||
call s:Send(a:instance, {'method': 'initialized', 'params': {}})
|
||||
call s:PostInit(a:result, a:instance)
|
||||
endfunction
|
||||
|
||||
function! s:InitializeError(error, instance) abort
|
||||
if !has_key(a:instance, 'startup_error')
|
||||
let a:instance.startup_error = 'Unexpected error E' . a:error.code . ' initializing language server: ' . a:error.message
|
||||
call a:instance.Close()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:StartupError() dict abort
|
||||
while (has_key(self, 'job') || has_key(self, 'client_id')) && !has_key(self, 'startup_error') && !has_key(self, 'serverInfo')
|
||||
sleep 10m
|
||||
endwhile
|
||||
if has_key(self, 'serverInfo')
|
||||
return ''
|
||||
else
|
||||
return get(self, 'startup_error', 'Something unexpected went wrong spawning the language server')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:StatusNotification(params, instance) abort
|
||||
let a:instance.status = a:params
|
||||
endfunction
|
||||
|
||||
function! s:Nop(...) abort
|
||||
return v:null
|
||||
endfunction
|
||||
|
||||
function! s:False(...) abort
|
||||
return v:false
|
||||
endfunction
|
||||
|
||||
function! s:Progress(params, instance) abort
|
||||
if has_key(a:instance.progress, a:params.token)
|
||||
call a:instance.progress[a:params.token](a:params.value)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
let s:notifications = {
|
||||
\ '$/progress': function('s:Progress'),
|
||||
\ 'featureFlagsNotification': function('s:Nop'),
|
||||
\ 'statusNotification': function('s:StatusNotification'),
|
||||
\ 'window/logMessage': function('copilot#handlers#window_logMessage'),
|
||||
\ }
|
||||
|
||||
let s:vim_handlers = {
|
||||
\ 'window/showMessageRequest': function('copilot#handlers#window_showMessageRequest'),
|
||||
\ 'window/showDocument': function('copilot#handlers#window_showDocument'),
|
||||
\ }
|
||||
|
||||
let s:vim_capabilities = {
|
||||
\ 'workspace': {'workspaceFolders': v:true},
|
||||
\ 'window': {'showDocument': {'support': v:true}},
|
||||
\ }
|
||||
|
||||
function! copilot#client#New(...) abort
|
||||
let opts = a:0 ? a:1 : {}
|
||||
let instance = {'requests': {},
|
||||
\ 'progress': {},
|
||||
\ 'workspaceFolders': {},
|
||||
\ 'after_initialized': [],
|
||||
\ 'status': {'status': 'Starting', 'message': ''},
|
||||
\ 'AfterInitialized': function('s:AfterInitialized'),
|
||||
\ 'Close': function('s:Nop'),
|
||||
\ 'Notify': function('s:False'),
|
||||
\ 'Request': function('s:VimRequest'),
|
||||
\ 'Attach': function('s:Nop'),
|
||||
\ 'IsAttached': function('s:False'),
|
||||
\ 'Call': function('s:Call'),
|
||||
\ 'Cancel': function('s:Cancel'),
|
||||
\ 'StartupError': function('s:StartupError'),
|
||||
\ }
|
||||
let instance.methods = copy(s:notifications)
|
||||
let [node, argv, command_error] = s:Command()
|
||||
if !empty(command_error)
|
||||
let instance.id = -1
|
||||
let instance.startup_error = command_error
|
||||
call copilot#logger#Error(command_error)
|
||||
return instance
|
||||
endif
|
||||
let instance.node = node
|
||||
let command = node + argv
|
||||
let opts = {}
|
||||
let opts.initializationOptions = {
|
||||
\ 'editorInfo': copilot#client#EditorInfo(),
|
||||
\ 'editorPluginInfo': copilot#client#EditorPluginInfo(),
|
||||
\ }
|
||||
let opts.workspaceFolders = []
|
||||
let settings = extend(copilot#client#Settings(), get(opts, 'editorConfiguration', {}))
|
||||
if type(get(g:, 'copilot_workspace_folders')) == v:t_list
|
||||
for folder in g:copilot_workspace_folders
|
||||
if type(folder) == v:t_string && !empty(folder) && folder !~# '\*\*\|^/$'
|
||||
for path in glob(folder . '/', 0, 1)
|
||||
let uri = s:UriFromPath(substitute(path, '[\/]*$', '', ''))
|
||||
call add(opts.workspaceFolders, {'uri': uri, 'name': fnamemodify(uri, ':t')})
|
||||
endfor
|
||||
elseif type(folder) == v:t_dict && has_key(v:t_dict, 'uri') && !empty(folder.uri) && has_key(folder, 'name')
|
||||
call add(opts.workspaceFolders, folder)
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
for folder in opts.workspaceFolders
|
||||
let instance.workspaceFolders[folder.uri] = v:true
|
||||
endfor
|
||||
if has('nvim')
|
||||
call extend(instance, {
|
||||
\ 'Close': function('s:NvimClose'),
|
||||
\ 'Notify': function('s:NvimNotify'),
|
||||
\ 'Request': function('s:NvimRequest'),
|
||||
\ 'Attach': function('s:NvimAttach'),
|
||||
\ 'IsAttached': function('s:NvimIsAttached'),
|
||||
\ })
|
||||
let instance.client_id = eval("v:lua.require'_copilot'.lsp_start_client(command, keys(instance.methods), opts, settings)")
|
||||
let instance.id = instance.client_id
|
||||
else
|
||||
call extend(instance, {
|
||||
\ 'Close': function('s:VimClose'),
|
||||
\ 'Notify': function('s:VimNotify'),
|
||||
\ 'Attach': function('s:VimAttach'),
|
||||
\ 'IsAttached': function('s:VimIsAttached'),
|
||||
\ })
|
||||
let state = {'headers': {}, 'mode': 'headers', 'buffer': ''}
|
||||
let instance.open_buffers = {}
|
||||
let instance.methods = extend(s:vim_handlers, instance.methods)
|
||||
let instance.job = job_start(command, {
|
||||
\ 'cwd': copilot#job#Cwd(),
|
||||
\ 'noblock': 1,
|
||||
\ 'stoponexit': '',
|
||||
\ 'in_mode': 'lsp',
|
||||
\ 'out_mode': 'lsp',
|
||||
\ 'out_cb': { j, d -> copilot#util#Defer(function('s:OnMessage'), instance, d) },
|
||||
\ 'err_cb': function('s:OnErr', [instance]),
|
||||
\ 'exit_cb': { j, d -> copilot#util#Defer(function('s:OnExit'), instance, d) },
|
||||
\ })
|
||||
let instance.id = job_info(instance.job).process
|
||||
let opts.capabilities = s:vim_capabilities
|
||||
let opts.processId = getpid()
|
||||
let request = instance.Request('initialize', opts, function('s:InitializeResult'), function('s:InitializeError'), instance)
|
||||
call call(remove(instance.after_initialized, 0), [])
|
||||
call instance.Notify('workspace/didChangeConfiguration', {'settings': settings})
|
||||
endif
|
||||
let s:instances[instance.id] = instance
|
||||
return instance
|
||||
endfunction
|
||||
|
||||
function! copilot#client#Cancel(request) abort
|
||||
if type(a:request) == type({}) && has_key(a:request, 'Cancel')
|
||||
call a:request.Cancel()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:Callback(request, type, callback, timer) abort
|
||||
call remove(a:request.waiting, a:timer)
|
||||
if has_key(a:request, a:type)
|
||||
call a:callback(a:request[a:type])
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#client#Result(request, callback) abort
|
||||
if has_key(a:request, 'resolve')
|
||||
call add(a:request.resolve, a:callback)
|
||||
elseif has_key(a:request, 'result')
|
||||
let a:request.waiting[timer_start(0, function('s:Callback', [a:request, 'result', a:callback]))] = 1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#client#Error(request, callback) abort
|
||||
if has_key(a:request, 'reject')
|
||||
call add(a:request.reject, a:callback)
|
||||
elseif has_key(a:request, 'error')
|
||||
let a:request.waiting[timer_start(0, function('s:Callback', [a:request, 'error', a:callback]))] = 1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:CloseBuffer(bufnr) abort
|
||||
for instance in values(s:instances)
|
||||
try
|
||||
if has_key(instance, 'job') && has_key(instance.open_buffers, a:bufnr)
|
||||
let buffer = remove(instance.open_buffers, a:bufnr)
|
||||
call instance.Notify('textDocument/didClose', {'textDocument': {'uri': buffer.uri}})
|
||||
endif
|
||||
catch
|
||||
call copilot#logger#Exception()
|
||||
endtry
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
augroup copilot_close
|
||||
autocmd!
|
||||
if !has('nvim')
|
||||
autocmd BufUnload * call s:CloseBuffer(+expand('<abuf>'))
|
||||
endif
|
||||
augroup END
|
@ -1,111 +0,0 @@
|
||||
scriptencoding utf-8
|
||||
|
||||
let s:slash = exists('+shellslash') ? '\' : '/'
|
||||
|
||||
function! copilot#doc#UTF16Width(str) abort
|
||||
return strchars(substitute(a:str, "\\%#=2[^\u0001-\uffff]", " ", 'g'))
|
||||
endfunction
|
||||
|
||||
if exists('*utf16idx')
|
||||
|
||||
function! copilot#doc#UTF16ToByteIdx(str, utf16_idx) abort
|
||||
return byteidx(a:str, a:utf16_idx, 1)
|
||||
endfunction
|
||||
|
||||
elseif has('nvim')
|
||||
|
||||
function! copilot#doc#UTF16ToByteIdx(str, utf16_idx) abort
|
||||
try
|
||||
return v:lua.vim.str_byteindex(a:str, a:utf16_idx, 1)
|
||||
catch /^Vim(return):E5108:/
|
||||
return -1
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
else
|
||||
|
||||
function! copilot#doc#UTF16ToByteIdx(str, utf16_idx) abort
|
||||
if copilot#doc#UTF16Width(a:str) < a:utf16_idx
|
||||
return -1
|
||||
endif
|
||||
let end_offset = len(a:str)
|
||||
while copilot#doc#UTF16Width(strpart(a:str, 0, end_offset)) > a:utf16_idx && end_offset > 0
|
||||
let end_offset -= 1
|
||||
endwhile
|
||||
return end_offset
|
||||
endfunction
|
||||
|
||||
endif
|
||||
|
||||
|
||||
let s:language_normalization_map = {
|
||||
\ "bash": "shellscript",
|
||||
\ "bst": "bibtex",
|
||||
\ "cs": "csharp",
|
||||
\ "cuda": "cuda-cpp",
|
||||
\ "dosbatch": "bat",
|
||||
\ "dosini": "ini",
|
||||
\ "gitcommit": "git-commit",
|
||||
\ "gitrebase": "git-rebase",
|
||||
\ "make": "makefile",
|
||||
\ "objc": "objective-c",
|
||||
\ "objcpp": "objective-cpp",
|
||||
\ "ps1": "powershell",
|
||||
\ "raku": "perl6",
|
||||
\ "sh": "shellscript",
|
||||
\ "text": "plaintext",
|
||||
\ }
|
||||
function! copilot#doc#LanguageForFileType(filetype) abort
|
||||
let filetype = substitute(a:filetype, '\..*', '', '')
|
||||
return get(s:language_normalization_map, empty(filetype) ? "text" : filetype, filetype)
|
||||
endfunction
|
||||
|
||||
function! s:RelativePath(absolute) abort
|
||||
if exists('b:copilot_relative_path')
|
||||
return b:copilot_relative_path
|
||||
elseif exists('b:copilot_root')
|
||||
let root = b:copilot_root
|
||||
elseif len(get(b:, 'projectionist', {}))
|
||||
let root = sort(keys(b:projectionist), { a, b -> a < b })[0]
|
||||
else
|
||||
let root = getcwd()
|
||||
endif
|
||||
let root = tr(root, s:slash, '/') . '/'
|
||||
if strpart(tr(a:absolute, 'A-Z', 'a-z'), 0, len(root)) ==# tr(root, 'A-Z', 'a-z')
|
||||
return strpart(a:absolute, len(root))
|
||||
else
|
||||
return fnamemodify(a:absolute, ':t')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! copilot#doc#Get() abort
|
||||
let absolute = tr(@%, s:slash, '/')
|
||||
if absolute !~# '^\a\+:\|^/\|^$' && &buftype =~# '^\%(nowrite\)\=$'
|
||||
let absolute = substitute(tr(getcwd(), s:slash, '/'), '/\=$', '/', '') . absolute
|
||||
endif
|
||||
let doc = {
|
||||
\ 'uri': bufnr(''),
|
||||
\ 'version': getbufvar('', 'changedtick'),
|
||||
\ 'relativePath': s:RelativePath(absolute),
|
||||
\ 'insertSpaces': &expandtab ? v:true : v:false,
|
||||
\ 'tabSize': shiftwidth(),
|
||||
\ 'indentSize': shiftwidth(),
|
||||
\ }
|
||||
let line = getline('.')
|
||||
let col_byte = col('.') - (mode() =~# '^[iR]' || empty(line))
|
||||
let col_utf16 = copilot#doc#UTF16Width(strpart(line, 0, col_byte))
|
||||
let doc.position = {'line': line('.') - 1, 'character': col_utf16}
|
||||
return doc
|
||||
endfunction
|
||||
|
||||
function! copilot#doc#Params(...) abort
|
||||
let extra = a:0 ? a:1 : {}
|
||||
let params = extend({'doc': extend(copilot#doc#Get(), get(extra, 'doc', {}))}, extra, 'keep')
|
||||
let params.textDocument = {
|
||||
\ 'uri': params.doc.uri,
|
||||
\ 'version': params.doc.version,
|
||||
\ 'relativePath': params.doc.relativePath,
|
||||
\ }
|
||||
let params.position = params.doc.position
|
||||
return params
|
||||
endfunction
|
@ -0,0 +1,31 @@
|
||||
function! copilot#handlers#window_logMessage(params, ...) abort
|
||||
call copilot#logger#Raw(get(a:params, 'type', 6), get(a:params, 'message', ''))
|
||||
endfunction
|
||||
|
||||
function! copilot#handlers#window_showMessageRequest(params, ...) abort
|
||||
let choice = inputlist([a:params.message . "\n\nRequest Actions:"] +
|
||||
\ map(copy(get(a:params, 'actions', [])), { i, v -> (i + 1) . '. ' . v.title}))
|
||||
return choice > 0 ? get(a:params.actions, choice - 1, v:null) : v:null
|
||||
endfunction
|
||||
|
||||
function! s:BrowserCallback(into, code) abort
|
||||
let a:into.code = a:code
|
||||
endfunction
|
||||
|
||||
function! copilot#handlers#window_showDocument(params, ...) abort
|
||||
echo a:params.uri
|
||||
if empty(get(a:params, 'external'))
|
||||
return {'success': v:false}
|
||||
endif
|
||||
let browser = copilot#Browser()
|
||||
if empty(browser)
|
||||
return {'success': v:false}
|
||||
endif
|
||||
let status = {}
|
||||
call copilot#job#Stream(browser + [a:params.uri], v:null, v:null, function('s:BrowserCallback', [status]))
|
||||
let time = reltime()
|
||||
while empty(status) && reltimefloat(reltime(time)) < 1
|
||||
sleep 10m
|
||||
endwhile
|
||||
return {'success': get(status, 'code') ? v:false : v:true}
|
||||
endfunction
|
@ -6,35 +6,59 @@ if !exists('s:log_file')
|
||||
endtry
|
||||
endif
|
||||
|
||||
function! copilot#logger#File() abort
|
||||
return s:log_file
|
||||
let s:logs = []
|
||||
|
||||
function! copilot#logger#BufReadCmd() abort
|
||||
try
|
||||
setlocal modifiable noreadonly
|
||||
silent call deletebufline('', 1, '$')
|
||||
if !empty(s:logs)
|
||||
call setline(1, s:logs)
|
||||
endif
|
||||
finally
|
||||
setlocal buftype=nofile bufhidden=wipe nobuflisted nomodified nomodifiable
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
let s:level_prefixes = ['', '[ERROR] ', '[WARN] ', '[INFO] ', '[DEBUG] ', '[DEBUG] ']
|
||||
|
||||
function! copilot#logger#Raw(level, message) abort
|
||||
if $COPILOT_AGENT_VERBOSE !~# '^\%(1\|true\)$' && a:level < 1
|
||||
return
|
||||
endif
|
||||
let lines = type(a:message) == v:t_list ? copy(a:message) : split(a:message, "\n", 1)
|
||||
let lines[0] = strftime('[%Y-%m-%d %H:%M:%S] ') . get(s:level_prefixes, a:level, '[UNKNOWN] ') . get(lines, 0, '')
|
||||
try
|
||||
if !filewritable(s:log_file)
|
||||
return
|
||||
endif
|
||||
call map(lines, { k, L -> type(L) == v:t_func ? call(L, []) : L })
|
||||
call writefile(lines, s:log_file, 'a')
|
||||
call extend(s:logs, lines)
|
||||
let overflow = len(s:logs) - get(g:, 'copilot_log_history', 10000)
|
||||
if overflow > 0
|
||||
call remove(s:logs, 0, overflow - 1)
|
||||
endif
|
||||
let bufnr = bufnr('copilot:///log')
|
||||
if bufnr > 0 && bufloaded(bufnr)
|
||||
call setbufvar(bufnr, '&modifiable', 1)
|
||||
call setbufline(bufnr, 1, s:logs)
|
||||
call setbufvar(bufnr, '&modifiable', 0)
|
||||
for winid in win_findbuf(bufnr)
|
||||
if has('nvim') && winid != win_getid()
|
||||
call nvim_win_set_cursor(winid, [len(s:logs), 0])
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
catch
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! copilot#logger#Trace(...) abort
|
||||
call copilot#logger#Raw(-1, a:000)
|
||||
endfunction
|
||||
|
||||
function! copilot#logger#Debug(...) abort
|
||||
call copilot#logger#Raw(0, a:000)
|
||||
if empty(get(g:, 'copilot_debug'))
|
||||
return
|
||||
endif
|
||||
call copilot#logger#Raw(4, a:000)
|
||||
endfunction
|
||||
|
||||
function! copilot#logger#Info(...) abort
|
||||
call copilot#logger#Raw(1, a:000)
|
||||
call copilot#logger#Raw(3, a:000)
|
||||
endfunction
|
||||
|
||||
function! copilot#logger#Warn(...) abort
|
||||
@ -42,18 +66,22 @@ function! copilot#logger#Warn(...) abort
|
||||
endfunction
|
||||
|
||||
function! copilot#logger#Error(...) abort
|
||||
call copilot#logger#Raw(3, a:000)
|
||||
call copilot#logger#Raw(1, a:000)
|
||||
endfunction
|
||||
|
||||
function! copilot#logger#Bare(...) abort
|
||||
call copilot#logger#Raw(0, a:000)
|
||||
endfunction
|
||||
|
||||
function! copilot#logger#Exception(...) abort
|
||||
if !empty(v:exception) && v:exception !=# 'Vim:Interrupt'
|
||||
call copilot#logger#Error('Exception: ' . v:exception . ' @ ' . v:throwpoint)
|
||||
let agent = copilot#RunningAgent()
|
||||
if !empty(agent)
|
||||
let client = copilot#RunningClient()
|
||||
if !empty(client)
|
||||
let [_, type, code, message; __] = matchlist(v:exception, '^\%(\(^[[:alnum:]_#]\+\)\%((\a\+)\)\=\%(\(:E-\=\d\+\)\)\=:\s*\)\=\(.*\)$')
|
||||
let stacklines = []
|
||||
for frame in split(substitute(v:throwpoint, ', \S\+ \(\d\+\)$', '[\1]', ''), '\.\@<!\.\.\.\@!')
|
||||
let fn_line = matchlist(frame, '^\%(function \)\=\(\S\+\)\[\(\d+\)\]$')
|
||||
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 "\*"$'
|
||||
@ -64,14 +92,14 @@ function! copilot#logger#Exception(...) abort
|
||||
call add(stacklines, {'function': '[redacted]'})
|
||||
endif
|
||||
endfor
|
||||
return agent.Request('telemetry/exception', {
|
||||
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
|
||||
|
@ -6,29 +6,35 @@ endif
|
||||
|
||||
let s:separator = repeat('─', 72)
|
||||
|
||||
function! s:Solutions(state) abort
|
||||
return sort(values(get(a:state, 'solutions', {})), { a, b -> a.score < b.score })
|
||||
endfunction
|
||||
|
||||
function! s:Render(panel_id) abort
|
||||
let bufnr = bufnr('^' . a:panel_id . '$')
|
||||
let state = getbufvar(bufnr, 'copilot_panel')
|
||||
if !bufloaded(bufnr) || type(state) != v:t_dict
|
||||
function! s:Render(state) abort
|
||||
let bufnr = bufnr('^' . a:state.panel . '$')
|
||||
let state = a:state
|
||||
if !bufloaded(bufnr)
|
||||
return
|
||||
endif
|
||||
let sorted = s:Solutions(state)
|
||||
if !empty(get(state, 'status', ''))
|
||||
let lines = ['Error: ' . state.status]
|
||||
let sorted = a:state.items
|
||||
if !empty(get(a:state, 'error'))
|
||||
let lines = ['Error: ' . a:state.error.message]
|
||||
let sorted = []
|
||||
elseif get(a:state, 'percentage') == 100
|
||||
let lines = ['Synthesized ' . (len(sorted) == 1 ? '1 completion' : len(sorted) . ' completions')]
|
||||
else
|
||||
let target = get(state, 'count_target', '?')
|
||||
let received = has_key(state, 'status') ? target : len(sorted)
|
||||
let lines = ['Synthesiz' . (has_key(state, 'status') ? 'ed ' : 'ing ') . received . '/' . target . ' solutions (Duplicates hidden)']
|
||||
let lines = [substitute('Synthesizing ' . matchstr(get(a:state, 'message', ''), '\d\+\%(/\d\+\)\=') . ' completions', ' \+', ' ', 'g')]
|
||||
endif
|
||||
if len(sorted)
|
||||
call add(lines, 'Press <CR> on a solution to accept')
|
||||
call add(lines, 'Press <CR> on a completion to accept')
|
||||
endif
|
||||
for solution in sorted
|
||||
let lines += [s:separator] + split(solution.displayText, "\n", 1)
|
||||
let leads = {}
|
||||
for item in sorted
|
||||
let insert = split(item.insertText, "\r\n\\=\\|\n", 1)
|
||||
let insert[0] = strpart(a:state.line, 0, copilot#util#UTF16ToByteIdx(a:state.line, item.range.start.character)) . insert[0]
|
||||
let lines += [s:separator] + insert
|
||||
if !has_key(leads, string(item.range.start))
|
||||
let match = insert[0 : a:state.position.line - item.range.start.line]
|
||||
let match[-1] = strpart(match[-1], 0, copilot#util#UTF16ToByteIdx(match[-1], a:state.position.character))
|
||||
call map(match, { k, v -> escape(v, '][^$.*\~') })
|
||||
let leads[string(item.range.start)] = join(match, '\n')
|
||||
endif
|
||||
endfor
|
||||
try
|
||||
call setbufvar(bufnr, '&modifiable', 1)
|
||||
@ -36,64 +42,61 @@ function! s:Render(panel_id) abort
|
||||
call setbufline(bufnr, 1, lines)
|
||||
finally
|
||||
call setbufvar(bufnr, '&modifiable', 0)
|
||||
call setbufvar(bufnr, '&readonly', 1)
|
||||
endtry
|
||||
call clearmatches()
|
||||
call matchadd('CopilotSuggestion', '\C^' . s:separator . '\n\zs\%(' . join(sort(values(leads), { a, b -> len(b) - len(a) }), '\|') . '\)', 10, 4)
|
||||
endfunction
|
||||
|
||||
function! copilot#panel#Solution(params, ...) abort
|
||||
let state = getbufvar('^' . a:params.panelId . '$', 'copilot_panel')
|
||||
if !bufloaded(a:params.panelId) || type(state) != v:t_dict
|
||||
return
|
||||
endif
|
||||
let state.solutions[a:params.solutionId] = a:params
|
||||
call s:Render(a:params.panelId)
|
||||
function! s:PartialResult(state, value) abort
|
||||
let items = type(a:value) == v:t_list ? a:value : a:value.items
|
||||
call extend(a:state.items, items)
|
||||
call s:Render(a:state)
|
||||
endfunction
|
||||
|
||||
function! copilot#panel#SolutionsDone(params, ...) abort
|
||||
let state = getbufvar('^' . a:params.panelId . '$', 'copilot_panel')
|
||||
if !bufloaded(a:params.panelId) || type(state) != v:t_dict
|
||||
call copilot#logger#Debug('SolutionsDone: ' . a:params.panelId)
|
||||
return
|
||||
function! s:WorkDone(state, value) abort
|
||||
if has_key(a:value, 'message')
|
||||
let a:state.message = a:value.message
|
||||
endif
|
||||
if has_key(a:value, 'percentage')
|
||||
let a:state.percentage = a:value.percentage
|
||||
call s:Render(a:state)
|
||||
endif
|
||||
let state.status = get(a:params, 'message', '')
|
||||
call s:Render(a:params.panelId)
|
||||
endfunction
|
||||
|
||||
function! copilot#panel#Accept(...) abort
|
||||
let state = get(b:, 'copilot_panel', {})
|
||||
let solutions = s:Solutions(state)
|
||||
if empty(solutions)
|
||||
if empty(state.items)
|
||||
return ''
|
||||
endif
|
||||
if !has_key(state, 'bufnr') || !bufloaded(get(state, 'bufnr', -1))
|
||||
return "echoerr 'Buffer was closed'"
|
||||
endif
|
||||
let at = a:0 ? a:1 : line('.')
|
||||
let solution_index = 0
|
||||
let index = 0
|
||||
for lnum in range(1, at)
|
||||
if getline(lnum) ==# s:separator
|
||||
let solution_index += 1
|
||||
let index += 1
|
||||
endif
|
||||
endfor
|
||||
if solution_index > 0 && solution_index <= len(solutions)
|
||||
let solution = solutions[solution_index - 1]
|
||||
let lnum = solution.range.start.line + 1
|
||||
if index > 0 && index <= len(state.items)
|
||||
let item = state.items[index - 1]
|
||||
let lnum = item.range.start.line + 1
|
||||
if getbufline(state.bufnr, lnum) !=# [state.line]
|
||||
return 'echoerr "Buffer has changed since synthesizing solution"'
|
||||
return 'echoerr "Buffer has changed since synthesizing completion"'
|
||||
endif
|
||||
let lines = split(solution.displayText, "\n", 1)
|
||||
let old_first = getline(solution.range.start.line + 1)
|
||||
let lines[0] = strpart(old_first, 0, copilot#doc#UTF16ToByteIdx(old_first, solution.range.start.character)) . lines[0]
|
||||
let old_last = getline(solution.range.end.line + 1)
|
||||
let lines[-1] .= strpart(old_last, copilot#doc#UTF16ToByteIdx(old_last, solution.range.start.character))
|
||||
call setbufline(state.bufnr, solution.range.start.line + 1, lines[0])
|
||||
call appendbufline(state.bufnr, solution.range.start.line + 1, lines[1:-1])
|
||||
call copilot#Request('notifyAccepted', {'uuid': solution.solutionId})
|
||||
let lines = split(item.insertText, "\n", 1)
|
||||
let old_first = getbufline(state.bufnr, item.range.start.line + 1)[0]
|
||||
let lines[0] = strpart(old_first, 0, copilot#util#UTF16ToByteIdx(old_first, item.range.start.character)) . lines[0]
|
||||
let old_last = getbufline(state.bufnr, item.range.end.line + 1)[0]
|
||||
let lines[-1] .= strpart(old_last, copilot#util#UTF16ToByteIdx(old_last, item.range.end.character))
|
||||
call deletebufline(state.bufnr, item.range.start.line + 1, item.range.end.line + 1)
|
||||
call appendbufline(state.bufnr, item.range.start.line, lines)
|
||||
call copilot#Request('workspace/executeCommand', item.command)
|
||||
bwipeout
|
||||
let win = bufwinnr(state.bufnr)
|
||||
if win > 0
|
||||
exe win . 'wincmd w'
|
||||
exe solution.range.start.line + len(lines)
|
||||
exe item.range.start.line + len(lines)
|
||||
if state.was_insert
|
||||
startinsert!
|
||||
else
|
||||
@ -107,49 +110,58 @@ endfunction
|
||||
function! s:Initialize(state) abort
|
||||
let &l:filetype = 'copilot' . (empty(a:state.filetype) ? '' : '.' . a:state.filetype)
|
||||
let &l:tabstop = a:state.tabstop
|
||||
call clearmatches()
|
||||
call matchadd('CopilotSuggestion', '\C^' . s:separator . '\n\zs' . escape(a:state.line, '][^$.*\~'), 10, 4)
|
||||
nmap <buffer><script> <CR> <Cmd>exe copilot#panel#Accept()<CR>
|
||||
nmap <buffer><script> [[ <Cmd>call search('^─\{9,}\n.', 'bWe')<CR>
|
||||
nmap <buffer><script> ]] <Cmd>call search('^─\{9,}\n.', 'We')<CR>
|
||||
endfunction
|
||||
|
||||
function! s:BufReadCmd() abort
|
||||
setlocal bufhidden=wipe buftype=nofile nobuflisted readonly nomodifiable
|
||||
setlocal bufhidden=wipe buftype=nofile nobuflisted nomodifiable
|
||||
let state = get(b:, 'copilot_panel')
|
||||
if type(state) != v:t_dict
|
||||
return
|
||||
endif
|
||||
call s:Initialize(state)
|
||||
call s:Render(expand('<amatch>'))
|
||||
call s:Render(state)
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
function! s:Result(state, result) abort
|
||||
let a:state.percentage = 100
|
||||
call s:PartialResult(a:state, a:result)
|
||||
endfunction
|
||||
|
||||
function! s:Error(state, error) abort
|
||||
let a:state.error = a:error
|
||||
call s:Render(a:state)
|
||||
endfunction
|
||||
|
||||
function! copilot#panel#Open(opts) abort
|
||||
let s:panel_id += 1
|
||||
let state = {'solutions': {}, 'filetype': &filetype, 'line': getline('.'), 'bufnr': bufnr(''), 'tabstop': &tabstop}
|
||||
let bufname = 'copilot:///' . s:panel_id
|
||||
let params = copilot#doc#Params({'panelId': bufname})
|
||||
let state.was_insert = mode() =~# '^[iR]'
|
||||
let state = {'items': [], 'filetype': &filetype, 'was_insert': mode() =~# '^[iR]', 'bufnr': bufnr(''), 'tabstop': &tabstop}
|
||||
let state.panel = 'copilot:///panel/' . s:panel_id
|
||||
if state.was_insert
|
||||
let state.position = copilot#util#AppendPosition()
|
||||
stopinsert
|
||||
else
|
||||
let params.doc.position.character = copilot#doc#UTF16Width(state.line)
|
||||
let params.position.character = params.doc.position.character
|
||||
let state.position = {'line': a:opts.line1 >= 1 ? a:opts.line1 - 1 : 0, 'character': copilot#util#UTF16Width(getline('.'))}
|
||||
endif
|
||||
let response = copilot#Request('getPanelCompletions', params).Wait()
|
||||
if response.status ==# 'error'
|
||||
return 'echoerr ' . string(response.error.message)
|
||||
endif
|
||||
let state.count_target = response.result.solutionCountTarget
|
||||
exe substitute(a:opts.mods, '\C\<tab\>', '-tab', 'g') 'keepalt split' bufname
|
||||
let state.line = getline(state.position.line + 1)
|
||||
let params = {
|
||||
\ 'textDocument': {'uri': state.bufnr},
|
||||
\ 'position': state.position,
|
||||
\ 'partialResultToken': function('s:PartialResult', [state]),
|
||||
\ 'workDoneToken': function('s:WorkDone', [state]),
|
||||
\ }
|
||||
let response = copilot#Request('textDocument/copilotPanelCompletion', params, function('s:Result', [state]), function('s:Error', [state]))
|
||||
exe substitute(a:opts.mods, '\C\<tab\>', '-tab', 'g') 'keepalt split' state.panel
|
||||
let b:copilot_panel = state
|
||||
call s:Initialize(state)
|
||||
call s:Render(@%)
|
||||
call s:Render(state)
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
augroup github_copilot_panel
|
||||
autocmd!
|
||||
autocmd BufReadCmd copilot:///* exe s:BufReadCmd()
|
||||
autocmd BufReadCmd copilot:///panel/* exe s:BufReadCmd()
|
||||
augroup END
|
||||
|
61
.vim/pack/plugins/opt/vim-copilot/autoload/copilot/util.vim
Normal file
61
.vim/pack/plugins/opt/vim-copilot/autoload/copilot/util.vim
Normal file
@ -0,0 +1,61 @@
|
||||
let s:deferred = []
|
||||
|
||||
function! copilot#util#Nop(...) abort
|
||||
return v:null
|
||||
endfunction
|
||||
|
||||
function! copilot#util#Defer(fn, ...) abort
|
||||
call add(s:deferred, function(a:fn, a:000))
|
||||
return timer_start(0, function('s:RunDeferred'))
|
||||
endfunction
|
||||
|
||||
function! s:RunDeferred(...) abort
|
||||
if empty(s:deferred)
|
||||
return
|
||||
endif
|
||||
let Fn = remove(s:deferred, 0)
|
||||
call timer_start(0, function('s:RunDeferred'))
|
||||
call call(Fn, [])
|
||||
endfunction
|
||||
|
||||
function! copilot#util#UTF16Width(str) abort
|
||||
return strchars(substitute(a:str, "\\%#=2[^\u0001-\uffff]", " ", 'g'))
|
||||
endfunction
|
||||
|
||||
if exists('*utf16idx')
|
||||
|
||||
function! copilot#util#UTF16ToByteIdx(str, utf16_idx) abort
|
||||
return byteidx(a:str, a:utf16_idx, 1)
|
||||
endfunction
|
||||
|
||||
elseif has('nvim')
|
||||
|
||||
function! copilot#util#UTF16ToByteIdx(str, utf16_idx) abort
|
||||
try
|
||||
return v:lua.vim.str_byteindex(a:str, a:utf16_idx, 1)
|
||||
catch /^Vim(return):E5108:/
|
||||
return -1
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
else
|
||||
|
||||
function! copilot#util#UTF16ToByteIdx(str, utf16_idx) abort
|
||||
if copilot#util#UTF16Width(a:str) < a:utf16_idx
|
||||
return -1
|
||||
endif
|
||||
let end_offset = len(a:str)
|
||||
while copilot#util#UTF16Width(strpart(a:str, 0, end_offset)) > a:utf16_idx && end_offset > 0
|
||||
let end_offset -= 1
|
||||
endwhile
|
||||
return end_offset
|
||||
endfunction
|
||||
|
||||
endif
|
||||
|
||||
function! copilot#util#AppendPosition() abort
|
||||
let line = getline('.')
|
||||
let col_byte = col('.') - (mode() =~# '^[iR]' || empty(line))
|
||||
let col_utf16 = copilot#util#UTF16Width(strpart(line, 0, col_byte))
|
||||
return {'line': line('.') - 1, 'character': col_utf16}
|
||||
endfunction
|
@ -1,3 +1,3 @@
|
||||
function! copilot#version#String() abort
|
||||
return '1.15.0'
|
||||
return '1.41.0'
|
||||
endfunction
|
||||
|
777
.vim/pack/plugins/opt/vim-copilot/dist/agent.js
vendored
777
.vim/pack/plugins/opt/vim-copilot/dist/agent.js
vendored
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.
BIN
.vim/pack/plugins/opt/vim-copilot/dist/crypt32.node
vendored
BIN
.vim/pack/plugins/opt/vim-copilot/dist/crypt32.node
vendored
Binary file not shown.
1067
.vim/pack/plugins/opt/vim-copilot/dist/language-server.js
vendored
Executable file
1067
.vim/pack/plugins/opt/vim-copilot/dist/language-server.js
vendored
Executable file
File diff suppressed because one or more lines are too long
6
.vim/pack/plugins/opt/vim-copilot/dist/language-server.js.map
vendored
Normal file
6
.vim/pack/plugins/opt/vim-copilot/dist/language-server.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
100256
.vim/pack/plugins/opt/vim-copilot/dist/resources/cl100k_base.tiktoken.noindex
vendored
Normal file
100256
.vim/pack/plugins/opt/vim-copilot/dist/resources/cl100k_base.tiktoken.noindex
vendored
Normal file
File diff suppressed because it is too large
Load Diff
199998
.vim/pack/plugins/opt/vim-copilot/dist/resources/o200k_base.tiktoken.noindex
vendored
Normal file
199998
.vim/pack/plugins/opt/vim-copilot/dist/resources/o200k_base.tiktoken.noindex
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -28,10 +28,10 @@ COMMANDS *:Copilot*
|
||||
|
||||
*:Copilot_panel*
|
||||
:Copilot panel Open a window with up to 10 completions for the
|
||||
current buffer. Use <CR> to accept a solution. Maps
|
||||
are also provided for [[ and ]] to jump from solution
|
||||
to solution. This is the default command if :Copilot
|
||||
is called without an argument.
|
||||
current buffer. Use <CR> to accept a completion.
|
||||
Maps are also provided for [[ and ]] to jump from
|
||||
completion to completion. This is the default command
|
||||
if :Copilot is called without an argument.
|
||||
|
||||
*:Copilot_version*
|
||||
:Copilot version Show version information.
|
||||
@ -75,12 +75,13 @@ g:copilot_node_command Tell Copilot what `node` binary to use with
|
||||
\ "~/.nodenv/versions/18.18.0/bin/node"
|
||||
<
|
||||
*g:copilot_proxy*
|
||||
g:copilot_proxy Tell Copilot what proxy server to use. This is a
|
||||
string in the format of `hostname:port` or
|
||||
`username:password@host:port`.
|
||||
g:copilot_proxy Tell Copilot what proxy server to use.
|
||||
>
|
||||
let g:copilot_proxy = 'localhost:3128'
|
||||
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
|
||||
@ -90,6 +91,23 @@ g:copilot_proxy_strict_ssl
|
||||
>
|
||||
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 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>*
|
||||
@ -158,10 +176,24 @@ Lua version:
|
||||
SYNTAX HIGHLIGHTING *copilot-highlighting*
|
||||
|
||||
Inline suggestions are highlighted using the CopilotSuggestion group,
|
||||
defaulting to a medium gray. The best place to override this is a file named
|
||||
after/colors/<colorschemename>.vim in your 'runtimepath' (e.g.,
|
||||
~/.config/nvim/after/colors/solarized.vim). Example declaration:
|
||||
defaulting to a medium gray. The best place to override this is with
|
||||
a |ColorScheme| autocommand:
|
||||
>
|
||||
highlight CopilotSuggestion guifg=#555555 ctermfg=8
|
||||
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,27 +0,0 @@
|
||||
:Copilot copilot.txt /*:Copilot*
|
||||
:Copilot_disable copilot.txt /*:Copilot_disable*
|
||||
:Copilot_enable copilot.txt /*:Copilot_enable*
|
||||
:Copilot_feedback copilot.txt /*:Copilot_feedback*
|
||||
:Copilot_panel copilot.txt /*:Copilot_panel*
|
||||
:Copilot_setup copilot.txt /*:Copilot_setup*
|
||||
:Copilot_signout copilot.txt /*:Copilot_signout*
|
||||
:Copilot_status copilot.txt /*:Copilot_status*
|
||||
:Copilot_version copilot.txt /*:Copilot_version*
|
||||
b:copilot_enabled copilot.txt /*b:copilot_enabled*
|
||||
copilot copilot.txt /*copilot*
|
||||
copilot#Accept() copilot.txt /*copilot#Accept()*
|
||||
copilot-highlighting copilot.txt /*copilot-highlighting*
|
||||
copilot-i_<Tab> copilot.txt /*copilot-i_<Tab>*
|
||||
copilot-i_ALT-CTRL-Right copilot.txt /*copilot-i_ALT-CTRL-Right*
|
||||
copilot-i_ALT-Right copilot.txt /*copilot-i_ALT-Right*
|
||||
copilot-i_ALT-[ copilot.txt /*copilot-i_ALT-[*
|
||||
copilot-i_ALT-\ copilot.txt /*copilot-i_ALT-\\*
|
||||
copilot-i_ALT-] copilot.txt /*copilot-i_ALT-]*
|
||||
copilot-i_CTRL-] copilot.txt /*copilot-i_CTRL-]*
|
||||
copilot-maps copilot.txt /*copilot-maps*
|
||||
copilot-options copilot.txt /*copilot-options*
|
||||
copilot.txt copilot.txt /*copilot.txt*
|
||||
g:copilot_filetypes copilot.txt /*g:copilot_filetypes*
|
||||
g:copilot_node_command copilot.txt /*g:copilot_node_command*
|
||||
g:copilot_proxy copilot.txt /*g:copilot_proxy*
|
||||
g:copilot_proxy_strict_ssl copilot.txt /*g:copilot_proxy_strict_ssl*
|
@ -1,62 +1,68 @@
|
||||
local copilot = {}
|
||||
|
||||
copilot.lsp_start_client = function(cmd, handler_names)
|
||||
local capabilities = vim.lsp.protocol.make_client_capabilities()
|
||||
local handlers = {}
|
||||
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, handler_names, opts, settings)
|
||||
local handlers = {['window/showDocument'] = showDocument}
|
||||
local id
|
||||
for _, name in ipairs(handler_names) do
|
||||
handlers[name] = function(err, result)
|
||||
handlers[name] = function(err, result, ctx, _)
|
||||
if result then
|
||||
local retval = vim.call('copilot#agent#LspHandle', id, { method = name, params = result })
|
||||
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
|
||||
if name:match('^copilot/') then
|
||||
capabilities.copilot = capabilities.copilot or {}
|
||||
capabilities.copilot[name:match('^copilot/(.*)$')] = true
|
||||
end
|
||||
end
|
||||
local workspace_folders = opts.workspaceFolders
|
||||
if #workspace_folders == 0 then
|
||||
workspace_folders = nil
|
||||
end
|
||||
id = vim.lsp.start_client({
|
||||
cmd = cmd,
|
||||
cmd_cwd = vim.call('copilot#job#Cwd'),
|
||||
name = 'copilot',
|
||||
capabilities = capabilities,
|
||||
name = 'GitHub Copilot',
|
||||
init_options = opts.initializationOptions,
|
||||
workspace_folders = workspace_folders,
|
||||
settings = settings,
|
||||
handlers = handlers,
|
||||
get_language_id = function(bufnr, filetype)
|
||||
return vim.call('copilot#doc#LanguageForFileType', filetype)
|
||||
end,
|
||||
on_init = function(client, initialize_result)
|
||||
vim.call('copilot#agent#LspInit', client.id, initialize_result)
|
||||
vim.call('copilot#client#LspInit', client.id, initialize_result)
|
||||
if vim.fn.has('nvim-0.8') == 0 then
|
||||
client.notify('workspace/didChangeConfiguration', { settings = settings })
|
||||
end
|
||||
end,
|
||||
on_exit = function(code, signal, client_id)
|
||||
vim.schedule(function()
|
||||
vim.call('copilot#agent#LspExit', client_id, code, signal)
|
||||
vim.call('copilot#client#LspExit', client_id, code, signal)
|
||||
end)
|
||||
end,
|
||||
})
|
||||
return id
|
||||
end
|
||||
|
||||
copilot.lsp_request = function(client_id, method, params)
|
||||
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
|
||||
pcall(vim.lsp.buf_attach_client, 0, client_id)
|
||||
for _, doc in ipairs({ params.doc, params.textDocument }) do
|
||||
if doc and type(doc.uri) == 'number' then
|
||||
local bufnr = doc.uri
|
||||
pcall(vim.lsp.buf_attach_client, bufnr, client_id)
|
||||
doc.uri = vim.uri_from_bufnr(bufnr)
|
||||
doc.version = vim.lsp.util.buf_versions[bufnr]
|
||||
end
|
||||
if bufnr == vim.NIL then
|
||||
bufnr = nil
|
||||
end
|
||||
local _, id
|
||||
_, id = client.request(method, params, function(err, result)
|
||||
vim.call('copilot#agent#LspResponse', client_id, { id = id, error = err, result = result })
|
||||
end)
|
||||
vim.call('copilot#client#LspResponse', client_id, { id = id, error = err, result = result })
|
||||
end, bufnr)
|
||||
return id
|
||||
end
|
||||
|
||||
@ -67,7 +73,7 @@ copilot.rpc_request = function(client_id, method, params)
|
||||
end
|
||||
local _, id
|
||||
_, id = client.rpc.request(method, params, function(err, result)
|
||||
vim.call('copilot#agent#LspResponse', client_id, { id = id, error = err, result = result })
|
||||
vim.call('copilot#client#LspResponse', client_id, { id = id, error = err, result = result })
|
||||
end)
|
||||
return id
|
||||
end
|
||||
|
@ -7,7 +7,7 @@ scriptencoding utf-8
|
||||
|
||||
command! -bang -nargs=? -range=-1 -complete=customlist,copilot#CommandComplete Copilot exe copilot#Command(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)
|
||||
|
||||
if v:version < 800 || !exists('##CompleteChanged')
|
||||
if v:version < 800 || !exists('##InsertLeavePre')
|
||||
finish
|
||||
endif
|
||||
|
||||
@ -15,9 +15,9 @@ function! s:ColorScheme() abort
|
||||
if &t_Co == 256
|
||||
hi def CopilotSuggestion guifg=#808080 ctermfg=244
|
||||
else
|
||||
hi def CopilotSuggestion guifg=#808080 ctermfg=8
|
||||
hi def CopilotSuggestion guifg=#808080 ctermfg=12
|
||||
endif
|
||||
hi def link CopilotAnnotation Normal
|
||||
hi def link CopilotAnnotation MoreMsg
|
||||
endfunction
|
||||
|
||||
function! s:MapTab() abort
|
||||
@ -26,7 +26,7 @@ function! s:MapTab() abort
|
||||
endif
|
||||
let tab_map = maparg('<Tab>', 'i', 0, 1)
|
||||
if !has_key(tab_map, 'rhs')
|
||||
imap <script><silent><nowait><expr> <Tab> copilot#Accept()
|
||||
imap <script><silent><nowait><expr> <Tab> empty(get(g:, 'copilot_no_tab_map')) ? copilot#Accept() : "\t"
|
||||
elseif tab_map.rhs !~# 'copilot'
|
||||
if tab_map.expr
|
||||
let tab_fallback = '{ -> ' . tab_map.rhs . ' }'
|
||||
@ -52,17 +52,20 @@ endfunction
|
||||
|
||||
augroup github_copilot
|
||||
autocmd!
|
||||
autocmd InsertLeave * call s:Event('InsertLeave')
|
||||
autocmd BufLeave * if mode() =~# '^[iR]'|call s:Event('InsertLeave')|endif
|
||||
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()
|
||||
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 readonly nomodifiable
|
||||
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()
|
||||
@ -81,7 +84,7 @@ if !get(g:, 'copilot_no_maps')
|
||||
if !has('nvim') && &encoding ==# 'utf-8'
|
||||
" avoid 8-bit meta collision with UTF-8 characters
|
||||
let s:restore_encoding = 1
|
||||
set encoding=cp949
|
||||
silent noautocmd set encoding=cp949
|
||||
endif
|
||||
if empty(mapcheck('<M-]>', 'i'))
|
||||
imap <M-]> <Plug>(copilot-next)
|
||||
@ -96,17 +99,15 @@ if !get(g:, 'copilot_no_maps')
|
||||
imap <M-Right> <Plug>(copilot-accept-word)
|
||||
endif
|
||||
if empty(mapcheck('<M-C-Right>', 'i'))
|
||||
imap <M-Down> <Plug>(copilot-accept-line)
|
||||
imap <M-C-Right> <Plug>(copilot-accept-line)
|
||||
endif
|
||||
finally
|
||||
if exists('s:restore_encoding')
|
||||
set encoding=utf-8
|
||||
silent noautocmd set encoding=utf-8
|
||||
endif
|
||||
endtry
|
||||
endif
|
||||
|
||||
call copilot#Init()
|
||||
|
||||
let s:dir = expand('<sfile>:h:h')
|
||||
if getftime(s:dir . '/doc/copilot.txt') > getftime(s:dir . '/doc/tags')
|
||||
silent! execute 'helptags' fnameescape(s:dir . '/doc')
|
||||
|
@ -6,12 +6,12 @@ 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'
|
||||
silent! exe 'syn include @copilotLanguageTop syntax/' . s:subtype . '.vim'
|
||||
unlet! b:current_syntax
|
||||
endif
|
||||
|
||||
syn region copilotHeader start="\%^" end="^─\@="
|
||||
syn region copilotSolution matchgroup=copilotSeparator start="^─\{9,}$" end="\%(^─\{9,\}$\)\@=\|\%$" keepend contains=@copilotLanguageTop
|
||||
syn region copilotPanelItem matchgroup=copilotSeparator start="^─\{9,}$" end="\%(^─\{9,\}$\)\@=\|\%$" keepend contains=@copilotLanguageTop
|
||||
|
||||
hi def link copilotHeader PreProc
|
||||
hi def link copilotSeparator Comment
|
||||
|
25
.vim/pack/plugins/opt/vim-copilot/syntax/copilotlog.vim
Normal file
25
.vim/pack/plugins/opt/vim-copilot/syntax/copilotlog.vim
Normal file
@ -0,0 +1,25 @@
|
||||
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,23 +0,0 @@
|
||||
==============================================
|
||||
This is a copy of the MIT license.
|
||||
==============================================
|
||||
Copyright (C) 2013 Zc He <farseer90718@gmail.com>
|
||||
Copyright (C) 2013 David J Patrick <davamundo@gmail.com>
|
||||
|
||||
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,223 +0,0 @@
|
||||
vim-taskwarrior
|
||||
===============
|
||||
|
||||
_a vim interface for [taskwarrior](https://taskwarrior.org)_
|
||||
|
||||
Taskwarrior is a command-line todo list manager. It helps you manage task lists
|
||||
with projects, tags, dates, dependencies, annotations, recurrences and apply
|
||||
complex (or simple) queries with attribute modifiers, boolean, regex filters
|
||||
and produce any number of reports, built-in or customizable reports, attributes
|
||||
and color themes. Task keeps data in JSON text files and it's always improving.
|
||||
Find out more at https://taskwarrior.org and read man task and man taskrc.
|
||||
|
||||
vim-taskwarrior is a vim plugin that extends taskwarrior with an interactive
|
||||
interface. It features a rich set of mappings and commands, is easy to customize,
|
||||
and makes adding, modifying, sorting, reporting and marking done, fast, easy and fun!
|
||||
Homepage: https://github.com/farseer90718/vim-taskwarrior, patches welcome!
|
||||
|
||||
----
|
||||
|
||||
### Prerequisites:
|
||||
|
||||
This plugin requires Taskwarrior 2.2.0 or higher, although >2.3.x is required
|
||||
for taskd sync functions, and recommended in general, and well worth the price;
|
||||
free :)
|
||||
see: https://taskwarrior.org/download/
|
||||
|
||||
Vim version 7.x is required.
|
||||
|
||||
Suggested plugins
|
||||
|
||||
* [vim-airline](https://github.com/bling/vim-airline) for [better statusline information](https://github.com/farseer90718/vim-taskwarrior#screenshot).
|
||||
* [unite.vim](https://github.com/Shougo/unite.vim) for easier bookmark/history operations.
|
||||
|
||||
If you experience line-wrapping issues, add the following line to your .vimrc
|
||||
|
||||
```
|
||||
let g:task_rc_override = 'rc.defaultwidth=0'
|
||||
```
|
||||
|
||||
If you experience task truncation (vim-taskwarrior not showing enough tasks), add:
|
||||
|
||||
```
|
||||
let g:task_rc_override = 'rc.defaultheight=0'
|
||||
```
|
||||
|
||||
|
||||
----
|
||||
|
||||
### Screenshot:
|
||||
|
||||
![screenshot](https://raw.github.com/farseer90718/vim-taskwarrior/master/screenshot.png)
|
||||
![vim-taskwarrior animated gif](http://taskextras.org/attachments/download/655/20131110_002753.gif)
|
||||
|
||||
### Installing:
|
||||
|
||||
Either [download zip file](https://github.com/farseer90718/vim-taskwarrior/archive/master.zip)
|
||||
and extract in ~/.vim or use your favorite plugin manager.
|
||||
|
||||
- [Pathogen](https://github.com/tpope/vim-pathogen)
|
||||
- `git clone https://github.com/farseer90718/vim-taskwarrior ~/.vim/bundle/vim-taskwarrior`
|
||||
- [Vundle](https://github.com/gmarik/vundle)
|
||||
1. Add `Bundle 'farseer90718/vim-taskwarrior'` to .vimrc
|
||||
2. Run `:BundleInstall`
|
||||
- [NeoBundle](https://github.com/Shougo/neobundle.vim)
|
||||
1. Add `NeoBundle 'farseer90718/vim-taskwarrior'` to .vimrc
|
||||
2. Run `:NeoBundleInstall`
|
||||
- [vim-plug](https://github.com/junegunn/vim-plug)
|
||||
1. Add `Plug 'blindFS/vim-taskwarrior'` to .vimrc
|
||||
2. Run `:PlugInstall`
|
||||
|
||||
----
|
||||
|
||||
### Default map:
|
||||
|
||||
```vim
|
||||
nnoremap <buffer> A ... " add annotation
|
||||
nnoremap <buffer> x ... " delete annotation.
|
||||
nnoremap <buffer> o ... " open the annotation as a file.
|
||||
nnoremap <buffer> a ... " create new task.
|
||||
nnoremap <buffer> d ... " set the task in current line done.
|
||||
nnoremap <buffer> D ... " delete task
|
||||
nnoremap <buffer> <Del> ... " delete field/annotation/task
|
||||
nnoremap <buffer> <Space> ... " select/remove current task to selected list
|
||||
nnoremap <buffer> m ... " modify current field.
|
||||
nnoremap <buffer> M ... " modify current task.
|
||||
nnoremap <buffer> f ... " change filter
|
||||
nnoremap <buffer> r ... " change report type
|
||||
nnoremap <buffer> c ... " execute a command for selected tasks/current task
|
||||
nnoremap <buffer> R ... " refresh the report/clear selected list
|
||||
nnoremap <buffer> q ... " quit buffer.
|
||||
nnoremap <buffer> X ... " clear all completed task.
|
||||
nnoremap <buffer> p ... " duplicate selected tasks
|
||||
nnoremap <buffer> u ... " undo last change.
|
||||
nnoremap <buffer> + ... " start task
|
||||
nnoremap <buffer> - ... " stop task
|
||||
nnoremap <buffer> S ... " sync with taskd server.
|
||||
nnoremap <buffer> s ... " sort by this column primarily.(if already of the highest priority then switch the polarity)
|
||||
nnoremap <buffer> < ... " sort by this column increasingly.(if already increasingly then increase its priority)
|
||||
nnoremap <buffer> > ... " sort by this column decreasingly.(if already decreasingly then decrease its priority)
|
||||
nnoremap <buffer> H ... " cycle column format left
|
||||
nnoremap <buffer> L ... " cycle column format right
|
||||
nnoremap <buffer> J ... " next historical entry
|
||||
nnoremap <buffer> K ... " previous historical entry
|
||||
nnoremap <buffer> B ... " create a bookmark for current combination
|
||||
nnoremap <buffer> <F1> ... " view the documents
|
||||
nnoremap <buffer> <CR> ... " show task info.
|
||||
nnoremap <buffer> <TAB> ... " jump to the next column
|
||||
nnoremap <buffer> <S-TAB> ... " jump to the previous column
|
||||
nnoremap <buffer> <right> ... " jump to the next non-empty column
|
||||
nnoremap <buffer> <left> ... " jump to the previous non-empty column
|
||||
vnoremap <buffer> d ... " set done to all visual selected tasks
|
||||
vnoremap <buffer> D ... " delete all visual selected tasks
|
||||
vnoremap <buffer> <CR> ... " show information about visual selected tasks
|
||||
vnoremap <buffer> <Space> ... " add visual selected tasks to selected list
|
||||
|
||||
```
|
||||
----
|
||||
|
||||
### Commands:
|
||||
|
||||
```vim
|
||||
:TW [args] " task [filter report arguments]
|
||||
:TWUndo " undo the previous modification
|
||||
:TWEditTaskrc " edit ~/.taskrc
|
||||
:TWEditVitrc " edit ~/.vitrc
|
||||
:TWDeleteCompleted " clear all completed tasks
|
||||
:TWAdd " add new tasks interactively
|
||||
:TWAnnotate " add an annotation
|
||||
:TWComplete " mark task done
|
||||
:TWDelete " deleta a task
|
||||
:TWDeleteAnnotation " delete an annotation
|
||||
:TWModifyInteractive " make changes to a task interactively (use with caution!)
|
||||
:TWReportInfo " run the info report
|
||||
:TWReportSort [args] " overide the sort method, reset to default if no arguments passed
|
||||
:TWSync " synchronise with taskd server
|
||||
:TWToggleReadonly " toggle readonly option
|
||||
:TWToggleHLField " toggle highlight field option
|
||||
:TWHistory " list history records using unite.vim
|
||||
:TWHistoryClear " clear history
|
||||
:TWBookmark " list bookmarks using unite.vim
|
||||
:TWBookmarkClear " clear bookmarks
|
||||
|
||||
```
|
||||
----
|
||||
|
||||
### Options:
|
||||
|
||||
```vim
|
||||
" default task report type
|
||||
let g:task_report_name = 'next'
|
||||
" custom reports have to be listed explicitly to make them available
|
||||
let g:task_report_command = []
|
||||
" whether the field under the cursor is highlighted
|
||||
let g:task_highlight_field = 1
|
||||
" can not make change to task data when set to 1
|
||||
let g:task_readonly = 0
|
||||
" vim built-in term for task undo in gvim
|
||||
let g:task_gui_term = 1
|
||||
" allows user to override task configurations. Seperated by space. Defaults to ''
|
||||
let g:task_rc_override = 'rc.defaultwidth=999'
|
||||
" default fields to ask when adding a new task
|
||||
let g:task_default_prompt = ['due', 'description']
|
||||
" whether the info window is splited vertically
|
||||
let g:task_info_vsplit = 0
|
||||
" info window size
|
||||
let g:task_info_size = 15
|
||||
" info window position
|
||||
let g:task_info_position = 'belowright'
|
||||
" directory to store log files defaults to taskwarrior data.location
|
||||
let g:task_log_directory = '~/.task'
|
||||
" max number of historical entries
|
||||
let g:task_log_max = '20'
|
||||
" forward arrow shown on statusline
|
||||
let g:task_left_arrow = ' <<'
|
||||
" backward arrow ...
|
||||
let g:task_left_arrow = '>> '
|
||||
|
||||
```
|
||||
----
|
||||
|
||||
### Syntax highlightling:
|
||||
|
||||
Default scheme:
|
||||
|
||||
```vim
|
||||
highlight default link taskwarrior_tablehead Tabline
|
||||
highlight default link taskwarrior_field IncSearch
|
||||
highlight default link taskwarrior_selected Visual
|
||||
highlight default link taskwarrior_id VarId
|
||||
highlight default link taskwarrior_project String
|
||||
highlight default link taskwarrior_Status Include
|
||||
highlight default link taskwarrior_priority Class
|
||||
highlight default link taskwarrior_due Todo
|
||||
highlight default link taskwarrior_end Keyword
|
||||
highlight default link taskwarrior_description Normal
|
||||
highlight default link taskwarrior_entry Special
|
||||
highlight default link taskwarrior_depends Todo
|
||||
highlight default link taskwarrior_tags Keyword
|
||||
highlight default link taskwarrior_uuid VarId
|
||||
highlight default link taskwarrior_urgency Todo
|
||||
```
|
||||
|
||||
Feel free to change any of above by something like:
|
||||
|
||||
```vim
|
||||
hi taskwarrior_xxx guibg = xxx guifg = xxx ctermbg = xxx ctermfg = xxx
|
||||
```
|
||||
|
||||
in your vimrc.
|
||||
|
||||
### Acknowledgement:
|
||||
|
||||
* [vim-airline](https://github.com/bling/vim-airline) by bling
|
||||
* [unite.vim](https://github.com/Shougo/unite.vim) by Shougo
|
||||
* [webapi-vim](https://github.com/mattn/webapi-vim) by mattn
|
||||
|
||||
### License:
|
||||
|
||||
[MIT](https://raw.github.com/farseer90718/vim-taskwarrior/master/LICENSE.txt)
|
||||
|
||||
|
||||
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/farseer90718/vim-taskwarrior/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
|
||||
|
@ -1,41 +0,0 @@
|
||||
function! airline#extensions#taskwarrior#apply(...)
|
||||
if &ft == 'taskreport'
|
||||
call a:1.add_section('airline_a', ' Taskwarrior ')
|
||||
call a:1.add_section('airline_b', ' %{b:command} %{&readonly ? g:airline_symbols.readonly : ""}')
|
||||
call a:1.add_section('airline_b', ' @%{b:context} ')
|
||||
call a:1.add_section('airline_b', g:task_left_arrow.' %{b:hist > 1 ? g:task_right_arrow : ""}')
|
||||
call a:1.add_section('airline_c', ' %{b:filter} ')
|
||||
call a:1.add_section('airline_c', ' %{b:sstring} ')
|
||||
call a:1.split()
|
||||
call a:1.add_section('airline_x', ' %{b:now} ')
|
||||
call a:1.add_section('airline_x', ' %{b:task_report_columns[taskwarrior#data#current_index()]} ')
|
||||
call a:1.add_section('airline_y', ' %{b:sort} ')
|
||||
if b:active != '0'
|
||||
call airline#parts#define_text('active', ' '.b:active.' ')
|
||||
call airline#parts#define_accent('active', 'orange')
|
||||
call a:1.add_section('airline_z', airline#section#create(['active']))
|
||||
endif
|
||||
call a:1.add_section('airline_z', ' %{b:summary[0]} ')
|
||||
call airline#parts#define_text('completed', ' '.b:summary[1].' ')
|
||||
call airline#parts#define_accent('completed', 'green')
|
||||
call a:1.add_section('airline_z', airline#section#create(['completed']))
|
||||
call a:1.add_section('airline_z', ' %{b:summary[2]} ')
|
||||
return 1
|
||||
elseif &ft == 'taskinfo'
|
||||
call a:1.add_section('airline_a', ' Taskinfo ')
|
||||
call a:1.add_section('airline_b', ' %{b:command." ".g:airline_symbols.readonly }')
|
||||
call a:1.add_section('airline_c', ' %{b:filter} ')
|
||||
call a:1.split()
|
||||
return 1
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function s:context()
|
||||
let con = split(system('task context show'), '\n')
|
||||
let con = con =~ 'No context' ? 'none' : con
|
||||
return con
|
||||
endfunction
|
||||
|
||||
function! airline#extensions#taskwarrior#init(ext)
|
||||
call a:ext.add_statusline_func('airline#extensions#taskwarrior#apply')
|
||||
endfunction
|
@ -1,44 +0,0 @@
|
||||
function! taskinfo#init(command, filter, info)
|
||||
if exists('g:task_info')
|
||||
call taskinfo#quit()
|
||||
endif
|
||||
|
||||
if a:command != 'info'
|
||||
\ && exists('g:task_info_arg')
|
||||
\ && g:task_info_arg == [a:command, a:filter]
|
||||
unlet g:task_info_arg
|
||||
return
|
||||
endif
|
||||
|
||||
execute g:task_info_position.' '.g:task_info_size.
|
||||
\ (g:task_info_vsplit ? 'v' : '').'split'
|
||||
edit taskinfo
|
||||
let g:task_info = bufnr('%')
|
||||
let g:task_info_arg = [a:command, a:filter]
|
||||
setlocal noswapfile
|
||||
setlocal modifiable
|
||||
call append(0, a:info)
|
||||
silent global/^[\t ]*$/delete
|
||||
silent global/^[ -]\+$/delete
|
||||
setlocal readonly
|
||||
setlocal nomodifiable
|
||||
setlocal buftype=nofile
|
||||
setlocal nowrap
|
||||
setlocal filetype=taskinfo
|
||||
1
|
||||
|
||||
let b:command = a:command
|
||||
let b:filter = a:filter
|
||||
|
||||
nnoremap <silent> <buffer> q :call taskinfo#quit()<CR>
|
||||
nnoremap <silent> <buffer> <enter> :call taskinfo#quit()<CR>
|
||||
|
||||
if a:command != 'info'
|
||||
wincmd W
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! taskinfo#quit()
|
||||
silent! execute g:task_info.'bd!'
|
||||
unlet g:task_info
|
||||
endfunction
|
@ -1,221 +0,0 @@
|
||||
function! taskwarrior#list(...) abort
|
||||
setlocal noreadonly
|
||||
setlocal modifiable
|
||||
let pos = getpos('.')
|
||||
%delete
|
||||
call taskwarrior#buffer_var_init()
|
||||
let b:command = get(a:, 1, b:command)
|
||||
let b:filter = get(a:, 2, b:filter)
|
||||
let b:type = get(a:, 3, b:type)
|
||||
let b:rc = get(a:, 4, b:rc). ' rc.defaultheight=0'
|
||||
|
||||
let b:rc .= ' '.join(filter(split(b:filter, ' '), "v:val =~ '^rc\..*'"))
|
||||
let b:filter = join(filter(split(b:filter, ' '), "v:val !~ '^rc\..*'"))
|
||||
let rcs = split(b:rc, ' ')
|
||||
let b:rc = join(filter(copy(rcs), "match(rcs, matchstr(v:val, '^[^=:]*'), v:key+1) == -1"), ' ')
|
||||
|
||||
if b:type == 'special'
|
||||
setlocal buftype=nofile
|
||||
call append(0, split(system('task '.b:rc.' '.b:filter.' '.b:command), '\n'))
|
||||
silent global/^[\t ]*$/delete
|
||||
execute 'setlocal filetype=task_'.b:command
|
||||
nnoremap <buffer> q :call taskwarrior#Bclose(bufnr('%'))<CR>
|
||||
call setpos('.', pos)
|
||||
return
|
||||
endif
|
||||
|
||||
let b:hist = get(b:, 'hist', 1)
|
||||
call taskwarrior#log#history('write')
|
||||
|
||||
let rcc = matchstr(b:rc, 'rc\.report\.'.b:command.'\.columns.\zs\S*')
|
||||
let rcl = matchstr(b:rc, 'rc\.report\.'.b:command.'\.labels.\zs\S*')
|
||||
" let b:task_report_columns = rcc == '' ? split(system("task _get -- rc.report.".b:command.".columns")[0:-2], ',') : split(rcc, ',')
|
||||
" let b:task_report_labels = rcl == '' ? split(system("task _get -- rc.report.".b:command.".labels")[0:-2], ',') : split(rcl, ',')
|
||||
let b:task_report_columns = rcc == '' ? split(matchstr(system("task show |grep report.".b:command.".columns")[0:-2], '\S*$'), ',') : split(rcc, ',')
|
||||
let b:task_report_labels = rcl == '' ? split(matchstr(system("task show |grep report.".b:command.".labels")[0:-2], '\S*$'), ',') : split(rcl, ',')
|
||||
let line1 = join(b:task_report_labels, ' ')
|
||||
|
||||
let context = split(substitute(
|
||||
\ system('task '.b:rc.' '.b:filter.' '.b:command),
|
||||
\ '\[[0-9;]\+m',
|
||||
\ '', 'g'),
|
||||
\ '\n')
|
||||
let split_lineno = match(context, '^[ -]\+$')
|
||||
if split_lineno == -1
|
||||
call append(0, line1)
|
||||
else
|
||||
let end = len(context)-match(reverse(copy(context)), '^$')
|
||||
call append(0, context[split_lineno-1:end-1])
|
||||
silent global/^[\t ]*$/delete
|
||||
silent global/^[ -]\+$/delete
|
||||
endif
|
||||
|
||||
call filter(b:task_report_columns, "index(split(getline(1), ' '), b:task_report_labels[v:key]) != -1")
|
||||
call filter(b:task_report_labels, "index(split(getline(1), ' '), v:val) != -1")
|
||||
|
||||
let b:task_columns = []
|
||||
let ci = 0
|
||||
1
|
||||
while ci != -1
|
||||
let b:task_columns += [ci]
|
||||
let ci = search('\s\S', 'W', 1)
|
||||
let ci = ci > 0 ? virtcol('.') : -1
|
||||
endwhile
|
||||
|
||||
let b:task_columns += [999]
|
||||
let b:summary = taskwarrior#data#global_stats()
|
||||
let b:sort = taskwarrior#sort#order_list()[0]
|
||||
let a_tasks = split(system('task active limit:1 rc.verbose:nothing
|
||||
\ rc.report.active.sort=start-
|
||||
\ rc.report.active.columns=start.active,start.age,id,description.desc
|
||||
\ rc.report.active.labels=A,Age,ID,Description'), '\n')
|
||||
let b:now = len(a_tasks) > 0 ? a_tasks[-1] : ''
|
||||
let b:active = split(system('task start.any: count'), '\n')[0]
|
||||
let b:selected = []
|
||||
let b:sline = []
|
||||
let b:sstring = ''
|
||||
let con = split(system('task context show'), '\n')[0]
|
||||
let b:context = con =~ 'No context' ? 'none' :
|
||||
\ matchstr(con, 'Context .\zs\S*\ze. ')
|
||||
|
||||
setlocal filetype=taskreport
|
||||
if exists('b:ct')
|
||||
for l in range(line('$'))
|
||||
if taskwarrior#data#get_uuid(l) == b:ct
|
||||
let pos[1] = l
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
call setpos('.', pos)
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#buffer_var_init()
|
||||
let b:command = get(b:, 'command', g:task_report_name)
|
||||
let b:filter = get(b:, 'filter', '')
|
||||
let b:type = get(b:, 'type', 'report')
|
||||
let b:rc = get(b:, 'rc', g:task_rc_override)
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#init(...)
|
||||
if exists(':TagbarClose')
|
||||
TagbarClose
|
||||
endif
|
||||
let argstring = join(a:000, ' ')
|
||||
let [command, filter, type] = taskwarrior#command_type(argstring)
|
||||
let rc = g:task_rc_override
|
||||
|
||||
if type == 'interactive'
|
||||
if !g:task_readonly
|
||||
execute '!task '.argstring
|
||||
call taskwarrior#refresh()
|
||||
endif
|
||||
return
|
||||
endif
|
||||
|
||||
execute 'edit task\ '.command.'\ '.type
|
||||
|
||||
if exists('g:task_view')
|
||||
let g:task_view += [bufnr('%')]
|
||||
else
|
||||
let g:task_view = [bufnr('%')]
|
||||
endif
|
||||
|
||||
setlocal noswapfile
|
||||
call taskwarrior#list(command, filter, type, rc)
|
||||
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#refresh()
|
||||
if exists('g:task_view')
|
||||
for bufn in g:task_view
|
||||
execute bufn.'buffer'
|
||||
call taskwarrior#list()
|
||||
endfor
|
||||
else
|
||||
call taskwarrior#init()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#Bclose(buffer)
|
||||
if a:buffer =~ '^\d\+$'
|
||||
let btarget = bufnr(str2nr(a:buffer))
|
||||
else
|
||||
let btarget = bufnr(a:buffer)
|
||||
endif
|
||||
if bufname(btarget) == ''
|
||||
bdelete
|
||||
return
|
||||
endif
|
||||
" Numbers of windows that view target buffer which we will delete.
|
||||
let wnums = filter(range(1, winnr('$')), 'winbufnr(v:val) == btarget')
|
||||
let wcurrent = winnr()
|
||||
for w in wnums
|
||||
execute w.'wincmd w'
|
||||
let prevbuf = bufnr('#')
|
||||
if prevbuf > 0 && buflisted(prevbuf) && prevbuf != w
|
||||
buffer #
|
||||
else
|
||||
bprevious
|
||||
endif
|
||||
if btarget == bufnr('%')
|
||||
" Numbers of listed buffers which are not the target to be deleted.
|
||||
let blisted = filter(range(1, bufnr('$')), 'buflisted(v:val) && v:val != btarget')
|
||||
" Listed, not target, and not displayed.
|
||||
let bhidden = filter(copy(blisted), 'bufwinnr(v:val) < 0')
|
||||
" Take the first buffer, if any (could be more intelligent).
|
||||
let bjump = (bhidden + blisted + [-1])[0]
|
||||
if bjump > 0
|
||||
execute 'buffer '.bjump
|
||||
else
|
||||
enew
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
execute 'silent! bdelete '.btarget
|
||||
execute wcurrent.'wincmd w'
|
||||
endfunction
|
||||
|
||||
|
||||
function! taskwarrior#hi_field()
|
||||
silent! syntax clear taskwarrior_field
|
||||
let index = taskwarrior#data#current_index()
|
||||
execute 'syntax match taskwarrior_field /\%>1l\%'.line('.').'l\%'.(b:task_columns[index]+1).'v.*\%<'.(b:task_columns[index+1]+1).'v/'
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#quit()
|
||||
call taskwarrior#Bclose(bufnr('%'))
|
||||
call remove(g:task_view, index(g:task_view, bufnr('%')))
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#quit_all()
|
||||
for bufn in g:task_view
|
||||
call taskwarrior#Bclose(bufn)
|
||||
endfor
|
||||
let g:task_view = []
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#system_call(filter, command, args, mode)
|
||||
if a:mode == 'silent'
|
||||
call system('task '.a:filter.' '.a:command.' '.a:args)
|
||||
elseif a:mode == 'echo'
|
||||
echo "\n----------------\n"
|
||||
echo system('task '.a:filter.' '.a:command.' '.a:args)
|
||||
else
|
||||
execute '!task '.a:filter.' '.a:command.' '.a:args
|
||||
endif
|
||||
call taskwarrior#refresh()
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#command_type(string)
|
||||
for sub in split(a:string, ' ')
|
||||
if index(g:task_report_command, sub) != -1
|
||||
return [ sub, substitute(' '.a:string, ' '.sub, '', ''), 'report' ]
|
||||
elseif index(g:task_interactive_command, sub) != -1
|
||||
return [ sub, substitute(' '.a:string, ' '.sub, '', ''), 'interactive' ]
|
||||
elseif index(g:task_all_commands, sub) != -1
|
||||
return [ sub, substitute(' '.a:string, ' '.sub, '', ''), 'special' ]
|
||||
endif
|
||||
endfor
|
||||
return [ g:task_report_name, a:string, 'report' ]
|
||||
endfunction
|
@ -1,370 +0,0 @@
|
||||
function! taskwarrior#action#new()
|
||||
call taskwarrior#system_call('', 'add', taskwarrior#data#get_args('add'), 'echo')
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#action#set_done()
|
||||
call taskwarrior#system_call(taskwarrior#data#get_uuid(), ' done', '', 'silent')
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#action#urgency() abort
|
||||
let cc = taskwarrior#data#current_column()
|
||||
let udas = split(system('task _udas'), '\n')
|
||||
let cmap = { 'start' : 'active',
|
||||
\ 'entry' : 'age',
|
||||
\ 'depends' : 'blocked',
|
||||
\ 'parent' : 'blocking',
|
||||
\ 'wait' : 'waiting',
|
||||
\ 'description' : 'annotations'
|
||||
\ }
|
||||
let isuda = 0
|
||||
if has_key(cmap, cc)
|
||||
let cc = cmap[cc]
|
||||
elseif index(['due', 'priority', 'project', 'tags', 'scheduled']
|
||||
\ , cc) == -1
|
||||
if index(udas, cc) == -1
|
||||
call taskwarrior#sort#by_arg('urgency-')
|
||||
return
|
||||
else
|
||||
let isuda = 1
|
||||
endif
|
||||
endif
|
||||
let rcfile = $HOME.'/.taskrc'
|
||||
if filereadable(rcfile)
|
||||
let cv = taskwarrior#data#get_value_by_column(line('.'), cc)
|
||||
let option = isuda ? 'urgency.uda.'.cc.'.coefficient' :
|
||||
\ 'urgency.'.cc.'.coefficient'
|
||||
if len(cv)
|
||||
let ctag = expand('<cword>')
|
||||
if cc == 'tags' && index(split(cv), ctag) != -1
|
||||
let option = 'urgency.user.tag.'.ctag.'.coefficient'
|
||||
elseif cc == 'project' && cv =~ '^[^ \t%\\*]\+$'
|
||||
let pl = split(cv, '\.')
|
||||
let idx = index(pl, expand('<cword>'))
|
||||
let option = 'urgency.user.project.'.
|
||||
\ join(pl[0:idx], '.').'.coefficient'
|
||||
elseif isuda && cv =~ '^\w\+$'
|
||||
let option = 'urgency.uda.'.cc.'.'.cv.'.coefficient'
|
||||
endif
|
||||
endif
|
||||
let default_raw = split(system('task _get rc.'.option), '\n')
|
||||
let default = len(default_raw) ? default_raw[0] : '0'
|
||||
let new = input(option.' : ', default)
|
||||
let lines = readfile(rcfile)
|
||||
let index = match(lines, option)
|
||||
if str2float(new) == str2float(default)
|
||||
elseif str2float(new) == 0
|
||||
call filter(lines, 'v:val !~ option')
|
||||
elseif index == -1
|
||||
call add(lines, option.'='.new)
|
||||
else
|
||||
let lines[index] = option.'='.new
|
||||
endif
|
||||
call writefile(lines, rcfile)
|
||||
endif
|
||||
call taskwarrior#sort#by_arg('urgency-')
|
||||
execute 'normal! :\<Esc>'
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#action#modify(mode)
|
||||
let uuid = taskwarrior#data#get_uuid()
|
||||
if uuid == ''
|
||||
return
|
||||
endif
|
||||
if a:mode == 'current'
|
||||
let field = taskwarrior#data#current_column()
|
||||
if index(['id', 'uuid', 'status', 'urgency'], field) != -1
|
||||
return
|
||||
elseif field == 'description'
|
||||
call taskwarrior#system_call(uuid, 'modify', taskwarrior#data#get_args('modify', [field]), 'external')
|
||||
else
|
||||
call taskwarrior#system_call(uuid, 'modify', taskwarrior#data#get_args('modify', [field]), 'silent')
|
||||
endif
|
||||
else
|
||||
call taskwarrior#system_call(uuid, 'modify', taskwarrior#data#get_args('modify'), 'external')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#action#delete()
|
||||
let uuid = taskwarrior#data#get_uuid()
|
||||
if uuid == ''
|
||||
call taskwarrior#action#annotate('del')
|
||||
else
|
||||
let ccol = taskwarrior#data#current_column()
|
||||
if index(['project', 'tags', 'due', 'priority', 'start', 'depends'], ccol) != -1
|
||||
call taskwarrior#system_call(uuid, 'modify', ccol.':', 'silent')
|
||||
else
|
||||
execute '!task '.uuid.' delete'
|
||||
endif
|
||||
endif
|
||||
call taskwarrior#refresh()
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#action#remove()
|
||||
execute '!task '.taskwarrior#data#get_uuid().' delete'
|
||||
call taskwarrior#list()
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#action#annotate(op)
|
||||
let ln = line('.')
|
||||
let offset = -1
|
||||
while ln > 1 && taskwarrior#data#get_uuid(ln) == ''
|
||||
let ln -= 1
|
||||
let offset += 1
|
||||
endwhile
|
||||
let uuid = taskwarrior#data#get_uuid(ln)
|
||||
if uuid == ''
|
||||
return
|
||||
endif
|
||||
if a:op == 'add'
|
||||
let annotation = input('new annotation:', '', 'file')
|
||||
call taskwarrior#system_call(uuid, ' annotate ', annotation, 'silent')
|
||||
elseif a:op == 'del'
|
||||
let annotation = input('annotation pattern to delete:')
|
||||
call taskwarrior#system_call(uuid, ' denotate ', annotation, 'silent')
|
||||
elseif offset >= 0
|
||||
let taskobj = taskwarrior#data#get_query(uuid)
|
||||
if exists('taskobj.annotations[offset].description')
|
||||
let file = substitute(taskobj.annotations[offset].description, '\s*\/\s*', '/', 'g')
|
||||
let file = escape(file, ' ')
|
||||
let ft = 'text'
|
||||
if executable('file')
|
||||
let ft = system('file '.file)[:-2]
|
||||
endif
|
||||
if ft =~ 'text$'
|
||||
execute 'e '.file
|
||||
elseif ft !~ '(No such file or directory)' || file =~ '[a-z]*:\/\/[^ >,;]*'
|
||||
if executable('xdg-open')
|
||||
call system('xdg-open '.file.'&')
|
||||
elseif executable('open')
|
||||
call system('open '.file.'&')
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#action#filter()
|
||||
let column = taskwarrior#data#current_column()
|
||||
if index(['project', 'tags', 'status', 'priority'], column) != -1 && line('.') > 1
|
||||
let filter = substitute(substitute(taskwarrior#data#get_args('modify', [column]), 'tags:', '+', ''), '\v^\s*\+(\s|$)', '', '')
|
||||
elseif column =~ '\v^(entry|end|due)$'
|
||||
let filter = column.'.before:'.input(column.'.before:', taskwarrior#data#get_value_by_column('.', column))
|
||||
elseif column == 'description'
|
||||
let filter = 'description:'.input('description:', taskwarrior#data#get_value_by_column('.', column) )
|
||||
else
|
||||
let filter = input('new filter:', b:filter, 'customlist,taskwarrior#complete#filter')
|
||||
endif
|
||||
let filter = substitute(filter, 'status:\(\s\|$\)', 'status.any: ', 'g')
|
||||
if filter != b:filter
|
||||
let b:filter = filter
|
||||
let b:hist = 1
|
||||
call taskwarrior#list()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#action#command()
|
||||
if len(b:selected) == 0
|
||||
let filter = taskwarrior#data#get_uuid()
|
||||
else
|
||||
let filter = join(b:selected, ',')
|
||||
endif
|
||||
let command = input('task '.filter.':', '', 'customlist,taskwarrior#complete#command')
|
||||
if index(g:task_all_commands, b:command) == -1
|
||||
return
|
||||
endif
|
||||
call taskwarrior#system_call(filter, command, '', 'interactive')
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#action#report()
|
||||
let command = input('new report:', g:task_report_name, 'customlist,taskwarrior#complete#report')
|
||||
if index(g:task_report_command, command) != -1 && command != b:command
|
||||
let b:command = command
|
||||
let b:hist = 1
|
||||
call taskwarrior#list()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#action#paste()
|
||||
if len(b:selected) == 0
|
||||
return
|
||||
elseif len(b:selected) < 3
|
||||
call taskwarrior#system_call(join(b:selected, ','), 'duplicate', '', 'echo')
|
||||
else
|
||||
call taskwarrior#system_call(join(b:selected, ','), 'duplicate', '', 'interactive')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#action#columns_format_change(direction)
|
||||
let ccol = taskwarrior#data#current_column()
|
||||
if !exists('g:task_columns_format[ccol]')
|
||||
return
|
||||
endif
|
||||
let clist = g:task_columns_format[ccol]
|
||||
if len(clist) == 1
|
||||
return
|
||||
endif
|
||||
let ccol_ful = b:task_report_columns[taskwarrior#data#current_index()]
|
||||
let ccol_sub = matchstr(ccol_ful, '\.\zs.*')
|
||||
let rcl = matchstr(b:rc, 'rc\.report\.'.b:command.'\.columns.\zs\S*')
|
||||
" let dfl = system('task _get -- rc.report.'.b:command.'.columns')[0:-2]
|
||||
let dfl = matchstr(system('task show | grep report.'.b:command.'.columns')[0:-2], '\S*$')
|
||||
let index = index(clist, ccol_sub)
|
||||
let index = index == -1 ? 0 : index
|
||||
if a:direction == 'left'
|
||||
let index -= 1
|
||||
else
|
||||
let index += 1
|
||||
if index == len(clist)
|
||||
let index = 0
|
||||
endif
|
||||
endif
|
||||
let newsub = index == 0 ? '' : '.'.clist[index]
|
||||
let b:rc .= ' rc.report.'.b:command.'.columns:'.
|
||||
\ substitute(
|
||||
\ rcl == '' ? dfl : rcl,
|
||||
\ '[=:,]\zs'.ccol_ful.'\ze\(,\|$\)',
|
||||
\ ccol.newsub, ''
|
||||
\ )
|
||||
let b:hist = 1
|
||||
call taskwarrior#list()
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#action#date(count)
|
||||
let ccol = taskwarrior#data#current_column()
|
||||
if index(['due', 'end', 'entry'], ccol) == -1
|
||||
return
|
||||
endif
|
||||
setlocal modifiable
|
||||
if exists('g:loaded_speeddating')
|
||||
call speeddating#increment(a:count)
|
||||
elseif a:count > 0
|
||||
execute 'normal! '.a:count.''
|
||||
else
|
||||
execute 'normal! '.-a:count.''
|
||||
endif
|
||||
let b:ct = taskwarrior#data#get_uuid()
|
||||
call taskwarrior#system_call(b:ct, 'modify', ccol.':'.taskwarrior#data#get_value_by_column('.', ccol, 'temp'), 'silent')
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#action#visual(action) range
|
||||
let line1 = getpos("'<")[1]
|
||||
let line2 = getpos("'>")[1]
|
||||
let fil = []
|
||||
let lin = []
|
||||
for l in range(line1, line2)
|
||||
let uuid = taskwarrior#data#get_uuid(l)
|
||||
if uuid !~ '^\s*$'
|
||||
let fil += [uuid]
|
||||
let lin += [l]
|
||||
endif
|
||||
endfor
|
||||
let filter = join(fil, ',')
|
||||
if a:action == 'done'
|
||||
call taskwarrior#system_call(filter, 'done', '', 'interactive')
|
||||
elseif a:action == 'delete'
|
||||
call taskwarrior#system_call(filter, 'delete', '', 'interactive')
|
||||
elseif a:action == 'info'
|
||||
call taskinfo#init('information', filter, split(system('task rc.color=no information '.filter), '\n'))
|
||||
elseif a:action == 'select'
|
||||
for var in fil
|
||||
let index = index(b:selected, var)
|
||||
if index == -1
|
||||
let b:selected += [var]
|
||||
let b:sline += [lin[index(fil, var)]]
|
||||
else
|
||||
call remove(b:selected, index)
|
||||
call remove(b:sline, index)
|
||||
endif
|
||||
endfor
|
||||
let b:sstring = join(b:selected, ' ')
|
||||
setlocal syntax=taskreport
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#action#move_cursor(direction, mode)
|
||||
let ci = taskwarrior#data#current_index()
|
||||
if ci == -1 || (ci == 0 && a:direction == 'left') || (ci == len(b:task_columns)-1 && a:direction == 'right')
|
||||
return
|
||||
endif
|
||||
if a:direction == 'left'
|
||||
call search('\%'.(b:task_columns[ci-1]+1).'v', 'be')
|
||||
else
|
||||
call search('\%'.(b:task_columns[ci+1]+1).'v', 'e')
|
||||
endif
|
||||
if a:mode == 'skip' && taskwarrior#data#get_value_by_index('.', taskwarrior#data#current_index()) =~ '^\s*$'
|
||||
call taskwarrior#action#move_cursor(a:direction, 'skip')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#action#undo()
|
||||
if has("gui_running")
|
||||
if exists('g:task_gui_term') && g:task_gui_term == 1
|
||||
!task rc.color=off undo
|
||||
elseif executable('xterm')
|
||||
silent !xterm -e 'task undo'
|
||||
elseif executable('urxvt')
|
||||
silent !urxvt -e task undo
|
||||
elseif executable('gnome-terminal')
|
||||
silent !gnome-terminal -e 'task undo'
|
||||
endif
|
||||
else
|
||||
sil !clear
|
||||
!task undo
|
||||
endif
|
||||
call taskwarrior#refresh()
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#action#clear_completed()
|
||||
!task status:completed delete
|
||||
call taskwarrior#refresh()
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#action#sync(action)
|
||||
execute '!task '.a:action.' '
|
||||
call taskwarrior#refresh()
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#action#select()
|
||||
let uuid = taskwarrior#data#get_uuid()
|
||||
if uuid == ''
|
||||
return
|
||||
endif
|
||||
let index = index(b:selected, uuid)
|
||||
if index == -1
|
||||
let b:selected += [uuid]
|
||||
let b:sline += [line('.')]
|
||||
else
|
||||
call remove(b:selected, index)
|
||||
call remove(b:sline, index)
|
||||
endif
|
||||
let b:sstring = join(b:selected, ' ')
|
||||
setlocal syntax=taskreport
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#action#show_info(...)
|
||||
if a:0 > 0
|
||||
let command = 'info'
|
||||
let filter = a:1
|
||||
else
|
||||
let ccol = taskwarrior#data#current_column()
|
||||
let dict = { 'project': 'projects',
|
||||
\ 'tags': 'tags',
|
||||
\ 'id': 'stats',
|
||||
\ 'depends': 'blocking',
|
||||
\ 'recur': 'recurring',
|
||||
\ 'due': 'overdue',
|
||||
\ 'wait': 'waiting',
|
||||
\ 'urgency': 'ready',
|
||||
\ 'entry': 'history.monthly',
|
||||
\ 'end': 'history.monthly'}
|
||||
let command = get(dict, ccol, 'summary')
|
||||
let uuid = taskwarrior#data#get_uuid()
|
||||
if uuid !~ '^\s*$'
|
||||
let command = substitute(command, '\v(summary|stats)', 'information', '')
|
||||
let filter = taskwarrior#data#get_uuid()
|
||||
else
|
||||
let filter = b:filter
|
||||
endif
|
||||
endif
|
||||
call taskinfo#init(command, filter, split(system('task rc.color=no '.command.' '.filter), '\n'))
|
||||
endfunction
|
@ -1,54 +0,0 @@
|
||||
function! taskwarrior#complete#TW(A, L, P)
|
||||
let command = copy(g:task_all_commands)
|
||||
let filter = copy(g:task_filter)
|
||||
let config = copy(g:task_all_configurations)
|
||||
let contexts = split(system('task _context'), '\n')
|
||||
let context_cmd = ['define', 'show', 'list', 'delete']
|
||||
let words = split(a:L, ' ')
|
||||
if len(words) > 1 && words[1] == 'context'
|
||||
if len(words) == 2 || index(context_cmd, words[2]) == -1
|
||||
return filter(context_cmd + contexts + ['none'],
|
||||
\ 'match(v:val, a:A) != -1')
|
||||
elseif words[2] == 'delete'
|
||||
return filter(contexts, 'match(v:val, a:A) != -1')
|
||||
else
|
||||
return []
|
||||
endif
|
||||
endif
|
||||
for ph in words
|
||||
if ph == 'config' || ph == 'show'
|
||||
return filter(config, 'match(v:val, a:A) != -1')
|
||||
elseif ph =~ '^rc\..*'
|
||||
return map(filter(config, 'match(v:val, a:A[3:]) != -1'),
|
||||
\ "'rc.'.v:val")
|
||||
elseif index(command, ph) != -1
|
||||
return filter(filter, 'match(v:val, a:A) != -1')
|
||||
endif
|
||||
endfor
|
||||
return filter(command+filter, 'match(v:val, a:A) != -1')
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#complete#sort(A, L, P)
|
||||
let cols = map(split(system('task _columns'), '\n'),
|
||||
\ 'matchstr(v:val, "^\\w*")')
|
||||
return filter(cols, 'match(v:val, a:A) != -1')
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#complete#filter(A, L, P)
|
||||
let lead = matchstr(a:A, '\S*$')
|
||||
let lead = lead == '' ? '.*' : lead
|
||||
let dict = copy(g:task_filter)
|
||||
for ph in split(a:L, ' ')
|
||||
call remove(dict, index(dict, matchstr(ph, '.*:\ze')))
|
||||
endfor
|
||||
return map(filter(dict, 'match(v:val, lead) != -1'),
|
||||
\ "matchstr(a:L, '.*\\ze\\s\\+\\S*').' '.v:val")
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#complete#command(A, L, P)
|
||||
return filter(copy(g:task_all_commands), 'match(v:val, a:A) != -1')
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#complete#report(A, L, P)
|
||||
return filter(copy(g:task_report_command), 'match(v:val, a:A) != -1')
|
||||
endfunction
|
@ -1,132 +0,0 @@
|
||||
function! taskwarrior#data#get_uuid(...)
|
||||
let line = a:0 == 0 ? '.' : a:1
|
||||
let vol = taskwarrior#data#get_value_by_column(line, 'uuid')
|
||||
let vol = vol =~ '[0-9a-f]\{8}\(-[0-9a-f]\{4}\)\{3}-[0-9a-f]\{12}' ?
|
||||
\ vol : taskwarrior#data#get_value_by_column(line, 'id')
|
||||
return vol =~ '^\s*-*\s*$' ? '' : vol
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#data#get_args(...)
|
||||
if a:0 == 0
|
||||
return
|
||||
elseif a:0 == 1
|
||||
return taskwarrior#data#get_args(a:1, g:task_default_prompt)
|
||||
endif
|
||||
let arg = ' '
|
||||
for key in a:2
|
||||
let default = a:1 == 'modify' ?
|
||||
\ taskwarrior#data#get_value_by_column('.', key)
|
||||
\ : ''
|
||||
let temp = shellescape(input(key.":", default), 1)
|
||||
if key == 'description'
|
||||
let arg .= ' '.temp
|
||||
elseif temp !~ '^[ \t]*$' || a:1 == 'modify'
|
||||
let arg .= ' '.key.':'.temp
|
||||
endif
|
||||
endfor
|
||||
echom arg
|
||||
return arg
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#data#get_value_by_column(line, column, ...)
|
||||
if a:line == 1 || (a:line == '.' && line('.') == 1)
|
||||
return ''
|
||||
endif
|
||||
if a:column == 'id' || a:column == 'uuid' || exists('a:1')
|
||||
let index = match(b:task_report_columns, '^'.a:column.'.*')
|
||||
return taskwarrior#data#get_value_by_index(a:line, index(b:task_report_columns, a:column))
|
||||
else
|
||||
let dict = taskwarrior#data#get_query()
|
||||
let val = get(dict, a:column, '')
|
||||
if type(val) == type('')
|
||||
return val
|
||||
elseif type(val) == type([])
|
||||
return join(val, ' ')
|
||||
else
|
||||
return string(val)
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#data#get_value_by_index(line, index)
|
||||
if exists('b:task_columns[a:index]')
|
||||
return substitute(getline(a:line)[b:task_columns[a:index]:b:task_columns[a:index+1]-1], '\(\s*$\|^\s*\)', '', 'g')
|
||||
endif
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#data#current_index()
|
||||
let i = 0
|
||||
while i < len(b:task_columns) && virtcol('.') >= b:task_columns[i]
|
||||
let i += 1
|
||||
endwhile
|
||||
return i-1
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#data#current_column()
|
||||
return matchstr(b:task_report_columns[taskwarrior#data#current_index()], '^\w\+')
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#data#get_stats(method)
|
||||
let dict = {}
|
||||
if a:method != 'current'
|
||||
let stat = split(system('task '.a:method.' stats'), '\n')
|
||||
else
|
||||
let uuid = taskwarrior#data#get_uuid()
|
||||
let stat = split(system('task '.taskwarrior#data#get_uuid().' stats'), '\n')
|
||||
if uuid == '' || len(stat) < 5
|
||||
return {}
|
||||
endif
|
||||
endif
|
||||
for line in stat[2:-1]
|
||||
if line !~ '^\W*$'
|
||||
let dict[split(line, '\s\s')[0]] = substitute(split(line, '\s\s')[-1], '^\s*', '', '')
|
||||
endif
|
||||
endfor
|
||||
return dict
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#data#get_query(...)
|
||||
let uuid = get(a:, 1, taskwarrior#data#get_uuid())
|
||||
if uuid == ''
|
||||
return {}
|
||||
endif
|
||||
let obj = webapi#json#decode(substitute(system(
|
||||
\ 'task rc.verbose=off '.uuid.' export'),
|
||||
\ '\nConfiguration.*', '', ''))
|
||||
return type(obj) == 3 ? obj[0] : obj
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#data#global_stats()
|
||||
let dict = taskwarrior#data#get_stats(b:filter)
|
||||
return [
|
||||
\ get(dict, 'Pending', 0),
|
||||
\ get(dict, 'Completed', 0),
|
||||
\ get(taskwarrior#data#get_stats(''), 'Pending', 0)
|
||||
\ ]
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#data#category()
|
||||
let dict = {}
|
||||
let dict.Pending = []
|
||||
let dict.Waiting = []
|
||||
let dict.Recurring = []
|
||||
let dict.Completed = []
|
||||
for i in range(2, line('$'))
|
||||
let uuid = taskwarrior#data#get_uuid(i)
|
||||
if uuid == ''
|
||||
continue
|
||||
endif
|
||||
let subdict = taskwarrior#data#get_stats(uuid)
|
||||
if subdict.Pending == '1'
|
||||
let dict.Pending += [i]
|
||||
elseif subdict.Waiting == '1'
|
||||
let dict.Waiting += [i]
|
||||
elseif subdict.Recurring == '1'
|
||||
let dict.Recurring += [i]
|
||||
elseif subdict.Completed == '1'
|
||||
let dict.Completed += [i]
|
||||
endif
|
||||
endfor
|
||||
return dict
|
||||
endfunction
|
@ -1,68 +0,0 @@
|
||||
if !isdirectory(expand(g:task_log_directory))
|
||||
call mkdir(expand(g:task_log_directory), 'p')
|
||||
endif
|
||||
let s:history_file = expand(g:task_log_directory.'/.vim_tw.history')
|
||||
let s:bookmark_file = expand(g:task_log_directory.'/.vim_tw.bookmark')
|
||||
|
||||
function! taskwarrior#log#history(action)
|
||||
if findfile(s:history_file) == ''
|
||||
call writefile([], s:history_file)
|
||||
endif
|
||||
if a:action == 'write' && filewritable(s:history_file) && b:hist == 1
|
||||
let fl = readfile(s:history_file)
|
||||
let numb = len(fl)
|
||||
let last = numb ? substitute(fl[-1], '\v($|\n|\t|\s)', '', 'g') : ''
|
||||
let current = join([b:command, b:filter, b:rc], ' ')
|
||||
if last == substitute(current, '[\t ]', '', 'g')
|
||||
return
|
||||
endif
|
||||
call add(fl, current)
|
||||
if numb >= g:task_log_max
|
||||
call remove(fl, 0)
|
||||
endif
|
||||
call writefile(fl, s:history_file)
|
||||
elseif a:action == 'read' && filereadable(s:history_file)
|
||||
call taskwarrior#init(join(split(readfile(s:history_file)[-1], ' '), ' '))
|
||||
elseif a:action == 'clear'
|
||||
call writefile([], s:history_file)
|
||||
elseif a:action != 'write'
|
||||
let hists = readfile(s:history_file)
|
||||
if a:action == 'previous'
|
||||
if b:hist >= len(hists)
|
||||
return
|
||||
endif
|
||||
let b:hist += 1
|
||||
elseif a:action == 'next'
|
||||
if b:hist == 1
|
||||
return
|
||||
endif
|
||||
let b:hist -= 1
|
||||
endif
|
||||
let hlist = split(substitute(hists[-b:hist], '\v($|\n)', ' ', ''), ' ')
|
||||
if len(hlist) != 3
|
||||
return
|
||||
endif
|
||||
let [b:command, b:filter, b:rc] = hlist
|
||||
call taskwarrior#list()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#log#bookmark(action)
|
||||
if findfile(s:bookmark_file) == ''
|
||||
call writefile([], s:bookmark_file)
|
||||
endif
|
||||
if a:action == 'new' && filewritable(s:bookmark_file)
|
||||
let now = b:command.' '.b:filter.' '.b:rc
|
||||
let ext = readfile(s:bookmark_file)
|
||||
if index(ext, now) == -1
|
||||
execute 'redir >> '.s:bookmark_file
|
||||
silent! echo now
|
||||
redir END
|
||||
echohl String
|
||||
echomsg 'New bookmark added.'
|
||||
echohl None
|
||||
endif
|
||||
elseif a:action == 'clear'
|
||||
call writefile([], s:bookmark_file)
|
||||
endif
|
||||
endfunction
|
@ -1,88 +0,0 @@
|
||||
function! taskwarrior#sort#by_arg(...)
|
||||
let args = substitute(join(a:000, ' '), '\s\+', ',', 'g')
|
||||
let args = substitute(args, '\w\zs,', '-,', 'g')
|
||||
let args = substitute(args, '\w\zs$', '-', '')
|
||||
if args =~ '^\s*$'
|
||||
let b:rc = substitute(b:rc, 'rc.report.'.b:command.'.sort[:=]\S*', '', 'g')
|
||||
else
|
||||
let b:rc .= args == '' ? '' : ' rc.report.'.b:command.'.sort:'.args
|
||||
endif
|
||||
let b:hist = 1
|
||||
call taskwarrior#list()
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#sort#by_column(polarity, column)
|
||||
let fromrc = matchstr(b:rc, 'rc\.report\.'.b:command.'\.sort.\zs\S*')
|
||||
" let default = system('task _get -- rc.report.'.b:command.'.sort')[0:-2]
|
||||
let default = matchstr(system('task show | grep report.'.b:command.'.sort')[0:-2], '\S*$')
|
||||
let colshort = map(copy(b:task_report_columns), 'matchstr(v:val, "^\\w*")')
|
||||
let ccol = index(colshort, a:column) == -1 ?
|
||||
\ taskwarrior#data#current_column() :
|
||||
\ a:column
|
||||
let list = split(fromrc, ',')
|
||||
let ind = index(split(fromrc, '[-+],\='), ccol)
|
||||
let dlist = split(default, ',')
|
||||
let dind = index(split(default, '[-+],\='), ccol)
|
||||
if fromrc == ''
|
||||
if dind != -1
|
||||
if a:polarity == 'm'
|
||||
if dind == 0
|
||||
let dlist[0] = dlist[0][0:-2].(dlist[0][-1:-1] == '+' ? '-' : '+')
|
||||
endif
|
||||
call insert(dlist, remove(dlist, dind))
|
||||
elseif dlist[dind] == ccol.a:polarity
|
||||
return
|
||||
else
|
||||
let dlist[dind] = ccol.a:polarity
|
||||
endif
|
||||
let b:rc .= ' rc.report.'.b:command.'.sort:'.join(dlist, ',')
|
||||
else
|
||||
let polarity = a:polarity == 'm' ? '-' : a:polarity
|
||||
let b:rc .= ' rc.report.'.b:command.'.sort:'.ccol.polarity.','.default
|
||||
endif
|
||||
elseif ind != -1
|
||||
if a:polarity == 'm'
|
||||
if ind == 0
|
||||
let list[0] = list[0][0:-2].(list[0][-1:-1] == '+' ? '-' : '+')
|
||||
else
|
||||
call insert(list, remove(list, ind))
|
||||
endif
|
||||
elseif list[ind] == ccol.a:polarity
|
||||
if a:polarity == '+'
|
||||
call insert(list, remove(list, ind), ind > 1 ? ind-1 : 0)
|
||||
else
|
||||
if ind > len(list)-3
|
||||
call add(list, remove(list, ind))
|
||||
else
|
||||
call insert(list, remove(list, ind), ind+1)
|
||||
endif
|
||||
endif
|
||||
else
|
||||
let list[ind] = ccol.a:polarity
|
||||
endif
|
||||
let g:listabc = list
|
||||
let b:rc = substitute(b:rc, 'report\.'.b:command.'\.sort.'.fromrc, 'report.'.b:command.'.sort:'.join(list, ','), '')
|
||||
else
|
||||
let polarity = a:polarity == 'm' ? '-' : a:polarity
|
||||
let b:rc = substitute(b:rc, 'report\.'.b:command.'\.sort.', 'report.'.b:command.'.sort:'.ccol.polarity.',', '')
|
||||
endif
|
||||
let b:hist = 1
|
||||
call taskwarrior#list()
|
||||
endfunction
|
||||
|
||||
function! taskwarrior#sort#order_list()
|
||||
let fromrc = matchstr(b:rc, 'rc\.report\.'.b:command.'\.sort.\zs\S*')
|
||||
if fromrc == ''
|
||||
" let list = split(system('task _get -- rc.report.'.b:command.'.sort')[0:-2], ',')
|
||||
let list = split(matchstr(system('task show | grep report.'.b:command.'.sort')[0:-2], '\S*$'), ',')
|
||||
else
|
||||
let list = split(fromrc, ',')
|
||||
endif
|
||||
while exists('list[0]') && match(b:task_report_columns, list[0][0:-2]) == -1 && system('task count '.list[0][0:-2].'.any:')[0] == '0'
|
||||
call remove(list, 0)
|
||||
endwhile
|
||||
if len(list) == 0
|
||||
let list = ['status-']
|
||||
endif
|
||||
return list
|
||||
endfunction
|
@ -1,23 +0,0 @@
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
function! unite#kinds#task#define()
|
||||
return s:kind
|
||||
endfunction
|
||||
|
||||
let s:kind = {
|
||||
\ 'name' : 'task',
|
||||
\ 'default_action' : 'show',
|
||||
\ 'action_table': {},
|
||||
\}
|
||||
|
||||
let s:kind.action_table.show = {
|
||||
\ 'description' : 'Show report',
|
||||
\ }
|
||||
|
||||
function! s:kind.action_table.show.func(candidate)
|
||||
call taskwarrior#init(join(split(a:candidate.word, '[ \t]'), ' '))
|
||||
endfunction
|
||||
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
@ -1,68 +0,0 @@
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
let s:template = {
|
||||
\ 'name' : 'task/',
|
||||
\ 'description' : 'vim-taskwarrior ',
|
||||
\ 'filters' : ['matcher_regexp'],
|
||||
\ 'action_table': {},
|
||||
\ 'hooks' : {},
|
||||
\ }
|
||||
|
||||
let s:bookmark = {
|
||||
\ 'name' : 'bookmark',
|
||||
\ 'logfile' : expand(g:task_log_directory.'/.vim_tw.bookmark')
|
||||
\ }
|
||||
|
||||
let s:history = {
|
||||
\ 'name' : 'history',
|
||||
\ 'logfile' : expand(g:task_log_directory.'/.vim_tw.history')
|
||||
\ }
|
||||
|
||||
function! s:make_source(dict)
|
||||
let source = deepcopy(s:template)
|
||||
let source.name .= a:dict.name
|
||||
let source.description .= a:dict.name
|
||||
let source.logfile = a:dict.logfile
|
||||
|
||||
function! source.hooks.on_syntax(args, context)
|
||||
syntax match uniteSource__task_rc /rc.*/ contained containedin=ALL contains=uniteCandidateInputKeyword
|
||||
syntax match uniteSource__task_report /\w\+\ze[ \t]\+/ contained containedin=ALL
|
||||
highlight default link uniteSource__task_rc String
|
||||
highlight default link uniteSource__task_report Keyword
|
||||
endfunction
|
||||
|
||||
function! source.gather_candidates(args, context)
|
||||
if findfile(self.logfile) == ''
|
||||
call writefile([], self.logfile)
|
||||
endif
|
||||
return map(reverse(readfile(self.logfile)),
|
||||
\ '{"word": v:val,
|
||||
\ "kind": "task",
|
||||
\ "source": "task/" . self.name,
|
||||
\ }')
|
||||
endfunction
|
||||
|
||||
let source.action_table.delete = {
|
||||
\ 'description' : 'remove the item',
|
||||
\ }
|
||||
|
||||
function! source.action_table.delete.func(candidate)
|
||||
let current = substitute(a:candidate.word, '\s', '', 'g')
|
||||
let lfile = g:task_log_directory.'/.vim_tw.bookmark'
|
||||
let all = readfile(lfile)
|
||||
let allns = map(copy(all), "substitute(v:val, '[ \t]', '', 'g')")
|
||||
call remove(all, index(allns, current))
|
||||
call writefile(all, lfile)
|
||||
endfunction
|
||||
|
||||
return source
|
||||
endfunction
|
||||
|
||||
function! unite#sources#task#define()
|
||||
return map([s:bookmark, s:history], 's:make_source(v:val)')
|
||||
endfunction
|
||||
|
||||
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
@ -1,135 +0,0 @@
|
||||
" json
|
||||
" Last Change: 2012-03-08
|
||||
" Maintainer: Yasuhiro Matsumoto <mattn.jp@gmail.com>
|
||||
" License: This file is placed in the public domain.
|
||||
" Reference:
|
||||
"
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
function! webapi#json#null()
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! webapi#json#true()
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! webapi#json#false()
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! s:nr2byte(nr)
|
||||
if a:nr < 0x80
|
||||
return nr2char(a:nr)
|
||||
elseif a:nr < 0x800
|
||||
return nr2char(a:nr/64+192).nr2char(a:nr%64+128)
|
||||
else
|
||||
return nr2char(a:nr/4096%16+224).nr2char(a:nr/64%64+128).nr2char(a:nr%64+128)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:nr2enc_char(charcode)
|
||||
if &encoding == 'utf-8'
|
||||
return nr2char(a:charcode)
|
||||
endif
|
||||
let char = s:nr2byte(a:charcode)
|
||||
if strlen(char) > 1
|
||||
let char = strtrans(iconv(char, 'utf-8', &encoding))
|
||||
endif
|
||||
return char
|
||||
endfunction
|
||||
|
||||
function! s:fixup(val, tmp)
|
||||
if type(a:val) == 0
|
||||
return a:val
|
||||
elseif type(a:val) == 1
|
||||
if a:val == a:tmp.'null'
|
||||
return function('webapi#json#null')
|
||||
elseif a:val == a:tmp.'true'
|
||||
return function('webapi#json#true')
|
||||
elseif a:val == a:tmp.'false'
|
||||
return function('webapi#json#false')
|
||||
endif
|
||||
return a:val
|
||||
elseif type(a:val) == 2
|
||||
return a:val
|
||||
elseif type(a:val) == 3
|
||||
return map(a:val, 's:fixup(v:val, a:tmp)')
|
||||
elseif type(a:val) == 4
|
||||
return map(a:val, 's:fixup(v:val, a:tmp)')
|
||||
else
|
||||
return string(a:val)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! webapi#json#decode(json)
|
||||
let json = iconv(a:json, "utf-8", &encoding)
|
||||
if get(g:, 'webapi#json#parse_strict', 1) == 1 && substitute(substitute(substitute(
|
||||
\ json,
|
||||
\ '\\\%(["\\/bfnrt]\|u[0-9a-fA-F]\{4}\)', '\@', 'g'),
|
||||
\ '"[^\"\\\n\r]*\"\|true\|false\|null\|-\?\d\+'
|
||||
\ . '\%(\.\d*\)\?\%([eE][+\-]\{-}\d\+\)\?', ']', 'g'),
|
||||
\ '\%(^\|:\|,\)\%(\s*\[\)\+', '', 'g') !~ '^[\],:{} \t\n]*$'
|
||||
throw json
|
||||
endif
|
||||
let json = substitute(json, '\n', '', 'g')
|
||||
let json = substitute(json, '\\u34;', '\\"', 'g')
|
||||
if v:version >= 703 && has('patch780')
|
||||
let json = substitute(json, '\\u\(\x\x\x\x\)', '\=iconv(nr2char(str2nr(submatch(1), 16), 1), "utf-8", &encoding)', 'g')
|
||||
else
|
||||
let json = substitute(json, '\\u\(\x\x\x\x\)', '\=s:nr2enc_char("0x".submatch(1))', 'g')
|
||||
endif
|
||||
if get(g:, 'webapi#json#allow_nil', 0) != 0
|
||||
let tmp = '__WEBAPI_JSON__'
|
||||
while 1
|
||||
if stridx(json, tmp) == -1
|
||||
break
|
||||
endif
|
||||
let tmp .= '_'
|
||||
endwhile
|
||||
let [null,true,false] = [
|
||||
\ tmp.'null',
|
||||
\ tmp.'true',
|
||||
\ tmp.'false']
|
||||
sandbox let ret = eval(json)
|
||||
call s:fixup(ret, tmp)
|
||||
else
|
||||
let [null,true,false] = [0,1,0]
|
||||
sandbox let ret = eval(json)
|
||||
endif
|
||||
return ret
|
||||
endfunction
|
||||
|
||||
function! webapi#json#encode(val)
|
||||
if type(a:val) == 0
|
||||
return a:val
|
||||
elseif type(a:val) == 1
|
||||
let json = '"' . escape(a:val, '\"') . '"'
|
||||
let json = substitute(json, "\r", '\\r', 'g')
|
||||
let json = substitute(json, "\n", '\\n', 'g')
|
||||
let json = substitute(json, "\t", '\\t', 'g')
|
||||
let json = substitute(json, '\([[:cntrl:]]\)', '\=printf("\x%02d", char2nr(submatch(1)))', 'g')
|
||||
return iconv(json, &encoding, "utf-8")
|
||||
elseif type(a:val) == 2
|
||||
let s = string(a:val)
|
||||
if s == "function('webapi#json#null')"
|
||||
return 'null'
|
||||
elseif s == "function('webapi#json#true')"
|
||||
return 'true'
|
||||
elseif s == "function('webapi#json#false')"
|
||||
return 'false'
|
||||
endif
|
||||
elseif type(a:val) == 3
|
||||
return '[' . join(map(copy(a:val), 'webapi#json#encode(v:val)'), ',') . ']'
|
||||
elseif type(a:val) == 4
|
||||
return '{' . join(map(keys(a:val), 'webapi#json#encode(v:val).":".webapi#json#encode(a:val[v:val])'), ',') . '}'
|
||||
else
|
||||
return string(a:val)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
||||
|
||||
" vim:set et:
|
@ -1,262 +0,0 @@
|
||||
*vim-tw.txt* a vim interface for |Taskwarrior| version 1.1 ~
|
||||
_ _ _ _ ~
|
||||
__ _(_)_ __ ___ | |_ __ _ __| |____ __ ____ _ _ _ _ _(_)___ _ _ ~
|
||||
\ V / | ' \ |___| | _/ _` (_-< / /\ V V / _` | '_| '_| / _ \ '_| ~
|
||||
\_/|_|_|_|_| \__\__,_/__/_\_\ \_/\_/\__,_|_| |_| |_\___/_| ~
|
||||
~
|
||||
~
|
||||
==============================================================================
|
||||
QUICK-REFERENCE *tw-quickref*
|
||||
|
||||
| a = add task | m = modify | S = taskd sync | <F1> = this help |
|
||||
| A = annotate | M = mod prompts | s = sort col | <CR> = task info |
|
||||
| d = task done | q = quit buffer | < = sort incr | <C>c = abort |
|
||||
| D = task delete | r = new report | > = sort decr | <Del> = task/anno |
|
||||
| c = task cmd | u = undo last | <TAB> = next col | <Space> = (de)select |
|
||||
| f = add filter | x = del annot | <S-TAB>= prev col | p = dupe select |
|
||||
| H = cycle fmt l | + = start task | <right>= r field | R = refresh |
|
||||
| L = cycle fmt r | - = stop task | <left> = l field | X = clear done |
|
||||
| J = next entry | K = prev entry | B = new bookmark | o = open annotation |
|
||||
|
||||
==============================================================================
|
||||
CONTENTS *tw-contents*
|
||||
|
||||
01. Intro ........................................................ |Taskwarrior|
|
||||
02. Prerequisites ........................................... |tw-prerequisites|
|
||||
03. Mappings ..................................................... |tw-mappings|
|
||||
04. Commands ..................................................... |tw-commands|
|
||||
4.1 Global Commands ....................................... |tw-gl-commands|
|
||||
4.2 Local Commands ........................................ |tw-lc-commands|
|
||||
05. Customization ........................................... |tw-customization|
|
||||
06. Troubleshooting ....................................... |tw-troubleshooting|
|
||||
07. Contributions ........................................... |tw-contributions|
|
||||
08. License ....................................................... |tw-license|
|
||||
09. Changelog ................................................... |tw-changelog|
|
||||
|
||||
==============================================================================
|
||||
1. INTRODUCTION *Taskwarrior*
|
||||
|
||||
Taskwarrior is a command-line todo list manager. It helps you manage task lists
|
||||
with projects, tags, dates, dependencies, annotations, recurrences and apply
|
||||
complex (or simple) queries with attribute-modifiers, booleans, regex filters,
|
||||
custom attributes and color themes, and any number of built-in or customizable
|
||||
reports. Task keeps data in JSON text files and it's always improving.
|
||||
Find out more at https://taskwarrior.org and read man task and man taskrc.
|
||||
|
||||
vim-taskwarrior is a vim plugin that extends taskwarrior with an interactive
|
||||
interface. It features a rich set of mappings and commands, is easy to customize
|
||||
and makes adding, modifying, sorting and marking tasks done, fast, easy and fun!
|
||||
Homepage: https://github.com/farseer90718/vim-taskwarrior, patches welcome!
|
||||
|
||||
==============================================================================
|
||||
2. PREREQUISITES *tw-prerequisites*
|
||||
|
||||
This plugin requires Taskwarrior 2.2.0 or higher, although > 2.3.x is required
|
||||
for taskd sync functions, and recommended in general, and well worth the price;
|
||||
free :) see: https://taskwarrior.org/download/
|
||||
|
||||
Vim version 7.x is required.
|
||||
|
||||
the vim-airline plugin (https://github.com/bling/vim-airline) is not required
|
||||
but it greatly enhances the status-bar and takes the guess-work out of reports.
|
||||
|
||||
If you experience line-wrapping issues, add the following line to your .vimrc
|
||||
let g:task_rc_override = 'defaultwidth=999'
|
||||
|
||||
==============================================================================
|
||||
3. MAPPING *tw-mappings*
|
||||
|
||||
<Plug>(taskwarrior_quickref) :quick reference
|
||||
<Plug>(taskwarrior_quit) :quit the buffer
|
||||
<Plug>(taskwarrior_skip_left) :move the cursor to the left field, skipping blanks
|
||||
<Plug>(taskwarrior_step_left) :move the cursor to the left field without skipping
|
||||
<Plug>(taskwarrior_skip_right) :... right ...
|
||||
<Plug>(taskwarrior_step_right) :... ...
|
||||
<Plug>(taskwarrior_sort_increase) :increase the priority of current field in sorting
|
||||
<Plug>(taskwarrior_sort_decrease) :decrease ...
|
||||
<Plug>(taskwarrior_sort_inverse) :invert the sorting method of the main field
|
||||
<Plug>(taskwarrior_show_info) :show information
|
||||
<Plug>(taskwarrior_filter) :apply a new filter to the tasks
|
||||
<Plug>(taskwarrior_next_format) :change the format of current field to the next one
|
||||
<Plug>(taskwarrior_previous_format) :... previous ...
|
||||
<Plug>(taskwarrior_next_history) :next history of report
|
||||
<Plug>(taskwarrior_previous_history) :previous ...
|
||||
<Plug>(taskwarrior_new_bookmark) :add a new bookmark for current setup
|
||||
<Plug>(taskwarrior_visual_show_info) :show informations about selected tasks
|
||||
<Plug>(taskwarrior_annotate) :add a new annotation
|
||||
<Plug>(taskwarrior_denotate) :delete the annotation
|
||||
<Plug>(taskwarrior_open_annotate) :open the annotation as a file or url
|
||||
<Plug>(taskwarrior_remove) :remove the task
|
||||
<Plug>(taskwarrior_delete) :remove the task/annotation
|
||||
<Plug>(taskwarrior_new) :new task
|
||||
<Plug>(taskwarrior_command) :apply a command to selected tasks
|
||||
<Plug>(taskwarrior_done) :set the selected tasks done
|
||||
<Plug>(taskwarrior_report) :change report type
|
||||
<Plug>(taskwarrior_refresh) :refresh the buffer
|
||||
<Plug>(taskwarrior_clear_completed) :clear all completed tasks
|
||||
<Plug>(taskwarrior_undo) :undo
|
||||
<Plug>(taskwarrior_sync) :synchronise with the remote server
|
||||
<Plug>(taskwarrior_modify_field) :modify current field of the task
|
||||
<Plug>(taskwarrior_modify_task) :modify the fields specified in |g:task_default_prompt|
|
||||
<Plug>(taskwarrior_paste) :duplicate the selected tasks
|
||||
<Plug>(taskwarrior_start_task) :start a task
|
||||
<Plug>(taskwarrior_stop_task) :stop a task
|
||||
<Plug>(taskwarrior_select) :select a task
|
||||
<Plug>(taskwarrior_increase) :increase a number/date
|
||||
<Plug>(taskwarrior_decrease) :decrease ...
|
||||
<Plug>(taskwarrior_visual_done) :set visual selected tasks done
|
||||
<Plug>(taskwarrior_visual_delete) :delete visual selected tasks
|
||||
<Plug>(taskwarrior_visual_select) :select visual selected tasks
|
||||
|
||||
Default ones
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
<F1> <Plug>(taskwarrior_quickref)
|
||||
q <Plug>(taskwarrior_quit)
|
||||
<left> <Plug>(taskwarrior_skip_left)
|
||||
<S-tab> <Plug>(taskwarrior_step_left)
|
||||
<right> <Plug>(taskwarrior_skip_right)
|
||||
<tab> <Plug>(taskwarrior_step_right)
|
||||
< <Plug>(taskwarrior_sort_increase)
|
||||
> <Plug>(taskwarrior_sort_decrease)
|
||||
s <Plug>(taskwarrior_sort_inverse)
|
||||
<CR> <Plug>(taskwarrior_show_info)
|
||||
f <Plug>(taskwarrior_filter)
|
||||
H <Plug>(taskwarrior_next_format)
|
||||
L <Plug>(taskwarrior_previous_format)
|
||||
J <Plug>(taskwarrior_next_history)
|
||||
K <Plug>(taskwarrior_previous_history)
|
||||
B <Plug>(taskwarrior_new_bookmark)
|
||||
<CR> <Plug>(taskwarrior_visual_show_info)
|
||||
|
||||
A <Plug>(taskwarrior_annotate)
|
||||
x <Plug>(taskwarrior_denotate)
|
||||
o <Plug>(taskwarrior_open_annotate)
|
||||
D <Plug>(taskwarrior_remove)
|
||||
<Del> <Plug>(taskwarrior_delete)
|
||||
a <Plug>(taskwarrior_new)
|
||||
c <Plug>(taskwarrior_command)
|
||||
d <Plug>(taskwarrior_done)
|
||||
r <Plug>(taskwarrior_report)
|
||||
R <Plug>(taskwarrior_refresh)
|
||||
X <Plug>(taskwarrior_clear_completed)
|
||||
u <Plug>(taskwarrior_undo)
|
||||
S <Plug>(taskwarrior_sync)
|
||||
m <Plug>(taskwarrior_modify_field)
|
||||
M <Plug>(taskwarrior_modify_task)
|
||||
p <Plug>(taskwarrior_paste)
|
||||
+ <Plug>(taskwarrior_start_task)
|
||||
- <Plug>(taskwarrior_stop_task)
|
||||
<Space> <Plug>(taskwarrior_select)
|
||||
<C-A> <Plug>(taskwarrior_increase)
|
||||
<C-X> <Plug>(taskwarrior_decrease)
|
||||
d <Plug>(taskwarrior_visual_done)
|
||||
D <Plug>(taskwarrior_visual_delete)
|
||||
<Del> <Plug>(taskwarrior_visual_delete)
|
||||
<Space> <Plug>(taskwarrior_visual_select)
|
||||
|
||||
How to change
|
||||
------------------------------------------------------------------------------
|
||||
Add something like these to your configuration files
|
||||
|
||||
augroup TaskwarriorMapping
|
||||
autocmd!
|
||||
autocmd FileType taskreport nmap <buffer> {key}
|
||||
\ <Plug>(taskwarrior_...)
|
||||
autocmd FileType taskreport nunmap <buffer> {key}
|
||||
augroup END
|
||||
|
||||
==============================================================================
|
||||
4. COMMAND *tw-commands*
|
||||
|
||||
4.1 Global Commands *tw-gl-commands*
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
Almost the same as the shell command 'task' *:TW*
|
||||
Undo last move *:TWUndo*
|
||||
Edit taskrc right away *:TWEditTaskrc*
|
||||
Edit vitrc *:TWEditVitrc*
|
||||
Delete completed tasks *:TWDeleteCompleted*
|
||||
list history records using |unite.vim| *:TWHistory*
|
||||
clear history *:TWHistoryClear*
|
||||
list bookmarks using |unite.vim| *:TWBookmark*
|
||||
clear bookmarks *:TWBookmarkClear*
|
||||
4.2 Local Commands *tw-lc-commands*
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
Add an annotation *:TWAnnotate*
|
||||
Mark task done *:TWComplete*
|
||||
Delete a task *:TWDelete*
|
||||
Delete an annotation *:TWDeleteAnnotation*
|
||||
Make changes to a task interactively *:TWModifyInteractive*
|
||||
Run the info report *:TWReportInfo*
|
||||
Overide the sort method *:TWReportSort*
|
||||
Synchronise with taskd server *:TWSync*
|
||||
Toggle readonly option *:TWToggleReadonly*
|
||||
Toggle highlight field option *:TWToggleHLField*
|
||||
|
||||
==============================================================================
|
||||
5. CUSTOMIZATION *tw-customization*
|
||||
|
||||
*g:task_report_name*
|
||||
Default task report type.
|
||||
Default value is 'next'.
|
||||
*g:task_highlight_field*
|
||||
Whether the field under the cursor is highlighted.
|
||||
Default value is 1.
|
||||
*g:task_readonly*
|
||||
Can not make change to task data when set to 1.
|
||||
Default value is 0.
|
||||
*g:task_rc_override*
|
||||
Allows user to override task configurations. Seperated by space.
|
||||
Default value is ''.
|
||||
*g:task_default_prompt*
|
||||
Default fields to ask when adding a new task.
|
||||
Default value is:
|
||||
['due', 'project', 'priority', 'description', 'tag', 'depends'].
|
||||
*g:task_info_vsplit*
|
||||
Whether the info window is splited vertically.
|
||||
Default value is 0.
|
||||
*g:task_info_size*
|
||||
Info window size.
|
||||
Default value is 15 for horizontal and 50 for vertical.
|
||||
*g:task_info_position*
|
||||
Info window position.
|
||||
Default value is 'belowright'.
|
||||
*g:task_log_directory*
|
||||
Directory to store log files defaults to taskwarrior data.location.
|
||||
Default value is taskwarrior data.location.
|
||||
*g:task_log_max*
|
||||
Max number of historical entries.
|
||||
Default value is 10.
|
||||
*g:task_left_arrow*
|
||||
Forward arrow shown on statusline.
|
||||
Default value is ' <<'
|
||||
*g:task_right_arrow*
|
||||
Backward arrow shown on statusline.
|
||||
Default value is '>> '
|
||||
*g:task_gui_term*
|
||||
Uses gvim's dumb terminal for undo commands when set to 1.
|
||||
Default value is 1
|
||||
|
||||
==============================================================================
|
||||
6. TROUBLESHOOTING *tw-troubleshooting*
|
||||
|
||||
|
||||
==============================================================================
|
||||
7. CONTRIBUTIONS *tw-contributions*
|
||||
|
||||
Contributions and pull requests are welcome.
|
||||
|
||||
==============================================================================
|
||||
8. LICENSE *tw-license*
|
||||
|
||||
MIT License.
|
||||
Copyright © 2013 Zc He.
|
||||
Copyright © 2013 David J Patrick.
|
||||
|
||||
==============================================================================
|
||||
9. CHANGELOG *tw-changelog*
|
||||
|
||||
.. in progress!
|
||||
|
@ -1,145 +0,0 @@
|
||||
setlocal buftype=nofile
|
||||
setlocal nomodifiable
|
||||
setlocal cursorline
|
||||
setlocal startofline
|
||||
setlocal nowrap
|
||||
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_quickref) :h tw-quickref<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_quit) :call taskwarrior#quit()<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_quit_all) :call taskwarrior#quit_all()<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_skip_left) :call taskwarrior#action#move_cursor('left', 'skip')<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_step_left) :call taskwarrior#action#move_cursor('left', 'step')<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_skip_right) :call taskwarrior#action#move_cursor('right', 'skip')<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_step_right) :call taskwarrior#action#move_cursor('right', 'step')<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_sort_increase) :call taskwarrior#sort#by_column('+', '')<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_sort_decrease) :call taskwarrior#sort#by_column('-', '')<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_sort_inverse) :call taskwarrior#sort#by_column('m', '')<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_show_info) :call taskwarrior#action#show_info()<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_filter) :call taskwarrior#action#filter()<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_next_format) :call taskwarrior#action#columns_format_change('left')<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_previous_format) :call taskwarrior#action#columns_format_change('right')<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_next_history) :call taskwarrior#log#history('next')<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_previous_history) :call taskwarrior#log#history('previous')<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_new_bookmark) :call taskwarrior#log#bookmark('new')<CR>
|
||||
vnoremap <silent> <buffer> <Plug>(taskwarrior_visual_show_info) :call taskwarrior#action#visual('info')<CR>
|
||||
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_annotate) :call taskwarrior#action#annotate('add')<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_denotate) :call taskwarrior#action#annotate('del')<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_open_annotate) :call taskwarrior#action#annotate('open')<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_remove) :call taskwarrior#action#remove()<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_delete) :call taskwarrior#action#delete()<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_new) :call taskwarrior#action#new()<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_command) :call taskwarrior#action#command()<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_done) :call taskwarrior#action#set_done()<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_report) :call taskwarrior#action#report()<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_refresh) :call taskwarrior#list()<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_clear_completed) :call taskwarrior#action#clear_completed()<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_undo) :call taskwarrior#action#undo()<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_urgency) :call taskwarrior#action#urgency()<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_sync) :call taskwarrior#action#sync('sync')<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_modify_field) :call taskwarrior#action#modify('current')<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_modify_task) :call taskwarrior#action#modify('')<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_paste) :call taskwarrior#action#paste()<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_start_task) :call taskwarrior#system_call(taskwarrior#data#get_uuid(), 'start', '', 'silent')<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_stop_task) :call taskwarrior#system_call(taskwarrior#data#get_uuid(), 'stop', '', 'silent')<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_select) :call taskwarrior#action#select()<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_increase) :<C-U>call taskwarrior#action#date(v:count1)<CR>
|
||||
nnoremap <silent> <buffer> <Plug>(taskwarrior_decrease) :<C-U>call taskwarrior#action#date(-v:count1)<CR>
|
||||
vnoremap <silent> <buffer> <Plug>(taskwarrior_visual_done) :call taskwarrior#action#visual('done')<CR>
|
||||
vnoremap <silent> <buffer> <Plug>(taskwarrior_visual_delete) :call taskwarrior#action#visual('delete')<CR>
|
||||
vnoremap <silent> <buffer> <Plug>(taskwarrior_visual_select) :call taskwarrior#action#visual('select')<CR>
|
||||
|
||||
nmap <buffer> <F1> <Plug>(taskwarrior_quickref)
|
||||
nmap <buffer> Q <Plug>(taskwarrior_quit_all)
|
||||
nmap <buffer> q <Plug>(taskwarrior_quit)
|
||||
nmap <buffer> <left> <Plug>(taskwarrior_skip_left)
|
||||
nmap <buffer> <S-tab> <Plug>(taskwarrior_step_left)
|
||||
nmap <buffer> <right> <Plug>(taskwarrior_skip_right)
|
||||
nmap <buffer> <tab> <Plug>(taskwarrior_step_right)
|
||||
nmap <buffer> < <Plug>(taskwarrior_sort_increase)
|
||||
nmap <buffer> > <Plug>(taskwarrior_sort_decrease)
|
||||
nmap <buffer> s <Plug>(taskwarrior_sort_inverse)
|
||||
nmap <buffer> <CR> <Plug>(taskwarrior_show_info)
|
||||
nmap <buffer> f <Plug>(taskwarrior_filter)
|
||||
nmap <buffer> H <Plug>(taskwarrior_next_format)
|
||||
nmap <buffer> L <Plug>(taskwarrior_previous_format)
|
||||
nmap <buffer> J <Plug>(taskwarrior_next_history)
|
||||
nmap <buffer> K <Plug>(taskwarrior_previous_history)
|
||||
nmap <buffer> B <Plug>(taskwarrior_new_bookmark)
|
||||
vmap <buffer> <CR> <Plug>(taskwarrior_visual_show_info)
|
||||
|
||||
if g:task_highlight_field
|
||||
autocmd CursorMoved <buffer> :call taskwarrior#hi_field()
|
||||
else
|
||||
autocmd! CursorMoved <buffer>
|
||||
endif
|
||||
|
||||
if g:task_readonly
|
||||
setlocal readonly
|
||||
if hasmapto('<Plug>(taskwarrior_undo)')
|
||||
nunmap <silent> <buffer> A
|
||||
nunmap <silent> <buffer> x
|
||||
nunmap <silent> <buffer> o
|
||||
nunmap <silent> <buffer> D
|
||||
nunmap <silent> <buffer> <Del>
|
||||
nunmap <silent> <buffer> a
|
||||
nunmap <silent> <buffer> c
|
||||
nunmap <silent> <buffer> d
|
||||
nunmap <silent> <buffer> r
|
||||
nunmap <silent> <buffer> R
|
||||
nunmap <silent> <buffer> X
|
||||
nunmap <silent> <buffer> u
|
||||
nunmap <silent> <buffer> S
|
||||
nunmap <silent> <buffer> m
|
||||
nunmap <silent> <buffer> M
|
||||
nunmap <silent> <buffer> p
|
||||
nunmap <silent> <buffer> +
|
||||
nunmap <silent> <buffer> -
|
||||
nunmap <silent> <buffer> <Space>
|
||||
nunmap <silent> <buffer> <C-A>
|
||||
nunmap <silent> <buffer> <C-X>
|
||||
vunmap <silent> <buffer> d
|
||||
vunmap <silent> <buffer> D
|
||||
vunmap <silent> <buffer> <Del>
|
||||
vunmap <silent> <buffer> <Space>
|
||||
endif
|
||||
else
|
||||
nmap <silent> <buffer> A <Plug>(taskwarrior_annotate)
|
||||
nmap <silent> <buffer> x <Plug>(taskwarrior_denotate)
|
||||
nmap <silent> <buffer> o <Plug>(taskwarrior_open_annotate)
|
||||
nmap <silent> <buffer> D <Plug>(taskwarrior_remove)
|
||||
nmap <silent> <buffer> <Del> <Plug>(taskwarrior_delete)
|
||||
nmap <silent> <buffer> a <Plug>(taskwarrior_new)
|
||||
nmap <silent> <buffer> c <Plug>(taskwarrior_command)
|
||||
nmap <silent> <buffer> d <Plug>(taskwarrior_done)
|
||||
nmap <silent> <buffer> r <Plug>(taskwarrior_report)
|
||||
nmap <silent> <buffer> R <Plug>(taskwarrior_refresh)
|
||||
nmap <silent> <buffer> X <Plug>(taskwarrior_clear_completed)
|
||||
nmap <silent> <buffer> u <Plug>(taskwarrior_undo)
|
||||
nmap <silent> <buffer> U <Plug>(taskwarrior_urgency)
|
||||
nmap <silent> <buffer> S <Plug>(taskwarrior_sync)
|
||||
nmap <silent> <buffer> m <Plug>(taskwarrior_modify_field)
|
||||
nmap <silent> <buffer> M <Plug>(taskwarrior_modify_task)
|
||||
nmap <silent> <buffer> p <Plug>(taskwarrior_paste)
|
||||
nmap <silent> <buffer> + <Plug>(taskwarrior_start_task)
|
||||
nmap <silent> <buffer> - <Plug>(taskwarrior_stop_task)
|
||||
nmap <silent> <buffer> <Space> <Plug>(taskwarrior_select)
|
||||
nmap <silent> <buffer> <C-A> <Plug>(taskwarrior_increase)
|
||||
nmap <silent> <buffer> <C-X> <Plug>(taskwarrior_decrease)
|
||||
vmap <silent> <buffer> d <Plug>(taskwarrior_visual_done)
|
||||
vmap <silent> <buffer> D <Plug>(taskwarrior_visual_delete)
|
||||
vmap <silent> <buffer> <Del> <Plug>(taskwarrior_visual_delete)
|
||||
vmap <silent> <buffer> <Space> <Plug>(taskwarrior_visual_select)
|
||||
|
||||
command! -buffer TWAdd :call taskwarrior#action#new()
|
||||
command! -buffer TWAnnotate :call taskwarrior#action#annotate('add')
|
||||
command! -buffer TWComplete :call taskwarrior#action#set_done()
|
||||
command! -buffer TWDelete :call taskwarrior#action#delete()
|
||||
command! -buffer TWDeleteAnnotation :call taskwarrior#action#annotate('del')
|
||||
command! -buffer TWModifyInteractive :call taskwarrior#modify('')
|
||||
command! -buffer TWSync :call taskwarrior#action#sync('sync')
|
||||
endif
|
||||
|
||||
command! -buffer TWToggleReadonly :let g:task_readonly = (g:task_readonly ? 0 : 1) | call taskwarrior#refresh()
|
||||
command! -buffer TWToggleHLField :let g:task_highlight_field = (g:task_highlight_field ? 0 : 1) | call taskwarrior#refresh()
|
||||
command! -buffer -nargs=? -complete=customlist,taskwarrior#complete#sort TWReportSort :call taskwarrior#action#sort_by_arg(<q-args>)
|
@ -1,110 +0,0 @@
|
||||
if exists('g:loaded_taskwarrior') && g:loaded_taskwarrior
|
||||
finish
|
||||
endif
|
||||
|
||||
if !executable('task')
|
||||
echoerr "This plugin depends on taskwarrior(https://taskwarrior.org)."
|
||||
finish
|
||||
endif
|
||||
|
||||
let g:task_report_command = get(g:, 'task_report_command', [])
|
||||
let s:task_report_command = ['active', 'all', 'blocked', 'blocking', 'completed', 'list', 'long', 'ls', 'minimal', 'newest', 'next', 'oldest', 'overdue', 'ready', 'recurring', 'unblocked', 'waiting']
|
||||
let g:task_report_command = extend(s:task_report_command, g:task_report_command)
|
||||
let g:task_interactive_command = ['annotate', 'denotate', 'execute', 'duplicate',
|
||||
\ 'append', 'prepend', 'stop', 'delete', 'done', 'undo',
|
||||
\ 'config', 'edit', 'start', 'sync', 'synchronize', 'add',
|
||||
\ 'modify', 'import', 'colors', 'color', 'logo', 'context']
|
||||
let g:task_filter = ['description:', 'proj:', 'pri:', 'status:', 'tag:', 'due.before:', 'due.after:', 'entry.before', 'entry.after', 'end.before', 'end.after', '+']
|
||||
let g:task_all_commands = split(system('task _command'), '\n')
|
||||
let g:task_all_configurations = split(system('task _config'), '\n')
|
||||
let g:task_report_name = index(g:task_report_command, get(g:, 'task_report_name')) != -1 ? get(g:, 'task_report_name') : 'next'
|
||||
let g:task_highlight_field = get(g:, 'task_highlight_field', 1)
|
||||
let g:task_readonly = get(g:, 'task_readonly', 0)
|
||||
let g:task_rc_override = get(g:, 'task_rc_override', '')
|
||||
let g:task_default_prompt = get(g:, 'task_default_prompt', ['due', 'project', 'priority', 'description', 'tag', 'depends'])
|
||||
let g:task_info_vsplit = get(g:, 'task_info_vsplit', 0)
|
||||
let g:task_info_size = get(g:, 'task_info_size', g:task_info_vsplit? 50 : 15)
|
||||
let g:task_info_position = get(g:, 'task_info_position', 'belowright')
|
||||
" let g:task_log_directory = get(g:, 'task_log_file', system('task _get -- rc.data.location')[0:-2])
|
||||
let g:task_log_directory = get(g:, 'task_log_file', matchstr(system('task show | grep data.location')[0:-2], '\S*$'))
|
||||
let g:task_log_max = get(g:, 'task_log_max', 10)
|
||||
let g:task_left_arrow = get(g:, 'task_left_arrow', ' <<')
|
||||
let g:task_right_arrow = get(g:, 'task_right_arrow', '>> ')
|
||||
let g:task_readonly_symbol = get(g:, 'task_readonly_symbol', ' ')
|
||||
let g:task_gui_term = get(g:, 'task_gui_term', 1)
|
||||
let g:task_columns_format = {
|
||||
\ 'depends': ['list', 'count', 'indicator'],
|
||||
\ 'description': ['combined', 'desc', 'oneline', 'truncated', 'count'],
|
||||
\ 'due': ['formatted', 'julian', 'epoch', 'iso', 'age', 'countdown'],
|
||||
\ 'end': ['formatted', 'julian', 'epoch', 'iso', 'age', 'countdown'],
|
||||
\ 'entry': ['formatted', 'julian', 'epoch', 'iso', 'age', 'countdown'],
|
||||
\ 'id': ['number'],
|
||||
\ 'imask': ['number'],
|
||||
\ 'mask': ['default'],
|
||||
\ 'modified': ['formatted', 'julian', 'epoch', 'iso', 'age', 'countdown'],
|
||||
\ 'parent': ['long', 'short'],
|
||||
\ 'priority': ['short', 'long'],
|
||||
\ 'project': ['full', 'parent', 'indented'],
|
||||
\ 'recur': ['duration', 'indicator'],
|
||||
\ 'scheduled': ['formatted', 'julian', 'epoch', 'iso', 'age', 'countdown'],
|
||||
\ 'start': ['formatted', 'julian', 'epoch', 'iso', 'age', 'countdown', 'active'],
|
||||
\ 'status': ['long', 'short'],
|
||||
\ 'tags': ['list', 'indicator', 'count'],
|
||||
\ 'until': ['formatted', 'julian', 'epoch', 'iso', 'age', 'countdown'],
|
||||
\ 'urgency': ['real', 'integer'],
|
||||
\ 'uuid': ['long', 'short'],
|
||||
\ 'wait': ['formatted', 'julian', 'epoch', 'iso', 'age', 'countdown'] }
|
||||
"
|
||||
"commented out pending taskd collision avoidance
|
||||
"command! TaskPush call tw#remote('push')
|
||||
"command! TaskPull call tw#remote('pull')
|
||||
"command! TaskMerge call tw#remote('merge')
|
||||
"
|
||||
"commands;
|
||||
"
|
||||
command! -nargs=? -complete=customlist,taskwarrior#complete#TW TW :call taskwarrior#init(<q-args>)
|
||||
command! -nargs=? TWReportInfo :call taskwarrior#action#show_info(<q-args>)
|
||||
"command! TWConfigColor
|
||||
command! TWDeleteCompleted :call taskwarrior#action#clear_completed()
|
||||
"command! TWDeleteNote
|
||||
"command! TWEdit
|
||||
"command! TWEditAnnotation
|
||||
"command! TWEditDescription
|
||||
command! TWEditTaskrc :execute "e ".$HOME."/.taskrc"
|
||||
command! TWEditVitrc :execute "e ".$HOME."/.vitrc"
|
||||
command! TWEditTaskopenrc :execute "e ".$HOME."/.taskopenrc"
|
||||
"command! TWExport
|
||||
"command! TWHelp
|
||||
command! TWHistory :Unite task/history
|
||||
command! TWHistoryClear :call taskwarrior#log#history('clear')
|
||||
command! TWBookmark :Unite task/bookmark
|
||||
command! TWBookmarkClear :call taskwarrior#log#bookmark('clear')
|
||||
"command! TWInsert
|
||||
"command! TWImport
|
||||
"command! TWNote
|
||||
"command! TWOpen
|
||||
"command! TWOpenInline
|
||||
"command! TWReport
|
||||
"command! TWReportAgenda
|
||||
"command! TWReportBreak
|
||||
"command! TWReportCalendar
|
||||
"command! TWReportDesc
|
||||
"command! TWReportEdit
|
||||
"command! TWReportGantt
|
||||
"command! TWReportProjects
|
||||
"command! TWReportTags
|
||||
"command! TWSyncFiles
|
||||
"command! TWSyncStatus
|
||||
"command! TWTheme
|
||||
"command! TWThemeEdit
|
||||
"command! TWThemeShow
|
||||
command! TWUndo :call taskwarrior#action#undo()
|
||||
"command! TWWiki
|
||||
"command! TWWikiDiary
|
||||
"command! TWWikiDiaryAdd
|
||||
"command! TWWikiGenIndex
|
||||
"command! TWWikiGenProject
|
||||
"command! TWWikiGenTag
|
||||
"command! TWWikiIndex
|
||||
|
||||
let g:loaded_taskwarrior = 1
|
Binary file not shown.
Before Width: | Height: | Size: 136 KiB |
@ -1,8 +0,0 @@
|
||||
if exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
|
||||
syntax match taskinfo_head /.*\%1l/
|
||||
highlight default link taskinfo_head Tabline
|
||||
|
||||
let b:current_syntax = 'taskinfo'
|
@ -1,43 +0,0 @@
|
||||
if exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
|
||||
if exists('b:task_report_labels')
|
||||
syntax match taskwarrior_tablehead /.*\%1l/
|
||||
endif
|
||||
|
||||
for n in b:sline
|
||||
execute 'syntax match taskwarrior_selected /.*\%'.n.'l/ contains=ALL'
|
||||
endfor
|
||||
|
||||
if search('[^\x00-\xff]') == 0
|
||||
let exp = 'syntax match taskwarrior_%s /\%%>1l\%%%dc.*\%%<%dc/'
|
||||
else
|
||||
let exp = 'syntax match taskwarrior_%s /\%%>1l\%%%dv.*\%%<%dv/'
|
||||
endif
|
||||
|
||||
if exists('b:task_columns') && exists('b:task_report_columns')
|
||||
for i in range(0, len(b:task_report_columns)-1)
|
||||
if exists('b:task_columns['.(i+1).']')
|
||||
execute printf(exp, matchstr(b:task_report_columns[i], '^\w\+') , b:task_columns[i]+1, b:task_columns[i+1]+1)
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
|
||||
highlight default link taskwarrior_tablehead Tabline
|
||||
highlight default link taskwarrior_field IncSearch
|
||||
highlight default link taskwarrior_selected Visual
|
||||
highlight default link taskwarrior_id VarId
|
||||
highlight default link taskwarrior_project String
|
||||
highlight default link taskwarrior_Status Include
|
||||
highlight default link taskwarrior_priority Class
|
||||
highlight default link taskwarrior_due Todo
|
||||
highlight default link taskwarrior_end Keyword
|
||||
highlight default link taskwarrior_description Normal
|
||||
highlight default link taskwarrior_entry Special
|
||||
highlight default link taskwarrior_depends Todo
|
||||
highlight default link taskwarrior_tags Keyword
|
||||
highlight default link taskwarrior_uuid VarId
|
||||
highlight default link taskwarrior_urgency Todo
|
||||
|
||||
let b:current_syntax = 'taskreport'
|
Loading…
Reference in New Issue
Block a user