From cf8df141f3c7e706c86aadba404ae8ad54d5c795 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 30 Jun 2022 14:21:43 +0800 Subject: vim-patch:8.0.1558: no right-click menu in a terminal Problem: No right-click menu in a terminal. Solution: Implement the right click menu for the terminal. https://github.com/vim/vim/commit/aef8c3da2ba59285b7cfde559ae21cdce6ba6919 --- src/nvim/menu.c | 59 +++++++++++++++++++- src/nvim/mouse.c | 43 +++++++++++++++ src/nvim/normal.c | 53 ++++++++++++++++-- src/nvim/popupmnu.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/nvim/screen.c | 7 +++ 5 files changed, 305 insertions(+), 8 deletions(-) diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 80f8406ab0..5e4dc12e40 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -11,6 +11,7 @@ #include #include "nvim/ascii.h" +#include "nvim/autocmd.h" #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/eval.h" @@ -22,6 +23,7 @@ #include "nvim/memory.h" #include "nvim/menu.h" #include "nvim/message.h" +#include "nvim/popupmnu.h" #include "nvim/screen.h" #include "nvim/state.h" #include "nvim/strings.h" @@ -1355,9 +1357,64 @@ static int menu_is_hidden(char *name) || (menu_is_popup(name) && name[5] != NUL); } +static int get_menu_mode(void) +{ + 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; +} + +/// 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 mode = get_menu_mode(); + if (mode == MENU_INDEX_INVALID) { + return; + } + mode = menu_mode_chars[mode]; + + char ename[2]; + ename[0] = (char)mode; + ename[1] = NUL; + apply_autocmds(EVENT_MENUPOPUP, ename, NULL, false, curbuf); + + vimmenu_T *menu; + + for (menu = root_menu; menu != NULL; menu = menu->next) { + if (STRNCMP("PopUp", menu->name, 5) == 0 && menu->name[5] == mode) { + 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. // "eap" is NULL for the window toolbar. -static void execute_menu(const exarg_T *eap, vimmenu_T *menu) +void execute_menu(const exarg_T *eap, vimmenu_T *menu) FUNC_ATTR_NONNULL_ARG(2) { int idx = -1; diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index fc5ecbc6a0..3aee20dc7b 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -30,6 +30,49 @@ static linenr_T orig_topline = 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. bool is_mouse_key(int c) { diff --git a/src/nvim/normal.c b/src/nvim/normal.c index d491a0ce84..beeb49ea66 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -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. /// Not used for Command-line mode. /// -/// Normal Mode: +/// Normal and Visual Mode: /// event modi- position visual change action /// fier cursor window /// left press - yes end yes /// 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 relse - yes start if moved no /// middle press - yes if not active no put register @@ -1787,9 +1787,52 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent) if (mouse_model_popup()) { if (which_button == MOUSE_RIGHT && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) { - // NOTE: Ignore right button down and drag mouse events. Windows only - // shows the popup menu on the button up event. - return false; + if (!is_click) { + // Ignore right button release events, only shows the popup + // menu on the button down event. + 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 && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))) { diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index ecaeca005d..cf87f469b9 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -18,6 +18,7 @@ #include "nvim/ex_cmds.h" #include "nvim/memline.h" #include "nvim/memory.h" +#include "nvim/menu.h" #include "nvim/move.h" #include "nvim/option.h" #include "nvim/popupmnu.h" @@ -512,9 +513,13 @@ void pum_redraw(void) char_u *st; char_u saved = *p; - *p = NUL; + if (saved != NUL) { + *p = NUL; + } st = (char_u *)transstr((const char *)s, true); - *p = saved; + if (saved != NUL) { + *p = saved; + } if (pum_rl) { char *rt = (char *)reverse_text(st); @@ -932,3 +937,145 @@ void pum_set_event_info(dict_T *dict) (void)tv_dict_add_bool(dict, S_LEN("scrollbar"), pum_scrollbar ? kBoolVarTrue : kBoolVarFalse); } + +static void pum_position_at_mouse(int min_width) +{ + if (Rows - mouse_row > pum_size) { + // Enough space below the mouse row. + 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_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) +{ + 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 idx = 0; + exarg_T ea; + + for (vimmenu_T *mp = menu->children; mp != NULL; mp = mp->next) { + if (idx++ == pum_selected) { + memset(&ea, 0, sizeof(ea)); + execute_menu(&ea, mp); + 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; + + for (vimmenu_T *mp = menu->children; mp != NULL; mp = mp->next) { + pum_size++; + } + + 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 { + 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(); + ui_flush(); + + int c = vgetc(); + if (c == ESC) { + break; + } else if (c == CAR || c == NL) { + // enter: select current item, if any, and close + pum_execute_menu(menu); + 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); + break; + } + if (c == K_LEFTMOUSE || c == K_LEFTMOUSE_NM) { + break; + } + } + } + + xfree(array); + pum_undisplay(true); +} diff --git a/src/nvim/screen.c b/src/nvim/screen.c index bc11883fd8..78b0d6b841 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -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. /// /// Most code shouldn't call this directly, rather use redraw_later() and -- cgit From 610cf9f95032bd219cb5695d169fe2cd698ec307 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 30 Jun 2022 15:30:54 +0800 Subject: vim-patch:8.0.1570: can't use :popup for a menu in the terminal Problem: Can't use :popup for a menu in the terminal. (Wei Zhang) Solution: Make :popup work in the terminal. Also fix that entries were included that don't work in the current state. https://github.com/vim/vim/commit/29a2c08d792e4458a0af8371f5341394829fce29 --- runtime/doc/gui.txt | 5 ++-- src/nvim/ex_cmds.lua | 2 +- src/nvim/ex_docmd.c | 6 +++++ src/nvim/menu.c | 56 ++++++++++++++++++++++++++++++++++++++++ src/nvim/popupmnu.c | 22 ++++++++++++++-- test/functional/api/vim_spec.lua | 2 +- 6 files changed, 86 insertions(+), 7 deletions(-) diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt index e296141c39..4205362484 100644 --- a/runtime/doc/gui.txt +++ b/runtime/doc/gui.txt @@ -577,8 +577,8 @@ a menu item - you don't need to do a :tunmenu as well. 5.9 Popup Menus -In the Win32 GUI, you can cause a menu to popup at the cursor. This behaves -similarly to the PopUp menus except that any menu tree can be popped up. +You can cause a menu to popup at the cursor. This behaves similarly to the +PopUp menus except that any menu tree can be popped up. This command is for backwards compatibility, using it is discouraged, because it behaves in a strange way. @@ -587,7 +587,6 @@ it behaves in a strange way. :popu[p] {name} Popup the menu {name}. The menu named must have at least one subentry, but need not 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 pointer instead of the cursor. diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index b18bdefc2a..cc69921883 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -1991,7 +1991,7 @@ module.cmds = { command='popup', flags=bit.bor(NEEDARG, EXTRA, BANG, TRLBAR, NOTRLCOM, CMDWIN), addr_type='ADDR_NONE', - func='ex_ni', + func='ex_popup', }, { command='ppop', diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 4c9c1665fd..2ca038e56d 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -62,6 +62,7 @@ #include "nvim/os/time.h" #include "nvim/os_unix.h" #include "nvim/path.h" +#include "nvim/popupmnu.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" #include "nvim/screen.h" @@ -7985,6 +7986,11 @@ static void ex_nogui(exarg_T *eap) 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) { if (curbuf->b_ml.ml_mfp == NULL || curbuf->b_ml.ml_mfp->mf_fname == NULL) { diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 5e4dc12e40..73472ff1c4 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -1382,6 +1382,16 @@ static int get_menu_mode(void) 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. @@ -1545,6 +1555,52 @@ void ex_emenu(exarg_T *eap) execute_menu(eap, menu); } +/// 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; +} + /* * Translation of menu names. Just a simple lookup table. */ diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index cf87f469b9..82ad409c39 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -1000,9 +1000,12 @@ 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) { - pum_size++; + if (menu_is_separator(mp->dname) || (mp->modes & mp->enabled & mode)) { + pum_size++; + } } int idx = 0; @@ -1011,7 +1014,7 @@ void pum_show_popupmenu(vimmenu_T *menu) for (vimmenu_T *mp = menu->children; mp != NULL; mp = mp->next) { if (menu_is_separator(mp->dname)) { array[idx++].pum_text = (char_u *)""; - } else { + } else if (mp->modes & mp->enabled & mode) { array[idx++].pum_text = (char_u *)mp->dname; } } @@ -1079,3 +1082,18 @@ void pum_show_popupmenu(vimmenu_T *menu) 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); + } +} diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 858463efbd..002bcd92a4 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -3748,7 +3748,7 @@ describe('API', function() eq("foo", meths.cmd({ cmd = "Foo" }, { output = true })) end) 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) it('works with empty arguments list', function() meths.cmd({ cmd = "update" }, {}) -- cgit From e4c2ff2da4fb1986e0cd88e6eea9d49ddaec9817 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 30 Jun 2022 15:55:38 +0800 Subject: vim-patch:8.0.1574: show cursor in wrong place when using popup menu Problem: Show cursor in wrong place when using popup menu. (Wei Zhang) Solution: Force updating the cursor position. Fix skipping over unused entries. https://github.com/vim/vim/commit/987723e084660290270b3c3d943eb13bd828d5da --- src/nvim/popupmnu.c | 10 +++++----- src/nvim/screen.c | 13 +++++++++---- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 82ad409c39..515fecb036 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -981,13 +981,13 @@ static void pum_select_mouse_pos(void) } /// Execute the currently selected popup menu item. -static void pum_execute_menu(vimmenu_T *menu) +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 (idx++ == pum_selected) { + if ((mp->modes & mp->enabled & mode) && idx++ == pum_selected) { memset(&ea, 0, sizeof(ea)); execute_menu(&ea, mp); break; @@ -1032,7 +1032,7 @@ void pum_show_popupmenu(vimmenu_T *menu) pum_is_visible = true; pum_is_drawn = true; pum_redraw(); - setcursor(); + setcursor_mayforce(true); ui_flush(); int c = vgetc(); @@ -1040,7 +1040,7 @@ void pum_show_popupmenu(vimmenu_T *menu) break; } else if (c == CAR || c == NL) { // enter: select current item, if any, and close - pum_execute_menu(menu); + pum_execute_menu(menu, mode); break; } else if (c == 'k' || c == K_UP || c == K_MOUSEUP) { // cursor up: select previous item @@ -1070,7 +1070,7 @@ void pum_show_popupmenu(vimmenu_T *menu) // right mouse release: select clicked item, close if any pum_select_mouse_pos(); if (pum_selected >= 0) { - pum_execute_menu(menu); + pum_execute_menu(menu, mode); break; } if (c == K_LEFTMOUSE || c == K_LEFTMOUSE_NM) { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 78b0d6b841..c2a88bb4a6 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -5757,12 +5757,17 @@ static void linecopy(ScreenGrid *grid, int to, int from, int col, int width) 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) { - 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(); ScreenGrid *grid = &curwin->w_grid; -- cgit From 5551a29d065d8b6632b61f327f22da9166c8f9c6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 30 Jun 2022 16:00:56 +0800 Subject: vim-patch:8.0.1588: popup menu hangs after typing CTRL-C Problem: Popup menu hangs after typing CTRL-C. Solution: Make CTRL-C exit the loop. (Ozaki Kiichi, closes vim/vim#2697) https://github.com/vim/vim/commit/52f18a112a073c39187cd93f26115d1e2bfd29ce --- src/nvim/popupmnu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 515fecb036..087e9bb041 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -1036,7 +1036,7 @@ void pum_show_popupmenu(vimmenu_T *menu) ui_flush(); int c = vgetc(); - if (c == ESC) { + if (c == ESC || c == Ctrl_C) { break; } else if (c == CAR || c == NL) { // enter: select current item, if any, and close -- cgit From 015778a3817178a8fdc1150ef1b0eaa3dde776f1 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 30 Jun 2022 17:17:27 +0800 Subject: vim-patch:8.1.0487: no menus specifically for the terminal window Problem: No menus specifically for the terminal window. Solution: Add :tlmenu. (Yee Cheng Chin, closes vim/vim#3439) Add a menu test. https://github.com/vim/vim/commit/4c5d815256099b50eca2ec5bf8f9aaa67a890211 ADDR_OHTER comes from patch 8.1.1241, which has already been ported. --- runtime/delmenu.vim | 1 + runtime/doc/autocmd.txt | 5 +- runtime/doc/gui.txt | 22 +++- runtime/doc/index.txt | 11 +- runtime/doc/nvim_terminal_emulator.txt | 3 + runtime/doc/usr_42.txt | 3 +- runtime/menu.vim | 3 + runtime/syntax/vim.vim | 2 +- src/nvim/buffer_defs.h | 5 +- src/nvim/ex_cmds.lua | 20 ++- src/nvim/ex_docmd.c | 3 + src/nvim/menu.c | 221 ++++++++++++++++++++++----------- src/nvim/menu.h | 1 + src/nvim/popupmnu.c | 2 +- src/nvim/testdir/test_menu.vim | 34 +++++ 15 files changed, 246 insertions(+), 90 deletions(-) diff --git a/runtime/delmenu.vim b/runtime/delmenu.vim index 5c20290152..040cc09aa9 100644 --- a/runtime/delmenu.vim +++ b/runtime/delmenu.vim @@ -5,6 +5,7 @@ " Last Change: 2019 Dec 10 aunmenu * +tlunmenu * unlet! g:did_install_default_menus unlet! g:did_install_syntax_menu diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index bf231044a0..59e5c078a3 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -727,13 +727,14 @@ MenuPopup Just before showing the popup menu (under the right mouse button). Useful for adjusting the menu for what is under the cursor or mouse pointer. - The pattern is matched against a single - character representing the mode: + The pattern is matched against one or two + characters representing the mode: n Normal v Visual o Operator-pending i Insert c Command line + tl Terminal *ModeChanged* ModeChanged After changing the mode. The pattern is matched against `'old_mode:new_mode'`, for diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt index 4205362484..b3b8b2d2c0 100644 --- a/runtime/doc/gui.txt +++ b/runtime/doc/gui.txt @@ -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", 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: & 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. *:am* *:amenu* *:an* *:anoremenu* -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 -some modes: +The ":amenu" command can be used to define menu entries for all modes at once, +expect for Terminal mode. To make the command work correctly, a character is +automatically inserted for some modes: mode inserted appended ~ Normal nothing nothing Visual @@ -469,6 +473,16 @@ Executing Menus *execute-menus* insert-mode menu Eg: > :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 GUI mode. See 'wildmenu' for an option that works well with this. See |console-menus| for an example. @@ -547,6 +561,8 @@ See section |42.4| in the user manual. :tu[nmenu] {menupath} Remove a tip for a menu or tool. {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 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 diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 279645009b..25b98ae4ab 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1622,17 +1622,20 @@ tag command action ~ |:tjump| :tj[ump] like ":tselect", but jump directly when there is only one match |:tlast| :tl[ast] jump to last matching tag -|:tmapclear| :tmapc[lear] remove all mappings for Terminal-Job mode -|:tmap| :tma[p] like ":map" but for Terminal-Job mode +|:tlmenu| :tlm[enu] add menu for |Terminal-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 |: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 |:tprevious| :tp[revious] jump to previous matching tag |:trewind| :tr[ewind] jump to first matching tag |:try| :try execute commands, abort on error or exception |: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 |:undo| :u[ndo] undo last change(s) |:undojoin| :undoj[oin] join next change with previous undo block diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt index a4c25009a9..487ccdb0c5 100644 --- a/runtime/doc/nvim_terminal_emulator.txt +++ b/runtime/doc/nvim_terminal_emulator.txt @@ -80,6 +80,9 @@ To use `ALT+{h,j,k,l}` to navigate windows from any mode: > :nnoremap k :nnoremap 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: - If the program has enabled mouse events, the corresponding events will be diff --git a/runtime/doc/usr_42.txt b/runtime/doc/usr_42.txt index ff3ae7057a..470f4e0fe5 100644 --- a/runtime/doc/usr_42.txt +++ b/runtime/doc/usr_42.txt @@ -150,7 +150,8 @@ like the variations on the ":map" command: :menu! Insert and Command-line mode :imenu Insert 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 ":noremenu", ":nnoremenu", ":anoremenu", etc. diff --git a/runtime/menu.vim b/runtime/menu.vim index 956b941971..e20720dbd2 100644 --- a/runtime/menu.vim +++ b/runtime/menu.vim @@ -164,6 +164,9 @@ if exists(':tlmenu') endif nnoremenu 20.360 &Edit.&Paste"+gP "+gP cnoremenu &Edit.&Paste"+gP + +if exists(':tlmenu') + tlnoremenu &Edit.&Paste"+gP "+ +endif exe 'vnoremenu