]> git.madduck.net Git - etc/vim.git/blobdiff - .vim/bundle/vim-lsp/autoload/lsp/internal/ui/quickpick.vim

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Merge commit 'a39f715c13be3352193ffd9c5b7536b8786eff64' as '.vim/bundle/vim-lsp'
[etc/vim.git] / .vim / bundle / vim-lsp / autoload / lsp / internal / ui / quickpick.vim
diff --git a/.vim/bundle/vim-lsp/autoload/lsp/internal/ui/quickpick.vim b/.vim/bundle/vim-lsp/autoload/lsp/internal/ui/quickpick.vim
new file mode 100644 (file)
index 0000000..3fe3755
--- /dev/null
@@ -0,0 +1,461 @@
+" https://github.com/prabirshrestha/quickpick.vim#968f00787c1a118228aee869351e754bec555298
+"    :QuickpickEmbed path=autoload/lsp/internal/ui/quickpick.vim namespace=lsp#internal#ui#quickpick prefix=lsp-quickpick
+
+let s:has_timer = exists('*timer_start') && exists('*timer_stop')
+let s:has_matchfuzzy = exists('*matchfuzzy')
+let s:has_matchfuzzypos = exists('*matchfuzzypos')
+let s:has_proptype = exists('*prop_type_add') && exists('*prop_type_delete')
+
+"
+" is_floating
+"
+if has('nvim')
+  function! s:is_floating(winid) abort
+    if !s:win_exists(a:winid)
+      return 0
+    endif
+    let l:config = nvim_win_get_config(a:winid)
+    return empty(l:config) || !empty(get(l:config, 'relative', ''))
+  endfunction
+else
+  function! s:is_floating(winid) abort
+    return s:win_exists(a:winid) && win_id2win(a:winid) == 0
+  endfunction
+endif
+
+function! s:win_exists(winid) abort
+  return winheight(a:winid) != -1
+endfunction
+
+function! lsp#internal#ui#quickpick#open(opt) abort
+  call lsp#internal#ui#quickpick#close() " hide existing picker if exists
+
+  " when key is empty, item is a string else it is a dict
+  " fitems is filtered items and is the item that is filtered
+  let s:state = extend({
+      \ 'items': [],
+      \ 'highlights': [],
+      \ 'fitems': [],
+      \ 'key': '',
+      \ 'busy': 0,
+      \ 'busyframes': ['-', '\', '|', '/'],
+      \ 'filetype': 'lsp-quickpick',
+      \ 'promptfiletype': 'lsp-quickpick-filter',
+      \ 'input': '',
+      \ 'maxheight': 10,
+      \ 'debounce': 250,
+      \ 'filter': 1,
+      \ }, a:opt)
+
+  let s:inputecharpre = 0
+  let s:state['busyframe'] = 0
+
+  let s:state['bufnr'] = bufnr('%')
+  let s:state['winid'] = win_getid()
+  let s:state['wininfo'] = getwininfo()
+
+  " create result buffer
+  exe printf('keepalt botright 3new %s', s:state['filetype'])
+  let s:state['resultsbufnr'] = bufnr('%')
+  let s:state['resultswinid'] = win_getid()
+  if s:has_proptype
+    call prop_type_add('highlight', { 'highlight': 'Directory', 'bufnr': s:state['resultsbufnr'] })
+  endif
+
+  " create prompt buffer
+  exe printf('keepalt botright 1new %s', s:state['promptfiletype'])
+  let s:state['promptbufnr'] = bufnr('%')
+  let s:state['promptwinid'] = win_getid()
+
+  call win_gotoid(s:state['resultswinid'])
+  call s:set_buffer_options()
+  setlocal cursorline
+  call s:update_items()
+  exec printf('setlocal filetype=' . s:state['filetype'])
+  call s:notify('open', { 'bufnr': s:state['bufnr'], 'winid': s:state['winid'] , 'resultsbufnr': s:state['resultsbufnr'], 'resultswinid': s:state['resultswinid'] })
+
+  call win_gotoid(s:state['promptwinid'])
+  call s:set_buffer_options()
+  call setline(1, s:state['input'])
+
+  " map keys
+  inoremap <buffer><silent> <Plug>(lsp-quickpick-accept) <ESC>:<C-u>call <SID>on_accept()<CR>
+  nnoremap <buffer><silent> <Plug>(lsp-quickpick-accept) :<C-u>call <SID>on_accept()<CR>
+
+  inoremap <buffer><silent> <Plug>(lsp-quickpick-close) <ESC>:<C-u>call lsp#internal#ui#quickpick#close()<CR>
+  nnoremap <buffer><silent> <Plug>(lsp-quickpick-close) :<C-u>call lsp#internal#ui#quickpick#close()<CR>
+
+  inoremap <buffer><silent> <Plug>(lsp-quickpick-cancel) <ESC>:<C-u>call <SID>on_cancel()<CR>
+  nnoremap <buffer><silent> <Plug>(lsp-quickpick-cancel) :<C-u>call <SID>on_cancel()<CR>
+
+  inoremap <buffer><silent> <Plug>(lsp-quickpick-move-next) <C-o>:<C-u>call <SID>on_move_next()<CR>
+  nnoremap <buffer><silent> <Plug>(lsp-quickpick-move-next) :<C-u>call <SID>on_move_next()<CR>
+
+  inoremap <buffer><silent> <Plug>(lsp-quickpick-move-previous) <C-o>:<C-u>call <SID>on_move_previous()<CR>
+  nnoremap <buffer><silent> <Plug>(lsp-quickpick-move-previous) :<C-u>call <SID>on_move_previous()<CR>
+
+  exec printf('setlocal filetype=' . s:state['promptfiletype'])
+
+  if !hasmapto('<Plug>(lsp-quickpick-accept)')
+    imap <buffer><cr> <Plug>(lsp-quickpick-accept)
+    nmap <buffer><cr> <Plug>(lsp-quickpick-accept)
+  endif
+
+  if !hasmapto('<Plug>(lsp-quickpick-cancel)')
+    imap <silent> <buffer> <C-c> <Plug>(lsp-quickpick-cancel)
+    map  <silent> <buffer> <C-c> <Plug>(lsp-quickpick-cancel)
+    imap <silent> <buffer> <Esc> <Plug>(lsp-quickpick-cancel)
+    map  <silent> <buffer> <Esc> <Plug>(lsp-quickpick-cancel)
+  endif
+
+  if !hasmapto('<Plug>(lsp-quickpick-move-next)')
+    imap <silent> <buffer> <C-n> <Plug>(lsp-quickpick-move-next)
+    nmap <silent> <buffer> <C-n> <Plug>(lsp-quickpick-move-next)
+    imap <silent> <buffer> <C-j> <Plug>(lsp-quickpick-move-next)
+    nmap <silent> <buffer> <C-j> <Plug>(lsp-quickpick-move-next)
+  endif
+
+  if !hasmapto('<Plug>(lsp-quickpick-move-previous)')
+    imap <silent> <buffer> <C-p> <Plug>(lsp-quickpick-move-previous)
+    nmap <silent> <buffer> <C-p> <Plug>(lsp-quickpick-move-previous)
+    imap <silent> <buffer> <C-k> <Plug>(lsp-quickpick-move-previous)
+    nmap <silent> <buffer> <C-k> <Plug>(lsp-quickpick-move-previous)
+  endif
+
+  call cursor(line('$'), 0)
+  call feedkeys('i', 'n')
+
+  augroup lsp#internal#ui#quickpick
+    autocmd!
+    autocmd InsertCharPre   <buffer> call s:on_insertcharpre()
+    autocmd TextChangedI    <buffer> call s:on_inputchanged()
+    autocmd InsertEnter     <buffer> call s:on_insertenter()
+    autocmd InsertLeave     <buffer> call s:on_insertleave()
+
+    if exists('##TextChangedP')
+      autocmd TextChangedP  <buffer> call s:on_inputchanged()
+    endif
+  augroup END
+
+  call s:notify_items()
+  call s:notify_selection()
+  call lsp#internal#ui#quickpick#busy(s:state['busy'])
+endfunction
+
+function! s:set_buffer_options() abort
+  " set buffer options
+  abc <buffer>
+  setlocal bufhidden=unload           " unload buf when no longer displayed
+  setlocal buftype=nofile             " buffer is not related to any file<Paste>
+  setlocal noswapfile                 " don't create swap file
+  setlocal nowrap                     " don't soft-wrap
+  setlocal nonumber                   " don't show line numbers
+  setlocal nolist                     " don't use list mode (visible tabs etc)
+  setlocal foldcolumn=0               " don't show a fold column at side
+  setlocal foldlevel=99               " don't fold anything
+  setlocal nospell                    " spell checking off
+  setlocal nobuflisted                " don't show up in the buffer list
+  setlocal textwidth=0                " don't hardwarp (break long lines)
+  setlocal nocursorline               " highlight the line cursor is off
+  setlocal nocursorcolumn             " disable cursor column
+  setlocal noundofile                 " don't enable undo
+  setlocal winfixheight
+  if exists('+colorcolumn') | setlocal colorcolumn=0 | endif
+  if exists('+relativenumber') | setlocal norelativenumber | endif
+  setlocal signcolumn=yes             " for prompt
+endfunction
+
+function! lsp#internal#ui#quickpick#close() abort
+  if !exists('s:state')
+    return
+  endif
+
+  call lsp#internal#ui#quickpick#busy(0)
+
+  call win_gotoid(s:state['winid'])
+  call s:notify('close', { 'bufnr': s:state['bufnr'], 'winid': s:state['winid'], 'resultsbufnr': s:state['resultsbufnr'], 'resultswinid': s:state['winid'] })
+
+  augroup lsp#internal#ui#quickpick
+    autocmd!
+  augroup END
+
+  exe 'silent! bunload! ' . s:state['promptbufnr']
+  exe 'silent! bunload! ' . s:state['resultsbufnr']
+  call s:restore_windows()
+
+  let s:inputecharpre = 0
+
+  unlet s:state
+endfunction
+
+function! s:restore_windows() abort
+  let [l:tabnr, l:_] = win_id2tabwin(s:state['winid'])
+  if l:tabnr == 0
+    return
+  endif
+
+  let l:Resizable = {_, info ->
+        \ info.tabnr == l:tabnr &&
+        \ s:win_exists(info.winid) &&
+        \ !s:is_floating(info.winid)
+        \ }
+  let l:wins_to_resize = sort(filter(s:state['wininfo'], l:Resizable), {l, r -> l.winnr - r.winnr})
+  let l:open_winids_to_resize = map(filter(getwininfo(), l:Resizable), {_, info -> info.winid})
+
+  let l:resize_cmd = ''
+  for l:info in l:wins_to_resize
+    if index(l:open_winids_to_resize, l:info.winid) == -1
+      return
+    endif
+
+    let l:resize_cmd .= printf('%dresize %d | vert %dresize %d |', l:info.winnr, l:info.height, l:info.winnr, l:info.width)
+  endfor
+
+  " winrestcmd repeats :resize commands twice after patch-8.2.2631.
+  " To simulate this behavior, execute the :resize commands twice.
+  " see https://github.com/vim/vim/issues/7988
+  exe 'silent! ' . l:resize_cmd . l:resize_cmd
+endfunction
+
+function! lsp#internal#ui#quickpick#items(items) abort
+  let s:state['items'] = a:items
+  call s:update_items()
+  call s:notify_items()
+  call s:notify_selection()
+endfunction
+
+function! lsp#internal#ui#quickpick#busy(busy) abort
+  let s:state['busy'] = a:busy
+  if a:busy
+    if !has_key(s:state, 'busytimer')
+      let s:state['busyframe'] = 0
+      let s:state['busytimer'] = timer_start(60, function('s:busy_tick'), { 'repeat': -1 })
+    endif
+  else
+    if has_key(s:state, 'busytimer')
+      call timer_stop(s:state['busytimer'])
+      call remove(s:state, 'busytimer')
+      redraw
+      echohl None
+      echo ''
+    endif
+  endif
+endfunction
+
+function! lsp#internal#ui#quickpick#results_winid() abort
+  if exists('s:state')
+    return s:state['resultswinid']
+  else
+    return 0
+  endif
+endfunction
+
+function! s:busy_tick(...) abort
+  let s:state['busyframe'] = s:state['busyframe'] + 1
+  if s:state['busyframe'] >= len(s:state['busyframes'])
+    let s:state['busyframe'] = 0
+  endif
+  redraw
+  echohl Question | echon s:state['busyframes'][s:state['busyframe']]
+  echohl None
+endfunction
+
+function! s:update_items() abort
+  call s:win_execute(s:state['resultswinid'], 'silent! %delete _')
+
+  let s:state['highlights'] = []
+
+  if s:state['filter'] " if filter is enabled
+    if empty(s:trim(s:state['input']))
+      let s:state['fitems'] = s:state['items']
+    else
+      if empty(s:state['key']) " item is string
+        if s:has_matchfuzzypos
+          let l:matchfuzzyresult = matchfuzzypos(s:state['items'], s:state['input'])
+          let l:fitems = l:matchfuzzyresult[0]
+          let l:highlights = l:matchfuzzyresult[1]
+          let s:state['fitems'] = l:fitems
+          let s:state['highlights'] = l:highlights
+        elseif s:has_matchfuzzy
+          let s:state['fitems'] = matchfuzzy(s:state['items'], s:state['input'])
+        else
+          let s:state['fitems'] = filter(copy(s:state['items']), 'stridx(toupper(v:val), toupper(s:state["input"])) >= 0')
+        endif
+      else " item is dict
+        if s:has_matchfuzzypos
+          " vim requires matchfuzzypos to have highlights.
+          " matchfuzzy only patch doesn't support dict search
+          let l:matchfuzzyresult = matchfuzzypos(s:state['items'], s:state['input'], { 'key': s:state['key'] })
+          let l:fitems = l:matchfuzzyresult[0]
+          let l:highlights = l:matchfuzzyresult[1]
+          let s:state['fitems'] = l:fitems
+          let s:state['highlights'] = l:highlights
+        else
+          let s:state['fitems'] = filter(copy(s:state['items']), 'stridx(toupper(v:val[s:state["key"]]), toupper(s:state["input"])) >= 0')
+        endif
+      endif
+    endif
+  else " if filter is disabled
+    let s:state['fitems'] = s:state['items']
+  endif
+
+
+  if empty(s:state['key']) " item is string
+    let l:lines = s:state['fitems']
+  else " item is dict
+    let l:lines = map(copy(s:state['fitems']), 'v:val[s:state["key"]]')
+  endif
+
+  call setbufline(s:state['resultsbufnr'], 1, l:lines)
+
+  if s:has_proptype && !empty(s:state['highlights'])
+    let l:i = 0
+    for l:line in s:state['highlights']
+      for l:pos in l:line
+        let l:cs = split(getbufline(s:state['resultsbufnr'], l:i + 1)[0], '\zs')
+        let l:mpos = strlen(join(l:cs[: l:pos - 1], ''))
+        let l:len =  strlen(l:cs[l:pos])
+        call prop_add(l:i + 1, l:mpos + 1, { 'length': l:len, 'type': 'highlight', 'bufnr': s:state['resultsbufnr'] })
+      endfor
+      let l:i += 1
+    endfor
+  endif
+
+  call s:win_execute(s:state['resultswinid'], printf('resize %d', min([len(s:state['fitems']), s:state['maxheight']])))
+  call s:win_execute(s:state['promptwinid'], 'resize 1')
+endfunction
+
+function! s:on_accept() abort
+  if win_gotoid(s:state['resultswinid'])
+    let l:index = line('.') - 1 " line is 1 index, list is 0 index
+    let l:fitems = s:state['fitems']
+    if l:index < 0 || len(l:fitems) <= l:index
+      let l:items = []
+    else
+      let l:items = [l:fitems[l:index]]
+    endif
+    call win_gotoid(s:state['winid'])
+    call s:notify('accept', { 'items': l:items })
+  end
+endfunction
+
+function! s:on_cancel() abort
+  call win_gotoid(s:state['winid'])
+  call s:notify('cancel', {})
+  call lsp#internal#ui#quickpick#close()
+endfunction
+
+function! s:on_move_next() abort
+  let l:col = col('.')
+  call s:win_execute(s:state['resultswinid'], 'normal! j')
+  call s:notify_selection()
+endfunction
+
+function! s:on_move_previous() abort
+  let l:col = col('.')
+  call s:win_execute(s:state['resultswinid'], 'normal! k')
+  call s:notify_selection()
+endfunction
+
+function! s:notify_items() abort
+  " items could be huge, so don't send the items as part of data
+  call s:notify('items', { 'bufnr': s:state['bufnr'], 'winid': s:state['winid'], 'resultsbufnr': s:state['resultsbufnr'], 'resultswinid': s:state['resultswinid'] })
+endfunction
+
+function! s:notify_selection() abort
+  let l:original_winid = win_getid()
+  call win_gotoid(s:state['resultswinid'])
+  let l:index = line('.') - 1 " line is 1 based, list is 0 based
+  if l:index < 0 || ((l:index + 1) > len(s:state['fitems']))
+    let l:items = []
+  else
+    let l:items = [s:state['fitems'][l:index]]
+  endif
+  let l:data = {
+    \ 'bufnr': s:state['bufnr'],
+    \ 'winid': s:state['winid'],
+    \ 'resultsbufnr': s:state['resultsbufnr'],
+    \ 'resultswinid': s:state['resultswinid'],
+    \ 'items': l:items,
+    \ }
+  noautocmd call win_gotoid(s:state['winid'])
+  call s:notify('selection', l:data)
+  noautocmd call win_gotoid(l:original_winid)
+endfunction
+
+function! s:on_inputchanged() abort
+  if s:inputecharpre
+    if s:has_timer && s:state['debounce'] > 0
+      call s:debounce_onchange()
+    else
+      call s:notify_onchange()
+    endif
+  endif
+endfunction
+
+function! s:on_insertcharpre() abort
+  let s:inputecharpre = 1
+endfunction
+
+function! s:on_insertenter() abort
+  let s:inputecharpre = 0
+endfunction
+
+function! s:on_insertleave() abort
+  if s:has_timer && has_key(s:state, 'debounce_onchange_timer')
+    call timer_stop(s:state['debounce_onchange_timer'])
+    call remove(s:state, 'debounce_onchange_timer')
+  endif
+endfunction
+
+function! s:debounce_onchange() abort
+  if has_key(s:state, 'debounce_onchange_timer')
+    call timer_stop(s:state['debounce_onchange_timer'])
+    call remove(s:state, 'debounce_onchange_timer')
+  endif
+  let s:state['debounce_onchange_timer'] = timer_start(s:state['debounce'], function('s:notify_onchange'))
+endfunction
+
+function! s:notify_onchange(...) abort
+  let s:state['input'] = getbufline(s:state['promptbufnr'], 1)[0]
+  call s:notify('change', { 'input': s:state['input'] })
+  if s:state['filter']
+    call s:update_items()
+    call s:notify_selection()
+  endif
+endfunction
+
+function! s:notify(name, data) abort
+  if has_key(s:state, 'on_event') | call s:state['on_event'](a:data, a:name) | endif
+  if has_key(s:state, 'on_' . a:name) | call s:state['on_' . a:name](a:data, a:name) | endif
+endfunction
+
+if exists('*win_execute')
+  function! s:win_execute(win_id, cmd) abort
+    call win_execute(a:win_id, a:cmd)
+  endfunction
+else
+  function! s:win_execute(winid, cmd) abort
+    let l:original_winid = win_getid()
+    if l:original_winid == a:winid
+      exec a:cmd
+    else
+      if win_gotoid(a:winid)
+        exec a:cmd
+        call win_gotoid(l:original_winid)
+      end
+    endif
+  endfunction
+endif
+
+if exists('*trim')
+  function! s:trim(str) abort
+    return trim(a:str)
+  endfunction
+else
+  function! s:trim(str) abort
+    return substitute(a:str, '^\s*\|\s*$', '', 'g')
+  endfunction
+endif
+
+" vim: set sw=2 ts=2 sts=2 et tw=78 foldmarker={{{,}}} foldmethod=marker spell: