mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +00:00 
			
		
		
		
	Merge pull request #6619 from bfredl/floating
Floating windows in TUI and Remote UI
This commit is contained in:
		@@ -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.
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1004,8 +1004,10 @@ 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++) {
 | 
			
		||||
    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));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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"));
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
  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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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,8 +432,10 @@ 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++) {
 | 
			
		||||
      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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -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,8 +136,8 @@ 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) {
 | 
			
		||||
@@ -143,12 +145,32 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    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 = kv_size(layers)-1;
 | 
			
		||||
  return true;
 | 
			
		||||
    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)
 | 
			
		||||
void ui_comp_set_screen_valid(bool valid)
 | 
			
		||||
{
 | 
			
		||||
  valid_screen = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ui_comp_grid_clear(UI *ui, Integer grid)
 | 
			
		||||
{
 | 
			
		||||
  // 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
 | 
			
		||||
 
 | 
			
		||||
@@ -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. */
 | 
			
		||||
  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).
 | 
			
		||||
     */
 | 
			
		||||
    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,6 +2446,8 @@ int win_close(win_T *win, bool free_buf)
 | 
			
		||||
    // using the window.
 | 
			
		||||
    check_cursor();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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.
 | 
			
		||||
@@ -2048,6 +2455,7 @@ int win_close(win_T *win, bool free_buf)
 | 
			
		||||
    } else {
 | 
			
		||||
      win_comp_pos();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (close_curwin) {
 | 
			
		||||
    win_enter_ext(wp, false, true, false, true, true);
 | 
			
		||||
@@ -2167,10 +2575,18 @@ win_free_mem (
 | 
			
		||||
  frame_T     *frp;
 | 
			
		||||
  win_T       *wp;
 | 
			
		||||
 | 
			
		||||
  /* Remove the window and its frame from the tree of frames. */
 | 
			
		||||
  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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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 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;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  redraw_all_later(NOT_VALID);
 | 
			
		||||
}
 | 
			
		||||
@@ -4358,15 +4828,17 @@ 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) {
 | 
			
		||||
        break;
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
  /* recompute the window positions */
 | 
			
		||||
    // 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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3030
									
								
								test/functional/ui/float_spec.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3030
									
								
								test/functional/ui/float_spec.lua
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user