scriptencoding utf-8 let s:is_vim = !has('nvim') let s:prefix = '[List Preview]' let s:sign_group = 'CocList' " filetype detect could be slow. let s:filetype_map = { \ 'c': 'c', \ 'py': 'python', \ 'vim': 'vim', \ 'ts': 'typescript', \ 'js': 'javascript', \ 'html': 'html', \ 'css': 'css' \ } let s:pwinid = -1 let s:pbufnr = -1 let s:match_ns = coc#highlight#create_namespace('list-match') let s:sign_range = 'CocCursorLine' let s:sign_popup_range = 'PopUpCocList' let s:current_line_hl = 'CocListCurrent' function! coc#list#getchar() abort return coc#prompt#getchar() endfunction function! coc#list#setlines(bufnr, lines, append) if a:append silent call appendbufline(a:bufnr, '$', a:lines) else if exists('*deletebufline') silent call deletebufline(a:bufnr, len(a:lines) + 1, '$') else let n = len(a:lines) + 1 let saved_reg = @" silent execute n.',$d' let @" = saved_reg endif silent call setbufline(a:bufnr, 1, a:lines) endif endfunction function! coc#list#options(...) let list = ['--top', '--tab', '--normal', '--no-sort', '--input=', '--strict', \ '--regex', '--interactive', '--number-select', '--auto-preview', \ '--ignore-case', '--no-quit', '--first', '--reverse', '--height='] if get(g:, 'coc_enabled', 0) let names = coc#rpc#request('listNames', []) call extend(list, names) endif return join(list, "\n") endfunction function! coc#list#names(...) abort let names = coc#rpc#request('listNames', []) return join(names, "\n") endfunction function! coc#list#status(name) if !exists('b:list_status') | return '' | endif return get(b:list_status, a:name, '') endfunction function! coc#list#create(position, height, name, numberSelect) if a:position ==# 'tab' call coc#ui#safe_open('silent tabe', 'list:///'.a:name) else call s:save_views(-1) let height = max([1, a:height]) let cmd = 'silent keepalt '.(a:position ==# 'top' ? '' : 'botright').height.'sp' call coc#ui#safe_open(cmd, 'list:///'.a:name) call s:set_height(height) call s:restore_views() endif if a:numberSelect setl norelativenumber setl number else setl nonumber setl norelativenumber endif setl colorcolumn="" return [bufnr('%'), win_getid(), tabpagenr()] endfunction " close list windows function! coc#list#clean_up() abort for i in range(1, winnr('$')) let bufname = bufname(winbufnr(i)) if bufname =~# 'list://' execute i.'close!' endif endfor endfunction function! coc#list#setup(source) let b:list_status = {} setl buftype=nofile nobuflisted nofen nowrap setl norelativenumber bufhidden=wipe nocursorline winfixheight setl tabstop=1 nolist nocursorcolumn undolevels=-1 setl signcolumn=auto if exists('&cursorlineopt') setl cursorlineopt=both endif if s:is_vim setl nocursorline else setl cursorline setl winhighlight=CursorLine:CocListLine endif if has('nvim-0.5.0') || has('patch-8.1.0864') setl scrolloff=0 endif setl filetype=list syntax case ignore let source = a:source[8:] let name = toupper(source[0]).source[1:] execute 'syntax match Coc'.name.'Line /\v^.*$/' if !s:is_vim " Repeat press and would invoke on vim nnoremap c endif endfunction function! coc#list#close(winid, position, target_win, saved_height) abort let tabnr = coc#window#tabnr(a:winid) if a:position ==# 'tab' if tabnr != -1 call coc#list#close_preview(tabnr, 0) endif call coc#window#close(a:winid) else call s:save_views(a:winid) if tabnr != -1 call coc#list#close_preview(tabnr, 0) endif if type(a:target_win) == v:t_number call win_gotoid(a:target_win) endif call coc#window#close(a:winid) call s:restore_views() if type(a:saved_height) == v:t_number call coc#window#set_height(a:target_win, a:saved_height) endif " call coc#rpc#notify('Log', ["close", a:target_win, v]) endif endfunction function! coc#list#select(bufnr, line) abort if s:is_vim && !empty(a:bufnr) && bufloaded(a:bufnr) call sign_unplace(s:sign_group, { 'buffer': a:bufnr }) if a:line > 0 call sign_place(6, s:sign_group, s:current_line_hl, a:bufnr, {'lnum': a:line}) endif endif endfunction " Check if previewwindow exists on current tab. function! coc#list#has_preview() if s:pwinid != -1 && coc#window#visible(s:pwinid) return 1 endif for i in range(1, winnr('$')) let preview = getwinvar(i, 'previewwindow', getwinvar(i, '&previewwindow', 0)) if preview return i endif endfor return 0 endfunction " Get previewwindow from tabnr, use 0 for current tab function! coc#list#get_preview(...) abort if s:pwinid != -1 && coc#window#visible(s:pwinid) return s:pwinid endif let tabnr = get(a:, 1, 0) == 0 ? tabpagenr() : a:1 let info = gettabinfo(tabnr) if !empty(info) for win in info[0]['windows'] if gettabwinvar(tabnr, win, 'previewwindow', 0) return win endif endfor endif return -1 endfunction function! coc#list#scroll_preview(dir) abort let winid = coc#list#get_preview() if winid == -1 return endif if exists('*win_execute') call win_execute(winid, "normal! ".(a:dir ==# 'up' ? "\" : "\")) else let id = win_getid() noa call win_gotoid(winid) execute "normal! ".(a:dir ==# 'up' ? "\" : "\") noa call win_gotoid(id) endif endfunction function! coc#list#close_preview(...) abort let tabnr = get(a:, 1, tabpagenr()) let winid = coc#list#get_preview(tabnr) if winid != -1 let keep = get(a:, 2, 1) && tabnr == tabpagenr() && !coc#window#is_float(winid) if keep call s:save_views(winid) endif call coc#window#close(winid) if keep call s:restore_views() endif endif endfunction function! s:get_preview_lines(lines, config) abort if empty(a:lines) if get(a:config, 'scheme', 'file') !=# 'file' let bufnr = s:load_buffer(get(a:config, 'name', '')) return bufnr == 0 ? [''] : getbufline(bufnr, 1, '$') else return [''] endif endif return a:lines endfunction function! coc#list#float_preview(lines, config) abort let position = get(a:config, 'position', 'bottom') if position ==# 'tab' throw 'unable to use float preview' endif let remain = 0 let winrow = win_screenpos(winnr())[0] if position ==# 'bottom' let remain = winrow - 2 else let winbottom = winrow + winheight(winnr()) let remain = &lines - &cmdheight - 1 - winbottom endif let lines = s:get_preview_lines(a:lines, a:config) let height = s:get_preview_height(lines, a:config) let height = min([remain, height + 2]) if height < 0 return endif let row = position ==# 'bottom' ? winrow - 3 - height : winrow + winheight(winnr()) let title = fnamemodify(get(a:config, 'name', ''), ':.') let total = get(get(b:, 'list_status', {}), 'total', 0) if !empty(total) let title .= ' ('.line('.').'/'.total.')' endif let lnum = min([get(a:config, 'lnum', 1), len(lines)]) let opts = { \ 'relative': 'editor', \ 'width': winwidth(winnr()) - 2, \ 'borderhighlight': 'MoreMsg', \ 'highlight': 'Normal', \ 'height': height, \ 'col': 0, \ 'index': lnum - 1, \ 'row': row, \ 'border': [1,1,1,1], \ 'rounded': 1, \ 'lines': lines, \ 'scrollinside': 1, \ 'title': title, \ } let result = coc#float#create_float_win(s:pwinid, s:pbufnr, opts) if empty(result) return endif let s:pwinid = result[0] let s:pbufnr = result[1] call setwinvar(s:pwinid, 'previewwindow', 1) let topline = s:get_topline(a:config, lnum, height) call coc#window#restview(s:pwinid, lnum, topline) call s:preview_highlights(s:pwinid, s:pbufnr, a:config, 1) endfunction " Improve preview performance by reused window & buffer. " lines - list of lines " config.position - could be 'bottom' 'top' 'tab'. " config.winid - id of original window. " config.name - (optional )name of preview buffer. " config.splitRight - (optional) split to right when 1. " config.lnum - (optional) current line number " config.filetype - (optional) filetype of lines. " config.range - (optional) highlight range. with hlGroup. " config.hlGroup - (optional) highlight group. " config.maxHeight - (optional) max height of window, valid for 'bottom' & 'top' position. function! coc#list#preview(lines, config) abort let lines = s:get_preview_lines(a:lines, a:config) let winid = coc#list#get_preview(0) let bufnr = winid == -1 ? 0 : winbufnr(winid) " Try reuse buffer & window let bufnr = coc#float#create_buf(bufnr, lines) if bufnr == 0 return endif let lnum = get(a:config, 'lnum', 1) let position = get(a:config, 'position', 'bottom') let original = get(a:config, 'winid', -1) if winid == -1 let change = position != 'tab' && get(a:config, 'splitRight', 0) let curr = win_getid() if change if original && win_id2win(original) noa call win_gotoid(original) else noa wincmd t endif execute 'noa belowright vert sb '.bufnr let winid = win_getid() elseif position == 'tab' || get(a:config, 'splitRight', 0) execute 'noa belowright vert sb '.bufnr let winid = win_getid() else let mod = position == 'top' ? 'below' : 'above' let height = s:get_preview_height(lines, a:config) call s:save_views(-1) execute 'noa '.mod.' sb +resize\ '.height.' '.bufnr call s:restore_views() let winid = win_getid() endif call setbufvar(bufnr, '&synmaxcol', 500) noa call winrestview({"lnum": lnum ,"topline":s:get_topline(a:config, lnum, winheight(winid))}) call s:set_preview_options(winid) noa call win_gotoid(curr) else let height = s:get_preview_height(lines, a:config) if height > 0 if s:is_vim let curr = win_getid() noa call win_gotoid(winid) execute 'silent! noa resize '.height noa call win_gotoid(curr) else call s:save_views(winid) call nvim_win_set_height(winid, height) call s:restore_views() endif endif call coc#window#restview(winid, lnum, s:get_topline(a:config, lnum, height)) endif call s:preview_highlights(winid, bufnr, a:config, 0) endfunction function! s:preview_highlights(winid, bufnr, config, float) abort let name = fnamemodify(get(a:config, 'name', ''), ':.') let newname = s:prefix.' '.name if newname !=# bufname(a:bufnr) if s:is_vim call win_execute(a:winid, 'noa file '.fnameescape(newname), 'silent!') else silent! noa call nvim_buf_set_name(a:bufnr, newname) endif endif let filetype = get(a:config, 'filetype', '') let extname = matchstr(name, '\.\zs[^.]\+$') if empty(filetype) && !empty(extname) let filetype = get(s:filetype_map, extname, '') endif " highlights let sign_group = s:is_vim && a:float ? s:sign_popup_range : s:sign_range call coc#compat#execute(a:winid, ['syntax clear', 'call clearmatches()']) call sign_unplace(sign_group, {'buffer': a:bufnr}) let lnum = get(a:config, 'lnum', 1) if !empty(filetype) let start = max([0, lnum - 300]) let end = min([coc#compat#buf_line_count(a:bufnr), lnum + 300]) call coc#highlight#highlight_lines(a:winid, [{'filetype': filetype, 'startLine': start, 'endLine': end}]) call coc#compat#execute(a:winid, 'syn sync fromstart') else call coc#compat#execute(a:winid, 'filetype detect') let ft = getbufvar(a:bufnr, '&filetype', '') if !empty(extname) && !empty(ft) let s:filetype_map[extname] = ft endif endif " selection range let targetRange = get(a:config, 'targetRange', v:null) if !empty(targetRange) for lnum in range(targetRange['start']['line'] + 1, targetRange['end']['line'] + 1) call sign_place(0, sign_group, s:current_line_hl, a:bufnr, {'lnum': lnum}) endfor endif let range = get(a:config, 'range', v:null) if !empty(range) let hlGroup = get(a:config, 'hlGroup', 'Search') call coc#highlight#match_ranges(a:winid, a:bufnr, [range], hlGroup, 10) endif endfunction function! s:get_preview_height(lines, config) abort if get(a:config, 'splitRight', 0) || get(a:config, 'position', 'bottom') == 'tab' return 0 endif let height = min([get(a:config, 'maxHeight', 10), len(a:lines), &lines - &cmdheight - 2]) return height endfunction function! s:load_buffer(name) abort if exists('*bufadd') && exists('*bufload') let bufnr = bufadd(a:name) call bufload(bufnr) return bufnr endif return 0 endfunction function! s:get_topline(config, lnum, winheight) abort let toplineStyle = get(a:config, 'toplineStyle', 'offset') if toplineStyle == 'middle' return max([1, a:lnum - a:winheight/2]) endif let toplineOffset = get(a:config, 'toplineOffset', 3) return max([1, a:lnum - toplineOffset]) endfunction function! s:set_preview_options(winid) abort call setwinvar(a:winid, '&foldmethod', 'manual') call setwinvar(a:winid, '&foldenable', 0) call setwinvar(a:winid, '&signcolumn', 'no') call setwinvar(a:winid, '&number', 1) call setwinvar(a:winid, '&cursorline', 0) call setwinvar(a:winid, '&relativenumber', 0) call setwinvar(a:winid, 'previewwindow', 1) endfunction " save views on current tabpage function! s:save_views(exclude) abort " Not work as expected when cursor becomes hidden if s:is_vim return endif for nr in range(1, winnr('$')) let winid = win_getid(nr) if winid != a:exclude && getwinvar(nr, 'previewwindow', 0) == 0 && !coc#window#is_float(winid) call coc#compat#execute(winid, 'let w:coc_list_saved_view = winsaveview()') endif endfor endfunction function! s:restore_views() abort if s:is_vim return endif for nr in range(1, winnr('$')) let saved = getwinvar(nr, 'coc_list_saved_view', v:null) if !empty(saved) let winid = win_getid(nr) call coc#compat#execute(winid, 'noa call winrestview(w:coc_list_saved_view) | unlet w:coc_list_saved_view') endif endfor endfunction function! s:set_height(height) abort let curr = winheight(0) if curr != a:height execute 'resize '.a:height endif endfunction