diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/buffer_defs.h | 62 | ||||
-rw-r--r-- | src/nvim/eval.c | 9 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 107 | ||||
-rw-r--r-- | src/nvim/ex_docmd.h | 14 | ||||
-rw-r--r-- | src/nvim/getchar.c | 2 | ||||
-rw-r--r-- | src/nvim/menu.c | 346 | ||||
-rw-r--r-- | src/nvim/menu.h | 34 | ||||
-rw-r--r-- | src/nvim/mouse.c | 33 | ||||
-rw-r--r-- | src/nvim/mouse.h | 1 | ||||
-rw-r--r-- | src/nvim/normal.c | 9 | ||||
-rw-r--r-- | src/nvim/screen.c | 49 | ||||
-rw-r--r-- | src/nvim/window.c | 75 |
12 files changed, 486 insertions, 255 deletions
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 3dbd3ea121..b87a2ba721 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -334,7 +334,7 @@ typedef struct { // Struct to hold the saved typeahead for save_typeahead(). typedef struct { typebuf_T save_typebuf; - int typebuf_valid; // TRUE when save_typebuf valid + bool typebuf_valid; // true when save_typebuf valid int old_char; int old_mod_mask; buffheader_T save_readbuf1; @@ -1062,6 +1062,48 @@ typedef struct pos_T w_cursor_corr; // corrected cursor position } pos_save_T; +/// Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode +/// \addtogroup MENU_INDEX +/// @{ +enum { + MENU_INDEX_INVALID = -1, + MENU_INDEX_NORMAL = 0, + MENU_INDEX_VISUAL = 1, + MENU_INDEX_SELECT = 2, + MENU_INDEX_OP_PENDING = 3, + MENU_INDEX_INSERT = 4, + MENU_INDEX_CMDLINE = 5, + MENU_INDEX_TIP = 6, + MENU_MODES = 7, +}; + +typedef struct VimMenu vimmenu_T; + +struct VimMenu { + int modes; ///< Which modes is this menu visible for + int enabled; ///< for which modes the menu is enabled + char_u *name; ///< Name of menu, possibly translated + char_u *dname; ///< Displayed Name ("name" without '&') + char_u *en_name; ///< "name" untranslated, NULL when + ///< was not translated + char_u *en_dname; ///< NULL when "dname" untranslated + int mnemonic; ///< mnemonic key (after '&') + char_u *actext; ///< accelerator text (after TAB) + long priority; ///< Menu order priority + char_u *strings[MENU_MODES]; ///< Mapped string for each mode + int noremap[MENU_MODES]; ///< A \ref REMAP_VALUES flag for each mode + bool silent[MENU_MODES]; ///< A silent flag for each mode + vimmenu_T *children; ///< Children of sub-menu + vimmenu_T *parent; ///< Parent of menu + vimmenu_T *next; ///< Next item in menu +}; + +typedef struct { + int wb_startcol; + int wb_endcol; + vimmenu_T *wb_menu; +} winbar_item_T; + /// Structure which contains all information that belongs to a window. /// /// All row numbers are relative to the start of the window, except w_winrow. @@ -1163,7 +1205,7 @@ struct window_S { // int w_winrow; // first row of window in screen int w_height; // number of rows in window, excluding - // status/command line(s) + // status/command/winbar line(s) int w_status_height; // number of status lines (0 or 1) int w_wincol; // Leftmost column of window in screen. int w_width; // Width of window, excluding separation. @@ -1271,13 +1313,15 @@ struct window_S { char_u *w_localdir; /* absolute path of local directory or NULL */ - /* - * Options local to a window. - * They are local because they influence the layout of the window or - * depend on the window layout. - * There are two values: w_onebuf_opt is local to the buffer currently in - * this window, w_allbuf_opt is for all buffers in this window. - */ + vimmenu_T *w_winbar; // The root of the WinBar menu hierarchy. + winbar_item_T *w_winbar_items; // list of items in the WinBar + int w_winbar_height; // 1 if there is a window toolbar + + // Options local to a window. + // They are local because they influence the layout of the window or + // depend on the window layout. + // There are two values: w_onebuf_opt is local to the buffer currently in + // this window, w_allbuf_opt is for all buffers in this window. winopt_T w_onebuf_opt; winopt_T w_allbuf_opt; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b8ec94999a..a6162af472 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6305,6 +6305,7 @@ dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) tv_dict_add_nr(dict, S_LEN("winrow"), wp->w_winrow + 1); tv_dict_add_nr(dict, S_LEN("topline"), wp->w_topline); tv_dict_add_nr(dict, S_LEN("botline"), wp->w_botline - 1); + tv_dict_add_nr(dict, S_LEN("winbar"), wp->w_winbar_height); tv_dict_add_nr(dict, S_LEN("width"), wp->w_width); tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum); tv_dict_add_nr(dict, S_LEN("wincol"), wp->w_wincol + 1); @@ -9258,13 +9259,7 @@ void ex_echo(exarg_T *eap) */ void ex_echohl(exarg_T *eap) { - int id; - - id = syn_name2id(eap->arg); - if (id == 0) - echo_attr = 0; - else - echo_attr = syn_id2attr(id); + echo_attr = syn_name2attr(eap->arg); } /* diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 362163aaf0..3f7d616b8f 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -8210,6 +8210,57 @@ void update_topline_cursor(void) update_curswant(); } +// Save the current State and go to Normal mode. +// Return true if the typeahead could be saved. +bool save_current_state(save_state_T *sst) + FUNC_ATTR_NONNULL_ALL +{ + sst->save_msg_scroll = msg_scroll; + sst->save_restart_edit = restart_edit; + sst->save_msg_didout = msg_didout; + sst->save_State = State; + sst->save_insertmode = p_im; + sst->save_finish_op = finish_op; + sst->save_opcount = opcount; + sst->save_reg_executing = reg_executing; + + msg_scroll = false; // no msg scrolling in Normal mode + restart_edit = 0; // don't go to Insert mode + p_im = false; // don't use 'insertmode + + // Save the current typeahead. This is required to allow using ":normal" + // from an event handler and makes sure we don't hang when the argument + // ends with half a command. + save_typeahead(&sst->tabuf); + return sst->tabuf.typebuf_valid; +} + +void restore_current_state(save_state_T *sst) + FUNC_ATTR_NONNULL_ALL +{ + // Restore the previous typeahead. + restore_typeahead(&sst->tabuf); + + msg_scroll = sst->save_msg_scroll; + if (force_restart_edit) { + force_restart_edit = false; + } else { + // Some function (terminal_enter()) was aware of ex_normal and decided to + // override the value of restart_edit anyway. + restart_edit = sst->save_restart_edit; + } + p_im = sst->save_insertmode; + finish_op = sst->save_finish_op; + opcount = sst->save_opcount; + reg_executing = sst->save_reg_executing; + msg_didout |= sst->save_msg_didout; // don't reset msg_didout now + + // Restore the state (needed when called from a function executed for + // 'indentexpr'). Update the mouse and cursor, they may have changed. + State = sst->save_State; + ui_cursor_shape(); // may show different cursor shape +} + /* * ":normal[!] {commands}": Execute normal mode commands. */ @@ -8219,15 +8270,7 @@ static void ex_normal(exarg_T *eap) EMSG("Can't re-enter normal mode from terminal mode"); return; } - int save_msg_scroll = msg_scroll; - int save_restart_edit = restart_edit; - int save_msg_didout = msg_didout; - int save_State = State; - tasave_T tabuf; - int save_insertmode = p_im; - int save_finish_op = finish_op; - long save_opcount = opcount; - const int save_reg_executing = reg_executing; + save_state_T save_state; char_u *arg = NULL; int l; char_u *p; @@ -8240,11 +8283,6 @@ static void ex_normal(exarg_T *eap) EMSG(_("E192: Recursive use of :normal too deep")); return; } - ++ex_normal_busy; - - msg_scroll = FALSE; /* no msg scrolling in Normal mode */ - restart_edit = 0; /* don't go to Insert mode */ - p_im = FALSE; /* don't use 'insertmode' */ /* * vgetc() expects a CSI and K_SPECIAL to have been escaped. Don't do @@ -8278,19 +8316,11 @@ static void ex_normal(exarg_T *eap) } } - /* - * Save the current typeahead. This is required to allow using ":normal" - * from an event handler and makes sure we don't hang when the argument - * ends with half a command. - */ - save_typeahead(&tabuf); - // TODO(philix): after save_typeahead() this is always TRUE - if (tabuf.typebuf_valid) { - /* - * Repeat the :normal command for each line in the range. When no - * range given, execute it just once, without positioning the cursor - * first. - */ + ex_normal_busy++; + if (save_current_state(&save_state)) { + // Repeat the :normal command for each line in the range. When no + // range given, execute it just once, without positioning the cursor + // first. do { if (eap->addr_count != 0) { curwin->w_cursor.lnum = eap->line1++; @@ -8307,29 +8337,12 @@ static void ex_normal(exarg_T *eap) /* Might not return to the main loop when in an event handler. */ update_topline_cursor(); - /* Restore the previous typeahead. */ - restore_typeahead(&tabuf); + restore_current_state(&save_state); - --ex_normal_busy; - msg_scroll = save_msg_scroll; - if (force_restart_edit) { - force_restart_edit = false; - } else { - // Some function (terminal_enter()) was aware of ex_normal and decided to - // override the value of restart_edit anyway. - restart_edit = save_restart_edit; - } - p_im = save_insertmode; - finish_op = save_finish_op; - opcount = save_opcount; - reg_executing = save_reg_executing; - msg_didout |= save_msg_didout; // don't reset msg_didout now + ex_normal_busy--; - /* Restore the state (needed when called from a function executed for - * 'indentexpr'). Update the mouse and cursor, they may have changed. */ - State = save_State; setmouse(); - ui_cursor_shape(); /* may show different cursor shape */ + ui_cursor_shape(); // may show different cursor shape xfree(arg); } diff --git a/src/nvim/ex_docmd.h b/src/nvim/ex_docmd.h index cff350de08..f6bd2adcd5 100644 --- a/src/nvim/ex_docmd.h +++ b/src/nvim/ex_docmd.h @@ -20,6 +20,20 @@ #define EXMODE_NORMAL 1 #define EXMODE_VIM 2 +// Structure used to save the current state. Used when executing Normal mode +// commands while in any other mode. +typedef struct { + int save_msg_scroll; + int save_restart_edit; + int save_msg_didout; + int save_State; + int save_insertmode; + bool save_finish_op; + long save_opcount; + int save_reg_executing; + tasave_T tabuf; +} save_state_T; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_docmd.h.generated.h" #endif diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 0debd39555..5ab5a7db1b 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1216,7 +1216,7 @@ void save_typeahead(tasave_T *tp) { tp->save_typebuf = typebuf; alloc_typebuf(); - tp->typebuf_valid = TRUE; + tp->typebuf_valid = true; tp->old_char = old_char; tp->old_mod_mask = old_mod_mask; old_char = -1; diff --git a/src/nvim/menu.c b/src/nvim/menu.c index a3e0130465..b060464383 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -27,6 +27,8 @@ #include "nvim/strings.h" #include "nvim/ui.h" #include "nvim/eval/typval.h" +#include "nvim/screen.h" +#include "nvim/window.h" #define MENUDEPTH 10 /* maximum depth of menus */ @@ -46,6 +48,24 @@ static char_u e_notsubmenu[] = N_( static char_u e_othermode[] = N_("E328: Menu only exists in another mode"); static char_u e_nomenu[] = N_("E329: No menu \"%s\""); +// Return true if "name" is a window toolbar menu name. +static bool menu_is_winbar(const char_u *const name) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + return (STRNCMP(name, "WinBar", 6) == 0); +} + +int winbar_height(const win_T *const wp) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + return wp->w_winbar != NULL && wp->w_winbar->children != NULL ? 1 : 0; +} + +static vimmenu_T **get_root_menu(const char_u *const name) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + return menu_is_winbar(name) ? &curwin->w_winbar : &root_menu; +} /// Do the :menu command and relatives. /// @param eap Ex command arguments @@ -170,6 +190,12 @@ ex_menu(exarg_T *eap) goto theend; } + vimmenu_T **root_menu_ptr = get_root_menu(menu_path); + if (root_menu_ptr == &curwin->w_winbar) { + // Assume the window toolbar menu will change. + redraw_later(NOT_VALID); + } + if (enable != kNone) { // Change sensitivity of the menu. // For the PopUp menu, remove a menu for each mode separately. @@ -182,11 +208,11 @@ ex_menu(exarg_T *eap) for (i = 0; i < MENU_INDEX_TIP; ++i) if (modes & (1 << i)) { p = popup_mode_name(menu_path, i); - menu_enable_recurse(root_menu, p, MENU_ALL_MODES, enable); + menu_enable_recurse(*root_menu_ptr, p, MENU_ALL_MODES, enable); xfree(p); } } - menu_enable_recurse(root_menu, menu_path, modes, enable); + menu_enable_recurse(*root_menu_ptr, menu_path, modes, enable); } else if (unmenu) { /* * Delete menu(s). @@ -201,13 +227,13 @@ ex_menu(exarg_T *eap) for (i = 0; i < MENU_INDEX_TIP; ++i) if (modes & (1 << i)) { p = popup_mode_name(menu_path, i); - remove_menu(&root_menu, p, MENU_ALL_MODES, TRUE); + remove_menu(root_menu_ptr, p, MENU_ALL_MODES, true); xfree(p); } } - /* Careful: remove_menu() changes menu_path */ - remove_menu(&root_menu, menu_path, modes, FALSE); + // Careful: remove_menu() changes menu_path + remove_menu(root_menu_ptr, menu_path, modes, false); } else { /* * Add menu(s). @@ -244,6 +270,19 @@ ex_menu(exarg_T *eap) xfree(map_buf); } + if (root_menu_ptr == &curwin->w_winbar) { + const int h = winbar_height(curwin); + + if (h != curwin->w_winbar_height) { + if (h == 0) { + curwin->w_height++; + } else if (curwin->w_height > 0) { + curwin->w_height--; + } + curwin->w_winbar_height = h; + } + } + ui_call_update_menu(); theend: @@ -266,7 +305,6 @@ add_menu_path( { char_u *path_name; int modes = menuarg->modes; - vimmenu_T **menup; vimmenu_T *menu = NULL; vimmenu_T *parent; vimmenu_T **lower_pri; @@ -285,7 +323,8 @@ add_menu_path( /* Make a copy so we can stuff around with it, since it could be const */ path_name = vim_strsave(menu_path); - menup = &root_menu; + vimmenu_T **root_menu_ptr = get_root_menu(menu_path); + vimmenu_T **menup = root_menu_ptr; parent = NULL; name = path_name; while (*name) { @@ -468,14 +507,16 @@ erret: /* Delete any empty submenu we added before discovering the error. Repeat * for higher levels. */ while (parent != NULL && parent->children == NULL) { - if (parent->parent == NULL) - menup = &root_menu; - else + if (parent->parent == NULL) { + menup = root_menu_ptr; + } else { menup = &parent->parent->children; - for (; *menup != NULL && *menup != parent; menup = &((*menup)->next)) - ; - if (*menup == NULL) /* safety check */ + } + for (; *menup != NULL && *menup != parent; menup = &((*menup)->next)) { + } + if (*menup == NULL) { // safety check break; + } parent = parent->parent; free_menu(menup); } @@ -620,6 +661,14 @@ remove_menu ( return OK; } +// Remove the WinBar menu from window "wp". +void remove_winbar(win_T *wp) + FUNC_ATTR_NONNULL_ALL +{ + remove_menu(&wp->w_winbar, (char_u *)"", MENU_ALL_MODES, true); + xfree(wp->w_winbar_items); +} + /* * Free the given menu structure and remove it from the linked list. */ @@ -740,7 +789,7 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes) /// @return false if could not find path_name bool menu_get(char_u *const path_name, int modes, list_T *list) { - vimmenu_T *menu = find_menu(root_menu, path_name, modes); + vimmenu_T *menu = find_menu(*get_root_menu(path_name), path_name, modes); if (!menu) { return false; } @@ -802,10 +851,8 @@ static vimmenu_T *find_menu(vimmenu_T *menu, char_u *name, int modes) /// Show the mapping associated with a menu item or hierarchy in a sub-menu. static int show_menus(char_u *const path_name, int modes) { - vimmenu_T *menu; - // First, find the (sub)menu with the given name - menu = find_menu(root_menu, path_name, modes); + vimmenu_T *menu = find_menu(*get_root_menu(path_name), path_name, modes); if (!menu) { return FAIL; } @@ -890,6 +937,7 @@ static void show_menus_recursive(vimmenu_T *menu, int modes, int depth) * Used when expanding menu names. */ static vimmenu_T *expand_menu = NULL; +static vimmenu_T *expand_menu_alt = NULL; static int expand_modes = 0x0; static int expand_emenu; /* TRUE for ":emenu" command */ @@ -940,14 +988,15 @@ char_u *set_context_in_menu_cmd(expand_T *xp, char_u *cmd, char_u *arg, int forc // ":popup" only uses menues, not entries expand_menus = !((*cmd == 't' && cmd[1] == 'e') || *cmd == 'p'); expand_emenu = (*cmd == 'e'); - if (expand_menus && ascii_iswhite(*p)) - return NULL; /* TODO: check for next command? */ - if (*p == NUL) { /* Complete the menu name */ - /* - * With :unmenu, you only want to match menus for the appropriate mode. - * With :menu though you might want to add a menu with the same name as - * one in another mode, so match menus from other modes too. - */ + if (expand_menus && ascii_iswhite(*p)) { + return NULL; // TODO(vim): check for next command? + } + if (*p == NUL) { // Complete the menu name + bool try_alt_menu = true; + + // With :unmenu, you only want to match menus for the appropriate mode. + // With :menu though you might want to add a menu with the same name as + // one in another mode, so match menus from other modes too. expand_modes = get_menu_cmd_modes(cmd, forceit, NULL, &unmenu); if (!unmenu) expand_modes = MENU_ALL_MODES; @@ -976,6 +1025,10 @@ char_u *set_context_in_menu_cmd(expand_T *xp, char_u *cmd, char_u *arg, int forc break; } menu = menu->next; + if (menu == NULL && try_alt_menu) { + menu = curwin->w_winbar; + try_alt_menu = false; + } } if (menu == NULL) { /* No menu found with the name we were looking for */ @@ -984,14 +1037,21 @@ char_u *set_context_in_menu_cmd(expand_T *xp, char_u *cmd, char_u *arg, int forc } name = p; menu = menu->children; + try_alt_menu = false; } xfree(path_name); xp->xp_context = expand_menus ? EXPAND_MENUNAMES : EXPAND_MENUS; xp->xp_pattern = after_dot; expand_menu = menu; - } else /* We're in the mapping part */ + if (expand_menu == root_menu) { + expand_menu_alt = curwin->w_winbar; + } else { + expand_menu_alt = NULL; + } + } else { // We're in the mapping part xp->xp_context = EXPAND_NOTHING; + } return NULL; } @@ -1002,19 +1062,26 @@ char_u *set_context_in_menu_cmd(expand_T *xp, char_u *cmd, char_u *arg, int forc char_u *get_menu_name(expand_T *xp, int idx) { static vimmenu_T *menu = NULL; + static bool did_alt_menu = false; char_u *str; static int should_advance = FALSE; if (idx == 0) { /* first call: start at first item */ menu = expand_menu; - should_advance = FALSE; + did_alt_menu = false; + should_advance = false; } /* Skip PopUp[nvoci]. */ while (menu != NULL && (menu_is_hidden(menu->dname) || menu_is_separator(menu->dname) - || menu->children == NULL)) + || menu->children == NULL)) { menu = menu->next; + if (menu == NULL && !did_alt_menu) { + menu = expand_menu_alt; + did_alt_menu = true; + } + } if (menu == NULL) /* at end of linked list */ return NULL; @@ -1030,9 +1097,14 @@ char_u *get_menu_name(expand_T *xp, int idx) else str = (char_u *)""; - if (should_advance) - /* Advance to next menu entry. */ + if (should_advance) { + // Advance to next menu entry. menu = menu->next; + if (menu == NULL && !did_alt_menu) { + menu = expand_menu_alt; + did_alt_menu = true; + } + } should_advance = !should_advance; @@ -1046,6 +1118,7 @@ char_u *get_menu_name(expand_T *xp, int idx) char_u *get_menu_names(expand_T *xp, int idx) { static vimmenu_T *menu = NULL; + static bool did_alt_menu = false; #define TBUFFER_LEN 256 static char_u tbuffer[TBUFFER_LEN]; /*hack*/ char_u *str; @@ -1053,16 +1126,21 @@ char_u *get_menu_names(expand_T *xp, int idx) if (idx == 0) { /* first call: start at first item */ menu = expand_menu; - should_advance = FALSE; + did_alt_menu = false; + should_advance = false; } /* Skip Browse-style entries, popup menus and separators. */ while (menu != NULL - && ( menu_is_hidden(menu->dname) - || (expand_emenu && menu_is_separator(menu->dname)) - || menu->dname[STRLEN(menu->dname) - 1] == '.' - )) + && (menu_is_hidden(menu->dname) + || (expand_emenu && menu_is_separator(menu->dname)) + || menu->dname[STRLEN(menu->dname) - 1] == '.')) { menu = menu->next; + if (menu == NULL && !did_alt_menu) { + menu = expand_menu_alt; + did_alt_menu = true; + } + } if (menu == NULL) /* at end of linked list */ return NULL; @@ -1092,9 +1170,14 @@ char_u *get_menu_names(expand_T *xp, int idx) } else str = (char_u *)""; - if (should_advance) - /* Advance to next menu entry. */ + if (should_advance) { + // Advance to next menu entry. menu = menu->next; + if (menu == NULL && !did_alt_menu) { + menu = expand_menu_alt; + did_alt_menu = true; + } + } should_advance = !should_advance; @@ -1279,29 +1362,27 @@ static char_u *menu_text(const char_u *str, int *mnemonic, char_u **actext) return text; } -/* - * Return TRUE if "name" can be a menu in the MenuBar. - */ -int menu_is_menubar(char_u *name) +// Return true if "name" can be a menu in the MenuBar. +bool menu_is_menubar(const char_u *const name) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { return !menu_is_popup(name) && !menu_is_toolbar(name) + && !menu_is_winbar(name) && *name != MNU_HIDDEN_CHAR; } -/* - * Return TRUE if "name" is a popup menu name. - */ -int menu_is_popup(char_u *name) +// Return true if "name" is a popup menu name. +bool menu_is_popup(const char_u *const name) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { return STRNCMP(name, "PopUp", 5) == 0; } -/* - * Return TRUE if "name" is a toolbar menu name. - */ -int menu_is_toolbar(char_u *name) +// Return true if "name" is a toolbar menu name. +bool menu_is_toolbar(const char_u *const name) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { return STRNCMP(name, "ToolBar", 7) == 0; } @@ -1325,53 +1406,15 @@ static int menu_is_hidden(char_u *name) || (menu_is_popup(name) && name[5] != NUL); } -/* - * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and - * execute it. - */ -void ex_emenu(exarg_T *eap) +// 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) + FUNC_ATTR_NONNULL_ARG(2) { - vimmenu_T *menu; - char_u *name; - char_u *saved_name; - char_u *p; - int idx; - char_u *mode; - - saved_name = vim_strsave(eap->arg); - - menu = root_menu; - name = saved_name; - while (*name) { - /* Find in the menu hierarchy */ - p = menu_name_skip(name); + int idx = -1; + char_u *mode; - while (menu != NULL) { - if (menu_name_equal(name, menu)) { - if (*p == NUL && menu->children != NULL) { - EMSG(_("E333: Menu path must lead to a menu item")); - menu = NULL; - } else if (*p != NUL && menu->children == NULL) { - EMSG(_(e_notsubmenu)); - menu = NULL; - } - break; - } - menu = menu->next; - } - if (menu == NULL || *p == NUL) - break; - menu = menu->children; - name = p; - } - xfree(saved_name); - if (menu == NULL) { - EMSG2(_("E334: Menu not found: %s"), eap->arg); - return; - } - - /* Found the menu, so execute. - * Use the Insert mode entry when returning to Insert mode. */ + // Use the Insert mode entry when returning to Insert mode. if (((State & INSERT) || restart_edit) && !current_sctx.sc_sid) { mode = (char_u *)"Insert"; idx = MENU_INDEX_INSERT; @@ -1384,7 +1427,7 @@ void ex_emenu(exarg_T *eap) * is. Just execute the visual binding for the menu. */ mode = (char_u *)"Visual"; idx = MENU_INDEX_VISUAL; - } else if (eap->addr_count) { + } else if (eap != NULL && eap->addr_count) { pos_T tpos; mode = (char_u *)"Visual"; @@ -1422,9 +1465,13 @@ void ex_emenu(exarg_T *eap) /* Adjust the cursor to make sure it is in the correct pos * for exclusive mode */ - if (*p_sel == 'e' && gchar_cursor() != NUL) - ++curwin->w_cursor.col; - } else { + if (*p_sel == 'e' && gchar_cursor() != NUL) { + curwin->w_cursor.col++; + } + } + + // For the WinBar menu always use the Normal mode menu. + if (idx == -1 || eap == NULL) { mode = (char_u *)"Normal"; idx = MENU_INDEX_NORMAL; } @@ -1432,19 +1479,114 @@ void ex_emenu(exarg_T *eap) assert(idx != MENU_INDEX_INVALID); if (menu->strings[idx] != NULL) { // When executing a script or function execute the commands right now. + // Also for the window toolbar // Otherwise put them in the typeahead buffer. - if (current_sctx.sc_sid != 0) { - exec_normal_cmd(menu->strings[idx], menu->noremap[idx], - menu->silent[idx]); + if (eap == NULL || current_sctx.sc_sid != 0) { + save_state_T save_state; + + ex_normal_busy++; + if (save_current_state(&save_state)) { + exec_normal_cmd(menu->strings[idx], menu->noremap[idx], + menu->silent[idx]); + } + restore_current_state(&save_state); + ex_normal_busy--; } else { ins_typebuf(menu->strings[idx], menu->noremap[idx], 0, true, menu->silent[idx]); } - } else { + } else if (eap != NULL) { EMSG2(_("E335: Menu not defined for %s mode"), mode); } } +// Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and +// execute it. +void ex_emenu(exarg_T *eap) +{ + char_u *saved_name = vim_strsave(eap->arg); + vimmenu_T *menu = *get_root_menu(saved_name); + char_u *name = saved_name; + while (*name) { + // Find in the menu hierarchy + char_u *p = menu_name_skip(name); + + while (menu != NULL) { + if (menu_name_equal(name, menu)) { + if (*p == NUL && menu->children != NULL) { + EMSG(_("E333: Menu path must lead to a menu item")); + menu = NULL; + } else if (*p != NUL && menu->children == NULL) { + EMSG(_(e_notsubmenu)); + menu = NULL; + } + break; + } + menu = menu->next; + } + if (menu == NULL || *p == NUL) { + break; + } + menu = menu->children; + name = p; + } + xfree(saved_name); + if (menu == NULL) { + EMSG2(_("E334: Menu not found: %s"), eap->arg); + return; + } + + // Found the menu, so execute. + execute_menu(eap, menu); +} + +// Handle a click in the window toolbar of "wp" at column "col". +void winbar_click(win_T *wp, int col) + FUNC_ATTR_NONNULL_ALL +{ + if (wp->w_winbar_items == NULL) { + return; + } + for (int idx = 0; wp->w_winbar_items[idx].wb_menu != NULL; idx++) { + winbar_item_T *item = &wp->w_winbar_items[idx]; + + if (col >= item->wb_startcol && col <= item->wb_endcol) { + win_T *save_curwin = NULL; + const pos_T save_visual = VIsual; + const int save_visual_active = VIsual_active; + const int save_visual_select = VIsual_select; + const int save_visual_reselect = VIsual_reselect; + const int save_visual_mode = VIsual_mode; + + if (wp != curwin) { + // Clicking in the window toolbar of a not-current window. + // Make that window the current one and save Visual mode. + save_curwin = curwin; + VIsual_active = false; + curwin = wp; + curbuf = curwin->w_buffer; + check_cursor(); + } + + // Note: the command might close the current window. + execute_menu(NULL, item->wb_menu); + + if (save_curwin != NULL && win_valid(save_curwin)) { + curwin = save_curwin; + curbuf = curwin->w_buffer; + VIsual = save_visual; + VIsual_active = save_visual_active; + VIsual_select = save_visual_select; + VIsual_reselect = save_visual_reselect; + VIsual_mode = save_visual_mode; + } + if (!win_valid(wp)) { + break; + } + } + } +} + /* * Translation of menu names. Just a simple lookup table. */ diff --git a/src/nvim/menu.h b/src/nvim/menu.h index 5ff979f2bf..642d9aafac 100644 --- a/src/nvim/menu.h +++ b/src/nvim/menu.h @@ -6,18 +6,6 @@ #include "nvim/types.h" // for char_u and expand_T #include "nvim/ex_cmds_defs.h" // for exarg_T -/// Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode -/// \addtogroup MENU_INDEX -/// @{ -#define MENU_INDEX_INVALID -1 -#define MENU_INDEX_NORMAL 0 -#define MENU_INDEX_VISUAL 1 -#define MENU_INDEX_SELECT 2 -#define MENU_INDEX_OP_PENDING 3 -#define MENU_INDEX_INSERT 4 -#define MENU_INDEX_CMDLINE 5 -#define MENU_INDEX_TIP 6 -#define MENU_MODES 7 /// @} /// note MENU_INDEX_TIP is not a 'real' mode @@ -37,28 +25,6 @@ /// Start a menu name with this to not include it on the main menu bar #define MNU_HIDDEN_CHAR ']' -typedef struct VimMenu vimmenu_T; - -struct VimMenu { - int modes; ///< Which modes is this menu visible for - int enabled; ///< for which modes the menu is enabled - char_u *name; ///< Name of menu, possibly translated - char_u *dname; ///< Displayed Name ("name" without '&') - char_u *en_name; ///< "name" untranslated, NULL when - ///< was not translated - char_u *en_dname; ///< NULL when "dname" untranslated - int mnemonic; ///< mnemonic key (after '&') - char_u *actext; ///< accelerator text (after TAB) - long priority; ///< Menu order priority - char_u *strings[MENU_MODES]; ///< Mapped string for each mode - int noremap[MENU_MODES]; ///< A \ref REMAP_VALUES flag for each mode - bool silent[MENU_MODES]; ///< A silent flag for each mode - vimmenu_T *children; ///< Children of sub-menu - vimmenu_T *parent; ///< Parent of menu - vimmenu_T *next; ///< Next item in menu -}; - - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "menu.h.generated.h" #endif diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 32d8352d9b..fcd9ee4f75 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -60,6 +60,7 @@ int jump_to_mouse(int flags, { static int on_status_line = 0; // #lines below bottom of window static int on_sep_line = 0; // on separator right of window + static bool in_winbar = false; static int prev_row = -1; static int prev_col = -1; static win_T *dragwin = NULL; // window being dragged @@ -93,10 +94,24 @@ int jump_to_mouse(int flags, retnomove: // before moving the cursor for a left click which is NOT in a status // line, stop Visual mode - if (on_status_line) + if (on_status_line) { return IN_STATUS_LINE; - if (on_sep_line) + } + if (on_sep_line) { return IN_SEP_LINE; + } + if (in_winbar) { + // A quick second click may arrive as a double-click, but we use it + // as a second click in the WinBar. + if ((mod_mask & MOD_MASK_MULTI_CLICK) && !(flags & MOUSE_RELEASED)) { + wp = mouse_find_win(&grid, &row, &col); + if (wp == NULL) { + return IN_UNKNOWN; + } + winbar_click(wp, col); + } + return IN_OTHER_WIN | MOUSE_WINBAR; + } if (flags & MOUSE_MAY_STOP_VIS) { end_visual_mode(); redraw_curbuf_later(INVERTED); // delete the inversion @@ -134,6 +149,16 @@ retnomove: } fdc = win_fdccol_count(wp); dragwin = NULL; + + if (row == -1) { + // A click in the window toolbar does not enter another window or + // change Visual highlighting. + winbar_click(wp, col); + in_winbar = true; + return IN_OTHER_WIN | MOUSE_WINBAR; + } + in_winbar = false; + // winpos and height may change in win_enter()! if (grid == DEFAULT_GRID_HANDLE && row >= wp->w_height) { // In (or below) status line @@ -223,6 +248,9 @@ retnomove: did_drag |= count; } return IN_SEP_LINE; // Cursor didn't move + } else if (in_winbar) { + // After a click on the window toolbar don't start Visual mode. + return IN_OTHER_WIN | MOUSE_WINBAR; } else { // keep_window_focus must be true // before moving the cursor for a left click, stop Visual mode @@ -471,6 +499,7 @@ win_T *mouse_find_win(int *gridp, int *rowp, int *colp) // exist. FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp == fp->fr_win) { + *rowp -= wp->w_winbar_height; return wp; } } diff --git a/src/nvim/mouse.h b/src/nvim/mouse.h index 0149f7c7c0..6c5bc5dc0e 100644 --- a/src/nvim/mouse.h +++ b/src/nvim/mouse.h @@ -16,6 +16,7 @@ #define CURSOR_MOVED 0x100 #define MOUSE_FOLD_CLOSE 0x200 // clicked on '-' in fold column #define MOUSE_FOLD_OPEN 0x400 // clicked on '+' in fold column +#define MOUSE_WINBAR 0x800 // in window toolbar // flags for jump_to_mouse() #define MOUSE_FOCUS 0x01 // need to stay in this window diff --git a/src/nvim/normal.c b/src/nvim/normal.c index fbd941c5fe..87d687198d 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2561,7 +2561,14 @@ do_mouse ( * JUMP! */ jump_flags = jump_to_mouse(jump_flags, - oap == NULL ? NULL : &(oap->inclusive), which_button); + oap == NULL ? NULL : &(oap->inclusive), + which_button); + + // A click in the window toolbar has no side effects. + if (jump_flags & MOUSE_WINBAR) { + return false; + } + moved = (jump_flags & CURSOR_MOVED); in_status_line = (jump_flags & IN_STATUS_LINE); in_sep_line = (jump_flags & IN_SEP_LINE); diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 9c590efdbc..45c56549c5 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1741,6 +1741,31 @@ static int advance_color_col(int vcol, int **color_cols) return **color_cols >= 0; } +// Returns the next grid column. +static int text_to_screenline(win_T *wp, char_u *text, int col, int off) + FUNC_ATTR_NONNULL_ALL +{ + int idx = wp->w_p_rl ? off : off + col; + LineState s = LINE_STATE(text); + + while (*s.p != NUL) { + // TODO(bfredl): cargo-culted from the old Vim code: + // if(col + cells > wp->w_width - (wp->w_p_rl ? col : 0)) { break; } + // This is obvious wrong. If Vim ever fixes this, solve for "cells" again + // in the correct condition. + const int maxcells = wp->w_grid.Columns - col - (wp->w_p_rl ? col : 0); + const int cells = line_putchar(&s, &linebuf_char[idx], maxcells, + wp->w_p_rl); + if (cells == -1) { + break; + } + col += cells; + idx += cells; + } + + return col; +} + // Compute the width of the foldcolumn. Based on 'foldcolumn' and how much // space is available for window "wp", minus "col". static int compute_foldcolumn(win_T *wp, int col) @@ -1942,29 +1967,7 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T // 5. move the text to linebuf_char[off]. Fill up with "fold". // Right-left text is put in columns 0 - number-col, normal text is put // in columns number-col - window-width. - int idx; - - if (wp->w_p_rl) { - idx = off; - } else { - idx = off + col; - } - - LineState s = LINE_STATE(text); - - while (*s.p != NUL) { - // TODO(bfredl): cargo-culted from the old Vim code: - // if(col + cells > wp->w_width - (wp->w_p_rl ? col : 0)) { break; } - // This is obvious wrong. If Vim ever fixes this, solve for "cells" again - // in the correct condition. - int maxcells = wp->w_grid.Columns - col - (wp->w_p_rl ? col : 0); - int cells = line_putchar(&s, &linebuf_char[idx], maxcells, wp->w_p_rl); - if (cells == -1) { - break; - } - col += cells; - idx += cells; - } + col = text_to_screenline(wp, text, col, off); /* Fill the rest of the line with the fold filler */ if (wp->w_p_rl) diff --git a/src/nvim/window.c b/src/nvim/window.c index a89715ff79..0fff93d984 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1112,7 +1112,8 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) // add a status line when p_ls == 1 and splitting the first window if (one_nonfloat() && p_ls == 1 && oldwin->w_status_height == 0) { - if (oldwin->w_height <= p_wmh && new_in_layout) { + if ((oldwin->w_height + oldwin->w_winbar_height) <= p_wmh + && new_in_layout) { EMSG(_(e_noroom)); return FAIL; } @@ -1209,7 +1210,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) * height. */ // Current window requires at least 1 space. - wmh1 = (p_wmh == 0 ? 1 : p_wmh); + wmh1 = (p_wmh == 0 ? 1 : p_wmh) + curwin->w_winbar_height; needed = wmh1 + STATUS_HEIGHT; if (flags & WSP_ROOM) { needed += p_wh - wmh1; @@ -1407,12 +1408,12 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) if (flags & (WSP_TOP | WSP_BOT)) { /* set height and row of new window to full height */ wp->w_winrow = tabline_height(); - win_new_height(wp, curfrp->fr_height - (p_ls > 0)); + win_new_height(wp, curfrp->fr_height - (p_ls > 0) - wp->w_winbar_height); wp->w_status_height = (p_ls > 0); } else { /* height and row of new window is same as current window */ wp->w_winrow = oldwin->w_winrow; - win_new_height(wp, oldwin->w_height); + win_new_height(wp, oldwin->w_height + oldwin->w_winbar_height); wp->w_status_height = oldwin->w_status_height; } frp->fr_height = curfrp->fr_height; @@ -1459,7 +1460,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) * one row for the status line */ win_new_height(wp, new_size); if (flags & (WSP_TOP | WSP_BOT)) { - int new_fr_height = curfrp->fr_height - new_size; + int new_fr_height = curfrp->fr_height - new_size + wp->w_winbar_height; if (!((flags & WSP_BOT) && p_ls == 0)) { new_fr_height -= STATUS_HEIGHT; @@ -1472,8 +1473,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) wp->w_winrow = oldwin->w_winrow; wp->w_status_height = STATUS_HEIGHT; oldwin->w_winrow += wp->w_height + STATUS_HEIGHT; - } else { /* new window below current one */ - wp->w_winrow = oldwin->w_winrow + oldwin->w_height + STATUS_HEIGHT; + } else { // new window below current one + wp->w_winrow = oldwin->w_winrow + oldwin->w_height + + STATUS_HEIGHT + oldwin->w_winbar_height; wp->w_status_height = oldwin->w_status_height; if (!(flags & WSP_BOT)) { oldwin->w_status_height = STATUS_HEIGHT; @@ -1689,8 +1691,9 @@ make_windows ( maxcount = (curwin->w_width + curwin->w_vsep_width - (p_wiw - p_wmw)) / (p_wmw + 1); } else { - /* Each window needs at least 'winminheight' lines and a status line. */ - maxcount = (curwin->w_height + curwin->w_status_height + // Each window needs at least 'winminheight' lines and a status line. + maxcount = (curwin->w_height + curwin->w_winbar_height + + curwin->w_status_height - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT); } @@ -3144,15 +3147,18 @@ frame_new_height ( int wfh /* obey 'winfixheight' when there is a choice; may cause the height not to be set */ ) + FUNC_ATTR_NONNULL_ALL { frame_T *frp; int extra_lines; int h; if (topfrp->fr_win != NULL) { - /* Simple case: just one window. */ + // Simple case: just one window. win_new_height(topfrp->fr_win, - height - topfrp->fr_win->w_status_height); + height + - topfrp->fr_win->w_status_height + - topfrp->fr_win->w_winbar_height); } else if (topfrp->fr_layout == FR_ROW) { do { // All frames in this row get the same new height. @@ -3457,8 +3463,10 @@ static void frame_fix_width(win_T *wp) * Set frame height from the window it contains. */ static void frame_fix_height(win_T *wp) + FUNC_ATTR_NONNULL_ALL { - wp->w_frame->fr_height = wp->w_height + wp->w_status_height; + wp->w_frame->fr_height = + wp->w_height + wp->w_status_height + wp->w_winbar_height; } /* @@ -3475,14 +3483,18 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin) int n; if (topfrp->fr_win != NULL) { - if (topfrp->fr_win == next_curwin) + if (topfrp->fr_win == next_curwin) { m = p_wh + topfrp->fr_win->w_status_height; - else { - /* window: minimal height of the window plus status line */ + } else { + // window: minimal height of the window plus status line m = p_wmh + topfrp->fr_win->w_status_height; - /* Current window is minimal one line high */ - if (p_wmh == 0 && topfrp->fr_win == curwin && next_curwin == NULL) - ++m; + if (topfrp->fr_win == curwin && next_curwin == NULL) { + // Current window is minimal one line high and WinBar is visible. + if (p_wmh == 0) { + m++; + } + m += curwin->w_winbar_height; + } } } else if (topfrp->fr_layout == FR_ROW) { /* get the minimal height from each frame in this row */ @@ -4782,6 +4794,7 @@ win_free ( qf_free_all(wp); + remove_winbar(wp); xfree(wp->w_p_cc_cols); @@ -5048,7 +5061,9 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col) wp->w_redr_status = true; wp->w_pos_changed = true; } - *row += wp->w_height + wp->w_status_height; + // WinBar will not show if the window height is zero + const int h = wp->w_height + wp->w_winbar_height + wp->w_status_height; + *row += h > topfrp->fr_height ? topfrp->fr_height : h; *col += wp->w_width + wp->w_vsep_width; } else { startrow = *row; @@ -5081,12 +5096,15 @@ void win_setheight(int height) void win_setheight_win(int height, win_T *win) { if (win == curwin) { - /* Always keep current window at least one line high, even when - * 'winminheight' is zero. */ - if (height < p_wmh) + // Always keep current window at least one line high, even when + // 'winminheight' is zero. + if (height < p_wmh) { height = p_wmh; - if (height == 0) + } + if (height == 0) { height = 1; + } + height += curwin->w_winbar_height; } if (win->w_floating) { @@ -5183,7 +5201,7 @@ static void frame_setheight(frame_T *curfrp, int height) } else { win_T *wp = lastwin_nofloating(); room_cmdline = Rows - p_ch - (wp->w_winrow - + wp->w_height + + + wp->w_height + wp->w_winbar_height + wp->w_status_height); if (room_cmdline < 0) { room_cmdline = 0; @@ -5708,11 +5726,10 @@ void set_fraction(win_T *wp) } } -/* - * Set the height of a window. - * This takes care of the things inside the window, not what happens to the - * window position, the frame or to other windows. - */ +// Set the height of a window. +// "height" excludes any window toolbar. +// This takes care of the things inside the window, not what happens to the +// window position, the frame or to other windows. void win_new_height(win_T *wp, int height) { // Don't want a negative height. Happens when splitting a tiny window. |