diff options
-rw-r--r-- | runtime/doc/ui.txt | 18 | ||||
-rw-r--r-- | runtime/doc/windows.txt | 4 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 4 | ||||
-rw-r--r-- | src/nvim/api/ui.c | 2 | ||||
-rw-r--r-- | src/nvim/api/ui_events.in.h | 9 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 78 | ||||
-rw-r--r-- | src/nvim/api/window.c | 38 | ||||
-rw-r--r-- | src/nvim/buffer.c | 8 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 31 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 12 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 10 | ||||
-rw-r--r-- | src/nvim/globals.h | 4 | ||||
-rw-r--r-- | src/nvim/grid_defs.h | 7 | ||||
-rw-r--r-- | src/nvim/main.c | 1 | ||||
-rw-r--r-- | src/nvim/memory.c | 7 | ||||
-rw-r--r-- | src/nvim/message.c | 8 | ||||
-rw-r--r-- | src/nvim/mouse.c | 31 | ||||
-rw-r--r-- | src/nvim/normal.c | 2 | ||||
-rw-r--r-- | src/nvim/os/input.c | 1 | ||||
-rw-r--r-- | src/nvim/popupmnu.c | 25 | ||||
-rw-r--r-- | src/nvim/popupmnu.h | 3 | ||||
-rw-r--r-- | src/nvim/screen.c | 77 | ||||
-rw-r--r-- | src/nvim/tui/tui.c | 1 | ||||
-rw-r--r-- | src/nvim/ui.c | 32 | ||||
-rw-r--r-- | src/nvim/ui.h | 3 | ||||
-rw-r--r-- | src/nvim/ui_compositor.c | 113 | ||||
-rw-r--r-- | src/nvim/window.c | 652 | ||||
-rw-r--r-- | test/functional/ui/float_spec.lua | 3030 | ||||
-rw-r--r-- | test/functional/ui/screen.lua | 70 | ||||
-rw-r--r-- | test/helpers.lua | 4 |
30 files changed, 4067 insertions, 218 deletions
diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index 77a829b150..270c4fb556 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -520,19 +520,33 @@ tabs. size). If the window was previously hidden, it should now be shown again. +["win_float_pos", grid, win, anchor, anchor_grid, anchor_row, anchor_col, focusable] + Display or reconfigure floating window `win`. The window should be + displayed above another grid `anchor_grid` at the specified position + `anchor_row` and `anchor_col`. For the meaning of `anchor` and more + details of positioning, see |nvim_open_win()|. + +["win_external_pos", grid, win] + Display or reconfigure external window `win`. The window should be + displayed as a separate top-level window in the desktop envirionment, + or something similar. + ["win_hide", grid] - Stop displaying the window. + Stop displaying the window. The window can be shown again later. ["win_scroll_over_start"] Hint that following `grid_scroll` on the default grid should scroll over windows. This is a temporary workaround to allow UIs to use the builtin message drawing. Later on, messages will be - drawn on a dedicated grid. + drawn on a dedicated grid. Using |ui-messages| also avoids this issue. ["win_scroll_over_reset"] Hint that scrolled over windows should be redrawn again, and not be overdrawn by default grid scrolling anymore. +["win_close", grid] + Close the window. + See |ui-linegrid| for grid events. See |nvim_ui_try_resize_grid| in |api-ui| to request changing the grid size. See |nvim_input_mouse| for sending mouse events to Nvim. diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index 2e4b6f6e76..6e1ad0f1f4 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -197,6 +197,10 @@ CTRL-W ^ Does ":split #", split window in two and edit alternate file. When a count is given, it becomes ":split #N", split window and edit buffer N. +CTRL-W ge *CTRL-W_ge* + Detach the current window as an external window. + Only available when using an UI with |ui-multigrid| support. + Note that the 'splitbelow' and 'splitright' options influence where a new window will appear. diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 19a3368c1c..c2b382804d 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1004,7 +1004,9 @@ static void init_ui_event_metadata(Dictionary *metadata) Array ui_options = ARRAY_DICT_INIT; ADD(ui_options, STRING_OBJ(cstr_to_string("rgb"))); for (UIExtension i = 0; i < kUIExtCount; i++) { - ADD(ui_options, STRING_OBJ(cstr_to_string(ui_ext_names[i]))); + if (ui_ext_names[i][0] != '_') { + ADD(ui_options, STRING_OBJ(cstr_to_string(ui_ext_names[i]))); + } } PUT(*metadata, "ui_options", ARRAY_OBJ(ui_options)); } diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 9e9be588e3..d3cbb46dad 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -17,6 +17,8 @@ #include "nvim/popupmnu.h" #include "nvim/cursor_shape.h" #include "nvim/highlight.h" +#include "nvim/screen.h" +#include "nvim/window.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/ui.c.generated.h" diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index b57cf8d3ef..b89c5b6014 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -76,7 +76,7 @@ void hl_attr_define(Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, void grid_resize(Integer grid, Integer width, Integer height) FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL; void grid_clear(Integer grid) - FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL; + FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL; void grid_cursor_goto(Integer grid, Integer row, Integer col) FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL; void grid_line(Integer grid, Integer row, Integer col_start, Array data) @@ -101,8 +101,15 @@ void event(char *name, Array args, bool *args_consumed) void win_pos(Integer grid, Integer win, Integer startrow, Integer startcol, Integer width, Integer height) FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; +void win_float_pos(Integer grid, Window win, String anchor, Integer anchor_grid, + Float anchor_row, Float anchor_col, Boolean focusable) + FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; +void win_external_pos(Integer grid, Window win) + FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; void win_hide(Integer grid) FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; +void win_close(Integer grid) + FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY; void win_scroll_over_start(void) FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL FUNC_API_COMPOSITOR_IMPL; void win_scroll_over_reset(void) diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 5a4d0a11e7..94d1697083 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -987,6 +987,84 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err) return buf->b_fnum; } +/// Open a new window. +/// +/// Currently this is used to open floating and external windows. +/// Floats are windows that are drawn above the split layout, at some anchor +/// position in some other window. Floats can be draw internally or by external +/// GUI with the |ui-multigrid| extension. External windows are only supported +/// with multigrid GUIs, and are displayed as separate top-level windows. +/// +/// Exactly one of `external` and `relative` must be specified. +/// +/// @param buffer handle of buffer to be displayed in the window +/// @param enter whether the window should be entered (made the current window) +/// @param width width of window (in character cells) +/// @param height height of window (in character cells) +/// @param options dict of options for configuring window positioning +/// accepts the following keys: +/// `relative`: If set, the window becomes a floating window. The window +/// will be placed with row,col coordinates relative one of the +/// following: +/// "editor" the global editor grid +/// "win" a window. Use 'win' option below to specify window id, +/// or current window will be used by default. +/// "cursor" the cursor position in current window. +/// `anchor`: the corner of the float that the row,col position defines +/// "NW" north-west (default) +/// "NE" north-east +/// "SW" south-west +/// "SE" south-east +/// `focusable`: Whether window can be focused by wincmds and +/// mouse events. Defaults to true. Even if set to false, the window +/// can still be entered using |nvim_set_current_win()| API call. +/// `row`: row position. Screen cell height are used as unit. Can be +/// floating point. +/// `col`: column position. Screen cell width is used as unit. Can be +/// floating point. +/// `win`: when using relative='win', window id of the window where the +/// position is defined. +/// `external` GUI should display the window as an external +/// top-level window. Currently accepts no other positioning options +/// together with this. +/// +/// With editor positioning row=0, col=0 refers to the top-left corner of the +/// screen-grid and row=Lines-1, Columns-1 refers to the bottom-right corner. +/// Floating point values are allowed, but the builtin implementation (used by +/// TUI and GUIs without multigrid support) will always round down to nearest +/// integer. +/// +/// Out-of-bounds values, and configurations that make the float not fit inside +/// the main editor, are allowed. The builtin implementation will truncate +/// values so floats are completely within the main screen grid. External GUIs +/// could let floats hover outside of the main window like a tooltip, but +/// this should not be used to specify arbitrary WM screen positions. +/// +/// @param[out] err Error details, if any +/// @return the window handle or 0 when error +Window nvim_open_win(Buffer buffer, Boolean enter, + Integer width, Integer height, + Dictionary options, Error *err) + FUNC_API_SINCE(6) +{ + win_T *old = curwin; + FloatConfig config = FLOAT_CONFIG_INIT; + if (!parse_float_config(options, &config, false, err)) { + return 0; + } + win_T *wp = win_new_float(NULL, (int)width, (int)height, config, err); + if (!wp) { + return 0; + } + if (buffer > 0) { + nvim_set_current_buf(buffer, err); + } + if (!enter) { + win_enter(old, false); + } + return wp->handle; +} + /// Gets the current list of tabpage handles. /// /// @return List of tabpage handles diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 33857f95b7..e1d84cfc9e 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -432,3 +432,41 @@ Boolean nvim_win_is_valid(Window window) return ret; } + +/// Configure window position. Currently this is only used to configure +/// floating and external windows (including changing a split window to these +/// types). +/// +/// See documentation at |nvim_open_win()|, for the meaning of parameters. Pass +/// in -1 for 'witdh' and 'height' to keep exiting size. +/// +/// When reconfiguring a floating window, absent option keys will not be +/// changed. The following restriction apply: `row`, `col` and `relative` +/// must be reconfigured together. Only changing a subset of these is an error. +void nvim_win_config(Window window, Integer width, Integer height, + Dictionary options, Error *err) + FUNC_API_SINCE(6) +{ + win_T *win = find_window_by_handle(window, err); + if (!win) { + return; + } + bool new_float = !win->w_floating; + width = width > 0 ? width: win->w_width; + height = height > 0 ? height : win->w_height; + // reuse old values, if not overriden + FloatConfig config = new_float ? FLOAT_CONFIG_INIT : win->w_float_config; + + if (!parse_float_config(options, &config, !new_float, err)) { + return; + } + if (new_float) { + if (!win_new_float(win, (int)width, (int)height, config, err)) { + return; + } + redraw_later(NOT_VALID); + } else { + win_config_float(win, (int)width, (int)height, config); + win->w_pos_changed = true; + } +} diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 6ae239c9c3..6e4e7afeb2 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -474,8 +474,8 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) return; } buf->b_locked--; - if (abort_if_last && one_window()) { - /* Autocommands made this the only window. */ + if (abort_if_last && last_nonfloat(win)) { + // Autocommands made this the only window. EMSG(_(e_auabort)); return; } @@ -491,8 +491,8 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) return; } buf->b_locked--; - if (abort_if_last && one_window()) { - /* Autocommands made this the only window. */ + if (abort_if_last && last_nonfloat(win)) { + // Autocommands made this the only window. EMSG(_(e_auabort)); return; } diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index da1e3ce9c6..48cef9b1e7 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -958,6 +958,35 @@ struct matchitem { int conceal_char; ///< cchar for Conceal highlighting }; +typedef enum { + kFloatAnchorEast = 1, + kFloatAnchorSouth = 2, + + kFloatAnchorNW = 0, + kFloatAnchorNE = 1, + kFloatAnchorSW = 2, + kFloatAnchorSE = 3, +} FloatAnchor; + +typedef enum { + kFloatRelativeEditor = 0, + kFloatRelativeWindow = 1, + kFloatRelativeCursor = 2, +} FloatRelative; + +typedef struct { + Window window; + double row, col; + FloatAnchor anchor; + FloatRelative relative; + bool external; + bool focusable; +} FloatConfig; + +#define FLOAT_CONFIG_INIT ((FloatConfig){ .row = 0, .col = 0, .anchor = 0, \ + .relative = 0, .external = false, \ + .focusable = true }) + /* * Structure which contains all information that belongs to a window * @@ -1221,6 +1250,8 @@ struct window_S { ScreenGrid w_grid; // the grid specific to the window bool w_pos_changed; // true if window position changed + bool w_floating; ///< whether the window is floating + FloatConfig w_float_config; /* * w_fraction is the fractional row of the cursor within the window, from diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index bbec977c74..8f9af1c2ec 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -6279,6 +6279,9 @@ void tabpage_close(int forceit) { // First close all the windows but the current one. If that worked then // close the last window in this tab, that will close it. + while (curwin->w_floating) { + ex_win_close(forceit, curwin, NULL); + } if (!ONE_WINDOW) { close_others(true, forceit); } @@ -6303,8 +6306,8 @@ void tabpage_close_other(tabpage_T *tp, int forceit) /* Limit to 1000 windows, autocommands may add a window while we close * one. OK, so I'm paranoid... */ while (++done < 1000) { - sprintf((char *)prev_idx, "%i", tabpage_index(tp)); - wp = tp->tp_firstwin; + snprintf((char *)prev_idx, sizeof(prev_idx), "%i", tabpage_index(tp)); + wp = tp->tp_lastwin; ex_win_close(forceit, wp, tp); /* Autocommands may delete the tab page under our fingers and we may @@ -6325,6 +6328,7 @@ static void ex_only(exarg_T *eap) { win_T *wp; int wnr; + if (eap->addr_count > 0) { wnr = eap->line2; for (wp = firstwin; --wnr > 0;) { @@ -6333,6 +6337,10 @@ static void ex_only(exarg_T *eap) else wp = wp->w_next; } + } else { + wp = curwin; + } + if (wp != curwin) { win_goto(wp); } close_others(TRUE, eap->forceit); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 27bd23a668..a50e18efce 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -3470,11 +3470,13 @@ void redrawcmd(void) void compute_cmdrow(void) { - if (exmode_active || msg_scrolled != 0) + if (exmode_active || msg_scrolled != 0) { cmdline_row = Rows - 1; - else - cmdline_row = lastwin->w_winrow + lastwin->w_height - + lastwin->w_status_height; + } else { + win_T *wp = lastwin_nofloating(); + cmdline_row = wp->w_winrow + wp->w_height + + wp->w_status_height; + } } static void cursorcmd(void) diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 1e2c6eea59..52c5d65512 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -1048,6 +1048,10 @@ EXTERN char_u e_cmdmap_repeated[] INIT(=N_( "E5521: <Cmd> mapping must end with <CR> before second <Cmd>")); EXTERN char_u e_cmdmap_key[] INIT(=N_( "E5522: <Cmd> mapping must not include %s key")); +EXTERN char_u e_floatonly[] INIT(=N_( + "E5601: Cannot close window, only floating window would remain")); +EXTERN char_u e_floatexchange[] INIT(=N_( + "E5602: Cannot exchange or rotate float")); EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM")); diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h index d9ccdbbdab..38fc513baa 100644 --- a/src/nvim/grid_defs.h +++ b/src/nvim/grid_defs.h @@ -47,6 +47,9 @@ typedef struct { int Rows; int Columns; + // The state of the grid is valid. Otherwise it needs to be redrawn. + bool valid; + // offsets for the grid relative to the global screen int row_offset; int col_offset; @@ -58,7 +61,7 @@ typedef struct { bool comp_disabled; } ScreenGrid; -#define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, \ - false } +#define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, 0, 0, false, 0, 0, 0, \ + 0, 0, false } #endif // NVIM_GRID_DEFS_H diff --git a/src/nvim/main.c b/src/nvim/main.c index 79513b1c21..5dffca95a2 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -46,6 +46,7 @@ #include "nvim/os/os_defs.h" #include "nvim/path.h" #include "nvim/profile.h" +#include "nvim/popupmnu.h" #include "nvim/quickfix.h" #include "nvim/screen.h" #include "nvim/state.h" diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 0f402611df..4ed816b157 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -572,8 +572,13 @@ void free_all_mem(void) p_ea = false; if (first_tabpage->tp_next != NULL) do_cmdline_cmd("tabonly!"); - if (!ONE_WINDOW) + + if (!ONE_WINDOW) { + // to keep things simple, don't perform this + // ritual inside a float + curwin = firstwin; do_cmdline_cmd("only!"); + } /* Free all spell info. */ spell_free_all(); diff --git a/src/nvim/message.c b/src/nvim/message.c index 9a0a3565ea..b4aa333a48 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -2771,9 +2771,11 @@ void msg_ext_flush_showmode(void) { // Showmode messages doesn't interrupt normal message flow, so we use // separate event. Still reuse the same chunking logic, for simplicity. - msg_ext_emit_chunk(); - ui_call_msg_showmode(msg_ext_chunks); - msg_ext_chunks = (Array)ARRAY_DICT_INIT; + if (ui_has(kUIMessages)) { + msg_ext_emit_chunk(); + ui_call_msg_showmode(msg_ext_chunks); + msg_ext_chunks = (Array)ARRAY_DICT_INIT; + } } void msg_ext_clear(bool force) diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 50dba92eca..6d1a517ce8 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -12,6 +12,7 @@ #include "nvim/screen.h" #include "nvim/syntax.h" #include "nvim/ui.h" +#include "nvim/ui_compositor.h" #include "nvim/os_unix.h" #include "nvim/fold.h" #include "nvim/diff.h" @@ -441,12 +442,6 @@ win_T *mouse_find_win(int *gridp, int *rowp, int *colp) return wp_grid; } - // TODO(bfredl): grid zero will have floats displayed on it, and will - // be adjusted to float grids. - if (*gridp == 0) { - *gridp = DEFAULT_GRID_HANDLE; - } - frame_T *fp; fp = topframe; @@ -478,15 +473,31 @@ win_T *mouse_find_win(int *gridp, int *rowp, int *colp) return NULL; } -static win_T *mouse_find_grid_win(int *grid, int *rowp, int *colp) +static win_T *mouse_find_grid_win(int *gridp, int *rowp, int *colp) { - if (*grid > 1) { - win_T *wp = get_win_by_grid_handle(*grid); - if (wp && wp->w_grid.chars) { + if (*gridp > 1) { + win_T *wp = get_win_by_grid_handle(*gridp); + if (wp && wp->w_grid.chars + && !(wp->w_floating && !wp->w_float_config.focusable)) { *rowp = MIN(*rowp, wp->w_grid.Rows-1); *colp = MIN(*colp, wp->w_grid.Columns-1); return wp; } + } else if (*gridp == 0) { + ScreenGrid *grid = ui_comp_mouse_focus(*rowp, *colp); + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (&wp->w_grid != grid || !wp->w_float_config.focusable) { + continue; + } + *gridp = grid->handle; + *rowp -= grid->comp_row; + *colp -= grid->comp_col; + return wp; + } + + // no float found, click on the default grid + // TODO(bfredl): grid can be &pum_grid, allow select pum items by mouse? + *gridp = DEFAULT_GRID_HANDLE; } return NULL; } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index d857d1a79e..49eef72a05 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2200,6 +2200,7 @@ do_mouse ( * one. Speeds up dragging the status line. */ if (vpeekc() != NUL) { int nc; + int save_mouse_grid = mouse_grid; int save_mouse_row = mouse_row; int save_mouse_col = mouse_col; @@ -2209,6 +2210,7 @@ do_mouse ( if (c == nc) continue; vungetc(nc); + mouse_grid = save_mouse_grid; mouse_row = save_mouse_row; mouse_col = save_mouse_col; } diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 5e2c9ecb36..8070f4c420 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -331,7 +331,6 @@ static unsigned int handle_mouse_event(char **ptr, uint8_t *buf, uint8_t modifiers = check_multiclick(mouse_code, mouse_grid, mouse_row, mouse_col); - if (modifiers) { if (buf[1] != KS_MODIFIER) { // no modifiers in the buffer yet, shift the bytes 3 positions diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 3c10b7ae0f..499ee11cad 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -38,16 +38,16 @@ static int pum_base_width; // width of pum items base static int pum_kind_width; // width of pum items kind column static int pum_scrollbar; // TRUE when scrollbar present +static int pum_anchor_grid; // grid where position is defined static int pum_row; // top row of pum static int pum_col; // left column of pum +static bool pum_above; // pum is drawn above cursor line static bool pum_is_visible = false; static bool pum_is_drawn = false; static bool pum_external = false; static bool pum_invalid = false; // the screen was just cleared -static ScreenGrid pum_grid = SCREEN_GRID_INIT; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "popupmnu.c.generated.h" #endif @@ -103,9 +103,9 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed) col = curwin->w_wcol; } - int grid = (int)curwin->w_grid.handle; + pum_anchor_grid = (int)curwin->w_grid.handle; if (!ui_has(kUIMultigrid)) { - grid = (int)default_grid.handle; + pum_anchor_grid = (int)default_grid.handle; row += curwin->w_winrow; col += curwin->w_wincol; } @@ -121,7 +121,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed) ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_info))); ADD(arr, ARRAY_OBJ(item)); } - ui_call_popupmenu_show(arr, selected, row, col, grid); + ui_call_popupmenu_show(arr, selected, row, col, pum_anchor_grid); } else { ui_call_popupmenu_select(selected); } @@ -165,6 +165,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed) if (row + 2 >= below_row - pum_height && row - above_row > (below_row - above_row) / 2) { // pum above "row" + pum_above = true; // Leave two lines of context if possible if (curwin->w_wrow - curwin->w_cline_row >= 2) { @@ -187,6 +188,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed) } } else { // pum below "row" + pum_above = false; // Leave two lines of context if possible if (curwin->w_cline_row + curwin->w_cline_height - curwin->w_wrow >= 3) { @@ -360,7 +362,7 @@ void pum_redraw(void) grid_assign_handle(&pum_grid); bool moved = ui_comp_put_grid(&pum_grid, pum_row, pum_col-col_off, - pum_height, grid_width); + pum_height, grid_width, false, true); bool invalid_grid = moved || pum_invalid; pum_invalid = false; @@ -371,6 +373,13 @@ void pum_redraw(void) } else if (invalid_grid) { grid_invalidate(&pum_grid); } + if (ui_has(kUIMultigrid)) { + const char *anchor = pum_above ? "SW" : "NW"; + int row_off = pum_above ? pum_height : 0; + ui_call_win_float_pos(pum_grid.handle, -1, cstr_to_string(anchor), + pum_anchor_grid, pum_row-row_off, pum_col-col_off, + false); + } // Never display more than we have @@ -783,6 +792,10 @@ void pum_check_clear(void) ui_call_popupmenu_hide(); } else { ui_comp_remove_grid(&pum_grid); + if (ui_has(kUIMultigrid)) { + ui_call_win_close(pum_grid.handle); + ui_call_grid_destroy(pum_grid.handle); + } // TODO(bfredl): consider keeping float grids allocated. grid_free(&pum_grid); } diff --git a/src/nvim/popupmnu.h b/src/nvim/popupmnu.h index 7e1588dbdd..42e6ef5653 100644 --- a/src/nvim/popupmnu.h +++ b/src/nvim/popupmnu.h @@ -1,6 +1,8 @@ #ifndef NVIM_POPUPMNU_H #define NVIM_POPUPMNU_H +#include "nvim/macros.h" +#include "nvim/grid_defs.h" #include "nvim/types.h" /// Used for popup menu items. @@ -11,6 +13,7 @@ typedef struct { char_u *pum_info; // extra info } pumitem_T; +EXTERN ScreenGrid pum_grid INIT(= SCREEN_GRID_INIT); #ifdef INCLUDE_GENERATED_DECLARATIONS # include "popupmnu.h.generated.h" diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 4cfb734cd1..08bb4e4a52 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -148,9 +148,6 @@ typedef struct { /// Whether to call "ui_call_grid_resize" in win_grid_alloc static bool send_grid_resize = false; -/// Highlight ids are no longer valid. Force retransmission -static bool highlights_invalid = false; - static bool conceal_cursor_used = false; static bool redraw_popupmenu = false; @@ -197,8 +194,10 @@ void redraw_all_later(int type) void screen_invalidate_highlights(void) { - redraw_all_later(NOT_VALID); - highlights_invalid = true; + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + redraw_win_later(wp, NOT_VALID); + wp->w_grid.valid = false; + } } /* @@ -338,6 +337,9 @@ void update_screen(int type) grid_ins_lines(&default_grid, 0, msg_scrolled, (int)Rows, 0, (int)Columns); FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_floating) { + continue; + } if (wp->w_winrow < msg_scrolled) { if (W_ENDROW(wp) > msg_scrolled && wp->w_redr_type < REDRAW_TOP @@ -361,6 +363,10 @@ void update_screen(int type) need_wait_return = FALSE; } + if (type >= NOT_VALID) { + ui_comp_set_screen_valid(false); + } + win_ui_flush_positions(); msg_ext_check_prompt(); /* reset cmdline_row now (may have been changed temporarily) */ @@ -376,9 +382,11 @@ void update_screen(int type) type = NOT_VALID; // must_redraw may be set indirectly, avoid another redraw later must_redraw = 0; - } else if (highlights_invalid) { + } else if (!default_grid.valid) { grid_invalidate(&default_grid); + default_grid.valid = true; } + ui_comp_set_screen_valid(true); if (clear_cmdline) /* going to clear cmdline (done below) */ check_for_delay(FALSE); @@ -449,7 +457,14 @@ void update_screen(int type) */ did_one = FALSE; search_hl.rm.regprog = NULL; + + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_redr_type == CLEAR && wp->w_floating && wp->w_grid.chars) { + grid_invalidate(&wp->w_grid); + wp->w_redr_type = NOT_VALID; + } + if (wp->w_redr_type != 0) { if (!did_one) { did_one = TRUE; @@ -471,7 +486,6 @@ void update_screen(int type) } send_grid_resize = false; - highlights_invalid = false; redraw_popupmenu = false; /* Reset b_mod_set flags. Going through all windows is probably faster @@ -4236,9 +4250,12 @@ win_line ( /// If UI did not request multigrid support, draw all windows on the /// default_grid. /// +/// NB: this function can only been used with window grids in a context where +/// win_grid_alloc already has been called! +/// /// If the default_grid is used, adjust window relative positions to global /// screen positions. -static void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off) +void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off) { if (!(*grid)->chars && *grid != &default_grid) { *row_off += (*grid)->row_offset; @@ -5242,7 +5259,6 @@ void grid_getbytes(ScreenGrid *grid, int row, int col, char_u *bytes, screen_adjust_grid(&grid, &row, &col); - // safety check if (grid->chars != NULL && row < grid->Rows && col < grid->Columns) { off = grid->line_offset[row] + col; @@ -5855,14 +5871,9 @@ void win_grid_alloc(win_T *wp) int rows = wp->w_height_inner; int cols = wp->w_width_inner; - // TODO(bfredl): floating windows should force this to true - bool want_allocation = ui_has(kUIMultigrid); + bool want_allocation = ui_has(kUIMultigrid) || wp->w_floating; bool has_allocation = (grid->chars != NULL); - if (want_allocation && has_allocation && highlights_invalid) { - grid_invalidate(grid); - } - if (grid->Rows != rows) { wp->w_lines_valid = 0; xfree(wp->w_lines); @@ -5874,15 +5885,20 @@ void win_grid_alloc(win_T *wp) || grid->Rows != rows || grid->Columns != cols) { if (want_allocation) { - grid_alloc(grid, rows, cols, true, true); + grid_alloc(grid, rows, cols, wp->w_grid.valid, wp->w_grid.valid); + grid->valid = true; } else { // Single grid mode, all rendering will be redirected to default_grid. // Only keep track of the size and offset of the window. grid_free(grid); grid->Rows = rows; grid->Columns = cols; + grid->valid = false; } was_resized = true; + } else if (want_allocation && has_allocation && !wp->w_grid.valid) { + grid_invalidate(grid); + grid->valid = true; } grid->row_offset = wp->w_winrow; @@ -5892,7 +5908,7 @@ void win_grid_alloc(win_T *wp) // - a grid was just resized // - screen_resize was called and all grid sizes must be sent // - the UI wants multigrid event (necessary) - if ((send_grid_resize || was_resized) && ui_has(kUIMultigrid)) { + if ((send_grid_resize || was_resized) && want_allocation) { ui_call_grid_resize(grid->handle, grid->Columns, grid->Rows); } } @@ -5954,7 +5970,7 @@ retry: // win_new_shellsize will recompute floats position, but tell the // compositor to not redraw them yet - ui_comp_invalidate_screen(); + ui_comp_set_screen_valid(false); win_new_shellsize(); /* fit the windows in the new sized shell */ @@ -6108,6 +6124,8 @@ void screenclear(void) } ui_call_grid_clear(1); // clear the display + ui_comp_set_screen_valid(true); + clear_cmdline = false; mode_displayed = false; @@ -6116,6 +6134,11 @@ void screenclear(void) redraw_tabline = true; redraw_popupmenu = true; pum_invalidate(); + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_floating) { + wp->w_redr_type = CLEAR; + } + } if (must_redraw == CLEAR) { must_redraw = NOT_VALID; // no need to clear again } @@ -6505,14 +6528,14 @@ int showmode(void) if (VIsual_active) clear_showcmd(); - /* If the last window has no status line, the ruler is after the mode - * message and must be redrawn */ - if (redrawing() - && lastwin->w_status_height == 0 - ) - win_redr_ruler(lastwin, TRUE); - redraw_cmdline = FALSE; - clear_cmdline = FALSE; + // If the last window has no status line, the ruler is after the mode + // message and must be redrawn + win_T *last = lastwin_nofloating(); + if (redrawing() && last->w_status_height == 0) { + win_redr_ruler(last, true); + } + redraw_cmdline = false; + clear_cmdline = false; return length; } @@ -7091,7 +7114,7 @@ void screen_resize(int width, int height) check_shellsize(); height = Rows; width = Columns; - ui_resize(width, height); + ui_call_grid_resize(1, width, height); send_grid_resize = true; diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 0b24a3ebf4..e20cf15a79 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -165,6 +165,7 @@ UI *tui_start(void) memset(ui->ui_ext, 0, sizeof(ui->ui_ext)); ui->ui_ext[kUILinegrid] = true; + ui->ui_ext[kUITermColors] = true; return ui_bridge_attach(ui, tui_main, tui_scheduler); } diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 16370f2d10..d07ea3179e 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -219,11 +219,6 @@ void ui_schedule_refresh(void) loop_schedule(&main_loop, event_create(ui_refresh_event, 0)); } -void ui_resize(int width, int height) -{ - ui_call_grid_resize(1, width, height); -} - void ui_default_colors_set(void) { ui_call_default_colors_set(normal_fg, normal_bg, normal_sp, @@ -249,7 +244,7 @@ void ui_attach_impl(UI *ui) if (ui_count == MAX_UI_COUNT) { abort(); } - if (!ui->ui_ext[kUIMultigrid]) { + if (!ui->ui_ext[kUIMultigrid] && !ui->ui_ext[kUIFloatDebug]) { ui_comp_attach(ui); } @@ -299,7 +294,7 @@ void ui_detach_impl(UI *ui) ui_schedule_refresh(); } - if (!ui->ui_ext[kUIMultigrid]) { + if (!ui->ui_ext[kUIMultigrid] && !ui->ui_ext[kUIFloatDebug]) { ui_comp_detach(ui); } } @@ -310,7 +305,7 @@ void ui_set_ext_option(UI *ui, UIExtension ext, bool active) ui_refresh(); return; } - if (ui->option_set) { + if (ui->option_set && (ui_ext_names[ext][0] != '_' || active)) { ui->option_set(ui, cstr_as_string((char *)ui_ext_names[ext]), BOOLEAN_OBJ(active)); } @@ -383,7 +378,7 @@ int ui_current_col(void) void ui_flush(void) { cmdline_ui_flush(); - win_ui_flush(); + win_ui_flush_positions(); msg_ext_ui_flush(); if (pending_cursor_update) { @@ -405,7 +400,6 @@ void ui_flush(void) ui_call_flush(); } - /// Check if current mode has changed. /// May update the shape of the cursor. void ui_cursor_shape(void) @@ -438,7 +432,9 @@ Array ui_array(void) PUT(info, "height", INTEGER_OBJ(ui->height)); PUT(info, "rgb", BOOLEAN_OBJ(ui->rgb)); for (UIExtension j = 0; j < kUIExtCount; j++) { - PUT(info, ui_ext_names[j], BOOLEAN_OBJ(ui->ui_ext[j])); + if (ui_ext_names[j][0] != '_' || ui->ui_ext[j]) { + PUT(info, ui_ext_names[j], BOOLEAN_OBJ(ui->ui_ext[j])); + } } if (ui->inspect) { ui->inspect(ui, &info); @@ -462,8 +458,14 @@ void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error) return; } - // non-positive indicates no request - wp->w_height_request = (int)MAX(height, 0); - wp->w_width_request = (int)MAX(width, 0); - win_set_inner_size(wp); + if (wp->w_floating) { + if (width != wp->w_width && height != wp->w_height) { + win_config_float(wp, (int)width, (int)height, wp->w_float_config); + } + } else { + // non-positive indicates no request + wp->w_height_request = (int)MAX(height, 0); + wp->w_width_request = (int)MAX(width, 0); + win_set_inner_size(wp); + } } diff --git a/src/nvim/ui.h b/src/nvim/ui.h index 490cc930b1..3f6b3babad 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -20,6 +20,7 @@ typedef enum { kUIMultigrid, kUIHlState, kUITermColors, + kUIFloatDebug, kUIExtCount, } UIExtension; @@ -33,9 +34,9 @@ EXTERN const char *ui_ext_names[] INIT(= { "ext_multigrid", "ext_hlstate", "ext_termcolors", + "_debug_float", }); - typedef struct ui_t UI; enum { diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index c7ba0306e4..e52ba437ee 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -17,6 +17,7 @@ #include "nvim/ui.h" #include "nvim/highlight.h" #include "nvim/memory.h" +#include "nvim/popupmnu.h" #include "nvim/ui_compositor.h" #include "nvim/ugrid.h" #include "nvim/screen.h" @@ -55,7 +56,6 @@ void ui_comp_init(void) compositor->rgb = true; compositor->grid_resize = ui_comp_grid_resize; - compositor->grid_clear = ui_comp_grid_clear; compositor->grid_scroll = ui_comp_grid_scroll; compositor->grid_cursor_goto = ui_comp_grid_cursor_goto; compositor->raw_line = ui_comp_raw_line; @@ -107,10 +107,12 @@ bool ui_comp_should_draw(void) /// TODO(bfredl): later on the compositor should just use win_float_pos events, /// though that will require slight event order adjustment: emit the win_pos /// events in the beginning of update_screen(0), rather than in ui_flush() -bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width) +bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width, + bool valid, bool on_top) { + bool moved; if (grid->comp_index != 0) { - bool moved = (row != grid->comp_row) || (col != grid->comp_col); + moved = (row != grid->comp_row) || (col != grid->comp_col); if (ui_comp_should_draw()) { // Redraw the area covered by the old position, and is not covered // by the new position. Disable the grid so that compose_area() will not @@ -134,21 +136,41 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width) } grid->comp_row = row; grid->comp_col = col; - return moved; - } + } else { + moved = true; #ifndef NDEBUG - for (size_t i = 0; i < kv_size(layers); i++) { - if (kv_A(layers, i) == grid) { - assert(false); + for (size_t i = 0; i < kv_size(layers); i++) { + if (kv_A(layers, i) == grid) { + assert(false); + } } - } #endif - // not found: new grid - kv_push(layers, grid); - grid->comp_row = row; - grid->comp_col = col; - grid->comp_index = kv_size(layers)-1; - return true; + + size_t insert_at = kv_size(layers); + if (kv_A(layers, insert_at-1) == &pum_grid) { + insert_at--; + } + if (insert_at > 1 && !on_top) { + insert_at--; + } + // not found: new grid + kv_push(layers, grid); + if (insert_at < kv_size(layers)-1) { + for (size_t i = kv_size(layers)-1; i > insert_at; i--) { + kv_A(layers, i) = kv_A(layers, i-i); + } + kv_A(layers, insert_at) = grid; + } + + grid->comp_row = row; + grid->comp_col = col; + grid->comp_index = insert_at; + } + if (moved && valid && ui_comp_should_draw()) { + compose_area(grid->comp_row, grid->comp_row+grid->Rows, + grid->comp_col, grid->comp_col+grid->Columns); + } + return moved; } void ui_comp_remove_grid(ScreenGrid *grid) @@ -194,6 +216,26 @@ bool ui_comp_set_grid(handle_T handle) return false; } +static void ui_comp_raise_grid(ScreenGrid *grid, size_t new_index) +{ + size_t old_index = grid->comp_index; + for (size_t i = old_index; i < new_index; i++) { + kv_A(layers, i) = kv_A(layers, i+1); + kv_A(layers, i)->comp_index = i; + } + kv_A(layers, new_index) = grid; + grid->comp_index = new_index; + for (size_t i = old_index; i < new_index; i++) { + ScreenGrid *grid2 = kv_A(layers, i); + int startcol = MAX(grid->comp_col, grid2->comp_col); + int endcol = MIN(grid->comp_col+grid->Columns, + grid2->comp_col+grid2->Columns); + compose_area(MAX(grid->comp_row, grid2->comp_row), + MIN(grid->comp_row+grid->Rows, grid2->comp_row+grid2->Rows), + startcol, endcol); + } +} + static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle, Integer r, Integer c) { @@ -203,6 +245,18 @@ static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle, int cursor_row = curgrid->comp_row+(int)r; int cursor_col = curgrid->comp_col+(int)c; + // TODO(bfredl): maybe not the best time to do this, for efficiency we + // should configure all grids before entering win_update() + if (curgrid != &default_grid) { + size_t new_index = kv_size(layers)-1; + if (kv_A(layers, new_index) == &pum_grid) { + new_index--; + } + if (curgrid->comp_index < new_index) { + ui_comp_raise_grid(curgrid, new_index); + } + } + if (cursor_col >= default_grid.Columns || cursor_row >= default_grid.Rows) { // TODO(bfredl): this happens with 'writedelay', refactor? // abort(); @@ -211,6 +265,18 @@ static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle, ui_composed_call_grid_cursor_goto(1, cursor_row, cursor_col); } +ScreenGrid *ui_comp_mouse_focus(int row, int col) +{ + // TODO(bfredl): click "through" unfocusable grids? + for (ssize_t i = (ssize_t)kv_size(layers)-1; i > 0; i--) { + ScreenGrid *grid = kv_A(layers, i); + if (row >= grid->comp_row && row < grid->comp_row+grid->Rows + && col >= grid->comp_col && col < grid->comp_col+grid->Columns) { + return grid; + } + } + return NULL; +} /// Baseline implementation. This is always correct, but we can sometimes /// do something more efficient (where efficiency means smaller deltas to @@ -260,7 +326,7 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, memcpy(attrbuf+(col-startcol), grid->attrs+off, n * sizeof(*attrbuf)); // 'pumblend' - if (grid != &default_grid && p_pb) { + if (grid == &pum_grid && p_pb) { for (int i = col-(int)startcol; i < until-startcol; i++) { bool thru = strequal((char *)linebuf[i], " "); // negative space attrbuf[i] = (sattr_T)hl_blend_attrs(bg_attrs[i], attrbuf[i], thru); @@ -348,7 +414,8 @@ static void ui_comp_raw_line(UI *ui, Integer grid, Integer row, assert(row < default_grid.Rows); assert(clearcol <= default_grid.Columns); if (flags & kLineFlagInvalid - || kv_size(layers) > (p_pb ? 1 : curgrid->comp_index+1)) { + || kv_size(layers) > curgrid->comp_index+1 + || (p_pb && curgrid == &pum_grid)) { compose_line(row, startcol, clearcol, flags); } else { ui_composed_call_raw_line(1, row, startcol, endcol, clearcol, clearattr, @@ -359,17 +426,9 @@ static void ui_comp_raw_line(UI *ui, Integer grid, Integer row, /// The screen is invalid and will soon be cleared /// /// Don't redraw floats until screen is cleared -void ui_comp_invalidate_screen(void) -{ - valid_screen = false; -} - -static void ui_comp_grid_clear(UI *ui, Integer grid) +void ui_comp_set_screen_valid(bool valid) { - // By design, only first grid uses clearing. - assert(grid == 1); - ui_composed_call_grid_clear(1); - valid_screen = true; + valid_screen = valid; } // TODO(bfredl): These events are somewhat of a hack. multiline messages diff --git a/src/nvim/window.c b/src/nvim/window.c index bb71b12aed..18fad76a95 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -6,6 +6,7 @@ #include <stdbool.h> #include "nvim/api/private/handle.h" +#include "nvim/api/private/helpers.h" #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/window.h" @@ -48,6 +49,7 @@ #include "nvim/terminal.h" #include "nvim/undo.h" #include "nvim/ui.h" +#include "nvim/ui_compositor.h" #include "nvim/os/os.h" @@ -203,14 +205,24 @@ newwindow: wp = wp->w_next; } } else { - if (nchar == 'W') { /* go to previous window */ + if (nchar == 'W') { // go to previous window wp = curwin->w_prev; - if (wp == NULL) - wp = lastwin; /* wrap around */ - } else { /* go to next window */ + if (wp == NULL) { + wp = lastwin; // wrap around + } + while (wp != NULL && wp->w_floating + && !wp->w_float_config.focusable) { + wp = wp->w_prev; + } + } else { // go to next window wp = curwin->w_next; - if (wp == NULL) - wp = firstwin; /* wrap around */ + while (wp != NULL && wp->w_floating + && !wp->w_float_config.focusable) { + wp = wp->w_next; + } + if (wp == NULL) { + wp = firstwin; // wrap around + } } } win_goto(wp); @@ -281,7 +293,7 @@ newwindow: /* cursor to bottom-right window */ case 'b': case Ctrl_B: - win_goto(lastwin); + win_goto(lastwin_nofloating()); break; /* cursor to last accessed (previous) window */ @@ -483,6 +495,22 @@ wingotofile: cmdmod.tab = tabpage_index(curtab) + 1; nchar = xchar; goto wingotofile; + + case 'e': + if (curwin->w_floating || !ui_has(kUIMultigrid)) { + beep_flush(); + break; + } + FloatConfig config = FLOAT_CONFIG_INIT; + config.external = true; + Error err = ERROR_INIT; + if (!win_new_float(curwin, curwin->w_width, curwin->w_height, config, + &err)) { + EMSG(err.msg); + api_clear_error(&err); + beep_flush(); + } + break; default: beep_flush(); break; @@ -504,6 +532,302 @@ static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize, } } +/// Create a new float. +/// +/// if wp == NULL allocate a new window, otherwise turn existing window into a +/// float. It must then already belong to the current tabpage! +/// +/// config must already have been validated! +win_T *win_new_float(win_T *wp, int width, int height, FloatConfig config, + Error *err) +{ + bool new = false; + if (wp == NULL) { + new = true; + wp = win_alloc(lastwin_nofloating(), false); + win_init(wp, curwin, 0); + } else { + assert(!wp->w_floating); + if (firstwin == wp && lastwin_nofloating() == wp) { + // last non-float + api_set_error(err, kErrorTypeException, + "Cannot change last window into float"); + return NULL; + } else if (!win_valid(wp)) { + api_set_error(err, kErrorTypeException, + "Cannot change window from different tabpage into float"); + return NULL; + } + int dir; + winframe_remove(wp, &dir, NULL); + xfree(wp->w_frame); + wp->w_frame = NULL; + (void)win_comp_pos(); // recompute window positions + win_remove(wp, NULL); + win_append(lastwin_nofloating(), wp); + } + wp->w_floating = 1; + wp->w_status_height = 0; + wp->w_vsep_width = 0; + win_config_float(wp, width, height, config); + wp->w_pos_changed = true; + redraw_win_later(wp, VALID); + if (new) { + win_enter(wp, false); + } + return wp; +} + +void win_config_float(win_T *wp, int width, int height, + FloatConfig config) +{ + wp->w_height = MAX(height, 1); + wp->w_width = MAX(width, 2); + + if (config.relative == kFloatRelativeCursor) { + config.relative = kFloatRelativeWindow; + config.row += curwin->w_wrow; + config.col += curwin->w_wcol; + config.window = curwin->handle; + } + + wp->w_float_config = config; + + if (!ui_has(kUIMultigrid)) { + wp->w_height = MIN(wp->w_height, Rows-1); + wp->w_width = MIN(wp->w_width, Columns); + } + + win_set_inner_size(wp); + must_redraw = MAX(must_redraw, VALID); + wp->w_pos_changed = true; +} + +static void ui_ext_win_position(win_T *wp) +{ + if (!wp->w_floating) { + ui_call_win_pos(wp->w_grid.handle, wp->handle, wp->w_winrow, + wp->w_wincol, wp->w_width, wp->w_height); + return; + } + const char *const anchor_str[] = { + "NW", + "NE", + "SW", + "SE" + }; + + FloatConfig c = wp->w_float_config; + if (!c.external) { + ScreenGrid *grid = &default_grid; + int row = c.row, col = c.col; + if (c.relative == kFloatRelativeWindow) { + Error dummy = ERROR_INIT; + win_T *win = find_window_by_handle(c.window, &dummy); + if (win) { + grid = &win->w_grid; + screen_adjust_grid(&grid, &row, &col); + } + api_clear_error(&dummy); + } + if (ui_has(kUIMultigrid)) { + String anchor = cstr_to_string(anchor_str[c.anchor]); + ui_call_win_float_pos(wp->w_grid.handle, wp->handle, anchor, grid->handle, + row, col, c.focusable); + } else { + // TODO(bfredl): ideally, compositor should work like any multigrid UI + // and use standard win_pos events. + bool east = c.anchor & kFloatAnchorEast; + bool south = c.anchor & kFloatAnchorSouth; + + row -= (south ? wp->w_height : 0); + col -= (east ? wp->w_width : 0); + row = MAX(MIN(row, Rows-wp->w_height-1), 0); + col = MAX(MIN(col, Columns-wp->w_width), 0); + wp->w_winrow = row; + wp->w_wincol = col; + bool valid = (wp->w_redr_type == 0); + bool on_top = (curwin == wp) || !curwin->w_floating; + ui_comp_put_grid(&wp->w_grid, row, col, wp->w_height, wp->w_width, + valid, on_top); + if (!valid) { + wp->w_grid.valid = false; + redraw_win_later(wp, NOT_VALID); + } + } + } else { + ui_call_win_external_pos(wp->w_grid.handle, wp->handle); + } + +} + + +static bool parse_float_anchor(String anchor, FloatAnchor *out) +{ + if (anchor.size == 0) { + *out = (FloatAnchor)0; + } + char *str = anchor.data; + if (!STRICMP(str, "NW")) { + *out = kFloatAnchorNW; + } else if (!STRICMP(str, "NE")) { + *out = kFloatAnchorNE; + } else if (!STRICMP(str, "SW")) { + *out = kFloatAnchorSW; + } else if (!STRICMP(str, "SE")) { + *out = kFloatAnchorSE; + } else { + return false; + } + return true; +} + +static bool parse_float_relative(String relative, FloatRelative *out) +{ + if (relative.size == 0) { + *out = (FloatRelative)0; + } + char *str = relative.data; + if (!STRICMP(str, "editor")) { + *out = kFloatRelativeEditor; + } else if (!STRICMP(str, "win")) { + *out = kFloatRelativeWindow; + } else if (!STRICMP(str, "cursor")) { + *out = kFloatRelativeCursor; + } else { + return false; + } + return true; +} + +bool parse_float_config(Dictionary config, FloatConfig *out, bool reconf, + Error *err) +{ + bool has_row = false, has_col = false, has_relative = false; + bool has_external = false, has_window = false; + + for (size_t i = 0; i < config.size; i++) { + char *key = config.items[i].key.data; + Object val = config.items[i].value; + if (!strcmp(key, "row")) { + has_row = true; + if (val.type == kObjectTypeInteger) { + out->row = val.data.integer; + } else if (val.type == kObjectTypeFloat) { + out->row = val.data.floating; + } else { + api_set_error(err, kErrorTypeValidation, + "'row' option has to be Integer or Float"); + return false; + } + } else if (!strcmp(key, "col")) { + has_col = true; + if (val.type == kObjectTypeInteger) { + out->col = val.data.integer; + } else if (val.type == kObjectTypeFloat) { + out->col = val.data.floating; + } else { + api_set_error(err, kErrorTypeValidation, + "'col' option has to be Integer or Float"); + return false; + } + } else if (!strcmp(key, "anchor")) { + if (val.type != kObjectTypeString) { + api_set_error(err, kErrorTypeValidation, + "'anchor' option has to be String"); + return false; + } + if (!parse_float_anchor(val.data.string, &out->anchor)) { + api_set_error(err, kErrorTypeValidation, + "Invalid value of 'anchor' option"); + return false; + } + } else if (!strcmp(key, "relative")) { + has_relative = true; + if (val.type != kObjectTypeString) { + api_set_error(err, kErrorTypeValidation, + "'relative' option has to be String"); + return false; + } + if (!parse_float_relative(val.data.string, &out->relative)) { + api_set_error(err, kErrorTypeValidation, + "Invalid value of 'relative' option"); + return false; + } + } else if (!strcmp(key, "win")) { + has_window = true; + if (val.type != kObjectTypeInteger + && val.type != kObjectTypeWindow) { + api_set_error(err, kErrorTypeValidation, + "'win' option has to be Integer or Window"); + return false; + } + out->window = val.data.integer; + } else if (!strcmp(key, "external")) { + if (val.type == kObjectTypeInteger) { + out->external = val.data.integer; + } else if (val.type == kObjectTypeBoolean) { + out->external = val.data.boolean; + } else { + api_set_error(err, kErrorTypeValidation, + "'external' option has to be Boolean"); + return false; + } + has_external = out->external; + } else if (!strcmp(key, "focusable")) { + if (val.type == kObjectTypeInteger) { + out->focusable = val.data.integer; + } else if (val.type == kObjectTypeBoolean) { + out->focusable = val.data.boolean; + } else { + api_set_error(err, kErrorTypeValidation, + "'focusable' option has to be Boolean"); + return false; + } + } else { + api_set_error(err, kErrorTypeValidation, + "Invalid options key '%s'", key); + return false; + } + } + + if (has_window && !(has_relative && out->relative == kFloatRelativeWindow)) { + api_set_error(err, kErrorTypeValidation, + "'win' option is only valid with relative='win'"); + return false; + } + + if ((has_relative && out->relative == kFloatRelativeWindow) + && (!has_window || out->window == 0)) { + out->window = curwin->handle; + } + + if (has_relative && has_external) { + api_set_error(err, kErrorTypeValidation, + "Only one of 'relative' and 'external' should be used"); + return false; + } else if (has_relative) { + out->external = false; + } else if (!reconf && !has_relative && !has_external) { + api_set_error(err, kErrorTypeValidation, + "One of 'relative' and 'external' must be used"); + return false; + } + + if (out->external && !ui_has(kUIMultigrid)) { + api_set_error(err, kErrorTypeValidation, + "UI doesn't support external windows"); + return false; + } + + if (has_relative != has_row || has_row != has_col) { + api_set_error(err, kErrorTypeValidation, "All of 'relative', 'row', and " + "'col' has to be specified at once"); + return false; + } + return true; +} + /* * split the current window, implements CTRL-W s and :split * @@ -566,16 +890,20 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) int wmh1; bool did_set_fraction = false; - if (flags & WSP_TOP) + if (flags & WSP_TOP) { oldwin = firstwin; - else if (flags & WSP_BOT) - oldwin = lastwin; - else + } else if (flags & WSP_BOT || curwin->w_floating) { + // can't split float, use last nonfloating window instead + oldwin = lastwin_nofloating(); + } else { oldwin = curwin; + } - /* add a status line when p_ls == 1 and splitting the first window */ - if (ONE_WINDOW && p_ls == 1 && oldwin->w_status_height == 0) { - if (oldwin->w_height <= p_wmh && new_wp == NULL) { + bool new_in_layout = (new_wp == NULL || new_wp->w_floating); + + // 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) { EMSG(_(e_noroom)); return FAIL; } @@ -624,7 +952,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) available = oldwin->w_frame->fr_width; needed += minwidth; } - if (available < needed && new_wp == NULL) { + if (available < needed && new_in_layout) { EMSG(_(e_noroom)); return FAIL; } @@ -702,7 +1030,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) available = oldwin->w_frame->fr_height; needed += minheight; } - if (available < needed && new_wp == NULL) { + if (available < needed && new_in_layout) { EMSG(_(e_noroom)); return FAIL; } @@ -790,6 +1118,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) /* make the contents of the new window the same as the current one */ win_init(wp, curwin, flags); + } else if (wp->w_floating) { + new_frame(wp); + wp->w_floating = false; } /* @@ -1192,7 +1523,13 @@ static void win_exchange(long Prenum) win_T *wp2; int temp; - if (ONE_WINDOW) { /* just one window */ + if (curwin->w_floating) { + EMSG(e_floatexchange); + return; + } + + if (firstwin == curwin && lastwin_nofloating() == curwin) { + // just one window beep_flush(); return; } @@ -1282,7 +1619,13 @@ static void win_rotate(int upwards, int count) frame_T *frp; int n; - if (ONE_WINDOW) { /* nothing to do */ + if (curwin->w_floating) { + EMSG(e_floatexchange); + return; + } + + if (firstwin == curwin && lastwin_nofloating() == curwin) { + // nothing to do beep_flush(); return; } @@ -1355,16 +1698,27 @@ static void win_rotate(int upwards, int count) */ static void win_totop(int size, int flags) { - int dir; + int dir = 0; int height = curwin->w_height; - if (ONE_WINDOW) { + if (firstwin == curwin && lastwin_nofloating() == curwin) { beep_flush(); return; } - /* Remove the window and frame from the tree of frames. */ - (void)winframe_remove(curwin, &dir, NULL); + if (curwin->w_floating) { + ui_comp_remove_grid(&curwin->w_grid); + if (ui_has(kUIMultigrid)) { + curwin->w_pos_changed = true; + } else { + // No longer a float, a non-multigrid UI shouldn't draw it as such + ui_call_win_hide(curwin->w_grid.handle); + win_free_grid(curwin, false); + } + } else { + // Remove the window and frame from the tree of frames. + (void)winframe_remove(curwin, &dir, NULL); + } win_remove(curwin, NULL); last_status(FALSE); /* may need to remove last status line */ (void)win_comp_pos(); /* recompute window positions */ @@ -1795,13 +2149,13 @@ static bool last_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT } /// Check that current tab page contains no more then one window other than -/// "aucmd_win". +/// "aucmd_win". Only counts floating window if it is current. bool one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { bool seen_one = false; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp != aucmd_win) { + if (wp != aucmd_win && (!wp->w_floating || wp == curwin)) { if (seen_one) { return false; } @@ -1811,6 +2165,20 @@ bool one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT return true; } +/// Like ONE_WINDOW but only considers non-floating windows +bool one_nonfloat(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return firstwin->w_next == NULL || firstwin->w_next->w_floating; +} + +/// if wp is the last non-floating window +/// +/// always false for a floating window +bool last_nonfloat(win_T *wp) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return firstwin == wp && !(wp->w_next && !wp->w_floating); +} + /// Close the possibly last window in a tab page. /// /// @param win window to close @@ -1882,9 +2250,9 @@ int win_close(win_T *win, bool free_buf) int dir; bool help_window = false; tabpage_T *prev_curtab = curtab; - frame_T *win_frame = win->w_frame->fr_parent; + frame_T *win_frame = win->w_floating ? NULL : win->w_frame->fr_parent; - if (last_window()) { + if (last_window() && !win->w_floating) { EMSG(_("E444: Cannot close last window")); return FAIL; } @@ -1897,10 +2265,17 @@ int win_close(win_T *win, bool free_buf) EMSG(_("E813: Cannot close autocmd window")); return FAIL; } - if ((firstwin == aucmd_win || lastwin == aucmd_win) && one_window()) { + if ((firstwin == aucmd_win || lastwin_nofloating() == aucmd_win) + && one_window()) { EMSG(_("E814: Cannot close window, only autocmd window would remain")); return FAIL; } + if ((firstwin == win && lastwin_nofloating() == win) + && lastwin->w_floating) { + // TODO(bfredl): we might close the float also instead + EMSG(e_floatonly); + return FAIL; + } /* When closing the last window in a tab page first go to another tab page * and then close the window and the tab page to avoid that curwin and @@ -1921,7 +2296,15 @@ int win_close(win_T *win, bool free_buf) * Guess which window is going to be the new current window. * This may change because of the autocommands (sigh). */ - wp = frame2win(win_altframe(win, NULL)); + if (!win->w_floating) { + wp = frame2win(win_altframe(win, NULL)); + } else { + if (win_valid(prevwin)) { + wp = prevwin; + } else { + wp = curtab->tp_firstwin; + } + } /* * Be careful: If autocommands delete the window or cause this window @@ -1949,6 +2332,27 @@ int win_close(win_T *win, bool free_buf) return FAIL; } + bool was_floating = win->w_floating; + if (ui_has(kUIMultigrid)) { + ui_call_win_close(win->w_grid.handle); + } + + if (win->w_floating) { + ui_comp_remove_grid(&win->w_grid); + if (win->w_float_config.external) { + for (tabpage_T *tp = first_tabpage; tp != NULL; tp = tp->tp_next) { + if (tp == curtab) { + continue; + } + if (tp->tp_curwin == win) { + // NB: an autocmd can still abort the closing of this window, + // bur carring out this change anyway shouldn't be a catastrophe. + tp->tp_curwin = tp->tp_firstwin; + } + } + } + } + /* Free independent synblock before the buffer is freed. */ if (win->w_buffer != NULL) @@ -1975,7 +2379,8 @@ int win_close(win_T *win, bool free_buf) if (only_one_window() && win_valid(win) && win->w_buffer == NULL && (last_window() || curtab != prev_curtab - || close_last_window_tabpage(win, free_buf, prev_curtab))) { + || close_last_window_tabpage(win, free_buf, prev_curtab)) + && !win->w_floating) { // Autocommands have closed all windows, quit now. Restore // curwin->w_buffer, otherwise writing ShaDa file may fail. if (curwin->w_buffer == NULL) { @@ -1992,7 +2397,7 @@ int win_close(win_T *win, bool free_buf) } // Autocommands may have closed the window already, or closed the only // other window or moved to another tab page. - if (!win_valid(win) || last_window() + if (!win_valid(win) || (!win->w_floating && last_window()) || close_last_window_tabpage(win, free_buf, prev_curtab)) { return FAIL; } @@ -2041,12 +2446,15 @@ int win_close(win_T *win, bool free_buf) // using the window. check_cursor(); } - if (p_ea && (*p_ead == 'b' || *p_ead == dir)) { - // If the frame of the closed window contains the new current window, - // only resize that frame. Otherwise resize all windows. - win_equal(curwin, curwin->w_frame->fr_parent == win_frame, dir); - } else { - win_comp_pos(); + + if (!was_floating) { + if (p_ea && (*p_ead == 'b' || *p_ead == dir)) { + // If the frame of the closed window contains the new current window, + // only resize that frame. Otherwise resize all windows. + win_equal(curwin, curwin->w_frame->fr_parent == win_frame, dir); + } else { + win_comp_pos(); + } } if (close_curwin) { @@ -2167,10 +2575,18 @@ win_free_mem ( frame_T *frp; win_T *wp; - /* Remove the window and its frame from the tree of frames. */ - frp = win->w_frame; - wp = winframe_remove(win, dirp, tp); - xfree(frp); + if (!win->w_floating) { + // Remove the window and its frame from the tree of frames. + frp = win->w_frame; + wp = winframe_remove(win, dirp, tp); + xfree(frp); + } else { + if (win_valid(prevwin)) { + wp = prevwin; + } else { + wp = curtab->tp_firstwin; + } + } win_free(win, tp); /* When deleting the current window of another tab page select a new @@ -2189,6 +2605,12 @@ void win_free_all(void) while (first_tabpage->tp_next != NULL) tabpage_close(TRUE); + while (lastwin != NULL && lastwin->w_floating) { + win_T *wp = lastwin; + win_remove(lastwin, NULL); + (void)win_free_mem(wp, &dummy, NULL); + } + if (aucmd_win != NULL) { (void)win_free_mem(aucmd_win, &dummy, NULL); aucmd_win = NULL; @@ -2870,11 +3292,19 @@ close_others ( win_T *nextwp; int r; - if (one_window()) { + if (curwin->w_floating) { + if (message && !autocmd_busy) { + EMSG(e_floatonly); + } + return; + } + + if (one_window() && !lastwin->w_floating) { if (message && !autocmd_busy - ) + ) { MSG(_(m_onlyone)); + } return; } @@ -3116,9 +3546,7 @@ int win_new_tabpage(int after, char_u *filename) redraw_all_later(NOT_VALID); - if (ui_has(kUIMultigrid)) { - tabpage_check_windows(tp); - } + tabpage_check_windows(tp); apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf); @@ -3317,7 +3745,7 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au lastwin = tp->tp_lastwin; topframe = tp->tp_topframe; - if (old_curtab != curtab && ui_has(kUIMultigrid)) { + if (old_curtab != curtab) { tabpage_check_windows(old_curtab); } @@ -3355,16 +3783,31 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au redraw_all_later(NOT_VALID); } -/// called when changing current tabpage from old_curtab to curtab +/// tells external UI that windows and inline floats in old_curtab are invisible +/// and that floats in curtab is now visible. +/// +/// External floats are considered independent of tabpages. This is +/// implemented by always moving them to curtab. static void tabpage_check_windows(tabpage_T *old_curtab) { win_T *next_wp; for (win_T *wp = old_curtab->tp_firstwin; wp; wp = next_wp) { next_wp = wp->w_next; + if (wp->w_floating) { + if (wp->w_float_config.external) { + win_remove(wp, old_curtab); + win_append(lastwin_nofloating(), wp); + } else { + ui_comp_remove_grid(&wp->w_grid); + } + } wp->w_pos_changed = true; } for (win_T *wp = firstwin; wp; wp = wp->w_next) { + if (wp->w_floating && !wp->w_float_config.external) { + win_config_float(wp, wp->w_width, wp->w_height, wp->w_float_config); + } wp->w_pos_changed = true; } } @@ -3577,6 +4020,12 @@ win_goto_ver ( frame_T *foundfr; foundfr = curwin->w_frame; + + if (curwin->w_floating) { + win_goto(prevwin); + return; + } + while (count--) { /* * First go upwards in the tree of frames until we find an upwards or @@ -3636,6 +4085,12 @@ win_goto_hor ( frame_T *foundfr; foundfr = curwin->w_frame; + + if (curwin->w_floating) { + win_goto(prevwin); + return; + } + while (count--) { /* * First go upwards in the tree of frames until we find a left or @@ -3740,6 +4195,7 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, } curwin = wp; curbuf = wp->w_buffer; + check_cursor(); if (!virtual_active()) curwin->w_cursor.coladd = 0; @@ -3809,9 +4265,10 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, else if (curwin->w_height == 0) win_setheight(1); - /* set window width to desired minimal value */ - if (curwin->w_width < p_wiw && !curwin->w_p_wfw) + // set window width to desired minimal value + if (curwin->w_width < p_wiw && !curwin->w_p_wfw && !wp->w_floating) { win_setwidth((int)p_wiw); + } setmouse(); /* in case jumped to/from help buffer */ @@ -3916,6 +4373,7 @@ static win_T *win_alloc(win_T *after, int hidden) new_wp->w_botline = 2; new_wp->w_cursor.lnum = 1; new_wp->w_scbind_pos = 1; + new_wp->w_floating = 0; /* We won't calculate w_fraction until resizing the window */ new_wp->w_fraction = 0; @@ -4204,6 +4662,13 @@ int win_comp_pos(void) int col = 0; frame_comp_pos(topframe, &row, &col); + + // Too often, but when we support anchoring floats to split windows, + // this will be needed + for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) { + win_config_float(wp, wp->w_width, wp->w_height, wp->w_float_config); + } + return row; } @@ -4263,8 +4728,6 @@ void win_setheight(int height) */ void win_setheight_win(int height, win_T *win) { - int row; - if (win == curwin) { /* Always keep current window at least one line high, even when * 'winminheight' is zero. */ @@ -4274,21 +4737,28 @@ void win_setheight_win(int height, win_T *win) height = 1; } - frame_setheight(win->w_frame, height + win->w_status_height); + if (win->w_floating) { + if (win->w_float_config.external) { + win_config_float(win, win->w_width, height, win->w_float_config); + } else { + beep_flush(); + return; + } + } else { + frame_setheight(win->w_frame, height + win->w_status_height); - /* recompute the window positions */ - row = win_comp_pos(); + // recompute the window positions + int row = win_comp_pos(); - /* - * If there is extra space created between the last window and the command - * line, clear it. - */ - if (full_screen && msg_scrolled == 0 && row < cmdline_row) { - grid_fill(&default_grid, row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); + // If there is extra space created between the last window and the command + // line, clear it. + if (full_screen && msg_scrolled == 0 && row < cmdline_row) { + grid_fill(&default_grid, row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); + } + cmdline_row = row; + msg_row = row; + msg_col = 0; } - cmdline_row = row; - msg_row = row; - msg_col = 0; redraw_all_later(NOT_VALID); } @@ -4358,14 +4828,16 @@ static void frame_setheight(frame_T *curfrp, int height) if (frp != curfrp) room -= frame_minheight(frp, NULL); } - if (curfrp->fr_width != Columns) + if (curfrp->fr_width != Columns) { room_cmdline = 0; - else { - room_cmdline = Rows - p_ch - (lastwin->w_winrow - + lastwin->w_height + - lastwin->w_status_height); - if (room_cmdline < 0) + } else { + win_T *wp = lastwin_nofloating(); + room_cmdline = Rows - p_ch - (wp->w_winrow + + wp->w_height + + wp->w_status_height); + if (room_cmdline < 0) { room_cmdline = 0; + } } if (height <= room + room_cmdline) { @@ -4470,11 +4942,19 @@ void win_setwidth_win(int width, win_T *wp) if (width == 0) width = 1; } + if (wp->w_floating) { + if (wp->w_float_config.external) { + win_config_float(wp, width, wp->w_height, wp->w_float_config); + } else { + beep_flush(); + return; + } + } else { + frame_setwidth(wp->w_frame, width + wp->w_vsep_width); - frame_setwidth(wp->w_frame, width + wp->w_vsep_width); - - /* recompute the window positions */ - (void)win_comp_pos(); + // recompute the window positions + (void)win_comp_pos(); + } redraw_all_later(NOT_VALID); } @@ -5015,6 +5495,7 @@ void win_set_inner_size(win_T *wp) if (!exiting) { scroll_to_fraction(wp, prev_height); } + redraw_win_later(wp, NOT_VALID); // SOME_VALID?? } if (width != wp->w_width_inner) { @@ -5026,6 +5507,7 @@ void win_set_inner_size(win_T *wp) update_topline(); curs_columns(true); // validate w_wrow } + redraw_win_later(wp, NOT_VALID); } if (wp->w_buffer->terminal) { @@ -5039,9 +5521,7 @@ void win_new_width(win_T *wp, int width) wp->w_width = width; win_set_inner_size(wp); - redraw_win_later(wp, NOT_VALID); - wp->w_redr_status = TRUE; - + wp->w_redr_status = true; wp->w_pos_changed = true; } @@ -5379,7 +5859,7 @@ int min_rows(void) /// Check that there is only one window (and only one tab page), not counting a /// help or preview window, unless it is the current window. Does not count -/// "aucmd_win". +/// "aucmd_win". Does not count floats unless it is current. bool only_one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { // If there is another tab page there always is another window. @@ -5390,7 +5870,7 @@ bool only_one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT int count = 0; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_buffer != NULL - && (!((bt_help(wp->w_buffer) && !bt_help(curbuf)) + && (!((bt_help(wp->w_buffer) && !bt_help(curbuf)) || wp->w_floating || wp->w_p_pvw) || wp == curwin) && wp != aucmd_win) { count++; } @@ -6080,22 +6560,24 @@ void win_findbuf(typval_T *argvars, list_T *list) } } -void win_ui_flush(void) +void win_ui_flush_positions(void) { - if (!ui_has(kUIMultigrid)) { - return; - } - FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_pos_changed && wp->w_grid.chars != NULL) { if (tp == curtab) { - ui_call_win_pos(wp->w_grid.handle, wp->handle, wp->w_winrow, - wp->w_wincol, wp->w_width, wp->w_height); + ui_ext_win_position(wp); } else { ui_call_win_hide(wp->w_grid.handle); } wp->w_pos_changed = false; } } +} +win_T *lastwin_nofloating(void) { + win_T *res = lastwin; + while (res->w_floating) { + res = res->w_prev; + } + return res; } diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua new file mode 100644 index 0000000000..0d0ae60ce9 --- /dev/null +++ b/test/functional/ui/float_spec.lua @@ -0,0 +1,3030 @@ +local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') +local os = require('os') +local clear, feed = helpers.clear, helpers.feed +local command, feed_command = helpers.command, helpers.feed_command +local eval = helpers.eval +local eq = helpers.eq +local meths = helpers.meths +local curbufmeths = helpers.curbufmeths +local funcs = helpers.funcs +local run = helpers.run +local meth_pcall = helpers.meth_pcall + +describe('floating windows', function() + before_each(function() + clear() + end) + local attrs = { + [0] = {bold=true, foreground=Screen.colors.Blue}, + [1] = {background = Screen.colors.LightMagenta}, + [2] = {background = Screen.colors.LightMagenta, bold = true, foreground = Screen.colors.Blue1}, + [3] = {bold = true}, + [4] = {bold = true, reverse = true}, + [5] = {reverse = true}, + [6] = {background = Screen.colors.LightMagenta, bold = true, reverse = true}, + [7] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, + [8] = {bold = true, foreground = Screen.colors.SeaGreen4}, + [9] = {background = Screen.colors.LightGrey, underline = true}, + [10] = {background = Screen.colors.LightGrey, underline = true, bold = true, foreground = Screen.colors.Magenta}, + [11] = {bold = true, foreground = Screen.colors.Magenta}, + [12] = {background = Screen.colors.Red, bold = true, foreground = Screen.colors.Blue1}, + [13] = {background = Screen.colors.WebGray} + } + + local function with_ext_multigrid(multigrid) + local screen + before_each(function() + screen = Screen.new(40,7) + screen:attach({ext_multigrid=multigrid}) + screen:set_default_attr_ids(attrs) + end) + + it('can be created and reconfigured', function() + local buf = meths.create_buf(false,false) + local win = meths.open_win(buf, false, 20, 2, {relative='editor', row=2, col=5}) + meths.win_set_option(win , 'winhl', 'Normal:PMenu') + local expected_pos = { + [3]={{id=1001}, 'NW', 1, 2, 5, true}, + } + + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1: }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^ | + {0:~ }| + {0:~ }{1: }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }| + {0:~ }| + | + ]]) + end + + + meths.win_config(win,0,0,{relative='editor', row=0, col=10}) + expected_pos[3][4] = 0 + expected_pos[3][5] = 10 + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1: }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^ {1: } | + {0:~ }{2:~ }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + end + end) + + it('API has proper error messages', function() + local buf = meths.create_buf(false,false) + eq({false, "Invalid options key 'bork'"}, + meth_pcall(meths.open_win,buf, false, 20, 2, {bork=true})) + eq({false, "'win' option is only valid with relative='win'"}, + meth_pcall(meths.open_win,buf, false, 20, 2, {relative='editor',row=0,col=0,win=0})) + eq({false, "Only one of 'relative' and 'external' should be used"}, + meth_pcall(meths.open_win,buf, false, 20, 2, {relative='editor',row=0,col=0,external=true})) + eq({false, "Invalid value of 'relative' option"}, + meth_pcall(meths.open_win,buf, false, 20, 2, {relative='shell',row=0,col=0})) + eq({false, "Invalid value of 'anchor' option"}, + meth_pcall(meths.open_win,buf, false, 20, 2, {relative='editor',row=0,col=0,anchor='bottom'})) + eq({false, "All of 'relative', 'row', and 'col' has to be specified at once"}, + meth_pcall(meths.open_win,buf, false, 20, 2, {relative='editor'})) + end) + + it('can be placed relative window or cursor', function() + screen:try_resize(40,9) + meths.buf_set_lines(0, 0, -1, true, {'just some', 'example text'}) + feed('gge') + local oldwin = meths.get_current_win() + command('below split') + if multigrid then + screen:expect([[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + [3:----------------------------------------]| + [3:----------------------------------------]| + [3:----------------------------------------]| + {4:[No Name] [+] }| + | + ## grid 2 + just some | + example text | + {0:~ }| + ## grid 3 + jus^t some | + example text | + {0:~ }| + ]]) + else + screen:expect([[ + just some | + example text | + {0:~ }| + {5:[No Name] [+] }| + jus^t some | + example text | + {0:~ }| + {4:[No Name] [+] }| + | + ]]) + end + + local buf = meths.create_buf(false,false) + -- no 'win' arg, relative default window + local win = meths.open_win(buf, false, 20, 2, {relative='win', row=0, col=10}) + meths.win_set_option(win, 'winhl', 'Normal:PMenu') + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + [3:----------------------------------------]| + [3:----------------------------------------]| + [3:----------------------------------------]| + {4:[No Name] [+] }| + | + ## grid 2 + just some | + example text | + {0:~ }| + ## grid 3 + jus^t some | + example text | + {0:~ }| + ## grid 4 + {1: }| + {2:~ }| + ]], float_pos={ + [4] = {{id = 1002}, "NW", 3, 0, 10, true} + }} + else + screen:expect([[ + just some | + example text | + {0:~ }| + {5:[No Name] [+] }| + jus^t some {1: } | + example te{2:~ } | + {0:~ }| + {4:[No Name] [+] }| + | + ]]) + end + + meths.win_config(win, -1, -1, {relative='cursor', row=1, col=-2}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + [3:----------------------------------------]| + [3:----------------------------------------]| + [3:----------------------------------------]| + {4:[No Name] [+] }| + | + ## grid 2 + just some | + example text | + {0:~ }| + ## grid 3 + jus^t some | + example text | + {0:~ }| + ## grid 4 + {1: }| + {2:~ }| + ]], float_pos={ + [4] = {{id = 1002}, "NW", 3, 1, 1, true} + }} + else + screen:expect([[ + just some | + example text | + {0:~ }| + {5:[No Name] [+] }| + jus^t some | + e{1: } | + {0:~}{2:~ }{0: }| + {4:[No Name] [+] }| + | + ]]) + end + + meths.win_config(win, -1, -1, {relative='cursor', row=0, col=0, anchor='SW'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + [3:----------------------------------------]| + [3:----------------------------------------]| + [3:----------------------------------------]| + {4:[No Name] [+] }| + | + ## grid 2 + just some | + example text | + {0:~ }| + ## grid 3 + jus^t some | + example text | + {0:~ }| + ## grid 4 + {1: }| + {2:~ }| + ]], float_pos={ + [4] = {{id = 1002}, "SW", 3, 0, 3, true} + }} + else + screen:expect([[ + just some | + example text | + {0:~ }{1: }{0: }| + {5:[No}{2:~ }{5: }| + jus^t some | + example text | + {0:~ }| + {4:[No Name] [+] }| + | + ]]) + end + + + meths.win_config(win, -1, -1, {relative='win', win=oldwin, row=1, col=10, anchor='NW'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + [3:----------------------------------------]| + [3:----------------------------------------]| + [3:----------------------------------------]| + {4:[No Name] [+] }| + | + ## grid 2 + just some | + example text | + {0:~ }| + ## grid 3 + jus^t some | + example text | + {0:~ }| + ## grid 4 + {1: }| + {2:~ }| + ]], float_pos={ + [4] = {{id = 1002}, "NW", 2, 1, 10, true} + }} + else + screen:expect([[ + just some | + example te{1: } | + {0:~ }{2:~ }{0: }| + {5:[No Name] [+] }| + jus^t some | + example text | + {0:~ }| + {4:[No Name] [+] }| + | + ]]) + end + + meths.win_config(win, -1, -1, {relative='win', win=oldwin, row=3, col=39, anchor='SE'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + [3:----------------------------------------]| + [3:----------------------------------------]| + [3:----------------------------------------]| + {4:[No Name] [+] }| + | + ## grid 2 + just some | + example text | + {0:~ }| + ## grid 3 + jus^t some | + example text | + {0:~ }| + ## grid 4 + {1: }| + {2:~ }| + ]], float_pos={ + [4] = {{id = 1002}, "SE", 2, 3, 39, true} + }} + else + screen:expect([[ + just some | + example text {1: } | + {0:~ }{2:~ }{0: }| + {5:[No Name] [+] }| + jus^t some | + example text | + {0:~ }| + {4:[No Name] [+] }| + | + ]]) + end + + meths.win_config(win, -1, -1, {relative='win', win=0, row=0, col=50, anchor='NE'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + [3:----------------------------------------]| + [3:----------------------------------------]| + [3:----------------------------------------]| + {4:[No Name] [+] }| + | + ## grid 2 + just some | + example text | + {0:~ }| + ## grid 3 + jus^t some | + example text | + {0:~ }| + ## grid 4 + {1: }| + {2:~ }| + ]], float_pos={ + [4] = {{id = 1002}, "NE", 3, 0, 50, true} + }} + else + screen:expect([[ + just some | + example text | + {0:~ }| + {5:[No Name] [+] }| + jus^t some {1: }| + example text {2:~ }| + {0:~ }| + {4:[No Name] [+] }| + | + ]]) + end + end) + + if multigrid then + pending("supports second UI without multigrid", function() + local session2 = helpers.connect(eval('v:servername')) + print(session2:request("nvim_eval", "2+2")) + local screen2 = Screen.new(40,7) + screen2:attach(nil, session2) + screen2:set_default_attr_ids(attrs) + local buf = meths.create_buf(false,false) + local win = meths.open_win(buf, true, 20, 2, {relative='editor', row=2, col=5}) + meths.win_set_option(win, 'winhl', 'Normal:PMenu') + local expected_pos = { + [2]={{id=1001}, 'NW', 1, 2, 5} + } + screen:expect{grid=[[ + ## grid 1 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ## grid 2 + {1:^ }| + {2:~ }| + ]], float_pos=expected_pos} + screen2:expect([[ + | + {0:~ }| + {0:~ }{1:^ }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }| + {0:~ }| + | + ]]) + end) + end + + + it('handles resized screen', function() + local buf = meths.create_buf(false,false) + meths.buf_set_lines(buf, 0, -1, true, {'such', 'very', 'float'}) + local win = meths.open_win(buf, false, 15, 4, {relative='editor', row=2, col=10}) + meths.win_set_option(win , 'winhl', 'Normal:PMenu') + local expected_pos = { + [3]={{id=1001}, 'NW', 1, 2, 10, true}, + } + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:such }| + {1:very }| + {1:float }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^ | + {0:~ }| + {0:~ }{1:such }{0: }| + {0:~ }{1:very }{0: }| + {0:~ }{1:float }{0: }| + {0:~ }{2:~ }{0: }| + | + ]]) + end + + screen:try_resize(40,5) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:such }| + {1:very }| + {1:float }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^ {1:such } | + {0:~ }{1:very }{0: }| + {0:~ }{1:float }{0: }| + {0:~ }{2:~ }{0: }| + | + ]]) + end + + screen:try_resize(40,4) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + ^ | + {0:~ }| + {0:~ }| + ## grid 3 + {1:such }| + {1:very }| + {1:float }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^ {1:such } | + {0:~ }{1:very }{0: }| + {0:~ }{1:float }{0: }| + | + ]]) + end + + screen:try_resize(40,3) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + ^ | + {0:~ }| + ## grid 3 + {1:such }| + {1:very }| + {1:float }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^ {1:such } | + {0:~ }{1:very }{0: }| + | + ]]) + end + feed('<c-w>wjj') + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + | + {0:~ }| + ## grid 3 + {1:such }| + {1:very }| + {1:^float }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + {1:very } | + {0:~ }{1:^float }{0: }| + | + ]]) + end + + screen:try_resize(40,7) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:such }| + {1:very }| + {1:^float }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + | + {0:~ }| + {0:~ }{1:very }{0: }| + {0:~ }{1:^float }{0: }| + {0:~ }| + {0:~ }| + | + ]]) + end + + meths.win_config(win, -1, 3, {}) + feed('gg') + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:^such }| + {1:very }| + {1:float }| + ]], float_pos=expected_pos} + else + screen:expect([[ + | + {0:~ }| + {0:~ }{1:^such }{0: }| + {0:~ }{1:very }{0: }| + {0:~ }{1:float }{0: }| + {0:~ }| + | + ]]) + end + + screen:try_resize(26,7) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:--------------------------]| + [2:--------------------------]| + [2:--------------------------]| + [2:--------------------------]| + [2:--------------------------]| + [2:--------------------------]| + | + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:^such }| + {1:very }| + {1:float }| + ]], float_pos=expected_pos} + else + screen:expect([[ + | + {0:~ }| + {0:~ }{1:^such }{0: }| + {0:~ }{1:very }{0: }| + {0:~ }{1:float }{0: }| + {0:~ }| + | + ]]) + end + + screen:try_resize(25,7) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + | + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:^such }| + {1:very }| + {1:float }| + ]], float_pos=expected_pos} + else + screen:expect([[ + | + {0:~ }| + {0:~ }{1:^such }| + {0:~ }{1:very }| + {0:~ }{1:float }| + {0:~ }| + | + ]]) + end + + screen:try_resize(24,7) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:------------------------]| + [2:------------------------]| + [2:------------------------]| + [2:------------------------]| + [2:------------------------]| + [2:------------------------]| + | + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:^such }| + {1:very }| + {1:float }| + ]], float_pos=expected_pos} + else + screen:expect([[ + | + {0:~ }| + {0:~ }{1:^such }| + {0:~ }{1:very }| + {0:~ }{1:float }| + {0:~ }| + | + ]]) + end + + screen:try_resize(16,7) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------]| + [2:----------------]| + [2:----------------]| + [2:----------------]| + [2:----------------]| + [2:----------------]| + | + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:^such }| + {1:very }| + {1:float }| + ]], float_pos=expected_pos} + else + screen:expect([[ + | + {0:~ }| + {0:~}{1:^such }| + {0:~}{1:very }| + {0:~}{1:float }| + {0:~ }| + | + ]]) + end + + screen:try_resize(15,7) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:---------------]| + [2:---------------]| + [2:---------------]| + [2:---------------]| + [2:---------------]| + [2:---------------]| + | + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:^such }| + {1:very }| + {1:float }| + ]], float_pos=expected_pos} + else + screen:expect([[ + | + {0:~ }| + {1:^such }| + {1:very }| + {1:float }| + {0:~ }| + | + ]]) + end + + screen:try_resize(14,7) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:--------------]| + [2:--------------]| + [2:--------------]| + [2:--------------]| + [2:--------------]| + [2:--------------]| + | + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:^such }| + {1:very }| + {1:float }| + ]], float_pos=expected_pos} + else + screen:expect([[ + | + {0:~ }| + {1:^such }| + {1:very }| + {1:float }| + {0:~ }| + | + ]]) + end + + screen:try_resize(12,7) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:------------]| + [2:------------]| + [2:------------]| + [2:------------]| + [2:------------]| + [2:------------]| + | + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:^such }| + {1:very }| + {1:float }| + ]], float_pos=expected_pos} + else + screen:expect([[ + | + {0:~ }| + {1:^such }| + {1:very }| + {1:float }| + {0:~ }| + | + ]]) + end + + -- Doesn't make much sense, but check nvim doesn't crash + screen:try_resize(1,1) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:------------]| + | + ## grid 2 + | + ## grid 3 + {1:^such }| + {1:very }| + {1:float }| + ]], float_pos=expected_pos} + else + screen:expect([[ + {1:^such }| + | + ]]) + end + + screen:try_resize(40,7) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:^such }| + {1:very }| + {1:float }| + ]], float_pos=expected_pos} + else + screen:expect([[ + | + {0:~ }| + {0:~ }{1:^such }{0: }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + end + end) + + describe('and completion', function() + before_each(function() + local buf = meths.create_buf(false,false) + local win = meths.open_win(buf, true, 12, 4, {relative='editor', row=2, col=5}) + meths.win_set_option(win , 'winhl', 'Normal:ErrorMsg') + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {7:^ }| + {12:~ }| + {12:~ }| + {12:~ }| + ]], float_pos={ + [3] = {{ id = 1001 }, "NW", 1, 2, 5, true}, + }} + else + screen:expect([[ + | + {0:~ }| + {0:~ }{7:^ }{0: }| + {0:~ }{12:~ }{0: }| + {0:~ }{12:~ }{0: }| + {0:~ }{12:~ }{0: }| + | + ]]) + end + end) + + it('with builtin popupmenu', function() + feed('ix ') + funcs.complete(3, {'aa', 'word', 'longtext'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {3:-- INSERT --} | + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {7:x aa^ }| + {12:~ }| + {12:~ }| + {12:~ }| + ## grid 4 + {13: aa }| + {1: word }| + {1: longtext }| + ]], float_pos={ + [3] = {{ id = 1001 }, "NW", 1, 2, 5, true}, + [4] = {{ id = -1 }, "NW", 3, 1, 1, false} + }} + else + screen:expect([[ + | + {0:~ }| + {0:~ }{7:x aa^ }{0: }| + {0:~ }{12:~}{13: aa }{0: }| + {0:~ }{12:~}{1: word }{0: }| + {0:~ }{12:~}{1: longtext }{0: }| + {3:-- INSERT --} | + ]]) + end + + feed('<esc>') + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {7:x a^a }| + {12:~ }| + {12:~ }| + {12:~ }| + ]], float_pos={ + [3] = {{ id = 1001 }, "NW", 1, 2, 5, true}, + }} + + else + screen:expect([[ + | + {0:~ }| + {0:~ }{7:x a^a }{0: }| + {0:~ }{12:~ }{0: }| + {0:~ }{12:~ }{0: }| + {0:~ }{12:~ }{0: }| + | + ]]) + end + + feed('<c-w>wi') + funcs.complete(1, {'xx', 'yy', 'zz'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {3:-- INSERT --} | + ## grid 2 + xx^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {7:x aa }| + {12:~ }| + {12:~ }| + {12:~ }| + ## grid 4 + {13:xx }| + {1:yy }| + {1:zz }| + ]], float_pos={ + [3] = {{ id = 1001 }, "NW", 1, 2, 5, true}, + [4] = {{ id = -1 }, "NW", 2, 1, 0, false} + }} + else + screen:expect([[ + xx^ | + {13:xx }{0: }| + {1:yy }{7: }{0: }| + {1:zz }{12: }{0: }| + {0:~ }{12:~ }{0: }| + {0:~ }{12:~ }{0: }| + {3:-- INSERT --} | + ]]) + end + + feed('<c-y>') + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {3:-- INSERT --} | + ## grid 2 + xx^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {7:x aa }| + {12:~ }| + {12:~ }| + {12:~ }| + ]], float_pos={ + [3] = {{ id = 1001 }, "NW", 1, 2, 5, true}, + }} + else + screen:expect([[ + xx^ | + {0:~ }| + {0:~ }{7:x aa }{0: }| + {0:~ }{12:~ }{0: }| + {0:~ }{12:~ }{0: }| + {0:~ }{12:~ }{0: }| + {3:-- INSERT --} | + ]]) + end + end) + + it('with ext_popupmenu', function() + screen:set_option('ext_popupmenu', true) + feed('ix ') + funcs.complete(3, {'aa', 'word', 'longtext'}) + local items = {{"aa", "", "", ""}, {"word", "", "", ""}, {"longtext", "", "", ""}} + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {3:-- INSERT --} | + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {7:x aa^ }| + {12:~ }| + {12:~ }| + {12:~ }| + ]], float_pos={ + [3] = {{ id = 1001 }, "NW", 1, 2, 5, true}, + }, popupmenu={ + anchor = {0, 2, 3}, items = items, pos = 0 + }} + else + screen:expect{grid=[[ + | + {0:~ }| + {0:~ }{7:x aa^ }{0: }| + {0:~ }{12:~ }{0: }| + {0:~ }{12:~ }{0: }| + {0:~ }{12:~ }{0: }| + {3:-- INSERT --} | + ]], popupmenu={ + anchor = {2, 7}, items = items, pos = 0 + }} + end + + feed('<esc>') + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {7:x a^a }| + {12:~ }| + {12:~ }| + {12:~ }| + ]], float_pos={ + [3] = {{ id = 1001 }, "NW", 1, 2, 5, true}, + }} + else + screen:expect([[ + | + {0:~ }| + {0:~ }{7:x a^a }{0: }| + {0:~ }{12:~ }{0: }| + {0:~ }{12:~ }{0: }| + {0:~ }{12:~ }{0: }| + | + ]]) + end + + feed('<c-w>wi') + funcs.complete(1, {'xx', 'yy', 'zz'}) + items = {{"xx", "", "", ""}, {"yy", "", "", ""}, {"zz", "", "", ""}} + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {3:-- INSERT --} | + ## grid 2 + xx^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {7:x aa }| + {12:~ }| + {12:~ }| + {12:~ }| + ]], float_pos={ + [3] = {{ id = 1001 }, "NW", 1, 2, 5, true}, + }, popupmenu={ + anchor = {0, 0, 2}, items = items, pos = 0 + }} + else + screen:expect{grid=[[ + xx^ | + {0:~ }| + {0:~ }{7:x aa }{0: }| + {0:~ }{12:~ }{0: }| + {0:~ }{12:~ }{0: }| + {0:~ }{12:~ }{0: }| + {3:-- INSERT --} | + ]], popupmenu={ + anchor = {0, 0}, items = items, pos = 0 + }} + end + + feed('<c-y>') + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {3:-- INSERT --} | + ## grid 2 + xx^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {7:x aa }| + {12:~ }| + {12:~ }| + {12:~ }| + ]], float_pos={ + [3] = {{ id = 1001 }, "NW", 1, 2, 5, true}, + }} + else + screen:expect([[ + xx^ | + {0:~ }| + {0:~ }{7:x aa }{0: }| + {0:~ }{12:~ }{0: }| + {0:~ }{12:~ }{0: }| + {0:~ }{12:~ }{0: }| + {3:-- INSERT --} | + ]]) + end + end) + + end) + + + describe("handles :wincmd", function() + local win + local expected_pos + before_each(function() + -- the default, but be explicit: + command("set laststatus=1") + command("set hidden") + meths.buf_set_lines(0,0,-1,true,{"x"}) + local buf = meths.create_buf(false,false) + win = meths.open_win(buf, false, 20, 2, {relative='editor', row=2, col=5}) + meths.buf_set_lines(buf,0,-1,true,{"y"}) + meths.win_set_option(win , 'winhl', 'Normal:PMenu') + expected_pos = { + [3]={{id=1001}, 'NW', 1, 2, 5, true} + } + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + ^x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^x | + {0:~ }| + {0:~ }{1:y }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }| + {0:~ }| + | + ]]) + end + end) + + it("w", function() + feed("<c-w>w") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:^y }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + x | + {0:~ }| + {0:~ }{1:^y }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }| + {0:~ }| + | + ]]) + end + + feed("<c-w>w") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + ^x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^x | + {0:~ }| + {0:~ }{1:y }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }| + {0:~ }| + | + ]]) + end + end) + + it("w with focusable=false", function() + meths.win_config(win, -1, -1, {focusable=false}) + expected_pos[3][6] = false + feed("<c-w>wi") -- i to provoke redraw + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {3:-- INSERT --} | + ## grid 2 + ^x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^x | + {0:~ }| + {0:~ }{1:y }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + end + + feed("<esc><c-w>w") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + ^x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^x | + {0:~ }| + {0:~ }{1:y }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }| + {0:~ }| + | + ]]) + end + end) + + it("W", function() + feed("<c-w>W") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:^y }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + x | + {0:~ }| + {0:~ }{1:^y }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }| + {0:~ }| + | + ]]) + end + + feed("<c-w>W") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + ^x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^x | + {0:~ }| + {0:~ }{1:y }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }| + {0:~ }| + | + ]]) + end + end) + + it("focus by mouse", function() + if multigrid then + meths.input_mouse('left', 'press', '', 3, 0, 0) + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:^y }| + {2:~ }| + ]], float_pos=expected_pos} + else + meths.input_mouse('left', 'press', '', 0, 2, 5) + screen:expect([[ + x | + {0:~ }| + {0:~ }{1:^y }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }| + {0:~ }| + | + ]]) + end + + if multigrid then + meths.input_mouse('left', 'press', '', 1, 0, 0) + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + ^x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ]], float_pos=expected_pos} + else + meths.input_mouse('left', 'press', '', 0, 0, 0) + screen:expect([[ + ^x | + {0:~ }| + {0:~ }{1:y }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }| + {0:~ }| + | + ]]) + end + end) + + it("focus by mouse (focusable=false)", function() + meths.win_config(win, -1, -1, {focusable=false}) + meths.buf_set_lines(0, -1, -1, true, {"a"}) + expected_pos[3][6] = false + if multigrid then + meths.input_mouse('left', 'press', '', 3, 0, 0) + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + ^x | + a | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ]], float_pos=expected_pos} + else + meths.input_mouse('left', 'press', '', 0, 2, 5) + screen:expect([[ + x | + ^a | + {0:~ }{1:y }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }| + {0:~ }| + | + ]]) + end + + if multigrid then + meths.input_mouse('left', 'press', '', 1, 0, 0) + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + ^x | + a | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ]], float_pos=expected_pos, unchanged=true} + else + meths.input_mouse('left', 'press', '', 0, 0, 0) + screen:expect([[ + ^x | + a | + {0:~ }{1:y }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }| + {0:~ }| + | + ]]) + end + end) + + + it("j", function() + feed("<c-w>ji") -- INSERT to trigger screen change + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {3:-- INSERT --} | + ## grid 2 + ^x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^x | + {0:~ }| + {0:~ }{1:y }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }| + {0:~ }| + {3:-- INSERT --} | + ]]) + end + + feed("<esc><c-w>w") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:^y }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + x | + {0:~ }| + {0:~ }{1:^y }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }| + {0:~ }| + | + ]]) + end + + feed("<c-w>j") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + ^x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^x | + {0:~ }| + {0:~ }{1:y }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }| + {0:~ }| + | + ]]) + end + + end) + + it("s :split (non-float)", function() + feed("<c-w>s") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [4:----------------------------------------]| + [4:----------------------------------------]| + {4:[No Name] [+] }| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + | + ## grid 2 + x | + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + ^x | + {0:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^x | + {0:~ }| + {4:[No N}{1:y }{4: }| + x {2:~ } | + {0:~ }| + {5:[No Name] [+] }| + | + ]]) + end + + feed("<c-w>w") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [4:----------------------------------------]| + [4:----------------------------------------]| + {5:[No Name] [+] }| + [2:----------------------------------------]| + [2:----------------------------------------]| + {4:[No Name] [+] }| + | + ## grid 2 + ^x | + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + x | + {0:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + x | + {0:~ }| + {5:[No N}{1:y }{5: }| + ^x {2:~ } | + {0:~ }| + {4:[No Name] [+] }| + | + ]]) + end + + feed("<c-w>w") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [4:----------------------------------------]| + [4:----------------------------------------]| + {5:[No Name] [+] }| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + | + ## grid 2 + x | + {0:~ }| + ## grid 3 + {1:^y }| + {2:~ }| + ## grid 4 + x | + {0:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + x | + {0:~ }| + {5:[No N}{1:^y }{5: }| + x {2:~ } | + {0:~ }| + {5:[No Name] [+] }| + | + ]]) + end + + + feed("<c-w>w") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [4:----------------------------------------]| + [4:----------------------------------------]| + {4:[No Name] [+] }| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + | + ## grid 2 + x | + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + ^x | + {0:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^x | + {0:~ }| + {4:[No N}{1:y }{4: }| + x {2:~ } | + {0:~ }| + {5:[No Name] [+] }| + | + ]]) + end + end) + + it("s :split (float)", function() + feed("<c-w>w<c-w>s") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [4:----------------------------------------]| + [4:----------------------------------------]| + {4:[No Name] [+] }| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + | + ## grid 2 + x | + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + {1:^y }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + {1:^y }| + {2:~ }| + {4:[No N}{1:y }{4: }| + x {2:~ } | + {0:~ }| + {5:[No Name] [+] }| + | + ]]) + end + + feed(":set winhighlight=<cr><c-l>") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [4:----------------------------------------]| + [4:----------------------------------------]| + {4:[No Name] [+] }| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + | + ## grid 2 + x | + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + ^y | + {0:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^y | + {0:~ }| + {4:[No N}{1:y }{4: }| + x {2:~ } | + {0:~ }| + {5:[No Name] [+] }| + | + ]]) + end + + + feed("<c-w>j") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [4:----------------------------------------]| + [4:----------------------------------------]| + {5:[No Name] [+] }| + [2:----------------------------------------]| + [2:----------------------------------------]| + {4:[No Name] [+] }| + | + ## grid 2 + ^x | + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + y | + {0:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + y | + {0:~ }| + {5:[No N}{1:y }{5: }| + ^x {2:~ } | + {0:~ }| + {4:[No Name] [+] }| + | + ]]) + end + + feed("<c-w>ji") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [4:----------------------------------------]| + [4:----------------------------------------]| + {5:[No Name] [+] }| + [2:----------------------------------------]| + [2:----------------------------------------]| + {4:[No Name] [+] }| + {3:-- INSERT --} | + ## grid 2 + ^x | + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + y | + {0:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + y | + {0:~ }| + {5:[No N}{1:y }{5: }| + ^x {2:~ } | + {0:~ }| + {4:[No Name] [+] }| + {3:-- INSERT --} | + ]]) + end + end) + + it(":new (non-float)", function() + feed(":new<cr>") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [4:----------------------------------------]| + [4:----------------------------------------]| + {4:[No Name] }| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + :new | + ## grid 2 + x | + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + ^ | + {0:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^ | + {0:~ }| + {4:[No N}{1:y }{4: }| + x {2:~ } | + {0:~ }| + {5:[No Name] [+] }| + :new | + ]]) + end + end) + + it(":new (float)", function() + feed("<c-w>w:new<cr>") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [4:----------------------------------------]| + [4:----------------------------------------]| + {4:[No Name] }| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + :new | + ## grid 2 + x | + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + ^ | + {0:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^ | + {0:~ }| + {4:[No N}{1:y }{4: }| + x {2:~ } | + {0:~ }| + {5:[No Name] [+] }| + :new | + ]]) + end + end) + + it("v :vsplit (non-float)", function() + feed("<c-w>v") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [4:--------------------]{5:│}[2:-------------------]| + [4:--------------------]{5:│}[2:-------------------]| + [4:--------------------]{5:│}[2:-------------------]| + [4:--------------------]{5:│}[2:-------------------]| + [4:--------------------]{5:│}[2:-------------------]| + {4:[No Name] [+] }{5:[No Name] [+] }| + | + ## grid 2 + x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + ^x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^x {5:│}x | + {0:~ }{5:│}{0:~ }| + {0:~ }{1:y }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }{5:│}{0:~ }| + {4:[No Name] [+] }{5:[No Name] [+] }| + | + ]]) + end + end) + + it(":vnew (non-float)", function() + feed(":vnew<cr>") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [4:--------------------]{5:│}[2:-------------------]| + [4:--------------------]{5:│}[2:-------------------]| + [4:--------------------]{5:│}[2:-------------------]| + [4:--------------------]{5:│}[2:-------------------]| + [4:--------------------]{5:│}[2:-------------------]| + {4:[No Name] }{5:[No Name] [+] }| + :vnew | + ## grid 2 + x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^ {5:│}x | + {0:~ }{5:│}{0:~ }| + {0:~ }{1:y }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }{5:│}{0:~ }| + {4:[No Name] }{5:[No Name] [+] }| + :vnew | + ]]) + end + end) + + it(":vnew (float)", function() + feed("<c-w>w:vnew<cr>") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [4:--------------------]{5:│}[2:-------------------]| + [4:--------------------]{5:│}[2:-------------------]| + [4:--------------------]{5:│}[2:-------------------]| + [4:--------------------]{5:│}[2:-------------------]| + [4:--------------------]{5:│}[2:-------------------]| + {4:[No Name] }{5:[No Name] [+] }| + :vnew | + ## grid 2 + x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^ {5:│}x | + {0:~ }{5:│}{0:~ }| + {0:~ }{1:y }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }{5:│}{0:~ }| + {4:[No Name] }{5:[No Name] [+] }| + :vnew | + ]]) + end + end) + + it("q (:quit) last non-float exits nvim", function() + command('autocmd VimLeave * call rpcrequest(1, "exit")') + -- avoid unsaved change in other buffer + feed("<c-w><c-w>:w Xtest_written2<cr><c-w><c-w>") + -- quit in last non-float + feed(":wq Xtest_written<cr>") + local exited = false + local function on_request(name, args) + eq("exit", name) + eq({}, args) + exited = true + return 0 + end + local function on_setup() + feed(":wq Xtest_written<cr>") + end + run(on_request, nil, on_setup) + os.remove('Xtest_written') + os.remove('Xtest_written2') + eq(exited, true) + end) + + it("o (:only) non-float", function() + feed("<c-w>o") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + ^x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ]]} + else + screen:expect([[ + ^x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + end + end) + + it("o (:only) float fails", function() + feed("<c-w>w<c-w>o") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {4: }| + {7:E5601: Cannot close window, only floatin}| + {7:g window would remain} | + {8:Press ENTER or type command to continue}^ | + ## grid 2 + x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + x | + {0:~ }| + {0:~ }{1:y }{0: }| + {4: }| + {7:E5601: Cannot close window, only floatin}| + {7:g window would remain} | + {8:Press ENTER or type command to continue}^ | + ]]) + end + + -- test message clear + feed('<cr>') + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + | + ## grid 2 + x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:^y }| + {2:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + x | + {0:~ }| + {0:~ }{1:^y }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }| + {0:~ }| + | + ]]) + end + end) + + it("o (:only) non-float with split", function() + feed("<c-w>s") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [4:----------------------------------------]| + [4:----------------------------------------]| + {4:[No Name] [+] }| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + | + ## grid 2 + x | + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + ^x | + {0:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + ^x | + {0:~ }| + {4:[No N}{1:y }{4: }| + x {2:~ } | + {0:~ }| + {5:[No Name] [+] }| + | + ]]) + end + + feed("<c-w>o") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + | + ## grid 4 + ^x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ]]} + else + screen:expect([[ + ^x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]) + end + end) + + it("o (:only) float with split", function() + feed("<c-w>s<c-w>W") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [4:----------------------------------------]| + [4:----------------------------------------]| + {5:[No Name] [+] }| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + | + ## grid 2 + x | + {0:~ }| + ## grid 3 + {1:^y }| + {2:~ }| + ## grid 4 + x | + {0:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + x | + {0:~ }| + {5:[No N}{1:^y }{5: }| + x {2:~ } | + {0:~ }| + {5:[No Name] [+] }| + | + ]]) + end + + feed("<c-w>o") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [4:----------------------------------------]| + [4:----------------------------------------]| + {5:[No Name] [+] }| + {4: }| + {7:E5601: Cannot close window, only floatin}| + {7:g window would remain} | + {8:Press ENTER or type command to continue}^ | + ## grid 2 + x | + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + x | + {0:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + x | + {0:~ }| + {5:[No N}{1:y }{5: }| + {4: }| + {7:E5601: Cannot close window, only floatin}| + {7:g window would remain} | + {8:Press ENTER or type command to continue}^ | + ]]) + end + end) + + it("J (float)", function() + feed("<c-w>w<c-w>J") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + [3:----------------------------------------]| + [3:----------------------------------------]| + {4:[No Name] [+] }| + | + ## grid 2 + x | + {0:~ }| + ## grid 3 + {1:^y }| + {2:~ }| + ]]} + else + screen:expect([[ + x | + {0:~ }| + {5:[No Name] [+] }| + {1:^y }| + {2:~ }| + {4:[No Name] [+] }| + | + ]]) + end + + if multigrid then + meths.win_config(0,-1,-1,{external=true}) + expected_pos = {[3]={external=true}} + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + | + ## grid 2 + x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:^y }| + {2:~ }| + ]], float_pos=expected_pos} + else + eq({false, "UI doesn't support external windows"}, + meth_pcall(meths.win_config, 0,-1,-1,{external=true})) + return + end + + feed("<c-w>J") + if multigrid then + screen:expect([[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + [3:----------------------------------------]| + [3:----------------------------------------]| + {4:[No Name] [+] }| + | + ## grid 2 + x | + {0:~ }| + ## grid 3 + {1:^y }| + {2:~ }| + ]]) + end + + end) + + it('movements with nested split layout', function() + command("set hidden") + feed("<c-w>s<c-w>v<c-w>b<c-w>v") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [5:--------------------]{5:│}[4:-------------------]| + [5:--------------------]{5:│}[4:-------------------]| + {5:[No Name] [+] [No Name] [+] }| + [6:--------------------]{5:│}[2:-------------------]| + [6:--------------------]{5:│}[2:-------------------]| + {4:[No Name] [+] }{5:[No Name] [+] }| + | + ## grid 2 + x | + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + x | + {0:~ }| + ## grid 5 + x | + {0:~ }| + ## grid 6 + ^x | + {0:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + x {5:│}x | + {0:~ }{5:│}{0:~ }| + {5:[No N}{1:y }{5:Name] [+] }| + ^x {2:~ } | + {0:~ }{5:│}{0:~ }| + {4:[No Name] [+] }{5:[No Name] [+] }| + | + ]]) + end + + -- verify that N<c-w>w works + for i = 1,5 do + feed(i.."<c-w>w") + feed_command("enew") + curbufmeths.set_lines(0,-1,true,{tostring(i)}) + end + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [5:-------------------]{5:│}[4:--------------------]| + [5:-------------------]{5:│}[4:--------------------]| + {5:[No Name] [+] [No Name] [+] }| + [6:-------------------]{5:│}[2:--------------------]| + [6:-------------------]{5:│}[2:--------------------]| + {5:[No Name] [+] [No Name] [+] }| + :enew | + ## grid 2 + 4 | + {0:~ }| + ## grid 3 + ^5 | + {0:~ }| + ## grid 4 + 2 | + {0:~ }| + ## grid 5 + 1 | + {0:~ }| + ## grid 6 + 3 | + {0:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + 1 {5:│}2 | + {0:~ }{5:│}{0:~ }| + {5:[No N}^5 {5:ame] [+] }| + 3 {0:~ } | + {0:~ }{5:│}{0:~ }| + {5:[No Name] [+] [No Name] [+] }| + :enew | + ]]) + end + + local movements = { + w={2,3,4,5,1}, + W={5,1,2,3,4}, + h={1,1,3,3,3}, + j={3,3,3,4,4}, + k={1,2,1,1,1}, + l={2,2,4,4,4}, + t={1,1,1,1,1}, + b={4,4,4,4,4}, + } + + for k,v in pairs(movements) do + for i = 1,5 do + feed(i.."<c-w>w") + feed('<c-w>'..k) + local nr = funcs.winnr() + eq(v[i],nr, "when using <c-w>"..k.." from window "..i) + end + end + + for i = 1,5 do + feed(i.."<c-w>w") + for j = 1,5 do + if j ~= i then + feed(j.."<c-w>w") + feed('<c-w>p') + local nr = funcs.winnr() + eq(i,nr, "when using <c-w>p to window "..i.." from window "..j) + end + end + end + + end) + + it(":tabnew and :tabnext", function() + feed(":tabnew<cr>") + if multigrid then + -- grid is not freed, but float is marked as closed (should it rather be "invisible"?) + screen:expect{grid=[[ + ## grid 1 + {9: }{10:2}{9:+ [No Name] }{3: [No Name] }{5: }{9:X}| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + :tabnew | + ## grid 2 + x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ]]} + else + screen:expect([[ + {9: }{10:2}{9:+ [No Name] }{3: [No Name] }{5: }{9:X}| + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + :tabnew | + ]]) + end + + feed(":tabnext<cr>") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + {3: }{11:2}{3:+ [No Name] }{9: [No Name] }{5: }{9:X}| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {4:[No Name] [+] }| + :tabnext | + ## grid 2 + ^x | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ]], float_pos=expected_pos} + else + screen:expect([[ + {3: }{11:2}{3:+ [No Name] }{9: [No Name] }{5: }{9:X}| + ^x | + {0:~ }{1:y }{0: }| + {0:~ }{2:~ }{0: }| + {0:~ }| + {4:[No Name] [+] }| + :tabnext | + ]]) + end + + feed(":tabnext<cr>") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + {9: }{10:2}{9:+ [No Name] }{3: [No Name] }{5: }{9:X}| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + :tabnext | + ## grid 2 + x | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ]]} + else + screen:expect([[ + {9: }{10:2}{9:+ [No Name] }{3: [No Name] }{5: }{9:X}| + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + :tabnext | + ]]) + end + end) + + it(":tabnew and :tabnext (external)", function() + if multigrid then + meths.win_config(win,-1,-1,{external=true}) + expected_pos = {[3]={external=true}} + feed(":tabnew<cr>") + screen:expect{grid=[[ + ## grid 1 + {9: + [No Name] }{3: }{11:2}{3:+ [No Name] }{5: }{9:X}| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + :tabnew | + ## grid 2 + x | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ]], float_pos=expected_pos} + else + eq({false, "UI doesn't support external windows"}, + meth_pcall(meths.win_config, 0,-1,-1,{external=true})) + end + + feed(":tabnext<cr>") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + {3: }{11:2}{3:+ [No Name] }{9: [No Name] }{5: }{9:X}| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {4:[No Name] [+] }| + :tabnext | + ## grid 2 + ^x | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ]], float_pos=expected_pos} + end + + feed(":tabnext<cr>") + if multigrid then + screen:expect{grid=[[ + ## grid 1 + {9: + [No Name] }{3: }{11:2}{3:+ [No Name] }{5: }{9:X}| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + {4:[No Name] }| + :tabnext | + ## grid 2 + x | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + {1:y }| + {2:~ }| + ## grid 4 + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + ]], float_pos=expected_pos} + end + end) + end) + end + + describe('with ext_multigrid', function() + with_ext_multigrid(true) + end) + describe('without ext_multigrid', function() + with_ext_multigrid(false) + end) + +end) + diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 2eae549ebd..8b1b77eb81 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -158,6 +158,7 @@ function Screen.new(width, height) wildmenu_items = nil, wildmenu_selected = nil, win_position = {}, + float_pos = {}, _session = nil, messages = {}, msg_history = {}, @@ -227,10 +228,9 @@ function Screen:attach(options, session) -- simplify test code by doing the same. self._options.rgb = true end - if self._options.ext_multigrid then + if self._options.ext_multigrid or self._options.ext_float then self._options.ext_linegrid = true end - self._session = session end function Screen:detach() @@ -256,7 +256,7 @@ end -- canonical order of ext keys, used to generate asserts local ext_keys = { 'popupmenu', 'cmdline', 'cmdline_block', 'wildmenu_items', 'wildmenu_pos', - 'messages', 'showmode', 'showcmd', 'ruler', + 'messages', 'showmode', 'showcmd', 'ruler', 'float_pos', } -- Asserts that the screen state eventually matches an expected state @@ -642,7 +642,7 @@ function Screen:_handle_grid_resize(grid, width, height) end if self._cursor.grid == grid then - self._cursor.row = 1 + self._cursor.row = 1 -- -1 ? self._cursor.col = 1 end self._grids[grid] = { @@ -676,7 +676,6 @@ function Screen:_reset() self.wildmenu_pos = nil end - function Screen:_handle_mode_info_set(cursor_style_enabled, mode_info) self._cursor_style_enabled = cursor_style_enabled for _, item in pairs(mode_info) do @@ -713,7 +712,6 @@ end function Screen:_handle_grid_destroy(grid) self._grids[grid] = nil if self._options.ext_multigrid then - assert(self.win_position[grid]) self.win_position[grid] = nil end end @@ -734,6 +732,36 @@ function Screen:_handle_grid_cursor_goto(grid, row, col) self._cursor.col = col + 1 end +function Screen:_handle_win_pos(grid, win, startrow, startcol, width, height) + self.win_position[grid] = { + win = win, + startrow = startrow, + startcol = startcol, + width = width, + height = height + } + self.float_pos[grid] = nil +end + +function Screen:_handle_win_float_pos(grid, ...) + self.win_position[grid] = nil + self.float_pos[grid] = {...} +end + +function Screen:_handle_win_external_pos(grid) + self.win_position[grid] = nil + self.float_pos[grid] = {external=true} +end + +function Screen:_handle_win_hide(grid) + self.win_position[grid] = nil + self.float_pos[grid] = nil +end + +function Screen:_handle_win_close(grid) + self.float_pos[grid] = nil +end + function Screen:_handle_busy_start() self._busy = true end @@ -815,20 +843,6 @@ function Screen:_handle_hl_attr_define(id, rgb_attrs, cterm_attrs, info) self._new_attrs = true end -function Screen:_handle_win_pos(grid, win, startrow, startcol, width, height) - self.win_position[grid] = { - win = win, - startrow = startrow, - startcol = startcol, - width = width, - height = height - } -end - -function Screen:_handle_win_hide(grid) - self.win_position[grid] = nil -end - function Screen:get_hl(val) if self._options.ext_newgrid then return self._attr_table[val][1] @@ -922,8 +936,11 @@ function Screen:_handle_option_set(name, value) self.options[name] = value end -function Screen:_handle_popupmenu_show(items, selected, row, col) - self.popupmenu = {items=items, pos=selected, anchor={row, col}} +function Screen:_handle_popupmenu_show(items, selected, row, col, grid) + if (not self._options.ext_multigrid) and grid == 1 then + grid = nil + end + self.popupmenu = {items=items, pos=selected, anchor={row, col, grid}} end function Screen:_handle_popupmenu_select(selected) @@ -1112,6 +1129,7 @@ function Screen:_extstate_repr(attr_state) showcmd=self:_chunks_repr(self.showcmd, attr_state), ruler=self:_chunks_repr(self.ruler, attr_state), msg_history=msg_history, + float_pos=self.float_pos } end @@ -1146,7 +1164,10 @@ function Screen:redraw_debug(attrs, ignore, timeout) local function notification_cb(method, args) assert(method == 'redraw') for _, update in ipairs(args) do - print(require('inspect')(update)) + -- mode_info_set is quite verbose, comment out the condition to debug it. + if update[1] ~= "mode_info_set" then + print(inspect(update)) + end end self:_redraw(args) self:print_snapshot(attrs, ignore) @@ -1159,7 +1180,7 @@ function Screen:redraw_debug(attrs, ignore, timeout) end function Screen:render(headers, attr_state, preview) - headers = headers and self._options.ext_multigrid + headers = headers and (self._options.ext_multigrid or self._options._debug_float) local rv = {} for igrid,grid in pairs(self._grids) do if headers then @@ -1227,6 +1248,7 @@ function Screen:print_snapshot(attrs, ignore) io.stdout:write( "]]"..attrstr) for _, k in ipairs(ext_keys) do if ext_state[k] ~= nil then + -- TODO(bfredl): improve formating, remove ext metatables io.stdout:write(", "..k.."="..inspect(ext_state[k])) end end diff --git a/test/helpers.lua b/test/helpers.lua index 9f998ef919..e4c3019adc 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -47,8 +47,8 @@ local check_logs_useless_lines = { ['See README_MISSING_SYSCALL_OR_IOCTL for guidance']=3, } -local function eq(expected, actual, ctx) - return assert.are.same(expected, actual, ctx) +local function eq(expected, actual, context) + return assert.are.same(expected, actual, context) end local function neq(expected, actual, context) return assert.are_not.same(expected, actual, context) |