" Related to float window create let s:is_vim = !has('nvim') let s:borderchars = get(g:, 'coc_borderchars', \ ['─', '│', '─', '│', '┌', '┐', '┘', '└']) let s:prompt_win_width = get(g:, 'coc_prompt_win_width', 32) let s:scrollbar_ns = exists('*nvim_create_namespace') ? nvim_create_namespace('coc-scrollbar') : 0 " winvar: border array of numbers, button boolean " detect if there's float window/popup created by coc.nvim function! coc#float#has_float() abort if s:is_vim if !exists('*popup_list') return 0 endif let arr = filter(popup_list(), 'getwinvar(v:val,"float",0)&&popup_getpos(v:val)["visible"]') return !empty(arr) endif for i in range(1, winnr('$')) if getwinvar(i, 'float') return 1 endif endfor return 0 endfunction function! coc#float#close_all() abort if !has('nvim') && exists('*popup_clear') call popup_clear() return endif let winids = coc#float#get_float_win_list() for id in winids call coc#float#close(id) endfor endfunction function! coc#float#jump() abort if s:is_vim return endif let winids = coc#float#get_float_win_list() if !empty(winids) call win_gotoid(winids[0]) endif endfunction function! coc#float#get_float_mode(lines, config) abort let allowSelection = get(a:config, 'allowSelection', 0) let pumAlignTop = get(a:config, 'pumAlignTop', 0) let mode = mode() let checked = (mode == 's' && allowSelection) || index(['i', 'n', 'ic'], mode) != -1 if !checked return v:null endif if !s:is_vim && mode ==# 'i' " helps to fix undo issue, don't know why. call feedkeys("\u", 'n') endif let dimension = coc#float#get_config_cursor(a:lines, a:config) if empty(dimension) return v:null endif if pumvisible() && ((pumAlignTop && dimension['row'] <0)|| (!pumAlignTop && dimension['row'] > 0)) return v:null endif return [mode, bufnr('%'), [line('.'), col('.')], dimension] endfunction " create/reuse float window for config position, config including: " - line: line count relative to cursor, nagetive number means abover cursor. " - col: column count relative to cursor, nagetive number means left of cursor. " - width: content width without border and title. " - height: content height without border and title. " - title: (optional) title. " - border: (optional) border as number list, like [1, 1, 1 ,1]. " - cursorline: (optional) enable cursorline when is 1. " - autohide: (optional) window should be closed on CursorMoved when is 1. function! coc#float#create_float_win(winid, bufnr, config) abort call coc#float#close_auto_hide_wins(a:winid) " use exists if a:winid && coc#float#valid(a:winid) if s:is_vim let [line, col] = s:popup_position(a:config) call popup_move(a:winid, { \ 'line': line, \ 'col': col, \ 'minwidth': a:config['width'], \ 'minheight': a:config['height'], \ 'maxwidth': a:config['width'], \ 'maxheight': a:config['height'], \ }) let opts = { \ 'cursorline': get(a:config, 'cursorline', 0), \ 'title': get(a:config, 'title', ''), \ } if !s:empty_border(get(a:config, 'border', [])) let opts['border'] = a:config['border'] endif call popup_setoptions(a:winid, opts) return [a:winid, winbufnr(a:winid)] else let config = s:convert_config_nvim(a:config) " not reuse related windows call coc#float#nvim_close_related(a:winid) call nvim_win_set_config(a:winid, config) call coc#float#nvim_create_related(a:winid, config, a:config) return [a:winid, winbufnr(a:winid)] endif endif let winid = 0 if s:is_vim let [line, col] = s:popup_position(a:config) let bufnr = coc#float#create_float_buf(a:bufnr) let title = get(a:config, 'title', '') let opts = { \ 'title': title, \ 'line': line, \ 'col': col, \ 'padding': empty(title) ? [0, 1, 0, 1] : [0, 0, 0, 0], \ 'borderchars': s:borderchars, \ 'highlight': 'CocFloating', \ 'fixed': 1, \ 'cursorline': get(a:config, 'cursorline', 0), \ 'minwidth': a:config['width'], \ 'minheight': a:config['height'], \ 'maxwidth': a:config['width'], \ 'maxheight': a:config['height'] \ } if get(a:config, 'close', 0) let opts['close'] = 'button' endif if !s:empty_border(get(a:config, 'border', [])) let opts['border'] = a:config['border'] endif let winid = popup_create(bufnr, opts) if winid == 0 return [] endif if has("patch-8.1.2281") call setwinvar(winid, '&showbreak', 'NONE') endif else let config = s:convert_config_nvim(a:config) let bufnr = coc#float#create_float_buf(a:bufnr) let winid = nvim_open_win(bufnr, 0, config) if winid == 0 return [] endif call setwinvar(winid, '&winhl', 'Normal:CocFloating,NormalNC:CocFloating,FoldColumn:CocFloating,CursorLine:CocMenuSel') call setwinvar(winid, '&signcolumn', 'no') " no left border if s:empty_border(get(a:config, 'border', [])) || a:config['border'][3] == 0 call setwinvar(winid, '&foldcolumn', 1) endif call coc#float#nvim_create_related(winid, config, a:config) endif if !s:is_vim " change cursorline option affects vim's own highlight call setwinvar(winid, '&cursorline', get(a:config, 'cursorline', 0)) call setwinvar(winid, 'border', get(a:config, 'border', [])) endif if get(a:config, 'autohide', 0) call setwinvar(winid, 'autohide', 1) endif if s:is_vim || has('nvim-0.5.0') call setwinvar(winid, '&scrolloff', 0) endif call setwinvar(winid, '&list', 0) call setwinvar(winid, '&number', 0) call setwinvar(winid, '&relativenumber', 0) call setwinvar(winid, '&cursorcolumn', 0) call setwinvar(winid, '&colorcolumn', 0) call setwinvar(winid, 'float', 1) call setwinvar(winid, '&wrap', 1) call setwinvar(winid, '&linebreak', 1) call setwinvar(winid, '&conceallevel', 2) let g:coc_last_float_win = winid call coc#util#do_autocmd('CocOpenFloat') return [winid, winbufnr(winid)] endfunction function! coc#float#valid(winid) abort if a:winid == 0 || type(a:winid) != 0 return 0 endif if s:is_vim return s:popup_visible(a:winid) endif if exists('*nvim_win_is_valid') && nvim_win_is_valid(a:winid) let config = nvim_win_get_config(a:winid) return !empty(get(config, 'relative', '')) endif return 0 endfunction " create buffer for popup/float window function! coc#float#create_float_buf(bufnr) abort " reuse buffer cause error on vim8 if a:bufnr && bufloaded(a:bufnr) return a:bufnr endif if s:is_vim noa let bufnr = bufadd('') noa call bufload(bufnr) else noa let bufnr = nvim_create_buf(v:false, v:true) endif " Don't use popup filetype, it would crash on reuse! call setbufvar(bufnr, '&buftype', 'nofile') call setbufvar(bufnr, '&bufhidden', 'hide') call setbufvar(bufnr, '&swapfile', 0) call setbufvar(bufnr, '&tabstop', 2) call setbufvar(bufnr, '&undolevels', -1) return bufnr endfunction " border window for neovim, content config with border function! coc#float#nvim_border_win(config, border, title, related) abort if s:empty_border(a:border) return endif " width height col row relative noa let bufnr = nvim_create_buf(v:false, v:true) call setbufvar(bufnr, '&bufhidden', 'wipe') let row = a:border[0] ? a:config['row'] - 1 : a:config['row'] let col = a:border[3] ? a:config['col'] - 1 : a:config['col'] let width = a:config['width'] + a:border[1] + a:border[3] let height = a:config['height'] + a:border[0] + a:border[2] let opt = { \ 'relative': a:config['relative'], \ 'width': width, \ 'height': height, \ 'row': row, \ 'col': col, \ 'focusable': v:false, \ 'style': 'minimal', \ } let winid = nvim_open_win(bufnr, 0, opt) if !winid return endif call setwinvar(winid, '&winhl', 'Normal:CocFloating,NormalNC:CocFloating') let lines = coc#float#create_border_lines(a:border, a:title, a:config['width'], a:config['height']) call nvim_buf_set_lines(bufnr, 0, -1, v:false, lines) call add(a:related, winid) endfunction function! coc#float#create_border_lines(border, title, width, height) abort let list = [] if a:border[0] let top = (a:border[3] ? s:borderchars[4]: '') \.repeat(s:borderchars[0], a:width) \.(a:border[1] ? s:borderchars[5] : '') if !empty(a:title) let top = coc#helper#str_compose(top, 1, a:title.' ') endif call add(list, top) endif let mid = (a:border[3] ? s:borderchars[3]: '') \.repeat(' ', a:width) \.(a:border[1] ? s:borderchars[1] : '') call extend(list, repeat([mid], a:height)) if a:border[2] let bot = (a:border[3] ? s:borderchars[7]: '') \.repeat(s:borderchars[2], a:width) \.(a:border[1] ? s:borderchars[6] : '') call add(list, bot) endif return list endfunction " Create float window for input function! coc#float#create_prompt_win(title, default) abort call coc#float#close_auto_hide_wins() noa let bufnr = nvim_create_buf(v:false, v:true) call nvim_buf_set_lines(bufnr, 0, -1, v:false, [a:default]) call setbufvar(bufnr, '&bufhidden', 'wipe') " Calculate col let curr = win_screenpos(winnr())[1] + wincol() - 2 let width = min([max([strdisplaywidth(a:title) + 2, s:prompt_win_width]), &columns - 2]) if width == &columns - 2 let col = 0 - curr else let col = curr + width <= &columns - 2 ? 0 : &columns - s:prompt_win_width endif let config = { \ 'relative': 'cursor', \ 'width': width, \ 'height': 1, \ 'row': 0, \ 'col': col, \ 'style': 'minimal', \ } let winid = nvim_open_win(bufnr, 0, config) if winid == 0 return [] endif call setwinvar(winid, '&winhl', 'Normal:CocFloating,NormalNC:CocFloating') let related = [] call coc#float#nvim_border_win(config, [1,1,1,1], a:title, related) call setwinvar(winid, 'related', related) call win_gotoid(winid) inoremap inoremap pumvisible() ? "\" : "\" exe 'inoremap =coc#float#close_i('.winid.')' exe 'nnoremap :call coc#float#close('.winid.')' exe 'inoremap "\=coc#float#prompt_insert('.winid.')\\"' call feedkeys('A', 'in') return [bufnr, winid] endfunction function! coc#float#close_i(winid) abort call coc#float#close(a:winid) return '' endfunction function! coc#float#prompt_insert(winid) abort let text = getline('.') let bufnr = winbufnr(a:winid) call coc#rpc#notify('PromptInsert',[text, bufnr]) call timer_start(50, { -> coc#float#close(a:winid)}) return '' endfunction " Position of cursor relative to editor function! s:win_position() abort let nr = winnr() let [row, col] = win_screenpos(nr) return [row + winline() - 2, col + wincol() - 2] endfunction " get popup position for vim8 based on config of neovim float window function! s:popup_position(config) abort let relative = get(a:config, 'relative', 'editor') if relative ==# 'cursor' return [s:popup_cursor(a:config['row']), s:popup_cursor(a:config['col'])] endif return [a:config['row'] + 1, a:config['col'] + 1] endfunction function! s:popup_cursor(n) abort if a:n == 0 return 'cursor' endif if a:n < 0 return 'cursor'.a:n endif return 'cursor+'.a:n endfunction " Close float window by id function! coc#float#close(winid) abort if !coc#float#valid(a:winid) return 0 endif if s:is_vim call popup_close(a:winid) return 1 else call coc#float#nvim_close_related(a:winid) call nvim_win_close(a:winid, 1) return 1 endif return 0 endfunction " Float window id on current tab. " return 0 if not found function! coc#float#get_float_win() abort if has('nvim') for i in range(1, winnr('$')) let id = win_getid(i) let config = nvim_win_get_config(id) if (!empty(config) && config['focusable'] == v:true && !empty(config['relative'])) if !getwinvar(id, 'button', 0) return id endif endif endfor elseif exists('*popup_list') let arr = filter(popup_list(), 'popup_getpos(v:val)["visible"]') if !empty(arr) return arr[0] endif endif return 0 endfunction function! coc#float#get_float_win_list() abort if s:is_vim && exists('*popup_list') return filter(popup_list(), 'popup_getpos(v:val)["visible"]') elseif has('nvim') && exists('*nvim_win_get_config') let res = [] for i in range(1, winnr('$')) let id = win_getid(i) let config = nvim_win_get_config(id) " ignore border & button window if (!empty(config) && config['focusable'] == v:true && !empty(config['relative'])) if !getwinvar(id, 'button', 0) call add(res, id) endif endif endfor return res endif return [] endfunction " Check if a float window is scrollable function! coc#float#scrollable(winid) abort let bufnr = winbufnr(a:winid) if bufnr == -1 return 0 endif if s:is_vim let pos = popup_getpos(a:winid) " scrollbar enabled if get(popup_getoptions(a:winid), 'scrollbar', 0) return get(pos, 'scrollbar', 0) endif let ch = coc#float#content_height(bufnr, pos['core_width'], getwinvar(a:winid, '&wrap')) return ch > pos['core_height'] else let height = nvim_win_get_height(a:winid) let width = nvim_win_get_width(a:winid) if width > 1 && getwinvar(a:winid, '&foldcolumn', 0) " since we use foldcolumn for left pading let width = width - 1 endif let ch = coc#float#content_height(bufnr, width, getwinvar(a:winid, '&wrap')) return ch > height endif endfunction function! coc#float#has_scroll() abort let win_ids = filter(coc#float#get_float_win_list(), 'coc#float#scrollable(v:val)') return !empty(win_ids) endfunction function! coc#float#scroll(forward, ...) if !has('nvim-0.4.3') && !has('patch-8.2.0750') throw 'coc#float#scroll() requires nvim >= 0.4.3 or vim >= 8.2.0750' endif let amount = get(a:, 1, 0) let win_ids = filter(coc#float#get_float_win_list(), 'coc#float#scrollable(v:val)') if empty(win_ids) return '' endif if has('nvim') call timer_start(10, { -> s:scroll_nvim(win_ids, a:forward, amount)}) else call timer_start(10, { -> s:scroll_vim(win_ids, a:forward, amount)}) endif return mode() =~ '^i' ? "" : "\" endfunction function! s:scroll_nvim(win_ids, forward, amount) abort let curr = win_getid() for id in a:win_ids if nvim_win_is_valid(id) let wrapped = 0 let width = nvim_win_get_width(id) if getwinvar(id, '&wrap', 0) if width > 1 && getwinvar(id, '&foldcolumn', 0) let width = width - 1 endif for line in nvim_buf_get_lines(winbufnr(id), 0, -1, v:false) if strdisplaywidth(line) > width let wrapped = 1 break endif endfor endif noa call win_gotoid(id) let height = nvim_win_get_height(id) let firstline = line('w0') let lastline = line('w$') let linecount = line('$') let delta = a:amount ? a:amount : max([1, height - 1]) if a:forward if lastline == linecount && strdisplaywidth(line('$')) <= width continue endif if !a:amount && firstline != lastline execute 'noa normal! Lzt' else execute 'noa normal! H'.delta.'jzt' endif let lnum = line('.') while lnum < linecount && line('w0') == firstline && line('w$') == lastline execute 'noa normal! jzt' let lnum = lnum + 1 endwhile else if !a:amount && firstline != lastline execute 'noa normal! Hzb' else execute 'noa normal! L'.delta.'kzb' endif let lnum = line('.') while lnum > 1 && line('w0') == firstline && line('w$') == lastline execute 'noa normal! kzb' let lnum = lnum - 1 endwhile endif call coc#float#nvim_scrollbar(id) endif endfor noa call win_gotoid(curr) redraw endfunction function! s:scroll_vim(win_ids, forward, amount) abort for id in a:win_ids if s:popup_visible(id) let pos = popup_getpos(id) let bufnr = winbufnr(id) let linecount = get(getbufinfo(bufnr)[0], 'linecount', 0) " for forward use last line (or last line + 1) as first line if a:forward if pos['firstline'] == pos['lastline'] call popup_setoptions(id, {'firstline': min([pos['firstline'] + 1, linecount])}) else if pos['lastline'] == linecount let win_width = pos['core_width'] let text = getbufline(bufnr, '$')[0] if strdisplaywidth(text) <= win_width " last line shown return endif endif let lnum = a:amount ? min([linecount, pos['firstline'] + a:amount]) : pos['lastline'] call popup_setoptions(id, {'firstline': lnum}) endif else if pos['firstline'] == 1 call win_execute(id, 'normal! gg0') return endif " we could only change firstline " iterate lines before last lines to fill content height - 1 let total_height = a:amount ? min([a:amount, pos['core_height']]) : pos['core_height'] - 1 if total_height == 0 call popup_setoptions(id, {'firstline': pos['firstline'] - 1}) else let lines = getbufline(bufnr, 1, '$') let curr = pos['firstline'] - 1 let width = pos['core_width'] let used = 0 while v:true if curr == 1 break endif let w = max([1, strdisplaywidth(lines[curr - 1])]) let used += float2nr(ceil(str2float(string(w))/width)) if used > total_height let curr = curr == pos['firstline'] -1 ? curr : curr + 1 break elseif used == total_height break endif let curr = curr - 1 endwhile call popup_setoptions(id, {'firstline': curr}) endif endif endif endfor redraw endfunction function! s:popup_visible(id) abort let pos = popup_getpos(a:id) if !empty(pos) && get(pos, 'visible', 0) return 1 endif return 0 endfunction function! s:convert_config_nvim(config) abort let result = coc#helper#dict_omit(a:config, ['title', 'border', 'cursorline', 'autohide', 'close']) let border = get(a:config, 'border', []) if !s:empty_border(border) if result['relative'] ==# 'cursor' && result['row'] < 0 " move top when has bottom border if get(border, 2, 0) let result['row'] = result['row'] - 1 endif else " move down when has top border if get(border, 0, 0) let result['row'] = result['row'] + 1 endif endif " move right when has left border if get(border, 3, 0) let result['col'] = result['col'] + 1 endif let result['width'] = result['width'] + 1 - get(border,3, 0) else let result['width'] = result['width'] + 1 endif return result endfunction " Close windows that could auto hide function! coc#float#close_auto_hide_wins(...) abort let winids = coc#float#get_float_win_list() let except = get(a:, 1, 0) for id in winids if except && id == except continue endif if getwinvar(id, 'autohide', 0) call coc#float#close(id) endif endfor endfunction " neovim only function! coc#float#nvim_close_btn(config, winid, close, border, related) abort if !a:close return endif let config = { \ 'relative': a:config['relative'], \ 'width': 1, \ 'height': 1, \ 'row': get(a:border, 0, 0) ? a:config['row'] - 1 : a:config['row'], \ 'col': a:config['col'] + a:config['width'], \ 'focusable': v:true, \ 'style': 'minimal', \ } noa let bufnr = nvim_create_buf(v:false, v:true) call setbufvar(bufnr, '&bufhidden', 'wipe') call nvim_buf_set_lines(bufnr, 0, -1, v:false, ['X']) let winid = nvim_open_win(bufnr, 0, config) " map for winid & close_winid if winid call setwinvar(winid, 'button', 1) call setwinvar(a:winid, 'close_winid', winid) call setwinvar(winid, '&winhl', 'Normal:CocFloating,NormalNC:CocFloating') call add(a:related, winid) endif endfunction function! coc#float#nvim_check_close(winid) abort let target = getwinvar(a:winid, 'target_winid', 0) if target call coc#float#close(target) endif endfunction " Create padding window by config of current window & border config function! coc#float#nvim_right_pad(config, border, related) abort " Check right border if !empty(a:border) && get(a:border, 1, 0) return endif let config = { \ 'relative': a:config['relative'], \ 'width': 1, \ 'height': a:config['height'], \ 'row': a:config['row'], \ 'col': a:config['col'] + a:config['width'], \ 'focusable': v:false, \ 'style': 'minimal', \ } noa let bufnr = nvim_create_buf(v:false, v:true) call setbufvar(bufnr, '&bufhidden', 'wipe') call nvim_buf_set_lines(bufnr, 0, -1, v:false, repeat([' '], a:config['height'])) let winid = nvim_open_win(bufnr, 0, config) if winid call setwinvar(winid, 'ispad', 1) call setwinvar(winid, '&winhl', 'Normal:CocFloating,NormalNC:CocFloating') call add(a:related, winid) endif endfunction function! coc#float#content_height(bufnr, width, wrap) abort if !bufloaded(a:bufnr) return 0 endif if !a:wrap return has('nvim') ? nvim_buf_line_count(a:bufnr) : len(getbufline(a:bufnr, 1, '$')) endif let lines = has('nvim') ? nvim_buf_get_lines(a:bufnr, 0, -1, 0) : getbufline(a:bufnr, 1, '$') let total = 0 for line in lines let dw = max([1, strdisplaywidth(line)]) let total += float2nr(ceil(str2float(string(dw))/a:width)) endfor return total endfunction function! s:add_related(winid, target) abort let arr = getwinvar(a:target, 'related', []) if index(arr, a:winid) >= 0 return endif call add(arr, a:winid) call setwinvar(a:target, 'related', arr) endfunction function! coc#float#nvim_refresh_scrollbar() abort let id = getwinvar(win_getid(), 'scrollbar', 0) if coc#float#valid(id) call coc#float#nvim_scrollbar(win_getid()) endif endfunction " Close related windows for neovim. function! coc#float#nvim_close_related(winid) abort if !has('nvim') || !a:winid return endif let winids = getwinvar(a:winid, 'related', []) if !empty(winids) call nvim_win_del_var(a:winid, 'related') endif for id in winids if nvim_win_is_valid(id) && id != a:winid call nvim_win_close(id, 1) endif endfor endfunction function! coc#float#nvim_create_related(winid, config, opts) abort let related = [] call coc#float#nvim_close_btn(a:config, a:winid, get(a:opts, 'close', 0), get(a:opts, 'border', []), related) call coc#float#nvim_border_win(a:config, get(a:opts, 'border', []), get(a:opts, 'title', ''), related) call coc#float#nvim_right_pad(a:config, get(a:opts, 'border', []), related) for id in related call setwinvar(id, 'target_winid', a:winid) endfor call setwinvar(a:winid, 'related', related) endfunction " Create scrollbar for winid " Need called on create, config, buffer change, scrolled function! coc#float#nvim_scrollbar(winid) abort if !has('nvim-0.4.3') return endif if a:winid == 0 || !nvim_win_is_valid(a:winid) || getwinvar(a:winid, 'button', 0) return endif let config = nvim_win_get_config(a:winid) " ignore border & button window if (!get(config, 'focusable', v:false) || empty(get(config, 'relative', ''))) return endif let [row, column] = nvim_win_get_position(a:winid) let width = nvim_win_get_width(a:winid) let height = nvim_win_get_height(a:winid) let bufnr = winbufnr(a:winid) let cw = getwinvar(a:winid, '&foldcolumn', 0) ? width - 1 : width let ch = coc#float#content_height(bufnr, cw, getwinvar(a:winid, '&wrap')) let close_winid = getwinvar(a:winid, 'close_winid', 0) let border = getwinvar(a:winid, 'border', []) let move_down = close_winid && !get(border, 0, 0) if move_down let height = height - 1 if height == 0 return endif endif let id = 0 if nvim_win_is_valid(getwinvar(a:winid, 'scrollbar', 0)) let id = getwinvar(a:winid, 'scrollbar', 0) endif if ch <= height || height == 0 " no scrollbar, remove exists if id call nvim_win_del_var(a:winid, 'scrollbar') call coc#float#close(id) endif return endif if height == 0 return endif if id && bufloaded(winbufnr(id)) let sbuf = winbufnr(id) else noa let sbuf = nvim_create_buf(v:false, v:true) call setbufvar(sbuf, '&bufhidden', 'wipe') endif call nvim_buf_set_lines(sbuf, 0, -1, v:false, repeat([' '], height)) let opts = { \ 'row': move_down ? row + 1 : row, \ 'col': column + width, \ 'relative': 'editor', \ 'width': 1, \ 'height': height, \ 'focusable': v:false, \ 'style': 'minimal', \ } if id call nvim_win_set_config(id, opts) else let id = nvim_open_win(sbuf, 0 , opts) call setwinvar(id, 'isscrollbar', 1) call setwinvar(id, 'target_winid', a:winid) endif let thumb_height = max([1, float2nr(floor(height * (height + 0.0)/ch))]) let curr = win_getid() if curr != a:winid noa call win_gotoid(a:winid) endif let firstline = line('w0') let lastline = line('w$') let linecount = line('$') if firstline == 1 let start = 0 elseif lastline == linecount let start = height - thumb_height else let start = max([1, float2nr(round((height - thumb_height + 0.0)*(firstline - 1.0)/(ch - height)))]) endif if curr != a:winid noa call win_gotoid(curr) endif " add highlights call nvim_buf_clear_namespace(sbuf, s:scrollbar_ns, 0, -1) for idx in range(0, height - 1) if idx >= start && idx < start + thumb_height call nvim_buf_add_highlight(sbuf, s:scrollbar_ns, 'PmenuThumb', idx, 0, 1) else call nvim_buf_add_highlight(sbuf, s:scrollbar_ns, 'PmenuSbar', idx, 0, 1) endif endfor " create scrollbar outside window call setwinvar(a:winid, 'scrollbar', id) call s:add_related(id, a:winid) endfunction function! coc#float#nvim_check_related() abort if !has('nvim') return endif let invalids = [] for i in range(1, winnr('$')) let id = win_getid(i) let target = getwinvar(id, 'target_winid', 0) if target && !nvim_win_is_valid(target) call add(invalids, id) endif endfor for id in invalids noa call nvim_win_close(id, 1) endfor endfunction " Dimension of window with lines relative to cursor " Width & height excludes border & padding function! coc#float#get_config_cursor(lines, config) abort let preferTop = get(a:config, 'preferTop', 0) let title = get(a:config, 'title', '') let border = get(a:config, 'border', [0, 0, 0, 0]) if s:empty_border(border) && len(title) let border = [1, 1, 1, 1] endif let bh = get(border, 0, 0) + get(border, 2, 0) let vh = &lines - &cmdheight - 1 if vh <= 0 return v:null endif let maxWidth = min([get(a:config, 'maxWidth', 80), &columns - 1]) if maxWidth < 3 return v:null endif let maxHeight = min([get(a:config, 'maxHeight', 80), vh]) let ch = 0 let width = min([40, strdisplaywidth(title)]) + 3 for line in a:lines let dw = max([1, strdisplaywidth(line)]) let width = max([width, dw + 2]) let ch += float2nr(ceil(str2float(string(dw))/(maxWidth - 2))) endfor let width = min([maxWidth, width]) let [lineIdx, colIdx] = s:win_position() " How much we should move left let offsetX = min([get(a:config, 'offsetX', 0), colIdx]) let showTop = 0 let hb = vh - lineIdx -1 if lineIdx > bh + 2 && (preferTop || (lineIdx > hb && hb < ch + bh)) let showTop = 1 endif let height = min([maxHeight, ch + bh, showTop ? lineIdx - 1 : hb]) if height <= bh return v:null endif let col = - max([offsetX, colIdx - (&columns - 1 - width)]) let row = showTop ? - height : 1 return { \ 'row': row, \ 'col': col, \ 'width': width - 2, \ 'height': height - bh \ } endfunction function! coc#float#get_config_pum(lines, pumconfig, maxwidth) abort if !pumvisible() return v:null endif let pw = a:pumconfig['width'] + get(a:pumconfig, 'scrollbar', 0) let rp = &columns - a:pumconfig['col'] - pw let showRight = a:pumconfig['col'] > rp ? 0 : 1 let maxWidth = showRight ? min([rp - 1, a:maxwidth]) : min([a:pumconfig['col'] - 1, a:maxwidth]) let maxHeight = &lines - a:pumconfig['row'] - &cmdheight - 1 if maxWidth <= 2 || maxHeight < 1 return v:null endif let ch = 0 let width = 0 for line in a:lines let dw = max([1, strdisplaywidth(line)]) let width = max([width, dw + 2]) let ch += float2nr(ceil(str2float(string(dw))/(maxWidth - 2))) endfor let width = min([maxWidth, width]) let height = min([maxHeight, ch]) return { \ 'col': showRight ? a:pumconfig['col'] + pw : a:pumconfig['col'] - width - 1, \ 'row': a:pumconfig['row'], \ 'height': height, \ 'width': width - 2 + (s:is_vim && ch > height ? -1 : 0), \ 'relative': 'editor' \ } endfunction function! s:empty_border(border) abort if empty(a:border) return 1 endif if a:border[0] == 0 && a:border[1] == 0 && a:border[2] == 0 && a:border[3] == 0 return 1 endif return 0 endfunction