Merge pull request #19170 from zeertzjq/vim-8.0.1558

vim-patch:8.0.{1558,1570,1574,1588},8.1.{0487,0695,1274}: menu features
This commit is contained in:
zeertzjq
2022-07-01 10:49:14 +08:00
committed by GitHub
22 changed files with 809 additions and 101 deletions

View File

@@ -5,6 +5,7 @@
" Last Change: 2019 Dec 10 " Last Change: 2019 Dec 10
aunmenu * aunmenu *
tlunmenu *
unlet! g:did_install_default_menus unlet! g:did_install_default_menus
unlet! g:did_install_syntax_menu unlet! g:did_install_syntax_menu

View File

@@ -727,13 +727,14 @@ MenuPopup Just before showing the popup menu (under the
right mouse button). Useful for adjusting the right mouse button). Useful for adjusting the
menu for what is under the cursor or mouse menu for what is under the cursor or mouse
pointer. pointer.
The pattern is matched against a single The pattern is matched against one or two
character representing the mode: characters representing the mode:
n Normal n Normal
v Visual v Visual
o Operator-pending o Operator-pending
i Insert i Insert
c Command line c Command line
tl Terminal
*ModeChanged* *ModeChanged*
ModeChanged After changing the mode. The pattern is ModeChanged After changing the mode. The pattern is
matched against `'old_mode:new_mode'`, for matched against `'old_mode:new_mode'`, for

View File

