Timoses / vim-venu

Simple menu for vim
20 stars 2 forks source link

Integration with vim native menus #2

Closed ozars closed 5 years ago

ozars commented 5 years ago

vim has a native menu (:menu) feature which supports even submenus. Is there a simple way for instructing this plugin to print commands from a particular native menu?

My use case: I started using an old plugin po.vim which happens to install some menu entries. I don't really want to map each menu entry to some key and there seems to be no other way for conveniently invoking menu entries (without using gvim ofc). Instead I will probably modify my fork of po.vim to depend on vim-venu by filling its entries manually, however it would be much better if vim-venu supports navigating native menu entries out-of-box.

Thanks for this great plugin!

Timoses commented 5 years ago

Glad you see venu as being useful : ).

So, what you propose is "importing" a vim :menu into venu? That would mean venu should accept the following format: https://github.com/ozars/po.vim/blob/master/plugin/po.vim#L338-L348 ?

AFAIK, vim menu seems to be only useful in GUI mode?

I can think of two approaches to import such menus: a) Read the literal text of "amenu menuName.subMenuName command" and translate that into a venu b) or somehow access the defined ":menu"'s data object and traverse it while simultaneously translating it into venu's data structure.

I'm not great in vimscript. b) sounds like it could be the easier option as the menu would be evaluated already by vim. However, I don't know how to access the "processed" menu in vimscript (":amenu" just prints the menu entries... but how would one actually access the data object behind the menu?). a) sounds like a bit of work.

I agree that it would be great to automatically "import" these menus into venu.

ozars commented 5 years ago

AFAIK, vim menu seems to be only useful in GUI mode?

Indeed, that's the current situation. In terminal mode it's not so useful, unless menus can be navigated with a plugin like this. Personally, I would love to use menus for key bindings or commands that I infrequently use. It may even be useful for looking up associated mapping for a custom command instead of delving into vimrc.

b) or somehow access the defined ":menu"'s data object and traverse it while simultaneously translating it into venu's data structure.

I actually started parsing menu output after opening this issue. :) This is a naughty hack which might be broken if the output format changes in a newer vim version, but it does the job for now.

However, I don't know how to access the "processed" menu in vimscript (":amenu" just prints the menu entries... but how would one actually access the data object behind the menu?).

I dig into this already but unfortunately I couldn't find any interface to fetch underlying menu objects, other than parsing string outputs of *menu commands. Moreover, there isn't any associated events fired if native menu is modified either (there is MenuPopup event which isn't useful for our purposes). For the short-term above hack works, but for a long-term solution vim may be modified to have a native function/command to list (sub)menus (e.g. menuls) and some native events fired when menu is modified.

I don't know if I can implement the latter, but I plan to implement the former. Shall I submit a PR in case I implement it?

Timoses commented 5 years ago

I added an issue for the priority part of :*menu as I think that's a nice feature https://github.com/Timoses/vim-venu/issues/3 .

b) or somehow access the defined ":menu"'s data object and traverse it while simultaneously translating it into venu's data structure.

I actually started parsing menu output after opening this issue. :) This is a naughty hack which might > be broken if the output format changes in a newer vim version, but it does the job for now.

Perhaps it would also be possible to define a new command like :venu-addItem which could replace a line like

amenu &PO-Editing.&Remove\ quotes :call <SID>Unstringify()<CR>

into something like

venu-entry &PO-Editing.&Remove\ quotes :call <SID>Unstringify()<CR>

The function implementing that command could then venu#create the menu "PO-Editing", call venu#addItem(<"PO-Editing"-menuHandle>, "Remove", <call unstringify>) and venu#register(<"PO-Editing"-menuHandle>). Perhaps it would be even better if there was a function venu#get(<menuName>) to retrieve the menu, simply append an item (with venu#addItem on the retrieved menu handle) and only venu#create and venu#register when the menu did not exist already (venu#get returned null).

