Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[discuss] Popup Window API design. #4063

Closed
skywind3000 opened this issue Mar 2, 2019 · 91 comments

Comments

Projects
None yet
@skywind3000
Copy link

commented Mar 2, 2019

This is not an issue, but I have no idea how to attach images in vim_dev group. So forgive me create this post in the issues and it can be closed immediately.

The human readable page is here:
https://github.com/skywind3000/vim-proposal/blob/master/popup_window.md


Popup Window API Proposal

Problem

Vim has already got some builtin popup-widgets for certain usage, like completion menu or balloon. But users and plugin authors are still asking for more:

In Vim conf 2018, Bram claimed that he has plan for this already:

Popup Windows:

Use for a notification:

  • Asynchronously show window with text “build done”.
  • Remove after a few seconds.

Use for picking an item:

  • Show window where each line is an item
  • Let user pick an item
  • A bit like confirm() but much nicer

Summary, there are two types of popup window: interactive and non-interactive.

For non-interactive popup windows, people want to use them to:

  • Display the documentation for a function:

emacs-2

  • Display linting errors or warnings:

kakoune-1

  • Preview a portion of file (can be used to preview grep results in quickfix):

emacs-1

  • Display funny help for newbies (like paperclip assistent in office 2000):

kakoune-2

For interactive popup windows, people want to use them to:

  • pick an item:

ex-pick

  • display a nice popup menu:

ex-menu

  • cmd line completion:

ex-cmdline

  • use fuzzy finder in a popup window:

ex-fuzzy

There are too many popup-related widgets for certain usage, designing one by one is nearly impossible.

Implementing a neovim's floating window will take too much time (it is working in progress for almost 2-years and still not merge to master).

Can we implement the popup window in a simple and adaptive way ? Is this possible to unify all their needs and simplify API design ?

The following parts of this article will introduce an overlay mechanism similar to Emacs's text overlay which is the backend of various popup windows in Emacs.

API Scope

Popup windows will draw into an overlay layer, A popup will remain after creation until an erase function is called. Everything in the overlay will not interfere vim's states, people can continue editing or using vim commands no matter there is a popup window or not.

So, the APIs are only designed for drawing a popup window and has nothing to do with user input. Dialogs like yes/no box and confirm box require user input, can be simulated with following steps:

function! Dialog_YesNo(...)
    while not_quit
       draw/update the popup window
       get input from getchar()
    endwhile
    erase the popup window
endfunc

Popup window APIs is not responsible for any interactive functionalities. Instead of implementing a complex widget/event system (which is too complex), it is sane to let user to handle the input by getchar().

There can be a popup.vim script contains some predefined popup windows/dialogs and will be shipped with vim itself. User can use the primitive APIs and getchar() to implement other complex dialogs like a popup fuzzy finder or a command line history completion box.

Overlay Buffer

The overlay buffer is a character matrix with the same size of the screen:

|\     |\
| \    | \
|  \   |  \
|   \  |   \
|   |  |   |
| 1 |  | 2 |   <---- Observer
|   |  |   |
|   /  |   /
|  /   |  /
| /    | /
|/     |/

^      ^
|      |
|   Overlay Buffer (M rows and N columns)
|
Ground Vim UI (M rows and N columns)
 

Similar to Video Buffer (0xb8000) in x86's text mode, the overlay buffer is a 2D array of characters and attributes. Change the content of the 2D array will change the text in the screen.

The overlay buffer is invisible by default and can be enabled by:

set guioptions+=o

Every existent text rendering code in both Vim & GVim needs to be updated to support this overlay buffer, once it finished, we can use it to build powerful popup windows.

There are also some basic APIs for overlay buffer:

  • add a text string with position and attribute.
  • erase a rectangle of text.
  • command redrawoverlay to update the overlay (it uses double buffer to prevent flicker).

With these primitive APIs, user can draw what ever they like on the overlay buffer.

