aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2024-05-02 07:49:07 -0700
committerGitHub <noreply@github.com>2024-05-02 07:49:07 -0700
commite5c69df679159cd56fe34d6fc66a898bed9a87d0 (patch)
treec2b20f0b3581f8e861134c177489ff294be911ea
parent350d81856473b45100d6b0e5920b757df1b4ad27 (diff)
parent037ea6e786b5d05f4a8965e4c2ba6aa60ec7c01a (diff)
downloadrneovim-e5c69df679159cd56fe34d6fc66a898bed9a87d0.tar.gz
rneovim-e5c69df679159cd56fe34d6fc66a898bed9a87d0.tar.bz2
rneovim-e5c69df679159cd56fe34d6fc66a898bed9a87d0.zip
Merge #28101 nvim__redraw
-rw-r--r--runtime/doc/api.txt29
-rw-r--r--runtime/doc/various.txt6
-rw-r--r--runtime/lua/vim/_meta/api.lua32
-rw-r--r--runtime/lua/vim/_meta/api_keysets.lua12
-rw-r--r--runtime/lua/vim/lsp/inlay_hint.lua6
-rw-r--r--runtime/lua/vim/lsp/semantic_tokens.lua2
-rw-r--r--runtime/lua/vim/treesitter/highlighter.lua4
-rw-r--r--src/nvim/api/buffer.c14
-rw-r--r--src/nvim/api/keysets_defs.h14
-rw-r--r--src/nvim/api/vim.c156
-rw-r--r--src/nvim/drawscreen.c22
-rw-r--r--src/nvim/ex_docmd.c2
-rw-r--r--src/nvim/lua/executor.h2
-rw-r--r--src/nvim/math.c17
-rw-r--r--src/nvim/popupmenu.c2
-rw-r--r--test/functional/api/vim_spec.lua212
-rw-r--r--test/functional/ui/cmdline_spec.lua4
-rw-r--r--test/functional/ui/inccommand_spec.lua6
-rw-r--r--test/functional/ui/statuscolumn_spec.lua53
19 files changed, 552 insertions, 43 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index d15230ba2d..86f4c3875c 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -1566,6 +1566,35 @@ nvim__invalidate_glyph_cache() *nvim__invalidate_glyph_cache()*
For testing. The condition in schar_cache_clear_if_full is hard to reach,
so this function can be used to force a cache clear in a test.
+nvim__redraw({opts}) *nvim__redraw()*
+ EXPERIMENTAL: this API may change in the future.
+
+ Instruct Nvim to redraw various components.
+
+ Parameters: ~
+ • {opts} Optional parameters.
+ • win: Target a specific |window-ID| as described below.
+ • buf: Target a specific buffer number as described below.
+ • flush: Update the screen with pending updates.
+ • valid: When present mark `win`, `buf`, or all windows for
+ redraw. When `true`, only redraw changed lines (useful for
+ decoration providers). When `false`, forcefully redraw.
+ • range: Redraw a range in `buf`, the buffer in `win` or the
+ current buffer (useful for decoration providers). Expects a
+ tuple `[first, last]` with the first and last line number of
+ the range, 0-based end-exclusive |api-indexing|.
+ • cursor: Immediately update cursor position on the screen in
+ `win` or the current window.
+ • statuscolumn: Redraw the 'statuscolumn' in `buf`, `win` or
+ all windows.
+ • statusline: Redraw the 'statusline' in `buf`, `win` or all
+ windows.
+ • winbar: Redraw the 'winbar' in `buf`, `win` or all windows.
+ • tabline: Redraw the 'tabline'.
+
+ See also: ~
+ • |:redraw|
+
nvim__stats() *nvim__stats()*
Gets internal stats.
diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt
index aa05d2bbb2..d41bdb5b00 100644
--- a/runtime/doc/various.txt
+++ b/runtime/doc/various.txt
@@ -14,6 +14,7 @@ Various commands *various*
*CTRL-L*
CTRL-L Clears and redraws the screen. The redraw may happen
later, after processing typeahead.
+ See also |nvim__redraw()|.
*CTRL-L-default*
By default, also clears search highlighting
|:nohlsearch| and updates diffs |:diffupdate|.
@@ -21,6 +22,7 @@ CTRL-L Clears and redraws the screen. The redraw may happen
*:mod* *:mode*
:mod[e] Clears and redraws the screen.
+ See also |nvim__redraw()|.
*:redr* *:redraw*
:redr[aw][!] Redraws pending screen updates now, or the entire
@@ -28,6 +30,7 @@ CTRL-L Clears and redraws the screen. The redraw may happen
|:mode| or |CTRL-L|.
Useful to update the screen during a script or
function (or a mapping if 'lazyredraw' set).
+ See also |nvim__redraw()|.
*:redraws* *:redrawstatus*
:redraws[tatus][!] Redraws the status line and window bar of the current
@@ -35,11 +38,12 @@ CTRL-L Clears and redraws the screen. The redraw may happen
included. Redraws the commandline instead if it contains
the 'ruler'. Useful if 'statusline' or 'winbar' includes
an item that doesn't cause automatic updating.
+ See also |nvim__redraw()|.
*:redrawt* *:redrawtabline*
:redrawt[abline] Redraw the tabline. Useful to update the tabline when
'tabline' includes an item that doesn't trigger
- automatic updating.
+ automatic updating. See also |nvim__redraw()|.
*N<Del>*
<Del> When entering a number: Remove the last digit.
diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index d799446647..64c67be076 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -14,12 +14,6 @@ function vim.api.nvim__buf_debug_extmarks(buffer, keys, dot) end
--- @private
--- @param buffer integer
---- @param first integer
---- @param last integer
-function vim.api.nvim__buf_redraw_range(buffer, first, last) end
-
---- @private
---- @param buffer integer
--- @return table<string,any>
function vim.api.nvim__buf_stats(buffer) end
@@ -106,6 +100,32 @@ function vim.api.nvim__inspect_cell(grid, row, col) end
function vim.api.nvim__invalidate_glyph_cache() end
--- @private
+--- EXPERIMENTAL: this API may change in the future.
+---
+--- Instruct Nvim to redraw various components.
+---
+--- @param opts vim.api.keyset.redraw Optional parameters.
+--- • win: Target a specific `window-ID` as described below.
+--- • buf: Target a specific buffer number as described below.
+--- • flush: Update the screen with pending updates.
+--- • valid: When present mark `win`, `buf`, or all windows for
+--- redraw. When `true`, only redraw changed lines (useful for
+--- decoration providers). When `false`, forcefully redraw.
+--- • range: Redraw a range in `buf`, the buffer in `win` or the
+--- current buffer (useful for decoration providers). Expects a
+--- tuple `[first, last]` with the first and last line number of
+--- the range, 0-based end-exclusive `api-indexing`.
+--- • cursor: Immediately update cursor position on the screen in
+--- `win` or the current window.
+--- • statuscolumn: Redraw the 'statuscolumn' in `buf`, `win` or
+--- all windows.
+--- • statusline: Redraw the 'statusline' in `buf`, `win` or all
+--- windows.
+--- • winbar: Redraw the 'winbar' in `buf`, `win` or all windows.
+--- • tabline: Redraw the 'tabline'.
+function vim.api.nvim__redraw(opts) end
+
+--- @private
--- @return any[]
function vim.api.nvim__runtime_inspect() end
diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua
index 9f9ade3e76..f7cd92a3b2 100644
--- a/runtime/lua/vim/_meta/api_keysets.lua
+++ b/runtime/lua/vim/_meta/api_keysets.lua
@@ -207,6 +207,18 @@ error('Cannot require a meta file')
--- @field buf? integer
--- @field filetype? string
+--- @class vim.api.keyset.redraw
+--- @field flush? boolean
+--- @field cursor? boolean
+--- @field valid? boolean
+--- @field statuscolumn? boolean
+--- @field statusline? boolean
+--- @field tabline? boolean
+--- @field winbar? boolean
+--- @field range? any[]
+--- @field win? integer
+--- @field buf? integer
+
--- @class vim.api.keyset.runtime
--- @field is_lua? boolean
--- @field do_source? boolean
diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua
index dc0b6d7037..2c81a3f465 100644
--- a/runtime/lua/vim/lsp/inlay_hint.lua
+++ b/runtime/lua/vim/lsp/inlay_hint.lua
@@ -62,7 +62,7 @@ function M.on_inlayhint(err, result, ctx, _)
if num_unprocessed == 0 then
client_hints[client_id] = {}
bufstate.version = ctx.version
- api.nvim__buf_redraw_range(bufnr, 0, -1)
+ api.nvim__redraw({ buf = bufnr, valid = true })
return
end
@@ -91,7 +91,7 @@ function M.on_inlayhint(err, result, ctx, _)
client_hints[client_id] = new_lnum_hints
bufstate.version = ctx.version
- api.nvim__buf_redraw_range(bufnr, 0, -1)
+ api.nvim__redraw({ buf = bufnr, valid = true })
end
--- |lsp-handler| for the method `textDocument/inlayHint/refresh`
@@ -224,7 +224,7 @@ local function clear(bufnr)
end
end
api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1)
- api.nvim__buf_redraw_range(bufnr, 0, -1)
+ api.nvim__redraw({ buf = bufnr, valid = true })
end
--- Disable inlay hints for a buffer
diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua
index 20ac0a125f..be2d6ee0ae 100644
--- a/runtime/lua/vim/lsp/semantic_tokens.lua
+++ b/runtime/lua/vim/lsp/semantic_tokens.lua
@@ -394,7 +394,7 @@ function STHighlighter:process_response(response, client, version)
current_result.namespace_cleared = false
-- redraw all windows displaying buffer
- api.nvim__buf_redraw_range(self.bufnr, 0, -1)
+ api.nvim__redraw({ buf = self.bufnr, valid = true })
end
--- on_win handler for the decoration provider (see |nvim_set_decoration_provider|)
diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua
index 3f7e31212c..d2f986b874 100644
--- a/runtime/lua/vim/treesitter/highlighter.lua
+++ b/runtime/lua/vim/treesitter/highlighter.lua
@@ -215,7 +215,7 @@ end
---@param start_row integer
---@param new_end integer
function TSHighlighter:on_bytes(_, _, start_row, _, _, _, _, _, new_end)
- api.nvim__buf_redraw_range(self.bufnr, start_row, start_row + new_end + 1)
+ api.nvim__redraw({ buf = self.bufnr, range = { start_row, start_row + new_end + 1 } })
end
---@package
@@ -227,7 +227,7 @@ end
---@param changes Range6[]
function TSHighlighter:on_changedtree(changes)
for _, ch in ipairs(changes) do
- api.nvim__buf_redraw_range(self.bufnr, ch[1], ch[4] + 1)
+ api.nvim__redraw({ buf = self.bufnr, range = { ch[1], ch[4] + 1 } })
end
end
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 452ba49e04..7e64808709 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -230,20 +230,6 @@ Boolean nvim_buf_detach(uint64_t channel_id, Buffer buffer, Error *err)
return true;
}
-/// @nodoc
-void nvim__buf_redraw_range(Buffer buffer, Integer first, Integer last, Error *err)
-{
- buf_T *buf = find_buffer_by_handle(buffer, err);
- if (!buf) {
- return;
- }
- if (last < 0) {
- last = buf->b_ml.ml_line_count;
- }
-
- redraw_buf_range_later(buf, (linenr_T)first + 1, (linenr_T)last);
-}
-
/// Gets a line-range from the buffer.
///
/// Indexing is zero-based, end-exclusive. Negative indices are interpreted
diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h
index 7c5fddff55..00d8aa8428 100644
--- a/src/nvim/api/keysets_defs.h
+++ b/src/nvim/api/keysets_defs.h
@@ -373,3 +373,17 @@ typedef struct {
Boolean ignore_blank_lines;
Boolean indent_heuristic;
} Dict(xdl_diff);
+
+typedef struct {
+ OptionalKeys is_set__redraw_;
+ Boolean flush;
+ Boolean cursor;
+ Boolean valid;
+ Boolean statuscolumn;
+ Boolean statusline;
+ Boolean tabline;
+ Boolean winbar;
+ Array range;
+ Window win;
+ Buffer buf;
+} Dict(redraw);
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index e75f4e629b..d8ebc4b94f 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -50,6 +50,7 @@
#include "nvim/mapping.h"
#include "nvim/mark.h"
#include "nvim/mark_defs.h"
+#include "nvim/math.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -2305,3 +2306,158 @@ Dictionary nvim__complete_set(Integer index, Dict(complete_set) *opts, Arena *ar
}
return rv;
}
+
+static void redraw_status(win_T *wp, Dict(redraw) *opts, bool *flush)
+{
+ if (opts->statuscolumn && *wp->w_p_stc != NUL) {
+ wp->w_nrwidth_line_count = 0;
+ changed_window_setting(wp);
+ }
+ win_grid_alloc(wp);
+
+ // Flush later in case winbar was just hidden or shown for the first time, or
+ // statuscolumn is being drawn.
+ if (wp->w_lines_valid == 0) {
+ *flush = true;
+ }
+
+ // Mark for redraw in case flush will happen, otherwise redraw now.
+ if (*flush && (opts->statusline || opts->winbar)) {
+ wp->w_redr_status = true;
+ } else if (opts->statusline || opts->winbar) {
+ win_check_ns_hl(wp);
+ if (opts->winbar) {
+ win_redr_winbar(wp);
+ }
+ if (opts->statusline) {
+ win_redr_status(wp);
+ }
+ win_check_ns_hl(NULL);
+ }
+}
+
+/// EXPERIMENTAL: this API may change in the future.
+///
+/// Instruct Nvim to redraw various components.
+///
+/// @see |:redraw|
+///
+/// @param opts Optional parameters.
+/// - win: Target a specific |window-ID| as described below.
+/// - buf: Target a specific buffer number as described below.
+/// - flush: Update the screen with pending updates.
+/// - valid: When present mark `win`, `buf`, or all windows for
+/// redraw. When `true`, only redraw changed lines (useful for
+/// decoration providers). When `false`, forcefully redraw.
+/// - range: Redraw a range in `buf`, the buffer in `win` or the
+/// current buffer (useful for decoration providers). Expects a
+/// tuple `[first, last]` with the first and last line number
+/// of the range, 0-based end-exclusive |api-indexing|.
+/// - cursor: Immediately update cursor position on the screen in
+/// `win` or the current window.
+/// - statuscolumn: Redraw the 'statuscolumn' in `buf`, `win` or
+/// all windows.
+/// - statusline: Redraw the 'statusline' in `buf`, `win` or all
+/// windows.
+/// - winbar: Redraw the 'winbar' in `buf`, `win` or all windows.
+/// - tabline: Redraw the 'tabline'.
+void nvim__redraw(Dict(redraw) *opts, Error *err)
+ FUNC_API_SINCE(12)
+{
+ win_T *win = NULL;
+ buf_T *buf = NULL;
+
+ if (HAS_KEY(opts, redraw, win)) {
+ win = find_window_by_handle(opts->win, err);
+ if (ERROR_SET(err)) {
+ return;
+ }
+ }
+
+ if (HAS_KEY(opts, redraw, buf)) {
+ VALIDATE(win == NULL, "%s", "cannot use both 'buf' and 'win'", {
+ return;
+ });
+ buf = find_buffer_by_handle(opts->buf, err);
+ if (ERROR_SET(err)) {
+ return;
+ }
+ }
+
+ int count = (win != NULL) + (buf != NULL);
+ VALIDATE(popcount(opts->is_set__redraw_) > count, "%s", "at least one action required", {
+ return;
+ });
+
+ if (HAS_KEY(opts, redraw, valid)) {
+ // UPD_VALID redraw type does not actually do anything on it's own. Setting
+ // it here without scrolling or changing buffer text seems pointless but
+ // the expectation is that this may be called by decoration providers whose
+ // "on_win" callback may set "w_redr_top/bot".
+ int type = opts->valid ? UPD_VALID : UPD_NOT_VALID;
+ if (win != NULL) {
+ redraw_later(win, type);
+ } else if (buf != NULL) {
+ redraw_buf_later(buf, type);
+ } else {
+ redraw_all_later(type);
+ }
+ }
+
+ if (HAS_KEY(opts, redraw, range)) {
+ VALIDATE(kv_size(opts->range) == 2
+ && kv_A(opts->range, 0).type == kObjectTypeInteger
+ && kv_A(opts->range, 1).type == kObjectTypeInteger
+ && kv_A(opts->range, 0).data.integer >= 0
+ && kv_A(opts->range, 1).data.integer >= -1,
+ "%s", "Invalid 'range': Expected 2-tuple of Integers", {
+ return;
+ });
+ linenr_T first = (linenr_T)kv_A(opts->range, 0).data.integer + 1;
+ linenr_T last = (linenr_T)kv_A(opts->range, 1).data.integer;
+ if (last < 0) {
+ last = buf->b_ml.ml_line_count;
+ }
+ redraw_buf_range_later(win ? win->w_buffer : (buf ? buf : curbuf), first, last);
+ }
+
+ if (opts->cursor) {
+ setcursor_mayforce(win ? win : curwin, true);
+ }
+
+ bool flush = opts->flush;
+ if (opts->tabline) {
+ // Flush later in case tabline was just hidden or shown for the first time.
+ if (redraw_tabline && firstwin->w_lines_valid == 0) {
+ flush = true;
+ } else {
+ draw_tabline();
+ }
+ }
+
+ bool save_lz = p_lz;
+ int save_rd = RedrawingDisabled;
+ RedrawingDisabled = 0;
+ p_lz = false;
+ if (opts->statuscolumn || opts->statusline || opts->winbar) {
+ if (win == NULL) {
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (buf == NULL || wp->w_buffer == buf) {
+ redraw_status(wp, opts, &flush);
+ }
+ }
+ } else {
+ redraw_status(win, opts, &flush);
+ }
+ }
+
+ // Flush pending screen updates if "flush" or "clear" is true, or when
+ // redrawing a status component may have changed the grid dimensions.
+ if (flush && !cmdpreview) {
+ update_screen();
+ }
+ ui_flush();
+
+ RedrawingDisabled = save_rd;
+ p_lz = save_lz;
+}
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index 5e834e4b79..bda0ccc870 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -821,25 +821,25 @@ static void win_redr_border(win_T *wp)
/// Set cursor to its position in the current window.
void setcursor(void)
{
- setcursor_mayforce(false);
+ setcursor_mayforce(curwin, false);
}
/// Set cursor to its position in the current window.
/// @param force when true, also when not redrawing.
-void setcursor_mayforce(bool force)
+void setcursor_mayforce(win_T *wp, bool force)
{
if (force || redrawing()) {
- validate_cursor(curwin);
+ validate_cursor(wp);
- ScreenGrid *grid = &curwin->w_grid;
- int row = curwin->w_wrow;
- int col = curwin->w_wcol;
- if (curwin->w_p_rl) {
+ ScreenGrid *grid = &wp->w_grid;
+ int row = wp->w_wrow;
+ int col = wp->w_wcol;
+ if (wp->w_p_rl) {
// With 'rightleft' set and the cursor on a double-wide character,
// position it on the leftmost column.
- col = curwin->w_width_inner - curwin->w_wcol
- - ((utf_ptr2cells(get_cursor_pos_ptr()) == 2
- && vim_isprintc(gchar_cursor())) ? 2 : 1);
+ char *cursor = ml_get_buf(wp->w_buffer, wp->w_cursor.lnum) + wp->w_cursor.col;
+ col = wp->w_width_inner - wp->w_wcol - ((utf_ptr2cells(cursor) == 2
+ && vim_isprintc(utf_ptr2char(cursor))) ? 2 : 1);
}
grid_adjust(&grid, &row, &col);
@@ -2713,7 +2713,7 @@ void redraw_buf_line_later(buf_T *buf, linenr_T line, bool force)
}
}
-void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline)
+void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_buffer == buf
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index e0a5e3e2bb..47b4c1e47d 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -5886,7 +5886,7 @@ static void ex_equal(exarg_T *eap)
static void ex_sleep(exarg_T *eap)
{
if (cursor_valid(curwin)) {
- setcursor_mayforce(true);
+ setcursor_mayforce(curwin, true);
}
int64_t len = eap->line2;
diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h
index 299df2fdde..32fde3853b 100644
--- a/src/nvim/lua/executor.h
+++ b/src/nvim/lua/executor.h
@@ -43,7 +43,7 @@ typedef enum {
kRetLuaref, ///< return value becomes a single Luaref, regardless of type (except NIL)
} LuaRetMode;
-/// To use with kRetNilBool for quick thuthyness check
+/// To use with kRetNilBool for quick truthiness check
#define LUARET_TRUTHY(res) ((res).type == kObjectTypeBoolean && (res).data.boolean == true)
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/math.c b/src/nvim/math.c
index 47a667416c..1ccf4d7806 100644
--- a/src/nvim/math.c
+++ b/src/nvim/math.c
@@ -77,6 +77,23 @@ int xctz(uint64_t x)
#endif
}
+/// Count number of set bits in bit field.
+int popcount(uint64_t x)
+{
+ // Use compiler builtin if possible.
+#if defined(__clang__) || defined(__GNUC__)
+ return __builtin_popcountll(x);
+#else
+ int count = 0;
+ for (; x != 0; x >>= 1) {
+ if (x & 1) {
+ count++;
+ }
+ }
+ return count;
+#endif
+}
+
/// For overflow detection, add a digit safely to an int value.
int vim_append_digit_int(int *value, int digit)
{
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c
index 0a8842a136..86f3611ec5 100644
--- a/src/nvim/popupmenu.c
+++ b/src/nvim/popupmenu.c
@@ -1281,7 +1281,7 @@ void pum_show_popupmenu(vimmenu_T *menu)
pum_is_drawn = true;
pum_grid.zindex = kZIndexCmdlinePopupMenu; // show above cmdline area #23275
pum_redraw();
- setcursor_mayforce(true);
+ setcursor_mayforce(curwin, true);
int c = vgetc();
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 2d3871a37d..fbb6d42633 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -5016,4 +5016,216 @@ describe('API', function()
eq(false, exec_lua('return _G.success'))
end)
end)
+
+ it('nvim__redraw', function()
+ local screen = Screen.new(60, 5)
+ screen:attach()
+ local win = api.nvim_get_current_win()
+ eq('at least one action required', pcall_err(api.nvim__redraw, {}))
+ eq('at least one action required', pcall_err(api.nvim__redraw, { buf = 0 }))
+ eq('at least one action required', pcall_err(api.nvim__redraw, { win = 0 }))
+ eq("cannot use both 'buf' and 'win'", pcall_err(api.nvim__redraw, { buf = 0, win = 0 }))
+ feed(':echo getchar()<CR>')
+ fn.setline(1, 'foobar')
+ command('vnew')
+ fn.setline(1, 'foobaz')
+ -- Can flush pending screen updates
+ api.nvim__redraw({ flush = true })
+ screen:expect({
+ grid = [[
+ foobaz │foobar |
+ {1:~ }│{1:~ }|*2
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ ^:echo getchar() |
+ ]],
+ })
+ -- Can update the grid cursor position #20793
+ api.nvim__redraw({ cursor = true })
+ screen:expect({
+ grid = [[
+ ^foobaz │foobar |
+ {1:~ }│{1:~ }|*2
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ :echo getchar() |
+ ]],
+ })
+ -- Also in non-current window
+ api.nvim__redraw({ cursor = true, win = win })
+ screen:expect({
+ grid = [[
+ foobaz │^foobar |
+ {1:~ }│{1:~ }|*2
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ :echo getchar() |
+ ]],
+ })
+ -- Can update the 'statusline' in a single window
+ api.nvim_set_option_value('statusline', 'statusline1', { win = 0 })
+ api.nvim_set_option_value('statusline', 'statusline2', { win = win })
+ api.nvim__redraw({ cursor = true, win = 0, statusline = true })
+ screen:expect({
+ grid = [[
+ ^foobaz │foobar |
+ {1:~ }│{1:~ }|*2
+ {3:statusline1 }{2:[No Name] [+] }|
+ :echo getchar() |
+ ]],
+ })
+ api.nvim__redraw({ win = win, statusline = true })
+ screen:expect({
+ grid = [[
+ ^foobaz │foobar |
+ {1:~ }│{1:~ }|*2
+ {3:statusline1 }{2:statusline2 }|
+ :echo getchar() |
+ ]],
+ })
+ -- Can update the 'statusline' in all windows
+ api.nvim_set_option_value('statusline', '', { win = win })
+ api.nvim_set_option_value('statusline', 'statusline3', {})
+ api.nvim__redraw({ statusline = true })
+ screen:expect({
+ grid = [[
+ ^foobaz │foobar |
+ {1:~ }│{1:~ }|*2
+ {3:statusline3 }{2:statusline3 }|
+ :echo getchar() |
+ ]],
+ })
+ -- Can update the 'statuscolumn'
+ api.nvim_set_option_value('statuscolumn', 'statuscolumn', { win = win })
+ api.nvim__redraw({ statuscolumn = true })
+ screen:expect({
+ grid = [[
+ ^foobaz │{8:statuscolumn}foobar |
+ {1:~ }│{1:~ }|*2
+ {3:statusline3 }{2:statusline3 }|
+ :echo getchar() |
+ ]],
+ })
+ -- Can update the 'winbar'
+ api.nvim_set_option_value('winbar', 'winbar', { win = 0 })
+ api.nvim__redraw({ win = 0, winbar = true })
+ screen:expect({
+ grid = [[
+ {5:^winbar }│{8:statuscolumn}foobar |
+ foobaz │{1:~ }|
+ {1:~ }│{1:~ }|
+ {3:statusline3 }{2:statusline3 }|
+ :echo getchar() |
+ ]],
+ })
+ -- Can update the 'tabline'
+ api.nvim_set_option_value('showtabline', 2, {})
+ api.nvim_set_option_value('tabline', 'tabline', {})
+ api.nvim__redraw({ tabline = true })
+ screen:expect({
+ grid = [[
+ {2:^tabline }|
+ {5:winbar }│{8:statuscolumn}foobar |
+ foobaz │{1:~ }|
+ {3:statusline3 }{2:statusline3 }|
+ :echo getchar() |
+ ]],
+ })
+ -- Can update multiple status widgets
+ api.nvim_set_option_value('tabline', 'tabline2', {})
+ api.nvim_set_option_value('statusline', 'statusline4', {})
+ api.nvim__redraw({ statusline = true, tabline = true })
+ screen:expect({
+ grid = [[
+ {2:^tabline2 }|
+ {5:winbar }│{8:statuscolumn}foobar |
+ foobaz │{1:~ }|
+ {3:statusline4 }{2:statusline4 }|
+ :echo getchar() |
+ ]],
+ })
+ -- Can update all status widgets
+ api.nvim_set_option_value('tabline', 'tabline3', {})
+ api.nvim_set_option_value('statusline', 'statusline5', {})
+ api.nvim_set_option_value('statuscolumn', 'statuscolumn2', {})
+ api.nvim_set_option_value('winbar', 'winbar2', {})
+ api.nvim__redraw({ statuscolumn = true, statusline = true, tabline = true, winbar = true })
+ screen:expect({
+ grid = [[
+ {2:^tabline3 }|
+ {5:winbar2 }│{5:winbar2 }|
+ {8:statuscolumn2}foobaz │{8:statuscolumn}foobar |
+ {3:statusline5 }{2:statusline5 }|
+ :echo getchar() |
+ ]],
+ })
+ -- Can update status widget for a specific window
+ feed('<CR><CR>')
+ command('let g:status=0')
+ api.nvim_set_option_value('statusline', '%{%g:status%}', { win = 0 })
+ command('vsplit')
+ screen:expect({
+ grid = [[
+ {2:tabline3 }|
+ {5:winbar2 }│{5:winbar2 }│{5:winbar2 }|
+ {8:statuscolumn2}^foobaz │{8:statuscolumn2}foobaz│{8:statuscolumn}foobar |
+ {3:0 }{2:0 statusline5 }|
+ 13 |
+ ]],
+ })
+ command('let g:status=1')
+ api.nvim__redraw({ win = 0, statusline = true })
+ screen:expect({
+ grid = [[
+ {2:tabline3 }|
+ {5:winbar2 }│{5:winbar2 }│{5:winbar2 }|
+ {8:statuscolumn2}^foobaz │{8:statuscolumn2}foobaz│{8:statuscolumn}foobar |
+ {3:1 }{2:0 statusline5 }|
+ 13 |
+ ]],
+ })
+ -- Can update status widget for a specific buffer
+ command('let g:status=2')
+ api.nvim__redraw({ buf = 0, statusline = true })
+ screen:expect({
+ grid = [[
+ {2:tabline3 }|
+ {5:winbar2 }│{5:winbar2 }│{5:winbar2 }|
+ {8:statuscolumn2}^foobaz │{8:statuscolumn2}foobaz│{8:statuscolumn}foobar |
+ {3:2 }{2:2 statusline5 }|
+ 13 |
+ ]],
+ })
+ -- valid = true does not draw any lines on its own
+ exec_lua([[
+ lines = 0
+ ns = vim.api.nvim_create_namespace('')
+ on_win = function()
+ if do_win then
+ vim.api.nvim_buf_set_extmark(0, ns, 0, 0, { hl_group = 'IncSearch', end_col = 6 })
+ end
+ end
+ vim.api.nvim_set_decoration_provider(ns, {
+ on_win = on_win,
+ on_line = function()
+ lines = lines + 1
+ end,
+ })
+ ]])
+ local lines = exec_lua('return lines')
+ api.nvim__redraw({ buf = 0, valid = true, flush = true })
+ eq(lines, exec_lua('return lines'))
+ -- valid = false does
+ api.nvim__redraw({ buf = 0, valid = false, flush = true })
+ neq(lines, exec_lua('return lines'))
+ -- valid = true does redraw lines if affected by on_win callback
+ exec_lua('do_win = true')
+ api.nvim__redraw({ buf = 0, valid = true, flush = true })
+ screen:expect({
+ grid = [[
+ {2:tabline3 }|
+ {5:winbar2 }│{5:winbar2 }│{5:winbar2 }|
+ {8:statuscolumn2}{2:^foobaz} │{8:statuscolumn2}{2:foobaz}│{8:statuscolumn}foobar |
+ {3:2 }{2:2 statusline5 }|
+ 13 |
+ ]],
+ })
+ end)
end)
diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua
index b3ec5f7535..6edfb4a49c 100644
--- a/test/functional/ui/cmdline_spec.lua
+++ b/test/functional/ui/cmdline_spec.lua
@@ -827,14 +827,14 @@ local function test_cmdline(linegrid)
]])
end)
- -- Needs new API
- pending('does not move cursor to curwin #20309', function()
+ it('does not move cursor to curwin #20309', function()
local win = api.nvim_get_current_win()
command('norm icmdlinewin')
command('new')
command('norm icurwin')
feed(':')
api.nvim_win_set_cursor(win, { 1, 7 })
+ api.nvim__redraw({ win = win, cursor = true })
screen:expect {
grid = [[
curwin |
diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua
index 6bcfae2eee..c11e009fef 100644
--- a/test/functional/ui/inccommand_spec.lua
+++ b/test/functional/ui/inccommand_spec.lua
@@ -2594,6 +2594,12 @@ it(':substitute with inccommand, timer-induced :redraw #9777', function()
{2:[Preview] }|
:%s/foo/ZZZ^ |
]])
+
+ -- Also with nvim__redraw()
+ command('call timer_start(10, {-> nvim__redraw(#{flush:1})}, {"repeat":-1})')
+ command('call timer_start(10, {-> nvim__redraw(#{statusline:1})}, {"repeat":-1})')
+ sleep(20) -- Allow some timer activity.
+ screen:expect_unchanged()
end)
it(':substitute with inccommand, allows :redraw before first separator is typed #18857', function()
diff --git a/test/functional/ui/statuscolumn_spec.lua b/test/functional/ui/statuscolumn_spec.lua
index cce9cca2ca..9a8629018f 100644
--- a/test/functional/ui/statuscolumn_spec.lua
+++ b/test/functional/ui/statuscolumn_spec.lua
@@ -918,4 +918,57 @@ describe('statuscolumn', function()
|
]])
end)
+
+ it('forces a rebuild with nvim__redraw', function()
+ screen:try_resize(40, 4)
+ -- Current window
+ command([[
+ let g:insert = v:false
+ set nonu stc=%{g:insert?'insert':''}
+ vsplit
+ au InsertEnter * let g:insert = v:true | call nvim__redraw(#{statuscolumn:1, win:0})
+ au InsertLeave * let g:insert = v:false | call nvim__redraw(#{statuscolumn:1, win:0})
+ ]])
+ feed('i')
+ screen:expect({
+ grid = [[
+ {8:insert}^aaaaa │aaaaa |
+ {8:insert}aaaaa │aaaaa |
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ {5:-- INSERT --} |
+ ]],
+ })
+ feed('<esc>')
+ screen:expect({
+ grid = [[
+ ^aaaaa │aaaaa |
+ aaaaa │aaaaa |
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ |
+ ]],
+ })
+ -- All windows
+ command([[
+ au! InsertEnter * let g:insert = v:true | call nvim__redraw(#{statuscolumn:1})
+ au! InsertLeave * let g:insert = v:false | call nvim__redraw(#{statuscolumn:1})
+ ]])
+ feed('i')
+ screen:expect({
+ grid = [[
+ {8:insert}^aaaaa │{8:insert}aaaaa |
+ {8:insert}aaaaa │{8:insert}aaaaa |
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ {5:-- INSERT --} |
+ ]],
+ })
+ feed('<esc>')
+ screen:expect({
+ grid = [[
+ ^aaaaa │aaaaa |
+ aaaaa │aaaaa |
+ {3:[No Name] [+] }{2:[No Name] [+] }|
+ |
+ ]],
+ })
+ end)
end)