refactor(sign): move legacy signs to extmarks

Problem:  The legacy signlist data structures and associated functions are
          redundant since the introduction of extmark signs.
Solution: Store signs defined through the legacy commands in a hashmap, placed
          signs in the extmark tree. Replace signlist associated functions.

Usage of the legacy sign commands should yield no change in behavior with the
exception of:
  - "orphaned signs" are now always removed when the line it is placed on is
    deleted. This used to depend on the value of 'signcolumn'.
  - It is no longer possible to place multiple signs with the same identifier
    in a single group on multiple lines. This will now move the sign instead.

Moreover, both signs placed through the legacy sign commands and through
|nvim_buf_set_extmark()|:
  - Will show up in both |sign-place| and |nvim_buf_get_extmarks()|.
  - Are displayed by increasing sign identifier, left to right.
    Extmark signs used to be ordered decreasingly as opposed to legacy signs.
This commit is contained in:
Luuk van Baal 2023-11-11 00:52:50 +01:00
parent ba58c6f8a4
commit c4afb9788c
33 changed files with 768 additions and 1899 deletions

View File

@ -2566,8 +2566,8 @@ nvim_buf_get_extmark_by_id({buffer}, {ns_id}, {id}, {opts})
*nvim_buf_get_extmarks()*
nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {*opts})
Gets |extmarks| in "traversal order" from a |charwise| region defined by
buffer positions (inclusive, 0-indexed |api-indexing|).
Gets |extmarks| (including |signs|) in "traversal order" from a |charwise|
region defined by buffer positions (inclusive, 0-indexed |api-indexing|).
Region can be given as (row,col) tuples, or valid extmark ids (whose
positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1)

View File

@ -80,6 +80,14 @@ The following changes may require adaptations in user config or plugins.
the option is set (e.g. using |:set| or |nvim_set_option_value()|) without a
scope, which means they now behave the same way as string options.
• Signs placed through the legacy |sign-commands| are now stored and displayed
as |extmarks| internally. Along with the following changes:
• A sign placed twice in the same group with the same identifier will be moved.
• Legacy signs are always deleted along with the line it is placed on.
• Legacy and extmark signs will show up in both |:sign-place-list| and |nvim_buf_get_extmarks()|.
• Legacy and extmark signs are displayed and listed with the same priority:
line number -> priority -> sign id -> recently placed
==============================================================================
NEW FEATURES *news-features*

View File

@ -5624,12 +5624,6 @@ A jump table for the options with a short description can be found at |Q_op|.
"number" display signs in the 'number' column. If the number
column is not present, then behaves like "auto".
Note regarding "orphaned signs": with signcolumn numbers higher than
1, deleting lines will also remove the associated signs automatically,
in contrast to the default Vim behavior of keeping and grouping them.
This is done in order for the signcolumn appearance not appear weird
during line deletion.
*'smartcase'* *'scs'* *'nosmartcase'* *'noscs'*
'smartcase' 'scs' boolean (default off)
global

View File

@ -53,11 +53,10 @@ If 'cursorline' is enabled, then the CursorLineSign highlight group is used
Each placed sign is identified by a number called the sign identifier. This
identifier is used to jump to the sign or to remove the sign. The identifier
is assigned when placing the sign using the |:sign-place| command or the
|sign_place()| function. Each sign identifier should be a unique number. If
multiple placed signs use the same identifier, then jumping to or removing a
sign becomes unpredictable. To avoid overlapping identifiers, sign groups can
be used. The |sign_place()| function can be called with a zero sign identifier
to allocate the next available identifier.
|sign_place()| function. Each sign identifier should be a unique number.
Placing the same identifier twice will move the previously placed sign. The
|sign_place()| function can be called with a zero sign identifier to allocate
the next available identifier.
*sign-group*
Each placed sign can be assigned to either the global group or a named group.
@ -77,9 +76,8 @@ When two signs with the same priority are present, and one has an icon or text
in the signcolumn while the other has line highlighting, then both are
displayed.
When the line on which the sign is placed is deleted, the sign is moved to the
next line (or the last line of the buffer, if there is no next line). When
the delete is undone the sign does not move back.
When the line on which the sign is placed is deleted, the sign is removed along
with it.
==============================================================================
2. Commands *sign-commands* *:sig* *:sign*
@ -177,11 +175,8 @@ See |sign_place()| for the equivalent Vim script function.
space is ignored.
The sign is remembered under {id}, this can be used for
further manipulation. {id} must be a number.
It's up to the user to make sure the {id} is used only once in
each file (if it's used several times unplacing will also have
to be done several times and making changes may not work as
expected).
further manipulation. {id} must be a number. Placing the
same {id} multiple times will move the sign.
The following optional sign attributes can be specified before
"file=":

View File

@ -340,6 +340,7 @@ Shell:
Signs:
Signs are removed if the associated line is deleted.
Signs placed twice with the same identifier in the same group are moved.
Startup:
|-e| and |-es| invoke the same "improved Ex mode" as -E and -Es.

View File

@ -317,8 +317,8 @@ function vim.api.nvim_buf_get_commands(buffer, opts) end
--- @return integer[]
function vim.api.nvim_buf_get_extmark_by_id(buffer, ns_id, id, opts) end
--- Gets `extmarks` in "traversal order" from a `charwise` region defined by
--- buffer positions (inclusive, 0-indexed `api-indexing`).
--- Gets `extmarks` (including `signs`) in "traversal order" from a `charwise`
--- region defined by buffer positions (inclusive, 0-indexed `api-indexing`).
--- Region can be given as (row,col) tuples, or valid extmark ids (whose
--- positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1)
--- respectively, thus the following are equivalent:

View File

@ -5923,12 +5923,6 @@ vim.go.siso = vim.go.sidescrolloff
--- "number" display signs in the 'number' column. If the number
--- column is not present, then behaves like "auto".
---
--- Note regarding "orphaned signs": with signcolumn numbers higher than
--- 1, deleting lines will also remove the associated signs automatically,
--- in contrast to the default Vim behavior of keeping and grouping them.
--- This is done in order for the signcolumn appearance not appear weird
--- during line deletion.
---
--- @type string
vim.o.signcolumn = "auto"
vim.o.scl = vim.o.signcolumn

View File