Overlay Panes

Overlay panes is an abstraction of the popup windows, it consists of:

  • position and size
  • z order (for overlapping calculation)
  • background color
  • border styles
  • lines of text and text-properties

There can be multiple panes at the same time, the panes can be manipulated by:

pane_create(int row, int col, int width, int height, ...);
pane_destroy(int pane_id);
pane_update(int pane_id, ...);
pane_move(int pane_id, int new_row, int new_col, int new_width, int new_height);
pane_show(int pane_id, bool show_hide);

(PS: they are provided as both C-apis and vim functions).

The life cycle of a pane is between pane_create and pane_destroy.

The (row, col) is using screen coordinate system, and there can be some functions to convert window based coordinate system to screen coordinate system. If you want to display a popup balloon right above your cursor, you can use them to calculate the position.

Finally, there is a function to render the pane list into the overlay buffer:

pane_flush();

If you create some panes, the overlay buffer will not change until pane_flush().

Popup Windows

All the common popup windows are implemented in popup.vim script, they will use panes to display a popup window and getchar() to provide interactive functionalities.

There are some predefined popup windows:

  • Popup_Message(): display a "build complete" message and hide after a few seconds.
  • Popup_LinterHint(): display a message right above cursor and hide if cursor moves outside current <cword>.
  • Popup_Menu(): display a menu (use getchar() to receive user input) and quit after user select an item.
  • Popup_YesNo(): display a yes/no box and return the result after user made a selection.
  • ...
  • and so on.

User or plugin authors can use the high level APIs provided by popup.vim or design their own popup window by utilizing lower level pane APIs or overlay APIs.

Summary

It is complex to design an event system or NeoVim's floating window, and nearly impossible to implement every type of popup window for certain usage.

To unify and simplify the interface, this proposal suggests to provide an overlay mechanism with some primitive APIs to:

  • render a popup window (pane)
  • erase a popup window
  • update a popup window

And let user handle input themself by getchar(). At last, makes it possible to enable users to create various popup windows with different styles and functionalities.

--

2019/3/2 edit: floating window got merged to master.

@prabirshrestha

This comment has been minimized.

Copy link

commented Mar 2, 2019

Few more things I would like to add.

  • It would also be good to see if we can come up with a different richer ui for GUI. For example embedding bold, italic, images and markdown texts, hr would be useful. Might be allow the user to set html and use the OS webview?
  • As for flush would be good to have an api that allows us to optimize re-renders. I'm working on creating a fast async quickpick with vimscript only that can support fuzzy finder and to make it look responsive I need to draw a spinner which only takes one char but because I'm calling redraw everything is getting redrawn. (If anyone knows how too force redraw statusline only let me know, that would also solve this issue).

https://github.com/prabirshrestha/quickpick.vim

NPM Picker

@oblitum

This comment has been minimized.

Copy link

commented Mar 2, 2019

@skywind3000: you may correct this statement:

it is working in progress for almost 2-years and still not merge to master

See r/vim/comments/discuss_vim_popup_window_api_design#comment.

@skywind3000

This comment has been minimized.

Copy link
Author

commented Mar 3, 2019

Possible implementation for GVim and Vim:

For Windows, create a layered child window on the top of textarea. Layered Window can use a color key to control transparent area. No need to update most of GVim code.

For terminal, ncurses has overlapping windows and panels. I don't know if they can be used because I have never used ncurses.

@epheien

This comment has been minimized.

Copy link

commented Mar 3, 2019

Currently, nvim support floating window!
Vim need some features against it.

@iamcco

This comment has been minimized.

Copy link

commented Mar 3, 2019

  • It would also be good to see if we can come up with a different richer ui for GUI. For example embedding bold, italic, images and markdown texts, hr would be useful. Might be allow the user to set html and use the OS webview?

webview is crazy great

@skywind3000

This comment has been minimized.

Copy link
Author