@@ -195,6 +195,10 @@ the mouse button down on this will pop up a menu containing the item
"Big Changes", which is a sub-menu containing the item "Delete All Spaces", "Big Changes", which is a sub-menu containing the item "Delete All Spaces",
which when selected, performs the operation. which when selected, performs the operation.
To create a menu for terminal mode, use |:tlmenu| instead of |:tmenu| unlike
key mapping (|:tmap|). This is because |:tmenu| is already used for defining
tooltips for menus. See |terminal-input|.
Special characters in a menu name: Special characters in a menu name:
& The next character is the shortcut key. Make sure each & The next character is the shortcut key. Make sure each
@@ -214,9 +218,9 @@ this menu can be used. The second part is shown as "Open :e". The ":e"
is right aligned, and the "O" is underlined, to indicate it is the shortcut. is right aligned, and the "O" is underlined, to indicate it is the shortcut.
*:am* *:amenu* *:an* *:anoremenu* *:am* *:amenu* *:an* *:anoremenu*
The ":amenu" command can be used to define menu entries for all modes at once. The ":amenu" command can be used to define menu entries for all modes at once,
To make the command work correctly, a character is automatically inserted for expect for Terminal mode. To make the command work correctly, a character is
some modes: automatically inserted for some modes:
mode inserted appended ~ mode inserted appended ~
Normal nothing nothing Normal nothing nothing
Visual <C-C> <C-\><C-G> Visual <C-C> <C-\><C-G>
@@ -469,6 +473,16 @@ Executing Menus *execute-menus*
insert-mode menu Eg: > insert-mode menu Eg: >
:emenu File.Exit :emenu File.Exit
:[range]em[enu] {mode} {menu} Like above, but execute the menu for {mode}:
'n': |:nmenu| Normal mode
'v': |:vmenu| Visual mode
's': |:smenu| Select mode
'o': |:omenu| Operator-pending mode
't': |:tlmenu| Terminal mode
'i': |:imenu| Insert mode
'c': |:cmenu| Cmdline mode
You can use :emenu to access useful menu items you may have got used to from You can use :emenu to access useful menu items you may have got used to from
GUI mode. See 'wildmenu' for an option that works well with this. See GUI mode. See 'wildmenu' for an option that works well with this. See
|console-menus| for an example. |console-menus| for an example.
@@ -494,7 +508,9 @@ may be used to complete the name of the menu item for the appropriate mode.
To remove all menus use: *:unmenu-all* > To remove all menus use: *:unmenu-all* >
:unmenu * " remove all menus in Normal and visual mode :unmenu * " remove all menus in Normal and visual mode
:unmenu! * " remove all menus in Insert and Command-line mode :unmenu! * " remove all menus in Insert and Command-line mode
:aunmenu * " remove all menus in all modes :aunmenu * " remove all menus in all modes, except for Terminal
" mode
:tlunmenu * " remove all menus in Terminal mode
If you want to get rid of the menu bar: > If you want to get rid of the menu bar: >
:set guioptions-=m :set guioptions-=m
@@ -547,6 +563,8 @@ See section |42.4| in the user manual.
:tu[nmenu] {menupath} Remove a tip for a menu or tool. :tu[nmenu] {menupath} Remove a tip for a menu or tool.
{only in X11 and Win32 GUI} {only in X11 and Win32 GUI}
Note: To create menus for terminal mode, use |:tlmenu| instead.
When a tip is defined for a menu item, it appears in the command-line area When a tip is defined for a menu item, it appears in the command-line area
when the mouse is over that item, much like a standard Windows menu hint in when the mouse is over that item, much like a standard Windows menu hint in
the status bar. (Except when Vim is in Command-line mode, when of course the status bar. (Except when Vim is in Command-line mode, when of course
@@ -577,8 +595,8 @@ a menu item - you don't need to do a :tunmenu as well.
5.9 Popup Menus 5.9 Popup Menus
In the Win32 GUI, you can cause a menu to popup at the cursor. This behaves You can cause a menu to popup at the cursor. This behaves similarly to the
similarly to the PopUp menus except that any menu tree can be popped up. PopUp menus except that any menu tree can be popped up.
This command is for backwards compatibility, using it is discouraged, because This command is for backwards compatibility, using it is discouraged, because
it behaves in a strange way. it behaves in a strange way.
@@ -587,7 +605,6 @@ it behaves in a strange way.
:popu[p] {name} Popup the menu {name}. The menu named must :popu[p] {name} Popup the menu {name}. The menu named must
have at least one subentry, but need not have at least one subentry, but need not
appear on the menu-bar (see |hidden-menus|). appear on the menu-bar (see |hidden-menus|).
{only available for Win32 GUI}
:popu[p]! {name} Like above, but use the position of the mouse :popu[p]! {name} Like above, but use the position of the mouse
pointer instead of the cursor. pointer instead of the cursor.

View File

@@ -1622,17 +1622,20 @@ tag command action ~
|:tjump| :tj[ump] like ":tselect", but jump directly when there |:tjump| :tj[ump] like ":tselect", but jump directly when there
is only one match is only one match
|:tlast| :tl[ast] jump to last matching tag |:tlast| :tl[ast] jump to last matching tag
|:tmapclear| :tmapc[lear] remove all mappings for Terminal-Job mode |:tlmenu| :tlm[enu] add menu for |Terminal-mode|
|:tmap| :tma[p] like ":map" but for Terminal-Job mode |:tlnoremenu| :tln[oremenu] like ":noremenu" but for |Terminal-mode|
|:tlunmenu| :tlu[nmenu] remove menu for |Terminal-mode|
|:tmapclear| :tmapc[lear] remove all mappings for |Terminal-mode|
|:tmap| :tma[p] like ":map" but for |Terminal-mode|
|:tmenu| :tm[enu] define menu tooltip |:tmenu| :tm[enu] define menu tooltip
|:tnext| :tn[ext] jump to next matching tag |:tnext| :tn[ext] jump to next matching tag
|:tnoremap| :tno[remap] like ":noremap" but for Terminal-Job mode |:tnoremap| :tno[remap] like ":noremap" but for |Terminal-mode|
|:topleft| :to[pleft] make split window appear at top or far left |:topleft| :to[pleft] make split window appear at top or far left
|:tprevious| :tp[revious] jump to previous matching tag |:tprevious| :tp[revious] jump to previous matching tag
|:trewind| :tr[ewind] jump to first matching tag |:trewind| :tr[ewind] jump to first matching tag
|:try| :try execute commands, abort on error or exception |:try| :try execute commands, abort on error or exception
|:tselect| :ts[elect] list matching tags and select one |:tselect| :ts[elect] list matching tags and select one
|:tunmap| :tunma[p] like ":unmap" but for Terminal-Job mode |:tunmap| :tunma[p] like ":unmap" but for |Terminal-mode|
|:tunmenu| :tu[nmenu] remove menu tooltip |:tunmenu| :tu[nmenu] remove menu tooltip
|:undo| :u[ndo] undo last change(s) |:undo| :u[ndo] undo last change(s)
|:undojoin| :undoj[oin] join next change with previous undo block |:undojoin| :undoj[oin] join next change with previous undo block

View File

@@ -80,6 +80,9 @@ To use `ALT+{h,j,k,l}` to navigate windows from any mode: >
:nnoremap <A-k> <C-w>k :nnoremap <A-k> <C-w>k
:nnoremap <A-l> <C-w>l :nnoremap <A-l> <C-w>l
You can also create menus similar to terminal mode mappings, but you have to
use |:tlmenu| instead of |:tmenu|.
Mouse input has the following behavior: Mouse input has the following behavior:
- If the program has enabled mouse events, the corresponding events will be - If the program has enabled mouse events, the corresponding events will be

View File

@@ -150,7 +150,8 @@ like the variations on the ":map" command:
:menu! Insert and Command-line mode :menu! Insert and Command-line mode
:imenu Insert mode :imenu Insert mode
:cmenu Command-line mode :cmenu Command-line mode
:amenu All modes :tlmenu Terminal mode
:amenu All modes (except for Terminal mode)
To avoid that the commands of a menu item are being mapped, use the command To avoid that the commands of a menu item are being mapped, use the command
":noremenu", ":nnoremenu", ":anoremenu", etc. ":noremenu", ":nnoremenu", ":anoremenu", etc.

View File

@@ -164,6 +164,9 @@ if exists(':tlmenu')
endif endif
nnoremenu 20.360 &Edit.&Paste<Tab>"+gP "+gP nnoremenu 20.360 &Edit.&Paste<Tab>"+gP "+gP
cnoremenu &Edit.&Paste<Tab>"+gP <C-R>+ cnoremenu &Edit.&Paste<Tab>"+gP <C-R>+
if exists(':tlmenu')
tlnoremenu &Edit.&Paste<Tab>"+gP <C-W>"+
endif
exe 'vnoremenu <script> &Edit.&Paste<Tab>"+gP ' . paste#paste_cmd['v'] exe 'vnoremenu <script> &Edit.&Paste<Tab>"+gP ' . paste#paste_cmd['v']
exe 'inoremenu <script> &Edit.&Paste<Tab>"+gP ' . paste#paste_cmd['i'] exe 'inoremenu <script> &Edit.&Paste<Tab>"+gP ' . paste#paste_cmd['i']
nnoremenu 20.370 &Edit.Put\ &Before<Tab>[p [p nnoremenu 20.370 &Edit.Put\ &Before<Tab>[p [p

View File

@@ -411,7 +411,7 @@ syn case match
" Menus: {{{2 " Menus: {{{2
" ===== " =====
syn cluster vimMenuList contains=vimMenuBang,vimMenuPriority,vimMenuName,vimMenuMod syn cluster vimMenuList contains=vimMenuBang,vimMenuPriority,vimMenuName,vimMenuMod
syn keyword vimCommand am[enu] an[oremenu] aun[menu] cme[nu] cnoreme[nu] cunme[nu] ime[nu] inoreme[nu] iunme[nu] me[nu] nme[nu] nnoreme[nu] noreme[nu] nunme[nu] ome[nu] onoreme[nu] ounme[nu] unme[nu] vme[nu] vnoreme[nu] vunme[nu] skipwhite nextgroup=@vimMenuList syn keyword vimCommand am[enu] an[oremenu] aun[menu] cme[nu] cnoreme[nu] cunme[nu] ime[nu] inoreme[nu] iunme[nu] me[nu] nme[nu] nnoreme[nu] noreme[nu] nunme[nu] ome[nu] onoreme[nu] ounme[nu] tlm[enu] tln[oremenu] tlu[nmenu] unme[nu] vme[nu] vnoreme[nu] vunme[nu] skipwhite nextgroup=@vimMenuList
syn match vimMenuName "[^ \t\\<]\+" contained nextgroup=vimMenuNameMore,vimMenuMap syn match vimMenuName "[^ \t\\<]\+" contained nextgroup=vimMenuNameMore,vimMenuMap
syn match vimMenuPriority "\d\+\(\.\d\+\)*" contained skipwhite nextgroup=vimMenuName syn match vimMenuPriority "\d\+\(\.\d\+\)*" contained skipwhite nextgroup=vimMenuName
syn match vimMenuNameMore "\c\\\s\|<tab>\|\\\." contained nextgroup=vimMenuName,vimMenuNameMore contains=vimNotation syn match vimMenuNameMore "\c\\\s\|<tab>\|\\\." contained nextgroup=vimMenuName,vimMenuNameMore contains=vimNotation

View File

@@ -1144,8 +1144,9 @@ enum {
MENU_INDEX_OP_PENDING = 3, MENU_INDEX_OP_PENDING = 3,
MENU_INDEX_INSERT = 4, MENU_INDEX_INSERT = 4,
MENU_INDEX_CMDLINE = 5, MENU_INDEX_CMDLINE = 5,
MENU_INDEX_TIP = 6, MENU_INDEX_TERMINAL = 6,
MENU_MODES = 7, MENU_INDEX_TIP = 7,
MENU_MODES = 8,
}; };
typedef struct VimMenu vimmenu_T; typedef struct VimMenu vimmenu_T;

View File

@@ -191,7 +191,7 @@ module.cmds = {
}, },
{ {
command='behave', command='behave',
flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN), flags=bit.bor(BANG, NEEDARG, WORD1, TRLBAR, CMDWIN),
addr_type='ADDR_NONE', addr_type='ADDR_NONE',
func='ex_behave', func='ex_behave',
}, },
@@ -1991,7 +1991,7 @@ module.cmds = {
command='popup', command='popup',
flags=bit.bor(NEEDARG, EXTRA, BANG, TRLBAR, NOTRLCOM, CMDWIN), flags=bit.bor(NEEDARG, EXTRA, BANG, TRLBAR, NOTRLCOM, CMDWIN),
addr_type='ADDR_NONE', addr_type='ADDR_NONE',
func='ex_ni', func='ex_popup',
}, },
{ {
command='ppop', command='ppop',
@@ -2878,6 +2878,24 @@ module.cmds = {
addr_type='ADDR_NONE', addr_type='ADDR_NONE',
func='ex_tag', func='ex_tag',
}, },
{
command='tlmenu',
flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='tlnoremenu',
flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{
command='tlunmenu',
flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),
addr_type='ADDR_OTHER',
func='ex_menu',
},
{ {
command='tmenu', command='tmenu',
flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN), flags=bit.bor(RANGE, ZEROR, EXTRA, TRLBAR, NOTRLCOM, CTRLV, CMDWIN),

View File

@@ -62,6 +62,7 @@
#include "nvim/os/time.h" #include "nvim/os/time.h"
#include "nvim/os_unix.h" #include "nvim/os_unix.h"
#include "nvim/path.h" #include "nvim/path.h"
#include "nvim/popupmnu.h"
#include "nvim/quickfix.h" #include "nvim/quickfix.h"
#include "nvim/regexp.h" #include "nvim/regexp.h"
#include "nvim/screen.h" #include "nvim/screen.h"
@@ -4059,6 +4060,9 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
case CMD_cmenu: case CMD_cmenu:
case CMD_cnoremenu: case CMD_cnoremenu:
case CMD_cunmenu: case CMD_cunmenu:
case CMD_tlmenu:
case CMD_tlnoremenu:
case CMD_tlunmenu:
case CMD_tmenu: case CMD_tmenu:
case CMD_tunmenu: case CMD_tunmenu:
case CMD_popup: case CMD_popup:
@@ -7985,6 +7989,11 @@ static void ex_nogui(exarg_T *eap)
eap->errmsg = N_("E25: Nvim does not have a built-in GUI"); eap->errmsg = N_("E25: Nvim does not have a built-in GUI");
} }
static void ex_popup(exarg_T *eap)
{
pum_make_popup(eap->arg, eap->forceit);
}
static void ex_swapname(exarg_T *eap) static void ex_swapname(exarg_T *eap)
{ {
if (curbuf->b_ml.ml_mfp == NULL || curbuf->b_ml.ml_mfp->mf_fname == NULL) { if (curbuf->b_ml.ml_mfp == NULL || curbuf->b_ml.ml_mfp->mf_fname == NULL) {

View File

@@ -986,6 +986,7 @@ EXTERN char e_notset[] INIT(= N_("E764: Option '%s' is not set"));
EXTERN char e_invalidreg[] INIT(= N_("E850: Invalid register name")); EXTERN char e_invalidreg[] INIT(= N_("E850: Invalid register name"));
EXTERN char e_dirnotf[] INIT(= N_("E919: Directory not found in '%s': \"%s\"")); EXTERN char e_dirnotf[] INIT(= N_("E919: Directory not found in '%s': \"%s\""));
EXTERN char e_au_recursive[] INIT(= N_("E952: Autocommand caused recursive behavior")); EXTERN char e_au_recursive[] INIT(= N_("E952: Autocommand caused recursive behavior"));
EXTERN char e_menuothermode[] INIT(= N_("E328: Menu only exists in another mode"));
EXTERN char e_autocmd_close[] INIT(= N_("E813: Cannot close autocmd window")); EXTERN char e_autocmd_close[] INIT(= N_("E813: Cannot close autocmd window"));
EXTERN char e_listarg[] INIT(= N_("E686: Argument of %s must be a List")); EXTERN char e_listarg[] INIT(= N_("E686: Argument of %s must be a List"));
EXTERN char e_unsupportedoption[] INIT(= N_("E519: Option not supported")); EXTERN char e_unsupportedoption[] INIT(= N_("E519: Option not supported"));

View File

@@ -11,6 +11,7 @@
#include <string.h> #include <string.h>
#include "nvim/ascii.h" #include "nvim/ascii.h"
#include "nvim/autocmd.h"
#include "nvim/charset.h" #include "nvim/charset.h"
#include "nvim/cursor.h" #include "nvim/cursor.h"
#include "nvim/eval.h" #include "nvim/eval.h"
@@ -22,6 +23,7 @@
#include "nvim/memory.h" #include "nvim/memory.h"
#include "nvim/menu.h" #include "nvim/menu.h"
#include "nvim/message.h" #include "nvim/message.h"
#include "nvim/popupmnu.h"
#include "nvim/screen.h" #include "nvim/screen.h"
#include "nvim/state.h" #include "nvim/state.h"
#include "nvim/strings.h" #include "nvim/strings.h"
@@ -36,10 +38,9 @@
#endif #endif
/// The character for each menu mode /// The character for each menu mode
static char menu_mode_chars[] = { 'n', 'v', 's', 'o', 'i', 'c', 't' }; static char *menu_mode_chars[] = { "n", "v", "s", "o", "i", "c", "tl", "t" };
static char e_notsubmenu[] = N_("E327: Part of menu-item path is not sub-menu"); static char e_notsubmenu[] = N_("E327: Part of menu-item path is not sub-menu");
static char e_othermode[] = N_("E328: Menu only exists in another mode");
static char e_nomenu[] = N_("E329: No menu \"%s\""); static char e_nomenu[] = N_("E329: No menu \"%s\"");
// Return true if "name" is a window toolbar menu name. // Return true if "name" is a window toolbar menu name.
@@ -571,7 +572,7 @@ static int remove_menu(vimmenu_T **menup, char *name, int modes, bool silent)
} }
} else if (*name != NUL) { } else if (*name != NUL) {
if (!silent) { if (!silent) {
emsg(_(e_othermode)); emsg(_(e_menuothermode));
} }
return FAIL; return FAIL;
} }
@@ -723,7 +724,7 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
(menu->noremap[bit] & REMAP_NONE) ? 1 : 0); (menu->noremap[bit] & REMAP_NONE) ? 1 : 0);
tv_dict_add_nr(impl, S_LEN("sid"), tv_dict_add_nr(impl, S_LEN("sid"),
(menu->noremap[bit] & REMAP_SCRIPT) ? 1 : 0); (menu->noremap[bit] & REMAP_SCRIPT) ? 1 : 0);
tv_dict_add_dict(commands, &menu_mode_chars[bit], 1, impl); tv_dict_add_dict(commands, menu_mode_chars[bit], 1, impl);
} }
} }
} else { } else {
@@ -785,7 +786,7 @@ static vimmenu_T *find_menu(vimmenu_T *menu, char *name, int modes)
emsg(_(e_notsubmenu)); emsg(_(e_notsubmenu));
return NULL; return NULL;
} else if ((menu->modes & modes) == 0x0) { } else if ((menu->modes & modes) == 0x0) {
emsg(_(e_othermode)); emsg(_(e_menuothermode));
return NULL; return NULL;
} else if (*p == NUL) { // found a full match } else if (*p == NUL) { // found a full match
return menu; return menu;
@@ -859,7 +860,7 @@ static void show_menus_recursive(vimmenu_T *menu, int modes, int depth)
for (i = 0; i < depth + 2; i++) { for (i = 0; i < depth + 2; i++) {
msg_puts(" "); msg_puts(" ");
} }
msg_putchar(menu_mode_chars[bit]); msg_puts(menu_mode_chars[bit]);
if (menu->noremap[bit] == REMAP_NONE) { if (menu->noremap[bit] == REMAP_NONE) {
msg_putchar('*'); msg_putchar('*');
} else if (menu->noremap[bit] == REMAP_SCRIPT) { } else if (menu->noremap[bit] == REMAP_SCRIPT) {
@@ -1213,6 +1214,11 @@ int get_menu_cmd_modes(const char *cmd, bool forceit, int *noremap, int *unmenu)
modes = MENU_INSERT_MODE; modes = MENU_INSERT_MODE;
break; break;
case 't': case 't':
if (*cmd == 'l') { // tlmenu, tlunmenu, tlnoremenu
modes = MENU_TERMINAL_MODE;
cmd++;
break;
}
modes = MENU_TIP_MODE; // tmenu modes = MENU_TIP_MODE; // tmenu
break; break;
case 'c': // cmenu case 'c': // cmenu
@@ -1259,9 +1265,13 @@ static char *popup_mode_name(char *name, int idx)
size_t len = STRLEN(name); size_t len = STRLEN(name);
assert(len >= 4); assert(len >= 4);
char *p = xstrnsave(name, len + 1); char *mode_chars = menu_mode_chars[idx];
memmove(p + 6, p + 5, len - 4); size_t mode_chars_len = strlen(mode_chars);
p[5] = menu_mode_chars[idx]; char *p = xstrnsave(name, len + mode_chars_len);
memmove(p + 5 + mode_chars_len, p + 5, len - 4);
for (size_t i = 0; i < mode_chars_len; i++) {
p[5 + i] = menu_mode_chars[idx][i];
}
return p; return p;
} }
@@ -1355,31 +1365,97 @@ static int menu_is_hidden(char *name)
|| (menu_is_popup(name) && name[5] != NUL); || (menu_is_popup(name) && name[5] != NUL);
} }
// Execute "menu". Use by ":emenu" and the window toolbar. static int get_menu_mode(void)
// "eap" is NULL for the window toolbar. {
static void execute_menu(const exarg_T *eap, vimmenu_T *menu) if (State & MODE_TERMINAL) {
return MENU_INDEX_TERMINAL;
}
if (VIsual_active) {
if (VIsual_select) {
return MENU_INDEX_SELECT;
}
return MENU_INDEX_VISUAL;
}
if (State & MODE_INSERT) {
return MENU_INDEX_INSERT;
}
if ((State & MODE_CMDLINE) || State == MODE_ASKMORE || State == MODE_HITRETURN) {
return MENU_INDEX_CMDLINE;
}
if (finish_op) {
return MENU_INDEX_OP_PENDING;
}
if (State & MODE_NORMAL) {
return MENU_INDEX_NORMAL;
}
if (State & MODE_LANGMAP) { // must be a "r" command, like Insert mode
return MENU_INDEX_INSERT;
}
return MENU_INDEX_INVALID;
}
int get_menu_mode_flag(void)
{
int mode = get_menu_mode();
if (mode == MENU_INDEX_INVALID) {
return 0;
}
return 1 << mode;
}
/// Display the Special "PopUp" menu as a pop-up at the current mouse
/// position. The "PopUpn" menu is for Normal mode, "PopUpi" for Insert mode,
/// etc.
void show_popupmenu(void)
{
int menu_mode = get_menu_mode();
if (menu_mode == MENU_INDEX_INVALID) {
return;
}
char *mode = menu_mode_chars[menu_mode];
size_t mode_len = strlen(mode);
apply_autocmds(EVENT_MENUPOPUP, mode, NULL, false, curbuf);
vimmenu_T *menu;
for (menu = root_menu; menu != NULL; menu = menu->next) {
if (STRNCMP("PopUp", menu->name, 5) == 0 && STRNCMP(menu->name + 5, mode, mode_len) == 0) {
break;
}
}
// Only show a popup when it is defined and has entries
if (menu != NULL && menu->children != NULL) {
pum_show_popupmenu(menu);
}
}
/// Execute "menu". Use by ":emenu" and the window toolbar.
/// @param eap NULL for the window toolbar.
/// @param mode_idx specify a MENU_INDEX_ value, use -1 to depend on the current state
void execute_menu(const exarg_T *eap, vimmenu_T *menu, int mode_idx)
FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_NONNULL_ARG(2)
{ {
int idx = -1; int idx = mode_idx;
char *mode;
if (idx < 0) {
// Use the Insert mode entry when returning to Insert mode. // Use the Insert mode entry when returning to Insert mode.
if (((State & MODE_INSERT) || restart_edit) && !current_sctx.sc_sid) { if (((State & MODE_INSERT) || restart_edit) && !current_sctx.sc_sid) {
mode = "Insert";
idx = MENU_INDEX_INSERT; idx = MENU_INDEX_INSERT;
} else if (State & MODE_CMDLINE) { } else if (State & MODE_CMDLINE) {
mode = "Command";
idx = MENU_INDEX_CMDLINE; idx = MENU_INDEX_CMDLINE;
} else if (State & MODE_TERMINAL) {
idx = MENU_INDEX_TERMINAL;
} else if (get_real_state() & MODE_VISUAL) { } else if (get_real_state() & MODE_VISUAL) {
// Detect real visual mode -- if we are really in visual mode we // Detect real visual mode -- if we are really in visual mode we
// don't need to do any guesswork to figure out what the selection // don't need to do any guesswork to figure out what the selection
// is. Just execute the visual binding for the menu. // is. Just execute the visual binding for the menu.
mode = "Visual";
idx = MENU_INDEX_VISUAL; idx = MENU_INDEX_VISUAL;
} else if (eap != NULL && eap->addr_count) { } else if (eap != NULL && eap->addr_count) {
pos_T tpos; pos_T tpos;
mode = "Visual";
idx = MENU_INDEX_VISUAL; idx = MENU_INDEX_VISUAL;
// GEDDES: This is not perfect - but it is a // GEDDES: This is not perfect - but it is a
@@ -1404,8 +1480,8 @@ static void execute_menu(const exarg_T *eap, vimmenu_T *menu)
} }
// Activate visual mode // Activate visual mode
VIsual_active = TRUE; VIsual_active = true;
VIsual_reselect = TRUE; VIsual_reselect = true;
check_cursor(); check_cursor();
VIsual = curwin->w_cursor; VIsual = curwin->w_cursor;
curwin->w_cursor = tpos; curwin->w_cursor = tpos;
@@ -1418,14 +1494,14 @@ static void execute_menu(const exarg_T *eap, vimmenu_T *menu)
curwin->w_cursor.col++; curwin->w_cursor.col++;
} }
} }
}
if (idx == -1 || eap == NULL) { if (idx == -1 || eap == NULL) {
mode = "Normal";
idx = MENU_INDEX_NORMAL; idx = MENU_INDEX_NORMAL;
} }
assert(idx != MENU_INDEX_INVALID); assert(idx != MENU_INDEX_INVALID);
if (menu->strings[idx] != NULL) { if (menu->strings[idx] != NULL && (menu->modes & (1 << idx))) {
// When executing a script or function execute the commands right now. // When executing a script or function execute the commands right now.
// Also for the window toolbar // Also for the window toolbar
// Otherwise put them in the typeahead buffer. // Otherwise put them in the typeahead buffer.
@@ -1444,6 +1520,30 @@ static void execute_menu(const exarg_T *eap, vimmenu_T *menu)
menu->silent[idx]); menu->silent[idx]);
} }
} else if (eap != NULL) { } else if (eap != NULL) {
char *mode;
switch (idx) {
case MENU_INDEX_VISUAL:
mode = "Visual";
break;
case MENU_INDEX_SELECT:
mode = "Select";
break;
case MENU_INDEX_OP_PENDING:
mode = "Op-pending";
break;
case MENU_INDEX_TERMINAL:
mode = "Terminal";
break;
case MENU_INDEX_INSERT:
mode = "Insert";
break;
case MENU_INDEX_CMDLINE:
mode = "Cmdline";
break;
// case MENU_INDEX_TIP: cannot happen
default:
mode = "Normal";
}
semsg(_("E335: Menu not defined for %s mode"), mode); semsg(_("E335: Menu not defined for %s mode"), mode);
} }
} }
@@ -1452,9 +1552,43 @@ static void execute_menu(const exarg_T *eap, vimmenu_T *menu)
// execute it. // execute it.
void ex_emenu(exarg_T *eap) void ex_emenu(exarg_T *eap)
{ {
char *saved_name = xstrdup(eap->arg); char *arg = eap->arg;
int mode_idx = -1;
if (arg[0] && ascii_iswhite(arg[1])) {
switch (arg[0]) {
case 'n':
mode_idx = MENU_INDEX_NORMAL;
break;
case 'v':
mode_idx = MENU_INDEX_VISUAL;
break;
case 's':
mode_idx = MENU_INDEX_SELECT;
break;
case 'o':
mode_idx = MENU_INDEX_OP_PENDING;
break;
case 't':
mode_idx = MENU_INDEX_TERMINAL;
break;
case 'i':
mode_idx = MENU_INDEX_INSERT;
break;
case 'c':
mode_idx = MENU_INDEX_CMDLINE;
break;
default:
semsg(_(e_invarg2), arg);
return;
}
arg = skipwhite(arg + 2);
}
char *saved_name = xstrdup(arg);
vimmenu_T *menu = *get_root_menu(saved_name); vimmenu_T *menu = *get_root_menu(saved_name);
char *name = saved_name; char *name = saved_name;
bool gave_emsg = false;
while (*name) { while (*name) {
// Find in the menu hierarchy // Find in the menu hierarchy
char *p = menu_name_skip(name); char *p = menu_name_skip(name);
@@ -1463,6 +1597,7 @@ void ex_emenu(exarg_T *eap)
if (menu_name_equal(name, menu)) { if (menu_name_equal(name, menu)) {
if (*p == NUL && menu->children != NULL) { if (*p == NUL && menu->children != NULL) {
emsg(_("E333: Menu path must lead to a menu item")); emsg(_("E333: Menu path must lead to a menu item"));
gave_emsg = true;
menu = NULL; menu = NULL;
} else if (*p != NUL && menu->children == NULL) { } else if (*p != NUL && menu->children == NULL) {
emsg(_(e_notsubmenu)); emsg(_(e_notsubmenu));
@@ -1480,12 +1615,60 @@ void ex_emenu(exarg_T *eap)
} }
xfree(saved_name); xfree(saved_name);
if (menu == NULL) { if (menu == NULL) {
semsg(_("E334: Menu not found: %s"), eap->arg); if (!gave_emsg) {
semsg(_("E334: Menu not found: %s"), arg);
}
return; return;
} }
// Found the menu, so execute. // Found the menu, so execute.
execute_menu(eap, menu); execute_menu(eap, menu, mode_idx);
}
/// Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy.
vimmenu_T *menu_find(const char *path_name)
{
vimmenu_T *menu = *get_root_menu(path_name);
char *saved_name = xstrdup(path_name);
char *name = saved_name;
while (*name) {
// find the end of one dot-separated name and put a NUL at the dot
char *p = menu_name_skip(name);
while (menu != NULL) {
if (menu_name_equal(name, menu)) {
if (menu->children == NULL) {
// found a menu item instead of a sub-menu
if (*p == NUL) {
emsg(_("E336: Menu path must lead to a sub-menu"));
} else {
emsg(_(e_notsubmenu));
}
menu = NULL;
goto theend;
}
if (*p == NUL) { // found a full match
goto theend;
}
break;
}
menu = menu->next;
}
if (menu == NULL) { // didn't find it
break;
}
// Found a match, search the sub-menu.
menu = menu->children;
name = p;
}
if (menu == NULL) {
emsg(_("E337: Menu not found - check menu names"));
}
theend:
xfree(saved_name);
return menu;
} }
/* /*

View File

@@ -18,6 +18,7 @@
#define MENU_OP_PENDING_MODE (1 << MENU_INDEX_OP_PENDING) #define MENU_OP_PENDING_MODE (1 << MENU_INDEX_OP_PENDING)
#define MENU_INSERT_MODE (1 << MENU_INDEX_INSERT) #define MENU_INSERT_MODE (1 << MENU_INDEX_INSERT)
#define MENU_CMDLINE_MODE (1 << MENU_INDEX_CMDLINE) #define MENU_CMDLINE_MODE (1 << MENU_INDEX_CMDLINE)
#define MENU_TERMINAL_MODE (1 << MENU_INDEX_TERMINAL)
#define MENU_TIP_MODE (1 << MENU_INDEX_TIP) #define MENU_TIP_MODE (1 << MENU_INDEX_TIP)
#define MENU_ALL_MODES ((1 << MENU_INDEX_TIP) - 1) #define MENU_ALL_MODES ((1 << MENU_INDEX_TIP) - 1)
/// @} /// @}

View File

@@ -30,6 +30,49 @@
static linenr_T orig_topline = 0; static linenr_T orig_topline = 0;
static int orig_topfill = 0; static int orig_topfill = 0;
/// Translate window coordinates to buffer position without any side effects
int get_fpos_of_mouse(pos_T *mpos)
{
int grid = mouse_grid;
int row = mouse_row;
int col = mouse_col;
if (row < 0 || col < 0) { // check if it makes sense
return IN_UNKNOWN;
}
// find the window where the row is in
win_T *wp = mouse_find_win(&grid, &row, &col);
if (wp == NULL) {
return IN_UNKNOWN;
}
// winpos and height may change in win_enter()!
if (row + wp->w_winbar_height >= wp->w_height) { // In (or below) status line
return IN_STATUS_LINE;
}
if (col >= wp->w_width) { // In vertical separator line
return IN_SEP_LINE;
}
if (wp != curwin) {
return IN_UNKNOWN;
}
// compute the position in the buffer line from the posn on the screen
if (mouse_comp_pos(curwin, &row, &col, &mpos->lnum)) {
return IN_STATUS_LINE; // past bottom
}
mpos->col = vcol2col(wp, mpos->lnum, col);
if (mpos->col > 0) {
mpos->col--;
}
mpos->coladd = 0;
return IN_BUFFER;
}
/// Return true if "c" is a mouse key. /// Return true if "c" is a mouse key.
bool is_mouse_key(int c) bool is_mouse_key(int c)
{ {

View File

@@ -1493,12 +1493,12 @@ static void call_click_def_func(StlClickDefinition *click_defs, int col, int whi
/// Do the appropriate action for the current mouse click in the current mode. /// Do the appropriate action for the current mouse click in the current mode.
/// Not used for Command-line mode. /// Not used for Command-line mode.
/// ///
/// Normal Mode: /// Normal and Visual Mode:
/// event modi- position visual change action /// event modi- position visual change action
/// fier cursor window /// fier cursor window
/// left press - yes end yes /// left press - yes end yes
/// left press C yes end yes "^]" (2) /// left press C yes end yes "^]" (2)
/// left press S yes end yes "*" (2) /// left press S yes end (popup: extend) yes "*" (2)
/// left drag - yes start if moved no /// left drag - yes start if moved no
/// left relse - yes start if moved no /// left relse - yes start if moved no
/// middle press - yes if not active no put register /// middle press - yes if not active no put register
@@ -1787,10 +1787,53 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
if (mouse_model_popup()) { if (mouse_model_popup()) {
if (which_button == MOUSE_RIGHT if (which_button == MOUSE_RIGHT
&& !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) { && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
// NOTE: Ignore right button down and drag mouse events. Windows only if (!is_click) {
// shows the popup menu on the button up event. // Ignore right button release events, only shows the popup
// menu on the button down event.
return false; return false;
} }
jump_flags = 0;
if (STRCMP(p_mousem, "popup_setpos") == 0) {
// First set the cursor position before showing the popup
// menu.
if (VIsual_active) {
pos_T m_pos;
// set MOUSE_MAY_STOP_VIS if we are outside the
// selection or the current window (might have false
// negative here)
if (mouse_row < curwin->w_winrow
|| mouse_row > (curwin->w_winrow + curwin->w_height)) {
jump_flags = MOUSE_MAY_STOP_VIS;
} else if (get_fpos_of_mouse(&m_pos) != IN_BUFFER) {
jump_flags = MOUSE_MAY_STOP_VIS;
} else {
if ((lt(curwin->w_cursor, VIsual)
&& (lt(m_pos, curwin->w_cursor) || lt(VIsual, m_pos)))
|| (lt(VIsual, curwin->w_cursor)
&& (lt(m_pos, VIsual) || lt(curwin->w_cursor, m_pos)))) {
jump_flags = MOUSE_MAY_STOP_VIS;
} else if (VIsual_mode == Ctrl_V) {
getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
if (m_pos.col < leftcol || m_pos.col > rightcol) {
jump_flags = MOUSE_MAY_STOP_VIS;
}
}
}
} else {
jump_flags = MOUSE_MAY_STOP_VIS;
}
}
if (jump_flags) {
jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
update_curbuf(VIsual_active ? INVERTED : VALID);
setcursor();
ui_flush(); // Update before showing popup menu
}
show_popupmenu();
got_click = false; // ignore release events
return (jump_flags & CURSOR_MOVED) != 0;
}
if (which_button == MOUSE_LEFT if (which_button == MOUSE_LEFT
&& (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))) { && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))) {
which_button = MOUSE_RIGHT; which_button = MOUSE_RIGHT;

View File

@@ -18,6 +18,7 @@
#include "nvim/ex_cmds.h" #include "nvim/ex_cmds.h"
#include "nvim/memline.h" #include "nvim/memline.h"
#include "nvim/memory.h" #include "nvim/memory.h"
#include "nvim/menu.h"
#include "nvim/move.h" #include "nvim/move.h"
#include "nvim/option.h" #include "nvim/option.h"
#include "nvim/popupmnu.h" #include "nvim/popupmnu.h"
@@ -512,9 +513,13 @@ void pum_redraw(void)
char_u *st; char_u *st;
char_u saved = *p; char_u saved = *p;
if (saved != NUL) {
*p = NUL; *p = NUL;
}
st = (char_u *)transstr((const char *)s, true); st = (char_u *)transstr((const char *)s, true);
if (saved != NUL) {
*p = saved; *p = saved;
}
if (pum_rl) { if (pum_rl) {
char *rt = (char *)reverse_text(st); char *rt = (char *)reverse_text(st);
@@ -932,3 +937,181 @@ void pum_set_event_info(dict_T *dict)
(void)tv_dict_add_bool(dict, S_LEN("scrollbar"), (void)tv_dict_add_bool(dict, S_LEN("scrollbar"),
pum_scrollbar ? kBoolVarTrue : kBoolVarFalse); pum_scrollbar ? kBoolVarTrue : kBoolVarFalse);
} }
static void pum_position_at_mouse(int min_width)
{
pum_anchor_grid = mouse_grid;
if (Rows - mouse_row > pum_size) {
// Enough space below the mouse row.
pum_above = false;
pum_row = mouse_row + 1;
if (pum_height > Rows - pum_row) {
pum_height = Rows - pum_row;
}
} else {
// Show above the mouse row, reduce height if it does not fit.
pum_above = true;
pum_row = mouse_row - pum_size;
if (pum_row < 0) {
pum_height += pum_row;
pum_row = 0;
}
}
if (Columns - mouse_col >= pum_base_width || Columns - mouse_col > min_width) {
// Enough space to show at mouse column.
pum_col = mouse_col;
} else {
// Not enough space, right align with window.
pum_col = Columns - (pum_base_width > min_width ? min_width : pum_base_width);
}
pum_width = Columns - pum_col;
if (pum_width > pum_base_width + 1) {
pum_width = pum_base_width + 1;
}
}
/// Select the pum entry at the mouse position.
static void pum_select_mouse_pos(void)
{
if (mouse_grid == pum_grid.handle) {
pum_selected = mouse_row;
return;
} else if (mouse_grid > 1) {
pum_selected = -1;
return;
}
int idx = mouse_row - pum_row;
if (idx < 0 || idx >= pum_size) {
pum_selected = -1;
} else if (*pum_array[idx].pum_text != NUL) {
pum_selected = idx;
}
}
/// Execute the currently selected popup menu item.
static void pum_execute_menu(vimmenu_T *menu, int mode)
{
int idx = 0;
exarg_T ea;
for (vimmenu_T *mp = menu->children; mp != NULL; mp = mp->next) {
if ((mp->modes & mp->enabled & mode) && idx++ == pum_selected) {
memset(&ea, 0, sizeof(ea));
execute_menu(&ea, mp, -1);
break;
}
}
}
/// Open the terminal version of the popup menu and don't return until it is closed.
void pum_show_popupmenu(vimmenu_T *menu)
{
pum_undisplay(true);
pum_size = 0;
int mode = get_menu_mode_flag();
for (vimmenu_T *mp = menu->children; mp != NULL; mp = mp->next) {
if (menu_is_separator(mp->dname) || (mp->modes & mp->enabled & mode)) {
pum_size++;
}
}
// When there are only Terminal mode menus, using "popup Edit" results in
// pum_size being zero.
if (pum_size <= 0) {
emsg(e_menuothermode);
return;
}
int idx = 0;
pumitem_T *array = (pumitem_T *)xcalloc((size_t)pum_size, sizeof(pumitem_T));
for (vimmenu_T *mp = menu->children; mp != NULL; mp = mp->next) {
if (menu_is_separator(mp->dname)) {
array[idx++].pum_text = (char_u *)"";
} else if (mp->modes & mp->enabled & mode) {
array[idx++].pum_text = (char_u *)mp->dname;
}
}
pum_array = array;
pum_compute_size();
pum_scrollbar = 0;
pum_height = pum_size;
pum_position_at_mouse(20);
pum_selected = -1;
pum_first = 0;
for (;;) {
pum_is_visible = true;
pum_is_drawn = true;
pum_redraw();
setcursor_mayforce(true);
ui_flush();
int c = vgetc();
if (c == ESC || c == Ctrl_C) {
break;
} else if (c == CAR || c == NL) {
// enter: select current item, if any, and close
pum_execute_menu(menu, mode);
break;
} else if (c == 'k' || c == K_UP || c == K_MOUSEUP) {
// cursor up: select previous item
while (pum_selected > 0) {
pum_selected--;
if (*array[pum_selected].pum_text != NUL) {
break;
}
}
} else if (c == 'j' || c == K_DOWN || c == K_MOUSEDOWN) {
// cursor down: select next item
while (pum_selected < pum_size - 1) {
pum_selected++;
if (*array[pum_selected].pum_text != NUL) {
break;
}
}
} else if (c == K_RIGHTMOUSE) {
// Right mouse down: reposition the menu.
vungetc(c);
break;
} else if (c == K_LEFTDRAG || c == K_RIGHTDRAG || c == K_MOUSEMOVE) {
// mouse moved: select item in the mouse row
pum_select_mouse_pos();
} else if (c == K_LEFTMOUSE || c == K_LEFTMOUSE_NM || c == K_RIGHTRELEASE) {
// left mouse click: select clicked item, if any, and close;
// right mouse release: select clicked item, close if any
pum_select_mouse_pos();
if (pum_selected >= 0) {
pum_execute_menu(menu, mode);
break;
}
if (c == K_LEFTMOUSE || c == K_LEFTMOUSE_NM) {
break;
}
}
}
xfree(array);
pum_undisplay(true);
}
void pum_make_popup(const char *path_name, int use_mouse_pos)
{
if (!use_mouse_pos) {
// Hack: set mouse position at the cursor so that the menu pops up
// around there.
mouse_row = curwin->w_winrow + curwin->w_wrow;
mouse_col = curwin->w_wincol + curwin->w_wcol;
}
vimmenu_T *menu = menu_find(path_name);
if (menu != NULL) {
pum_show_popupmenu(menu);
}
}

View File

@@ -298,6 +298,13 @@ void redraw_win_signcol(win_T *wp)
} }
} }
/// Update all windows that are editing the current buffer.
void update_curbuf(int type)
{
redraw_curbuf_later(type);
update_screen(type);
}
/// Redraw the parts of the screen that is marked for redraw. /// Redraw the parts of the screen that is marked for redraw.
/// ///
/// Most code shouldn't call this directly, rather use redraw_later() and /// Most code shouldn't call this directly, rather use redraw_later() and
@@ -5750,12 +5757,17 @@ static void linecopy(ScreenGrid *grid, int to, int from, int col, int width)
width * sizeof(sattr_T)); width * sizeof(sattr_T));
} }
/* /// Set cursor to its position in the current window.
* Set cursor to its position in the current window.
*/
void setcursor(void) void setcursor(void)
{ {
if (redrawing()) { setcursor_mayforce(false);
}
/// Set cursor to its position in the current window.
/// @param force when true, also when not redrawing.
void setcursor_mayforce(bool force)
{
if (force || redrawing()) {
validate_cursor(); validate_cursor();
ScreenGrid *grid = &curwin->w_grid; ScreenGrid *grid = &curwin->w_grid;

View File

@@ -36,3 +36,52 @@ func Test_translate_menu()
source $VIMRUNTIME/delmenu.vim source $VIMRUNTIME/delmenu.vim
endfunc endfunc
func Test_menu_commands()
nmenu 2 Test.FooBar :let g:did_menu = 'normal'<CR>
vmenu 2 Test.FooBar :let g:did_menu = 'visual'<CR>
smenu 2 Test.FooBar :let g:did_menu = 'select'<CR>
omenu 2 Test.FooBar :let g:did_menu = 'op-pending'<CR>
tlmenu 2 Test.FooBar :let g:did_menu = 'terminal'<CR>
imenu 2 Test.FooBar :let g:did_menu = 'insert'<CR>
cmenu 2 Test.FooBar :let g:did_menu = 'cmdline'<CR>
emenu n Test.FooBar
call assert_equal('normal', g:did_menu)
emenu v Test.FooBar
call assert_equal('visual', g:did_menu)
emenu s Test.FooBar
call assert_equal('select', g:did_menu)
emenu o Test.FooBar
call assert_equal('op-pending', g:did_menu)
emenu t Test.FooBar
call assert_equal('terminal', g:did_menu)
emenu i Test.FooBar
call assert_equal('insert', g:did_menu)
emenu c Test.FooBar
call assert_equal('cmdline', g:did_menu)
nunmenu Test.FooBar
call assert_fails('emenu n Test.FooBar', 'E335: Menu not defined for Normal mode')
vunmenu Test.FooBar
call assert_fails('emenu v Test.FooBar', 'E335: Menu not defined for Visual mode')
vmenu 2 Test.FooBar :let g:did_menu = 'visual'<CR>
sunmenu Test.FooBar
call assert_fails('emenu s Test.FooBar', 'E335: Menu not defined for Select mode')
ounmenu Test.FooBar
call assert_fails('emenu o Test.FooBar', 'E335: Menu not defined for Op-pending mode')
iunmenu Test.FooBar
call assert_fails('emenu i Test.FooBar', 'E335: Menu not defined for Insert mode')
cunmenu Test.FooBar
call assert_fails('emenu c Test.FooBar', 'E335: Menu not defined for Cmdline mode')
tlunmenu Test.FooBar
call assert_fails('emenu t Test.FooBar', 'E335: Menu not defined for Terminal mode')
aunmenu Test.FooBar
call assert_fails('emenu n Test.FooBar', 'E334:')
nmenu 2 Test.FooBar.Child :let g:did_menu = 'foobar'<CR>
call assert_fails('emenu n Test.FooBar', 'E333:')
nunmenu Test.FooBar.Child
unlet g:did_menu
endfun

View File

@@ -913,7 +913,7 @@ func Test_popup_complete_backwards_ctrl_p()
bwipe! bwipe!
endfunc endfunc
fun! Test_complete_o_tab() func Test_complete_o_tab()
CheckFunction test_override CheckFunction test_override
let s:o_char_pressed = 0 let s:o_char_pressed = 0
@@ -922,7 +922,7 @@ fun! Test_complete_o_tab()
let s:o_char_pressed = 0 let s:o_char_pressed = 0
call feedkeys("\<c-x>\<c-n>", 'i') call feedkeys("\<c-x>\<c-n>", 'i')
endif endif
endf endfunc
set completeopt=menu,noselect set completeopt=menu,noselect
new new
@@ -941,7 +941,21 @@ fun! Test_complete_o_tab()
bwipe! bwipe!
set completeopt& set completeopt&
delfunc s:act_on_text_changed delfunc s:act_on_text_changed
endf endfunc
func Test_menu_only_exists_in_terminal()
if !exists(':tlmenu') || has('gui_running')
return
endif
tlnoremenu &Edit.&Paste<Tab>"+gP <C-W>"+
aunmenu *
try
popup Edit
call assert_false(1, 'command should have failed')
catch
call assert_exception('E328:')
endtry
endfunc
func Test_popup_complete_info_01() func Test_popup_complete_info_01()
new new

View File

@@ -3748,7 +3748,7 @@ describe('API', function()
eq("foo", meths.cmd({ cmd = "Foo" }, { output = true })) eq("foo", meths.cmd({ cmd = "Foo" }, { output = true }))
end) end)
it('errors if command is not implemented', function() it('errors if command is not implemented', function()
eq("Command not implemented: popup", pcall_err(meths.cmd, { cmd = "popup" }, {})) eq("Command not implemented: winpos", pcall_err(meths.cmd, { cmd = "winpos" }, {}))
end) end)
it('works with empty arguments list', function() it('works with empty arguments list', function()
meths.cmd({ cmd = "update" }, {}) meths.cmd({ cmd = "update" }, {})

View File

@@ -11,6 +11,7 @@ local get_pathsep = helpers.get_pathsep
local eq = helpers.eq local eq = helpers.eq
local pcall_err = helpers.pcall_err local pcall_err = helpers.pcall_err
local exec_lua = helpers.exec_lua local exec_lua = helpers.exec_lua
local exec = helpers.exec
describe('ui/ext_popupmenu', function() describe('ui/ext_popupmenu', function()
local screen local screen
@@ -2359,6 +2360,74 @@ describe('builtin popupmenu', function()
{2:-- INSERT --} | {2:-- INSERT --} |
]]) ]])
end) end)
it('supports mousemodel=popup', function()
screen:try_resize(32, 6)
exec([[
call setline(1, 'popup menu test')
set mouse=a mousemodel=popup
menu PopUp.foo :let g:menustr = 'foo'<CR>
menu PopUp.bar :let g:menustr = 'bar'<CR>
menu PopUp.baz :let g:menustr = 'baz'<CR>
]])
meths.input_mouse('right', 'press', '', 0, 0, 4)
screen:expect([[
^popup menu test |
{1:~ }{n: foo }{1: }|
{1:~ }{n: bar }{1: }|
{1:~ }{n: baz }{1: }|
{1:~ }|
|
]])
feed('<Down>')
screen:expect([[
^popup menu test |
{1:~ }{s: foo }{1: }|
{1:~ }{n: bar }{1: }|
{1:~ }{n: baz }{1: }|
{1:~ }|
|
]])
feed('<Down>')
screen:expect([[
^popup menu test |
{1:~ }{n: foo }{1: }|
{1:~ }{s: bar }{1: }|
{1:~ }{n: baz }{1: }|
{1:~ }|
|
]])
feed('<CR>')
screen:expect([[
^popup menu test |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
:let g:menustr = 'bar' |
]])
eq('bar', meths.get_var('menustr'))
meths.input_mouse('right', 'press', '', 0, 1, 20)
screen:expect([[
^popup menu test |
{1:~ }|
{1:~ }{n: foo }{1: }|
{1:~ }{n: bar }{1: }|
{1:~ }{n: baz }{1: }|
:let g:menustr = 'bar' |
]])
meths.input_mouse('left', 'press', '', 0, 4, 22)
screen:expect([[
^popup menu test |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
:let g:menustr = 'baz' |
]])
eq('baz', meths.get_var('menustr'))
end)
end) end)
describe('builtin popupmenu with ui/ext_multigrid', function() describe('builtin popupmenu with ui/ext_multigrid', function()
@@ -2450,4 +2519,57 @@ describe('builtin popupmenu with ui/ext_multigrid', function()
{n: 哦哦哦哦哦哦哦哦哦>}{s: }| {n: 哦哦哦哦哦哦哦哦哦>}{s: }|
]], float_pos={[4] = {{id = -1}, 'NW', 2, 1, 11, false, 100}}}) ]], float_pos={[4] = {{id = -1}, 'NW', 2, 1, 11, false, 100}}})
end) end)
it('supports mousemodel=popup', function()
screen:try_resize(32, 6)
exec([[
call setline(1, 'popup menu test')
set mouse=a mousemodel=popup
menu PopUp.foo :let g:menustr = 'foo'<CR>
menu PopUp.bar :let g:menustr = 'bar'<CR>
menu PopUp.baz :let g:menustr = 'baz'<CR>
]])
meths.input_mouse('right', 'press', '', 2, 1, 20)
screen:expect({grid=[[
## grid 1
[2:--------------------------------]|
[2:--------------------------------]|
[2:--------------------------------]|
[2:--------------------------------]|
[2:--------------------------------]|
[3:--------------------------------]|
## grid 2
^popup menu test |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
## grid 3
|
## grid 4
{n: foo }|
{n: bar }|
{n: baz }|
]], float_pos={[4] = {{id = -1}, 'NW', 2, 2, 19, false, 100}}})
meths.input_mouse('left', 'press', '', 4, 2, 2)
screen:expect({grid=[[
## grid 1
[2:--------------------------------]|
[2:--------------------------------]|
[2:--------------------------------]|
[2:--------------------------------]|
[2:--------------------------------]|
[3:--------------------------------]|
## grid 2
^popup menu test |
{1:~ }|
{1:~ }|
{1:~ }|
{1:~ }|
## grid 3
:let g:menustr = 'baz' |
]]})
eq('baz', meths.get_var('menustr'))
end)
end) end)