aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/ui.txt18
-rw-r--r--runtime/doc/windows.txt4
-rw-r--r--src/nvim/api/private/helpers.c4
-rw-r--r--src/nvim/api/ui.c2
-rw-r--r--src/nvim/api/ui_events.in.h9
-rw-r--r--src/nvim/api/vim.c78
-rw-r--r--src/nvim/api/window.c38
-rw-r--r--src/nvim/buffer.c8
-rw-r--r--src/nvim/buffer_defs.h31
-rw-r--r--src/nvim/ex_docmd.c12
-rw-r--r--src/nvim/ex_getln.c10
-rw-r--r--src/nvim/globals.h4
-rw-r--r--src/nvim/grid_defs.h7
-rw-r--r--src/nvim/main.c1
-rw-r--r--src/nvim/memory.c7
-rw-r--r--src/nvim/message.c8
-rw-r--r--src/nvim/mouse.c31
-rw-r--r--src/nvim/normal.c2
-rw-r--r--src/nvim/os/input.c1
-rw-r--r--src/nvim/popupmnu.c25
-rw-r--r--src/nvim/popupmnu.h3
-rw-r--r--src/nvim/screen.c77
-rw-r--r--src/nvim/tui/tui.c1
-rw-r--r--src/nvim/ui.c32
-rw-r--r--src/nvim/ui.h3
-rw-r--r--src/nvim/ui_compositor.c113
-rw-r--r--src/nvim/window.c652
-rw-r--r--test/functional/ui/float_spec.lua3030
-rw-r--r--test/functional/ui/screen.lua70
-rw-r--r--test/helpers.lua4
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)