commented Mar 3, 2019

@iamcco @prabirshrestha Webview is too big for vim, GVim needs to include at least 20MB dynamic libraries to support webview. You can check the binary size of CEF or Electron.

The webview can be implemented in a separated process, and attach to GVim's main window.

@prabirshrestha

This comment has been minimized.

Copy link

commented Mar 3, 2019

By webview I mean the OS default browser that is for Mac it would be safari and windows IE. Not sure about Linux. But if it isn’t portable then not worth it. Or if one could make a markdown renderer that would also be great.

@marcotrosi

This comment has been minimized.

Copy link

commented Mar 3, 2019

I think it is very important that the implementation is super simple (KISS) and NOT super feature rich. We need only the basic overlay with just the proposed controls and new highlight groups and then the users are free to use it for various stuff. There will be many new ideas, you'll see.

@epheien

This comment has been minimized.

Copy link

commented Mar 3, 2019

Prefer text ui, not webview.
neovim's floating window is awesome feature!

@bstaletic

This comment has been minimized.

Copy link

commented Mar 3, 2019

For the plugin writers' sanity sake, we should really try to make our implementation's API as close to neovim's as possible. Though, that's a little hard at the moment, considering that neovim's documentation isn't available just yet.

@bfredl

This comment has been minimized.

Copy link

commented Mar 3, 2019

@bstaletic I'm working on it here neovim/neovim#9669, though I need adjust the script to show the indented text in nvim_open_win() correctly, so currently that part can be read here.

Edit: initial documentation is on master, see :help api-floatwin

@skywind3000

This comment has been minimized.

Copy link
Author

commented Mar 4, 2019

@bstaletic

This comment has been minimized.

Copy link

commented Mar 4, 2019

That RFC has been updated in my vim fork: https://github.com/bstaletic/vim/tree/current_argument_update

@chemzqm

This comment has been minimized.

Copy link

commented Mar 15, 2019

Highlight support is great, like this:
Screen Shot 2019-03-15 at 6 02 41 PM

One of reasons that I don't like balloon is that doesn't allow highlight.

@myitcv

This comment has been minimized.

Copy link

commented Apr 17, 2019

As part of https://github.com/myitcv/govim I'd like to see development in this area. govim is backed by gopls, an LSP server. So we have lots of rich information some users might like to be surfaced.

One of the key use cases is signature help. That is, when the user is in the middle of typing an expression that represents a function/method call, signature help reminds them of the function/method's signature, which parameter they are currently providing etc.

Some users will prefer this sort of detail in the status line.

Others, particularly those perhaps more used to things like VSCode, will appreciate the option of a balloon-like presentation of the signature help immediately above/below the relevant position.

Highlighting support would be a bonus, not least because it can be used to show the current parameter. (Highlighting support in balloons would also be useful too)

@brammool - what are your thoughts on this space in general?

@oblitum

This comment has been minimized.

Copy link

commented Apr 23, 2019

@myitcv reminder that you have all that available in neovim master/nightly, with coc.nvim making use of it for quite a while already. I find the govim's approach regarding adopting Vim8-only late remote API instead of NeoVim's one (and producing/using a wrapper over it for Vim8) kinda... strange. Is it worth it? I see even @Shougo preferring the latest approach with his plugins, even despite he being the original author of Vim's remote API implementation, if I recall correctly.

I think the remote API feature is one hard-to-borrow idea if you simply dismiss implementing it in a compatible way, and since NeoVim took the lead, I've seen many authors leveraging its remote API before Vim came with its own, unrelated one, that's of no use for the already existing remote plugins in NeoVim-land. Reason why I hardly see any Vim8-only remote plugin around.

@myitcv

This comment has been minimized.

Copy link

commented Apr 23, 2019

@oblitum - I'd be happy to continue the conversation about govim, Neovim vs Vim8, different APIs etc in an issue over at https://github.com/myitcv/govim. I don't think this thread is the best place for that discussion.

