mirror of
https://github.com/neovim/neovim.git
synced 2025-10-07 10:26:31 +00:00
Merge pull request #20130 from bfredl/inline
feat(ui): inline virtual text
This commit is contained in:
@@ -477,6 +477,8 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
|
||||
/// - "overlay": display over the specified column, without
|
||||
/// shifting the underlying text.
|
||||
/// - "right_align": display right aligned in the window.
|
||||
/// - "inline": display at the specified column, and
|
||||
/// shift the buffer text to the right as needed
|
||||
/// - virt_text_win_col : position the virtual text at a fixed
|
||||
/// window column (starting from the first
|
||||
/// text column)
|
||||
@@ -695,6 +697,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
|
||||
decor.virt_text_pos = kVTOverlay;
|
||||
} else if (strequal("right_align", str.data)) {
|
||||
decor.virt_text_pos = kVTRightAlign;
|
||||
} else if (strequal("inline", str.data)) {
|
||||
decor.virt_text_pos = kVTInline;
|
||||
} else {
|
||||
VALIDATE_S(false, "virt_text_pos", "", {
|
||||
goto error;
|
||||
|
@@ -808,6 +808,7 @@ struct file_buffer {
|
||||
|
||||
MarkTree b_marktree[1];
|
||||
Map(uint32_t, uint32_t) b_extmark_ns[1]; // extmark namespaces
|
||||
size_t b_virt_text_inline; // number of inline virtual texts
|
||||
size_t b_virt_line_blocks; // number of virt_line blocks
|
||||
size_t b_signs; // number of sign extmarks
|
||||
size_t b_signs_with_text; // number of sign extmarks with text
|
||||
|
@@ -989,6 +989,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
|
||||
}
|
||||
|
||||
chartabsize_T cts;
|
||||
bool on_NUL = false;
|
||||
init_chartabsize_arg(&cts, wp, pos->lnum, 0, line, line);
|
||||
|
||||
// This function is used very often, do some speed optimizations.
|
||||
@@ -1052,8 +1053,9 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
|
||||
|
||||
// make sure we don't go past the end of the line
|
||||
if (*cts.cts_ptr == NUL) {
|
||||
// NUL at end of line only takes one column
|
||||
incr = 1;
|
||||
// NUL at end of line only takes one column, unless there is virtual text
|
||||
incr = MAX(1, cts.cts_cur_text_width_left + cts.cts_cur_text_width_right);
|
||||
on_NUL = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1079,8 +1081,6 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
|
||||
}
|
||||
|
||||
if (cursor != NULL) {
|
||||
// cursor is after inserted text
|
||||
vcol += cts.cts_cur_text_width;
|
||||
if ((*ptr == TAB)
|
||||
&& (State & MODE_NORMAL)
|
||||
&& !wp->w_p_list
|
||||
@@ -1089,6 +1089,13 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
|
||||
// cursor at end
|
||||
*cursor = vcol + incr - 1;
|
||||
} else {
|
||||
if (!on_NUL) {
|
||||
// cursor is after inserted text, unless on the NUL
|
||||
vcol += cts.cts_cur_text_width_left;
|
||||
if ((State & MODE_INSERT) == 0) {
|
||||
vcol += cts.cts_cur_text_width_right;
|
||||
}
|
||||
}
|
||||
// cursor at start
|
||||
*cursor = vcol + head;
|
||||
}
|
||||
|
@@ -83,6 +83,9 @@ void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor)
|
||||
|
||||
if (decor && decor_virt_pos(*decor)) {
|
||||
redraw_buf_line_later(buf, row1 + 1, false);
|
||||
if (decor->virt_text_pos == kVTInline) {
|
||||
changed_line_display_buf(buf);
|
||||
}
|
||||
}
|
||||
|
||||
if (decor && kv_size(decor->virt_lines)) {
|
||||
@@ -95,6 +98,10 @@ void decor_remove(buf_T *buf, int row, int row2, Decoration *decor)
|
||||
{
|
||||
decor_redraw(buf, row, row2, decor);
|
||||
if (decor) {
|
||||
if (kv_size(decor->virt_text) && decor->virt_text_pos == kVTInline) {
|
||||
assert(buf->b_virt_text_inline > 0);
|
||||
buf->b_virt_text_inline--;
|
||||
}
|
||||
if (kv_size(decor->virt_lines)) {
|
||||
assert(buf->b_virt_line_blocks > 0);
|
||||
buf->b_virt_line_blocks--;
|
||||
|
@@ -23,9 +23,11 @@ typedef enum {
|
||||
kVTOverlay,
|
||||
kVTWinCol,
|
||||
kVTRightAlign,
|
||||
kVTInline,
|
||||
} VirtTextPos;
|
||||
|
||||
EXTERN const char *const virt_text_pos_str[] INIT(= { "eol", "overlay", "win_col", "right_align" });
|
||||
EXTERN const char *const virt_text_pos_str[] INIT(= { "eol", "overlay", "win_col", "right_align",
|
||||
"inline" });
|
||||
|
||||
typedef enum {
|
||||
kHlModeUnknown,
|
||||
|
@@ -98,6 +98,7 @@ typedef struct {
|
||||
int char_attr; ///< attributes for next character
|
||||
|
||||
int n_extra; ///< number of extra bytes
|
||||
int n_attr; ///< chars with special attr
|
||||
char *p_extra; ///< string of extra chars, plus NUL, only used
|
||||
///< when c_extra and c_final are NUL
|
||||
char *p_extra_free; ///< p_extra buffer that needs to be freed
|
||||
@@ -105,9 +106,12 @@ typedef struct {
|
||||
int c_extra; ///< extra chars, all the same
|
||||
int c_final; ///< final char, mandatory if set
|
||||
|
||||
bool extra_for_extmark;
|
||||
|
||||
// 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;
|
||||
@@ -120,6 +124,14 @@ typedef struct {
|
||||
int filler_lines; ///< nr of filler lines to be drawn
|
||||
int filler_todo; ///< nr of filler lines still to do + 1
|
||||
SignTextAttrs sattrs[SIGN_SHOW_MAX]; ///< sign attributes for the sign column
|
||||
|
||||
VirtText virt_inline;
|
||||
size_t virt_inline_i;
|
||||
|
||||
bool reset_extra_attr;
|
||||
|
||||
int skip_cells; // nr of cells to skip for virtual text
|
||||
int skipped_cells; // nr of skipped virtual text cells
|
||||
} winlinevars_T;
|
||||
|
||||
/// for line_putchar. Contains the state that needs to be remembered from
|
||||
@@ -851,6 +863,73 @@ static void apply_cursorline_highlight(win_T *wp, winlinevars_T *wlv)
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t v, bool *do_save)
|
||||
{
|
||||
while (true) {
|
||||
// we could already be inside an existing inline text with multiple chunks
|
||||
if (!(wlv->virt_inline_i < kv_size(wlv->virt_inline))) {
|
||||
DecorState *state = &decor_state;
|
||||
for (size_t i = 0; i < kv_size(state->active); i++) {
|
||||
DecorRange *item = &kv_A(state->active, i);
|
||||
if (item->start_row != state->row
|
||||
|| !kv_size(item->decor.virt_text)
|
||||
|| item->decor.virt_text_pos != kVTInline) {
|
||||
continue;
|
||||
}
|
||||
if (item->win_col >= -1 && item->start_col == v) {
|
||||
wlv->virt_inline = item->decor.virt_text;
|
||||
wlv->virt_inline_i = 0;
|
||||
item->win_col = -2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (wlv->n_extra == 0 || !wlv->extra_for_extmark) {
|
||||
wlv->reset_extra_attr = false;
|
||||
}
|
||||
|
||||
if (wlv->n_extra <= 0 && wlv->virt_inline_i < kv_size(wlv->virt_inline)) {
|
||||
VirtTextChunk vtc = kv_A(wlv->virt_inline, wlv->virt_inline_i);
|
||||
wlv->p_extra = vtc.text;
|
||||
wlv->n_extra = (int)strlen(wlv->p_extra);
|
||||
wlv->extra_for_extmark = true;
|
||||
wlv->c_extra = NUL;
|
||||
wlv->c_final = NUL;
|
||||
wlv->extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0;
|
||||
wlv->n_attr = mb_charlen(vtc.text);
|
||||
wlv->virt_inline_i++;
|
||||
*do_save = true;
|
||||
// If the text didn't reach until the first window
|
||||
// column we need to skip cells.
|
||||
if (wlv->skip_cells > 0) {
|
||||
int virt_text_len = wlv->n_attr;
|
||||
if (virt_text_len > wlv->skip_cells) {
|
||||
int len = mb_charlen2bytelen(wlv->p_extra, wlv->skip_cells);
|
||||
wlv->n_extra -= len;
|
||||
wlv->p_extra += len;
|
||||
wlv->n_attr -= wlv->skip_cells;
|
||||
// Skipped cells needed to be accounted for in vcol.
|
||||
wlv->skipped_cells += wlv->skip_cells;
|
||||
wlv->skip_cells = 0;
|
||||
} else {
|
||||
// the whole text is left of the window, drop
|
||||
// it and advance to the next one
|
||||
wlv->skip_cells -= virt_text_len;
|
||||
// Skipped cells needed to be accounted for in vcol.
|
||||
wlv->skipped_cells += virt_text_len;
|
||||
wlv->n_attr = 0;
|
||||
wlv->n_extra = 0;
|
||||
|
||||
// go to the start so the next virtual text chunk can be selected.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool check_mb_utf8(int *c, int *u8cc)
|
||||
{
|
||||
if (utf_char2len(*c) > 1) {
|
||||
@@ -917,6 +996,7 @@ static void win_line_start(win_T *wp, winlinevars_T *wlv, bool save_extra)
|
||||
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->saved_char_attr = wlv->char_attr;
|
||||
@@ -931,6 +1011,8 @@ 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->extra_for_extmark = wlv->saved_extra_for_extmark;
|
||||
wlv->c_extra = wlv->saved_c_extra;
|
||||
wlv->c_final = wlv->saved_c_final;
|
||||
wlv->p_extra = wlv->saved_p_extra;
|
||||
@@ -969,12 +1051,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
// at end-of-line
|
||||
bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0;
|
||||
|
||||
int n_attr = 0; // chars with special attr
|
||||
int saved_attr2 = 0; // char_attr saved for n_attr
|
||||
int n_attr3 = 0; // chars with overruling special attr
|
||||
int saved_attr3 = 0; // char_attr saved for n_attr3
|
||||
|
||||
int n_skip = 0; // nr of chars to skip for 'nowrap'
|
||||
int n_skip = 0; // nr of chars to skip for 'nowrap' or
|
||||
// concealing
|
||||
|
||||
int fromcol_prev = -2; // start of inverting after cursor
|
||||
bool noinvcur = false; // don't invert the cursor
|
||||
@@ -986,7 +1068,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
bool area_highlighting = false; // Visual or incsearch highlighting in this line
|
||||
int vi_attr = 0; // attributes for Visual and incsearch highlighting
|
||||
int area_attr = 0; // attributes desired by highlighting
|
||||
int saved_area_attr = 0; // idem for area_attr
|
||||
int search_attr = 0; // attributes desired by 'hlsearch'
|
||||
int saved_search_attr = 0; // search_attr to be used when n_extra
|
||||
// goes to zero
|
||||
int vcol_save_attr = 0; // saved attr for 'cursorcolumn'
|
||||
int syntax_attr = 0; // attributes desired by syntax
|
||||
bool has_syntax = false; // this buffer has syntax highl.
|
||||
@@ -1024,6 +1109,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
int prev_c1 = 0; // first composing char for prev_c
|
||||
|
||||
bool search_attr_from_match = false; // if search_attr is from :match
|
||||
bool saved_search_attr_from_match = false; // if search_attr is from :match
|
||||
bool has_decor = false; // this buffer has decoration
|
||||
int win_col_offset = 0; // offset for window columns
|
||||
|
||||
@@ -1436,6 +1522,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
}
|
||||
}
|
||||
|
||||
// If there the text doesn't reach to the desired column, need to skip
|
||||
// "skip_cells" cells when virtual text follows.
|
||||
if (!wp->w_p_wrap && v > wlv.vcol) {
|
||||
wlv.skip_cells = (int)(v - wlv.vcol);
|
||||
}
|
||||
|
||||
// Adjust for when the inverted text is before the screen,
|
||||
// and when the start of the inverted text is before the screen.
|
||||
if (wlv.tocol <= wlv.vcol) {
|
||||
@@ -1714,7 +1806,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
wlv.n_extra = 0;
|
||||
}
|
||||
|
||||
if (wlv.draw_state == WL_LINE && (area_highlighting || has_spell)) {
|
||||
int extmark_attr = 0;
|
||||
if (wlv.draw_state == WL_LINE
|
||||
&& (area_highlighting || has_spell || (extra_check && !has_fold))) {
|
||||
// handle Visual or match highlighting in this line
|
||||
if (wlv.vcol == wlv.fromcol
|
||||
|| (wlv.vcol + 1 == wlv.fromcol && wlv.n_extra == 0
|
||||
@@ -1732,7 +1826,29 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
area_active = false;
|
||||
}
|
||||
|
||||
if (!wlv.n_extra) {
|
||||
if (has_decor && v >= 0) {
|
||||
bool selected = (area_active || (area_highlighting && noinvcur
|
||||
&& wlv.vcol == wp->w_virtcol));
|
||||
extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off,
|
||||
selected, &decor_state);
|
||||
|
||||
bool do_save = false;
|
||||
handle_inline_virtual_text(wp, &wlv, v, &do_save);
|
||||
if (do_save) {
|
||||
// restore search_attr and area_attr when n_extra is down to zero
|
||||
// TODO(bfredl): this is ugly as fuck. look if we can do this some other way.
|
||||
saved_search_attr = search_attr;
|
||||
saved_area_attr = area_attr;
|
||||
saved_search_attr_from_match = search_attr_from_match;
|
||||
search_attr_from_match = false;
|
||||
search_attr = 0;
|
||||
area_attr = 0;
|
||||
extmark_attr = 0;
|
||||
n_skip = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (wlv.n_extra == 0) {
|
||||
// Check for start/end of 'hlsearch' and other matches.
|
||||
// After end, check for start/end of next match.
|
||||
// When another match, have to check for start again.
|
||||
@@ -1750,12 +1866,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
}
|
||||
|
||||
if (wlv.diff_hlf != (hlf_T)0) {
|
||||
// When there is extra text (eg: virtual text) it gets the
|
||||
// diff highlighting for the line, but not for changed text.
|
||||
if (wlv.diff_hlf == HLF_CHD && ptr - line >= change_start
|
||||
&& wlv.n_extra == 0) {
|
||||
wlv.diff_hlf = HLF_TXD; // changed text
|
||||
}
|
||||
if (wlv.diff_hlf == HLF_TXD && ptr - line > change_end
|
||||
&& wlv.n_extra == 0) {
|
||||
if (wlv.diff_hlf == HLF_TXD && ((ptr - line > change_end && wlv.n_extra == 0)
|
||||
|| (wlv.n_extra > 0 && wlv.extra_for_extmark))) {
|
||||
wlv.diff_hlf = HLF_CHD; // changed line
|
||||
}
|
||||
wlv.line_attr = win_hl_attr(wp, (int)wlv.diff_hlf);
|
||||
@@ -1856,6 +1974,26 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
wlv.p_extra++;
|
||||
}
|
||||
wlv.n_extra--;
|
||||
|
||||
// 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;
|
||||
}
|
||||
if (area_attr == 0 && *ptr != NUL) {
|
||||
area_attr = saved_area_attr;
|
||||
}
|
||||
|
||||
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 (foldinfo.fi_lines > 0) {
|
||||
// skip writing the buffer line itself
|
||||
c = NUL;
|
||||
@@ -1907,7 +2045,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
wlv.c_extra = NUL;
|
||||
wlv.c_final = NUL;
|
||||
if (area_attr == 0 && search_attr == 0) {
|
||||
n_attr = wlv.n_extra + 1;
|
||||
wlv.n_attr = wlv.n_extra + 1;
|
||||
wlv.extra_attr = win_hl_attr(wp, HLF_8);
|
||||
saved_attr2 = wlv.char_attr; // save current attr
|
||||
}
|
||||
@@ -1962,7 +2100,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
wlv.c_final = NUL;
|
||||
c = ' ';
|
||||
if (area_attr == 0 && search_attr == 0) {
|
||||
n_attr = wlv.n_extra + 1;
|
||||
wlv.n_attr = wlv.n_extra + 1;
|
||||
wlv.extra_attr = win_hl_attr(wp, HLF_AT);
|
||||
saved_attr2 = wlv.char_attr; // save current attr
|
||||
}
|
||||
@@ -2027,10 +2165,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
}
|
||||
|
||||
if (has_decor && v > 0) {
|
||||
bool selected = (area_active || (area_highlighting && noinvcur
|
||||
&& wlv.vcol == wp->w_virtcol));
|
||||
int extmark_attr = decor_redraw_col(wp, (colnr_T)v - 1, wlv.off,
|
||||
selected, &decor_state);
|
||||
if (extmark_attr != 0) {
|
||||
if (!attr_pri) {
|
||||
wlv.char_attr = hl_combine_attr(wlv.char_attr, extmark_attr);
|
||||
@@ -2135,7 +2269,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
chartabsize_T cts;
|
||||
|
||||
init_chartabsize_arg(&cts, wp, lnum, wlv.vcol, line, p);
|
||||
// do not want virtual text to be counted here
|
||||
cts.cts_has_virt_text = false;
|
||||
wlv.n_extra = win_lbr_chartabsize(&cts, NULL) - 1;
|
||||
clear_chartabsize_arg(&cts);
|
||||
|
||||
// We have just drawn the showbreak value, no need to add
|
||||
// space for it again.
|
||||
@@ -2167,7 +2304,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
c = ' ';
|
||||
}
|
||||
}
|
||||
clear_chartabsize_arg(&cts);
|
||||
}
|
||||
|
||||
in_multispace = c == ' ' && ((ptr > line + 1 && ptr[-2] == ' ') || *ptr == ' ');
|
||||
@@ -2198,7 +2334,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
} else {
|
||||
c = (c == ' ') ? wp->w_p_lcs_chars.space : wp->w_p_lcs_chars.nbsp;
|
||||
}
|
||||
n_attr = 1;
|
||||
wlv.n_attr = 1;
|
||||
wlv.extra_attr = win_hl_attr(wp, HLF_0);
|
||||
saved_attr2 = wlv.char_attr; // save current attr
|
||||
mb_c = c;
|
||||
@@ -2221,7 +2357,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
c = wp->w_p_lcs_chars.space;
|
||||
}
|
||||
|
||||
n_attr = 1;
|
||||
wlv.n_attr = 1;
|
||||
wlv.extra_attr = win_hl_attr(wp, HLF_0);
|
||||
saved_attr2 = wlv.char_attr; // save current attr
|
||||
mb_c = c;
|
||||
@@ -2337,7 +2473,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
wlv.c_extra = wp->w_p_lcs_chars.tab2;
|
||||
}
|
||||
wlv.c_final = wp->w_p_lcs_chars.tab3;
|
||||
n_attr = tab_len + 1;
|
||||
wlv.n_attr = tab_len + 1;
|
||||
wlv.extra_attr = win_hl_attr(wp, HLF_0);
|
||||
saved_attr2 = wlv.char_attr; // save current attr
|
||||
mb_c = c;
|
||||
@@ -2364,15 +2500,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
&& wlv.line_attr == 0
|
||||
&& wlv.line_attr_lowprio == 0) {
|
||||
// In virtualedit, visual selections may extend beyond end of line
|
||||
if (area_highlighting && virtual_active()
|
||||
&& wlv.tocol != MAXCOL && wlv.vcol < wlv.tocol) {
|
||||
wlv.n_extra = 0;
|
||||
} else {
|
||||
if (!(area_highlighting && virtual_active()
|
||||
&& wlv.tocol != MAXCOL && wlv.vcol < wlv.tocol)) {
|
||||
wlv.p_extra = at_end_str;
|
||||
wlv.n_extra = 1;
|
||||
wlv.c_extra = NUL;
|
||||
wlv.c_final = NUL;
|
||||
}
|
||||
wlv.n_extra = 0;
|
||||
}
|
||||
if (wp->w_p_list && wp->w_p_lcs_chars.eol > 0) {
|
||||
c = wp->w_p_lcs_chars.eol;
|
||||
@@ -2382,7 +2514,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
lcs_eol_one = -1;
|
||||
ptr--; // put it back at the NUL
|
||||
wlv.extra_attr = win_hl_attr(wp, HLF_AT);
|
||||
n_attr = 1;
|
||||
wlv.n_attr = 1;
|
||||
mb_c = c;
|
||||
mb_utf8 = check_mb_utf8(&c, u8cc);
|
||||
} else if (c != NUL) {
|
||||
@@ -2411,7 +2543,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
wlv.n_extra = byte2cells(c) - 1;
|
||||
c = (uint8_t)(*wlv.p_extra++);
|
||||
}
|
||||
n_attr = wlv.n_extra + 1;
|
||||
wlv.n_attr = wlv.n_extra + 1;
|
||||
wlv.extra_attr = win_hl_attr(wp, HLF_8);
|
||||
saved_attr2 = wlv.char_attr; // save current attr
|
||||
mb_utf8 = false; // don't draw as UTF-8
|
||||
@@ -2471,7 +2603,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
}
|
||||
}
|
||||
wlv.n_extra = 0;
|
||||
n_attr = 0;
|
||||
wlv.n_attr = 0;
|
||||
} else if (n_skip == 0) {
|
||||
is_concealing = true;
|
||||
n_skip = 1;
|
||||
@@ -2506,8 +2638,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
}
|
||||
|
||||
// Don't override visual selection highlighting.
|
||||
if (n_attr > 0 && wlv.draw_state == WL_LINE && !search_attr_from_match) {
|
||||
if (wlv.n_attr > 0 && wlv.draw_state == WL_LINE && !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;
|
||||
wlv.extra_attr = 0;
|
||||
// search_attr_from_match can be restored now that the extra_attr has been applied
|
||||
search_attr_from_match = saved_search_attr_from_match;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the case where we are in column 0 but not on the first
|
||||
@@ -2527,7 +2665,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
wlv.c_extra = MB_FILLER_CHAR;
|
||||
wlv.c_final = NUL;
|
||||
wlv.n_extra = 1;
|
||||
n_attr = 2;
|
||||
wlv.n_attr = 2;
|
||||
wlv.extra_attr = win_hl_attr(wp, HLF_AT);
|
||||
}
|
||||
mb_c = c;
|
||||
@@ -2743,7 +2881,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
&& !has_fold
|
||||
&& (*ptr != NUL
|
||||
|| lcs_eol_one > 0
|
||||
|| (wlv.n_extra && (wlv.c_extra != NUL || *wlv.p_extra != NUL)))) {
|
||||
|| (wlv.n_extra > 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL)))) {
|
||||
c = wp->w_p_lcs_chars.ext;
|
||||
wlv.char_attr = win_hl_attr(wp, HLF_AT);
|
||||
mb_c = c;
|
||||
@@ -2864,7 +3002,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
wlv.boguscols += wlv.n_extra;
|
||||
}
|
||||
wlv.n_extra = 0;
|
||||
n_attr = 0;
|
||||
wlv.n_attr = 0;
|
||||
}
|
||||
|
||||
if (utf_char2cells(mb_c) > 1) {
|
||||
@@ -2889,13 +3027,19 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
if (wlv.n_extra > 0) {
|
||||
wlv.vcol += wlv.n_extra;
|
||||
wlv.n_extra = 0;
|
||||
n_attr = 0;
|
||||
wlv.n_attr = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
n_skip--;
|
||||
}
|
||||
|
||||
// The skipped cells need to be accounted for in vcol.
|
||||
if (wlv.draw_state > WL_STC && 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
|
||||
@@ -2913,7 +3057,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
|
||||
}
|
||||
|
||||
// restore attributes after last 'listchars' or 'number' char
|
||||
if (n_attr > 0 && wlv.draw_state == WL_LINE && --n_attr == 0) {
|
||||
if (wlv.n_attr > 0 && wlv.draw_state == WL_LINE && --wlv.n_attr == 0) {
|
||||
wlv.char_attr = saved_attr2;
|
||||
}
|
||||
|
||||
|
@@ -232,7 +232,7 @@ static void insert_enter(InsertState *s)
|
||||
stop_insert_mode = false;
|
||||
|
||||
// need to position cursor again when on a TAB
|
||||
if (gchar_cursor() == TAB) {
|
||||
if (gchar_cursor() == TAB || curbuf->b_virt_text_inline > 0) {
|
||||
curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL);
|
||||
}
|
||||
|
||||
@@ -3471,7 +3471,7 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
|
||||
State = MODE_NORMAL;
|
||||
may_trigger_modechanged();
|
||||
// need to position cursor again when on a TAB
|
||||
if (gchar_cursor() == TAB) {
|
||||
if (gchar_cursor() == TAB || curbuf->b_virt_text_inline > 0) {
|
||||
curwin->w_valid &= ~(VALID_WROW|VALID_WCOL|VALID_VIRTCOL);
|
||||
}
|
||||
|
||||
|
@@ -148,6 +148,9 @@ revised:
|
||||
}
|
||||
|
||||
if (decor) {
|
||||
if (kv_size(decor->virt_text) && decor->virt_text_pos == kVTInline) {
|
||||
buf->b_virt_text_inline++;
|
||||
}
|
||||
if (kv_size(decor->virt_lines)) {
|
||||
buf->b_virt_line_blocks++;
|
||||
}
|
||||
|
@@ -2024,6 +2024,24 @@ int mb_charlen(const char *str)
|
||||
return count;
|
||||
}
|
||||
|
||||
int mb_charlen2bytelen(const char *str, int charlen)
|
||||
{
|
||||
const char *p = str;
|
||||
int count = 0;
|
||||
|
||||
if (p == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; *p != NUL && i < charlen; i++) {
|
||||
int b = utfc_ptr2len(p);
|
||||
p += b;
|
||||
count += b;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/// Like mb_charlen() but for a string with specified length.
|
||||
int mb_charlen_len(const char *str, int len)
|
||||
{
|
||||
|
@@ -102,12 +102,16 @@ int plines_win_nofold(win_T *wp, linenr_T lnum)
|
||||
char *s;
|
||||
unsigned col;
|
||||
int width;
|
||||
chartabsize_T cts;
|
||||
|
||||
s = ml_get_buf(wp->w_buffer, lnum, false);
|
||||
if (*s == NUL) { // empty line
|
||||
return 1;
|
||||
init_chartabsize_arg(&cts, wp, lnum, 0, s, s);
|
||||
if (*s == NUL && !cts.cts_has_virt_text) {
|
||||
return 1; // be quick for an empty line
|
||||
}
|
||||
col = win_linetabsize(wp, lnum, s, MAXCOL);
|
||||
win_linetabsize_cts(&cts, (colnr_T)MAXCOL);
|
||||
clear_chartabsize_arg(&cts);
|
||||
col = (unsigned)cts.cts_vcol;
|
||||
|
||||
// If list mode is on, then the '$' at the end of the line may take up one
|
||||
// extra column.
|
||||
@@ -262,6 +266,11 @@ int linetabsize_col(int startcol, char *s)
|
||||
while (*cts.cts_ptr != NUL) {
|
||||
cts.cts_vcol += lbr_chartabsize_adv(&cts);
|
||||
}
|
||||
if (cts.cts_has_virt_text && cts.cts_ptr == cts.cts_line) {
|
||||
// check for virtual text in an empty line
|
||||
(void)lbr_chartabsize_adv(&cts);
|
||||
cts.cts_vcol += cts.cts_cur_text_width_left + cts.cts_cur_text_width_right;
|
||||
}
|
||||
clear_chartabsize_arg(&cts);
|
||||
return cts.cts_vcol;
|
||||
}
|
||||
@@ -277,10 +286,7 @@ unsigned win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len)
|
||||
{
|
||||
chartabsize_T cts;
|
||||
init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
|
||||
for (; *cts.cts_ptr != NUL && (len == MAXCOL || cts.cts_ptr < line + len);
|
||||
MB_PTR_ADV(cts.cts_ptr)) {
|
||||
cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
|
||||
}
|
||||
win_linetabsize_cts(&cts, len);
|
||||
clear_chartabsize_arg(&cts);
|
||||
return (unsigned)cts.cts_vcol;
|
||||
}
|
||||
@@ -292,20 +298,43 @@ unsigned linetabsize(win_T *wp, linenr_T lnum)
|
||||
return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum, false), (colnr_T)MAXCOL);
|
||||
}
|
||||
|
||||
void win_linetabsize_cts(chartabsize_T *cts, colnr_T len)
|
||||
{
|
||||
for (; *cts->cts_ptr != NUL && (len == MAXCOL || cts->cts_ptr < cts->cts_line + len);
|
||||
MB_PTR_ADV(cts->cts_ptr)) {
|
||||
cts->cts_vcol += win_lbr_chartabsize(cts, NULL);
|
||||
}
|
||||
// check for a virtual text on an empty line
|
||||
if (cts->cts_has_virt_text && *cts->cts_ptr == NUL
|
||||
&& cts->cts_ptr == cts->cts_line) {
|
||||
(void)win_lbr_chartabsize(cts, NULL);
|
||||
cts->cts_vcol += cts->cts_cur_text_width_left + cts->cts_cur_text_width_right;
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepare the structure passed to chartabsize functions.
|
||||
///
|
||||
/// "line" is the start of the line, "ptr" is the first relevant character.
|
||||
/// When "lnum" is zero do not use text properties that insert text.
|
||||
void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum FUNC_ATTR_UNUSED,
|
||||
colnr_T col, char *line, char *ptr)
|
||||
/// When "lnum" is zero do not use inline virtual text.
|
||||
void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T col, char *line,
|
||||
char *ptr)
|
||||
{
|
||||
cts->cts_win = wp;
|
||||
cts->cts_vcol = col;
|
||||
cts->cts_line = line;
|
||||
cts->cts_ptr = ptr;
|
||||
cts->cts_cur_text_width = 0;
|
||||
// TODO(bfredl): actually lookup inline virtual text here
|
||||
cts->cts_cur_text_width_left = 0;
|
||||
cts->cts_cur_text_width_right = 0;
|
||||
cts->cts_has_virt_text = false;
|
||||
cts->cts_row = lnum - 1;
|
||||
|
||||
if (cts->cts_row >= 0) {
|
||||
marktree_itr_get(wp->w_buffer->b_marktree, cts->cts_row, 0, cts->cts_iter);
|
||||
mtkey_t mark = marktree_itr_current(cts->cts_iter);
|
||||
if (mark.pos.row == cts->cts_row) {
|
||||
cts->cts_has_virt_text = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Free any allocated item in "cts".
|
||||
@@ -369,7 +398,8 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
|
||||
int mb_added = 0;
|
||||
int numberextra;
|
||||
|
||||
cts->cts_cur_text_width = 0;
|
||||
cts->cts_cur_text_width_left = 0;
|
||||
cts->cts_cur_text_width_right = 0;
|
||||
|
||||
// No 'linebreak', 'showbreak' and 'breakindent': return quickly.
|
||||
if (!wp->w_p_lbr && !wp->w_p_bri && *get_showbreak_value(wp) == NUL
|
||||
@@ -383,7 +413,34 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
|
||||
// First get normal size, without 'linebreak' or virtual text
|
||||
int size = win_chartabsize(wp, s, vcol);
|
||||
if (cts->cts_has_virt_text) {
|
||||
// TODO(bfredl): inline virtual text
|
||||
int tab_size = size;
|
||||
int charlen = *s == NUL ? 1 : utf_ptr2len(s);
|
||||
int col = (int)(s - line);
|
||||
while (true) {
|
||||
mtkey_t mark = marktree_itr_current(cts->cts_iter);
|
||||
if (mark.pos.row != cts->cts_row || mark.pos.col > col) {
|
||||
break;
|
||||
} else if (mark.pos.col >= col && mark.pos.col < col + charlen) {
|
||||
if (!mt_end(mark)) {
|
||||
Decoration decor = get_decor(mark);
|
||||
if (decor.virt_text_pos == kVTInline) {
|
||||
if (mt_right(mark)) {
|
||||
cts->cts_cur_text_width_right += decor.virt_text_width;
|
||||
} else {
|
||||
cts->cts_cur_text_width_left += decor.virt_text_width;
|
||||
}
|
||||
size += decor.virt_text_width;
|
||||
if (*s == TAB) {
|
||||
// tab size changes because of the inserted text
|
||||
size -= tab_size;
|
||||
tab_size = win_chartabsize(wp, s, vcol + size);
|
||||
size += tab_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
marktree_itr_next(wp->w_buffer->b_marktree, cts->cts_iter);
|
||||
}
|
||||
}
|
||||
|
||||
int c = (uint8_t)(*s);
|
||||
|
@@ -11,10 +11,12 @@ typedef struct {
|
||||
win_T *cts_win;
|
||||
char *cts_line; // start of the line
|
||||
char *cts_ptr; // current position in line
|
||||
int cts_row;
|
||||
|
||||
bool cts_has_virt_text; // true if if a property inserts text
|
||||
int cts_cur_text_width; // width of current inserted text
|
||||
// TODO(bfredl): iterator in to the marktree for scanning virt text
|
||||
int cts_cur_text_width_left; // width of virtual text left of cursor
|
||||
int cts_cur_text_width_right; // width of virtual text right of cursor
|
||||
MarkTreeIter cts_iter[1];
|
||||
|
||||
int cts_vcol; // virtual column at current position
|
||||
} chartabsize_T;
|
||||
|
Reference in New Issue
Block a user