aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLewis Russell <lewis6991@gmail.com>2022-01-03 12:22:13 +0000
committerLewis Russell <lewis6991@gmail.com>2022-03-05 16:51:59 +0000
commit30e4cc3b3f2133e9a7170da9da8175832681f39a (patch)
tree76cca17f4b3389246743afc5febaeb11deeb20b1
parent83fc914337100d03f2e41a3943ccf0107d893698 (diff)
downloadrneovim-30e4cc3b3f2133e9a7170da9da8175832681f39a.tar.gz
rneovim-30e4cc3b3f2133e9a7170da9da8175832681f39a.tar.bz2
rneovim-30e4cc3b3f2133e9a7170da9da8175832681f39a.zip
feat(decorations): support signs
Add the following options to extmarks: - sign_text - sign_hl_group - number_hl_group - line_hl_group - cursorline_hl_group Note: ranges are unsupported and decorations are only applied to start_row
-rw-r--r--src/nvim/api/extmark.c56
-rw-r--r--src/nvim/api/keysets.lua5
-rw-r--r--src/nvim/api/private/helpers.c36
-rw-r--r--src/nvim/buffer.c19
-rw-r--r--src/nvim/buffer_defs.h1
-rw-r--r--src/nvim/decoration.c174
-rw-r--r--src/nvim/decoration.h19
-rw-r--r--src/nvim/extmark.c7
-rw-r--r--src/nvim/marktree.h5
-rw-r--r--src/nvim/screen.c8
-rw-r--r--src/nvim/sign.c2
-rw-r--r--src/nvim/sign_defs.h1
-rw-r--r--test/functional/ui/decorations_spec.lua273
13 files changed, 589 insertions, 17 deletions
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index 3a968f07ab..0ee8134ec4 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -445,6 +445,27 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// - strict: boolean that indicates extmark should not be placed
/// if the line or column value is past the end of the
/// buffer or end of the line respectively. Defaults to true.
+/// - sign_text: string of length 1-2 used to display in the
+/// sign column.
+/// Note: ranges are unsupported and decorations are only
+/// applied to start_row
+/// - sign_hl_group: name of the highlight group used to
+/// highlight the sign column text.
+/// Note: ranges are unsupported and decorations are only
+/// applied to start_row
+/// - number_hl_group: name of the highlight group used to
+/// highlight the number column.
+/// Note: ranges are unsupported and decorations are only
+/// applied to start_row
+/// - line_hl_group: name of the highlight group used to
+/// highlight the whole line.
+/// Note: ranges are unsupported and decorations are only
+/// applied to start_row
+/// - cursorline_hl_group: name of the highlight group used to
+/// highlight the line when the cursor is on the same line
+/// as the mark and 'cursorline' is enabled.
+/// Note: ranges are unsupported and decorations are only
+/// applied to start_row
///
/// @param[out] err Error details, if any
/// @return Id of the created/updated extmark
@@ -519,10 +540,25 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
goto error;
}
- if (HAS_KEY(opts->hl_group)) {
- decor.hl_id = object_to_hl_id(opts->hl_group, "hl_group", err);
- if (ERROR_SET(err)) {
- goto error;
+ struct {
+ const char *name;
+ Object *opt;
+ int *dest;
+ } hls[] = {
+ { "hl_group" , &opts->hl_group , &decor.hl_id },
+ { "sign_hl_group" , &opts->sign_hl_group , &decor.sign_hl_id },
+ { "number_hl_group" , &opts->number_hl_group , &decor.number_hl_id },
+ { "line_hl_group" , &opts->line_hl_group , &decor.line_hl_id },
+ { "cursorline_hl_group", &opts->cursorline_hl_group, &decor.cursorline_hl_id },
+ { NULL, NULL, NULL },
+ };
+
+ for (int j = 0; hls[j].name && hls[j].dest; j++) {
+ if (HAS_KEY(*hls[j].opt)) {
+ *hls[j].dest = object_to_hl_id(*hls[j].opt, hls[j].name, err);
+ if (ERROR_SET(err)) {
+ goto error;
+ }
}
}
@@ -622,6 +658,17 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
goto error;
}
+ if (opts->sign_text.type == kObjectTypeString) {
+ if (!init_sign_text(&decor.sign_text,
+ (char_u *)opts->sign_text.data.string.data)) {
+ api_set_error(err, kErrorTypeValidation, "sign_text is not a valid value");
+ goto error;
+ }
+ } else if (HAS_KEY(opts->sign_text)) {
+ api_set_error(err, kErrorTypeValidation, "sign_text is not a String");
+ goto error;
+ }
+
bool right_gravity = true;
OPTION_TO_BOOL(right_gravity, right_gravity, true);
@@ -709,6 +756,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
error:
clear_virttext(&decor.virt_text);
+ xfree(decor.sign_text);
return 0;
}
diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua
index b05816f8ac..856b336a98 100644
--- a/src/nvim/api/keysets.lua
+++ b/src/nvim/api/keysets.lua
@@ -22,6 +22,11 @@ return {
"virt_lines_above";
"virt_lines_leftcol";
"strict";
+ "sign_text";
+ "sign_hl_group";
+ "number_hl_group";
+ "line_hl_group";
+ "cursorline_hl_group";
};
keymap = {
"noremap";
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 35e8589255..8056950e26 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -1643,3 +1643,39 @@ sctx_T api_set_sctx(uint64_t channel_id)
}
return old_current_sctx;
}
+
+// adapted from sign.c:sign_define_init_text.
+// TODO(lewis6991): Consider merging
+int init_sign_text(char_u **sign_text, char_u *text)
+{
+ char_u *s;
+
+ char_u *endp = text + (int)STRLEN(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))) {
+ break;
+ }
+ cells += utf_ptr2cells(s);
+ }
+ // Currently must be empty, one or two display cells
+ if (s != endp || cells > 2) {
+ return FAIL;
+ }
+ if (cells < 1) {
+ return OK;
+ }
+
+ // Allocate one byte more if we need to pad up
+ // with a space.
+ size_t len = (size_t)(endp - text + ((cells == 1) ? 1 : 0));
+ *sign_text = vim_strnsave(text, len);
+
+ if (cells == 1) {
+ STRCPY(*sign_text + len - 1, " ");
+ }
+
+ return OK;
+}
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 9e82b4e80b..084e18c6cb 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -36,6 +36,7 @@
#include "nvim/cursor.h"
#include "nvim/diff.h"
#include "nvim/digraph.h"
+#include "nvim/decoration.h"
#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
@@ -5469,6 +5470,11 @@ static int buf_signcols_inner(buf_T *buf, int maximum)
FOR_ALL_SIGNS_IN_BUF(buf, sign) {
if (sign->se_lnum > curline) {
+ // Counted all signs, now add extmark signs
+ if (curline > 0) {
+ linesum += decor_signcols(buf, &decor_state, (int)curline-1, (int)curline-1,
+ maximum-linesum);
+ }
if (linesum > signcols) {
signcols = linesum;
if (signcols >= maximum) {
@@ -5483,6 +5489,19 @@ static int buf_signcols_inner(buf_T *buf, int maximum)
}
}
+ if (curline > 0) {
+ linesum += decor_signcols(buf, &decor_state, (int)curline-1, (int)curline-1, maximum-linesum);
+ }
+ if (linesum > signcols) {
+ signcols = linesum;
+ if (signcols >= maximum) {
+ return maximum;
+ }
+ }
+
+ // Check extmarks between signs
+ linesum = decor_signcols(buf, &decor_state, 0, (int)buf->b_ml.ml_line_count-1, maximum);
+
if (linesum > signcols) {
signcols = linesum;
if (signcols >= maximum) {
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 1e0c837056..c7afa5f9d0 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -875,6 +875,7 @@ struct file_buffer {
MarkTree b_marktree[1];
Map(uint32_t, uint32_t) b_extmark_ns[1]; // extmark namespaces
size_t b_virt_line_blocks; // number of virt_line blocks
+ size_t b_signs; // number of sign extmarks
// array of channel_id:s which have asked to receive updates for this
// buffer.
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index 935b233752..6c006b7fe0 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -5,6 +5,7 @@
#include "nvim/extmark.h"
#include "nvim/highlight.h"
#include "nvim/lua/executor.h"
+#include "nvim/move.h"
#include "nvim/screen.h"
#include "nvim/syntax.h"
#include "nvim/vim.h"
@@ -65,8 +66,15 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start
void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor)
{
- if ((!decor || decor->hl_id) && row2 >= row1) {
- redraw_buf_range_later(buf, row1+1, row2+1);
+ if (row2 >= row1) {
+ if (decor && decor->sign_text) {
+ buf->b_signcols_valid = false;
+ changed_line_abv_curs();
+ }
+
+ if (!decor || decor->hl_id || decor_has_sign(decor)) {
+ redraw_buf_range_later(buf, row1+1, row2+1);
+ }
}
if (decor && kv_size(decor->virt_text)) {
@@ -82,9 +90,15 @@ void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor)
void decor_remove(buf_T *buf, int row, int row2, Decoration *decor)
{
decor_redraw(buf, row, row2, decor);
- if (decor && kv_size(decor->virt_lines)) {
- assert(buf->b_virt_line_blocks > 0);
- buf->b_virt_line_blocks--;
+ if (decor) {
+ if (kv_size(decor->virt_lines)) {
+ assert(buf->b_virt_line_blocks > 0);
+ buf->b_virt_line_blocks--;
+ }
+ if (decor_has_sign(decor)) {
+ assert(buf->b_signs > 0);
+ buf->b_signs--;
+ }
}
decor_free(decor);
}
@@ -97,6 +111,7 @@ void decor_free(Decoration *decor)
clear_virttext(&kv_A(decor->virt_lines, i).line);
}
kv_destroy(decor->virt_lines);
+ xfree(decor->sign_text);
xfree(decor);
}
}
@@ -136,6 +151,7 @@ bool decor_redraw_reset(buf_T *buf, DecorState *state)
{
state->row = -1;
state->buf = buf;
+ state->has_sign_decor = false;
for (size_t i = 0; i < kv_size(state->active); i++) {
DecorRange item = kv_A(state->active, i);
if (item.virt_text_owned) {
@@ -180,9 +196,23 @@ bool decor_redraw_start(buf_T *buf, int top_row, DecorState *state)
Decoration decor = get_decor(mark);
+ // Exclude non-paired marks unless they contain virt_text or a sign
+ if (!mt_paired(mark)
+ && !kv_size(decor.virt_text)
+ && !decor_has_sign(&decor)) {
+ goto next_mark;
+ }
+
+ // Don't add signs for end marks as the start mark has already been added.
+ if (mt_end(mark) && decor_has_sign(&decor)) {
+ goto next_mark;
+ }
+
mtpos_t altpos = marktree_get_altpos(buf->b_marktree, mark, NULL);
- if ((!mt_end(mark) && altpos.row < top_row
+ // Exclude start marks if the end mark position is above the top row
+ // Exclude end marks if we have already added the start mark
+ if ((mt_start(mark) && altpos.row < top_row
&& !kv_size(decor.virt_text))
|| (mt_end(mark) && altpos.row >= top_row)) {
goto next_mark;
@@ -241,6 +271,10 @@ static void decor_add(DecorState *state, int start_row, int start_col, int end_r
kv_A(state->active, index) = kv_A(state->active, index-1);
}
kv_A(state->active, index) = range;
+
+ if (decor_has_sign(decor)) {
+ state->has_sign_decor = true;
+ }
}
int decor_redraw_col(buf_T *buf, int col, int win_col, bool hidden, DecorState *state)
@@ -294,7 +328,8 @@ next_mark:
bool active = false, keep = true;
if (item.end_row < state->row
|| (item.end_row == state->row && item.end_col <= col)) {
- if (!(item.start_row >= state->row && kv_size(item.decor.virt_text))) {
+ if (!(item.start_row >= state->row && kv_size(item.decor.virt_text))
+ && !decor_has_sign(&item.decor)) {
keep = false;
}
} else {
@@ -329,6 +364,131 @@ next_mark:
return attr;
}
+void decor_redraw_signs(buf_T *buf, DecorState *state, int row,
+ int *num_signs, sign_attrs_T sattrs[])
+{
+ for (size_t i = 0; i < kv_size(state->active); i++) {
+ DecorRange item = kv_A(state->active, i);
+ Decoration *decor = &item.decor;
+
+ if (!decor_has_sign(decor)) {
+ continue;
+ }
+
+ if (state->row != item.start_row) {
+ continue;
+ }
+
+ int j;
+ for (j = (*num_signs); j > 0; j--) {
+ if (sattrs[j].sat_prio <= decor->priority) {
+ break;
+ }
+ sattrs[j] = sattrs[j-1];
+ }
+ if (j < SIGN_SHOW_MAX) {
+ memset(&sattrs[j], 0, sizeof(sign_attrs_T));
+ sattrs[j].sat_text = decor->sign_text;
+ if (decor->sign_hl_id != 0) {
+ sattrs[j].sat_texthl = syn_id2attr(decor->sign_hl_id);
+ }
+ if (decor->number_hl_id != 0) {
+ sattrs[j].sat_numhl = syn_id2attr(decor->number_hl_id);
+ }
+ if (decor->line_hl_id != 0) {
+ sattrs[j].sat_linehl = syn_id2attr(decor->line_hl_id);
+ }
+ if (decor->cursorline_hl_id != 0) {
+ sattrs[j].sat_culhl = syn_id2attr(decor->cursorline_hl_id);
+ }
+ sattrs[j].sat_prio = decor->priority;
+ (*num_signs)++;
+ }
+ }
+}
+
+// Get the maximum required amount of sign columns needed between row and
+// end_row.
+int decor_signcols(buf_T *buf, DecorState *state, int row, int end_row, int max)
+{
+ int count = 0; // count for the number of signs on a given row
+ int count_remove = 0; // how much to decrement count by when iterating marks for a new row
+ int signcols = 0; // highest value of count
+ int currow = -1; // current row
+
+ if (max <= 1 && buf->b_signs >= (size_t)max) {
+ return max;
+ }
+
+ if (buf->b_signs == 0) {
+ return 0;
+ }
+
+ MarkTreeIter itr[1] = { 0 };
+ marktree_itr_get(buf->b_marktree, 0, -1, itr);
+ while (true) {
+ mtkey_t mark = marktree_itr_current(itr);
+ if (mark.pos.row < 0 || mark.pos.row > end_row) {
+ break;
+ }
+
+ if ((mark.pos.row < row && mt_end(mark))
+ || marktree_decor_level(mark) < kDecorLevelVisible
+ || !mark.decor_full) {
+ goto next_mark;
+ }
+
+ Decoration decor = get_decor(mark);
+
+ if (!decor.sign_text) {
+ goto next_mark;
+ }
+
+ if (mark.pos.row > currow) {
+ count -= count_remove;
+ count_remove = 0;
+ currow = mark.pos.row;
+ }
+
+ if (!mt_paired(mark)) {
+ if (mark.pos.row >= row) {
+ count++;
+ if (count > signcols) {
+ signcols = count;
+ if (signcols >= max) {
+ return max;
+ }
+ }
+ count_remove++;
+ }
+ goto next_mark;
+ }
+
+ mtpos_t altpos = marktree_get_altpos(buf->b_marktree, mark, NULL);
+
+ if (mt_end(mark)) {
+ if (mark.pos.row >= row && altpos.row <= end_row) {
+ count_remove++;
+ }
+ } else {
+ if (altpos.row >= row) {
+ count++;
+ if (count > signcols) {
+ signcols = count;
+ if (signcols >= max) {
+ return max;
+ }
+ }
+ }
+ }
+
+next_mark:
+ marktree_itr_next(buf->b_marktree, itr);
+ }
+
+ return signcols;
+}
+
void decor_redraw_end(DecorState *state)
{
state->buf = NULL;
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 02472d09e4..7e6dbdf1a7 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -47,13 +47,18 @@ struct Decoration {
bool virt_text_hide;
bool hl_eol;
bool virt_lines_above;
- // TODO(bfredl): style, signs, etc
+ // TODO(bfredl): style, etc
DecorPriority priority;
int col; // fixed col value, like win_col
int virt_text_width; // width of virt_text
+ char_u *sign_text;
+ int sign_hl_id;
+ int number_hl_id;
+ int line_hl_id;
+ int cursorline_hl_id;
};
#define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, kHlModeUnknown, \
- false, false, false, DECOR_PRIORITY_BASE, 0, 0 }
+ false, false, false, DECOR_PRIORITY_BASE, 0, 0, NULL, 0, 0, 0, 0 }
typedef struct {
int start_row;
@@ -75,6 +80,7 @@ typedef struct {
int col_until;
int current;
int eol_col;
+ bool has_sign_decor;
} DecorState;
typedef struct {
@@ -98,6 +104,15 @@ EXTERN bool provider_active INIT(= false);
LUA_NOREF, LUA_NOREF, LUA_NOREF, \
LUA_NOREF, -1 }
+static inline bool decor_has_sign(Decoration *decor)
+{
+ return decor->sign_text
+ || decor->sign_hl_id
+ || decor->number_hl_id
+ || decor->line_hl_id
+ || decor->cursorline_hl_id;
+}
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "decoration.h.generated.h"
#endif
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c
index 48e57e20e1..e1f1ed7d13 100644
--- a/src/nvim/extmark.c
+++ b/src/nvim/extmark.c
@@ -67,7 +67,9 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col
uint8_t decor_level = kDecorLevelNone; // no decor
if (decor) {
- if (kv_size(decor->virt_text) || kv_size(decor->virt_lines)) {
+ if (kv_size(decor->virt_text)
+ || kv_size(decor->virt_lines)
+ || decor_has_sign(decor)) {
decor_full = true;
decor = xmemdup(decor, sizeof *decor);
}
@@ -142,6 +144,9 @@ revised:
if (kv_size(decor->virt_lines)) {
buf->b_virt_line_blocks++;
}
+ if (decor_has_sign(decor)) {
+ buf->b_signs++;
+ }
decor_redraw(buf, row, end_row > -1 ? end_row : row, decor);
}
diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h
index 30f5aacebc..440816930b 100644
--- a/src/nvim/marktree.h
+++ b/src/nvim/marktree.h
@@ -87,6 +87,11 @@ static inline bool mt_end(mtkey_t key)
return key.flags & MT_FLAG_END;
}
+static inline bool mt_start(mtkey_t key)
+{
+ return mt_paired(key) && !mt_end(key);
+}
+
static inline bool mt_right(mtkey_t key)
{
return key.flags & MT_FLAG_RIGHT_GRAVITY;
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 7fafe3dd6e..ee1acd8d03 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -2208,6 +2208,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
buf_T *buf = wp->w_buffer;
bool end_fill = (lnum == buf->b_ml.ml_line_count+1);
+ has_decor = decor_redraw_line(buf, lnum-1, &decor_state);
+
if (!number_only) {
// To speed up the loop below, set extra_check when there is linebreak,
// trailing white space and/or syntax processing to be done.
@@ -2229,9 +2231,6 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
}
}
- has_decor = decor_redraw_line(wp->w_buffer, lnum-1,
- &decor_state);
-
for (size_t k = 0; k < kv_size(*providers); k++) {
DecorProvider *p = kv_A(*providers, k);
if (p && p->redraw_line != LUA_NOREF) {
@@ -2460,6 +2459,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc
memset(sattrs, 0, sizeof(sattrs));
num_signs = buf_get_signattrs(wp->w_buffer, lnum, sattrs);
+ if (decor_state.has_sign_decor) {
+ decor_redraw_signs(buf, &decor_state, lnum-1, &num_signs, sattrs);
+ }
// If this line has a sign with line highlighting set line_attr.
// TODO(bfredl, vigoux): this should not take priority over decoration!
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index 8b41781c98..ad48003f3b 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -506,6 +506,8 @@ int buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T sattrs[])
if (sp->sn_num_hl != 0) {
sattr.sat_numhl = syn_id2attr(sp->sn_num_hl);
}
+ // Store the priority so we can mesh in extmark signs later
+ sattr.sat_prio = sign->se_priority;
}
sattrs[nr_matches] = sattr;
diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h
index c734502878..6d28c1849a 100644
--- a/src/nvim/sign_defs.h
+++ b/src/nvim/sign_defs.h
@@ -40,6 +40,7 @@ typedef struct sign_attrs_S {
int sat_linehl;
int sat_culhl;
int sat_numhl;
+ int sat_prio; // Used for inserting extmark signs
} sign_attrs_T;
#define SIGN_SHOW_MAX 9
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index 1575cab591..ae5d34fa7a 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -1362,3 +1362,276 @@ if (h->n_buckets < new_n_buckets) { // expand
end)
end)
+
+describe('decorations: signs', function()
+ local screen, ns
+ before_each(function()
+ clear()
+ screen = Screen.new(50, 10)
+ screen:attach()
+ screen:set_default_attr_ids {
+ [1] = {foreground = Screen.colors.Blue4, background = Screen.colors.Grey};
+ [2] = {foreground = Screen.colors.Blue1, bold = true};
+ }
+
+ ns = meths.create_namespace 'test'
+ meths.win_set_option(0, 'signcolumn', 'auto:9')
+ end)
+
+ local example_text = [[
+l1
+l2
+l3
+l4
+l5
+]]
+
+ it('can add a single sign (no end row)', function()
+ insert(example_text)
+ feed 'gg'
+
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S'})
+
+ screen:expect{grid=[[
+ {1: }^l1 |
+ S l2 |
+ {1: }l3 |
+ {1: }l4 |
+ {1: }l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+
+ end)
+
+ it('can add a single sign (with end row)', function()
+ insert(example_text)
+ feed 'gg'
+
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S', end_row=1})
+
+ screen:expect{grid=[[
+ {1: }^l1 |
+ S l2 |
+ {1: }l3 |
+ {1: }l4 |
+ {1: }l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+
+ end)
+
+ it('can add multiple signs (single extmark)', function()
+ pending('TODO(lewis6991): Support ranged signs')
+ insert(example_text)
+ feed 'gg'
+
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S', end_row = 2})
+
+ screen:expect{grid=[[
+ {1: }^l1 |
+ S l2 |
+ S l3 |
+ {1: }l4 |
+ {1: }l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+
+ end)
+
+ it('can add multiple signs (multiple extmarks)', function()
+ pending('TODO(lewis6991): Support ranged signs')
+ insert(example_text)
+ feed'gg'
+
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S1'})
+ meths.buf_set_extmark(0, ns, 3, -1, {sign_text='S2', end_row = 4})
+
+ screen:expect{grid=[[
+ {1: }^l1 |
+ S1l2 |
+ {1: }l3 |
+ S2l4 |
+ S2l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+
+ end)
+
+ it('can add multiple signs (multiple extmarks) 2', function()
+ insert(example_text)
+ feed 'gg'
+
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S1'})
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S2'})
+
+ screen:expect{grid=[[
+ {1: }^l1 |
+ S2S1l2 |
+ {1: }l3 |
+ {1: }l4 |
+ {1: }l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+
+ -- TODO(lewis6991): Support ranged signs
+ -- meths.buf_set_extmark(1, ns, 1, -1, {sign_text='S3', end_row = 2})
+
+ -- screen:expect{grid=[[
+ -- {1: }^l1 |
+ -- S3S2S1l2 |
+ -- S3{1: }l3 |
+ -- {1: }l4 |
+ -- {1: }l5 |
+ -- {1: } |
+ -- {2:~ }|
+ -- {2:~ }|
+ -- {2:~ }|
+ -- |
+ -- ]]}
+
+ end)
+
+ it('can add multiple signs (multiple extmarks) 3', function()
+ pending('TODO(lewis6991): Support ranged signs')
+
+ insert(example_text)
+ feed 'gg'
+
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S1', end_row=2})
+ meths.buf_set_extmark(0, ns, 2, -1, {sign_text='S2', end_row=3})
+
+ screen:expect{grid=[[
+ {1: }^l1 |
+ S1{1: }l2 |
+ S2S1l3 |
+ S2{1: }l4 |
+ {1: }l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+ end)
+
+ it('can add multiple signs (multiple extmarks) 4', function()
+ insert(example_text)
+ feed 'gg'
+
+ meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S1', end_row=0})
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S2', end_row=1})
+
+ screen:expect{grid=[[
+ S1^l1 |
+ S2l2 |
+ {1: }l3 |
+ {1: }l4 |
+ {1: }l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+ end)
+
+ it('works with old signs', function()
+ insert(example_text)
+ feed 'gg'
+
+ helpers.command('sign define Oldsign text=x')
+ helpers.command([[exe 'sign place 42 line=2 name=Oldsign buffer=' . bufnr('')]])
+
+ meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S1'})
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S2'})
+ meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S4'})
+ meths.buf_set_extmark(0, ns, 2, -1, {sign_text='S5'})
+
+ screen:expect{grid=[[
+ S4S1^l1 |
+ S2x l2 |
+ S5{1: }l3 |
+ {1: }l4 |
+ {1: }l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+ end)
+
+ it('works with old signs (with range)', function()
+ pending('TODO(lewis6991): Support ranged signs')
+ insert(example_text)
+ feed 'gg'
+
+ helpers.command('sign define Oldsign text=x')
+ helpers.command([[exe 'sign place 42 line=2 name=Oldsign buffer=' . bufnr('')]])
+
+ meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S1'})
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='S2'})
+ meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S3', end_row = 4})
+ meths.buf_set_extmark(0, ns, 0, -1, {sign_text='S4'})
+ meths.buf_set_extmark(0, ns, 2, -1, {sign_text='S5'})
+
+ screen:expect{grid=[[
+ S3S4S1^l1 |
+ S2S3x l2 |
+ S5S3{1: }l3 |
+ S3{1: }l4 |
+ S3{1: }l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+ end)
+
+ it('can add a ranged sign (with start out of view)', function()
+ pending('TODO(lewis6991): Support ranged signs')
+
+ insert(example_text)
+ command 'set signcolumn=yes:2'
+ feed 'gg'
+ feed '2<C-e>'
+
+ meths.buf_set_extmark(0, ns, 1, -1, {sign_text='X', end_row=3})
+
+ screen:expect{grid=[[
+ X {1: }^l3 |
+ X {1: }l4 |
+ {1: }l5 |
+ {1: } |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]]}
+
+ end)
+
+end)