@brammool

This comment has been minimized.

Copy link
Contributor

commented May 12, 2019

I have added a detailed design in patch 8.1.1329. Feel free to comment.

@puremourning

This comment has been minimized.

Copy link

commented May 13, 2019

Regarding the API, i'm a strong believer in "write the usage code first", i.e. you can't really know if the API works without some practical code which uses it. Bram, if you have a toy or POC implementation of the proposed API, i am more than willing to rewrite our my RFC demo to use it and let you know how it goes ? But on the face of it, your proposed API does seem to be a superset of what i proposed in that RFC.

In my original proposal i said this(;

hint_pum_set( {arg} ) = set the contents of the hint menu

For some reason i recall that i wanted to set the text, then display it separately. Maybe this was just implementation convenience, but perhaps it would make sense to allow popup_create which does all of what popup_show does, but doesn't actually show it ? This might be YAGNI, though.

@bstaletic

This comment has been minimized.

Copy link

commented May 13, 2019

@puremourning

For some reason i recall that i wanted to set the text, then display it separately.

I guess that was your idea at some point, but you also scrapped it and didn't end up using it in the actual code for the RFC mentioned, because you also had:

hint_pum_show( {arg} ) = set the contents and show the hint menu

Your branch didn't use hint_pum_set() at all by the time I took over the branch to rebase it onto, at the time, latest changes to the upstream vim.

@puremourning

This comment has been minimized.

Copy link

commented May 13, 2019

OK then, carry on... :)

@tpimh

This comment has been minimized.

Copy link

commented May 15, 2019

Your paperclip design is awesome!

@brammool

This comment has been minimized.

Copy link
Contributor

commented May 15, 2019

I was wondering about mouse clicks. We can probably do this:

A mouse click arrives as . The coordinates are in
v:mouse_popup_col and v:mouse_popup_row. The top-left screen cell of the
popup is col 1, row 1 (not counting the border).

@brammool

This comment has been minimized.

Copy link
Contributor

commented May 18, 2019

See a discussion about using a buffer instead of a List of lines: https://groups.google.com/forum/#!topic/vim_dev/6RXkRnhpMNM

@epheien

This comment has been minimized.

Copy link

commented May 20, 2019

I think, it's better to merge floating window feature from neovim.
Floating window is more generic than popup.

@Yggdroot

This comment has been minimized.

Copy link

commented May 31, 2019

It also mentions "reconfigure a normal window into a float". Where > is explained how that happens? Looks like this actually isn't > supported. This is supported and done by nvim_win_set_config function. It detaches the window from the split layout and displays it as a float.

Aha. Can it also be put back?