@ -22,6 +22,7 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/pos.h"
#include "nvim/sign.h"
#include "nvim/strings.h"
#include "nvim/vim.h"
@ -81,7 +82,7 @@ Dictionary nvim_get_namespaces(void)
return retval;
}
const char *describe_ns(NS ns_id)
const char *describe_ns(NS ns_id, const char *unknown)
{
String name;
handle_T id;
@ -90,7 +91,7 @@ const char *describe_ns(NS ns_id)
return name.data;
}
})
return "(UNKNOWN PLUGIN)";
return unknown;
}
// Is the Namespace in use?
@ -314,8 +315,8 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
return extmark_to_array(&extmark, false, details, hl_name);
}
/// Gets |extmarks| in "traversal order" from a |charwise| region defined by
/// buffer positions (inclusive, 0-indexed |api-indexing|).
/// Gets |extmarks| (including |signs|) in "traversal order" from a |charwise|
/// region defined by buffer positions (inclusive, 0-indexed |api-indexing|).
///
/// Region can be given as (row,col) tuples, or valid extmark ids (whose
/// positions define the bounds). 0 and -1 are understood as (0,0) and (-1,-1)
@ -750,7 +751,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
}
if (HAS_KEY(opts, set_extmark, sign_text)) {
VALIDATE_S(init_sign_text(&decor.sign_text, opts->sign_text.data),
VALIDATE_S(init_sign_text(NULL, &decor.sign_text, opts->sign_text.data),
"sign_text", "", {
goto error;
});
@ -1150,41 +1151,6 @@ static bool extmark_get_index_from_obj(buf_T *buf, Integer ns_id, Object obj, in
});
}
}
// adapted from sign.c:sign_define_init_text.
// TODO(lewis6991): Consider merging
static int init_sign_text(char **sign_text, char *text)
{
char *s;
char *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 = xstrnsave(text, len);
if (cells == 1) {
STRCPY(*sign_text + len - 1, " ");
}
return OK;
}
VirtText parse_virt_text(Array chunks, Error *err, int *width)
{

View File

@ -2196,13 +2196,12 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
}
}
if (statuscol_lnum) {
HlPriId line = { 0 };
HlPriId cul = { 0 };
HlPriId num = { 0 };
int line_id = 0;
int cul_id = 0;
int num_id = 0;
linenr_T lnum = statuscol_lnum;
int num_signs = buf_get_signattrs(wp->w_buffer, lnum, sattrs, &num, &line, &cul);
decor_redraw_signs(wp->w_buffer, lnum - 1, &num_signs, sattrs, &num, &line, &cul);
wp->w_scwidth = win_signcol_count(wp);
decor_redraw_signs(wp, wp->w_buffer, lnum - 1, sattrs, &line_id, &cul_id, &num_id);
statuscol.sattrs = sattrs;
statuscol.foldinfo = fold_info(wp, lnum);
@ -2215,9 +2214,9 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
statuscol.use_cul = lnum == wp->w_cursorline && (wp->w_p_culopt_flags & CULOPT_NBR);
}
statuscol.sign_cul_id = statuscol.use_cul ? cul.hl_id : 0;
if (num.hl_id) {
stc_hl_id = num.hl_id;
statuscol.sign_cul_id = statuscol.use_cul ? cul_id : 0;
if (num_id) {
stc_hl_id = num_id;
} else if (statuscol.use_cul) {
stc_hl_id = HLF_CLN + 1;
} else if (wp->w_p_rnu) {

View File

@ -917,7 +917,6 @@ static void free_buffer_stuff(buf_T *buf, int free_flags)
buf_init_changedtick(buf);
}
uc_clear(&buf->b_ucmds); // clear local user commands
buf_delete_signs(buf, "*"); // delete any signs
extmark_free_all(buf); // delete any extmarks
map_clear_mode(buf, MAP_ALL_MODES, true, false); // clear local mappings
map_clear_mode(buf, MAP_ALL_MODES, true, true); // clear local abbrevs
@ -4021,62 +4020,6 @@ char *buf_spname(buf_T *buf)
return NULL;
}
static int buf_signcols_inner(buf_T *buf, int maximum)
{
sign_entry_T *sign; // a sign in the sign list
int signcols = 0;
int linesum = 0;
linenr_T curline = 0;
buf->b_signcols.sentinel = 0;
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);
}
curline = sign->se_lnum;
if (linesum > signcols) {
signcols = linesum;
buf->b_signcols.sentinel = curline;
if (signcols >= maximum) {
return maximum;
}
}
linesum = 0;
}
if (sign->se_has_text_or_icon) {
linesum++;
}
}
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;
buf->b_signcols.sentinel = curline;
if (signcols >= maximum) {
return maximum;
}
}
return signcols;
}
/// Invalidate the signcolumn if needed after deleting
/// signs between line1 and line2 (inclusive).
///
@ -4106,18 +4049,18 @@ void buf_signcols_del_check(buf_T *buf, linenr_T line1, linenr_T line2)
///
/// @param buf buffer to check
/// @param added sign being added
void buf_signcols_add_check(buf_T *buf, sign_entry_T *added)
void buf_signcols_add_check(buf_T *buf, linenr_T lnum)
{
if (!buf->b_signcols.valid) {
return;
}
if (!added || !buf->b_signcols.sentinel) {
if (!buf->b_signcols.sentinel) {
buf->b_signcols.valid = false;
return;
}
if (added->se_lnum == buf->b_signcols.sentinel) {
if (lnum == buf->b_signcols.sentinel) {
if (buf->b_signcols.size == buf->b_signcols.max) {
buf->b_signcols.max++;
}
@ -4126,42 +4069,32 @@ void buf_signcols_add_check(buf_T *buf, sign_entry_T *added)
return;
}
sign_entry_T *s;
int signcols = decor_signcols(buf, lnum - 1, lnum - 1, SIGN_SHOW_MAX);
// Get first sign for added lnum
for (s = added; s->se_prev && s->se_lnum == s->se_prev->se_lnum; s = s->se_prev) {}
// Count signs for lnum
int linesum = 1;
for (; s->se_next && s->se_lnum == s->se_next->se_lnum; s = s->se_next) {
linesum++;
}
linesum += decor_signcols(buf, &decor_state, (int)s->se_lnum - 1, (int)s->se_lnum - 1,
SIGN_SHOW_MAX - linesum);
if (linesum > buf->b_signcols.size) {
buf->b_signcols.size = linesum;
buf->b_signcols.max = linesum;
buf->b_signcols.sentinel = added->se_lnum;
if (signcols > buf->b_signcols.size) {
buf->b_signcols.size = signcols;
buf->b_signcols.max = signcols;
buf->b_signcols.sentinel = lnum;
redraw_buf_later(buf, UPD_NOT_VALID);
}
}
int buf_signcols(buf_T *buf, int maximum)
int buf_signcols(buf_T *buf, int max)
{
// The maximum can be determined from 'signcolumn' which is window scoped so
// need to invalidate signcols if the maximum is greater than the previous
// maximum.
if (maximum > buf->b_signcols.max) {
// (valid) maximum.
if (buf->b_signcols.max && max > buf->b_signcols.max) {
buf->b_signcols.valid = false;
}
if (!buf->b_signcols.valid) {
int signcols = buf_signcols_inner(buf, maximum);
buf->b_signcols.sentinel = 0;
int signcols = decor_signcols(buf, 0, (int)buf->b_ml.ml_line_count - 1, max);
// Check if we need to redraw
if (signcols != buf->b_signcols.size) {
buf->b_signcols.size = signcols;
buf->b_signcols.max = maximum;
buf->b_signcols.max = max;
redraw_buf_later(buf, UPD_NOT_VALID);
}

View File

@ -708,7 +708,6 @@ struct file_buffer {
// normally points to this, but some windows
// may use a different synblock_T.
sign_entry_T *b_signlist; // list of placed signs
struct {
int size; // last calculated number of sign columns
bool valid; // calculated sign columns is valid

View File

@ -11,7 +11,7 @@
#include "nvim/memory.h"
#include "nvim/move.h"
#include "nvim/pos.h"
#include "nvim/sign_defs.h"
#include "nvim/sign.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "decoration.c.generated.h"
@ -92,6 +92,8 @@ void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor)
}
}
static int sign_add_id = 0;
void decor_add(buf_T *buf, int row, int row2, Decoration *decor, bool hl_id)
{
if (decor) {
@ -102,12 +104,12 @@ void decor_add(buf_T *buf, int row, int row2, Decoration *decor, bool hl_id)
buf->b_virt_line_blocks++;
}
if (decor_has_sign(decor)) {
decor->sign_add_id = sign_add_id++;
buf->b_signs++;
}
if (decor->sign_text) {
buf->b_signs_with_text++;
// TODO(lewis6991): smarter invalidation
buf_signcols_add_check(buf, NULL);
buf_signcols_add_check(buf, row + 1);
}
}
if (decor || hl_id) {
@ -152,6 +154,7 @@ void decor_clear(Decoration *decor)
}
kv_destroy(decor->virt_lines);
xfree(decor->sign_text);
xfree(decor->sign_name);
}
void decor_free(Decoration *decor)
@ -429,107 +432,73 @@ next_mark:
return attr;
}
void decor_redraw_signs(buf_T *buf, int row, int *num_signs, SignTextAttrs sattrs[],
HlPriId *num_id, HlPriId *line_id, HlPriId *cul_id)
/// Return the sign attributes on the currently refreshed row.
///
/// @param[out] sattrs Output array for sign text and texthl id
/// @param[out] line_attr Highest priority linehl id
/// @param[out] cul_attr Highest priority culhl id
/// @param[out] num_attr Highest priority numhl id
void decor_redraw_signs(win_T *wp, buf_T *buf, int row, SignTextAttrs sattrs[], int *line_id,
int *cul_id, int *num_id)
{
if (!buf->b_signs) {
return;
}
MarkTreeIter itr[1] = { 0 };
marktree_itr_get(buf->b_marktree, row, 0, itr);
// TODO(bfredl): integrate with main decor loop.
if (!marktree_itr_get_overlap(buf->b_marktree, row, 0, itr)) {
MarkTreeIter itr[1];
if (!buf->b_signs || !marktree_itr_get_overlap(buf->b_marktree, row, 0, itr)) {
return;
}
MTPair pair;
int num_text = 0;
kvec_t(MTKey) signs = KV_INITIAL_VALUE;
// TODO(bfredl): integrate with main decor loop.
while (marktree_itr_step_overlap(buf->b_marktree, itr, &pair)) {
if (mt_invalid(pair.start) || marktree_decor_level(pair.start) < kDecorLevelVisible) {
continue;
if (!mt_invalid(pair.start) && pair.start.decor_full && decor_has_sign(pair.start.decor_full)) {
pair.start.pos.row = row;
num_text += (pair.start.decor_full->sign_text != NULL);
kv_push(signs, pair.start);
}
Decoration *decor = pair.start.decor_full;
if (!decor || !decor_has_sign(decor)) {
continue;
}
decor_to_sign(decor, num_signs, sattrs, num_id, line_id, cul_id);
}
while (true) {
while (itr->x) {
MTKey mark = marktree_itr_current(itr);
if (mark.pos.row < 0 || mark.pos.row > row) {
if (mark.pos.row != row) {
break;
}
if (mt_end(mark) || mt_invalid(mark) || marktree_decor_level(mark) < kDecorLevelVisible) {
goto next_mark;
if (!mt_end(mark) && !mt_invalid(mark) && mark.decor_full && decor_has_sign(mark.decor_full)) {
num_text += (mark.decor_full->sign_text != NULL);
kv_push(signs, mark);
}
Decoration *decor = mark.decor_full;
if (!decor || !decor_has_sign(decor)) {
goto next_mark;
}
decor_to_sign(decor, num_signs, sattrs, num_id, line_id, cul_id);
next_mark:
marktree_itr_next(buf->b_marktree, itr);
}
}
static void decor_to_sign(Decoration *decor, int *num_signs, SignTextAttrs sattrs[],
HlPriId *num_id, HlPriId *line_id, HlPriId *cul_id)
{
if (decor->sign_text) {
int j;
for (j = (*num_signs); j > 0; j--) {
if (sattrs[j - 1].priority >= decor->priority) {
break;
if (kv_size(signs)) {
int width = (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u') ? 1 : wp->w_scwidth;
int idx = MIN(width, num_text) - 1;
qsort((void *)&kv_A(signs, 0), kv_size(signs), sizeof(MTKey), sign_cmp);
for (size_t i = 0; i < kv_size(signs); i++) {
Decoration *decor = kv_A(signs, i).decor_full;
if (idx >= 0 && decor->sign_text) {
sattrs[idx].text = decor->sign_text;
sattrs[idx--].hl_id = decor->sign_hl_id;
}
if (j < SIGN_SHOW_MAX) {
sattrs[j] = sattrs[j - 1];
if (*num_id == 0) {
*num_id = decor->number_hl_id;
}
if (*line_id == 0) {
*line_id = decor->line_hl_id;
}
if (*cul_id == 0) {
*cul_id = decor->cursorline_hl_id;
}
}
if (j < SIGN_SHOW_MAX) {
sattrs[j] = (SignTextAttrs) {
.text = decor->sign_text,
.hl_id = decor->sign_hl_id,
.priority = decor->priority
};
(*num_signs)++;
}
}
struct { HlPriId *dest; int hl; } cattrs[] = {
{ line_id, decor->line_hl_id },
{ num_id, decor->number_hl_id },
{ cul_id, decor->cursorline_hl_id },
{ NULL, -1 },
};
for (int i = 0; cattrs[i].dest; i++) {
if (cattrs[i].hl != 0 && decor->priority >= cattrs[i].dest->priority) {
*cattrs[i].dest = (HlPriId) {
.hl_id = cattrs[i].hl,
.priority = decor->priority
};
}
kv_destroy(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 decor_signcols(buf_T *buf, 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_with_text >= (size_t)max) {
return max;
}
@ -538,66 +507,41 @@ int decor_signcols(buf_T *buf, DecorState *state, int row, int end_row, int max)
return 0;
}
MarkTreeIter itr[1] = { 0 };
marktree_itr_get(buf->b_marktree, 0, -1, itr);
while (true) {
MTKey mark = marktree_itr_current(itr);
if (mark.pos.row < 0 || mark.pos.row > end_row) {
break;
int signcols = 0; // highest value of count
for (int currow = row; currow <= end_row; currow++) {
MarkTreeIter itr[1];
if (!marktree_itr_get_overlap(buf->b_marktree, currow, 0, itr)) {
continue;
}
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) {
int count = 0;
MTPair pair;
while (marktree_itr_step_overlap(buf->b_marktree, itr, &pair)) {
if (!mt_invalid(pair.start) && pair.start.decor_full && pair.start.decor_full->sign_text) {
count++;
if (count > signcols) {
signcols = count;
if (signcols >= max) {
return max;
}
}
count_remove++;
}
goto next_mark;
}
MTPos altpos = marktree_get_altpos(buf->b_marktree, mark, NULL);
if (mt_end(mark)) {
if (mark.pos.row >= row && altpos.row <= end_row) {
count_remove++;
while (itr->x) {
MTKey mark = marktree_itr_current(itr);
if (mark.pos.row != currow) {
break;
}
} else {
if (altpos.row >= row) {
if (!mt_invalid(mark) && !mt_end(mark) && mark.decor_full && mark.decor_full->sign_text) {
count++;
if (count > signcols) {
signcols = count;
if (signcols >= max) {
return max;
}
}
}
marktree_itr_next(buf->b_marktree, itr);
}
next_mark:
marktree_itr_next(buf->b_marktree, itr);
if (count > signcols) {
if (row != end_row) {
buf->b_signcols.sentinel = currow + 1;
}
if (count >= max) {
return max;
}
signcols = count;
}
}
return signcols;

View File

@ -60,7 +60,9 @@ struct Decoration {
int col; // fixed col value, like win_col
int virt_text_width; // width of virt_text
char *sign_text;
char *sign_name;
int sign_hl_id;
int sign_add_id;
int number_hl_id;
int line_hl_id;
int cursorline_hl_id;
@ -71,7 +73,7 @@ struct Decoration {
};
#define DECORATION_INIT { KV_INITIAL_VALUE, KV_INITIAL_VALUE, 0, kVTEndOfLine, \
kHlModeUnknown, false, false, false, false, kNone, \
DECOR_PRIORITY_BASE, 0, 0, NULL, 0, 0, 0, 0, 0, false }
DECOR_PRIORITY_BASE, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, false }
typedef struct {
int start_row;

View File

@ -25,7 +25,7 @@ static kvec_t(DecorProvider) decor_providers = KV_INITIAL_VALUE;
static void decor_provider_error(DecorProvider *provider, const char *name, const char *msg)
{
const char *ns_name = describe_ns(provider->ns_id);
const char *ns_name = describe_ns(provider->ns_id, "(UNKNOWN PLUGIN)");
ELOG("error in provider %s.%s: %s", ns_name, name, msg);
msg_schedule_semsg_multiline("Error in decoration provider %s.%s:\n%s", ns_name, name, msg);
}

View File

@ -460,85 +460,37 @@ size_t fill_foldcolumn(char *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum, i
/// 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.
/// 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)
{
// Draw cells with the sign value or blank.
wlv->c_extra = ' ';
SignTextAttrs sattr = wlv->sattrs[sign_idx];
wlv->c_final = NUL;
if (nrcol) {
wlv->n_extra = number_width(wp) + 1;
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;
} else {
if (use_cursor_line_highlight(wp, wlv->lnum)) {
wlv->char_attr = win_hl_attr(wp, HLF_CLS);
} else {
wlv->char_attr = win_hl_attr(wp, HLF_SC);
}
wlv->n_extra = win_signcol_width(wp);
}
if (wlv->row == wlv->startrow + wlv->filler_lines && wlv->filler_todo <= 0) {
SignTextAttrs *sattr = sign_get_attr(sign_idx, wlv->sattrs, wp->w_scwidth);
if (sattr != NULL) {
wlv->p_extra = sattr->text;
if (wlv->p_extra != NULL) {
wlv->c_extra = NUL;
wlv->c_final = NUL;
if (nrcol) {
int width = number_width(wp) - 2;
size_t n;
for (n = 0; (int)n < width; n++) {
wlv->extra[n] = ' ';
}
wlv->extra[n] = NUL;
snprintf(wlv->extra + n, sizeof(wlv->extra) - n, "%s ", wlv->p_extra);
wlv->p_extra = wlv->extra;
wlv->n_extra = (int)strlen(wlv->p_extra);
} else {
size_t symbol_blen = strlen(wlv->p_extra);
// TODO(oni-link): Is sign text already extended to
// full cell width?
assert((size_t)win_signcol_width(wp) >= mb_string2cells(wlv->p_extra));
// symbol(s) bytes + (filling spaces) (one byte each)
wlv->n_extra = (int)symbol_blen + win_signcol_width(wp) -
(int)mb_string2cells(wlv->p_extra);
assert(sizeof(wlv->extra) > symbol_blen);
memset(wlv->extra, ' ', sizeof(wlv->extra));
memcpy(wlv->extra, wlv->p_extra, symbol_blen);
wlv->p_extra = wlv->extra;
wlv->p_extra[wlv->n_extra] = NUL;
}
}
if (use_cursor_line_highlight(wp, wlv->lnum) && sign_cul_attr > 0) {
wlv->char_attr = sign_cul_attr;
} else {
wlv->char_attr = sattr->hl_id ? syn_id2attr(sattr->hl_id) : 0;
}
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);
}
}
}
/// Returns width of the signcolumn that should be used for the whole window
///
/// @param wp window we want signcolumn width from
/// @return max width of signcolumn (cell unit)
///
/// @note Returns a constant for now but hopefully we can improve neovim so that
/// the returned value width adapts to the maximum number of marks to draw
/// for the window
/// TODO(teto)
int win_signcol_width(win_T *wp)
{
// 2 is vim default value
return 2;
}
static inline void get_line_number_str(win_T *wp, linenr_T lnum, char *buf, size_t buf_len)
{
linenr_T num;
@ -602,8 +554,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 num_signs, int sign_idx,
int sign_num_attr, int sign_cul_attr)
static void handle_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;
@ -614,8 +565,8 @@ static void handle_lnum_col(win_T *wp, winlinevars_T *wlv, int num_signs, int si
&& !((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_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u' && num_signs > 0) {
get_sign_display_info(true, wp, wlv, sign_idx, sign_cul_attr);
if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u' && wlv->sattrs[0].text) {
get_sign_display_info(true, wp, wlv, 0, sign_cul_attr);
} else {
// Draw the line number (empty space after wrapping).
if (wlv->row == wlv->startrow + wlv->filler_lines
@ -1333,15 +1284,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
area_highlighting = true;
}
HlPriId line_id = { 0 };
HlPriId sign_cul = { 0 };
HlPriId sign_num = { 0 };
// TODO(bfredl, vigoux): line_attr should not take priority over decoration!
int num_signs = buf_get_signattrs(buf, wlv.lnum, wlv.sattrs, &sign_num, &line_id, &sign_cul);
decor_redraw_signs(buf, wlv.lnum - 1, &num_signs, wlv.sattrs, &sign_num, &line_id, &sign_cul);
int line_attr = 0;
int sign_cul_attr = 0;
int sign_num_attr = 0;
// TODO(bfredl, vigoux): line_attr should not take priority over decoration!
decor_redraw_signs(wp, buf, wlv.lnum - 1, wlv.sattrs, &line_attr, &sign_cul_attr, &sign_num_attr);
statuscol_T statuscol = { 0 };
if (*wp->w_p_stc != NUL) {
// Draw the 'statuscolumn' if option is set.
@ -1350,18 +1298,18 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
statuscol.foldinfo = foldinfo;
statuscol.width = win_col_off(wp) - (cmdwin_type != 0 && wp == curwin);
statuscol.use_cul = use_cursor_line_highlight(wp, lnum);
statuscol.sign_cul_id = statuscol.use_cul ? sign_cul.hl_id : 0;
statuscol.num_attr = sign_num.hl_id > 0 ? syn_id2attr(sign_num.hl_id) : 0;
statuscol.sign_cul_id = statuscol.use_cul ? sign_cul_attr : 0;
statuscol.num_attr = sign_num_attr > 0 ? syn_id2attr(sign_num_attr) : 0;
} else {
if (sign_cul.hl_id > 0) {
sign_cul_attr = syn_id2attr(sign_cul.hl_id);
if (sign_cul_attr > 0) {
sign_cul_attr = syn_id2attr(sign_cul_attr);
}
if (sign_num.hl_id > 0) {
sign_num_attr = syn_id2attr(sign_num.hl_id);
if (sign_num_attr > 0) {
sign_num_attr = syn_id2attr(sign_num_attr);
}
}
if (line_id.hl_id > 0) {
wlv.line_attr = syn_id2attr(line_id.hl_id);
if (line_attr > 0) {
wlv.line_attr = syn_id2attr(line_attr);
}
// Highlight the current line in the quickfix window.
@ -1677,8 +1625,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
wlv.draw_state = WL_SIGN;
if (wp->w_scwidth > 0) {
get_sign_display_info(false, wp, &wlv, sign_idx, sign_cul_attr);
sign_idx++;
if (sign_idx < wp->w_scwidth) {
if (++sign_idx < wp->w_scwidth) {
wlv.draw_state = WL_SIGN - 1;
} else {
sign_idx = 0;
@ -1689,14 +1636,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
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, num_signs, sign_idx, sign_num_attr, sign_cul_attr);
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.hl_id == 0) {
if (sign_num_attr == 0) {
statuscol.num_attr = get_line_number_attr(wp, &wlv);
}
if (statuscol.textp == NULL) {

View File

@ -2558,7 +2558,7 @@ void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, int endr
// draw the sign column
int count = wp->w_scwidth;
if (count > 0) {
n = win_fill_end(wp, ' ', ' ', n, win_signcol_width(wp) * count, row,
n = win_fill_end(wp, ' ', ' ', n, SIGN_WIDTH * count, row,
endrow, win_hl_attr(wp, HLF_SC));
}
// draw the number column
@ -2633,7 +2633,7 @@ int number_width(win_T *wp)
// If 'signcolumn' is set to 'number' and there is a sign to display, then
// the minimal width for the number column is 2.
if (n < 2 && (wp->w_buffer->b_signlist != NULL)
if (n < 2 && wp->w_buffer->b_signs_with_text
&& (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')) {
n = 2;
}

View File

@ -504,7 +504,7 @@ static dict_T *get_buffer_info(buf_T *buf)
}
tv_dict_add_list(dict, S_LEN("windows"), windows);
if (buf->b_signlist != NULL) {
if (buf->b_signs) {
// List of signs placed in this buffer
tv_dict_add_list(dict, S_LEN("signs"), get_buffer_signs(buf));
}

View File

@ -30,7 +30,6 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/buffer_updates.h"
#include "nvim/decoration.h"
@ -161,21 +160,22 @@ static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col)
return true;
}
linenr_T extmark_del_id(buf_T *buf, uint32_t ns_id, uint32_t id)
/// Remove an extmark in "ns_id" by "id"
///
/// @return false on missing id
bool extmark_del_id(buf_T *buf, uint32_t ns_id, uint32_t id)
{
MarkTreeIter it[1] = { 0 };
MTKey key = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, it);
if (!key.id) {
return 0;
MarkTreeIter itr[1] = { 0 };
MTKey key = marktree_lookup_ns(buf->b_marktree, ns_id, id, false, itr);
if (key.id) {
extmark_del(buf, itr, key, false);
}
return extmark_del(buf, it, key, false);
return key.id > 0;
}
/// Remove a (paired) extmark "key" pointed to by "itr"
///
/// @return line number of the deleted mark
linenr_T extmark_del(buf_T *buf, MarkTreeIter *itr, MTKey key, bool restore)
void extmark_del(buf_T *buf, MarkTreeIter *itr, MTKey key, bool restore)
{
assert(key.pos.row >= 0);
@ -195,7 +195,6 @@ linenr_T extmark_del(buf_T *buf, MarkTreeIter *itr, MTKey key, bool restore)
}
// TODO(bfredl): delete it from current undo header, opportunistically?
return key.pos.row + 1;
}
/// Free extmarks in a ns between lines
@ -242,8 +241,8 @@ bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_r
///
/// if upper_lnum or upper_col are negative the buffer
/// will be searched to the start, or end
/// dir can be set to control the order of the array
/// amount = amount of marks to find or -1 for all
/// reverse can be set to control the order of the array
/// amount = amount of marks to find or INT64_MAX for all
ExtmarkInfoArray extmark_get(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_row,
colnr_T u_col, int64_t amount, bool reverse, ExtmarkType type_filter,
bool overlap)
@ -301,7 +300,10 @@ static void push_mark(ExtmarkInfoArray *array, uint32_t ns_id, ExtmarkType type_
if (type_filter != kExtmarkNone) {
Decoration *decor = mark.decor_full;
if (decor && (decor->sign_text || decor->number_hl_id)) {
type_flags |= kExtmarkSign;
type_flags |= (kExtmarkSignHL|kExtmarkSign);
}
if (decor && (decor->line_hl_id || decor->cursorline_hl_id)) {
type_flags |= (kExtmarkSignHL|kExtmarkHighlight);
}
if (decor && decor->virt_text.size) {
type_flags |= kExtmarkVirtText;
@ -309,8 +311,7 @@ static void push_mark(ExtmarkInfoArray *array, uint32_t ns_id, ExtmarkType type_
if (decor && decor->virt_lines.size) {
type_flags |= kExtmarkVirtLines;
}
if ((decor && (decor->line_hl_id || decor->cursorline_hl_id))
|| mark.hl_id) {
if (mark.hl_id) {
type_flags |= kExtmarkHighlight;
}
@ -598,6 +599,20 @@ void extmark_splice_impl(buf_T *buf, int start_row, colnr_T start_col, bcount_t
extmark_splice_delete(buf, start_row, start_col, end_row, end_col, undo);
}
// Move the signcolumn sentinel line
if (buf->b_signs_with_text && buf->b_signcols.sentinel) {
linenr_T se_lnum = buf->b_signcols.sentinel;
if (se_lnum >= start_row) {
if (old_row != 0 && se_lnum > old_row + start_row) {
buf->b_signcols.sentinel += new_row - old_row;
} else if (new_row == 0) {
buf->b_signcols.sentinel = 0;
} else {
buf->b_signcols.sentinel += new_row;
}
}
}
marktree_splice(buf->b_marktree, (int32_t)start_row, start_col,
old_row, old_col,
new_row, new_col);

View File

@ -84,9 +84,10 @@ typedef enum {
typedef enum {
kExtmarkNone = 0x1,
kExtmarkSign = 0x2,
kExtmarkVirtText = 0x4,
kExtmarkVirtLines = 0x8,
kExtmarkHighlight = 0x10,
kExtmarkSignHL = 0x4,
kExtmarkVirtText = 0x8,
kExtmarkVirtLines = 0x10,
kExtmarkHighlight = 0x20,
} ExtmarkType;
// TODO(bfredl): reduce the number of undo action types

View File

@ -474,10 +474,6 @@ EXTERN buf_T *curbuf INIT( = NULL); // currently active buffer
#define FOR_ALL_BUF_WININFO(buf, wip) \
for ((wip) = (buf)->b_wininfo; (wip) != NULL; (wip) = (wip)->wi_next) // NOLINT
// Iterate through all the signs placed in a buffer
#define FOR_ALL_SIGNS_IN_BUF(buf, sign) \
for ((sign) = (buf)->b_signlist; (sign) != NULL; (sign) = (sign)->se_next) // NOLINT
// List of files being edited (global argument list). curwin->w_alist points
// to this when the window is using the global argument list.
EXTERN alist_T global_alist; // global argument list

View File

@ -246,9 +246,3 @@ typedef struct {
} ColorItem;
#define COLOR_ITEM_INITIALIZER { .attr_id = -1, .link_id = -1, .version = -1, \
.is_default = false, .link_global = false }
/// highlight attributes with associated priorities
typedef struct {
int hl_id;
int priority;
} HlPriId;

View File

@ -70,7 +70,6 @@
#include "nvim/quickfix.h"
#include "nvim/runtime.h"
#include "nvim/shada.h"
#include "nvim/sign.h"
#include "nvim/statusline.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
@ -218,8 +217,6 @@ void early_init(mparm_T *paramp)
TIME_MSG("inits 1");
set_lang_var(); // set v:lang and v:ctype
init_signs();
}
#ifdef MAKE_LIB

View File

@ -33,7 +33,6 @@
#include "nvim/os/os.h"
#include "nvim/path.h"
#include "nvim/quickfix.h"
#include "nvim/sign.h"
#include "nvim/strings.h"
#include "nvim/textobject.h"
#include "nvim/vim.h"
@ -1190,8 +1189,6 @@ void mark_adjust_buf(buf_T *buf, linenr_T line1, linenr_T line2, linenr_T amount
if (!found_one) {
buf->b_has_qf_entry &= ~BUF_HAS_LL_ENTRY;
}
sign_mark_adjust(buf, line1, line2, amount, amount_after);
}
if (op != kExtmarkNOOP) {

View File

@ -758,8 +758,7 @@ int win_col_off(win_T *wp)
return ((wp->w_p_nu || wp->w_p_rnu || *wp->w_p_stc != NUL) ?
(number_width(wp) + (*wp->w_p_stc == NUL)) : 0)
+ ((cmdwin_type == 0 || wp != curwin) ? 0 : 1)
+ win_fdccol_count(wp)
+ (win_signcol_count(wp) * win_signcol_width(wp));
+ win_fdccol_count(wp) + (win_signcol_count(wp) * SIGN_WIDTH);
}
int curwin_col_off(void)

View File

@ -6170,7 +6170,7 @@ bool fish_like_shell(void)
/// buffer signs and on user configuration.
int win_signcol_count(win_T *wp)
{
return win_signcol_configured(wp, NULL);
return win_signcol_configured(wp);
}
/// Return true when window "wp" has no sign column.
@ -6182,14 +6182,10 @@ bool win_no_signcol(win_T *wp)
}
/// Return the number of requested sign columns, based on user / configuration.
int win_signcol_configured(win_T *wp, int *is_fixed)
int win_signcol_configured(win_T *wp)
{
const char *scl = wp->w_p_scl;
if (is_fixed) {
*is_fixed = 1;
}
if (win_no_signcol(wp)) {
return 0;
}
@ -6203,11 +6199,6 @@ int win_signcol_configured(win_T *wp, int *is_fixed)
return 1;
}
if (is_fixed) {
// auto or auto:<NUM>
*is_fixed = 0;
}
int minimum = 0, maximum = 1;
if (!strncmp(scl, "auto:", 5)) {

View File

@ -7590,12 +7590,6 @@ return {
number (maximum 9), e.g. "yes:3"
"number" display signs in the 'number' column. If the number
column is not present, then behaves like "auto".
Note regarding "orphaned signs": with signcolumn numbers higher than
1, deleting lines will also remove the associated signs automatically,
in contrast to the default Vim behavior of keeping and grouping them.
This is done in order for the signcolumn appearance not appear weird
during line deletion.
]=],
expand_cb = 'expand_set_signcolumn',
full_name = 'signcolumn',

File diff suppressed because it is too large Load Diff

View File

@ -5,47 +5,23 @@
#include "nvim/pos.h"
#include "nvim/types.h"
// signs: line annotations
// Sign group
typedef struct signgroup_S {
int sg_next_sign_id; ///< next sign id for this group
uint16_t sg_refcount; ///< number of signs in this group
char sg_name[]; ///< sign group name
} signgroup_T;
// Macros to get the sign group structure from the group name
#define SGN_KEY_OFF offsetof(signgroup_T, sg_name)
#define HI2SG(hi) ((signgroup_T *)((hi)->hi_key - SGN_KEY_OFF))
typedef struct sign_entry sign_entry_T;
struct sign_entry {
int se_id; // unique identifier for each placed sign
int se_typenr; // typenr of sign
int se_priority; // priority for highlighting
bool se_has_text_or_icon; // has text or icon
linenr_T se_lnum; // line number which has this sign
signgroup_T *se_group; // sign group
sign_entry_T *se_next; // next entry in a list of signs
sign_entry_T *se_prev; // previous entry -- for easy reordering
};
/// Sign attributes. Used by the screen refresh routines.
typedef struct {
char *text;
int hl_id;
int priority;
} SignTextAttrs;
#define SIGN_SHOW_MAX 9
/// Struct to hold the sign properties.
typedef struct sign {
char *sn_name; // name of sign
char *sn_icon; // name of pixmap
char *sn_text; // 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;
// Default sign priority for highlighting
#define SIGN_DEF_PRIO 10
// type argument for sign_get_attr()
typedef enum {
SIGN_LINEHL,
SIGN_NUMHL,
SIGN_TEXT,
} SignType;
#define SIGN_WIDTH 2 // Number of display cells for a sign in the signcolumn
#define SIGN_SHOW_MAX 9 // Maximum number of signs shown on a single line
#define SIGN_DEF_PRIO 10 // Default sign highlight priority

View File

@ -1656,9 +1656,9 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n
varnumber_T virtnum = get_vim_var_nr(VV_VIRTNUM);
for (int i = 0; i < width; i++) {
if (!fold) {
SignTextAttrs *sattr = virtnum ? NULL : sign_get_attr(i, stcp->sattrs, wp->w_scwidth);
SignTextAttrs *sattr = virtnum ? NULL : &stcp->sattrs[i];
p = sattr && sattr->text ? sattr->text : " ";
stl_items[curitem].minwid = -(sattr ? stcp->sign_cul_id ? stcp->sign_cul_id
stl_items[curitem].minwid = -(sattr && sattr->text ? stcp->sign_cul_id ? stcp->sign_cul_id
: sattr->hl_id : (stcp->use_cul ? HLF_CLS : HLF_SC) + 1);
}
stl_items[curitem].type = Highlight;

View File

@ -4680,7 +4680,7 @@ l5
screen:expect{grid=[[
{1: }^l1 |
S2S1l2 |
S1S2l2 |
{1: }l3 |
{1: }l4 |
{1: }l5 |
@ -4720,7 +4720,7 @@ l5
screen:expect{grid=[[
{1: }^l1 |
S1{1: }l2 |
S2S1l3 |
S1S2l3 |
S2{1: }l4 |
{1: }l5 |
{1: } |
@ -4765,7 +4765,7 @@ l5
meths.buf_set_extmark(0, ns, 2, -1, {sign_text='S5'})
screen:expect{grid=[[
S4S1^l1 |
S1S4^l1 |
x S2l2 |
S5{1: }l3 |
{1: }l4 |
@ -4792,9 +4792,9 @@ l5
meths.buf_set_extmark(0, ns, 2, -1, {sign_text='S5'})
screen:expect{grid=[[
S3S4S1^l1 |
S1S3S4^l1 |
x S2S3l2 |
S5S3{1: }l3 |
S3S5{1: }l3 |
S3{1: }l4 |
S3{1: }l5 |
{1: } |
@ -4848,15 +4848,15 @@ l5
end
screen:expect{grid=[[
X Y Z W {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
X Y Z W {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
X Y Z W {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
X Y Z W {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
X Y Z W {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
X Y Z W {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
X Y Z W {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
X Y Z W {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
X Y Z W {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:^h} |
W X Y Z {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
W X Y Z {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
W X Y Z {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
W X Y Z {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
W X Y Z {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
W X Y Z {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
W X Y Z {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
W X Y Z {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:h} |
W X Y Z {3:a} {3:b} {3:c} {3:d} {3:e} {3:f} {3:g} {3:^h} |
|
]]}
end)

View File

@ -274,9 +274,9 @@ describe('Signs', function()
-- Line 3 checks that with a limit over the maximum number
-- of signs, the ones with the highest Ids are being picked,
-- and presented by their sorted Id order.
command('sign place 4 line=3 name=pietSearch buffer=1')
command('sign place 5 line=3 name=pietWarn buffer=1')
command('sign place 3 line=3 name=pietError buffer=1')
command('sign place 6 line=3 name=pietSearch buffer=1')
command('sign place 7 line=3 name=pietWarn buffer=1')
command('sign place 5 line=3 name=pietError buffer=1')
screen:expect([[
{1:>>}{8:XX}{6: 1 }a |
{8:XX}{1:>>}{6: 2 }b |

View File

@ -377,6 +377,7 @@ describe('statuscolumn', function()
|
]])
command('set breakindent')
command('sign unplace 2')
feed('J2gjg0')
screen:expect([[
{2: }{4: 0}{1:>>}{2: }{4: }{5:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}|

View File

@ -1501,50 +1501,33 @@ func Test_sign_priority()
call sign_place(3, '', 'sign3', 'Xsign',
\ {'lnum' : 4, 'priority' : 20})
let s = sign_getplaced('Xsign', {'group' : '*'})
call assert_equal([
let se = [
\ {'id' : 3, 'name' : 'sign3', 'lnum' : 4, 'group' : '',
\ 'priority' : 20},
\ {'id' : 2, 'name' : 'sign2', 'lnum' : 4, 'group' : '',
\ 'priority' : 20},
\ {'id' : 1, 'name' : 'sign1', 'lnum' : 4, 'group' : '',
\ 'priority' : 20}],
\ s[0].signs)
\ 'priority' : 20}]
call assert_equal(se, s[0].signs)
" Nvim: signs are always sorted lnum->priority->sign_id->last_modified
" Last modified does not take precedence over sign_id here.
" Place the last sign again with the same priority
call sign_place(1, '', 'sign1', 'Xsign',
\ {'lnum' : 4, 'priority' : 20})
let s = sign_getplaced('Xsign', {'group' : '*'})
call assert_equal([
\ {'id' : 1, 'name' : 'sign1', 'lnum' : 4, 'group' : '',
\ 'priority' : 20},
\ {'id' : 3, 'name' : 'sign3', 'lnum' : 4, 'group' : '',
\ 'priority' : 20},
\ {'id' : 2, 'name' : 'sign2', 'lnum' : 4, 'group' : '',
\ 'priority' : 20}],
\ s[0].signs)
call assert_equal(se, s[0].signs)
" Place the first sign again with the same priority
call sign_place(1, '', 'sign1', 'Xsign',
\ {'lnum' : 4, 'priority' : 20})
let s = sign_getplaced('Xsign', {'group' : '*'})
call assert_equal([
\ {'id' : 1, 'name' : 'sign1', 'lnum' : 4, 'group' : '',
\ 'priority' : 20},
\ {'id' : 3, 'name' : 'sign3', 'lnum' : 4, 'group' : '',
\ 'priority' : 20},
\ {'id' : 2, 'name' : 'sign2', 'lnum' : 4, 'group' : '',
\ 'priority' : 20}],
\ s[0].signs)
call assert_equal(se, s[0].signs)
" Place the middle sign again with the same priority
call sign_place(3, '', 'sign3', 'Xsign',
\ {'lnum' : 4, 'priority' : 20})
let s = sign_getplaced('Xsign', {'group' : '*'})
call assert_equal([
\ {'id' : 3, 'name' : 'sign3', 'lnum' : 4, 'group' : '',
\ 'priority' : 20},
\ {'id' : 1, 'name' : 'sign1', 'lnum' : 4, 'group' : '',
\ 'priority' : 20},
\ {'id' : 2, 'name' : 'sign2', 'lnum' : 4, 'group' : '',
\ 'priority' : 20}],
\ s[0].signs)
call assert_equal(se, s[0].signs)
call sign_unplace('*')
@ -1670,34 +1653,33 @@ func Test_sign_lnum_adjust()
" changes made by this function.
let &g:undolevels=&g:undolevels
" Nvim: make sign adjustment when deleting lines match Vim
set signcolumn=yes:1
" Nvim: deleting a line removes the signs along with it.
" Delete the line with the sign
call deletebufline('', 4)
let l = sign_getplaced(bufnr(''))
call assert_equal(4, l[0].signs[0].lnum)
" " Delete the line with the sign
" call deletebufline('', 4)
" let l = sign_getplaced(bufnr(''))
" call assert_equal(4, l[0].signs[0].lnum)
" Undo the delete operation
undo
let l = sign_getplaced(bufnr(''))
call assert_equal(5, l[0].signs[0].lnum)
" " Undo the delete operation
" undo
" let l = sign_getplaced(bufnr(''))
" call assert_equal(5, l[0].signs[0].lnum)
" Break the undo
let &g:undolevels=&g:undolevels
" " Break the undo
" let &g:undolevels=&g:undolevels
" Delete few lines at the end of the buffer including the line with the sign
" Sign line number should not change (as it is placed outside of the buffer)
call deletebufline('', 3, 6)
let l = sign_getplaced(bufnr(''))
call assert_equal(5, l[0].signs[0].lnum)
" " Delete few lines at the end of the buffer including the line with the sign
" " Sign line number should not change (as it is placed outside of the buffer)
" call deletebufline('', 3, 6)
" let l = sign_getplaced(bufnr(''))
" call assert_equal(5, l[0].signs[0].lnum)
" Undo the delete operation. Sign should be restored to the previous line
undo
let l = sign_getplaced(bufnr(''))
call assert_equal(5, l[0].signs[0].lnum)
" " Undo the delete operation. Sign should be restored to the previous line
" undo
" let l = sign_getplaced(bufnr(''))
" call assert_equal(5, l[0].signs[0].lnum)
set signcolumn&
" set signcolumn&
sign unplace * group=*
sign undefine sign1
@ -1971,7 +1953,8 @@ func Test_sign_funcs_multi()
call sign_unplace('*')
" Place multiple signs at once with auto-generated sign identifier
call assert_equal([1, 1, 5], sign_placelist([
" Nvim: next sign id is not reset and is always incremented
call assert_equal([2, 3, 4], sign_placelist([
\ {'group' : 'g1', 'name' : 'sign1',
\ 'buffer' : 'Xsign', 'lnum' : 11},
\ {'group' : 'g2', 'name' : 'sign2',
@ -1980,17 +1963,17 @@ func Test_sign_funcs_multi()
\ 'buffer' : 'Xsign', 'lnum' : 11}]))
let s = sign_getplaced('Xsign', {'group' : '*'})
call assert_equal([
\ {'id' : 5, 'name' : 'sign3', 'lnum' : 11,
\ {'id' : 4, 'name' : 'sign3', 'lnum' : 11,
\ 'group' : '', 'priority' : 10},
\ {'id' : 1, 'name' : 'sign2', 'lnum' : 11,
\ {'id' : 3, 'name' : 'sign2', 'lnum' : 11,
\ 'group' : 'g2', 'priority' : 10},
\ {'id' : 1, 'name' : 'sign1', 'lnum' : 11,
\ {'id' : 2, 'name' : 'sign1', 'lnum' : 11,
\ 'group' : 'g1', 'priority' : 10}], s[0].signs)
" Change an existing sign without specifying the group
call assert_equal([5], [{'id' : 5, 'name' : 'sign1', 'buffer' : 'Xsign'}]->sign_placelist())
let s = sign_getplaced('Xsign', {'id' : 5, 'group' : ''})
call assert_equal([{'id' : 5, 'name' : 'sign1', 'lnum' : 11,
call assert_equal([4], [{'id' : 4, 'name' : 'sign1', 'buffer' : 'Xsign'}]->sign_placelist())
let s = sign_getplaced('Xsign', {'id' : 4, 'group' : ''})
call assert_equal([{'id' : 4, 'name' : 'sign1', 'lnum' : 11,
\ 'group' : '', 'priority' : 10}], s[0].signs)
" Place a sign using '.' as the line number
@ -2017,8 +2000,8 @@ func Test_sign_funcs_multi()
call assert_fails('call sign_placelist([100])', "E715:")
" Unplace multiple signs
call assert_equal([0, 0, 0], sign_unplacelist([{'id' : 5},
\ {'id' : 1, 'group' : 'g1'}, {'id' : 1, 'group' : 'g2'}]))
call assert_equal([0, 0, 0], sign_unplacelist([{'id' : 4},
\ {'id' : 2, 'group' : 'g1'}, {'id' : 3, 'group' : 'g2'}]))
" Invalid arguments
call assert_equal([], []->sign_unplacelist())