Of course this would mean that one would have to rewrite :*menu into :venu-addItem everywhere. Or, is it possible to "overwrite" the command :*menu? That would be an easy entry in .vimrc for example (command! amenu call venu#entry(), where I'm not so sure about the nomenclature of entry yet).

ozars commented 5 years ago

Of course this would mean that one would have to rewrite :*menu into :venu-addItem everywhere. Or, is it possible to "overwrite" the command :*menu? That would be an easy entry in .vimrc for example (command! amenu call venu#entry(), where I'm not so sure about the nomenclature of entry yet).

Overwriting vim's commands seems not to be possible using command. There is a workaround specified, by using cabbrev. I made a simple experiment by placing these lines in my vimrc:

cabbrev amenu let g:amenu_replaced = 1 \| "
amenu

Unfortunately, it didn't wrap amenu. It replaces if user types :amenu in normal mode, but this is not useful for us.

How about adding a utility function venu#native#import which will import native commands, possibly after filtering with an expression given by user? There can be a User event for MenuUpdate, which is expected to be called by other plugin developers after modifying menu entries. On User MenuUpdate, Venu reimports native menu entries (it's hard to detect exact changes on menu entries). This can be done lazily before printing. When the event is fired a flag is set so that entries are reimported just before printing if the flag was set. This requires keeping native menu entries on a different variable than those created by user by calling venu#register. These two dictionaries can be merged before printing.

I submitted #5. So far the implementation of parsing menu output is done. I can proceed after we decide on how to implement importing. Please let me know your feedback (on submitted PR and importing) if you think something could be done better.

Timoses commented 5 years ago

I really like the idea of an importer. Would there be any more use cases for importing a menu from somewhere? Perhaps that would be an incentive to make it quite generic. Then again, I'm not sure how generic vim code can really be.

I'll have to find some time to think about it. Probably not before next weekend though.

ozars commented 5 years ago

Okay. I can implement import meanwhile. I won't implement MenuUpdate I mentioned above, instead we can rely on importing and merging every time before printing. In return other plugins won't need to depend on this plugin or a custom user event and they can just manipulate native menus. If this somehow results in some performance penalty while printing, then merging only after menu is updated can be considered.

Would there be any more use cases for importing a menu from somewhere? Perhaps that would be an incentive to make it quite generic.

FWIW, I can list a few differences between (vim's) menu and (Venu's) venu from what I understood so far.

There can be buffer specific menus :*menu <buffer>, whereas venus (as in plural of venu :-) ) handle this by filtering wrt _filetype field of global venu list s:menus. Introducing configurable buffer-specific b:venu_list and global g:venu_list variables or adding a buffer_only argument to venu#register can mimic same behavior. Those two lists can be merged while merging native menus and sorting them all. Not so necessary though (at least for me). ps. I just saw examples on your prio patch.

Menus are mode-aware. I just learned and liked the idea behind :amenu. It prepends and appends necessary characters to menu command to escape from insert mode and returning back to insert mode if the menu item is invoked while in insert mode. I don't know why there isn't an equivalent of this for mapping such as :amap though. I have a few key bindings where I had to create imap variation manually. (Well... That's a good reason for me to write a simple Amap.vim plugin :-) )

Menus support shortcuts in a classic way: by putting an ampersand (&) before the shortcut letter in the name of menu entry. Venus uses enumeration for shortcuts. Having both of them would be best of two worlds, but not so crucial again.

One nice to have feature would be having right-aligned descriptions of a menu entry. Menus support it by setting the portion of name after as description. It is useful for describing mapping for the menu entry. Example from NERDComment:

Toggle                    \c<space>

There is also priority feature which seems like you brought to venu, which is sweet!

Ability to use menus through Venu and sorting them wrt their priorities seems enough to me for my purposes. I listed those in case you'd like to modify venu in any of these directions.

ozars commented 5 years ago

Closed by #5.