Apparently the window is truncated. Depends what "truncated" means. The floating window will be reduced in size to fit the size of the screen, and remain fully functional (unless the screen is really small, so normal windows doesn't render properly anyway).

That's what I meant with truncated. "sized to fit" would be a better description, no text it lost or hidden.

Strangely there is no priority argument, thus when two windows > overlap one can't control which one goes on top. Yeah this is something we need to add. There is an ordering mechanism internally, but it is not exposed by the API. Currently order can only be changed by focusing the window. > Apparently the floating window behaves like a normal window, one can > put focus in it and edit the text. That's not really what we want > with a popup. It will trigger creeping featurism to add a window > manager... The rationale for us was rather to reduce the need for special API:s for float features. Text and highlighting can be set with the same mechanisms as for an ordinary window (including preview windows). Existing plugin code that uses a split window to display info (or show a menu, fuzzy search prompt etc) can be extended to support floating windows. Also, it is possible to disable focus support on a per-float basis. I understand if you prefer a different design though.

If it's possible to put focus in a floating window this triggers all kinds of questions, and probably many commands will need to behave differently. E.g., what does "split" do? If it actually splits inside the floating window it's like the floating window is a tabpage. What does CTRL-W CTRL-W do? It's easy to think of more. Not putting focus in the floating/popup window makes it easier (but there will still be lots of things to check, e.g. how options are used).
It's clear that many plugin authors need popup windows, but focusing them is not needed, and even undesired. So that's what I want to build (at least now).
A small group of users would like floating windows for editing, toplevel GUI windows, non-tiling window layout, etc. I think this is what Neovim is intending to provide, and have popup windows as a side effect.

@brammool
Focusable popup window is much desirable for me. I want to extend my fuzzy finder plugin LeaderF to support popup window, but it seems impossible without focusing, because LeaderF has two modes, when it is in normal mode, it should work as in normal window/buffer. However I can easily extend LeaderF to support float window on neovim. The scope of usage is too narrow for popup window if it can only be used to display something.
So can an option be added to specify whether to support focusing?

@chrisbra

This comment has been minimized.

Copy link
Member

commented May 31, 2019

but it seems impossible without focusing

please explain why you need focusing.

@Yggdroot

This comment has been minimized.

Copy link

commented May 31, 2019

but it seems impossible without focusing

please explain why you need focusing.

If you have a try my plugin LeaderF, you will know why. It has a normal mode, for example, if you run command: Leaderf function, then press <tab>, it will switch to normal mode, you can use j, k to navigate the result, and even you can copy something from the result using the copy command.
If I convert the result window to a popup window, how to get in the normal mode as I described above?

@bfredl

This comment has been minimized.

Copy link

commented May 31, 2019

Focusing popup seems already possible, though only with a hack:

split
let wid = popup_create("text", {'minwidth': 15, 'minheight': 5})
call win_execute(wid, winnr()."close")

Editing and redrawing works just fine, but wincmds do not (need win_gotoid() to leave the popup window).

@Yggdroot

This comment has been minimized.

Copy link

commented May 31, 2019

Focusing popup seems already possible, though only with a hack:

split
let wid = popup_create("text", {'minwidth': 15, 'minheight': 5})
call win_execute(wid, winnr()."close")

Editing and redrawing works just fine, but wincmds do not (need win_gotoid() to leave the popup window).

The hack has some issues, for example, :qa can not quit from vim. If the focus leave the popup window, it can not get back again.

It is best that vim can support focusing.

@bfredl

This comment has been minimized.

Copy link

commented May 31, 2019

Indeed, the point was to show that the design is compatible with focusing, but the wincmd issues would need to be fixed for real usage.

Similarly displaying and interacting with a terminal buffer in a popup window also sort-of works (like neovim float), but there are some drawing glitches and crashes. I think because some of the code checks for "popup" buffer type instead of popup window, which creates issues with special buffer types. I've seen it used to use a helper program to display a menu, like fzf fuzzy finder.

@puremourning

This comment has been minimized.

Copy link

commented May 31, 2019

I worry that focussable pop ups is the tip of a scope creep iceberg. There were very specific use cases in the original request/rfc and in the plugin author survey.

Overlaying entire windows with complex interactions seems beyond that scope IMO.

E.g. for such things, what makes pop ups better than say splits?

@brammool

This comment has been minimized.

Copy link
Contributor

commented May 31, 2019

@brammool

This comment has been minimized.

Copy link
Contributor

commented May 31, 2019

@brammool

This comment has been minimized.

Copy link
Contributor

commented May 31, 2019

@brammool

This comment has been minimized.

Copy link
Contributor

commented May 31, 2019

@Yggdroot

This comment has been minimized.

Copy link

commented May 31, 2019

Anything you cannot do with a popup window filter? It's not implemented yet, but you can read the docs for the planned functionality: :help popup-filter

I have already read all the docs about popup window, and have tried writing some little tests. I think I can understand what popup-filter will do. Unfortunately it can't meet my requirement. It can only work like fzf, which has only one mode.

Anyway, thanks for your great work.

@brammool

This comment has been minimized.

Copy link
Contributor

commented May 31, 2019

@bfredl

This comment has been minimized.

Copy link

commented May 31, 2019

@brammool I think there still will be issues from that a lot of "interactive" features can be activated indirectly via commands, like :normal , feedkeys(), :wincmd and so on. A quick example, call win_execute(popup, "wincmd s") generates weird behavior.

@brammool

This comment has been minimized.

Copy link
Contributor

commented May 31, 2019

@myitcv

This comment has been minimized.

Copy link

commented Jun 6, 2019

@puremourning - how are you finding popup windows for providing signature help?

I'm toying with the idea of adding support to https://github.com/myitcv/govim; wondering whether the ideas you were exploring in https://github.com/puremourning/YouCompleteMe/tree/signature-help are still current?

Thanks

@mattn

This comment has been minimized.

Copy link

commented Jun 6, 2019

@Yggdroot you can implement with filter. This is confirm dialog when open new buffer.

https://github.com/mattn/vimgon-quest-menu

@puremourning

This comment has been minimized.

Copy link

commented Jun 6, 2019

@puremourning - how are you finding popup windows for providing signature help?

It's brilliant. I'm already using it every day pretty much.

I susepect that now ycmd uses gopls it would just work with go. I haven't tried it though.

@myitcv

This comment has been minimized.

Copy link

commented Jun 6, 2019

Great to hear. It looks like your changes haven't been merged yet, so I assume you're still working off that branch?

@puremourning

This comment has been minimized.

Copy link

commented Jun 6, 2019

Yes I am dogfooding.

@puremourning

This comment has been minimized.

Copy link

commented Jun 8, 2019

@myitcv as mentioned, YCM just works with gopls (though gopls seems to have a bug identifying the current argument):

YCM-sig-help-go

@myitcv

This comment has been minimized.

Copy link

commented Jun 8, 2019

@puremourning - that looks fantastic!

@blayz3r blayz3r referenced this issue Jun 8, 2019

Open

Floating Windows Support VIM Popup #821

3 of 9 tasks complete
@oblitum

This comment has been minimized.

Copy link

commented Jun 9, 2019

@puremourning: though gopls seems to have a bug identifying the current argument

This issue doesn't happen when snippet is expanded, which just adds an empty () but leaves the cursor inside. The server doesn't seem prepared to handle signatureHelp with an open parenthesis without a closing one following.

There was a previous relevant issue on saibing/bingo#20, it was fixed, but project development stopped in favor of gopls.

@Yggdroot

This comment has been minimized.

Copy link

commented Jun 11, 2019

@Yggdroot you can implement with filter. This is confirm dialog when open new buffer.

https://github.com/mattn/vimgon-quest-menu

I know, this is easy to implement, however, what I need is a window that can get focusing just like neovim's floating window.

@mattn

This comment has been minimized.

Copy link

commented Jun 11, 2019

@Yggdroot If using filter, you can make prompt in the popup window.

@skywind3000

This comment has been minimized.

Copy link
Author

commented Jun 11, 2019

someone would write a readline library for popups?

@prabirshrestha

This comment has been minimized.

Copy link

commented Jun 15, 2019

Here is another example where we needed to focus popup in order to be able to scroll.

prabirshrestha/vim-lsp#395
image
It is using :LspHover to get information of the symbol under the cursor. But the language server provides quite a lot of doc so impossible for me to read the entire docs. There could also be cases where I would actually like to even copy some of the sample docs in popup and paste it in my file.

Let me know if we want to create a new issue for this.

@skywind3000

This comment has been minimized.

Copy link
Author

commented Jun 15, 2019

@prabirshrestha , Scroll your content by

call win_execute("normal \<c-e>")
call win_execute("normal \<c-y>")

?? and bind them to <m-e> and <m-y>, you will be able to scroll your popup window without focsing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.