aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/api/extmark.c4
-rw-r--r--src/nvim/decoration.h4
-rw-r--r--src/nvim/drawline.c49
-rw-r--r--src/nvim/plines.c33
-rw-r--r--src/nvim/plines.h2
-rw-r--r--test/functional/ui/decorations_spec.lua79
6 files changed, 160 insertions, 11 deletions
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index d6f0288f94..dd256a54f0 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -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;
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 92001d496d..95c9655742 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -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,
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 863d237062..012cce7b22 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -1030,6 +1030,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
int left_curline_col = 0;
int right_curline_col = 0;
+ VirtText virt_inline = KV_INITIAL_VALUE;
+ size_t virt_inline_i = 0;
+
int match_conc = 0; ///< cchar for match functions
bool on_last_col = false;
int syntax_flags = 0;
@@ -1706,7 +1709,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
@@ -1787,6 +1792,44 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
wlv.char_attr = 0;
}
}
+
+ 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);
+
+ // we could already be inside an existing virt_line with multiple chunks
+ if (!(virt_inline_i < kv_size(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) {
+ virt_inline = item->decor.virt_text;
+ virt_inline_i = 0;
+ item->win_col = -2;
+ break;
+ }
+ }
+ }
+
+ if (wlv.n_extra <= 0 && virt_inline_i < kv_size(virt_inline)) {
+ VirtTextChunk vtc = kv_A(virt_inline, virt_inline_i);
+ wlv.p_extra = vtc.text;
+ wlv.n_extra = (int)strlen(wlv.p_extra);
+ wlv.c_extra = NUL;
+ wlv.c_final = NUL;
+ wlv.extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0;
+ n_attr = wlv.n_extra;
+ extmark_attr = 0;
+ virt_inline_i++;
+ }
+ }
}
// Get the next character to put on the screen.
@@ -2019,10 +2062,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);
diff --git a/src/nvim/plines.c b/src/nvim/plines.c
index 3e69e547cb..738057b696 100644
--- a/src/nvim/plines.c
+++ b/src/nvim/plines.c
@@ -295,17 +295,25 @@ unsigned linetabsize(win_T *wp, linenr_T lnum)
/// 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_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".
@@ -383,7 +391,22 @@ 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 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) { // TODO: or maybe unconditionally, what if byte-misaligned?
+ if (!mt_end(mark)) {
+ Decoration decor = get_decor(mark);
+ if (decor.virt_text_pos == kVTInline) {
+ cts->cts_cur_text_width = decor.virt_text_width;
+ size += cts->cts_cur_text_width;
+ }
+ }
+ }
+ marktree_itr_next(wp->w_buffer->b_marktree, cts->cts_iter);
+ }
}
int c = (uint8_t)(*s);
diff --git a/src/nvim/plines.h b/src/nvim/plines.h
index 808f6d284e..a15c234bba 100644
--- a/src/nvim/plines.h
+++ b/src/nvim/plines.h
@@ -11,9 +11,11 @@ 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
+ MarkTreeIter cts_iter[1];
// TODO(bfredl): iterator in to the marktree for scanning virt text
int cts_vcol; // virtual column at current position
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index ccf1810ee1..80dfebd33e 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -645,6 +645,7 @@ describe('extmark decorations', function()
[25] = {background = Screen.colors.LightRed};
[26] = {background=Screen.colors.DarkGrey, foreground=Screen.colors.LightGrey};
[27] = {background = Screen.colors.Plum1};
+ [28] = {foreground = Screen.colors.SlateBlue};
}
ns = meths.create_namespace 'test'
@@ -1166,6 +1167,84 @@ end]]
screen:expect_unchanged(true)
end)
+ it('can have virtual text of inline position', function()
+ insert(example_text)
+ feed 'gg'
+ screen:expect{grid=[[
+ ^for _,item in ipairs(items) do |
+ local text, hl_id_cell, count = unpack(item) |
+ if hl_id_cell ~= nil then |
+ hl_id = hl_id_cell |
+ end |
+ for _ = 1, (count or 1) do |
+ local cell = line[colpos] |
+ cell.text = text |
+ cell.hl_id = hl_id |
+ colpos = colpos+1 |
+ end |
+ end |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ meths.buf_set_extmark(0, ns, 1, 14, {virt_text={{': ', 'Special'}, {'string', 'Type'}}, virt_text_pos='inline'})
+ screen:expect{grid=[[
+ ^for _,item in ipairs(items) do |
+ local text{28:: }{3:string}, hl_id_cell, count = unpack|
+ (item) |
+ if hl_id_cell ~= nil then |
+ hl_id = hl_id_cell |
+ end |
+ for _ = 1, (count or 1) do |
+ local cell = line[colpos] |
+ cell.text = text |
+ cell.hl_id = hl_id |
+ colpos = colpos+1 |
+ end |
+ end |
+ {1:~ }|
+ |
+ ]]}
+
+ screen:try_resize(55, 15)
+ screen:expect{grid=[[
+ ^for _,item in ipairs(items) do |
+ local text{28:: }{3:string}, hl_id_cell, count = unpack(item|
+ ) |
+ if hl_id_cell ~= nil then |
+ hl_id = hl_id_cell |
+ end |
+ for _ = 1, (count or 1) do |
+ local cell = line[colpos] |
+ cell.text = text |
+ cell.hl_id = hl_id |
+ colpos = colpos+1 |
+ end |
+ end |
+ {1:~ }|
+ |
+ ]]}
+
+ screen:try_resize(56, 15)
+ screen:expect{grid=[[
+ ^for _,item in ipairs(items) do |
+ local text{28:: }{3:string}, hl_id_cell, count = unpack(item)|
+ if hl_id_cell ~= nil then |
+ hl_id = hl_id_cell |
+ end |
+ for _ = 1, (count or 1) do |
+ local cell = line[colpos] |
+ cell.text = text |
+ cell.hl_id = hl_id |
+ colpos = colpos+1 |
+ end |
+ end |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+ end)
end)
describe('decorations: virtual lines', function()