The latest version of Vim, 8.2, has a new feature called "popups" or "popup windows". They sound interesting but I'm not quite sure what they're good for. Can you enlighten me...preferably with some examples?
-
@MartinTournoij I just noticed the large bounty you've attached. My understanding of bounties was pretty one-dimensional (i.e. it's about attracting additional and/or better answers) but now that I've done some research I think I understand your actual intent (as indicated by your chosen reason and the chosen waiting period) to which I say, "Thanks! That's very generous of you." :) – B Layer Apr 10 '20 at 0:09
-
Ya, I just wanted to add it to your answer. For some reason you still have to wait 24 hours when you fill in "reward existing answer" 🤷♂️ I got way too much reputation anyway just because I was active in the early days; I figured I might as well attach them to some exceptional answers as I come across them. – Martin Tournoij Apr 10 '20 at 12:02
-
In the various meta threads I read a lot of people questioned the 24h requirement despite Jeff Atwood's (IMO not very convincing) insistence in one thread that it is necessary. But there were also a number of people who said they like it and, in fact, some choose the full week deferral because it's an added reward for the poster to whom they intend to give the bounty. How? The extra visibility bountied questions/answers get, they reason, can lead to additional votes for the poster that they'd not otherwise receive. Anyways, thanks again! – B Layer Apr 10 '20 at 18:51
Background: I was playing around with Vim 8.2's popups recently (I have a small plugin that allows navigation of sections in a markup document and I was looking at showing section hierarchies in popups). I figured the info I collected would make a decent introduction...
Overview
Vim 8.2's popup windows allow Vimscript authors and plugin developers to create one or more floating, modal windows that, among other things, can be used for tool tips, notifications/alerts, dialog boxes and transient menus.
Window and Buffer
Each popup is a Vim window and an associated buffer. The window and buffer have a number of unique characteristics including:
- Popups cover some or all of vim's regular windows/buffers. They contain no cursor but the cursor in the regular buffer underneath it is visible through the popup window. (For the sake of not losing track of your place, I assume.)
- Popups can be stacked on top of each other with stacking order determined by value of each windows'
zindex
attribute. - The buffer is not
'buflisted'
, is'bufhidden'
and has a'buftype'
of "popup". - A buffer's initial contents can be specified several ways including by putting the contents in a string or a list of strings. You can also use an existing buffer created with
bufadd()
(and having a couple other required characteristics). - The buffer has no swapfile or undo capability and is not editable by normal means but the content can be modified post-creation with
popup_settext()
. - The window/buffer look and feel is highly customizable.
Creation
There are six different functions for creating a popup. That's a lot but only one of them is required, popup_create()
and the others are "pre-packaged" configurations with particular purposes. Here they are with brief descriptions (from :h popup-functions
):
popup_create()
- centered in the screenpopup_atcursor()
- just above the cursor position, closes on mouse movepopup_beval()
- at pos indicated by v:beval_ variables, closes on mouse movepopup_notification()
- show a notification for three secondspopup_dialog()
- centered with padding and borderpopup_menu()
- prompt for selecting an item from a list
Customization
Text appearance can be controlled with syntax highlighting or using text properties (another major new feature in Vim 8.2...like syntax highlighting except it sticks to associated text as it moves in the buffer). There are also a number of attributes or "options" that control certain other look-and-feel characteristics. This is a sampling of those options.
- Placement/Size:
line
,col
,pos
(e.g. "botright"),maxheight
,resize
(by mouse),drag
(with mouse),minwidth
(Note: window height/width is ordinarily based on the width and height of the contained text.) - Appearance:
padding
,border
,borderchars
,highlight
(a la:hi
),title
,mask
(see example below) - Behavior:
time
(until close),callback
(function),filter
(input processing)
Option values can be defined on creation of a popup window or afterwards through the popup_setoptions()
function.
Interaction
A popup's lifetime can be controlled several ways including by timer, upon mouse movement or user input or programmatically with popup_close()
. In a pinch, there is the nuclear option popup_clear()
that will dismiss any and all existing popups.
How a popup processes user input can be controlled with so-called "filters". There are a couple of builtin filters including the "yesno" filter demonstrated in the examples below.
Each popup can have an assigned callback function. When the popup is closed this function is called with the "result". This is most applicable for popups acting as menus. The result, in that case, is usually just the number of the row that the user selected. We'll see this in action, too, in the examples.
Examples
Let's look at examples of some of the features mentioned above.
Notification
For a time-limited notification popup:
call popup_notification("Get the hell out of Dodge!", #{ line: 5, col: 10, highlight: 'WildMenu', } )
As noted above this will show for three seconds then close.
Menu
Now for a simple menu:
func! MenuCB(id, result)
echo "You chose item #".a:result
endfunc
call popup_menu(['The quick fox...', '...jumped over...', '...the lazy dogs!'],
#{ title: "Well? Pick one", callback: 'MenuCB', line: 25, col: 40, highlight: 'Question', border: [], close: 'click', padding: [1,1,0,1]} )
Note the added title
and padding
option to put a little space between the title and side borders and the menu items. Choosing the last row results in...
Yes/No Dialog
In this example we'll use the builtin filter popup_filter_yesno
. This will wait until the user has clicked a close key (x
or <Esc>
) or one of Y
, y
, N
, or n
. Yes replies cause the callback to be called with result 1. No sends result 0.
func! YayOrNay(id, result)
if a:result | echo "You said 'yay'" | endif
endfunc
call popup_dialog('[y]ay or [n]ay?', #{ filter: 'popup_filter_yesno', callback: 'YayOrNay'})
If I choose Y
or y
the message ("You said 'yay'") will show on the status line.
If you specify your own filter function then you can intercept a subset of key presses, handling them in whatever way you choose and pass the rest on to the generic filter, popup_filter_menu()
. There's an example at :h popup-examples
.
The Base Case
Let's try the base function, popup_create()
.
call popup_create(poplist, #{ close: 'click' })
Where poplist
begins with: [' 1 Vim Launch and App Startup', ' 2 General Use (Editing)', ' 3 Auto Commands', ' 4 Scripting', ' 5 Syntax and Basics', ' 6 Variables', ' 7 Windows', ...etc...]
. The idea here was to see what a hierarchical menu (i.e. having depth > 1) would look like.
This is what a popup with all default options looks like (not counting the close: 'click'
option so I had an easy way to dismiss it): positioned in the middle of the window, no borders, dimensions dictated by the contained text.
The Z-Index and Masking
Finally, let's see the z-index functionality and, for amusement mostly, demonstrate the mask attribute. With 'mask' you can specify popup coordinates and those sections of the popup will be rendered as transparent. I've not thought of a totally practical use for this yet so here I just cut off a couple corners and poked a couple big holes in the thing.
call popup_create(poplist, #{ zindex: 20, line: 5, col: 20, close: 'click',
mask: [[1, 10, 1, 3], [-10, -4, 3, 5], [10, 23, -5, -2], [-10, -1, -3, -1]] })
Normally one would expect additional popups to be placed over existing ones. But by specifying a lower value for the zindex
attribute....
call popup_create(poplist, #{ zindex: 10, line: 7, col: 25, close: 'click', highlight: 'CursorLine' })
...we can make it a pop-under:
(The garish pink? That's the default color for popups in many color schemes. :p A lot of auto-completion users probably recognize it.)
Other Features
Okay, hopefully that gives everyone a good feeling for what popups are capable of. There are certainly some features I haven't covered or only mentioned briefly including:
- Asynchronous content fill-in
- Popup manipulation functions (e.g.
popup_move()
,popup_setoptions()
) - User-defined filters
- Key mappings
- Running a terminal in a popup window
To learn more about those and everything else popup related visit the dedicated 1000+ line chapter in the Vim help docs with :h popups
.
-
1That markdown outline jumping thing sounds useful... i’d love to stay posted on its development – D. Ben Knoble♦ Apr 5 '20 at 14:21
-
3@D.BenKnoble I created it for myself but I can share it if it reaches an appropriate level of stability/usability. Note, though, that I said markUP. Specifically, I use asciidoc(tor) which is similar to but much more powerful than markdown. – B Layer Apr 5 '20 at 22:37
-
1
-
1@MaximKim Looks like we're working on similar things. Add
scrollbar: 1
to the option map when you callpopup_create
and a scrollbar will be there if the text doesn't fit. I haven't tried them yet because even my largest asciidoctor doc, which would probably render out to 150 printed pages (I should write a book) has "only" 19 top-level sections. I do have a huge monitor and use tall windows, to be fair. – B Layer Apr 8 '20 at 20:02 -
1@D.BenKnoble The markup section navigator thingy works nicely with popups. I had to add custom key handler so numbers can be used to select sections (by default you get nothing more than Up/Down or j/k which sucks with 40 or more sections) I'm digging it and use it every day. Probably a while yet until I share it...todo list is still long...but I almost certainly will. – B Layer May 10 '20 at 14:14