aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/api
diff options
context:
space:
mode:
authorLuuk van Baal <luukvbaal@gmail.com>2024-04-10 11:42:46 +0200
committerLuuk van Baal <luukvbaal@gmail.com>2024-05-02 15:57:06 +0200
commit037ea6e786b5d05f4a8965e4c2ba6aa60ec7c01a (patch)
treed59f33ff739409f1a280d2c3f9b8140a814c9e38 /src/nvim/api
parent7b14eb543d43344e2498335dc93a68d200469516 (diff)
downloadrneovim-037ea6e786b5d05f4a8965e4c2ba6aa60ec7c01a.tar.gz
rneovim-037ea6e786b5d05f4a8965e4c2ba6aa60ec7c01a.tar.bz2
rneovim-037ea6e786b5d05f4a8965e4c2ba6aa60ec7c01a.zip
feat(api): add nvim__redraw for more granular redrawing
Experimental and subject to future changes. Add a way to redraw certain elements that are not redrawn while Nvim is waiting for input, or currently have no API to do so. This API covers all that can be done with the :redraw* commands, in addition to the following new features: - Immediately move the cursor to a (non-current) window. - Target a specific window or buffer to mark for redraw. - Mark a buffer range for redraw (replaces nvim__buf_redraw_range()). - Redraw the 'statuscolumn'.
Diffstat (limited to 'src/nvim/api')
-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
3 files changed, 170 insertions, 14 deletions
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;
+}