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.
1 " Author: Jan-Grimo Sobez <jan-grimo.sobez@phys.chem.ethz.ch>
2 " Author: Kevin Clark <kevin.clark@gmail.com>
3 " Author: D. Ben Knoble <ben.knoble+github@gmail.com>
4 " Author: Shaun Duncan <shaun.duncan@gmail.com>
5 " Description: Floating preview window for showing whatever information in.
7 " Precondition: exists('*nvim_open_win') || has('popupwin')
9 function! ale#floating_preview#Show(lines, ...) abort
10 if !exists('*nvim_open_win') && !has('popupwin')
12 echom 'Floating windows not supported in this vim instance.'
17 let l:options = get(a:000, 0, {})
20 call s:NvimShow(a:lines, l:options)
22 call s:VimShow(a:lines, l:options)
28 function! s:NvimShow(lines, options) abort
29 " Remove the close autocmd so it doesn't happen mid update
30 augroup ale_floating_preview_window
34 " Only create a new window if we need it
35 if !exists('w:preview') || index(nvim_list_wins(), w:preview['id']) is# -1
36 call s:NvimCreate(a:options)
38 call nvim_buf_set_option(w:preview['buffer'], 'modifiable', v:true)
41 " Execute commands in window context
42 if exists('*win_execute')
43 for l:command in get(a:options, 'commands', [])
44 call win_execute(w:preview['id'], l:command)
47 let l:parent_window = nvim_get_current_win()
49 call nvim_set_current_win(w:preview['id'])
51 for l:command in get(a:options, 'commands', [])
52 call execute(l:command)
55 call nvim_set_current_win(l:parent_window)
58 " Return to parent context on move
59 augroup ale_floating_preview_window
62 if g:ale_close_preview_on_insert
63 autocmd CursorMoved,TabLeave,WinLeave,BufWinLeave,WinScrolled,InsertEnter <buffer> ++once call s:NvimClose()
65 autocmd CursorMoved,TabLeave,WinLeave,BufWinLeave,WinScrolled <buffer> ++once call s:NvimClose()
69 let [l:lines, l:width, l:height] = s:NvimPrepareWindowContent(a:lines)
71 call nvim_win_set_width(w:preview['id'], l:width)
72 call nvim_win_set_height(w:preview['id'], l:height)
73 call nvim_buf_set_lines(w:preview['buffer'], 0, -1, v:false, l:lines)
74 call nvim_buf_set_option(w:preview['buffer'], 'modified', v:false)
75 call nvim_buf_set_option(w:preview['buffer'], 'modifiable', v:false)
78 function! s:VimShow(lines, options) abort
79 if g:ale_close_preview_on_insert
80 " Remove the close autocmd so it doesn't happen mid update
81 silent! autocmd! ale_floating_preview_window
84 " Only create a new window if we need it
85 if !exists('w:preview') || index(popup_list(), w:preview['id']) is# -1
86 call s:VimCreate(a:options)
89 " Execute commands in window context
90 for l:command in get(a:options, 'commands', [])
91 call win_execute(w:preview['id'], l:command)
94 call popup_settext(w:preview['id'], a:lines)
96 if g:ale_close_preview_on_insert
97 augroup ale_floating_preview_window
99 autocmd InsertEnter * ++once call s:VimClose()
104 function! s:NvimPrepareWindowContent(lines) abort
105 let l:max_height = 10
107 let l:width = max(map(copy(a:lines), 'strdisplaywidth(v:val)'))
108 let l:height = min([len(a:lines), l:max_height])
110 return [a:lines[0:l:height-1], l:width, l:height]
113 function! s:NvimCreate(options) abort
114 let l:left = get(g:ale_floating_window_border, 0, '|')
115 let l:top = get(g:ale_floating_window_border, 1, '-')
117 let l:popup_opts = extend({
118 \ 'relative': 'cursor',
123 \ 'style': 'minimal',
124 \ 'border': empty(g:ale_floating_window_border) ? 'none' : [
125 \ get(g:ale_floating_window_border, 2, '+'),
127 \ get(g:ale_floating_window_border, 3, '+'),
128 \ get(g:ale_floating_window_border, 6, l:left),
129 \ get(g:ale_floating_window_border, 4, '+'),
130 \ get(g:ale_floating_window_border, 7, l:top),
131 \ get(g:ale_floating_window_border, 5, '+'),
134 \ }, s:GetPopupOpts())
136 let l:buffer = nvim_create_buf(v:false, v:false)
137 let l:winid = nvim_open_win(l:buffer, v:false, l:popup_opts)
139 call nvim_buf_set_option(l:buffer, 'buftype', 'acwrite')
140 call nvim_buf_set_option(l:buffer, 'bufhidden', 'delete')
141 call nvim_buf_set_option(l:buffer, 'swapfile', v:false)
142 call nvim_buf_set_option(l:buffer, 'filetype', get(a:options, 'filetype', 'ale-preview'))
144 let w:preview = {'id': l:winid, 'buffer': l:buffer}
147 function! s:VimCreate(options) abort
149 let l:popup_opts = extend({
150 \ 'line': 'cursor+1',
155 \ 'padding': [0, 1, 0, 1],
157 \ 'borderchars': empty(g:ale_floating_window_border) ? [' '] : [
158 \ get(g:ale_floating_window_border, 1, '-'),
159 \ get(g:ale_floating_window_border, 6, '|'),
160 \ get(g:ale_floating_window_border, 7, '-'),
161 \ get(g:ale_floating_window_border, 0, '|'),
162 \ get(g:ale_floating_window_border, 2, '+'),
163 \ get(g:ale_floating_window_border, 3, '+'),
164 \ get(g:ale_floating_window_border, 4, '+'),
165 \ get(g:ale_floating_window_border, 5, '+'),
168 \ }, s:GetPopupOpts())
170 let l:popup_id = popup_create([], l:popup_opts)
171 call setbufvar(winbufnr(l:popup_id), '&filetype', get(a:options, 'filetype', 'ale-preview'))
172 let w:preview = {'id': l:popup_id}
175 function! s:NvimClose() abort
177 let l:restore_visual = l:mode is# 'v' || l:mode is# 'V' || l:mode is# "\<C-V>"
179 if !exists('w:preview')
183 call setbufvar(w:preview['buffer'], '&modified', 0)
185 if win_id2win(w:preview['id']) > 0
186 execute win_id2win(w:preview['id']).'wincmd c'
196 function! s:VimClose() abort
197 if !exists('w:preview')
201 call popup_close(w:preview['id'])
205 " get either the results of a function callback or dictionary for popup overrides
206 function! s:GetPopupOpts() abort
207 if exists('g:ale_floating_preview_popup_opts')
208 let l:ref = g:ale_floating_preview_popup_opts
210 if type(l:ref) is# v:t_dict
212 elseif type(l:ref) is# v:t_string
214 return function(l:ref)()