aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/buffer.c58
-rw-r--r--src/nvim/decoration.c91
-rw-r--r--src/nvim/decoration.h8
-rw-r--r--src/nvim/screen.c55
4 files changed, 143 insertions, 69 deletions
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 48eef20d59..c1f2842b52 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -1425,6 +1425,11 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id,
/// - hl_group : name of the highlight group used to highlight
/// this mark.
/// - virt_text : virtual text to link to this mark.
+/// - virt_text_pos : positioning of virtual text. Possible
+/// values:
+/// - "eol": right after eol character (default)
+/// - "overlay": display over the specified column, without
+/// shifting the underlying text.
/// - ephemeral : for use with |nvim_set_decoration_provider|
/// callbacks. The mark will only be used for the current
/// redraw cycle, and not be permantently stored in the
@@ -1474,8 +1479,9 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
uint64_t id = 0;
int line2 = -1, hl_id = 0;
DecorPriority priority = DECOR_PRIORITY_BASE;
- colnr_T col2 = 0;
+ colnr_T col2 = -1;
VirtText virt_text = KV_INITIAL_VALUE;
+ VirtTextPos virt_text_pos = kVTEndOfLine;
bool right_gravity = true;
bool end_right_gravity = false;
bool end_gravity_set = false;
@@ -1544,6 +1550,22 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
if (ERROR_SET(err)) {
goto error;
}
+ } else if (strequal("virt_text_pos", k.data)) {
+ if (v->type != kObjectTypeString) {
+ api_set_error(err, kErrorTypeValidation,
+ "virt_text_pos is not a String");
+ goto error;
+ }
+ String str = v->data.string;
+ if (strequal("eol", str.data)) {
+ virt_text_pos = kVTEndOfLine;
+ } else if (strequal("overlay", str.data)) {
+ virt_text_pos = kVTOverlay;
+ } else {
+ api_set_error(err, kErrorTypeValidation,
+ "virt_text_pos: invalid value");
+ goto error;
+ }
} else if (strequal("ephemeral", k.data)) {
ephemeral = api_object_to_bool(*v, "ephemeral", false, err);
if (ERROR_SET(err)) {
@@ -1585,7 +1607,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
// Only error out if they try to set end_right_gravity without
// setting end_col or end_line
- if (line2 == -1 && col2 == 0 && end_gravity_set) {
+ if (line2 == -1 && col2 == -1 && end_gravity_set) {
api_set_error(err, kErrorTypeValidation,
"cannot set end_right_gravity "
"without setting end_line or end_col");
@@ -1609,30 +1631,28 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
col2 = 0;
}
+ Decoration *decor = NULL, tmp = { 0 };
+
+ if (kv_size(virt_text) || priority != DECOR_PRIORITY_BASE) {
+ // TODO(bfredl): this is a bit sketchy. eventually we should
+ // have predefined decorations for both marks/ephemerals
+ decor = ephemeral ? &tmp : xcalloc(1, sizeof(*decor));
+ decor->hl_id = hl_id;
+ decor->virt_text = virt_text;
+ decor->priority = priority;
+ decor->virt_text_pos = virt_text_pos;
+ } else if (hl_id) {
+ decor = decor_hl(hl_id);
+ }
+
// TODO(bfredl): synergize these two branches even more
if (ephemeral && decor_state.buf == buf) {
- int attr_id = hl_id > 0 ? syn_id2attr(hl_id) : 0;
- VirtText *vt_allocated = NULL;
- if (kv_size(virt_text)) {
- vt_allocated = xmalloc(sizeof *vt_allocated);
- *vt_allocated = virt_text;
- }
- decor_add_ephemeral(attr_id, (int)line, (colnr_T)col,
- (int)line2, (colnr_T)col2, priority, vt_allocated);
+ decor_add_ephemeral((int)line, (int)col, line2, col2, decor, 0);
} else {
if (ephemeral) {
api_set_error(err, kErrorTypeException, "not yet implemented");
goto error;
}
- Decoration *decor = NULL;
- if (kv_size(virt_text)) {
- decor = xcalloc(1, sizeof(*decor));
- decor->hl_id = hl_id;
- decor->virt_text = virt_text;
- } else if (hl_id) {
- decor = decor_hl(hl_id);
- decor->priority = priority;
- }
id = extmark_set(buf, (uint64_t)ns_id, id, (int)line, (colnr_T)col,
line2, col2, decor, right_gravity,
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index f3ee42fab1..a1289f202a 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -188,20 +188,21 @@ bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state)
goto next_mark;
}
- int attr_id = decor->hl_id > 0 ? syn_id2attr(decor->hl_id) : 0;
- VirtText *vt = kv_size(decor->virt_text) ? &decor->virt_text : NULL;
- HlRange range;
if (mark.id&MARKTREE_END_FLAG) {
- range = (HlRange){ altpos.row, altpos.col, mark.row, mark.col,
- attr_id, decor->priority, vt, false };
+ decor_add(state, altpos.row, altpos.col, mark.row, mark.col,
+ decor, false, 0);
} else {
- range = (HlRange){ mark.row, mark.col, altpos.row,
- altpos.col, attr_id, decor->priority, vt, false };
+ if (altpos.row == -1) {
+ altpos.row = mark.row;
+ altpos.col = mark.col;
+ }
+ decor_add(state, mark.row, mark.col, altpos.row, altpos.col,
+ decor, false, 0);
}
- hlrange_activate(range, state);
next_mark:
if (marktree_itr_node_done(state->itr)) {
+ marktree_itr_next(buf->b_marktree, state->itr);
break;
}
marktree_itr_next(buf->b_marktree, state->itr);
@@ -220,41 +221,39 @@ bool decor_redraw_line(buf_T *buf, int row, DecorState *state)
return true; // TODO(bfredl): be more precise
}
-static void hlrange_activate(HlRange range, DecorState *state)
+static void decor_add(DecorState *state, int start_row, int start_col,
+ int end_row, int end_col, Decoration *decor, bool owned,
+ DecorPriority priority)
{
- // Get size before preparing the push, to have the number of elements
- size_t s = kv_size(state->active);
-
- kv_pushp(state->active);
+ int attr_id = decor->hl_id > 0 ? syn_id2attr(decor->hl_id) : 0;
- size_t dest_index = 0;
+ HlRange range = { start_row, start_col, end_row, end_col,
+ attr_id, MAX(priority, decor->priority),
+ kv_size(decor->virt_text) ? &decor->virt_text : NULL,
+ decor->virt_text_pos,
+ kv_size(decor->virt_text) && owned, -1 };
- // Determine insertion dest_index
- while (dest_index < s) {
- HlRange item = kv_A(state->active, dest_index);
- if (item.priority > range.priority) {
+ kv_pushp(state->active);
+ size_t index;
+ for (index = kv_size(state->active)-1; index > 0; index--) {
+ HlRange item = kv_A(state->active, index-1);
+ if (item.priority <= range.priority) {
break;
}
-
- dest_index++;
- }
-
- // Splice
- for (size_t index = s; index > dest_index; index--) {
kv_A(state->active, index) = kv_A(state->active, index-1);
}
-
- // Insert
- kv_A(state->active, dest_index) = range;
+ kv_A(state->active, index) = range;
}
-int decor_redraw_col(buf_T *buf, int col, DecorState *state)
+int decor_redraw_col(buf_T *buf, int col, int virt_col, DecorState *state)
{
if (col <= state->col_until) {
return state->current;
}
state->col_until = MAXCOL;
while (true) {
+ // TODO(bfredl): check duplicate entry in "intersection"
+ // branch
mtmark_t mark = marktree_itr_current(state->itr);
if (mark.row < 0 || mark.row > state->row) {
break;
@@ -278,6 +277,11 @@ int decor_redraw_col(buf_T *buf, int col, DecorState *state)
}
Decoration *decor = item->decor;
+ if (endpos.row == -1) {
+ endpos.row = mark.row;
+ endpos.col = mark.col;
+ }
+
if (endpos.row < mark.row
|| (endpos.row == mark.row && endpos.col <= mark.col)) {
if (!kv_size(decor->virt_text)) {
@@ -285,12 +289,8 @@ int decor_redraw_col(buf_T *buf, int col, DecorState *state)
}
}
- int attr_id = decor->hl_id > 0 ? syn_id2attr(decor->hl_id) : 0;
- VirtText *vt = kv_size(decor->virt_text) ? &decor->virt_text : NULL;
- hlrange_activate((HlRange){ mark.row, mark.col,
- endpos.row, endpos.col,
- attr_id, decor->priority,
- vt, false }, state);
+ decor_add(state, mark.row, mark.col, endpos.row, endpos.col,
+ decor, false, 0);
next_mark:
marktree_itr_next(buf->b_marktree, state->itr);
@@ -310,7 +310,7 @@ next_mark:
if (item.start_row < state->row
|| (item.start_row == state->row && item.start_col <= col)) {
active = true;
- if (item.end_row == state->row) {
+ if (item.end_row == state->row && item.end_col > col) {
state->col_until = MIN(state->col_until, item.end_col-1);
}
} else {
@@ -322,8 +322,12 @@ next_mark:
if (active && item.attr_id > 0) {
attr = hl_combine_attr(attr, item.attr_id);
}
+ if ((item.start_row == state->row && item.start_col <= col)
+ && item.virt_text && item.virt_col == -1) {
+ item.virt_col = virt_col;
+ }
if (keep) {
- kv_A(state->active, j++) = kv_A(state->active, i);
+ kv_A(state->active, j++) = item;
} else if (item.virt_text_owned) {
clear_virttext(item.virt_text);
xfree(item.virt_text);
@@ -341,21 +345,20 @@ void decor_redraw_end(DecorState *state)
VirtText *decor_redraw_virt_text(buf_T *buf, DecorState *state)
{
- decor_redraw_col(buf, MAXCOL, state);
+ decor_redraw_col(buf, MAXCOL, MAXCOL, state);
for (size_t i = 0; i < kv_size(state->active); i++) {
HlRange item = kv_A(state->active, i);
- if (item.start_row == state->row && item.virt_text) {
+ if (item.start_row == state->row && item.virt_text
+ && item.virt_text_pos == kVTEndOfLine) {
return item.virt_text;
}
}
return NULL;
}
-void decor_add_ephemeral(int attr_id, int start_row, int start_col,
- int end_row, int end_col, DecorPriority priority,
- VirtText *virt_text)
+void decor_add_ephemeral(int start_row, int start_col, int end_row, int end_col,
+ Decoration *decor, DecorPriority priority)
{
-hlrange_activate(((HlRange){ start_row, start_col, end_row, end_col, attr_id,
- priority, virt_text, virt_text != NULL }),
- &decor_state);
+ decor_add(&decor_state, start_row, start_col, end_row, end_col, decor, true,
+ priority);
}
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 2533a641dd..47bd9abbc3 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -18,10 +18,16 @@ typedef kvec_t(VirtTextChunk) VirtText;
typedef uint16_t DecorPriority;
#define DECOR_PRIORITY_BASE 0x1000
+typedef enum {
+ kVTEndOfLine,
+ kVTOverlay,
+} VirtTextPos;
+
struct Decoration
{
int hl_id; // highlight group
VirtText virt_text;
+ VirtTextPos virt_text_pos;
// TODO(bfredl): style, signs, etc
DecorPriority priority;
bool shared; // shared decoration, don't free
@@ -35,7 +41,9 @@ typedef struct {
int attr_id;
DecorPriority priority;
VirtText *virt_text;
+ VirtTextPos virt_text_pos;
bool virt_text_owned;
+ int virt_col;
} HlRange;
typedef struct {
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index c16fe46955..dd28c6fb35 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -143,7 +143,7 @@ long tab_page_click_defs_size = 0;
// for line_putchar. Contains the state that needs to be remembered from
// putting one character to the next.
typedef struct {
- const char_u *p;
+ const char *p;
int prev_c; // previous Arabic character
int prev_c1; // first composing char for prev_c
} LineState;
@@ -1857,7 +1857,7 @@ static int compute_foldcolumn(win_T *wp, int col)
/// Handles composing chars and arabic shaping state.
static int line_putchar(LineState *s, schar_T *dest, int maxcells, bool rl)
{
- const char_u *p = s->p;
+ const char_u *p = (char_u *)s->p;
int cells = utf_ptr2cells(p);
int c_len = utfc_ptr2len(p);
int u8c, u8cc[MAX_MCO];
@@ -2870,6 +2870,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
&& vcol >= (long)wp->w_virtcol)
|| (number_only && draw_state > WL_NR))
&& filler_todo <= 0) {
+ draw_virt_text(buf, &col, grid->Columns);
grid_put_linebuf(grid, row, 0, col, -grid->Columns, wp->w_p_rl, wp,
wp->w_hl_attr_normal, false);
// Pretend we have finished updating the window. Except when
@@ -3397,7 +3398,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
}
if (has_decor && v > 0) {
- int extmark_attr = decor_redraw_col(wp->w_buffer, (colnr_T)v-1,
+ int extmark_attr = decor_redraw_col(wp->w_buffer, (colnr_T)v-1, off,
&decor_state);
if (extmark_attr != 0) {
if (!attr_pri) {
@@ -3919,7 +3920,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
int i;
size_t virt_pos = 0;
- LineState s = LINE_STATE((char_u *)"");
+ LineState s = LINE_STATE("");
int virt_attr = 0;
// Make sure alignment is the same regardless
@@ -3963,7 +3964,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
if (do_virttext && !delay_virttext) {
if (*s.p == NUL) {
if (virt_pos < virt_text.size) {
- s.p = (char_u *)kv_A(virt_text, virt_pos).text;
+ s.p = kv_A(virt_text, virt_pos).text;
int hl_id = kv_A(virt_text, virt_pos).hl_id;
virt_attr = hl_id > 0 ? syn_id2attr(hl_id) : 0;
virt_pos++;
@@ -4029,6 +4030,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
col += n;
}
}
+
+ draw_virt_text(buf, &col, grid->Columns);
grid_put_linebuf(grid, row, 0, col, grid->Columns, wp->w_p_rl, wp,
wp->w_hl_attr_normal, false);
row++;
@@ -4247,7 +4250,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
&& (grid->Columns == Columns // Window spans the width of the screen,
|| ui_has(kUIMultigrid)) // or has dedicated grid.
&& !wp->w_p_rl; // Not right-to-left.
- grid_put_linebuf(grid, row, 0, col - boguscols, grid->Columns, wp->w_p_rl,
+
+ int draw_col = col - boguscols;
+ draw_virt_text(buf, &draw_col, grid->Columns);
+ grid_put_linebuf(grid, row, 0, draw_col, grid->Columns, wp->w_p_rl,
wp, wp->w_hl_attr_normal, wrap);
if (wrap) {
ScreenGrid *current_grid = grid;
@@ -4323,6 +4329,43 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
return row;
}
+void draw_virt_text(buf_T *buf, int *end_col, int max_col)
+{
+ DecorState *state = &decor_state;
+ for (size_t i = 0; i < kv_size(state->active); i++) {
+ HlRange *item = &kv_A(state->active, i);
+ if (item->start_row == state->row && item->virt_text
+ && item->virt_text_pos == kVTOverlay
+ && item->virt_col >= 0) {
+ VirtText vt = *item->virt_text;
+ LineState s = LINE_STATE("");
+ int virt_attr = 0;
+ int col = item->virt_col;
+ size_t virt_pos = 0;
+ item->virt_col = -2; // deactivate
+
+ while (col < max_col) {
+ if (!*s.p) {
+ if (virt_pos == kv_size(vt)) {
+ break;
+ }
+ s.p = kv_A(vt, virt_pos).text;
+ int hl_id = kv_A(vt, virt_pos).hl_id;
+ virt_attr = hl_id > 0 ? syn_id2attr(hl_id) : 0;
+ virt_pos++;
+ continue;
+ }
+ int cells = line_putchar(&s, &linebuf_char[col], 2, false);
+ linebuf_attr[col++] = virt_attr;
+ if (cells > 1) {
+ linebuf_attr[col++] = virt_attr;
+ }
+ }
+ *end_col = MAX(*end_col, col);
+ }
+ }
+}
+
/// Determine if dedicated window grid should be used or the default_grid
///
/// If UI did not request multigrid support, draw all windows on the