1
+ let s: supports_floating = exists (' *nvim_open_win' ) || has (' patch-8.1.1517' )
2
+ let s: winid = v: false
3
+ let s: prevwin = v: false
4
+ let s: preview_data = v: false
5
+
6
+ function ! lsp#ui#vim#output#closepreview () abort
7
+ if win_getid () == s: winid
8
+ " Don't close if window got focus
9
+ return
10
+ endif
11
+ " closing floats in vim8.1 must use popup_close() (nvim could use nvim_win_close but pclose
12
+ " works)
13
+ if s: supports_floating && s: winid && g: lsp_preview_float && ! has (' nvim' )
14
+ call popup_close (s: winid )
15
+ else
16
+ pclose
17
+ endif
18
+ let s: winid = v: false
19
+ let s: preview_data = v: false
20
+ augroup lsp_float_preview_close
21
+ augroup end
22
+ autocmd ! lsp_float_preview_close CursorMoved ,CursorMovedI ,VimResized *
23
+ doautocmd User lsp_float_closed
24
+ endfunction
25
+
26
+ function ! lsp#ui#vim#output#focuspreview () abort
27
+ " This does not work for vim8.1 popup but will work for nvim and old preview
28
+ if s: winid
29
+ if win_getid () != s: winid
30
+ let s: prevwin = win_getid ()
31
+ call win_gotoid (s: winid )
32
+ elseif s: prevwin
33
+ " Temporarily disable hooks
34
+ " TODO: remove this when closing logic is able to distinguish different move directions
35
+ autocmd ! lsp_float_preview_close CursorMoved ,CursorMovedI ,VimResized *
36
+ call win_gotoid (s: prevwin )
37
+ call s: add_float_closing_hooks ()
38
+ let s: prevwin = v: false
39
+ endif
40
+ endif
41
+ endfunction
42
+
43
+ function ! s: bufwidth () abort
44
+ let width = winwidth (0 )
45
+ let numberwidth = max ([&numberwidth , strlen (line (' $' ))+ 1 ])
46
+ let numwidth = (&number || &relativenumber )? numberwidth : 0
47
+ let foldwidth = &foldcolumn
48
+
49
+ if &signcolumn == ? ' yes'
50
+ let signwidth = 2
51
+ elseif &signcolumn == ? ' auto'
52
+ let signs = execute (printf (' sign place buffer=%d' , bufnr (' ' )))
53
+ let signs = split (signs, " \n " )
54
+ let signwidth = len (signs)>2 ? 2 : 0
55
+ else
56
+ let signwidth = 0
57
+ endif
58
+ return width - numwidth - foldwidth - signwidth
59
+ endfunction
60
+
61
+
62
+ function ! s: get_float_positioning (height, width) abort
63
+ let l: height = a: height
64
+ let l: width = a: width
65
+ " For a start show it below/above the cursor
66
+ " TODO: add option to configure it 'docked' at the bottom/top/right
67
+ let l: y = winline ()
68
+ if l: y + l: height >= winheight (0 )
69
+ " Float does not fit
70
+ if l: y - 2 > l: height
71
+ " Fits above
72
+ let l: y = winline () - l: height -1
73
+ elseif l: y - 2 > winheight (0 ) - l: y
74
+ " Take space above cursor
75
+ let l: y = 1
76
+ let l: height = winline ()-2
77
+ else
78
+ " Take space below cursor
79
+ let l: height = winheight (0 ) - l: y
80
+ endif
81
+ endif
82
+ let l: col = col (' .' )
83
+ " Positioning is not window but screen relative
84
+ let l: opts = {
85
+ \ ' relative' : ' win' ,
86
+ \ ' row' : l: y ,
87
+ \ ' col' : l: col ,
88
+ \ ' width' : l: width ,
89
+ \ ' height' : l: height ,
90
+ \ }
91
+ return l: opts
92
+ endfunction
93
+
94
+ function ! lsp#ui#vim#output#floatingpreview (data) abort
95
+ if has (' nvim' )
96
+ let l: buf = nvim_create_buf (v: false , v: true )
97
+ call setbufvar (l: buf , ' &signcolumn' , ' no' )
98
+
99
+ " Try to get as much pace right-bolow the cursor, but at least 10x10
100
+ let l: width = max ([s: bufwidth (), 10 ])
101
+ let l: height = max ([&lines - winline () + 1 , 10 ])
102
+
103
+ let l: opts = s: get_float_positioning (l: height , l: width )
104
+
105
+ let s: winid = nvim_open_win (buf , v: true , l: opts )
106
+ call nvim_win_set_option (s: winid , ' winhl' , ' Normal:Pmenu,NormalNC:Pmenu' )
107
+ call nvim_win_set_option (s: winid , ' foldenable' , v: false )
108
+ call nvim_win_set_option (s: winid , ' wrap' , v: true )
109
+ call nvim_win_set_option (s: winid , ' statusline' , ' ' )
110
+ call nvim_win_set_option (s: winid , ' number' , v: false )
111
+ call nvim_win_set_option (s: winid , ' relativenumber' , v: false )
112
+ call nvim_win_set_option (s: winid , ' cursorline' , v: false )
113
+ " Enable closing the preview with esc, but map only in the scratch buffer
114
+ nmap <buffer> <silent> <esc> :pclose<cr>
115
+ else
116
+ let s: winid = popup_atcursor (' ...' , {
117
+ \ ' moved' : ' any' ,
118
+ \ ' border' : [1 , 1 , 1 , 1 ],
119
+ \} )
120
+ endif
121
+ return s: winid
122
+ endfunction
123
+
124
+ function ! s: setcontent (lines , ft ) abort
125
+ if s: supports_floating && g: lsp_preview_float && ! has (' nvim' )
126
+ " vim popup
127
+ call setbufline (winbufnr (s: winid ), 1 , a: lines )
128
+ let l: lightline_toggle = v: false
129
+ if exists (' #lightline' ) && ! has (' nvim' )
130
+ " Lightline does not work in popups but does not recognize it yet.
131
+ " It is ugly to have an check for an other plugin here, better fix lightline...
132
+ let l: lightline_toggle = v: true
133
+ call lightline#disable ()
134
+ endif
135
+ call win_execute (s: winid , ' setlocal filetype=' . a: ft . ' .lsp-hover' )
136
+ if l: lightline_toggle
137
+ call lightline#enable ()
138
+ endif
139
+ else
140
+ " nvim floating
141
+ call setline (1 , a: lines )
142
+ setlocal readonly nomodifiable
143
+ let &l: filetype = a: ft . ' .lsp-hover'
144
+ endif
145
+ endfunction
146
+
147
+ function ! s: adjust_float_placement (bufferlines, maxwidth) abort
148
+ if has (' nvim' )
149
+ let l: win_config = {}
150
+ let l: height = min ([winheight (s: winid ), a: bufferlines ])
151
+ let l: width = min ([winwidth (s: winid ), a: maxwidth ])
152
+ let l: win_config = s: get_float_positioning (l: height , l: width )
153
+ call nvim_win_set_config (s: winid , l: win_config )
154
+ endif
155
+ endfunction
156
+
157
+ function ! s: add_float_closing_hooks () abort
158
+ if g: lsp_preview_autoclose
159
+ augroup lsp_float_preview_close
160
+ autocmd ! lsp_float_preview_close CursorMoved ,CursorMovedI ,VimResized *
161
+ autocmd CursorMoved ,CursorMovedI ,VimResized * call lsp#ui#vim#output#closepreview ()
162
+ augroup END
163
+ endif
164
+ endfunction
165
+
166
+ function ! lsp#ui#vim#output#getpreviewwinid () abort
167
+ return s: winid
168
+ endfunction
169
+
170
+ function ! s: open_preview (data) abort
171
+ if s: supports_floating && g: lsp_preview_float
172
+ let l: winid = lsp#ui#vim#output#floatingpreview (a: data )
173
+ else
174
+ execute &previewheight .' new'
175
+ let l: winid = win_getid ()
176
+ endif
177
+ return l: winid
178
+ endfunction
179
+
1
180
function ! lsp#ui#vim#output#preview (data) abort
181
+ if s: winid && type (s: preview_data ) == type (a: data )
182
+ \ && s: preview_data == a: data
183
+ \ && type (g: lsp_preview_doubletap ) == 3
184
+ \ && len (g: lsp_preview_doubletap ) >= 1
185
+ \ && type (g: lsp_preview_doubletap [0 ]) == 2
186
+ echo ' '
187
+ return call (g: lsp_preview_doubletap [0 ], [])
188
+ endif
2
189
" Close any previously opened preview window
3
190
pclose
4
191
5
192
let l: current_window_id = win_getid ()
6
193
7
- execute &previewheight .' new'
8
-
9
- let l: ft = s: append (a: data )
10
- " Delete first empty line
11
- 0 delete _
194
+ let s: winid = s: open_preview (a: data )
12
195
13
- setlocal readonly nomodifiable
196
+ let s: preview_data = a: data
197
+ let l: lines = []
198
+ let l: ft = s: append (a: data , l: lines )
199
+ call s: setcontent (l: lines , l: ft )
14
200
15
- let &l: filetype = l: ft . ' .lsp-hover'
201
+ " Get size information while still having the buffer active
202
+ let l: bufferlines = line (' $' )
203
+ let l: maxwidth = max (map (getline (1 , ' $' ), ' strdisplaywidth(v:val)' ))
16
204
17
205
if g: lsp_preview_keep_focus
18
206
" restore focus to the previous window
@@ -21,28 +209,35 @@ function! lsp#ui#vim#output#preview(data) abort
21
209
22
210
echo ' '
23
211
212
+ if s: supports_floating && s: winid && g: lsp_preview_float
213
+ if has (' nvim' )
214
+ call s: adjust_float_placement (l: bufferlines , l: maxwidth )
215
+ call s: add_float_closing_hooks ()
216
+ endif
217
+ doautocmd User lsp_float_opened
218
+ endif
24
219
return ' '
25
220
endfunction
26
221
27
- function ! s: append (data) abort
222
+ function ! s: append (data, lines ) abort
28
223
if type (a: data ) == type ([])
29
224
for l: entry in a: data
30
- call s: append (entry)
225
+ call s: append (entry, a: lines )
31
226
endfor
32
227
33
228
return ' markdown'
34
229
elseif type (a: data ) == type (' ' )
35
- silent put = a: data
230
+ call extend ( a: lines , split ( a: data, " \n " ))
36
231
37
232
return ' markdown'
38
233
elseif type (a: data ) == type ({}) && has_key (a: data , ' language' )
39
- silent put = ' ```' .a: data .language
40
- silent put = a: data .value
41
- silent put = ' ```'
234
+ call add ( a: lines , ' ```' .a: data .language )
235
+ call extend ( a: lines , split ( a: data .value, ' \n ' ))
236
+ call add ( a: lines , ' ```' )
42
237
43
238
return ' markdown'
44
239
elseif type (a: data ) == type ({}) && has_key (a: data , ' kind' )
45
- silent put = a: data .value
240
+ call add ( a: lines , a: data .value)
46
241
47
242
return a: data .kind == ? ' plaintext' ? ' text' : a: data .kind
48
243
endif
0 commit comments