@@ -12,11 +12,15 @@ function! s:popup__close() dict abort
12
12
return
13
13
endif
14
14
15
- let winnr = self .get_winnr ()
16
- if winnr > 0
17
- " Without this 'noautocmd', the BufWipeout event will be triggered and
18
- " this function will be called again.
19
- noautocmd execute winnr . ' wincmd c'
15
+ if self .type == # ' popup'
16
+ call popup_close (self .win_id)
17
+ else
18
+ let winnr = self .get_winnr ()
19
+ if winnr > 0
20
+ " Without this 'noautocmd', the BufWipeout event will be triggered and
21
+ " this function will be called again.
22
+ noautocmd execute winnr . ' wincmd c'
23
+ endif
20
24
endif
21
25
22
26
unlet self .bufnr
@@ -48,6 +52,9 @@ endfunction
48
52
let s: popup .set_buf_var = funcref (' s:popup__set_buf_var' )
49
53
50
54
function ! s: popup__scroll (map ) dict abort
55
+ if self .type == # ' popup'
56
+ return
57
+ endif
51
58
let winnr = self .get_winnr ()
52
59
if winnr == 0
53
60
return
@@ -60,6 +67,9 @@ endfunction
60
67
let s: popup .scroll = funcref (' s:popup__scroll' )
61
68
62
69
function ! s: popup__into () dict abort
70
+ if self .type == # ' popup'
71
+ return
72
+ endif
63
73
let winnr = self .get_winnr ()
64
74
if winnr == 0
65
75
return
@@ -150,10 +160,138 @@ function! s:popup__get_opener_winnr() dict abort
150
160
endfunction
151
161
let s: popup .get_opener_winnr = funcref (' s:popup__get_opener_winnr' )
152
162
163
+ function ! s: popup__vimpopup_keymaps () dict abort
164
+ " TODO: allow customisation via config var once happy with dict key names
165
+ return {
166
+ \ ' scroll_down_1' : [" \<c-e> " , " \<c-n> " , " \<Down> " ],
167
+ \ ' scroll_up_1' : [" \<c-y> " , " \<c-p> " , " \<Up> " ],
168
+ \ ' scroll_down_page' : [" \<c-f> " , " \<PageDown> " ],
169
+ \ ' scroll_up_page' : [" \<c-b> " , " \<PageUp> " ],
170
+ \ ' scroll_down_half' : [" \<c-d> " ],
171
+ \ ' scroll_up_half' : [" \<c-u> " ],
172
+ \ }
173
+ endfunction
174
+ let s: popup .vimpopup_keymaps = funcref (' s:popup__vimpopup_keymaps' )
175
+
176
+ function ! s: popup__vimpopup_win_filter (win_id, key ) dict abort
177
+ " Note: default q handler assumes we are in the popup window, but in Vim we
178
+ " cannot enter the popup window, so we override the handling here for now
179
+ let keymaps = self .vimpopup_keymaps ()
180
+ if a: key == # ' q'
181
+ call self .close ()
182
+ elseif a: key == # ' ?'
183
+ call self .echo_help ()
184
+ elseif has_key (self .opts, ' mappings' ) && has_key (self .opts.mappings, a: key )
185
+ call self .opts.mappings[a: key ][0 ]()
186
+ elseif index (keymaps[' scroll_down_1' ], a: key ) >= 0
187
+ call win_execute (a: win_id , " normal! \<c-e> " )
188
+ elseif index (keymaps[' scroll_up_1' ], a: key ) >= 0
189
+ call win_execute (a: win_id , " normal! \<c-y> " )
190
+ elseif index (keymaps[' scroll_down_page' ], a: key ) >= 0
191
+ call win_execute (a: win_id , " normal! \<c-f> " )
192
+ elseif index (keymaps[' scroll_up_page' ], a: key ) >= 0
193
+ call win_execute (a: win_id , " normal! \<c-b> " )
194
+ elseif index (keymaps[' scroll_down_half' ], a: key ) >= 0
195
+ call win_execute (a: win_id , " normal! \<c-d> " )
196
+ elseif index (keymaps[' scroll_up_half' ], a: key ) >= 0
197
+ call win_execute (a: win_id , " normal! \<c-u> " )
198
+ elseif a: key == ? " \<ScrollWheelUp> "
199
+ let pos = getmousepos ()
200
+ if pos.winid == a: win_id
201
+ call win_execute (a: win_id , " normal! 3\<c-y> " )
202
+ else
203
+ return 0
204
+ endif
205
+ elseif a: key == ? " \<ScrollWheelDown> "
206
+ let pos = getmousepos ()
207
+ if pos.winid == a: win_id
208
+ call win_execute (a: win_id , " normal! 3\<c-e> " )
209
+ else
210
+ return 0
211
+ endif
212
+ else
213
+ return 0
214
+ endif
215
+ return 1
216
+ endfunction
217
+ let s: popup .vimpopup_win_filter = funcref (' s:popup__vimpopup_win_filter' )
218
+
219
+ function ! s: popup__vimpopup_win_opts (width, height) dict abort
220
+ " Note: calculations here are not the same as for Neovim floating window as
221
+ " Vim popup positioning relative to the editor window is slightly different,
222
+ " but the end result is that the popup is in same position in Vim as Neovim
223
+ if self .opened_at[0 ] + a: height <= &lines
224
+ let vert = ' top'
225
+ let row = self .opened_at[0 ] + 1
226
+ else
227
+ let vert = ' bot'
228
+ let row = self .opened_at[0 ] - 1
229
+ endif
230
+
231
+ if self .opened_at[1 ] + a: width <= &columns
232
+ let hor = ' left'
233
+ let col = self .opened_at[1 ]
234
+ else
235
+ let hor = ' right'
236
+ let col = self .opened_at[1 ]
237
+ endif
238
+
239
+ " Note: scrollbar disabled as seems buggy, even in Vim 9.1, scrollbar does
240
+ " not reliably appear when content does not fit, which means scroll is not
241
+ " always enabled when needed, so handle scroll in filter function instead.
242
+ " This now works the same as Neovim, no scrollbar, but mouse scroll works.
243
+ return extend ({
244
+ \ ' line' : row,
245
+ \ ' col' : col ,
246
+ \ ' pos' : vert . hor ,
247
+ \ ' filtermode' : ' n' ,
248
+ \ ' filter' : self .vimpopup_win_filter,
249
+ \ ' minwidth' : a: width ,
250
+ \ ' maxwidth' : a: width ,
251
+ \ ' minheight' : a: height ,
252
+ \ ' maxheight' : a: height ,
253
+ \ ' scrollbar' : v: false ,
254
+ \ ' highlight' : ' gitmessengerPopupNormal'
255
+ \ },
256
+ \ g: git_messenger_vimpopup_win_opts )
257
+ endfunction
258
+ let s: popup .vimpopup_win_opts = funcref (' s:popup__vimpopup_win_opts' )
259
+
260
+ function ! s: popup__vimpopup_win_callback (win_id, result) dict abort
261
+ " Hacky custom cleanup for vimpopup, necessary as buffer never entered
262
+ silent ! unlet b: __gitmessenger_popup
263
+ autocmd ! plugin - git- messenger- close * <buffer>
264
+ autocmd ! plugin - git- messenger- buf - enter
265
+ endfunction
266
+ let s: popup .vimpopup_win_callback = funcref (' s:popup__vimpopup_win_callback' )
267
+
153
268
function ! s: popup__open () dict abort
154
269
let self .opened_at = s: get_global_pos ()
155
270
let self .opener_bufnr = bufnr (' %' )
156
271
let self .opener_winid = win_getid ()
272
+
273
+ if g: git_messenger_vimpopup_enabled && has (' popupwin' )
274
+ let self .type = ' popup'
275
+ let [width, height] = self .window_size ()
276
+ let win_id = popup_create (' ' , self .vimpopup_win_opts (width, height))
277
+ " Note: all local options are automatically set for new popup buffers
278
+ " in Vim so we only need to override a few, see :help popup-buffer
279
+ call win_execute (win_id, ' setlocal nomodified nofoldenable nomodeline conceallevel=2' )
280
+ call popup_settext (win_id, self .contents)
281
+ call win_execute (win_id, ' setlocal nomodified nomodifiable' )
282
+ if has_key (self .opts, ' filetype' )
283
+ " Note: setbufvar() seems necessary to trigger Filetype autocmds
284
+ call setbufvar (winbufnr (win_id), ' &filetype' , self .opts.filetype )
285
+ endif
286
+ " Allow multiple invocations of :GitMessenger command to toggle popup
287
+ " See gitmessenger#popup#close_current_popup() and gitmessenger#new()
288
+ let b: __gitmessenger_popup = self " local to opener, removed by callback
289
+ call popup_setoptions (win_id, { ' callback' : self .vimpopup_win_callback })
290
+ let self .bufnr = winbufnr (win_id)
291
+ let self .win_id = win_id
292
+ return
293
+ endif
294
+
157
295
let self .type = s: floating_window_available ? ' floating' : ' preview'
158
296
159
297
let [width, height] = self .window_size ()
@@ -228,6 +366,17 @@ endfunction
228
366
let s: popup .open = funcref (' s:popup__open' )
229
367
230
368
function ! s: popup__update () dict abort
369
+
370
+ if self .type == # ' popup'
371
+ let [width, height] = self .window_size ()
372
+ let win_id = self .win_id
373
+ call popup_setoptions (self .win_id, self .vimpopup_win_opts (width, height))
374
+ call win_execute (win_id, ' setlocal modifiable' )
375
+ call popup_settext (win_id, self .contents)
376
+ call win_execute (win_id, ' setlocal nomodified nomodifiable' )
377
+ return
378
+ endif
379
+
231
380
" Note: `:noautocmd` to prevent BufLeave autocmd event (#13)
232
381
" It should be ok because the cursor position is finally back to the first
233
382
" position.
@@ -291,6 +440,15 @@ function! s:popup__echo_help() dict abort
291
440
call sort (maps, ' i' )
292
441
let maps += [' ?' ]
293
442
443
+ " When using Vim popup only one echo command output is shown in cmdline
444
+ if self .type == # ' popup'
445
+ let lines = map (maps, {_, map ->
446
+ \ map . ' : ' . ( map ==# '?' ? 'Show this help' : self.opts.mappings[map][1] )
447
+ \ })
448
+ echo join (lines , " \n " )
449
+ return
450
+ endif
451
+
294
452
for map in maps
295
453
if map ==# '?'
296
454
let desc = ' Show this help'
0 commit comments