dialog.vim 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  1. scriptencoding utf-8
  2. let s:is_vim = !has('nvim')
  3. let s:root = expand('<sfile>:h:h:h')
  4. let s:prompt_win_bufnr = 0
  5. let s:list_win_bufnr = 0
  6. let s:prompt_win_width = get(g:, 'coc_prompt_win_width', 32)
  7. let s:frames = ['· ', '·· ', '···', ' ··', ' ·', ' ']
  8. let s:sign_group = 'PopUpCocDialog'
  9. let s:detail_bufnr = 0
  10. " Float window aside pum
  11. function! coc#dialog#create_pum_float(lines, config) abort
  12. let winid = coc#float#get_float_by_kind('pumdetail')
  13. if empty(a:lines) || !coc#pum#visible()
  14. if winid
  15. call coc#float#close(winid)
  16. endif
  17. return
  18. endif
  19. let pumbounding = coc#pum#info()
  20. let border = get(a:config, 'border', [])
  21. let pw = pumbounding['width'] + (empty(border) ? get(pumbounding, 'scrollbar', 0) : 0)
  22. let rp = &columns - pumbounding['col'] - pw
  23. let showRight = pumbounding['col'] > rp ? 0 : 1
  24. let maxWidth = showRight ? coc#math#min(rp - 1, a:config['maxWidth']) : coc#math#min(pumbounding['col'] - 1, a:config['maxWidth'])
  25. let bh = get(border, 0 ,0) + get(border, 2, 0)
  26. let maxHeight = &lines - pumbounding['row'] - &cmdheight - 1 - bh
  27. if maxWidth <= 2 || maxHeight < 1
  28. return v:null
  29. endif
  30. let width = 0
  31. for line in a:lines
  32. let dw = max([1, strdisplaywidth(line)])
  33. let width = max([width, dw + 2])
  34. endfor
  35. let width = float2nr(coc#math#min(maxWidth, width))
  36. let ch = coc#string#content_height(a:lines, width - 2)
  37. let height = float2nr(coc#math#min(maxHeight, ch))
  38. let lines = map(a:lines, {_, s -> s =~# '^─' ? repeat('─', width - 2 + (s:is_vim && ch > height ? -1 : 0)) : s})
  39. let opts = {
  40. \ 'lines': lines,
  41. \ 'highlights': get(a:config, 'highlights', []),
  42. \ 'relative': 'editor',
  43. \ 'col': showRight ? pumbounding['col'] + pw : pumbounding['col'] - width,
  44. \ 'row': pumbounding['row'],
  45. \ 'height': height,
  46. \ 'width': width - 2 + (s:is_vim && ch > height ? -1 : 0),
  47. \ 'scrollinside': showRight ? 0 : 1,
  48. \ 'codes': get(a:config, 'codes', []),
  49. \ }
  50. for key in ['border', 'highlight', 'borderhighlight', 'winblend', 'focusable', 'shadow', 'rounded']
  51. if has_key(a:config, key)
  52. let opts[key] = a:config[key]
  53. endif
  54. endfor
  55. call s:close_auto_hide_wins(winid)
  56. let result = coc#float#create_float_win(winid, s:detail_bufnr, opts)
  57. if empty(result)
  58. return
  59. endif
  60. let s:detail_bufnr = result[1]
  61. call setwinvar(result[0], 'kind', 'pumdetail')
  62. if !s:is_vim
  63. call coc#float#nvim_scrollbar(result[0])
  64. endif
  65. endfunction
  66. " Float window below/above cursor
  67. function! coc#dialog#create_cursor_float(winid, bufnr, lines, config) abort
  68. if coc#prompt#activated()
  69. return v:null
  70. endif
  71. let pumAlignTop = get(a:config, 'pumAlignTop', 0)
  72. let modes = get(a:config, 'modes', ['n', 'i', 'ic', 's'])
  73. let mode = mode()
  74. let currbuf = bufnr('%')
  75. let pos = [line('.'), col('.')]
  76. if index(modes, mode) == -1
  77. return v:null
  78. endif
  79. if !s:is_vim && !has('nvim-0.5.0') && mode ==# 'i'
  80. " helps to fix undo issue, don't know why.
  81. call feedkeys("\<C-g>u", 'n')
  82. endif
  83. if mode ==# 's' && has('patch-8.2.4969') && !has('patch-8.2.4996')
  84. echohl WarningMsg | echon 'Popup not created to avoid issue #10466 on vim >= 8.2.4969' | echohl None
  85. return v:null
  86. endif
  87. let dimension = coc#dialog#get_config_cursor(a:lines, a:config)
  88. if empty(dimension)
  89. return v:null
  90. endif
  91. if coc#pum#visible() && ((pumAlignTop && dimension['row'] <0)|| (!pumAlignTop && dimension['row'] > 0))
  92. return v:null
  93. endif
  94. let width = dimension['width']
  95. let lines = map(a:lines, {_, s -> s =~# '^─' ? repeat('─', width) : s})
  96. let config = extend(extend({'lines': lines, 'relative': 'cursor'}, a:config), dimension)
  97. call s:close_auto_hide_wins(a:winid)
  98. let res = coc#float#create_float_win(a:winid, a:bufnr, config)
  99. if empty(res)
  100. return v:null
  101. endif
  102. let alignTop = dimension['row'] < 0
  103. let winid = res[0]
  104. let bufnr = res[1]
  105. redraw
  106. if has('nvim')
  107. call coc#float#nvim_scrollbar(winid)
  108. endif
  109. return [currbuf, pos, winid, bufnr, alignTop]
  110. endfunction
  111. " Create float window for input
  112. function! coc#dialog#create_prompt_win(title, default, opts) abort
  113. call s:close_auto_hide_wins()
  114. let bufnr = has('nvim') ? s:prompt_win_bufnr : 0
  115. if s:is_vim
  116. execute 'hi link CocPopupTerminal '.get(a:opts, 'highlight', 'CocFloating')
  117. let node = expand(get(g:, 'coc_node_path', 'node'))
  118. let bufnr = term_start([node, s:root . '/bin/prompt.js', a:default], {
  119. \ 'term_rows': 1,
  120. \ 'term_highlight': 'CocPopupTerminal',
  121. \ 'hidden': 1,
  122. \ 'term_finish': 'close'
  123. \ })
  124. call term_setapi(bufnr, 'Coc')
  125. call setbufvar(bufnr, 'current', type(a:default) == v:t_string ? a:default : '')
  126. endif
  127. let config = s:get_prompt_dimension(a:title, a:default, a:opts)
  128. let res = coc#float#create_float_win(0, bufnr, extend(config, {
  129. \ 'style': 'minimal',
  130. \ 'border': get(a:opts, 'border', [1,1,1,1]),
  131. \ 'rounded': get(a:opts, 'rounded', 1),
  132. \ 'prompt': 1,
  133. \ 'title': a:title,
  134. \ 'lines': s:is_vim ? v:null : [a:default],
  135. \ 'highlight': get(a:opts, 'highlight', 'CocFloating'),
  136. \ 'borderhighlight': [get(a:opts, 'borderhighlight', 'CocFloating')],
  137. \ }))
  138. if empty(res)
  139. return
  140. endif
  141. let winid = res[0]
  142. let bufnr = res[1]
  143. if has('nvim')
  144. let s:prompt_win_bufnr = res[1]
  145. execute 'sign unplace 6 buffer='.s:prompt_win_bufnr
  146. call nvim_set_current_win(winid)
  147. inoremap <buffer> <C-a> <Home>
  148. inoremap <buffer><expr><C-e> pumvisible() ? "\<C-e>" : "\<End>"
  149. exe 'imap <silent><nowait><buffer> <esc> <esc><esc>'
  150. exe 'nnoremap <silent><buffer> <esc> :call coc#float#close('.winid.')<CR>'
  151. exe 'inoremap <silent><expr><nowait><buffer> <cr> "\<C-r>=coc#dialog#prompt_insert(getline(''.''))\<cr>\<esc>"'
  152. if get(a:opts, 'list', 0)
  153. for key in ['<C-j>', '<C-k>', '<C-n>', '<C-p>', '<up>', '<down>', '<C-f>', '<C-b>', '<C-space>']
  154. " Can't use < in remap
  155. let escaped = key ==# '<C-space>' ? "C-@" : strcharpart(key, 1, strchars(key) - 2)
  156. exe 'inoremap <nowait><buffer> '.key.' <Cmd>call coc#rpc#notify("PromptKeyPress", ['.bufnr.', "'.escaped.'"])<CR>'
  157. endfor
  158. endif
  159. call feedkeys('A', 'in')
  160. endif
  161. call coc#util#do_autocmd('CocOpenFloatPrompt')
  162. if s:is_vim
  163. let pos = popup_getpos(winid)
  164. " width height row col
  165. let dimension = [pos['width'], pos['height'], pos['line'] - 1, pos['col'] - 1]
  166. else
  167. let id = coc#float#get_related(winid, 'border')
  168. if !has('nvim-0.6.0')
  169. redraw
  170. endif
  171. let pos = nvim_win_get_position(id)
  172. let dimension = [nvim_win_get_width(id), nvim_win_get_height(id), pos[0], pos[1]]
  173. endif
  174. return [bufnr, winid, dimension]
  175. endfunction
  176. " Create list window under target window
  177. function! coc#dialog#create_list(target, dimension, opts) abort
  178. let maxHeight = get(a:opts, 'maxHeight', 10)
  179. let height = max([1, len(get(a:opts, 'lines', []))])
  180. let height = min([maxHeight, height, &lines - &cmdheight - 1 - a:dimension['row'] + a:dimension['height']])
  181. let chars = get(a:opts, 'rounded', 1) ? ['╯', '╰'] : ['┘', '└']
  182. let config = extend(copy(a:opts), {
  183. \ 'relative': 'editor',
  184. \ 'row': a:dimension['row'] + a:dimension['height'],
  185. \ 'col': a:dimension['col'],
  186. \ 'width': a:dimension['width'] - 2,
  187. \ 'height': height,
  188. \ 'border': [1, 1, 1, 1],
  189. \ 'scrollinside': 1,
  190. \ 'borderchars': extend(['─', '│', '─', '│', '├', '┤'], chars)
  191. \ })
  192. let bufnr = 0
  193. let result = coc#float#create_float_win(0, s:list_win_bufnr, config)
  194. if empty(result)
  195. return
  196. endif
  197. let winid = result[0]
  198. call coc#float#add_related(winid, a:target)
  199. call setwinvar(winid, 'auto_height', get(a:opts, 'autoHeight', 1))
  200. call setwinvar(winid, 'max_height', maxHeight)
  201. call setwinvar(winid, 'target_winid', a:target)
  202. call setwinvar(winid, 'kind', 'list')
  203. call coc#dialog#check_scroll_vim(a:target)
  204. return result
  205. endfunction
  206. " Create menu picker for pick single item
  207. function! coc#dialog#create_menu(lines, config) abort
  208. call s:close_auto_hide_wins()
  209. let highlight = get(a:config, 'highlight', 'CocFloating')
  210. let borderhighlight = get(a:config, 'borderhighlight', [highlight])
  211. let relative = get(a:config, 'relative', 'cursor')
  212. let lines = copy(a:lines)
  213. let content = get(a:config, 'content', '')
  214. let maxWidth = get(a:config, 'maxWidth', 80)
  215. let highlights = get(a:config, 'highlights', [])
  216. let contentCount = 0
  217. if !empty(content)
  218. let contentLines = coc#string#reflow(split(content, '\r\?\n'), maxWidth)
  219. let contentCount = len(contentLines)
  220. let lines = extend(contentLines, lines)
  221. if !empty(highlights)
  222. for item in highlights
  223. let item['lnum'] = item['lnum'] + contentCount
  224. endfor
  225. endif
  226. endif
  227. let opts = {
  228. \ 'lines': lines,
  229. \ 'highlight': highlight,
  230. \ 'title': get(a:config, 'title', ''),
  231. \ 'borderhighlight': borderhighlight,
  232. \ 'maxWidth': maxWidth,
  233. \ 'maxHeight': get(a:config, 'maxHeight', 80),
  234. \ 'rounded': get(a:config, 'rounded', 0),
  235. \ 'border': [1, 1, 1, 1],
  236. \ 'highlights': highlights,
  237. \ 'relative': relative,
  238. \ }
  239. if relative ==# 'editor'
  240. let dimension = coc#dialog#get_config_editor(lines, opts)
  241. else
  242. let dimension = coc#dialog#get_config_cursor(lines, opts)
  243. endif
  244. call extend(opts, dimension)
  245. let ids = coc#float#create_float_win(0, s:prompt_win_bufnr, opts)
  246. if empty(ids)
  247. return
  248. endif
  249. let s:prompt_win_bufnr = ids[1]
  250. call coc#dialog#set_cursor(ids[0], ids[1], contentCount + 1)
  251. redraw
  252. if has('nvim')
  253. call coc#float#nvim_scrollbar(ids[0])
  254. endif
  255. return [ids[0], ids[1], contentCount]
  256. endfunction
  257. " Create dialog at center of screen
  258. function! coc#dialog#create_dialog(lines, config) abort
  259. call s:close_auto_hide_wins()
  260. " dialog always have borders
  261. let title = get(a:config, 'title', '')
  262. let buttons = get(a:config, 'buttons', [])
  263. let highlight = get(a:config, 'highlight', 'CocFloating')
  264. let borderhighlight = get(a:config, 'borderhighlight', [highlight])
  265. let opts = {
  266. \ 'title': title,
  267. \ 'rounded': get(a:config, 'rounded', 0),
  268. \ 'relative': 'editor',
  269. \ 'border': [1,1,1,1],
  270. \ 'close': get(a:config, 'close', 1),
  271. \ 'highlight': highlight,
  272. \ 'highlights': get(a:config, 'highlights', []),
  273. \ 'buttons': buttons,
  274. \ 'borderhighlight': borderhighlight,
  275. \ 'getchar': get(a:config, 'getchar', 0)
  276. \ }
  277. call extend(opts, coc#dialog#get_config_editor(a:lines, a:config))
  278. let bufnr = coc#float#create_buf(0, a:lines)
  279. let res = coc#float#create_float_win(0, bufnr, opts)
  280. if empty(res)
  281. return
  282. endif
  283. if get(a:config, 'cursorline', 0)
  284. call coc#dialog#place_sign(bufnr, 1)
  285. endif
  286. if has('nvim')
  287. redraw
  288. call coc#float#nvim_scrollbar(res[0])
  289. endif
  290. return res
  291. endfunction
  292. function! coc#dialog#prompt_confirm(title, cb) abort
  293. call s:close_auto_hide_wins()
  294. if s:is_vim && exists('*popup_dialog')
  295. try
  296. call popup_dialog(a:title. ' (y/n)?', {
  297. \ 'highlight': 'Normal',
  298. \ 'filter': 'popup_filter_yesno',
  299. \ 'callback': {id, res -> a:cb(v:null, res)},
  300. \ 'borderchars': get(g:, 'coc_borderchars', ['─', '│', '─', '│', '╭', '╮', '╯', '╰']),
  301. \ 'borderhighlight': ['MoreMsg']
  302. \ })
  303. catch /.*/
  304. call a:cb(v:exception)
  305. endtry
  306. return
  307. endif
  308. if has('nvim-0.4.0')
  309. let text = ' '. a:title . ' (y/n)? '
  310. let maxWidth = coc#math#min(78, &columns - 2)
  311. let width = coc#math#min(maxWidth, strdisplaywidth(text))
  312. let maxHeight = &lines - &cmdheight - 1
  313. let height = coc#math#min(maxHeight, float2nr(ceil(str2float(string(strdisplaywidth(text)))/width)))
  314. let arr = coc#float#create_float_win(0, s:prompt_win_bufnr, {
  315. \ 'col': &columns/2 - width/2 - 1,
  316. \ 'row': maxHeight/2 - height/2 - 1,
  317. \ 'width': width,
  318. \ 'height': height,
  319. \ 'border': [1,1,1,1],
  320. \ 'focusable': v:false,
  321. \ 'relative': 'editor',
  322. \ 'highlight': 'Normal',
  323. \ 'borderhighlight': 'MoreMsg',
  324. \ 'style': 'minimal',
  325. \ 'lines': [text],
  326. \ })
  327. if empty(arr)
  328. call a:cb('Window create failed!')
  329. return
  330. endif
  331. let winid = arr[0]
  332. let s:prompt_win_bufnr = arr[1]
  333. let res = 0
  334. redraw
  335. " same result as vim
  336. while 1
  337. let key = nr2char(getchar())
  338. if key == "\<C-c>"
  339. let res = -1
  340. break
  341. elseif key == "\<esc>" || key == 'n' || key == 'N'
  342. let res = 0
  343. break
  344. elseif key == 'y' || key == 'Y'
  345. let res = 1
  346. break
  347. endif
  348. endw
  349. call coc#float#close(winid)
  350. call a:cb(v:null, res)
  351. " use relative editor since neovim doesn't support center position
  352. elseif exists('*confirm')
  353. let choice = confirm(a:title, "&Yes\n&No")
  354. call a:cb(v:null, choice == 1)
  355. else
  356. echohl MoreMsg
  357. echom a:title.' (y/n)'
  358. echohl None
  359. let confirm = nr2char(getchar())
  360. redraw!
  361. if !(confirm ==? "y" || confirm ==? "\r")
  362. echohl Moremsg | echo 'Cancelled.' | echohl None
  363. return 0
  364. call a:cb(v:null, 0)
  365. end
  366. call a:cb(v:null, 1)
  367. endif
  368. endfunction
  369. function! coc#dialog#get_config_editor(lines, config) abort
  370. let title = get(a:config, 'title', '')
  371. let maxheight = min([get(a:config, 'maxHeight', 78), &lines - &cmdheight - 6])
  372. let maxwidth = min([get(a:config, 'maxWidth', 78), &columns - 2])
  373. let buttons = get(a:config, 'buttons', [])
  374. let minwidth = s:min_btns_width(buttons)
  375. if maxheight <= 0 || maxwidth <= 0 || minwidth > maxwidth
  376. throw 'Not enough spaces for float window'
  377. endif
  378. let ch = 0
  379. let width = min([strdisplaywidth(title) + 1, maxwidth])
  380. for line in a:lines
  381. let dw = max([1, strdisplaywidth(line)])
  382. if dw < maxwidth && dw > width
  383. let width = dw
  384. elseif dw >= maxwidth
  385. let width = maxwidth
  386. endif
  387. let ch += float2nr(ceil(str2float(string(dw))/maxwidth))
  388. endfor
  389. let width = max([minwidth, width])
  390. let height = coc#math#min(ch ,maxheight)
  391. return {
  392. \ 'row': &lines/2 - (height + 4)/2,
  393. \ 'col': &columns/2 - (width + 2)/2,
  394. \ 'width': width,
  395. \ 'height': height,
  396. \ }
  397. endfunction
  398. function! coc#dialog#prompt_insert(text) abort
  399. call coc#rpc#notify('PromptInsert', [a:text, bufnr('%')])
  400. return ''
  401. endfunction
  402. " Dimension of window with lines relative to cursor
  403. " Width & height excludes border & padding
  404. function! coc#dialog#get_config_cursor(lines, config) abort
  405. let preferTop = get(a:config, 'preferTop', 0)
  406. let title = get(a:config, 'title', '')
  407. let border = get(a:config, 'border', [])
  408. if empty(border) && len(title)
  409. let border = [1, 1, 1, 1]
  410. endif
  411. let bh = get(border, 0, 0) + get(border, 2, 0)
  412. let vh = &lines - &cmdheight - 1
  413. if vh <= 0
  414. return v:null
  415. endif
  416. let maxWidth = coc#math#min(get(a:config, 'maxWidth', &columns - 1), &columns - 1)
  417. if maxWidth < 3
  418. return v:null
  419. endif
  420. let maxHeight = coc#math#min(get(a:config, 'maxHeight', vh), vh)
  421. let width = coc#math#min(40, strdisplaywidth(title)) + 3
  422. for line in a:lines
  423. let dw = max([1, strdisplaywidth(line)])
  424. let width = max([width, dw + 2])
  425. endfor
  426. let width = coc#math#min(maxWidth, width)
  427. let ch = coc#string#content_height(a:lines, width - 2)
  428. let [lineIdx, colIdx] = coc#cursor#screen_pos()
  429. " How much we should move left
  430. let offsetX = coc#math#min(get(a:config, 'offsetX', 0), colIdx)
  431. let showTop = 0
  432. let hb = vh - lineIdx -1
  433. if lineIdx > bh + 2 && (preferTop || (lineIdx > hb && hb < ch + bh))
  434. let showTop = 1
  435. endif
  436. let height = coc#math#min(maxHeight, ch + bh, showTop ? lineIdx - 1 : hb)
  437. if height <= bh
  438. return v:null
  439. endif
  440. let col = - max([offsetX, colIdx - (&columns - 1 - width)])
  441. let row = showTop ? - height + bh : 1
  442. return {
  443. \ 'row': row,
  444. \ 'col': col,
  445. \ 'width': width - 2,
  446. \ 'height': height - bh
  447. \ }
  448. endfunction
  449. function! coc#dialog#change_border_hl(winid, hlgroup) abort
  450. if !hlexists(a:hlgroup)
  451. return
  452. endif
  453. if s:is_vim
  454. if coc#float#valid(a:winid)
  455. call popup_setoptions(a:winid, {'borderhighlight': repeat([a:hlgroup], 4)})
  456. redraw
  457. endif
  458. else
  459. let winid = coc#float#get_related(a:winid, 'border')
  460. if winid > 0
  461. call setwinvar(winid, '&winhl', 'Normal:'.a:hlgroup.',NormalNC:'.a:hlgroup)
  462. endif
  463. endif
  464. endfunction
  465. function! coc#dialog#change_title(winid, title) abort
  466. if s:is_vim
  467. if coc#float#valid(a:winid)
  468. call popup_setoptions(a:winid, {'title': a:title})
  469. redraw
  470. endif
  471. else
  472. let winid = coc#float#get_related(a:winid, 'border')
  473. if winid > 0
  474. let bufnr = winbufnr(winid)
  475. let line = getbufline(bufnr, 1)[0]
  476. let top = strcharpart(line, 0, 1)
  477. \.repeat('─', strchars(line) - 2)
  478. \.strcharpart(line, strchars(line) - 1, 1)
  479. if !empty(a:title)
  480. let top = coc#string#compose(top, 1, a:title.' ')
  481. endif
  482. call nvim_buf_set_lines(bufnr, 0, 1, v:false, [top])
  483. endif
  484. endif
  485. endfunction
  486. function! coc#dialog#change_loading(winid, loading) abort
  487. if coc#float#valid(a:winid)
  488. let winid = coc#float#get_related(a:winid, 'loading')
  489. if !a:loading && winid > 0
  490. call coc#float#close(winid)
  491. endif
  492. if a:loading && winid == 0
  493. let bufnr = s:create_loading_buf()
  494. if s:is_vim
  495. let pos = popup_getpos(a:winid)
  496. let winid = popup_create(bufnr, {
  497. \ 'line': pos['line'] + 1,
  498. \ 'col': pos['col'] + pos['width'] - 4,
  499. \ 'maxheight': 1,
  500. \ 'maxwidth': 3,
  501. \ 'zindex': 999,
  502. \ 'highlight': get(popup_getoptions(a:winid), 'highlight', 'CocFloating')
  503. \ })
  504. else
  505. let pos = nvim_win_get_position(a:winid)
  506. let width = nvim_win_get_width(a:winid)
  507. let opts = {
  508. \ 'relative': 'editor',
  509. \ 'row': pos[0],
  510. \ 'col': pos[1] + width - 3,
  511. \ 'focusable': v:false,
  512. \ 'width': 3,
  513. \ 'height': 1,
  514. \ 'style': 'minimal',
  515. \ }
  516. if has('nvim-0.5.1')
  517. let opts['zindex'] = 900
  518. endif
  519. let winid = nvim_open_win(bufnr, v:false, opts)
  520. call setwinvar(winid, '&winhl', getwinvar(a:winid, '&winhl'))
  521. endif
  522. call setwinvar(winid, 'kind', 'loading')
  523. call setbufvar(bufnr, 'target_winid', a:winid)
  524. call setbufvar(bufnr, 'popup', winid)
  525. call coc#float#add_related(winid, a:winid)
  526. endif
  527. endif
  528. endfunction
  529. " Update list with new lines and highlights
  530. function! coc#dialog#update_list(winid, bufnr, lines, highlights) abort
  531. if coc#window#tabnr(a:winid) == tabpagenr()
  532. if getwinvar(a:winid, 'auto_height', 0)
  533. let row = coc#float#get_row(a:winid)
  534. " core height
  535. let height = max([1, len(copy(a:lines))])
  536. let height = min([getwinvar(a:winid, 'max_height', 10), height, &lines - &cmdheight - 1 - row])
  537. let curr = s:is_vim ? popup_getpos(a:winid)['core_height'] : nvim_win_get_height(a:winid)
  538. let delta = height - curr
  539. if delta != 0
  540. call coc#float#change_height(a:winid, delta)
  541. endif
  542. endif
  543. call coc#compat#buf_set_lines(a:bufnr, 0, -1, a:lines)
  544. call coc#highlight#add_highlights(a:winid, [], a:highlights)
  545. if s:is_vim
  546. let target = getwinvar(a:winid, 'target_winid', -1)
  547. if target != -1
  548. call coc#dialog#check_scroll_vim(target)
  549. endif
  550. call win_execute(a:winid, 'exe 1')
  551. endif
  552. endif
  553. endfunction
  554. " Fix width of prompt window same as list window on scrollbar change
  555. function! coc#dialog#check_scroll_vim(winid) abort
  556. if s:is_vim && coc#float#valid(a:winid)
  557. let winid = coc#float#get_related(a:winid, 'list')
  558. if winid
  559. redraw
  560. let pos = popup_getpos(winid)
  561. let width = pos['width'] + (pos['scrollbar'] ? 1 : 0)
  562. if width != popup_getpos(a:winid)['width']
  563. call popup_move(a:winid, {
  564. \ 'minwidth': width - 2,
  565. \ 'maxwidth': width - 2,
  566. \ })
  567. endif
  568. endif
  569. endif
  570. endfunction
  571. function! coc#dialog#set_cursor(winid, bufnr, line) abort
  572. if s:is_vim
  573. call coc#compat#execute(a:winid, 'exe '.a:line, 'silent!')
  574. call popup_setoptions(a:winid, {'cursorline' : 1})
  575. call popup_setoptions(a:winid, {'cursorline' : 0})
  576. else
  577. call nvim_win_set_cursor(a:winid, [a:line, 0])
  578. endif
  579. call coc#dialog#place_sign(a:bufnr, a:line)
  580. endfunction
  581. function! coc#dialog#place_sign(bufnr, line) abort
  582. call sign_unplace(s:sign_group, { 'buffer': a:bufnr })
  583. if a:line > 0
  584. call sign_place(6, s:sign_group, 'CocCurrentLine', a:bufnr, {'lnum': a:line})
  585. endif
  586. endfunction
  587. " Could be center(with optional marginTop) or cursor
  588. function! s:get_prompt_dimension(title, default, opts) abort
  589. let relative = get(a:opts, 'position', 'cursor') ==# 'cursor' ? 'cursor' : 'editor'
  590. let curr = win_screenpos(winnr())[1] + wincol() - 2
  591. let minWidth = get(a:opts, 'minWidth', s:prompt_win_width)
  592. let width = min([max([strwidth(a:default) + 2, strwidth(a:title) + 2, minWidth]), &columns - 2])
  593. if get(a:opts, 'maxWidth', 0)
  594. let width = min([width, a:opts['maxWidth']])
  595. endif
  596. if relative ==# 'cursor'
  597. let [lineIdx, colIdx] = coc#cursor#screen_pos()
  598. if width == &columns - 2
  599. let col = 0 - curr
  600. else
  601. let col = curr + width <= &columns - 2 ? 0 : curr + width - &columns + 2
  602. endif
  603. let config = {
  604. \ 'row': lineIdx == 0 ? 1 : 0,
  605. \ 'col': colIdx == 0 ? 0 : col - 1,
  606. \ }
  607. else
  608. let marginTop = get(a:opts, 'marginTop', v:null)
  609. if marginTop is v:null
  610. let row = (&lines - &cmdheight - 2) / 2
  611. else
  612. let row = marginTop < 2 ? 1 : min([marginTop, &columns - &cmdheight])
  613. endif
  614. let config = {
  615. \ 'col': float2nr((&columns - width) / 2),
  616. \ 'row': row - s:is_vim,
  617. \ }
  618. endif
  619. return extend(config, {'relative': relative, 'width': width, 'height': 1})
  620. endfunction
  621. function! s:min_btns_width(buttons) abort
  622. if empty(a:buttons)
  623. return 0
  624. endif
  625. let minwidth = len(a:buttons)*3 - 1
  626. for txt in a:buttons
  627. let minwidth = minwidth + strdisplaywidth(txt)
  628. endfor
  629. return minwidth
  630. endfunction
  631. " Close windows that should auto hide
  632. function! s:close_auto_hide_wins(...) abort
  633. let winids = coc#float#get_float_win_list()
  634. let except = get(a:, 1, 0)
  635. for id in winids
  636. if except && id == except
  637. continue
  638. endif
  639. if coc#window#get_var(id, 'autohide', 0)
  640. call coc#float#close(id)
  641. endif
  642. endfor
  643. endfunction
  644. function! s:create_loading_buf() abort
  645. let bufnr = coc#float#create_buf(0)
  646. call s:change_loading_buf(bufnr, 0)
  647. return bufnr
  648. endfunction
  649. function! s:change_loading_buf(bufnr, idx) abort
  650. if bufloaded(a:bufnr)
  651. let target = getbufvar(a:bufnr, 'target_winid', v:null)
  652. if !empty(target) && !coc#float#valid(target)
  653. call coc#float#close(getbufvar(a:bufnr, 'popup'))
  654. return
  655. endif
  656. let line = get(s:frames, a:idx, ' ')
  657. call setbufline(a:bufnr, 1, line)
  658. call coc#highlight#add_highlight(a:bufnr, -1, 'CocNotificationProgress', 0, 0, -1)
  659. let idx = a:idx == len(s:frames) - 1 ? 0 : a:idx + 1
  660. call timer_start(100, { -> s:change_loading_buf(a:bufnr, idx)})
  661. endif
  662. endfunction