aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuuk van Baal <luukvbaal@gmail.com>2024-11-23 23:03:46 +0100
committerluukvbaal <luukvbaal@gmail.com>2025-02-25 13:09:01 +0100
commitf58e7d5fac1c4f63f0ba3e59134591239182910e (patch)
tree1b180a27cb3c4f4f521188b8e6416d3adfc79206
parenta31ccc3b1f65fd86780c03fec9c6e1bf56e30e35 (diff)
downloadrneovim-f58e7d5fac1c4f63f0ba3e59134591239182910e.tar.gz
rneovim-f58e7d5fac1c4f63f0ba3e59134591239182910e.tar.bz2
rneovim-f58e7d5fac1c4f63f0ba3e59134591239182910e.zip
feat(marks): add conceal_lines to nvim_buf_set_extmark()
Implement an extmark property that conceals lines vertically.
-rw-r--r--runtime/doc/api.txt4
-rw-r--r--runtime/doc/news.txt13
-rw-r--r--runtime/lua/vim/_meta/api.lua4
-rw-r--r--runtime/lua/vim/_meta/api_keysets.lua1
-rw-r--r--src/nvim/api/extmark.c28
-rw-r--r--src/nvim/api/keysets_defs.h1
-rw-r--r--src/nvim/buffer_defs.h1
-rw-r--r--src/nvim/change.c3
-rw-r--r--src/nvim/decoration.c77
-rw-r--r--src/nvim/decoration_defs.h1
-rw-r--r--src/nvim/diff.c3
-rw-r--r--src/nvim/drawline.c34
-rw-r--r--src/nvim/drawscreen.c70
-rw-r--r--src/nvim/edit.c23
-rw-r--r--src/nvim/fold.c4
-rw-r--r--src/nvim/marktree.c36
-rw-r--r--src/nvim/marktree.h10
-rw-r--r--src/nvim/marktree_defs.h3
-rw-r--r--src/nvim/move.c93
-rw-r--r--src/nvim/normal.c30
-rw-r--r--src/nvim/plines.c24
-rw-r--r--src/nvim/window.c10
-rw-r--r--test/functional/api/extmark_spec.lua2
-rw-r--r--test/functional/ui/decorations_spec.lua221
24 files changed, 529 insertions, 167 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index ec3dfebbc0..760d0804a6 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -2698,6 +2698,10 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {opts})
When a character is supplied it is used as |:syn-cchar|.
"hl_group" is used as highlight for the cchar if provided,
otherwise it defaults to |hl-Conceal|.
+ • conceal_lines: string which should be empty. When
+ provided, lines in the range are not drawn at all
+ (according to 'conceallevel'); the next unconcealed line
+ is drawn instead.
• spell: boolean indicating that spell checking should be
performed within this extmark
• ui_watched: boolean that indicates the mark should be
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 05d14600f8..25f6255ed3 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -202,13 +202,14 @@ API
• |nvim_echo()| `err` field to print error messages and `chunks` accepts
highlight group IDs.
• |nvim_open_win()| `relative` field can be set to "laststatus" and "tabline".
-• |nvim_buf_set_extmark()| new field `virt_lines_overflow` accepts value `scroll` to
- enable horizontal scrolling for virtual lines with 'nowrap'.
- right aligned text that truncates before covering up buffer text.
-• |nvim_buf_set_extmark()| `hl_group` field can be an array of layered groups.
+• Additions to |nvim_buf_set_extmark()|:
+ • `conceal_lines` field to conceal an entire line.
+ • `hl_group` field can be an array of layered groups.
+ • `virt_text_pos` field accepts value `eol_right_align` to allow for right
+ aligned text that truncates before covering up buffer text.
+ • `virt_lines_overflow` field accepts value `scroll` to enable horizontal
+ scrolling for virtual lines with 'nowrap'.
• |vim.hl.range()| now has a optional `timeout` field which allows for a timed highlight
-• |nvim_buf_set_extmark()| `virt_text_pos` field accepts value `eol_right_align` to
- allow for right aligned text that truncates before covering up buffer text.
DEFAULTS
diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index a6ffb43146..94469a768c 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -685,6 +685,10 @@ function vim.api.nvim_buf_line_count(buffer) end
--- When a character is supplied it is used as `:syn-cchar`.
--- "hl_group" is used as highlight for the cchar if provided,
--- otherwise it defaults to `hl-Conceal`.
+--- - conceal_lines: string which should be empty. When
+--- provided, lines in the range are not drawn at all
+--- (according to 'conceallevel'); the next unconcealed line
+--- is drawn instead.
--- - spell: boolean indicating that spell checking should be
--- performed within this extmark
--- - ui_watched: boolean that indicates the mark should be drawn
diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua
index a66e373851..e574c4c1b3 100644
--- a/runtime/lua/vim/_meta/api_keysets.lua
+++ b/runtime/lua/vim/_meta/api_keysets.lua
@@ -266,6 +266,7 @@ error('Cannot require a meta file')
--- @field line_hl_group? integer|string
--- @field cursorline_hl_group? integer|string
--- @field conceal? string
+--- @field conceal_lines? string
--- @field spell? boolean
--- @field ui_watched? boolean
--- @field undo_restore? boolean
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index ef09dbb0aa..e4bb9d6953 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -492,6 +492,10 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// When a character is supplied it is used as |:syn-cchar|.
/// "hl_group" is used as highlight for the cchar if provided,
/// otherwise it defaults to |hl-Conceal|.
+/// - conceal_lines: string which should be empty. When
+/// provided, lines in the range are not drawn at all
+/// (according to 'conceallevel'); the next unconcealed line
+/// is drawn instead.
/// - spell: boolean indicating that spell checking should be
/// performed within this extmark
/// - ui_watched: boolean that indicates the mark should be drawn
@@ -607,14 +611,22 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
if (HAS_KEY(opts, set_extmark, conceal)) {
hl.flags |= kSHConceal;
has_hl = true;
- String c = opts->conceal;
- if (c.size > 0) {
+ if (opts->conceal.size > 0) {
int ch;
- hl.conceal_char = utfc_ptr2schar(c.data, &ch);
- if (!hl.conceal_char || !vim_isprintc(ch)) {
- api_set_error(err, kErrorTypeValidation, "conceal char has to be printable");
+ hl.conceal_char = utfc_ptr2schar(opts->conceal.data, &ch);
+ VALIDATE(hl.conceal_char && vim_isprintc(ch), "%s", "conceal char has to be printable", {
goto error;
- }
+ });
+ }
+ }
+
+ if (HAS_KEY(opts, set_extmark, conceal_lines)) {
+ hl.flags |= kSHConcealLines;
+ has_hl = true;
+ if (opts->conceal_lines.size > 0) {
+ VALIDATE(*opts->conceal_lines.data == NUL, "%s", "conceal_lines has to be an empty string", {
+ goto error;
+ });
}
}
@@ -863,6 +875,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
}
}
+ if (hl.flags & kSHConcealLines) {
+ decor_flags |= MT_FLAG_DECOR_CONCEAL_LINES;
+ }
+
DecorInline decor = DECOR_INLINE_INIT;
if (decor_alloc || decor_indexed != DECOR_ID_INVALID || url != NULL
|| schar_high(hl.conceal_char)) {
diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h
index b3015911f9..b9bd8ea286 100644
--- a/src/nvim/api/keysets_defs.h
+++ b/src/nvim/api/keysets_defs.h
@@ -53,6 +53,7 @@ typedef struct {
HLGroupID line_hl_group;
HLGroupID cursorline_hl_group;
String conceal;
+ String conceal_lines;
Boolean spell;
Boolean ui_watched;
Boolean undo_restore;
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 6d42b5d9ad..7f0768ec60 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -813,6 +813,7 @@ typedef struct {
uint16_t wl_size; // height in screen lines
char wl_valid; // true values are valid for text in buffer
char wl_folded; // true when this is a range of folded lines
+ linenr_T wl_foldend; // last buffer line number for folded line
linenr_T wl_lastlnum; // last buffer line number for logical line
} wline_T;
diff --git a/src/nvim/change.c b/src/nvim/change.c
index 31962b52ce..84053619d7 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -205,9 +205,10 @@ static void changed_lines_invalidate_win(win_T *wp, linenr_T lnum, colnr_T col,
} else if (xtra != 0) {
// line below change
wp->w_lines[i].wl_lnum += xtra;
+ wp->w_lines[i].wl_foldend += xtra;
wp->w_lines[i].wl_lastlnum += xtra;
}
- } else if (wp->w_lines[i].wl_lastlnum >= lnum) {
+ } else if (wp->w_lines[i].wl_foldend >= lnum) {
// change somewhere inside this range of folded lines,
// may need to be redrawn
wp->w_lines[i].wl_valid = false;
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index d41e7e32be..f13e91ee0f 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -22,6 +22,7 @@
#include "nvim/marktree.h"
#include "nvim/memory.h"
#include "nvim/memory_defs.h"
+#include "nvim/move.h"
#include "nvim/option_vars.h"
#include "nvim/pos_defs.h"
#include "nvim/sign.h"
@@ -124,6 +125,15 @@ void decor_redraw_sh(buf_T *buf, int row1, int row2, DecorSignHighlight sh)
redraw_buf_range_later(buf, row1 + 1, row2 + 1);
}
}
+ if (sh.flags & kSHConcealLines) {
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ // TODO(luukvbaal): redraw only unconcealed lines, and scroll lines below
+ // it up or down. Also when opening/closing a fold.
+ if (wp->w_buffer == buf) {
+ changed_window_setting(wp);
+ }
+ }
+ }
if (sh.flags & kSHUIWatched) {
redraw_buf_line_later(buf, row1 + 1, false);
}
@@ -830,6 +840,56 @@ next_mark:
return attr;
}
+static const uint32_t conceal_filter[kMTMetaCount] = {[kMTMetaConcealLines] = kMTFilterSelect };
+
+/// Called by draw, move and plines code to determine whether a line is concealed.
+/// Scans the marktree for conceal_line marks on "row", if necessary.
+///
+/// @param check_cursor If true, avoid an early return for an unconcealed cursorline.
+/// Depending on the callsite, we still want to know whether the
+/// cursor line would be concealed if it was not the cursorline.
+///
+/// @return whether "row" is concealed
+bool decor_conceal_line(win_T *wp, int row, bool check_cursor)
+{
+ if (wp->w_p_cole < 2 || !buf_meta_total(wp->w_buffer, kMTMetaConcealLines)
+ || (!check_cursor && wp == curwin && row + 1 == wp->w_cursor.lnum
+ && !conceal_cursor_line(wp))) {
+ return false;
+ }
+
+ // Scan the marktree for any conceal_line marks on this row.
+ MTPair pair;
+ MarkTreeIter itr[1];
+ marktree_itr_get_overlap(wp->w_buffer->b_marktree, row, 0, itr);
+ while (marktree_itr_step_overlap(wp->w_buffer->b_marktree, itr, &pair)) {
+ if (mt_conceal_lines(pair.start) && ns_in_win(pair.start.ns, wp)) {
+ return true;
+ }
+ }
+
+ marktree_itr_step_out_filter(wp->w_buffer->b_marktree, itr, conceal_filter);
+
+ while (itr->x) {
+ MTKey mark = marktree_itr_current(itr);
+ if (mark.pos.row > row) {
+ break;
+ }
+ if (mt_conceal_lines(mark) && ns_in_win(pair.start.ns, wp)) {
+ return true;
+ }
+ marktree_itr_next_filter(wp->w_buffer->b_marktree, itr, row + 1, 0, conceal_filter);
+ }
+ return false;
+}
+
+/// @return whether a window may have folded or concealed lines
+bool win_lines_concealed(win_T *wp)
+{
+ return hasAnyFolding(wp)
+ || (wp->w_p_cole >= 2 && buf_meta_total(wp->w_buffer, kMTMetaConcealLines));
+}
+
int sign_item_cmp(const void *p1, const void *p2)
{
const SignItem *s1 = (SignItem *)p1;
@@ -850,8 +910,8 @@ int sign_item_cmp(const void *p1, const void *p2)
return 0;
}
-static const uint32_t sign_filter[4] = {[kMTMetaSignText] = kMTFilterSelect,
- [kMTMetaSignHL] = kMTFilterSelect };
+static const uint32_t sign_filter[kMTMetaCount] = {[kMTMetaSignText] = kMTFilterSelect,
+ [kMTMetaSignHL] = kMTFilterSelect };
/// Return the signs and highest priority sign attributes on a row.
///
@@ -873,7 +933,7 @@ void decor_redraw_signs(win_T *wp, buf_T *buf, int row, SignTextAttrs sattrs[],
// TODO(bfredl): integrate with main decor loop.
marktree_itr_get_overlap(buf->b_marktree, row, 0, itr);
while (marktree_itr_step_overlap(buf->b_marktree, itr, &pair)) {
- if (!mt_invalid(pair.start) && mt_decor_sign(pair.start)) {
+ if (!mt_invalid(pair.start) && mt_decor_sign(pair.start) && ns_in_win(pair.start.ns, wp)) {
DecorSignHighlight *sh = decor_find_sign(mt_decor(pair.start));
num_text += (sh->text[0] != NUL);
kv_push(signs, ((SignItem){ sh, pair.start.id }));
@@ -940,7 +1000,7 @@ DecorSignHighlight *decor_find_sign(DecorInline decor)
}
}
-static const uint32_t signtext_filter[4] = {[kMTMetaSignText] = kMTFilterSelect };
+static const uint32_t signtext_filter[kMTMetaCount] = {[kMTMetaSignText] = kMTFilterSelect };
/// Count the number of signs in a range after adding/removing a sign, or to
/// (re-)initialize a range in "b_signcols.count".
@@ -1035,7 +1095,7 @@ bool decor_redraw_eol(win_T *wp, DecorState *state, int *eol_attr, int eol_col)
return has_virt_pos;
}
-static const uint32_t lines_filter[4] = {[kMTMetaLines] = kMTFilterSelect };
+static const uint32_t lines_filter[kMTMetaCount] = {[kMTMetaLines] = kMTFilterSelect };
/// @param apply_folds Only count virtual lines that are not in folds.
int decor_virt_lines(win_T *wp, int start_row, int end_row, int *num_below, VirtLines *lines,
@@ -1067,7 +1127,8 @@ int decor_virt_lines(win_T *wp, int start_row, int end_row, int *num_below, Virt
int mrow = mark.pos.row;
int draw_row = mrow + (above ? 0 : 1);
if (draw_row >= start_row && draw_row < end_row
- && (!apply_folds || !hasFolding(wp, mrow + 1, NULL, NULL))) {
+ && (!apply_folds || !(hasFolding(wp, mrow + 1, NULL, NULL)
+ || decor_conceal_line(wp, mrow, false)))) {
virt_lines += (int)kv_size(vt->data.virt_lines);
if (lines) {
kv_splice(*lines, vt->data.virt_lines);
@@ -1137,6 +1198,10 @@ void decor_to_dict_legacy(Dict *dict, DecorInline decor, bool hl_name, Arena *ar
PUT_C(*dict, "conceal", CSTR_TO_ARENA_OBJ(arena, buf));
}
+ if (sh_hl.flags & kSHConcealLines) {
+ PUT_C(*dict, "conceal_lines", STRING_OBJ(cstr_as_string("")));
+ }
+
if (sh_hl.flags & kSHSpellOn) {
PUT_C(*dict, "spell", BOOLEAN_OBJ(true));
} else if (sh_hl.flags & kSHSpellOff) {
diff --git a/src/nvim/decoration_defs.h b/src/nvim/decoration_defs.h
index 8be988cd82..f84952a0e7 100644
--- a/src/nvim/decoration_defs.h
+++ b/src/nvim/decoration_defs.h
@@ -54,6 +54,7 @@ enum {
kSHSpellOn = 16,
kSHSpellOff = 32,
kSHConceal = 64,
+ kSHConcealLines = 128,
};
typedef struct {
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 68441f7adc..4a4d2baf94 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -24,6 +24,7 @@
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/decoration.h"
#include "nvim/diff.h"
#include "nvim/drawscreen.h"
#include "nvim/errors.h"
@@ -2107,7 +2108,7 @@ int diff_check_with_linestatus(win_T *wp, linenr_T lnum, int *linestatus)
}
// A closed fold never has filler lines.
- if (hasFolding(wp, lnum, NULL, NULL)) {
+ if (hasFolding(wp, lnum, NULL, NULL) || decor_conceal_line(wp, lnum - 1, false)) {
return 0;
}
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 884abb6b88..9e65abe3e4 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -1013,6 +1013,7 @@ static int get_rightmost_vcol(win_T *wp, const int *color_cols)
/// @param endrow last grid row to be redrawn
/// @param col_rows set to the height of the line when only updating the columns,
/// otherwise set to 0
+/// @param concealed only draw virtual lines belonging to the line above
/// @param spv 'spell' related variables kept between calls for "wp"
/// @param foldinfo fold info for this line
/// @param[in, out] providers decoration providers active this line
@@ -1020,8 +1021,8 @@ static int get_rightmost_vcol(win_T *wp, const int *color_cols)
/// or explicitly return `false`.
///
/// @return the number of last row the line occupies.
-int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, spellvars_T *spv,
- foldinfo_T foldinfo)
+int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, bool concealed,
+ spellvars_T *spv, foldinfo_T foldinfo)
{
colnr_T vcol_prev = -1; // "wlv.vcol" of previous character
ScreenGrid *grid = &wp->w_grid; // grid specific to the window
@@ -1122,14 +1123,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
};
buf_T *buf = wp->w_buffer;
- const bool end_fill = (lnum == buf->b_ml.ml_line_count + 1);
+ // Not drawing text when line is concealed or drawing filler lines beyond last line.
+ const bool draw_text = !concealed && (lnum != buf->b_ml.ml_line_count + 1);
- if (col_rows == 0) {
+ if (col_rows == 0 && draw_text) {
// To speed up the loop below, set extra_check when there is linebreak,
// trailing white space and/or syntax processing to be done.
extra_check = wp->w_p_lbr;
- if (syntax_present(wp) && !wp->w_s->b_syn_error && !wp->w_s->b_syn_slow
- && !has_foldtext && !end_fill) {
+ if (syntax_present(wp) && !wp->w_s->b_syn_error && !wp->w_s->b_syn_slow && !has_foldtext) {
// Prepare for syntax highlighting in this line. When there is an
// error, stop syntax highlighting.
int save_did_emsg = did_emsg;
@@ -1146,9 +1147,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
}
}
- if (!end_fill) {
- decor_providers_invoke_line(wp, lnum - 1);
- }
+ decor_providers_invoke_line(wp, lnum - 1);
has_decor = decor_redraw_line(wp, lnum - 1, &decor_state);
@@ -1331,7 +1330,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
int line_attr_save = wlv.line_attr;
int line_attr_lowprio_save = wlv.line_attr_lowprio;
- if (spv->spv_has_spell && col_rows == 0) {
+ if (spv->spv_has_spell && col_rows == 0 && draw_text) {
// Prepare for spell checking.
extra_check = true;
@@ -1357,7 +1356,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
char *line = ml_get_buf(wp->w_buffer, lnum + 1);
spell_cat_line(nextline + SPWORDLEN, line, SPWORDLEN);
}
- assert(!end_fill);
char *line = ml_get_buf(wp->w_buffer, lnum);
// If current line is empty, check first word in next line for capital.
@@ -1394,7 +1392,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
}
// current line
- char *line = end_fill ? "" : ml_get_buf(wp->w_buffer, lnum);
+ char *line = draw_text ? ml_get_buf(wp->w_buffer, lnum) : "";
// current position in "line"
char *ptr = line;
@@ -1405,7 +1403,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
const schar_T lcs_eol = wp->w_p_lcs_chars.eol; // 'eol' value
schar_T lcs_prec_todo = wp->w_p_lcs_chars.prec; // 'prec' until it's been used, then NUL
- if (wp->w_p_list && !has_foldtext && !end_fill) {
+ if (wp->w_p_list && !has_foldtext && draw_text) {
if (wp->w_p_lcs_chars.space
|| wp->w_p_lcs_chars.multispace != NULL
|| wp->w_p_lcs_chars.leadmultispace != NULL
@@ -1577,7 +1575,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
}
}
- if (col_rows == 0 && !has_foldtext && !end_fill) {
+ if (col_rows == 0 && draw_text && !has_foldtext) {
const int v = (int)(ptr - line);
area_highlighting |= prepare_search_hl_line(wp, lnum, v,
&line, &screen_search_hl, &search_attr,
@@ -1645,7 +1643,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
if (wp->w_redr_statuscol) {
break;
}
- if (!end_fill) {
+ if (draw_text) {
// Get the line again as evaluating 'statuscolumn' may free it.
line = ml_get_buf(wp->w_buffer, lnum);
ptr = line + v;
@@ -1681,7 +1679,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
break;
}
wlv.filler_todo--;
- if (wlv.filler_todo == 0 && (wp->w_botfill || end_fill)) {
+ if (wlv.filler_todo == 0 && (wp->w_botfill || !draw_text)) {
break;
}
// win_line_start(wp, &wlv);
@@ -3072,8 +3070,8 @@ end_check:
virt_line_index = -1;
virt_line_flags = 0;
// When the filler lines are actually below the last line of the
- // file, don't draw the line itself, break here.
- if (wlv.filler_todo == 0 && (wp->w_botfill || end_fill)) {
+ // file, or we are not drawing text for this line, break here.
+ if (wlv.filler_todo == 0 && (wp->w_botfill || !draw_text)) {
break;
}
}
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index 162bcb910b..bb2c41d91a 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -147,6 +147,11 @@ void conceal_check_cursor_line(void)
}
redrawWinline(curwin, curwin->w_cursor.lnum);
+
+ // Concealed line visibility toggled.
+ if (decor_conceal_line(curwin, curwin->w_cursor.lnum - 1, true)) {
+ changed_window_setting(curwin);
+ }
// Need to recompute cursor column, e.g., when starting Visual mode
// without concealing.
curs_columns(curwin, true);
@@ -1621,7 +1626,7 @@ static void win_update(win_T *wp)
}
}
- if (mod_top != 0 && hasAnyFolding(wp)) {
+ if (mod_top != 0 && win_lines_concealed(wp)) {
// A change in a line can cause lines above it to become folded or
// unfolded. Find the top most buffer line that may be affected.
// If the line was previously folded and displayed, get the first
@@ -1718,12 +1723,12 @@ static void win_update(win_T *wp)
&& wp->w_topfill > wp->w_old_topfill))) {
// New topline is above old topline: May scroll down.
int j;
- if (hasAnyFolding(wp)) {
- // count the number of lines we are off, counting a sequence
- // of folded lines as one
+ if (win_lines_concealed(wp)) {
+ // Count the number of lines we are off, counting a sequence
+ // of folded lines as one, and skip concealed lines.
j = 0;
for (linenr_T ln = wp->w_topline; ln < wp->w_lines[0].wl_lnum; ln++) {
- j++;
+ j += !decor_conceal_line(wp, ln - 1, false);
if (j >= wp->w_grid.rows - 2) {
break;
}
@@ -2101,6 +2106,31 @@ static void win_update(win_T *wp)
top_to_mod = false;
}
+ // When lines are folded, display one line for all of them.
+ // Otherwise, display normally (can be several display lines when
+ // 'wrap' is on).
+ foldinfo_T foldinfo = wp->w_p_cul && lnum == wp->w_cursor.lnum
+ ? cursorline_fi : fold_info(wp, lnum);
+
+ // If the line is concealed and has no filler lines, go to the next line.
+ bool concealed = decor_conceal_line(wp, lnum - 1, false);
+ if (concealed) {
+ if (wp == curwin && lnum == curwin->w_cursor.lnum) {
+ conceal_cursor_used = conceal_cursor_line(curwin);
+ }
+ if (idx > 0) {
+ wp->w_lines[idx - 1].wl_lastlnum = lnum + foldinfo.fi_lines - 1;
+ }
+ if (lnum == mod_top && lnum < mod_bot) {
+ mod_top += foldinfo.fi_lines ? foldinfo.fi_lines : 1;
+ }
+ if (win_get_fill(wp, lnum) == 0) {
+ lnum += foldinfo.fi_lines ? foldinfo.fi_lines : 1;
+ spv.spv_capcol_lnum = 0;
+ continue;
+ }
+ }
+
// When at start of changed lines: May scroll following lines
// up or down to minimize redrawing.
// Don't do this when the change continues until the end.
@@ -2150,8 +2180,10 @@ static void win_update(win_T *wp)
// rows, and may insert/delete lines
int j = idx;
for (l = lnum; l < mod_bot; l++) {
+ linenr_T first = l;
+ int prev_rows = new_rows;
if (hasFolding(wp, l, NULL, &l)) {
- new_rows++;
+ new_rows += !decor_conceal_line(wp, first - 1, false);
} else if (l == wp->w_topline) {
int n = plines_win_nofill(wp, l, false) + wp->w_topfill
- adjust_plines_for_skipcol(wp);
@@ -2160,7 +2192,8 @@ static void win_update(win_T *wp)
} else {
new_rows += plines_win(wp, l, true);
}
- j++;
+ // Do not increment when height was 0 (for a concealed line).
+ j += (prev_rows != new_rows);
if (new_rows > wp->w_grid.rows - row - 2) {
// it's getting too much, must redraw the rest
new_rows = 9999;
@@ -2242,12 +2275,6 @@ static void win_update(win_T *wp)
}
}
- // When lines are folded, display one line for all of them.
- // Otherwise, display normally (can be several display lines when
- // 'wrap' is on).
- foldinfo_T foldinfo = wp->w_p_cul && lnum == wp->w_cursor.lnum
- ? cursorline_fi : fold_info(wp, lnum);
-
if (foldinfo.fi_lines == 0
&& idx < wp->w_lines_valid
&& wp->w_lines[idx].wl_valid
@@ -2267,11 +2294,11 @@ static void win_update(win_T *wp)
syntax_end_parsing(wp, syntax_last_parsed + 1);
}
- bool display_buf_line = (foldinfo.fi_lines == 0 || *wp->w_p_fdt == NUL);
+ bool display_buf_line = !concealed && (foldinfo.fi_lines == 0 || *wp->w_p_fdt == NUL);
// Display one line
spellvars_T zero_spv = { 0 };
- row = win_line(wp, lnum, srow, wp->w_grid.rows, 0,
+ row = win_line(wp, lnum, srow, wp->w_grid.rows, 0, concealed,
display_buf_line ? &spv : &zero_spv, foldinfo);
if (display_buf_line) {
@@ -2282,11 +2309,13 @@ static void win_update(win_T *wp)
if (foldinfo.fi_lines == 0) {
wp->w_lines[idx].wl_folded = false;
+ wp->w_lines[idx].wl_foldend = lnum;
wp->w_lines[idx].wl_lastlnum = lnum;
did_update = DID_LINE;
} else {
foldinfo.fi_lines--;
wp->w_lines[idx].wl_folded = true;
+ wp->w_lines[idx].wl_foldend = lnum + foldinfo.fi_lines;
wp->w_lines[idx].wl_lastlnum = lnum + foldinfo.fi_lines;
did_update = DID_FOLD;
}
@@ -2313,12 +2342,13 @@ static void win_update(win_T *wp)
// - 'number' is set and below inserted/deleted lines, or
// - 'relativenumber' is set and cursor moved vertically,
// the text doesn't need to be redrawn, but the number column does.
- if ((wp->w_p_nu && mod_top != 0 && lnum >= mod_bot
- && buf->b_mod_set && buf->b_mod_xlines != 0)
- || (wp->w_p_rnu && wp->w_last_cursor_lnum_rnu != wp->w_cursor.lnum)) {
+ if (((wp->w_p_nu && mod_top != 0 && lnum >= mod_bot
+ && buf->b_mod_set && buf->b_mod_xlines != 0)
+ || (wp->w_p_rnu && wp->w_last_cursor_lnum_rnu != wp->w_cursor.lnum))
+ && !decor_conceal_line(wp, lnum - 1, true)) {
foldinfo_T info = wp->w_p_cul && lnum == wp->w_cursor.lnum
? cursorline_fi : fold_info(wp, lnum);
- win_line(wp, lnum, srow, wp->w_grid.rows, wp->w_lines[idx].wl_size, &spv, info);
+ win_line(wp, lnum, srow, wp->w_grid.rows, wp->w_lines[idx].wl_size, false, &spv, info);
}
// This line does not need to be drawn, advance to the next one.
@@ -2414,7 +2444,7 @@ redr_statuscol:
// for ml_line_count+1 and only draw filler lines
spellvars_T zero_spv = { 0 };
foldinfo_T zero_foldinfo = { 0 };
- row = win_line(wp, wp->w_botline, row, wp->w_grid.rows, 0, &zero_spv, zero_foldinfo);
+ row = win_line(wp, wp->w_botline, row, wp->w_grid.rows, 0, false, &zero_spv, zero_foldinfo);
if (wp->w_redr_statuscol) {
eof = false;
goto redr_statuscol;
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 6277cd83b6..af3471184d 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -17,6 +17,7 @@
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/decoration.h"
#include "nvim/digraph.h"
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
@@ -2586,15 +2587,15 @@ int oneleft(void)
return OK;
}
-/// Move the cursor up "n" lines in window "wp".
-/// Takes care of closed folds.
-void cursor_up_inner(win_T *wp, linenr_T n)
+/// Move the cursor up "n" lines in window "wp". Takes care of closed folds.
+/// Skips over concealed lines when "skip_conceal" is true.
+void cursor_up_inner(win_T *wp, linenr_T n, bool skip_conceal)
{
linenr_T lnum = wp->w_cursor.lnum;
if (n >= lnum) {
lnum = 1;
- } else if (hasAnyFolding(wp)) {
+ } else if (win_lines_concealed(wp)) {
// Count each sequence of folded lines as one logical line.
// go to the start of the current fold
@@ -2603,6 +2604,7 @@ void cursor_up_inner(win_T *wp, linenr_T n)
while (n--) {
// move up one line
lnum--;
+ n += skip_conceal && decor_conceal_line(wp, lnum - 1, true);
if (lnum <= 1) {
break;
}
@@ -2628,7 +2630,7 @@ int cursor_up(linenr_T n, bool upd_topline)
if (n > 0 && curwin->w_cursor.lnum <= 1) {
return FAIL;
}
- cursor_up_inner(curwin, n);
+ cursor_up_inner(curwin, n, false);
// try to advance to the column we want to be at
coladvance(curwin, curwin->w_curswant);
@@ -2640,16 +2642,16 @@ int cursor_up(linenr_T n, bool upd_topline)
return OK;
}
-/// Move the cursor down "n" lines in window "wp".
-/// Takes care of closed folds.
-void cursor_down_inner(win_T *wp, int n)
+/// Move the cursor down "n" lines in window "wp". Takes care of closed folds.
+/// Skips over concealed lines when "skip_conceal" is true.
+void cursor_down_inner(win_T *wp, int n, bool skip_conceal)
{
linenr_T lnum = wp->w_cursor.lnum;
linenr_T line_count = wp->w_buffer->b_ml.ml_line_count;
if (lnum + n >= line_count) {
lnum = line_count;
- } else if (hasAnyFolding(wp)) {
+ } else if (win_lines_concealed(wp)) {
linenr_T last;
// count each sequence of folded lines as one logical line
@@ -2659,6 +2661,7 @@ void cursor_down_inner(win_T *wp, int n)
} else {
lnum++;
}
+ n += skip_conceal && decor_conceal_line(wp, lnum - 1, true);
if (lnum >= line_count) {
break;
}
@@ -2680,7 +2683,7 @@ int cursor_down(int n, bool upd_topline)
if (n > 0 && lnum >= curwin->w_buffer->b_ml.ml_line_count) {
return FAIL;
}
- cursor_down_inner(curwin, n);
+ cursor_down_inner(curwin, n, false);
// try to advance to the column we want to be at
coladvance(curwin, curwin->w_curswant);
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 41b5ce9a32..80a1ed9667 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -194,7 +194,7 @@ bool hasFoldingWin(win_T *const win, const linenr_T lnum, linenr_T *const firstp
const int x = find_wl_entry(win, lnum);
if (x >= 0) {
first = win->w_lines[x].wl_lnum;
- last = win->w_lines[x].wl_lastlnum;
+ last = win->w_lines[x].wl_foldend;
had_folded = win->w_lines[x].wl_folded;
}
}
@@ -971,7 +971,7 @@ int find_wl_entry(win_T *win, linenr_T lnum)
if (lnum < win->w_lines[i].wl_lnum) {
return -1;
}
- if (lnum <= win->w_lines[i].wl_lastlnum) {
+ if (lnum <= win->w_lines[i].wl_foldend) {
return i;
}
}
diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c
index 5ccd4fd45d..e2b4296fcd 100644
--- a/src/nvim/marktree.c
+++ b/src/nvim/marktree.c
@@ -250,7 +250,7 @@ static inline void split_node(MarkTree *b, MTNode *x, const int i, MTKey next)
refkey(b, x, i);
x->n++;
- uint32_t meta_inc[4];
+ uint32_t meta_inc[kMTMetaCount];
meta_describe_key(meta_inc, x->key[i]);
for (int m = 0; m < kMTMetaCount; m++) {
// y used contain all of z and x->key[i], discount those
@@ -451,6 +451,7 @@ static void meta_describe_key_inc(uint32_t *meta_inc, MTKey *k)
meta_inc[kMTMetaLines] += (k->flags & MT_FLAG_DECOR_VIRT_LINES) ? 1 : 0;
meta_inc[kMTMetaSignHL] += (k->flags & MT_FLAG_DECOR_SIGNHL) ? 1 : 0;
meta_inc[kMTMetaSignText] += (k->flags & MT_FLAG_DECOR_SIGNTEXT) ? 1 : 0;
+ meta_inc[kMTMetaConcealLines] += (k->flags & MT_FLAG_DECOR_CONCEAL_LINES) ? 1 : 0;
}
}
@@ -505,10 +506,10 @@ void marktree_put_key(MarkTree *b, MTKey k)
r = s;
}
- uint32_t meta_inc[4];
+ uint32_t meta_inc[kMTMetaCount];
meta_describe_key(meta_inc, k);
marktree_putp_aux(b, r, k, meta_inc);
- for (int m = 0; m < 4; m++) {
+ for (int m = 0; m < kMTMetaCount; m++) {
b->meta_root[m] += meta_inc[m];
}
b->n_keys++;
@@ -579,7 +580,7 @@ uint64_t marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev)
assert(x->level == 0);
MTKey intkey = x->key[itr->i];
- uint32_t meta_inc[4];
+ uint32_t meta_inc[kMTMetaCount];
meta_describe_key(meta_inc, intkey);
if (x->n > itr->i + 1) {
memmove(&x->key[itr->i], &x->key[itr->i + 1],
@@ -776,7 +777,7 @@ uint64_t marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev)
void marktree_revise_meta(MarkTree *b, MarkTreeIter *itr, MTKey old_key)
{
- uint32_t meta_old[4], meta_new[4];
+ uint32_t meta_old[kMTMetaCount], meta_new[kMTMetaCount];
meta_describe_key(meta_old, old_key);
meta_describe_key(meta_new, rawkey(itr));
@@ -1038,7 +1039,7 @@ static MTNode *merge_node(MarkTree *b, MTNode *p, int i)
relative(p->key[i - 1].pos, &x->key[x->n].pos);
}
- uint32_t meta_inc[4];
+ uint32_t meta_inc[kMTMetaCount];
meta_describe_key(meta_inc, x->key[x->n]);
memmove(&x->key[x->n + 1], y->key, (size_t)y->n * sizeof(MTKey));
@@ -1128,9 +1129,9 @@ static void pivot_right(MarkTree *b, MTPos p_pos, MTNode *p, const int i)
p->key[i] = x->key[x->n - 1];
refkey(b, p, i);
- uint32_t meta_inc_y[4];
+ uint32_t meta_inc_y[kMTMetaCount];
meta_describe_key(meta_inc_y, y->key[0]);
- uint32_t meta_inc_x[4];
+ uint32_t meta_inc_x[kMTMetaCount];
meta_describe_key(meta_inc_x, p->key[i]);
for (int m = 0; m < kMTMetaCount; m++) {
@@ -1214,9 +1215,9 @@ static void pivot_left(MarkTree *b, MTPos p_pos, MTNode *p, int i)
p->key[i] = y->key[0];
refkey(b, p, i);
- uint32_t meta_inc_x[4];
+ uint32_t meta_inc_x[kMTMetaCount];
meta_describe_key(meta_inc_x, x->key[x->n]);
- uint32_t meta_inc_y[4];
+ uint32_t meta_inc_y[kMTMetaCount];
meta_describe_key(meta_inc_y, p->key[i]);
for (int m = 0; m < kMTMetaCount; m++) {
p->meta[i][m] += meta_inc_x[m];
@@ -1619,8 +1620,13 @@ bool marktree_itr_next_filter(MarkTree *b, MarkTreeIter *itr, int stop_row, int
return marktree_itr_check_filter(b, itr, stop_row, stop_col, meta_filter);
}
-const uint32_t meta_map[4] = { MT_FLAG_DECOR_VIRT_TEXT_INLINE, MT_FLAG_DECOR_VIRT_LINES,
- MT_FLAG_DECOR_SIGNHL, MT_FLAG_DECOR_SIGNTEXT };
+const uint32_t meta_map[kMTMetaCount] = {
+ MT_FLAG_DECOR_VIRT_TEXT_INLINE,
+ MT_FLAG_DECOR_VIRT_LINES,
+ MT_FLAG_DECOR_SIGNHL,
+ MT_FLAG_DECOR_SIGNTEXT,
+ MT_FLAG_DECOR_CONCEAL_LINES
+};
static bool marktree_itr_check_filter(MarkTree *b, MarkTreeIter *itr, int stop_row, int stop_col,
MetaFilter meta_filter)
{
@@ -1860,9 +1866,9 @@ static void swap_keys(MarkTree *b, MarkTreeIter *itr1, MarkTreeIter *itr2, Damag
itr2->i, itr1->i }));
}
- uint32_t meta_inc_1[4];
+ uint32_t meta_inc_1[kMTMetaCount];
meta_describe_key(meta_inc_1, rawkey(itr1));
- uint32_t meta_inc_2[4];
+ uint32_t meta_inc_2[kMTMetaCount];
meta_describe_key(meta_inc_2, rawkey(itr2));
if (memcmp(meta_inc_1, meta_inc_2, sizeof(meta_inc_1)) != 0) {
@@ -2373,7 +2379,7 @@ size_t marktree_check_node(MarkTree *b, MTNode *x, MTPos *last, bool *last_right
*last = x->key[x->n - 1].pos;
}
- uint32_t meta_node[4];
+ uint32_t meta_node[kMTMetaCount];
meta_describe_node(meta_node, x);
for (int m = 0; m < kMTMetaCount; m++) {
assert(meta_node_ref[m] == meta_node[m]);
diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h
index 1fb1a74487..e5d5ce1c78 100644
--- a/src/nvim/marktree.h
+++ b/src/nvim/marktree.h
@@ -33,6 +33,7 @@
#define MT_FLAG_DECOR_SIGNHL (((uint16_t)1) << 10)
#define MT_FLAG_DECOR_VIRT_LINES (((uint16_t)1) << 11)
#define MT_FLAG_DECOR_VIRT_TEXT_INLINE (((uint16_t)1) << 12)
+#define MT_FLAG_DECOR_CONCEAL_LINES (((uint16_t)1) << 13)
// These _must_ be last to preserve ordering of marks
#define MT_FLAG_RIGHT_GRAVITY (((uint16_t)1) << 14)
@@ -42,8 +43,8 @@
| MT_FLAG_DECOR_SIGNHL | MT_FLAG_DECOR_VIRT_LINES \
| MT_FLAG_DECOR_VIRT_TEXT_INLINE)
-#define MT_FLAG_EXTERNAL_MASK (MT_FLAG_DECOR_MASK | MT_FLAG_NO_UNDO \
- | MT_FLAG_INVALIDATE | MT_FLAG_INVALID)
+#define MT_FLAG_EXTERNAL_MASK (MT_FLAG_DECOR_MASK | MT_FLAG_NO_UNDO | MT_FLAG_INVALIDATE \
+ | MT_FLAG_INVALID | MT_FLAG_DECOR_CONCEAL_LINES)
// this is defined so that start and end of the same range have adjacent ids
#define MARKTREE_END_FLAG ((uint64_t)1)
@@ -107,6 +108,11 @@ static inline bool mt_decor_sign(MTKey key)
return key.flags & (MT_FLAG_DECOR_SIGNTEXT | MT_FLAG_DECOR_SIGNHL);
}
+static inline bool mt_conceal_lines(MTKey key)
+{
+ return key.flags & MT_FLAG_DECOR_CONCEAL_LINES;
+}
+
static inline uint16_t mt_flags(bool right_gravity, bool no_undo, bool invalidate, bool decor_ext)
{
return (uint16_t)((right_gravity ? MT_FLAG_RIGHT_GRAVITY : 0)
diff --git a/src/nvim/marktree_defs.h b/src/nvim/marktree_defs.h
index d43130db6f..32a27a51a1 100644
--- a/src/nvim/marktree_defs.h
+++ b/src/nvim/marktree_defs.h
@@ -22,13 +22,12 @@ typedef struct {
} MTPos;
#define MTPos(r, c) ((MTPos){ .row = (r), .col = (c) })
-// Currently there are four counts, which makes for a uint32_t[4] per node
-// which makes for nice autovectorization into a single XMM or NEON register
typedef enum {
kMTMetaInline,
kMTMetaLines,
kMTMetaSignHL,
kMTMetaSignText,
+ kMTMetaConcealLines,
kMTMetaCount, // sentinel, must be last
} MetaIndex;
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 424f2a6f3b..bb02bc9eb5 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -17,6 +17,7 @@
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/cursor.h"
+#include "nvim/decoration.h"
#include "nvim/diff.h"
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
@@ -317,14 +318,13 @@ void update_topline(win_T *wp)
halfheight = 2;
}
int64_t n;
- if (hasAnyFolding(wp)) {
+ if (win_lines_concealed(wp)) {
// Count the number of logical lines between the cursor and
// topline + p_so (approximation of how much will be
// scrolled).
n = 0;
- for (linenr_T lnum = wp->w_cursor.lnum;
- lnum < wp->w_topline + *so_ptr; lnum++) {
- n++;
+ for (linenr_T lnum = wp->w_cursor.lnum; lnum < wp->w_topline + *so_ptr; lnum++) {
+ n += !decor_conceal_line(wp, lnum, false);
// stop at end of file or when we know we are far off
assert(wp->w_buffer != 0);
if (lnum >= wp->w_buffer->b_ml.ml_line_count || n >= halfheight) {
@@ -366,7 +366,7 @@ void update_topline(win_T *wp)
assert(wp->w_buffer != 0);
if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count) {
if (wp->w_cursor.lnum < wp->w_botline) {
- if ((wp->w_cursor.lnum >= wp->w_botline - *so_ptr || hasAnyFolding(wp))) {
+ if ((wp->w_cursor.lnum >= wp->w_botline - *so_ptr || win_lines_concealed(wp))) {
lineoff_T loff;
// Cursor is (a few lines) above botline, check if there are
@@ -398,13 +398,12 @@ void update_topline(win_T *wp)
}
if (check_botline) {
int line_count = 0;
- if (hasAnyFolding(wp)) {
+ if (win_lines_concealed(wp)) {
// Count the number of logical lines between the cursor and
// botline - p_so (approximation of how much will be
// scrolled).
- for (linenr_T lnum = wp->w_cursor.lnum;
- lnum >= wp->w_botline - *so_ptr; lnum--) {
- line_count++;
+ for (linenr_T lnum = wp->w_cursor.lnum; lnum >= wp->w_botline - *so_ptr; lnum--) {
+ line_count += !decor_conceal_line(wp, lnum - 1, false);
// stop at end of file or when we know we are far off
if (lnum <= 0 || line_count > wp->w_height_inner + 1) {
break;
@@ -461,7 +460,7 @@ static int scrolljump_value(win_T *wp)
static bool check_top_offset(win_T *wp)
{
int so = get_scrolloff_value(wp);
- if (wp->w_cursor.lnum < wp->w_topline + so || hasAnyFolding(wp)) {
+ if (wp->w_cursor.lnum < wp->w_topline + so || win_lines_concealed(wp)) {
lineoff_T loff;
loff.lnum = wp->w_cursor.lnum;
loff.fill = 0;
@@ -505,6 +504,13 @@ void check_cursor_moved(win_T *wp)
if (wp->w_cursor.lnum != wp->w_valid_cursor.lnum) {
wp->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL
|VALID_CHEIGHT|VALID_CROW|VALID_TOPLINE);
+
+ // Concealed line visibility toggled.
+ if (wp->w_p_cole >= 2 && !conceal_cursor_line(wp)
+ && (decor_conceal_line(wp, wp->w_cursor.lnum - 1, true)
+ || decor_conceal_line(wp, wp->w_valid_cursor.lnum - 1, true))) {
+ changed_window_setting(wp);
+ }
wp->w_valid_cursor = wp->w_cursor;
wp->w_valid_leftcol = wp->w_leftcol;
wp->w_valid_skipcol = wp->w_skipcol;
@@ -1351,13 +1357,14 @@ bool scrolldown(win_T *wp, linenr_T line_count, int byfold)
// A sequence of folded lines only counts for one logical line
linenr_T first;
if (hasFolding(wp, wp->w_topline, &first, NULL)) {
- done++;
+ done += !decor_conceal_line(wp, first - 1, false);
if (!byfold) {
todo -= wp->w_topline - first - 1;
}
wp->w_botline -= wp->w_topline - first;
wp->w_topline = first;
} else {
+ todo += decor_conceal_line(wp, wp->w_topline - 1, false);
if (do_sms) {
int size = linetabsize_eol(wp, wp->w_topline);
if (size > width1) {
@@ -1400,12 +1407,8 @@ bool scrolldown(win_T *wp, linenr_T line_count, int byfold)
while (wrow >= wp->w_height_inner && wp->w_cursor.lnum > 1) {
linenr_T first;
if (hasFolding(wp, wp->w_cursor.lnum, &first, NULL)) {
- wrow--;
- if (first == 1) {
- wp->w_cursor.lnum = 1;
- } else {
- wp->w_cursor.lnum = first - 1;
- }
+ wrow -= !decor_conceal_line(wp, wp->w_cursor.lnum - 1, false);
+ wp->w_cursor.lnum = MAX(first - 1, 1);
} else {
wrow -= plines_win(wp, wp->w_cursor.lnum--, true);
}
@@ -1433,7 +1436,7 @@ bool scrollup(win_T *wp, linenr_T line_count, bool byfold)
linenr_T botline = wp->w_botline;
bool do_sms = wp->w_p_wrap && wp->w_p_sms;
- if (do_sms || (byfold && hasAnyFolding(wp)) || win_may_fill(wp)) {
+ if (do_sms || (byfold && win_lines_concealed(wp)) || win_may_fill(wp)) {
int width1 = wp->w_width_inner - win_col_off(wp);
int width2 = width1 + win_col_off2(wp);
int size = 0;
@@ -1448,6 +1451,7 @@ bool scrollup(win_T *wp, linenr_T line_count, bool byfold)
// the line, then advance to the next line.
// folding: count each sequence of folded lines as one logical line.
for (int todo = line_count; todo > 0; todo--) {
+ todo += decor_conceal_line(wp, wp->w_topline - 1, false);
if (wp->w_topfill > 0) {
wp->w_topfill--;
} else {
@@ -1504,10 +1508,8 @@ bool scrollup(win_T *wp, linenr_T line_count, bool byfold)
check_topfill(wp, false);
- if (hasAnyFolding(wp)) {
- // Make sure w_topline is at the first of a sequence of folded lines.
- hasFolding(wp, wp->w_topline, &wp->w_topline, NULL);
- }
+ // Make sure w_topline is at the first of a sequence of folded lines.
+ hasFolding(wp, wp->w_topline, &wp->w_topline, NULL);
wp->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
if (wp->w_cursor.lnum < wp->w_topline) {
@@ -1710,8 +1712,8 @@ static void topline_back_winheight(win_T *wp, lineoff_T *lp, int winheight)
if (lp->lnum < 1) {
lp->height = MAXCOL;
} else if (hasFolding(wp, lp->lnum, &lp->lnum, NULL)) {
- // Add a closed fold
- lp->height = 1;
+ // Add a closed fold unless concealed.
+ lp->height = !decor_conceal_line(wp, lp->lnum - 1, false);
} else {
lp->height = plines_win_nofill(wp, lp->lnum, winheight);
}
@@ -1740,8 +1742,8 @@ static void botline_forw(win_T *wp, lineoff_T *lp)
if (lp->lnum > wp->w_buffer->b_ml.ml_line_count) {
lp->height = MAXCOL;
} else if (hasFolding(wp, lp->lnum, NULL, &lp->lnum)) {
- // Add a closed fold
- lp->height = 1;
+ // Add a closed fold unless concealed.
+ lp->height = !decor_conceal_line(wp, lp->lnum - 1, false);
} else {
lp->height = plines_win_nofill(wp, lp->lnum, true);
}
@@ -1793,9 +1795,8 @@ void scroll_cursor_top(win_T *wp, int min_scroll, int always)
// Check if the lines from "top" to "bot" fit in the window. If they do,
// set new_topline and advance "top" and "bot" to include more lines.
while (top > 0) {
- int i = hasFolding(wp, top, &top, NULL)
- ? 1 // count one logical line for a sequence of folded lines
- : plines_win_nofill(wp, top, true);
+ int i = plines_win_nofill(wp, top, true);
+ hasFolding(wp, top, &top, NULL);
if (top < wp->w_topline) {
scrolled += i;
}
@@ -1807,12 +1808,7 @@ void scroll_cursor_top(win_T *wp, int min_scroll, int always)
used += i;
if (extra + i <= off && bot < wp->w_buffer->b_ml.ml_line_count) {
- if (hasFolding(wp, bot, NULL, &bot)) {
- // count one logical line for a sequence of folded lines
- used++;
- } else {
- used += plines_win(wp, bot, true);
- }
+ used += plines_win_full(wp, bot, &bot, NULL, true, true);
}
if (used > wp->w_height_inner) {
break;
@@ -2273,7 +2269,7 @@ void cursor_correct(win_T *wp)
linenr_T cln = wp->w_cursor.lnum; // Cursor Line Number
if (cln >= wp->w_topline + above_wanted
&& cln < wp->w_botline - below_wanted
- && !hasAnyFolding(wp)) {
+ && !win_lines_concealed(wp)) {
return;
}
@@ -2298,19 +2294,12 @@ void cursor_correct(win_T *wp)
int below = wp->w_filler_rows; // screen lines below botline
while ((above < above_wanted || below < below_wanted) && topline < botline) {
if (below < below_wanted && (below <= above || above >= above_wanted)) {
- if (hasFolding(wp, botline, &botline, NULL)) {
- below++;
- } else {
- below += plines_win(wp, botline, true);
- }
+ below += plines_win_full(wp, botline, &botline, NULL, true, true);
botline--;
}
if (above < above_wanted && (above < below || below >= below_wanted)) {
- if (hasFolding(wp, topline, NULL, &topline)) {
- above++;
- } else {
- above += plines_win_nofill(wp, topline, true);
- }
+ above += plines_win_nofill(wp, topline, true);
+ hasFolding(wp, topline, NULL, &topline);
// Count filler lines below this line as context.
if (topline < botline) {
@@ -2464,7 +2453,8 @@ int pagescroll(Direction dir, int count, bool half)
int curscount = count;
// Adjust count so as to not reveal end of buffer lines.
if (dir == FORWARD
- && (curwin->w_topline + curwin->w_height_inner + count > buflen || hasAnyFolding(curwin))) {
+ && (curwin->w_topline + curwin->w_height_inner + count > buflen
+ || win_lines_concealed(curwin))) {
int n = plines_correct_topline(curwin, curwin->w_topline, NULL, false, NULL);
if (n - count < curwin->w_height_inner && curwin->w_topline < buflen) {
n += plines_m_win(curwin, curwin->w_topline + 1, buflen, curwin->w_height_inner + count);
@@ -2482,13 +2472,14 @@ int pagescroll(Direction dir, int count, bool half)
curwin->w_curswant = prev_curswant;
}
- // Move the cursor the same amount of screen lines.
+ // Move the cursor the same amount of screen lines, skipping over
+ // concealed lines as those were not included in "curscount".
if (curwin->w_p_wrap) {
- nv_screengo(&oa, dir, curscount);
+ nv_screengo(&oa, dir, curscount, true);
} else if (dir == FORWARD) {
- cursor_down_inner(curwin, curscount);
+ cursor_down_inner(curwin, curscount, true);
} else {
- cursor_up_inner(curwin, curscount);
+ cursor_up_inner(curwin, curscount, true);
}
} else {
// Scroll [count] times 'window' or current window height lines.
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 0cd60c7dc7..44e48e9e72 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -24,6 +24,7 @@
#include "nvim/charset.h"
#include "nvim/cmdhist.h"
#include "nvim/cursor.h"
+#include "nvim/decoration.h"
#include "nvim/diff.h"
#include "nvim/digraph.h"
#include "nvim/drawscreen.h"
@@ -2486,7 +2487,7 @@ bool find_decl(char *ptr, size_t len, bool locally, bool thisblock, int flags_ar
/// 'dist' must be positive.
///
/// @return true if able to move cursor, false otherwise.
-bool nv_screengo(oparg_T *oap, int dir, int dist)
+bool nv_screengo(oparg_T *oap, int dir, int dist, bool skip_conceal)
{
int linelen = linetabsize(curwin, curwin->w_cursor.lnum);
bool retval = true;
@@ -2548,7 +2549,7 @@ bool nv_screengo(oparg_T *oap, int dir, int dist)
retval = false;
break;
}
- cursor_up_inner(curwin, 1);
+ cursor_up_inner(curwin, 1, skip_conceal);
linelen = linetabsize(curwin, curwin->w_cursor.lnum);
if (linelen > width1) {
@@ -2573,7 +2574,7 @@ bool nv_screengo(oparg_T *oap, int dir, int dist)
retval = false;
break;
}
- cursor_down_inner(curwin, 1);
+ cursor_down_inner(curwin, 1, skip_conceal);
curwin->w_curswant %= width2;
// Check if the cursor has moved below the number display
@@ -3616,12 +3617,11 @@ static void nv_scroll(cmdarg_T *cap)
if (cap->count1 - 1 >= curwin->w_cursor.lnum) {
curwin->w_cursor.lnum = 1;
} else {
- if (hasAnyFolding(curwin)) {
+ if (win_lines_concealed(curwin)) {
// Count a fold for one screen line.
- for (n = cap->count1 - 1; n > 0
- && curwin->w_cursor.lnum > curwin->w_topline; n--) {
- hasFolding(curwin, curwin->w_cursor.lnum,
- &curwin->w_cursor.lnum, NULL);
+ for (n = cap->count1 - 1; n > 0 && curwin->w_cursor.lnum > curwin->w_topline; n--) {
+ hasFolding(curwin, curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL);
+ n += decor_conceal_line(curwin, curwin->w_cursor.lnum, true);
if (curwin->w_cursor.lnum > curwin->w_topline) {
curwin->w_cursor.lnum--;
}
@@ -3634,8 +3634,7 @@ static void nv_scroll(cmdarg_T *cap)
if (cap->cmdchar == 'M') {
int used = 0;
// Don't count filler lines above the window.
- used -= win_get_fill(curwin, curwin->w_topline)
- - curwin->w_topfill;
+ used -= win_get_fill(curwin, curwin->w_topline) - curwin->w_topfill;
validate_botline(curwin); // make sure w_empty_rows is valid
int half = (curwin->w_height_inner - curwin->w_empty_rows + 1) / 2;
for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; n++) {
@@ -3658,10 +3657,11 @@ static void nv_scroll(cmdarg_T *cap)
}
} else { // (cap->cmdchar == 'H')
n = cap->count1 - 1;
- if (hasAnyFolding(curwin)) {
+ if (win_lines_concealed(curwin)) {
// Count a fold for one screen line.
lnum = curwin->w_topline;
- while (n-- > 0 && lnum < curwin->w_botline - 1) {
+ while ((decor_conceal_line(curwin, lnum - 1, true) || n-- > 0)
+ && lnum < curwin->w_botline - 1) {
hasFolding(curwin, lnum, NULL, &lnum);
lnum++;
}
@@ -5316,7 +5316,7 @@ static void nv_g_dollar_cmd(cmdarg_T *cap)
curwin->w_cursor.col--;
}
}
- } else if (nv_screengo(oap, FORWARD, cap->count1 - 1) == false) {
+ } else if (nv_screengo(oap, FORWARD, cap->count1 - 1, false) == false) {
clearopbeep(oap);
}
} else {
@@ -5443,7 +5443,7 @@ static void nv_g_cmd(cmdarg_T *cap)
oap->motion_type = kMTLineWise;
i = cursor_down(cap->count1, oap->op_type == OP_NOP);
} else {
- i = nv_screengo(oap, FORWARD, cap->count1);
+ i = nv_screengo(oap, FORWARD, cap->count1, false);
}
if (!i) {
clearopbeep(oap);
@@ -5457,7 +5457,7 @@ static void nv_g_cmd(cmdarg_T *cap)
oap->motion_type = kMTLineWise;
i = cursor_up(cap->count1, oap->op_type == OP_NOP);
} else {
- i = nv_screengo(oap, BACKWARD, cap->count1);
+ i = nv_screengo(oap, BACKWARD, cap->count1, false);
}
if (!i) {
clearopbeep(oap);
diff --git a/src/nvim/plines.c b/src/nvim/plines.c
index 4d1cf80e77..3f0fb3795e 100644
--- a/src/nvim/plines.c
+++ b/src/nvim/plines.c
@@ -88,7 +88,7 @@ int linetabsize_eol(win_T *wp, linenr_T lnum)
+ ((wp->w_p_list && wp->w_p_lcs_chars.eol != NUL) ? 1 : 0);
}
-static const uint32_t inline_filter[4] = {[kMTMetaInline] = kMTFilterSelect };
+static const uint32_t inline_filter[kMTMetaCount] = {[kMTMetaInline] = kMTFilterSelect };
/// Prepare the structure passed to charsize functions.
///
@@ -757,6 +757,10 @@ int plines_win(win_T *wp, linenr_T lnum, bool limit_winheight)
/// @param limit_winheight when true limit to window height
int plines_win_nofill(win_T *wp, linenr_T lnum, bool limit_winheight)
{
+ if (decor_conceal_line(wp, lnum - 1, false)) {
+ return 0;
+ }
+
if (!wp->w_p_wrap) {
return 1;
}
@@ -893,8 +897,14 @@ int plines_win_full(win_T *wp, linenr_T lnum, linenr_T *const nextp, bool *const
if (foldedp != NULL) {
*foldedp = folded;
}
- return ((folded ? 1 : plines_win_nofill(wp, lnum, limit_winheight)) +
- (lnum == wp->w_topline ? wp->w_topfill : win_get_fill(wp, lnum)));
+
+ int filler_lines = lnum == wp->w_topline ? wp->w_topfill : win_get_fill(wp, lnum);
+
+ if (decor_conceal_line(wp, lnum - 1, false)) {
+ return filler_lines;
+ }
+
+ return (folded ? 1 : plines_win_nofill(wp, lnum, limit_winheight)) + filler_lines;
}
/// Return number of window lines a physical line range will occupy in window "wp".
@@ -971,8 +981,8 @@ int64_t win_text_height(win_T *const wp, const linenr_T start_lnum, const int64_
if (start_vcol >= 0) {
linenr_T lnum_next = lnum;
- const bool folded = hasFolding(wp, lnum, &lnum, &lnum_next);
- height_cur_nofill = folded ? 1 : plines_win_nofill(wp, lnum, false);
+ hasFolding(wp, lnum, &lnum, &lnum_next);
+ height_cur_nofill = plines_win_nofill(wp, lnum, false);
height_sum_nofill += height_cur_nofill;
const int64_t row_off = (start_vcol < width1 || width2 <= 0)
? 0
@@ -983,9 +993,9 @@ int64_t win_text_height(win_T *const wp, const linenr_T start_lnum, const int64_
while (lnum <= end_lnum) {
linenr_T lnum_next = lnum;
- const bool folded = hasFolding(wp, lnum, &lnum, &lnum_next);
+ hasFolding(wp, lnum, &lnum, &lnum_next);
height_sum_fill += win_get_fill(wp, lnum);
- height_cur_nofill = folded ? 1 : plines_win_nofill(wp, lnum, false);
+ height_cur_nofill = plines_win_nofill(wp, lnum, false);
height_sum_nofill += height_cur_nofill;
lnum = lnum_next + 1;
}
diff --git a/src/nvim/window.c b/src/nvim/window.c
index e8a357a708..14b775db63 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -6436,9 +6436,9 @@ void win_fix_scroll(bool resize)
// Add difference in height and row to botline.
if (diff > 0) {
- cursor_down_inner(wp, diff);
+ cursor_down_inner(wp, diff, false);
} else {
- cursor_up_inner(wp, -diff);
+ cursor_up_inner(wp, -diff, false);
}
// Scroll to put the new cursor position at the bottom of the
@@ -6485,11 +6485,11 @@ static void win_fix_cursor(bool normal)
linenr_T lnum = wp->w_cursor.lnum;
wp->w_cursor.lnum = wp->w_topline;
- cursor_down_inner(wp, so);
+ cursor_down_inner(wp, so, false);
linenr_T top = wp->w_cursor.lnum;
wp->w_cursor.lnum = wp->w_botline - 1;
- cursor_up_inner(wp, so);
+ cursor_up_inner(wp, so, false);
linenr_T bot = wp->w_cursor.lnum;
wp->w_cursor.lnum = lnum;
@@ -6583,7 +6583,7 @@ void scroll_to_fraction(win_T *wp, int prev_height)
hasFolding(wp, lnum, &lnum, NULL);
if (lnum == 1) {
// first line in buffer is folded
- line_size = 1;
+ line_size = !decor_conceal_line(wp, lnum - 1, false);
sline--;
break;
}
diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua
index fd392d479d..6f53076f1f 100644
--- a/test/functional/api/extmark_spec.lua
+++ b/test/functional/api/extmark_spec.lua
@@ -1556,6 +1556,7 @@ describe('API/extmarks', function()
it('can get details', function()
set_extmark(ns, marks[1], 0, 0, {
conceal = 'c',
+ conceal_lines = '',
cursorline_hl_group = 'Statement',
end_col = 0,
end_right_gravity = true,
@@ -1585,6 +1586,7 @@ describe('API/extmarks', function()
0,
{
conceal = 'c',
+ conceal_lines = '',
cursorline_hl_group = 'Statement',
end_col = 0,
end_right_gravity = true,
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index 99e6b5f78f..1d0021363b 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -2625,6 +2625,227 @@ describe('extmark decorations', function()
|
]])
end)
+
+ it ('conceal_lines', function()
+ insert(example_text)
+ exec('set number conceallevel=3')
+ feed('ggj')
+ local not_concealed = {
+ grid = [[
+ {2: 1 }for _,item in ipairs(items) do |
+ {2: 2 }^ local text, hl_id_cell, count = unpack(ite|
+ {2: }m) |
+ {2: 3 } if hl_id_cell ~= nil then |
+ {2: 4 } hl_id = hl_id_cell |
+ {2: 5 } end |
+ {2: 6 } for _ = 1, (count or 1) do |
+ {2: 7 } local cell = line[colpos] |
+ {2: 8 } cell.text = text |
+ {2: 9 } cell.hl_id = hl_id |
+ {2: 10 } colpos = colpos+1 |
+ {2: 11 } end |
+ {2: 12 }end |
+ {1:~ }|
+ |
+ ]]
+ }
+ screen:expect(not_concealed)
+ api.nvim_buf_set_extmark(0, ns, 1, 0, { conceal_lines = "" })
+ screen:expect_unchanged()
+ feed('j')
+ local concealed = {
+ grid = [[
+ {2: 1 }for _,item in ipairs(items) do |
+ {2: 3 }^ if hl_id_cell ~= nil then |
+ {2: 4 } hl_id = hl_id_cell |
+ {2: 5 } end |
+ {2: 6 } for _ = 1, (count or 1) do |
+ {2: 7 } local cell = line[colpos] |
+ {2: 8 } cell.text = text |
+ {2: 9 } cell.hl_id = hl_id |
+ {2: 10 } colpos = colpos+1 |
+ {2: 11 } end |
+ {2: 12 }end |
+ {1:~ }|*3
+ |
+ ]]
+ }
+ screen:expect(concealed)
+ feed('k')
+ screen:expect(not_concealed)
+ exec('set concealcursor=n')
+ screen:expect(concealed)
+ api.nvim_buf_set_extmark(0, ns, 3, 0, { conceal_lines = "" })
+ screen:expect({
+ grid = [[
+ {2: 1 }for _,item in ipairs(items) do |
+ {2: 3 }^ if hl_id_cell ~= nil then |
+ {2: 5 } end |
+ {2: 6 } for _ = 1, (count or 1) do |
+ {2: 7 } local cell = line[colpos] |
+ {2: 8 } cell.text = text |
+ {2: 9 } cell.hl_id = hl_id |
+ {2: 10 } colpos = colpos+1 |
+ {2: 11 } end |
+ {2: 12 }end |
+ {1:~ }|*4
+ |
+ ]]
+ })
+ feed('kjj')
+ screen:expect_unchanged()
+ api.nvim_buf_set_extmark(0, ns, 4, 0, { conceal_lines = "" })
+ feed('kjjjC')
+ screen:expect({
+ grid = [[
+ {2: 1 }for _,item in ipairs(items) do |
+ {2: 3 } if hl_id_cell ~= nil then |
+ {2: 5 }^ |
+ {2: 6 } for _ = 1, (count or 1) do |
+ {2: 7 } local cell = line[colpos] |
+ {2: 8 } cell.text = text |
+ {2: 9 } cell.hl_id = hl_id |
+ {2: 10 } colpos = colpos+1 |
+ {2: 11 } end |
+ {2: 12 }end |
+ {1:~ }|*4
+ {24:-- INSERT --} |
+ ]]
+ })
+ feed('<esc>')
+ screen:expect({
+ grid = [[
+ {2: 1 }for _,item in ipairs(items) do |
+ {2: 3 } if hl_id_cell ~= nil then |
+ {2: 6 }^ for _ = 1, (count or 1) do |
+ {2: 7 } local cell = line[colpos] |
+ {2: 8 } cell.text = text |
+ {2: 9 } cell.hl_id = hl_id |
+ {2: 10 } colpos = colpos+1 |
+ {2: 11 } end |
+ {2: 12 }end |
+ {1:~ }|*5
+ |
+ ]]
+ })
+ feed('kji')
+ screen:expect({
+ grid = [[
+ {2: 1 }for _,item in ipairs(items) do |
+ {2: 3 } if hl_id_cell ~= nil then |
+ {2: 5 }^ |
+ {2: 6 } for _ = 1, (count or 1) do |
+ {2: 7 } local cell = line[colpos] |
+ {2: 8 } cell.text = text |
+ {2: 9 } cell.hl_id = hl_id |
+ {2: 10 } colpos = colpos+1 |
+ {2: 11 } end |
+ {2: 12 }end |
+ {1:~ }|*4
+ {24:-- INSERT --} |
+ ]]
+ })
+ feed('conceal text')
+ screen:expect({
+ grid = [[
+ {2: 1 }for _,item in ipairs(items) do |
+ {2: 3 } if hl_id_cell ~= nil then |
+ {2: 5 }conceal text^ |
+ {2: 6 } for _ = 1, (count or 1) do |
+ {2: 7 } local cell = line[colpos] |
+ {2: 8 } cell.text = text |
+ {2: 9 } cell.hl_id = hl_id |
+ {2: 10 } colpos = colpos+1 |
+ {2: 11 } end |
+ {2: 12 }end |
+ {1:~ }|*4
+ {24:-- INSERT --} |
+ ]]
+ })
+ feed('<esc>')
+ screen:expect({
+ grid = [[
+ {2: 1 }for _,item in ipairs(items) do |
+ {2: 3 } if hl_id_cell ~= nil then |
+ {2: 6 } for _ =^ 1, (count or 1) do |
+ {2: 7 } local cell = line[colpos] |
+ {2: 8 } cell.text = text |
+ {2: 9 } cell.hl_id = hl_id |
+ {2: 10 } colpos = colpos+1 |
+ {2: 11 } end |
+ {2: 12 }end |
+ {1:~ }|*5
+ |
+ ]]
+ })
+ feed('ggzfj')
+ screen:expect({
+ grid = [[
+ {2: 1 }{33:^+-- 2 lines: for _,item in ipairs(items) do··}|
+ {2: 3 } if hl_id_cell ~= nil then |
+ {2: 6 } for _ = 1, (count or 1) do |
+ {2: 7 } local cell = line[colpos] |
+ {2: 8 } cell.text = text |
+ {2: 9 } cell.hl_id = hl_id |
+ {2: 10 } colpos = colpos+1 |
+ {2: 11 } end |
+ {2: 12 }end |
+ {1:~ }|*5
+ |
+ ]]
+ })
+ feed('j')
+ screen:expect({
+ grid = [[
+ {2: 1 }{33:+-- 2 lines: for _,item in ipairs(items) do··}|
+ {2: 3 }^ if hl_id_cell ~= nil then |
+ {2: 6 } for _ = 1, (count or 1) do |
+ {2: 7 } local cell = line[colpos] |
+ {2: 8 } cell.text = text |
+ {2: 9 } cell.hl_id = hl_id |
+ {2: 10 } colpos = colpos+1 |
+ {2: 11 } end |
+ {2: 12 }end |
+ {1:~ }|*5
+ |
+ ]]
+ })
+ feed('ggzdjzfj')
+ screen:expect({
+ grid = [[
+ {2: 1 }for _,item in ipairs(items) do |
+ {2: 6 }^ for _ = 1, (count or 1) do |
+ {2: 7 } local cell = line[colpos] |
+ {2: 8 } cell.text = text |
+ {2: 9 } cell.hl_id = hl_id |
+ {2: 10 } colpos = colpos+1 |
+ {2: 11 } end |
+ {2: 12 }end |
+ {1:~ }|*6
+ |
+ ]]
+ })
+ feed('jj')
+ screen:expect_unchanged()
+ -- Below virtual line belonging to line above concealed line is drawn.
+ api.nvim_buf_set_extmark(0, ns, 0, 0, { virt_lines = { { { 'line 1 below' } } } })
+ -- Above virtual line belonging to concealed line isn't.
+ api.nvim_buf_set_extmark(0, ns, 1, 0, {
+ virt_lines = { { { 'line 2 above' } } },
+ virt_lines_above = true
+ })
+ screen:expect([[
+ {2: 1 }for _,item in ipairs(items) do |
+ {2: }line 1 below |
+ {2: 6 }^ for _ = 1, (count or 1) do |
+ {2: 9 } cell.hl_id = hl_id |
+ {2: 10 } colpos = colpos+1 |
+ {2: 11 } end |
+ {2: 12 }end |
+ {1:~ }|*7
+ |
+ ]])
+ end)
end)
describe('decorations: inline virtual text', function()