aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbfredl <bjorn.linse@gmail.com>2023-12-22 11:49:06 +0100
committerGitHub <noreply@github.com>2023-12-22 11:49:06 +0100
commitd9d9d94343cc3ee2c9bcda37e987d63e84928d79 (patch)
treed07465d7fbd6c758fa3f393edf55a6b0a1a45f3a
parent92204b06e7365cf4c68e6ea8258dce801f0a5df9 (diff)
parent66ac327db27c8097cfa6c1f136dca96151b074f4 (diff)
downloadrneovim-d9d9d94343cc3ee2c9bcda37e987d63e84928d79.tar.gz
rneovim-d9d9d94343cc3ee2c9bcda37e987d63e84928d79.tar.bz2
rneovim-d9d9d94343cc3ee2c9bcda37e987d63e84928d79.zip
Merge pull request #26528 from bfredl/nodrawstate
refactor(drawline): remove LineDrawState and wlv->saved_n_extra
-rw-r--r--src/nvim/api/extmark.c7
-rw-r--r--src/nvim/decoration.c36
-rw-r--r--src/nvim/decoration_defs.h8
-rw-r--r--src/nvim/drawline.c697
-rw-r--r--src/nvim/sign.c68
-rw-r--r--src/nvim/sign_defs.h7
-rw-r--r--src/nvim/statusline.c22
-rw-r--r--src/nvim/types_defs.h2
-rw-r--r--test/functional/ui/sign_spec.lua10
9 files changed, 393 insertions, 464 deletions
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index ec47d7227e..fa3c5afcc6 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -665,9 +665,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
}
if (HAS_KEY(opts, set_extmark, sign_text)) {
- sign.text.ptr = NULL;
- VALIDATE_S(init_sign_text(NULL, &sign.text.ptr, opts->sign_text.data),
- "sign_text", "", {
+ sign.text[0] = 0;
+ VALIDATE_S(init_sign_text(NULL, sign.text, opts->sign_text.data), "sign_text", "", {
goto error;
});
sign.flags |= kSHIsSign;
@@ -785,7 +784,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
uint32_t decor_indexed = DECOR_ID_INVALID;
if (sign.flags & kSHIsSign) {
decor_indexed = decor_put_sh(sign);
- if (sign.text.ptr != NULL) {
+ if (sign.text[0]) {
decor_flags |= MT_FLAG_DECOR_SIGNTEXT;
}
if (sign.number_hl_id || sign.line_hl_id || sign.cursorline_hl_id) {
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index ad2482c2e4..6f1416711a 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -5,6 +5,7 @@
#include "nvim/api/extmark.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/ascii_defs.h"
#include "nvim/buffer_defs.h"
#include "nvim/decoration.h"
#include "nvim/drawscreen.h"
@@ -19,6 +20,7 @@
#include "nvim/move.h"
#include "nvim/option_vars.h"
#include "nvim/pos_defs.h"
+#include "nvim/sign.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "decoration.c.generated.h"
@@ -158,7 +160,7 @@ DecorSignHighlight decor_sh_from_inline(DecorHighlightInline item)
DecorSignHighlight conv = {
.flags = item.flags,
.priority = item.priority,
- .text.sc[0] = item.conceal_char,
+ .text[0] = item.conceal_char,
.hl_id = item.hl_id,
.number_hl_id = 0,
.line_hl_id = 0,
@@ -207,7 +209,7 @@ void buf_put_decor_sh(buf_T *buf, DecorSignHighlight *sh, int row1, int row2)
if (sh->flags & kSHIsSign) {
sh->sign_add_id = sign_add_id++;
buf->b_signs++;
- if (sh->text.ptr) {
+ if (sh->text[0]) {
buf->b_signs_with_text++;
buf_signcols_invalidate_range(buf, row1, row2, 1);
}
@@ -253,7 +255,7 @@ void buf_remove_decor_sh(buf_T *buf, int row1, int row2, DecorSignHighlight *sh)
if (sh->flags & kSHIsSign) {
assert(buf->b_signs > 0);
buf->b_signs--;
- if (sh->text.ptr) {
+ if (sh->text[0]) {
assert(buf->b_signs_with_text > 0);
buf->b_signs_with_text--;
if (row2 >= row1) {
@@ -312,9 +314,6 @@ void decor_free_inner(DecorVirtText *vt, uint32_t first_idx)
while (idx != DECOR_ID_INVALID) {
DecorSignHighlight *sh = &kv_A(decor_items, idx);
if (sh->flags & kSHIsSign) {
- xfree(sh->text.ptr);
- }
- if (sh->flags & kSHIsSign) {
xfree(sh->sign_name);
}
sh->flags = 0;
@@ -362,8 +361,11 @@ void decor_check_invalid_glyphs(void)
{
for (size_t i = 0; i < kv_size(decor_items); i++) {
DecorSignHighlight *it = &kv_A(decor_items, i);
- if ((it->flags & kSHConceal) && schar_high(it->text.sc[0])) {
- it->text.sc[0] = schar_from_char(schar_get_first_codepoint(it->text.sc[0]));
+ int width = (it->flags & kSHIsSign) ? SIGN_WIDTH : ((it->flags & kSHConceal) ? 1 : 0);
+ for (int j = 0; j < width; j++) {
+ if (schar_high(it->text[j])) {
+ it->text[j] = schar_from_char(schar_get_first_codepoint(it->text[j]));
+ }
}
}
}
@@ -661,7 +663,7 @@ next_mark:
if (item.start_row == state->row && item.start_col == col) {
DecorSignHighlight *sh = &item.data.sh;
conceal = 2;
- conceal_char = sh->text.sc[0];
+ conceal_char = sh->text[0];
state->col_until = MIN(state->col_until, item.start_col);
conceal_attr = item.attr_id;
}
@@ -729,7 +731,7 @@ void decor_redraw_signs(win_T *wp, buf_T *buf, int row, SignTextAttrs sattrs[],
while (marktree_itr_step_overlap(buf->b_marktree, itr, &pair)) {
if (!mt_invalid(pair.start) && mt_decor_sign(pair.start)) {
DecorSignHighlight *sh = decor_find_sign(mt_decor(pair.start));
- num_text += (sh->text.ptr != NULL);
+ num_text += (sh->text[0] != NUL);
kv_push(signs, ((SignItem){ sh, pair.start.id }));
}
}
@@ -741,7 +743,7 @@ void decor_redraw_signs(win_T *wp, buf_T *buf, int row, SignTextAttrs sattrs[],
}
if (!mt_end(mark) && !mt_invalid(mark) && mt_decor_sign(mark)) {
DecorSignHighlight *sh = decor_find_sign(mt_decor(mark));
- num_text += (sh->text.ptr != NULL);
+ num_text += (sh->text[0] != NUL);
kv_push(signs, ((SignItem){ sh, mark.id }));
}
@@ -755,8 +757,8 @@ void decor_redraw_signs(win_T *wp, buf_T *buf, int row, SignTextAttrs sattrs[],
for (size_t i = 0; i < kv_size(signs); i++) {
DecorSignHighlight *sh = kv_A(signs, i).sh;
- if (idx >= 0 && sh->text.ptr) {
- sattrs[idx].text = sh->text.ptr;
+ if (idx >= 0 && sh->text[0]) {
+ memcpy(sattrs[idx].text, sh->text, SIGN_WIDTH * sizeof(sattr_T));
sattrs[idx--].hl_id = sh->hl_id;
}
if (*num_id == 0) {
@@ -1033,7 +1035,7 @@ void decor_to_dict_legacy(Dictionary *dict, DecorInline decor, bool hl_name)
PUT(*dict, "hl_eol", BOOLEAN_OBJ(sh_hl.flags & kSHHlEol));
if (sh_hl.flags & kSHConceal) {
char buf[MAX_SCHAR_SIZE];
- schar_get(buf, sh_hl.text.sc[0]);
+ schar_get(buf, sh_hl.text[0]);
PUT(*dict, "conceal", CSTR_TO_OBJ(buf));
}
@@ -1081,8 +1083,10 @@ void decor_to_dict_legacy(Dictionary *dict, DecorInline decor, bool hl_name)
}
if (sh_sign.flags & kSHIsSign) {
- if (sh_sign.text.ptr) {
- PUT(*dict, "sign_text", CSTR_TO_OBJ(sh_sign.text.ptr));
+ if (sh_sign.text[0]) {
+ char buf[SIGN_WIDTH * MAX_SCHAR_SIZE];
+ describe_sign_text(buf, sh_sign.text);
+ PUT(*dict, "sign_text", CSTR_TO_OBJ(buf));
}
if (sh_sign.sign_name) {
diff --git a/src/nvim/decoration_defs.h b/src/nvim/decoration_defs.h
index f272753f46..4550ae0a42 100644
--- a/src/nvim/decoration_defs.h
+++ b/src/nvim/decoration_defs.h
@@ -59,11 +59,7 @@ typedef struct {
uint16_t flags;
DecorPriority priority;
int hl_id; // if sign: highlight of sign text
- // TODO(bfredl): Later signs should use sc[2] as well.
- union {
- char *ptr; // sign
- schar_T sc[2]; // conceal text (only sc[0] used)
- } text;
+ schar_T text[SIGN_WIDTH]; // conceal text only uses text[0]
// NOTE: if more functionality is added to a Highlight these should be overloaded
// or restructured
char *sign_name;
@@ -74,7 +70,7 @@ typedef struct {
uint32_t next;
} DecorSignHighlight;
-#define DECOR_SIGN_HIGHLIGHT_INIT { 0, DECOR_PRIORITY_BASE, 0, { .ptr = NULL }, NULL, 0, 0, 0, 0, \
+#define DECOR_SIGN_HIGHLIGHT_INIT { 0, DECOR_PRIORITY_BASE, 0, { 0, 0 }, NULL, 0, 0, 0, 0, \
DECOR_ID_INVALID }
enum {
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 4f62fda2c5..29221e64f9 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -52,23 +52,8 @@
#define MB_FILLER_CHAR '<' // character used when a double-width character doesn't fit.
-/// possible draw states in win_line(), drawn in sequence.
-typedef enum {
- WL_START = 0, // nothing done yet
- WL_CMDLINE, // cmdline window column
- WL_FOLD, // 'foldcolumn'
- WL_SIGN, // column for signs
- WL_NR, // line number
- WL_STC, // 'statuscolumn'
- WL_BRI, // 'breakindent'
- WL_SBR, // 'showbreak' or 'diff'
- WL_LINE, // text in the line
-} LineDrawState;
-
/// structure with variables passed between win_line() and other functions
typedef struct {
- LineDrawState draw_state; ///< what to draw next
-
linenr_T lnum; ///< line number to be drawn
foldinfo_T foldinfo; ///< fold info for this line
@@ -102,18 +87,8 @@ typedef struct {
int c_extra; ///< extra chars, all the same
int c_final; ///< final char, mandatory if set
- int n_closing; ///< number of chars in fdc which will be closing
-
bool extra_for_extmark; ///< n_extra set for inline virtual text
- // saved "extra" items for when draw_state becomes WL_LINE (again)
- int saved_n_extra;
- char *saved_p_extra;
- bool saved_extra_for_extmark;
- int saved_c_extra;
- int saved_c_final;
- int saved_char_attr;
-
char extra[57]; ///< sign, line number and 'fdc' must fit in here
hlf_T diff_hlf; ///< type of diff highlighting
@@ -136,6 +111,8 @@ typedef struct {
///< or w_skipcol or concealing
int skipped_cells; ///< nr of skipped cells for virtual text
///< to be added to wlv.vcol later
+
+ int *color_cols; ///< if not NULL, highlight colorcolumn using according columns array
} winlinevars_T;
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -163,15 +140,17 @@ void drawline_free_all_mem(void)
}
#endif
-/// Advance **color_cols
-///
-/// @return true when there are columns to draw.
-static bool advance_color_col(int vcol, int **color_cols)
+/// Advance wlv->color_cols if not NULL
+static void advance_color_col(winlinevars_T *wlv, int vcol)
{
- while (**color_cols >= 0 && vcol > **color_cols) {
- (*color_cols)++;
+ if (wlv->color_cols) {
+ while (*wlv->color_cols >= 0 && vcol > *wlv->color_cols) {
+ wlv->color_cols++;
+ }
+ if (*wlv->color_cols < 0) {
+ wlv->color_cols = NULL;
+ }
}
- return **color_cols >= 0;
}
/// Used when 'cursorlineopt' contains "screenline": compute the margins between
@@ -360,6 +339,38 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode,
return col;
}
+// TODO(bfredl): integrate with grid.c linebuf code? madness?
+static void draw_col_buf(win_T *wp, winlinevars_T *wlv, const char *text, size_t len, int attr,
+ bool vcol)
+{
+ const char *ptr = text;
+ while (ptr < text + len && wlv->off < wp->w_grid.cols) {
+ int cells = line_putchar(wp->w_buffer, &ptr, &linebuf_char[wlv->off],
+ wp->w_grid.cols - wlv->off, wlv->off);
+ int myattr = attr;
+ if (vcol) {
+ advance_color_col(wlv, wlv->vcol);
+ if (wlv->color_cols && wlv->vcol == *wlv->color_cols) {
+ myattr = hl_combine_attr(win_hl_attr(wp, HLF_MC), myattr);
+ }
+ }
+ for (int c = 0; c < cells; c++) {
+ linebuf_attr[wlv->off] = myattr;
+ linebuf_vcol[wlv->off] = vcol ? wlv->vcol++ : -1;
+ wlv->off++;
+ }
+ }
+}
+
+static void draw_col_fill(winlinevars_T *wlv, schar_T fillchar, int width, int attr)
+{
+ for (int i = 0; i < width; i++) {
+ linebuf_char[wlv->off] = fillchar;
+ linebuf_attr[wlv->off] = attr;
+ wlv->off++;
+ }
+}
+
/// Return true if CursorLineSign highlight is to be used.
static bool use_cursor_line_highlight(win_T *wp, linenr_T lnum)
{
@@ -368,28 +379,18 @@ static bool use_cursor_line_highlight(win_T *wp, linenr_T lnum)
&& (wp->w_p_culopt_flags & CULOPT_NBR);
}
-static char fdc_buf[MB_MAXCHAR * 10 + 1];
-
/// Setup for drawing the 'foldcolumn', if there is one.
-static void handle_foldcolumn(win_T *wp, winlinevars_T *wlv)
+static void draw_foldcolumn(win_T *wp, winlinevars_T *wlv)
{
int fdc = compute_foldcolumn(wp, 0);
if (fdc <= 0) {
return;
}
- // Use a separate buffer as `extra_buf` might be in use.
- wlv->n_extra = (int)fill_foldcolumn(fdc_buf, wp, wlv->foldinfo, wlv->lnum,
- &wlv->n_closing);
- fdc_buf[wlv->n_extra] = NUL;
- wlv->p_extra = fdc_buf;
- wlv->c_extra = NUL;
- wlv->c_final = NUL;
- if (use_cursor_line_highlight(wp, wlv->lnum)) {
- wlv->char_attr = win_hl_attr(wp, HLF_CLF);
- } else {
- wlv->char_attr = win_hl_attr(wp, HLF_FC);
- }
+ int attr = use_cursor_line_highlight(wp, wlv->lnum)
+ ? win_hl_attr(wp, HLF_CLF) : win_hl_attr(wp, HLF_FC);
+
+ fill_foldcolumn(wlv, wp, wlv->foldinfo, wlv->lnum, attr, NULL);
}
/// Fills the foldcolumn at "p" for window "wp".
@@ -401,29 +402,25 @@ static void handle_foldcolumn(win_T *wp, winlinevars_T *wlv)
///
/// Assume monocell characters
/// @return number of chars added to \param p
-size_t fill_foldcolumn(char *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum, int *n_closing)
+size_t fill_foldcolumn(void *maybe_wlv, win_T *wp, foldinfo_T foldinfo, linenr_T lnum, int attr,
+ schar_T *out_buffer)
{
int i = 0;
int fdc = compute_foldcolumn(wp, 0); // available cell width
- size_t char_counter = 0;
- int symbol = 0;
- int len = 0;
+ int char_counter = 0;
bool closed = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0;
- // Init to all spaces.
- memset(p, ' ', MB_MAXCHAR * (size_t)fdc + 1);
int level = foldinfo.fi_level;
+ winlinevars_T *wlv = maybe_wlv; // TODO(bfredl): this is bullshit
+
// If the column is too narrow, we start at the lowest level that
// fits and use numbers to indicate the depth.
- int first_level = level - fdc - closed + 1;
- if (first_level < 1) {
- first_level = 1;
- }
+ int first_level = MAX(level - fdc - closed + 1, 1);
for (i = 0; i < MIN(fdc, level); i++) {
- if (foldinfo.fi_lnum == lnum
- && first_level + i >= foldinfo.fi_low_level) {
+ int symbol = 0;
+ if (foldinfo.fi_lnum == lnum && first_level + i >= foldinfo.fi_low_level) {
symbol = wp->w_p_fcs_chars.foldopen;
} else if (first_level == 1) {
symbol = wp->w_p_fcs_chars.foldsep;
@@ -433,64 +430,74 @@ size_t fill_foldcolumn(char *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum, i
symbol = '>';
}
- len = utf_char2bytes(symbol, &p[char_counter]);
- char_counter += (size_t)len;
+ if (out_buffer) {
+ out_buffer[char_counter++] = schar_from_char(symbol);
+ } else {
+ linebuf_vcol[wlv->off] = -3;
+ linebuf_attr[wlv->off] = attr;
+ linebuf_char[wlv->off++] = schar_from_char(symbol);
+ char_counter++;
+ }
+
if (first_level + i >= level) {
i++;
break;
}
}
- int n_closing_val = i;
-
if (closed) {
- if (symbol != 0) {
+ if (char_counter > 0) {
// rollback previous write
- char_counter -= (size_t)len;
- memset(&p[char_counter], ' ', (size_t)len);
- n_closing_val--;
+ char_counter--;
+ if (!out_buffer) {
+ wlv->off--;
+ }
+ }
+ if (out_buffer) {
+ out_buffer[char_counter++] = schar_from_char(wp->w_p_fcs_chars.foldclosed);
+ } else {
+ linebuf_vcol[wlv->off] = -2;
+ linebuf_attr[wlv->off] = attr;
+ linebuf_char[wlv->off++] = schar_from_char(wp->w_p_fcs_chars.foldclosed);
+ char_counter++;
}
- len = utf_char2bytes(wp->w_p_fcs_chars.foldclosed, &p[char_counter]);
- char_counter += (size_t)len;
}
- if (n_closing) {
- *n_closing = n_closing_val;
+ int width = MAX(char_counter + (fdc - i), fdc);
+ if (char_counter < width) {
+ if (out_buffer) {
+ while (char_counter < width) {
+ out_buffer[char_counter++] = schar_from_ascii(' ');
+ }
+ } else {
+ draw_col_fill(wlv, schar_from_ascii(' '), width - char_counter, attr);
+ }
}
-
- return MAX(char_counter + (size_t)(fdc - i), (size_t)fdc);
+ return (size_t)width;
}
/// Get information needed to display the sign in line "wlv->lnum" in window "wp".
/// If "nrcol" is true, the sign is going to be displayed in the number column.
/// Otherwise the sign is going to be displayed in the sign column. If there is no
/// sign, draw blank cells instead.
-static void get_sign_display_info(bool nrcol, win_T *wp, winlinevars_T *wlv, int sign_idx,
- int sign_cul_attr)
+static void draw_sign(bool nrcol, win_T *wp, winlinevars_T *wlv, int sign_idx, int sign_cul_attr)
{
SignTextAttrs sattr = wlv->sattrs[sign_idx];
wlv->c_final = NUL;
- if (sattr.text && wlv->row == wlv->startrow + wlv->filler_lines && wlv->filler_todo <= 0) {
- size_t fill = nrcol ? (size_t)number_width(wp) - SIGN_WIDTH : 0;
- size_t sign_len = strlen(sattr.text);
-
- // Spaces + sign: " " + ">>" + ' '
- wlv->n_extra = (int)(fill + sign_len + nrcol);
- if (nrcol) {
- memset(wlv->extra, ' ', (size_t)wlv->n_extra);
- }
- memcpy(wlv->extra + fill, sattr.text, sign_len);
- wlv->p_extra = wlv->extra;
- wlv->c_extra = NUL;
- wlv->char_attr = (use_cursor_line_highlight(wp, wlv->lnum) && sign_cul_attr)
- ? sign_cul_attr : sattr.hl_id ? syn_id2attr(sattr.hl_id) : 0;
+ if (sattr.text[0] && wlv->row == wlv->startrow + wlv->filler_lines && wlv->filler_todo <= 0) {
+ int attr = (use_cursor_line_highlight(wp, wlv->lnum) && sign_cul_attr)
+ ? sign_cul_attr : sattr.hl_id ? syn_id2attr(sattr.hl_id) : 0;
+ int fill = nrcol ? number_width(wp) + 1 : SIGN_WIDTH;
+ draw_col_fill(wlv, schar_from_ascii(' '), fill, attr);
+ int sign_pos = wlv->off - SIGN_WIDTH - (int)nrcol;
+ linebuf_char[sign_pos] = sattr.text[0];
+ linebuf_char[sign_pos + 1] = sattr.text[1];
} else {
+ assert(!nrcol); // handled in draw_lnum_col()
wlv->c_extra = ' ';
- wlv->n_extra = nrcol ? number_width(wp) + 1 : SIGN_WIDTH;
- if (!nrcol) {
- wlv->char_attr = win_hl_attr(wp, use_cursor_line_highlight(wp, wlv->lnum) ? HLF_CLS : HLF_SC);
- }
+ int attr = win_hl_attr(wp, use_cursor_line_highlight(wp, wlv->lnum) ? HLF_CLS : HLF_SC);
+ draw_col_fill(wlv, schar_from_ascii(' '), SIGN_WIDTH, attr);
}
}
@@ -557,7 +564,7 @@ static int get_line_number_attr(win_T *wp, winlinevars_T *wlv)
/// Display the absolute or relative line number. After the first row fill with
/// blanks when the 'n' flag isn't in 'cpo'.
-static void handle_lnum_col(win_T *wp, winlinevars_T *wlv, int sign_num_attr, int sign_cul_attr)
+static void draw_lnum_col(win_T *wp, winlinevars_T *wlv, int sign_num_attr, int sign_cul_attr)
{
bool has_cpo_n = vim_strchr(p_cpo, CPO_NUMCOL) != NULL;
@@ -568,33 +575,30 @@ static void handle_lnum_col(win_T *wp, winlinevars_T *wlv, int sign_num_attr, in
&& !((has_cpo_n && !wp->w_p_bri) && wp->w_skipcol > 0 && wlv->lnum == wp->w_topline)) {
// If 'signcolumn' is set to 'number' and a sign is present in "lnum",
// then display the sign instead of the line number.
- if (wp->w_minscwidth == SCL_NUM && wlv->sattrs[0].text) {
- get_sign_display_info(true, wp, wlv, 0, sign_cul_attr);
+ if (wp->w_minscwidth == SCL_NUM && wlv->sattrs[0].text[0]
+ && wlv->row == wlv->startrow + wlv->filler_lines && wlv->filler_todo <= 0) {
+ draw_sign(true, wp, wlv, 0, sign_cul_attr);
} else {
// Draw the line number (empty space after wrapping).
+ int width = number_width(wp) + 1;
+ int attr = (sign_num_attr > 0 && wlv->filler_todo <= 0)
+ ? sign_num_attr : get_line_number_attr(wp, wlv);
if (wlv->row == wlv->startrow + wlv->filler_lines
&& (wp->w_skipcol == 0 || wlv->row > 0 || (wp->w_p_nu && wp->w_p_rnu))) {
- get_line_number_str(wp, wlv->lnum, wlv->extra, sizeof(wlv->extra));
+ char buf[32];
+ get_line_number_str(wp, wlv->lnum, buf, sizeof(buf));
if (wp->w_skipcol > 0 && wlv->startrow == 0) {
- for (wlv->p_extra = wlv->extra; *wlv->p_extra == ' '; wlv->p_extra++) {
- *wlv->p_extra = '-';
+ for (char *c = buf; *c == ' '; c++) {
+ *c = '-';
}
}
if (wp->w_p_rl) { // reverse line numbers
- char *num = skipwhite(wlv->extra);
+ char *num = skipwhite(buf);
rl_mirror_ascii(num, skiptowhite(num));
}
- wlv->p_extra = wlv->extra;
- wlv->c_extra = NUL;
+ draw_col_buf(wp, wlv, buf, (size_t)width, attr, false);
} else {
- wlv->c_extra = ' ';
- }
- wlv->c_final = NUL;
- wlv->n_extra = number_width(wp) + 1;
- if (sign_num_attr > 0) {
- wlv->char_attr = sign_num_attr;
- } else {
- wlv->char_attr = get_line_number_attr(wp, wlv);
+ draw_col_fill(wlv, schar_from_ascii(' '), width, attr);
}
}
}
@@ -663,124 +667,108 @@ static void get_statuscol_str(win_T *wp, linenr_T lnum, int virtnum, statuscol_T
///
/// @param stcp Status column attributes
/// @param[in,out] wlv
-static void get_statuscol_display_info(statuscol_T *stcp, winlinevars_T *wlv)
+static void draw_statuscol(win_T *wp, statuscol_T *stcp, winlinevars_T *wlv)
{
- wlv->c_extra = NUL;
- wlv->c_final = NUL;
do {
- wlv->draw_state = WL_STC;
- wlv->char_attr = stcp->cur_attr;
- wlv->p_extra = stcp->textp;
- char *const section_end = stcp->hlrecp->start ? stcp->hlrecp->start : stcp->text_end;
- wlv->n_extra = (int)(section_end - stcp->textp);
+ int attr = stcp->cur_attr;
+ char *text = stcp->textp;
+ char *section_end = stcp->hlrecp->start ? stcp->hlrecp->start : stcp->text_end;
+ ptrdiff_t len = section_end - text;
// Prepare for next highlight section if not yet at the end
if (section_end < stcp->text_end) {
int hl = stcp->hlrecp->userhl;
stcp->textp = stcp->hlrecp->start;
stcp->cur_attr = hl < 0 ? syn_id2attr(-hl) : stcp->num_attr;
stcp->hlrecp++;
- wlv->draw_state = WL_STC - 1;
+ } else {
+ stcp->textp = section_end;
}
// Skip over empty highlight sections
- } while (wlv->n_extra == 0 && stcp->textp < stcp->text_end);
- if (wlv->n_extra > 0) {
- static char transbuf[(MAX_NUMBERWIDTH + 9 + 9 * 2) * MB_MAXBYTES + 1];
- wlv->n_extra = (int)transstr_buf(wlv->p_extra, wlv->n_extra, transbuf, sizeof transbuf, true);
- wlv->p_extra = transbuf;
- }
+ if (len) {
+ static char transbuf[(MAX_NUMBERWIDTH + 9 + 9 * SIGN_WIDTH) * MB_MAXBYTES + 1];
+ size_t translen = transstr_buf(text, len, transbuf, sizeof transbuf, true);
+ draw_col_buf(wp, wlv, transbuf, translen, attr, false);
+ }
+ } while (stcp->textp < stcp->text_end);
}
static void handle_breakindent(win_T *wp, winlinevars_T *wlv)
{
- if (wp->w_briopt_sbr && wlv->draw_state == WL_BRI - 1
- && *get_showbreak_value(wp) != NUL) {
- // draw indent after showbreak value
- wlv->draw_state = WL_BRI;
- } else if (wp->w_briopt_sbr && wlv->draw_state == WL_SBR) {
- // after the showbreak, draw the breakindent
- wlv->draw_state = WL_BRI - 1;
- }
-
// draw 'breakindent': indent wrapped text accordingly
- if (wlv->draw_state == WL_BRI - 1 && wlv->n_extra == 0) {
- wlv->draw_state = WL_BRI;
- // if wlv->need_showbreak is set, breakindent also applies
- if (wp->w_p_bri && (wlv->row > wlv->startrow + wlv->filler_lines
- || wlv->need_showbreak)) {
- wlv->char_attr = 0;
- if (wlv->diff_hlf != (hlf_T)0) {
- wlv->char_attr = win_hl_attr(wp, (int)wlv->diff_hlf);
- }
- wlv->p_extra = NULL;
- wlv->c_extra = ' ';
- wlv->c_final = NUL;
- wlv->n_extra = get_breakindent_win(wp, ml_get_buf(wp->w_buffer, wlv->lnum));
- if (wlv->row == wlv->startrow) {
- wlv->n_extra -= win_col_off2(wp);
- if (wlv->n_extra < 0) {
- wlv->n_extra = 0;
- }
- }
- if (wp->w_skipcol > 0 && wlv->startrow == 0 && wp->w_p_wrap && wp->w_briopt_sbr) {
- wlv->need_showbreak = false;
+ // if wlv->need_showbreak is set, breakindent also applies
+ if (wp->w_p_bri && (wlv->row > wlv->startrow + wlv->filler_lines
+ || wlv->need_showbreak)) {
+ int attr = 0;
+ if (wlv->diff_hlf != (hlf_T)0) {
+ attr = win_hl_attr(wp, (int)wlv->diff_hlf);
+ }
+ int num = get_breakindent_win(wp, ml_get_buf(wp->w_buffer, wlv->lnum));
+ if (wlv->row == wlv->startrow) {
+ num -= win_col_off2(wp);
+ if (wlv->n_extra < 0) {
+ num = 0;
}
- // Correct end of highlighted area for 'breakindent',
- // required wen 'linebreak' is also set.
- if (wlv->tocol == wlv->vcol) {
- wlv->tocol += wlv->n_extra;
+ }
+
+ // Correct end of highlighted area for 'breakindent',
+ // required wen 'linebreak' is also set.
+ if (wlv->tocol == wlv->vcol) {
+ wlv->tocol += num;
+ }
+
+ for (int i = 0; i < num; i++) {
+ linebuf_char[wlv->off] = schar_from_ascii(' ');
+
+ advance_color_col(wlv, wlv->vcol);
+ int myattr = attr;
+ if (wlv->color_cols && wlv->vcol == *wlv->color_cols) {
+ myattr = hl_combine_attr(win_hl_attr(wp, HLF_MC), myattr);
}
+ linebuf_attr[wlv->off] = myattr;
+ linebuf_vcol[wlv->off] = wlv->vcol++; // These are vcols, sorry I don't make the rules
+ wlv->off++;
+ }
+
+ if (wp->w_skipcol > 0 && wlv->startrow == 0 && wp->w_p_wrap && wp->w_briopt_sbr) {
+ wlv->need_showbreak = false;
}
}
}
static void handle_showbreak_and_filler(win_T *wp, winlinevars_T *wlv)
{
+ int remaining = wp->w_grid.cols - wlv->off;
if (wlv->filler_todo > wlv->filler_lines - wlv->n_virt_lines) {
// TODO(bfredl): check this doesn't inhibit TUI-style
// clear-to-end-of-line.
- wlv->c_extra = ' ';
- wlv->c_final = NUL;
- wlv->n_extra = wp->w_grid.cols - wlv->col;
- wlv->char_attr = 0;
+ draw_col_fill(wlv, schar_from_ascii(' '), remaining, 0);
} else if (wlv->filler_todo > 0) {
// Draw "deleted" diff line(s)
- if (char2cells(wp->w_p_fcs_chars.diff) > 1) {
- wlv->c_extra = '-';
- wlv->c_final = NUL;
- } else {
- wlv->c_extra = wp->w_p_fcs_chars.diff;
- wlv->c_final = NUL;
- }
- wlv->n_extra = wp->w_grid.cols - wlv->col;
- wlv->char_attr = win_hl_attr(wp, HLF_DED);
+ int c = (char2cells(wp->w_p_fcs_chars.diff) > 1) ? '-' : wp->w_p_fcs_chars.diff;
+ draw_col_fill(wlv, schar_from_char(c), remaining, win_hl_attr(wp, HLF_DED));
}
char *const sbr = get_showbreak_value(wp);
if (*sbr != NUL && wlv->need_showbreak) {
// Draw 'showbreak' at the start of each broken line.
- wlv->p_extra = sbr;
- wlv->c_extra = NUL;
- wlv->c_final = NUL;
- wlv->n_extra = (int)strlen(sbr);
- wlv->char_attr = win_hl_attr(wp, HLF_AT);
if (wp->w_skipcol == 0 || wlv->startrow != 0 || !wp->w_p_wrap) {
wlv->need_showbreak = false;
}
- wlv->vcol_sbr = wlv->vcol + mb_charlen(sbr);
+ // Combine 'showbreak' with 'cursorline', prioritizing 'showbreak'.
+ int attr = hl_combine_attr(wlv->cul_attr, win_hl_attr(wp, HLF_AT));
+ int vcol_before = wlv->vcol;
+ draw_col_buf(wp, wlv, sbr, strlen(sbr), attr, true);
+ wlv->vcol_sbr = wlv->vcol;
// Correct start of highlighted area for 'showbreak'.
- if (wlv->fromcol >= wlv->vcol && wlv->fromcol < wlv->vcol_sbr) {
+ if (wlv->fromcol >= vcol_before && wlv->fromcol < wlv->vcol_sbr) {
wlv->fromcol = wlv->vcol_sbr;
}
// Correct end of highlighted area for 'showbreak',
// required when 'linebreak' is also set.
- if (wlv->tocol == wlv->vcol) {
- wlv->tocol += wlv->n_extra;
- }
- // Combine 'showbreak' with 'cursorline', prioritizing 'showbreak'.
- if (wlv->cul_attr) {
- wlv->char_attr = hl_combine_attr(wlv->cul_attr, wlv->char_attr);
+ if (wlv->tocol == vcol_before) {
+ wlv->tocol = wlv->vcol;
}
}
}
@@ -943,37 +931,7 @@ static void win_line_start(win_T *wp, winlinevars_T *wlv, bool save_extra)
wlv->col = 0;
wlv->off = 0;
wlv->need_lbr = false;
-
- if (save_extra) {
- // reset the drawing state for the start of a wrapped line
- wlv->draw_state = WL_START;
- wlv->saved_n_extra = wlv->n_extra;
- wlv->saved_p_extra = wlv->p_extra;
- wlv->saved_extra_for_extmark = wlv->extra_for_extmark;
- wlv->saved_c_extra = wlv->c_extra;
- wlv->saved_c_final = wlv->c_final;
- wlv->need_lbr = true;
- wlv->saved_char_attr = wlv->char_attr;
-
- wlv->n_extra = 0;
- }
-}
-
-/// Called when wlv->draw_state is set to WL_LINE.
-static void win_line_continue(winlinevars_T *wlv)
-{
- if (wlv->saved_n_extra > 0) {
- // Continue item from end of wrapped line.
- wlv->n_extra = wlv->saved_n_extra;
- wlv->saved_n_extra = 0;
- wlv->c_extra = wlv->saved_c_extra;
- wlv->c_final = wlv->saved_c_final;
- wlv->p_extra = wlv->saved_p_extra;
- wlv->extra_for_extmark = wlv->saved_extra_for_extmark;
- wlv->char_attr = wlv->saved_char_attr;
- } else {
- wlv->char_attr = 0;
- }
+ memset(linebuf_vcol, -1, (size_t)wp->w_grid.cols * sizeof(*linebuf_vcol));
}
/// Display line "lnum" of window "wp" on the screen.
@@ -1025,8 +983,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
int folded_attr = 0; // attributes for folded line
int save_did_emsg;
int eol_hl_off = 0; // 1 if highlighted char after EOL
- bool draw_color_col = false; // highlight colorcolumn
- int *color_cols = NULL; // pointer to according columns array
#define SPWORDLEN 150
char nextline[SPWORDLEN * 2]; // text with start of the next line
int nextlinecol = 0; // column where nextline[] starts
@@ -1079,7 +1035,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
bool is_concealing = false;
bool did_wcol = false;
int old_boguscols = 0;
-#define VCOL_HLC (wlv.vcol - wlv.vcol_off)
+#define vcol_hlc(wlv) ((wlv).vcol - (wlv).vcol_off)
#define FIX_FOR_BOGUSCOLS \
{ \
wlv.n_extra += wlv.vcol_off; \
@@ -1136,10 +1092,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
}
// Check for columns to display for 'colorcolumn'.
- color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols;
- if (color_cols != NULL) {
- draw_color_col = advance_color_col(VCOL_HLC, &color_cols);
- }
+ wlv.color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols;
+ advance_color_col(&wlv, vcol_hlc(wlv));
// handle Visual active in this window
if (VIsual_active && wp->w_buffer == curwin->w_buffer) {
@@ -1453,7 +1407,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// - the visual mode is active,
// the end of the line may be before the start of the displayed part.
if (wlv.vcol < v && (wp->w_p_cuc
- || draw_color_col
+ || wlv.color_cols
|| virtual_active()
|| (VIsual_active && wp->w_buffer == curwin->w_buffer))) {
wlv.vcol = (colnr_T)v;
@@ -1550,6 +1504,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
}
win_line_start(wp, &wlv, false);
+ bool draw_cols = true;
+ int leftcols_width = 0;
// won't highlight after TERM_ATTRS_MAX columns
int term_attrs[TERM_ATTRS_MAX] = { 0 };
@@ -1558,7 +1514,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
extra_check = true;
}
- int sign_idx = 0;
int virt_line_index;
int virt_line_offset = -1;
// Repeat for the whole displayed line.
@@ -1569,119 +1524,85 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
bool did_decrement_ptr = false;
// Skip this quickly when working on the text.
- if (wlv.draw_state != WL_LINE) {
+ if (draw_cols) {
if (cul_screenline) {
wlv.cul_attr = 0;
wlv.line_attr = line_attr_save;
wlv.line_attr_lowprio = line_attr_lowprio_save;
}
- if (wlv.draw_state == WL_CMDLINE - 1 && wlv.n_extra == 0) {
- wlv.draw_state = WL_CMDLINE;
- if (cmdwin_type != 0 && wp == curwin) {
- // Draw the cmdline character.
- wlv.n_extra = 1;
- wlv.c_extra = cmdwin_type;
- wlv.c_final = NUL;
- wlv.char_attr = win_hl_attr(wp, HLF_AT);
- }
- }
-
- if (wlv.draw_state == WL_FOLD - 1 && wlv.n_extra == 0) {
- if (wlv.filler_todo > 0) {
- int index = wlv.filler_todo - (wlv.filler_lines - wlv.n_virt_lines);
- if (index > 0) {
- virt_line_index = (int)kv_size(virt_lines) - index;
- assert(virt_line_index >= 0);
- virt_line_offset = kv_A(virt_lines, virt_line_index).left_col ? 0 : win_col_off(wp);
- }
- }
- if (virt_line_offset == 0) {
- // Skip the column states if there is a "virt_left_col" line.
- wlv.draw_state = WL_BRI - 1;
- } else if (statuscol.draw) {
- // Skip fold, sign and number states if 'statuscolumn' is set.
- wlv.draw_state = WL_STC - 1;
- }
- }
+ assert(wlv.off == 0);
- if (wlv.draw_state == WL_FOLD - 1 && wlv.n_extra == 0) {
- wlv.draw_state = WL_FOLD;
- handle_foldcolumn(wp, &wlv);
+ if (cmdwin_type != 0 && wp == curwin) {
+ // Draw the cmdline character.
+ draw_col_fill(&wlv, schar_from_ascii(cmdwin_type), 1, win_hl_attr(wp, HLF_AT));
}
- // sign column, this is hit until sign_idx reaches count
- if (wlv.draw_state == WL_SIGN - 1 && wlv.n_extra == 0) {
- // Show the sign column when desired.
- wlv.draw_state = WL_SIGN;
- if (wp->w_scwidth > 0) {
- get_sign_display_info(false, wp, &wlv, sign_idx, sign_cul_attr);
- if (++sign_idx < wp->w_scwidth) {
- wlv.draw_state = WL_SIGN - 1;
- } else {
- sign_idx = 0;
- }
+ if (wlv.filler_todo > 0) {
+ int index = wlv.filler_todo - (wlv.filler_lines - wlv.n_virt_lines);
+ if (index > 0) {
+ virt_line_index = (int)kv_size(virt_lines) - index;
+ assert(virt_line_index >= 0);
+ virt_line_offset = kv_A(virt_lines, virt_line_index).left_col ? 0 : win_col_off(wp);
}
}
- if (wlv.draw_state == WL_NR - 1 && wlv.n_extra == 0) {
- // Show the line number, if desired.
- wlv.draw_state = WL_NR;
- handle_lnum_col(wp, &wlv, sign_num_attr, sign_cul_attr);
- }
-
- if (wlv.draw_state == WL_STC - 1 && wlv.n_extra == 0) {
- wlv.draw_state = WL_STC;
- // Draw the 'statuscolumn' if option is set.
- if (statuscol.draw) {
- if (sign_num_attr == 0) {
- statuscol.num_attr = get_line_number_attr(wp, &wlv);
+ if (virt_line_offset == 0) {
+ // skip columns
+ } else if (statuscol.draw) {
+ // Draw 'statuscolumn' if it is set.
+ if (sign_num_attr == 0) {
+ statuscol.num_attr = get_line_number_attr(wp, &wlv);
+ }
+ if (statuscol.textp == NULL) {
+ v = (ptr - line);
+ get_statuscol_str(wp, lnum, wlv.row - startrow - wlv.filler_lines, &statuscol);
+ if (!end_fill) {
+ // Get the line again as evaluating 'statuscolumn' may free it.
+ line = ml_get_buf(wp->w_buffer, lnum);
+ ptr = line + v;
}
- if (statuscol.textp == NULL) {
- v = (ptr - line);
- get_statuscol_str(wp, lnum, wlv.row - startrow - wlv.filler_lines, &statuscol);
- if (!end_fill) {
- // Get the line again as evaluating 'statuscolumn' may free it.
- line = ml_get_buf(wp->w_buffer, lnum);
- ptr = line + v;
- }
- if (wp->w_redr_statuscol) {
- break;
- }
+ if (wp->w_redr_statuscol) {
+ break;
}
- get_statuscol_display_info(&statuscol, &wlv);
}
- }
+ draw_statuscol(wp, &statuscol, &wlv);
+ } else {
+ // draw builtin info columns: fold, sign, number
+ draw_foldcolumn(wp, &wlv);
+
+ // wp->w_scwidth is zero if signcol=number is used
+ for (int sign_idx = 0; sign_idx < wp->w_scwidth; sign_idx++) {
+ draw_sign(false, wp, &wlv, sign_idx, sign_cul_attr);
+ }
- if (wlv.draw_state == WL_STC && wlv.n_extra == 0) {
- win_col_offset = wlv.off;
+ draw_lnum_col(wp, &wlv, sign_num_attr, sign_cul_attr);
}
+ win_col_offset = wlv.off;
+
// Check if 'breakindent' applies and show it.
// May change wlv.draw_state to WL_BRI or WL_BRI - 1.
- if (wlv.n_extra == 0) {
+ if (!wp->w_briopt_sbr) {
handle_breakindent(wp, &wlv);
}
-
- if (wlv.draw_state == WL_SBR - 1 && wlv.n_extra == 0) {
- wlv.draw_state = WL_SBR;
- handle_showbreak_and_filler(wp, &wlv);
+ handle_showbreak_and_filler(wp, &wlv);
+ if (wp->w_briopt_sbr) {
+ handle_breakindent(wp, &wlv);
}
- if (wlv.draw_state == WL_LINE - 1 && wlv.n_extra == 0) {
- sign_idx = 0;
- wlv.draw_state = WL_LINE;
- if (has_decor && wlv.row == startrow + wlv.filler_lines) {
- // hide virt_text on text hidden by 'nowrap' or 'smoothscroll'
- decor_redraw_col(wp, (colnr_T)(ptr - line) - 1, wlv.off, true, &decor_state);
- }
- win_line_continue(&wlv); // use wlv.saved_ values
+ wlv.col = wlv.off;
+ draw_cols = false;
+ if (wlv.filler_todo <= 0) {
+ leftcols_width = wlv.off;
+ }
+ if (has_decor && wlv.row == startrow + wlv.filler_lines) {
+ // hide virt_text on text hidden by 'nowrap' or 'smoothscroll'
+ decor_redraw_col(wp, (colnr_T)(ptr - line) - 1, wlv.off, true, &decor_state);
}
}
- if (cul_screenline && wlv.draw_state == WL_LINE
- && wlv.vcol >= left_curline_col
- && wlv.vcol < right_curline_col) {
+ if (cul_screenline && wlv.vcol >= left_curline_col && wlv.vcol < right_curline_col) {
apply_cursorline_highlight(wp, &wlv);
}
@@ -1690,7 +1611,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
&& wp == curwin
&& lnum == wp->w_cursor.lnum
&& wlv.vcol >= wp->w_virtcol)
- || (number_only && wlv.draw_state > WL_STC))
+ || number_only)
&& wlv.filler_todo <= 0) {
draw_virt_text(wp, buf, win_col_offset, &wlv.col, wlv.row);
// don't clear anything after wlv.col
@@ -1705,15 +1626,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
break;
}
- const bool draw_folded = wlv.draw_state == WL_LINE && has_fold
- && wlv.row == startrow + wlv.filler_lines;
+ const bool draw_folded = has_fold && wlv.row == startrow + wlv.filler_lines;
if (draw_folded && wlv.n_extra == 0) {
wlv.char_attr = folded_attr = win_hl_attr(wp, HLF_FL);
}
int extmark_attr = 0;
- if (wlv.draw_state == WL_LINE
- && (area_highlighting || spv->spv_has_spell || extra_check)) {
+ if (area_highlighting || spv->spv_has_spell || extra_check) {
if (wlv.n_extra == 0 || !wlv.extra_for_extmark) {
wlv.reset_extra_attr = false;
}
@@ -1740,7 +1659,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
decor_recheck_draw_col(wlv.off, selected, &decor_state);
decor_need_recheck = false;
}
- extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, selected, &decor_state);
+ if (wlv.filler_todo <= 0) {
+ extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, selected, &decor_state);
+ }
if (!has_fold && wp->w_buffer->b_virt_text_inline > 0) {
handle_inline_virtual_text(wp, &wlv, v);
@@ -1924,28 +1845,31 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// Only restore search_attr and area_attr after "n_extra" in
// the next screen line is also done.
if (wlv.n_extra <= 0) {
- if (wlv.saved_n_extra <= 0) {
- if (search_attr == 0) {
- search_attr = saved_search_attr;
- saved_search_attr = 0;
- }
- if (area_attr == 0 && *ptr != NUL) {
- area_attr = saved_area_attr;
- saved_area_attr = 0;
- }
- if (decor_attr == 0) {
- decor_attr = saved_decor_attr;
- saved_decor_attr = 0;
- }
+ if (search_attr == 0) {
+ search_attr = saved_search_attr;
+ saved_search_attr = 0;
+ }
+ if (area_attr == 0 && *ptr != NUL) {
+ area_attr = saved_area_attr;
+ saved_area_attr = 0;
+ }
+ if (decor_attr == 0) {
+ decor_attr = saved_decor_attr;
+ saved_decor_attr = 0;
+ }
- if (wlv.extra_for_extmark) {
- // wlv.extra_attr should be used at this position but not
- // any further.
- wlv.reset_extra_attr = true;
- }
+ if (wlv.extra_for_extmark) {
+ // wlv.extra_attr should be used at this position but not
+ // any further.
+ wlv.reset_extra_attr = true;
}
wlv.extra_for_extmark = false;
}
+ } else if (wlv.filler_todo > 0) {
+ // Wait with reading text until filler lines are done. Still need to
+ // initialize these.
+ mb_c = ' ';
+ mb_schar = schar_from_ascii(' ');
} else if (has_fold) {
// skip writing the buffer line itself
mb_c = NUL;
@@ -2519,7 +2443,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// In the cursor line and we may be concealing characters: correct
// the cursor column when we reach its position.
- if (!did_wcol && wlv.draw_state == WL_LINE
+ if (!did_wcol
&& wp == curwin && lnum == wp->w_cursor.lnum
&& conceal_cursor_line(wp)
&& (int)wp->w_virtcol <= wlv.vcol + wlv.skip_cells) {
@@ -2530,7 +2454,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
}
// Don't override visual selection highlighting.
- if (wlv.n_attr > 0 && wlv.draw_state == WL_LINE && !search_attr_from_match) {
+ if (wlv.n_attr > 0 && !search_attr_from_match) {
wlv.char_attr = hl_combine_attr(wlv.char_attr, wlv.extra_attr);
if (wlv.reset_extra_attr) {
wlv.reset_extra_attr = false;
@@ -2547,7 +2471,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
&& wp->w_p_list
&& (wp->w_p_wrap ? (wp->w_skipcol > 0 && wlv.row == 0) : wp->w_leftcol > 0)
&& wlv.filler_todo <= 0
- && wlv.draw_state > WL_STC
&& mb_c != NUL) {
mb_c = wp->w_p_lcs_chars.prec;
lcs_prec_todo = NUL;
@@ -2638,9 +2561,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
wlv.col -= wlv.boguscols;
wlv.boguscols = 0;
- if (draw_color_col) {
- draw_color_col = advance_color_col(VCOL_HLC, &color_cols);
- }
+ advance_color_col(&wlv, vcol_hlc(wlv));
bool has_virttext = false;
// Make sure alignment is the same regardless
@@ -2653,10 +2574,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
}
if (((wp->w_p_cuc
- && wp->w_virtcol >= VCOL_HLC - eol_hl_off
+ && wp->w_virtcol >= vcol_hlc(wlv) - eol_hl_off
&& wp->w_virtcol < grid->cols * (ptrdiff_t)(wlv.row - startrow + 1) + v
&& lnum != wp->w_cursor.lnum)
- || draw_color_col || wlv.line_attr_lowprio || wlv.line_attr
+ || wlv.color_cols || wlv.line_attr_lowprio || wlv.line_attr
|| wlv.diff_hlf != 0 || has_virttext)) {
int rightmost_vcol = 0;
@@ -2664,11 +2585,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
rightmost_vcol = wp->w_virtcol;
}
- if (draw_color_col) {
+ if (wlv.color_cols) {
// determine rightmost colorcolumn to possibly draw
- for (int i = 0; color_cols[i] >= 0; i++) {
- if (rightmost_vcol < color_cols[i]) {
- rightmost_vcol = color_cols[i];
+ for (int i = 0; wlv.color_cols[i] >= 0; i++) {
+ if (rightmost_vcol < wlv.color_cols[i]) {
+ rightmost_vcol = wlv.color_cols[i];
}
}
}
@@ -2693,15 +2614,13 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
linebuf_char[wlv.off] = schar_from_ascii(' ');
linebuf_vcol[wlv.off] = MAXCOL;
wlv.col++;
- if (draw_color_col) {
- draw_color_col = advance_color_col(VCOL_HLC, &color_cols);
- }
+ advance_color_col(&wlv, vcol_hlc(wlv));
int col_attr = base_attr;
- if (wp->w_p_cuc && VCOL_HLC == wp->w_virtcol) {
+ if (wp->w_p_cuc && vcol_hlc(wlv) == wp->w_virtcol) {
col_attr = cuc_attr;
- } else if (draw_color_col && VCOL_HLC == *color_cols) {
+ } else if (wlv.color_cols && vcol_hlc(wlv) == *wlv.color_cols) {
col_attr = hl_combine_attr(wlv.line_attr_lowprio, mc_attr);
}
@@ -2710,7 +2629,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
linebuf_attr[wlv.off] = col_attr;
wlv.off++;
- if (VCOL_HLC >= rightmost_vcol) {
+ if (vcol_hlc(wlv) >= rightmost_vcol) {
break;
}
@@ -2747,13 +2666,20 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW);
conceal_cursor_used = conceal_cursor_line(curwin);
}
+
+ // When the window is too narrow draw all "@" lines.
+ if (leftcols_width >= wp->w_grid.cols && wp->w_p_wrap) {
+ win_draw_end(wp, '@', ' ', true, wlv.row, wp->w_grid.rows, HLF_AT);
+ set_empty_rows(wp, wlv.row);
+ wlv.row = endrow;
+ }
+
break;
}
// Show "extends" character from 'listchars' if beyond the line end and
// 'list' is set.
if (wp->w_p_lcs_chars.ext != NUL
- && wlv.draw_state == WL_LINE
&& wp->w_p_list
&& !wp->w_p_wrap
&& wlv.filler_todo <= 0
@@ -2773,47 +2699,38 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
}
}
- // advance to the next 'colorcolumn'
- if (draw_color_col) {
- draw_color_col = advance_color_col(VCOL_HLC, &color_cols);
- }
+ advance_color_col(&wlv, vcol_hlc(wlv));
// Highlight the cursor column if 'cursorcolumn' is set. But don't
// highlight the cursor position itself.
// Also highlight the 'colorcolumn' if it is different than
// 'cursorcolumn'
- // Also highlight the 'colorcolumn' if 'breakindent' and/or 'showbreak'
- // options are set
vcol_save_attr = -1;
- if ((wlv.draw_state == WL_LINE
- || wlv.draw_state == WL_BRI
- || wlv.draw_state == WL_SBR)
- && !lnum_in_visual_area
+ if (!lnum_in_visual_area
&& search_attr == 0
&& area_attr == 0
&& wlv.filler_todo <= 0) {
- if (wp->w_p_cuc && VCOL_HLC == wp->w_virtcol
+ if (wp->w_p_cuc && vcol_hlc(wlv) == wp->w_virtcol
&& lnum != wp->w_cursor.lnum) {
vcol_save_attr = wlv.char_attr;
wlv.char_attr = hl_combine_attr(win_hl_attr(wp, HLF_CUC), wlv.char_attr);
- } else if (draw_color_col && VCOL_HLC == *color_cols) {
+ } else if (wlv.color_cols && vcol_hlc(wlv) == *wlv.color_cols) {
vcol_save_attr = wlv.char_attr;
wlv.char_attr = hl_combine_attr(win_hl_attr(wp, HLF_MC), wlv.char_attr);
}
}
// Apply lowest-priority line attr now, so everything can override it.
- if (wlv.draw_state == WL_LINE) {
- wlv.char_attr = hl_combine_attr(wlv.line_attr_lowprio, wlv.char_attr);
- }
+ wlv.char_attr = hl_combine_attr(wlv.line_attr_lowprio, wlv.char_attr);
- if (wlv.draw_state == WL_LINE) {
- vcol_prev = wlv.vcol;
- }
+ vcol_prev = wlv.vcol;
// Store character to be displayed.
// Skip characters that are left of the screen for 'nowrap'.
- if (wlv.draw_state < WL_LINE || wlv.skip_cells <= 0) {
+ if (wlv.filler_todo > 0) {
+ // TODO(bfredl): the main render loop should get called also with the virtual
+ // lines chunks, so we get line wrapping and other Nice Things.
+ } else if (wlv.skip_cells <= 0) {
// Store the character.
linebuf_char[wlv.off] = mb_schar;
if (multi_attr) {
@@ -2823,18 +2740,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
linebuf_attr[wlv.off] = wlv.char_attr;
}
- if (wlv.draw_state > WL_STC && wlv.filler_todo <= 0) {
- linebuf_vcol[wlv.off] = wlv.vcol;
- } else if (wlv.draw_state == WL_FOLD) {
- if (wlv.n_closing > 0) {
- linebuf_vcol[wlv.off] = -3;
- wlv.n_closing--;
- } else {
- linebuf_vcol[wlv.off] = -2;
- }
- } else {
- linebuf_vcol[wlv.off] = -1;
- }
+ linebuf_vcol[wlv.off] = wlv.vcol;
if (utf_char2cells(mb_c) > 1) {
// Need to fill two screen columns.
@@ -2844,11 +2750,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
linebuf_char[wlv.off] = 0;
linebuf_attr[wlv.off] = linebuf_attr[wlv.off - 1];
- if (wlv.draw_state > WL_STC && wlv.filler_todo <= 0) {
- linebuf_vcol[wlv.off] = ++wlv.vcol;
- } else {
- linebuf_vcol[wlv.off] = -1;
- }
+ linebuf_vcol[wlv.off] = ++wlv.vcol;
// When "wlv.tocol" is halfway through a character, set it to the end
// of the character, otherwise highlighting won't stop.
@@ -2904,15 +2806,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
}
// The skipped cells need to be accounted for in vcol.
- if (wlv.draw_state > WL_STC && wlv.skipped_cells > 0) {
+ if (wlv.skipped_cells > 0) {
wlv.vcol += wlv.skipped_cells;
wlv.skipped_cells = 0;
}
// Only advance the "wlv.vcol" when after the 'number' or
// 'relativenumber' column.
- if (wlv.draw_state > WL_STC
- && wlv.filler_todo <= 0) {
+ if (wlv.filler_todo <= 0) {
wlv.vcol++;
}
@@ -2921,12 +2822,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
}
// restore attributes after "predeces" in 'listchars'
- if (wlv.draw_state > WL_STC && n_attr3 > 0 && --n_attr3 == 0) {
+ if (n_attr3 > 0 && --n_attr3 == 0) {
wlv.char_attr = saved_attr3;
}
// restore attributes after last 'listchars' or 'number' char
- if (wlv.n_attr > 0 && wlv.draw_state == WL_LINE && --wlv.n_attr == 0) {
+ if (wlv.n_attr > 0 && --wlv.n_attr == 0) {
wlv.char_attr = saved_attr2;
}
@@ -2947,8 +2848,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// At end of screen line and there is more to come: Display the line
// so far. If there is no more to display it is caught above.
if (wlv.col >= grid->cols && (!has_fold || virt_line_offset >= 0)
- && (wlv.draw_state != WL_LINE
- || *ptr != NUL
+ && (*ptr != NUL
|| wlv.filler_todo > 0
|| (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL
&& wlv.p_extra != at_end_str)
@@ -2992,7 +2892,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
}
// When the window is too narrow draw all "@" lines.
- if (wlv.draw_state != WL_LINE && wlv.filler_todo <= 0) {
+ if (wlv.col <= leftcols_width) {
win_draw_end(wp, '@', ' ', true, wlv.row, wp->w_grid.rows, HLF_AT);
set_empty_rows(wp, wlv.row);
wlv.row = endrow;
@@ -3005,6 +2905,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
}
win_line_start(wp, &wlv, true);
+ draw_cols = true;
lcs_prec_todo = wp->w_p_lcs_chars.prec;
if (wlv.filler_todo <= 0) {
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index 0647d65e93..690da8999e 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -26,6 +26,7 @@
#include "nvim/fold.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
+#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/macros_defs.h"
@@ -104,7 +105,7 @@ static void buf_set_sign(buf_T *buf, uint32_t *id, char *group, int prio, linenr
DecorSignHighlight sign = DECOR_SIGN_HIGHLIGHT_INIT;
sign.flags |= kSHIsSign;
- sign.text.ptr = sp->sn_text ? xstrdup(sp->sn_text) : NULL;
+ memcpy(sign.text, sp->sn_text, SIGN_WIDTH * sizeof(schar_T));
sign.sign_name = xstrdup(sp->sn_name);
sign.hl_id = sp->sn_text_hl;
sign.line_hl_id = sp->sn_line_hl;
@@ -113,7 +114,7 @@ static void buf_set_sign(buf_T *buf, uint32_t *id, char *group, int prio, linenr
sign.priority = (DecorPriority)prio;
bool has_hl = (sp->sn_line_hl || sp->sn_num_hl || sp->sn_cul_hl);
- uint16_t decor_flags = (sp->sn_text ? MT_FLAG_DECOR_SIGNTEXT : 0)
+ uint16_t decor_flags = (sp->sn_text[0] ? MT_FLAG_DECOR_SIGNTEXT : 0)
| (has_hl ? MT_FLAG_DECOR_SIGNHL : 0);
DecorInline decor = { .ext = true, .data.ext = { .vt = NULL, .sh_idx = decor_put_sh(sign) } };
@@ -333,9 +334,24 @@ static int sign_cmd_idx(char *begin_cmd, char *end_cmd)
return idx;
}
+/// buf must be SIGN_WIDTH * MAX_SCHAR_SIZE (no extra +1 needed)
+size_t describe_sign_text(char *buf, schar_T *sign_text)
+{
+ size_t p = 0;
+ for (int i = 0; i < SIGN_WIDTH; i++) {
+ schar_get(buf + p, sign_text[i]);
+ size_t len = strlen(buf + p);
+ if (len == 0) {
+ break;
+ }
+ p += len;
+ }
+ return p;
+}
+
/// Initialize the "text" for a new sign and store in "sign_text".
/// "sp" is NULL for signs added through nvim_buf_set_extmark().
-int init_sign_text(sign_T *sp, char **sign_text, char *text)
+int init_sign_text(sign_T *sp, schar_T *sign_text, char *text)
{
char *s;
char *endp = text + (int)strlen(text);
@@ -350,34 +366,29 @@ int init_sign_text(sign_T *sp, char **sign_text, char *text)
// Count cells and check for non-printable chars
int cells = 0;
for (s = text; s < endp; s += utfc_ptr2len(s)) {
- if (!vim_isprintc(utf_ptr2char(s))) {
+ int c;
+ sign_text[cells] = utfc_ptr2schar(s, &c);
+ if (!vim_isprintc(c)) {
break;
}
- cells += utf_ptr2cells(s);
+ int width = utf_char2cells(c);
+ if (width == 2) {
+ sign_text[cells + 1] = 0;
+ }
+ cells += width;
}
// Currently must be empty, one or two display cells
- if (s != endp || cells > 2) {
+ if (s != endp || cells > SIGN_WIDTH) {
if (sp != NULL) {
semsg(_("E239: Invalid sign text: %s"), text);
}
return FAIL;
}
- if (cells < 1) {
- if (sp != NULL) {
- sp->sn_text = NULL;
- }
- return OK;
- }
- if (sp != NULL) {
- xfree(sp->sn_text);
- }
- // Allocate one byte more if we need to pad up with a space.
- size_t len = (size_t)(endp - text + (cells == 1));
- *sign_text = xstrnsave(text, len);
-
- if (cells == 1) {
- STRCPY(*sign_text + len - 1, " ");
+ if (cells < 1) {
+ sign_text[0] = 0;
+ } else if (cells == 1) {
+ sign_text[1] = schar_from_ascii(' ');
}
return OK;
@@ -411,7 +422,7 @@ static int sign_define_by_name(char *name, char *icon, char *text, char *linehl,
backslash_halve((*sp)->sn_icon);
}
- if (text != NULL && (init_sign_text(*sp, &(*sp)->sn_text, text) == FAIL)) {
+ if (text != NULL && (init_sign_text(*sp, (*sp)->sn_text, text) == FAIL)) {
return FAIL;
}
@@ -436,7 +447,6 @@ static int sign_undefine_by_name(const char *name)
}
xfree(sp->sn_name);
- xfree(sp->sn_text);
xfree(sp->sn_icon);
xfree(sp);
return OK;
@@ -451,9 +461,11 @@ static void sign_list_defined(sign_T *sp)
msg_outtrans(sp->sn_icon, 0);
msg_puts(_(" (not supported)"));
}
- if (sp->sn_text != NULL) {
+ if (sp->sn_text[0]) {
msg_puts(" text=");
- msg_outtrans(sp->sn_text, 0);
+ char buf[SIGN_WIDTH * MAX_SCHAR_SIZE];
+ describe_sign_text(buf, sp->sn_text);
+ msg_outtrans(buf, 0);
}
static char *arg[] = { " linehl=", " texthl=", " culhl=", " numhl=" };
int hl[] = { sp->sn_line_hl, sp->sn_text_hl, sp->sn_cul_hl, sp->sn_num_hl };
@@ -880,8 +892,10 @@ static dict_T *sign_get_info_dict(sign_T *sp)
if (sp->sn_icon != NULL) {
tv_dict_add_str(d, S_LEN("icon"), sp->sn_icon);
}
- if (sp->sn_text != NULL) {
- tv_dict_add_str(d, S_LEN("text"), sp->sn_text);
+ if (sp->sn_text[0]) {
+ char buf[SIGN_WIDTH * MAX_SCHAR_SIZE];
+ describe_sign_text(buf, sp->sn_text);
+ tv_dict_add_str(d, S_LEN("text"), buf);
}
static char *arg[] = { "linehl", "texthl", "culhl", "numhl" };
int hl[] = { sp->sn_line_hl, sp->sn_text_hl, sp->sn_cul_hl, sp->sn_num_hl };
diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h
index 79d21585fc..7676fa5319 100644
--- a/src/nvim/sign_defs.h
+++ b/src/nvim/sign_defs.h
@@ -1,8 +1,10 @@
#pragma once
+#include "nvim/types_defs.h"
+
/// Sign attributes. Used by the screen refresh routines.
typedef struct {
- char *text;
+ schar_T text[SIGN_WIDTH];
int hl_id;
} SignTextAttrs;
@@ -10,13 +12,12 @@ typedef struct {
typedef struct sign {
char *sn_name; // name of sign
char *sn_icon; // name of pixmap
- char *sn_text; // text used instead of pixmap
+ schar_T sn_text[SIGN_WIDTH]; // text used instead of pixmap
int sn_line_hl; // highlight ID for line
int sn_text_hl; // highlight ID for text
int sn_cul_hl; // highlight ID for text on current line when 'cursorline' is set
int sn_num_hl; // highlight ID for line number
} sign_T;
-enum { SIGN_WIDTH = 2, }; ///< Number of display cells for a sign in the signcolumn
enum { SIGN_SHOW_MAX = 9, }; ///< Maximum number of signs shown on a single line
enum { SIGN_DEF_PRIO = 10, }; ///< Default sign highlight priority
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
index 112a433d9d..e249317bd3 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -1640,20 +1640,32 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
char *p = NULL;
if (fold) {
- size_t n = fill_foldcolumn(out_p, wp, stcp->foldinfo,
- (linenr_T)get_vim_var_nr(VV_LNUM), NULL);
+ schar_T fold_buf[10];
+ size_t n = fill_foldcolumn(NULL, wp, stcp->foldinfo,
+ (linenr_T)get_vim_var_nr(VV_LNUM), 0, fold_buf);
stl_items[curitem].minwid = -((stcp->use_cul ? HLF_CLF : HLF_FC) + 1);
+ size_t buflen = 0;
+ // TODO(bfredl): this is very backwards. we must support schar_T
+ // being used directly in 'statuscol'
+ for (size_t i = 0; i < n; i++) {
+ schar_get(out_p + buflen, fold_buf[i]);
+ buflen += strlen(out_p + buflen);
+ }
p = out_p;
- p[n] = NUL;
}
+ char buf[SIGN_WIDTH * MAX_SCHAR_SIZE];
size_t buflen = 0;
varnumber_T virtnum = get_vim_var_nr(VV_VIRTNUM);
for (int i = 0; i < width; i++) {
if (!fold) {
SignTextAttrs *sattr = virtnum ? NULL : &stcp->sattrs[i];
- p = sattr && sattr->text ? sattr->text : " ";
- stl_items[curitem].minwid = -(sattr && sattr->text
+ p = " ";
+ if (sattr && sattr->text[0]) {
+ describe_sign_text(buf, sattr->text);
+ p = buf;
+ }
+ stl_items[curitem].minwid = -(sattr && sattr->text[0]
? (stcp->sign_cul_id ? stcp->sign_cul_id : sattr->hl_id)
: (stcp->use_cul ? HLF_CLS : HLF_SC) + 1);
}
diff --git a/src/nvim/types_defs.h b/src/nvim/types_defs.h
index b2c7101f60..0573439b73 100644
--- a/src/nvim/types_defs.h
+++ b/src/nvim/types_defs.h
@@ -56,6 +56,8 @@ typedef struct {
} SignRange;
#define SIGNRANGE_INIT { 0, 0 }
+enum { SIGN_WIDTH = 2, }; ///< Number of display cells for a sign in the signcolumn
+
typedef struct file_buffer buf_T;
typedef struct loop Loop;
typedef struct regprog regprog_T;
diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua
index b1a9b72cf7..c07db8d3de 100644
--- a/test/functional/ui/sign_spec.lua
+++ b/test/functional/ui/sign_spec.lua
@@ -415,8 +415,8 @@ describe('Signs', function()
feed('gg100aa<Esc>')
screen:expect([[
{1: >> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
- aa^a |
+ {8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {8: }aa^a |
{8: 2 }b |
{6: 3 }c |
{6: 4 } |
@@ -430,10 +430,10 @@ describe('Signs', function()
feed('<C-Y>')
-- number column on virtual lines should be empty
screen:expect([[
- VIRT LINES |
+ {6: }VIRT LINES |
{1: >> }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
- aa^a |
+ {8: }aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ {8: }aa^a |
{8: 2 }b |
{6: 3 }c |
{6: 4 } |