mirror of
https://github.com/neovim/neovim.git
synced 2025-09-20 10:18:18 +00:00
vim-patch:8.0.1123: cannot define a toolbar for a window
Problem: Cannot define a toolbar for a window.
Solution: Add a window-local toolbar.
1b9645de3c
"WinBar" code in "screen.c" was not ported.
Fix https://github.com/neovim/neovim/issues/11513#issuecomment-562012827
This commit is contained in:
285
src/nvim/menu.c
285
src/nvim/menu.c
@@ -27,6 +27,7 @@
|
||||
#include "nvim/strings.h"
|
||||
#include "nvim/ui.h"
|
||||
#include "nvim/eval/typval.h"
|
||||
#include "nvim/screen.h"
|
||||
|
||||
#define MENUDEPTH 10 /* maximum depth of menus */
|
||||
|
||||
@@ -46,6 +47,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", 5) == 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 +189,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 +207,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 +226,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 +269,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 +304,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 +322,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 +506,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 +660,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 +788,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 +850,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 +936,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 +987,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 +1024,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 +1036,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 +1061,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 +1096,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 +1117,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 +1125,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 +1169,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;
|
||||
|
||||
@@ -1325,53 +1407,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;
|
||||
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);
|
||||
|
||||
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 +1428,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";
|
||||
@@ -1432,19 +1476,92 @@ 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) {
|
||||
if (eap == NULL || current_sctx.sc_sid != 0) {
|
||||
exec_normal_cmd(menu->strings[idx], menu->noremap[idx],
|
||||
menu->silent[idx]);
|
||||
} 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;
|
||||
|
||||
if (wp != curwin) {
|
||||
// Clicking in the window toolbar of a not-current window.
|
||||
// Make that window the current one and go to Normal mode.
|
||||
save_curwin = curwin;
|
||||
curwin = wp;
|
||||
curbuf = curwin->w_buffer;
|
||||
check_cursor();
|
||||
}
|
||||
|
||||
execute_menu(NULL, item->wb_menu);
|
||||
|
||||
if (save_curwin != NULL) {
|
||||
curwin = save_curwin;
|
||||
curbuf = curwin->w_buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Translation of menu names. Just a simple lookup table.
|
||||
*/
|
||||
|
Reference in New Issue
Block a user