aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbfredl <bjorn.linse@gmail.com>2023-11-18 15:04:14 +0100
committerGitHub <noreply@github.com>2023-11-18 15:04:14 +0100
commitdf87266b23b32b96d1e3d4d26eb721a9dd63c2a4 (patch)
treeef277cb1f5aad32d85313a1044196df640074d32
parentcdc8bacc7945da816738e330555fa85d3ffffd56 (diff)
parentc4afb9788c4f139eb2e3b7aa4d6a6a20b67ba156 (diff)
downloadrneovim-df87266b23b32b96d1e3d4d26eb721a9dd63c2a4.tar.gz
rneovim-df87266b23b32b96d1e3d4d26eb721a9dd63c2a4.tar.bz2
rneovim-df87266b23b32b96d1e3d4d26eb721a9dd63c2a4.zip
Merge pull request #25724 from luukvbaal/signmerge
refactor(sign): move legacy signs to extmarks
-rw-r--r--runtime/doc/api.txt4
-rw-r--r--runtime/doc/news.txt8
-rw-r--r--runtime/doc/options.txt6
-rw-r--r--runtime/doc/sign.txt21
-rw-r--r--runtime/doc/vim_diff.txt1
-rw-r--r--runtime/lua/vim/_meta/api.lua4
-rw-r--r--runtime/lua/vim/_meta/options.lua6
-rw-r--r--src/nvim/api/extmark.c46
-rw-r--r--src/nvim/api/vim.c15
-rw-r--r--src/nvim/buffer.c95
-rw-r--r--src/nvim/buffer_defs.h1
-rw-r--r--src/nvim/decoration.c200
-rw-r--r--src/nvim/decoration.h4
-rw-r--r--src/nvim/decoration_provider.c2
-rw-r--r--src/nvim/drawline.c131
-rw-r--r--src/nvim/drawscreen.c4
-rw-r--r--src/nvim/eval/buffer.c2
-rw-r--r--src/nvim/extmark.c46
-rw-r--r--src/nvim/extmark.h7
-rw-r--r--src/nvim/globals.h4
-rw-r--r--src/nvim/highlight_defs.h6
-rw-r--r--src/nvim/main.c3
-rw-r--r--src/nvim/mark.c3
-rw-r--r--src/nvim/move.c3
-rw-r--r--src/nvim/option.c13
-rw-r--r--src/nvim/options.lua6
-rw-r--r--src/nvim/sign.c1825
-rw-r--r--src/nvim/sign_defs.h52
-rw-r--r--src/nvim/statusline.c4
-rw-r--r--test/functional/ui/decorations_spec.lua28
-rw-r--r--test/functional/ui/sign_spec.lua6
-rw-r--r--test/functional/ui/statuscolumn_spec.lua1
-rw-r--r--test/old/testdir/test_signs.vim99
33 files changed, 763 insertions, 1893 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index 908198de4c..57491b34d6 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -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)
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 59f419128d..8073a4f162 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -87,6 +87,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*
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 07326c8c13..f47093782c 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -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
diff --git a/runtime/doc/sign.txt b/runtime/doc/sign.txt
index 8ecfadce17..733b0fe212 100644
--- a/runtime/doc/sign.txt
+++ b/runtime/doc/sign.txt
@@ -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=":
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 5e09cc2481..cf9b3cf0e5 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -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.
diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index 0c8979054f..006996ad4e 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -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:
diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index be4a4dd49c..d2bdab4d28 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -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
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index 84b89a7428..efcdd0e7f7 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -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)
{
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 1d5898c488..88cbdbe1de 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -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) {
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 6d5c7a1766..deec0662a1 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -919,7 +919,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
@@ -4023,62 +4022,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).
///
@@ -4108,18 +4051,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++;
}
@@ -4128,42 +4071,32 @@ void buf_signcols_add_check(buf_T *buf, sign_entry_T *added)
return;
}
- sign_entry_T *s;
-
- // 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);
+ int signcols = decor_signcols(buf, lnum - 1, lnum - 1, SIGN_SHOW_MAX);
- 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);
}
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 32ebe52018..039484d2e2 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -704,7 +704,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
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index b111b01fe9..48dffea24b 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -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;
- }
-
- Decoration *decor = pair.start.decor_full;
-
- if (!decor || !decor_has_sign(decor)) {
- 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);
}
-
- 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;
- }
-
- Decoration *decor = mark.decor_full;
-
- if (!decor || !decor_has_sign(decor)) {
- 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);
}
-
- 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;
- }
-
- 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;
+ 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 (!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;
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 2e224135ed..dacdc7683a 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -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;
diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c
index cc1ae6ac92..2a00e9f373 100644
--- a/src/nvim/decoration_provider.c
+++ b/src/nvim/decoration_provider.c
@@ -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);
}
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 0cfab5cec9..172c72145b 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -456,85 +456,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;
- } 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);
+ 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);
- 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;
- }
+ // 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 {
+ 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;
@@ -598,8 +550,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;
@@ -610,8 +561,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
@@ -1317,15 +1268,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.
@@ -1334,18 +1282,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.
@@ -1661,8 +1609,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;
@@ -1673,14 +1620,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) {
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index 29f1cb2470..dbcb16cae3 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -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;
}
diff --git a/src/nvim/eval/buffer.c b/src/nvim/eval/buffer.c
index a26ba54ca3..616c1e06fc 100644
--- a/src/nvim/eval/buffer.c
+++ b/src/nvim/eval/buffer.c
@@ -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));
}
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c
index d24b304270..ff9fa55388 100644
--- a/src/nvim/extmark.c
+++ b/src/nvim/extmark.c
@@ -159,21 +159,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);
@@ -193,7 +194,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
@@ -240,8 +240,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)
@@ -299,7 +299,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;
@@ -307,8 +310,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;
}
@@ -596,6 +598,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);
diff --git a/src/nvim/extmark.h b/src/nvim/extmark.h
index 771edf3983..b4979c4aea 100644
--- a/src/nvim/extmark.h
+++ b/src/nvim/extmark.h
@@ -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
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 8e773d691a..4144a7c8ac 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -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
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index faec66b2bc..96a38b752f 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -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;
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 5c0fc758b9..3fc4b98c6c 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -69,7 +69,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"
@@ -217,8 +216,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
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index b4d405e2f1..532c20b874 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -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) {
diff --git a/src/nvim/move.c b/src/nvim/move.c
index ca3614ef97..b10bdd8ffe 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -760,8 +760,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)
diff --git a/src/nvim/option.c b/src/nvim/option.c
index d3a8e2ce73..d64fb0c615 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -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)) {
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 9ca1396753..c5dfa91f27 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -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',
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index 73ef1580ba..4f696f5c91 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -2,11 +2,11 @@
#include <inttypes.h>
#include <stdbool.h>
-#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include "nvim/api/extmark.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
@@ -19,10 +19,10 @@
#include "nvim/eval/typval.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
+#include "nvim/extmark.h"
#include "nvim/fold.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
-#include "nvim/hashtab.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/macros.h"
@@ -30,7 +30,6 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/move.h"
-#include "nvim/option.h"
#include "nvim/pos.h"
#include "nvim/sign.h"
#include "nvim/sign_defs.h"
@@ -39,26 +38,8 @@
#include "nvim/vim.h"
#include "nvim/window.h"
-/// Struct to hold the sign properties.
-typedef struct sign sign_T;
-
-struct sign {
- sign_T *sn_next; // next sign in list
- int sn_typenr; // type number of 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
-};
-
-static sign_T *first_sign = NULL;
-static int next_sign_typenr = 1;
-
-static void sign_list_defined(sign_T *sp);
-static void sign_undefine(sign_T *sp, sign_T *sp_prev);
+static PMap(cstr_t) sign_map INIT( = MAP_INIT);
+static kvec_t(Integer) sign_ns INIT( = MAP_INIT);
static char *cmds[] = {
"define",
@@ -77,736 +58,229 @@ static char *cmds[] = {
#define SIGNCMD_LAST 6
};
-static hashtab_T sg_table; // sign group (signgroup_T) hashtable
-static int next_sign_id = 1; // next sign id in the global group
-
-/// Initialize data needed for managing signs
-void init_signs(void)
-{
- hash_init(&sg_table); // sign group hash table
-}
-
-/// A new sign in group 'groupname' is added. If the group is not present,
-/// create it. Otherwise reference the group.
-static signgroup_T *sign_group_ref(const char *groupname)
-{
- hash_T hash;
- hashitem_T *hi;
- signgroup_T *group;
-
- hash = hash_hash(groupname);
- hi = hash_lookup(&sg_table, groupname, strlen(groupname), hash);
- if (HASHITEM_EMPTY(hi)) {
- // new group
- group = xmalloc(offsetof(signgroup_T, sg_name) + strlen(groupname) + 1);
-
- STRCPY(group->sg_name, groupname);
- group->sg_refcount = 1;
- group->sg_next_sign_id = 1;
- hash_add_item(&sg_table, hi, group->sg_name, hash);
- } else {
- // existing group
- group = HI2SG(hi);
- group->sg_refcount++;
- }
-
- return group;
-}
-
-/// A sign in group 'groupname' is removed. If all the signs in this group are
-/// removed, then remove the group.
-static void sign_group_unref(char *groupname)
+// Convert the supplied "group" to a namespace filter
+static int64_t group_get_ns(const char *group)
{
- hashitem_T *hi = hash_find(&sg_table, groupname);
- if (HASHITEM_EMPTY(hi)) {
- return;
- }
-
- signgroup_T *group = HI2SG(hi);
- group->sg_refcount--;
- if (group->sg_refcount == 0) {
- // All the signs in this group are removed
- hash_remove(&sg_table, hi);
- xfree(group);
+ if (group == NULL) {
+ return 0; // Global namespace
+ } else if (strcmp(group, "*") == 0) {
+ return UINT32_MAX; // All namespaces
}
+ // Specific or non-existing namespace
+ int ns = map_get(String, int)(&namespace_ids, cstr_as_string((char *)group));
+ return ns ? ns : -1;
}
-/// @return true if 'sign' is in 'group'.
-/// A sign can either be in the global group (sign->group == NULL)
-/// or in a named group. If 'group' is '*', then the sign is part of the group.
-static bool sign_in_group(sign_entry_T *sign, const char *group)
+static const char *sign_get_name(MTKey mark)
{
- return ((group != NULL && strcmp(group, "*") == 0)
- || (group == NULL && sign->se_group == NULL)
- || (group != NULL && sign->se_group != NULL
- && strcmp(group, sign->se_group->sg_name) == 0));
+ char *name = mark.decor_full->sign_name;
+ return !name ? "" : map_has(cstr_t, &sign_map, name) ? name : "[Deleted]";
}
-/// Get the next free sign identifier in the specified group
-static int sign_group_get_next_signid(buf_T *buf, const char *groupname)
-{
- int id = 1;
- signgroup_T *group = NULL;
- sign_entry_T *sign;
- int found = false;
-
- if (groupname != NULL) {
- hashitem_T *hi = hash_find(&sg_table, groupname);
- if (HASHITEM_EMPTY(hi)) {
- return id;
- }
- group = HI2SG(hi);
- }
-
- // Search for the next usable sign identifier
- while (!found) {
- if (group == NULL) {
- id = next_sign_id++; // global group
- } else {
- id = group->sg_next_sign_id++;
- }
-
- // Check whether this sign is already placed in the buffer
- found = true;
- FOR_ALL_SIGNS_IN_BUF(buf, sign) {
- if (id == sign->se_id && sign_in_group(sign, groupname)) {
- found = false; // sign identifier is in use
- break;
- }
- }
- }
-
- return id;
-}
-
-/// Insert a new sign into the signlist for buffer 'buf' between the 'prev' and
-/// 'next' signs.
+/// Create or update a sign extmark.
///
/// @param buf buffer to store sign in
-/// @param prev previous sign entry
-/// @param next next sign entry
/// @param id sign ID
-/// @param group sign group; NULL for global group
+/// @param group sign group
/// @param prio sign priority
/// @param lnum line number which gets the mark
-/// @param typenr typenr of sign we are adding
-/// @param has_text_or_icon sign has text or icon
-static void insert_sign(buf_T *buf, sign_entry_T *prev, sign_entry_T *next, int id,
- const char *group, int prio, linenr_T lnum, int typenr,
- bool has_text_or_icon)
+/// @param sp sign properties
+static void buf_set_sign(buf_T *buf, uint32_t *id, char *group, int prio, linenr_T lnum, sign_T *sp)
{
- sign_entry_T *newsign = xmalloc(sizeof(sign_entry_T));
- newsign->se_id = id;
- newsign->se_lnum = lnum;
- newsign->se_typenr = typenr;
- newsign->se_has_text_or_icon = has_text_or_icon;
- if (group != NULL) {
- newsign->se_group = sign_group_ref(group);
- } else {
- newsign->se_group = NULL;
+ if (group && !map_get(String, int)(&namespace_ids, cstr_as_string(group))) {
+ kv_push(sign_ns, nvim_create_namespace(cstr_as_string(group)));
}
- newsign->se_priority = prio;
- newsign->se_next = next;
- newsign->se_prev = prev;
- if (next != NULL) {
- next->se_prev = newsign;
- }
-
- buf_signcols_add_check(buf, newsign);
-
- if (prev == NULL) {
- // When adding first sign need to redraw the windows to create the
- // column for signs.
- if (buf->b_signlist == NULL) {
- redraw_buf_later(buf, UPD_NOT_VALID);
- changed_line_abv_curs();
- }
- // first sign in signlist
- buf->b_signlist = newsign;
- } else {
- prev->se_next = newsign;
- }
+ uint32_t ns = group ? (uint32_t)nvim_create_namespace(cstr_as_string(group)) : 0;
+ Decoration decor = DECORATION_INIT;
+ decor.sign_text = sp->sn_text ? xstrdup(sp->sn_text) : NULL;
+ decor.sign_name = xstrdup(sp->sn_name);
+ decor.sign_hl_id = sp->sn_text_hl;
+ decor.line_hl_id = sp->sn_line_hl;
+ decor.number_hl_id = sp->sn_num_hl;
+ decor.cursorline_hl_id = sp->sn_cul_hl;
+ decor.priority = (DecorPriority)prio;
+ extmark_set(buf, ns, id, lnum - 1, 0, -1, -1, &decor, true, false, true, true, NULL);
}
-/// Insert a new sign sorted by line number and sign priority.
+/// For an existing, placed sign with "id", modify the sign, group or priority.
+/// Returns the line number of the sign, or zero if the sign is not found.
///
/// @param buf buffer to store sign in
-/// @param prev previous sign entry
/// @param id sign ID
-/// @param group sign group; NULL for global group
+/// @param group sign group
/// @param prio sign priority
-/// @param lnum line number which gets the mark
-/// @param typenr typenr of sign we are adding
-/// @param has_text_or_icon sign has text or icon
-static void insert_sign_by_lnum_prio(buf_T *buf, sign_entry_T *prev, int id, const char *group,
- int prio, linenr_T lnum, int typenr, bool has_text_or_icon)
-{
- sign_entry_T *sign;
-
- // keep signs sorted by lnum, priority and id: insert new sign at
- // the proper position in the list for this lnum.
- while (prev != NULL && prev->se_lnum == lnum
- && (prev->se_priority < prio
- || (prev->se_priority == prio && prev->se_id <= id))) {
- prev = prev->se_prev;
- }
- if (prev == NULL) {
- sign = buf->b_signlist;
- } else {
- sign = prev->se_next;
- }
-
- insert_sign(buf, prev, sign, id, group, prio, lnum, typenr, has_text_or_icon);
-}
-
-/// Lookup a sign by typenr. Returns NULL if sign is not found.
-static sign_T *find_sign_by_typenr(int typenr)
-{
- sign_T *sp;
-
- for (sp = first_sign; sp != NULL; sp = sp->sn_next) {
- if (sp->sn_typenr == typenr) {
- return sp;
- }
- }
- return NULL;
-}
-
-/// Get the name of a sign by its typenr.
-static char *sign_typenr2name(int typenr)
-{
- sign_T *sp;
-
- for (sp = first_sign; sp != NULL; sp = sp->sn_next) {
- if (sp->sn_typenr == typenr) {
- return sp->sn_name;
- }
- }
- return _("[Deleted]");
-}
-
-/// Return information about a sign in a Dict
-static dict_T *sign_get_info(sign_entry_T *sign)
-{
- dict_T *d = tv_dict_alloc();
- tv_dict_add_nr(d, S_LEN("id"), sign->se_id);
- tv_dict_add_str(d, S_LEN("group"), ((sign->se_group == NULL)
- ? ""
- : sign->se_group->sg_name));
- tv_dict_add_nr(d, S_LEN("lnum"), sign->se_lnum);
- tv_dict_add_str(d, S_LEN("name"), sign_typenr2name(sign->se_typenr));
- tv_dict_add_nr(d, S_LEN("priority"), sign->se_priority);
-
- return d;
-}
-
-// Sort the signs placed on the same line as "sign" by priority. Invoked after
-// changing the priority of an already placed sign. Assumes the signs in the
-// buffer are sorted by line number and priority.
-static void sign_sort_by_prio_on_line(buf_T *buf, sign_entry_T *sign)
- FUNC_ATTR_NONNULL_ALL
+/// @param sp sign pointer
+static linenr_T buf_mod_sign(buf_T *buf, uint32_t *id, char *group, int prio, sign_T *sp)
{
- // If there is only one sign in the buffer or only one sign on the line or
- // the sign is already sorted by priority, then return.
- if ((sign->se_prev == NULL
- || sign->se_prev->se_lnum != sign->se_lnum
- || sign->se_prev->se_priority > sign->se_priority)
- && (sign->se_next == NULL
- || sign->se_next->se_lnum != sign->se_lnum
- || sign->se_next->se_priority < sign->se_priority)) {
- return;
- }
-
- // One or more signs on the same line as 'sign'
- // Find a sign after which 'sign' should be inserted
-
- // First search backward for a sign with higher priority on the same line
- sign_entry_T *p = sign;
- while (p->se_prev != NULL
- && p->se_prev->se_lnum == sign->se_lnum
- && p->se_prev->se_priority <= sign->se_priority) {
- p = p->se_prev;
- }
- if (p == sign) {
- // Sign not found. Search forward for a sign with priority just before
- // 'sign'.
- p = sign->se_next;
- while (p->se_next != NULL
- && p->se_next->se_lnum == sign->se_lnum
- && p->se_next->se_priority > sign->se_priority) {
- p = p->se_next;
- }
- }
-
- // Remove 'sign' from the list
- if (buf->b_signlist == sign) {
- buf->b_signlist = sign->se_next;
- }
- if (sign->se_prev != NULL) {
- sign->se_prev->se_next = sign->se_next;
- }
- if (sign->se_next != NULL) {
- sign->se_next->se_prev = sign->se_prev;
+ int64_t ns = group_get_ns(group);
+ if (ns < 0 || (group && ns == 0)) {
+ return 0;
}
- sign->se_prev = NULL;
- sign->se_next = NULL;
- // Re-insert 'sign' at the right place
- if (p->se_priority <= sign->se_priority) {
- // 'sign' has a higher priority and should be inserted before 'p'
- sign->se_prev = p->se_prev;
- sign->se_next = p;
- p->se_prev = sign;
- if (sign->se_prev != NULL) {
- sign->se_prev->se_next = sign;
- }
- if (buf->b_signlist == p) {
- buf->b_signlist = sign;
- }
- } else {
- // 'sign' has a lower priority and should be inserted after 'p'
- sign->se_prev = p;
- sign->se_next = p->se_next;
- p->se_next = sign;
- if (sign->se_next != NULL) {
- sign->se_next->se_prev = sign;
- }
+ MTKey mark = marktree_lookup_ns(buf->b_marktree, (uint32_t)ns, *id, false, NULL);
+ if (mark.pos.row >= 0) {
+ buf_set_sign(buf, id, group, prio, mark.pos.row + 1, sp);
}
+ return mark.pos.row + 1;
}
-/// Add the sign into the signlist. Find the right spot to do it though.
+/// Find the line number of the sign with the requested id in group 'group'. If
+/// the sign does not exist, return 0 as the line number. This will still let
+/// the correct file get loaded.
///
/// @param buf buffer to store sign in
/// @param id sign ID
-/// @param groupname sign group
-/// @param prio sign priority
-/// @param lnum line number which gets the mark
-/// @param typenr typenr of sign we are adding
-/// @param has_text_or_icon sign has text or icon
-static void buf_addsign(buf_T *buf, int id, const char *groupname, int prio, linenr_T lnum,
- int typenr, bool has_text_or_icon)
-{
- sign_entry_T *sign; // a sign in the signlist
- sign_entry_T *prev; // the previous sign
-
- prev = NULL;
- FOR_ALL_SIGNS_IN_BUF(buf, sign) {
- if (lnum == sign->se_lnum && id == sign->se_id
- && sign_in_group(sign, groupname)) {
- // Update an existing sign
- sign->se_typenr = typenr;
- sign->se_priority = prio;
- sign_sort_by_prio_on_line(buf, sign);
- return;
- } else if (lnum < sign->se_lnum) {
- insert_sign_by_lnum_prio(buf,
- prev,
- id,
- groupname,
- prio,
- lnum,
- typenr,
- has_text_or_icon);
- return;
- }
- prev = sign;
- }
-
- insert_sign_by_lnum_prio(buf,
- prev,
- id,
- groupname,
- prio,
- lnum,
- typenr,
- has_text_or_icon);
-}
-
-/// For an existing, placed sign "markId" change the type to "typenr".
-/// Returns the line number of the sign, or zero if the sign is not found.
-///
-/// @param buf buffer to store sign in
-/// @param markId sign ID
/// @param group sign group
-/// @param typenr typenr of sign we are adding
-/// @param prio sign priority
-static linenr_T buf_change_sign_type(buf_T *buf, int markId, const char *group, int typenr,
- int prio)
+static int buf_findsign(buf_T *buf, int id, char *group)
{
- sign_entry_T *sign; // a sign in the signlist
-
- FOR_ALL_SIGNS_IN_BUF(buf, sign) {
- if (sign->se_id == markId && sign_in_group(sign, group)) {
- sign->se_typenr = typenr;
- sign->se_priority = prio;
- sign_sort_by_prio_on_line(buf, sign);
- return sign->se_lnum;
- }
+ int64_t ns = group_get_ns(group);
+ if (ns < 0 || (group && ns == 0)) {
+ return 0;
}
-
- return 0;
+ return marktree_lookup_ns(buf->b_marktree, (uint32_t)ns, (uint32_t)id, false, NULL).pos.row + 1;
}
-/// Return the sign attrs which has the attribute specified by 'type'. Returns
-/// NULL if a sign is not found with the specified attribute.
-/// @param type Type of sign to look for
-/// @param sattrs Sign attrs to search through
-/// @param idx if there multiple signs, this index will pick the n-th
-/// out of the most `max_signs` sorted ascending by Id.
-/// @param max_signs the number of signs, with priority for the ones
-/// with the highest Ids.
-/// @return Attrs of the matching sign, or NULL
-SignTextAttrs *sign_get_attr(int idx, SignTextAttrs sattrs[], int max_signs)
+/// qsort() function to sort signs by line number, priority, id and recency.
+int sign_cmp(const void *p1, const void *p2)
{
- SignTextAttrs *matches[SIGN_SHOW_MAX];
- int sattr_matches = 0;
-
- for (int i = 0; i < SIGN_SHOW_MAX; i++) {
- if (sattrs[i].text != NULL) {
- matches[sattr_matches++] = &sattrs[i];
- // attr list is sorted with most important (priority, id), thus we
- // may stop as soon as we have max_signs matches
- if (sattr_matches >= max_signs) {
- break;
- }
- }
- }
-
- if (sattr_matches > idx) {
- return matches[sattr_matches - idx - 1];
- }
+ const MTKey *s1 = (MTKey *)p1;
+ const MTKey *s2 = (MTKey *)p2;
+ int n = s1->pos.row - s2->pos.row;
- return NULL;
+ return n ? n : (n = s2->decor_full->priority - s1->decor_full->priority)
+ ? n : (n = (int)(s2->id - s1->id))
+ ? n : (s2->decor_full->sign_add_id - s1->decor_full->sign_add_id);
}
-/// Return the attributes of all the signs placed on line 'lnum' in buffer
-/// 'buf'. Used when refreshing the screen. Returns the number of signs.
-/// @param buf Buffer in which to search
-/// @param lnum Line in which to search
-/// @param sattrs Output array for attrs
-/// @return Number of signs of which attrs were found
-int buf_get_signattrs(buf_T *buf, linenr_T lnum, SignTextAttrs sattrs[], HlPriId *num_id,
- HlPriId *line_id, HlPriId *cul_id)
-{
- sign_entry_T *sign;
-
- int sattr_matches = 0;
-
- FOR_ALL_SIGNS_IN_BUF(buf, sign) {
- if (sign->se_lnum > lnum) {
- // Signs are sorted by line number in the buffer. No need to check
- // for signs after the specified line number 'lnum'.
- break;
- }
-
- if (sign->se_lnum < lnum) {
- continue;
- }
-
- sign_T *sp = find_sign_by_typenr(sign->se_typenr);
- if (sp == NULL) {
- continue;
- }
-
- if (sp->sn_text != NULL && sattr_matches < SIGN_SHOW_MAX) {
- sattrs[sattr_matches++] = (SignTextAttrs) {
- .text = sp->sn_text,
- .hl_id = sp->sn_text_hl,
- .priority = sign->se_priority
- };
- }
-
- struct { HlPriId *dest; int hl; } cattrs[] = {
- { line_id, sp->sn_line_hl },
- { num_id, sp->sn_num_hl },
- { cul_id, sp->sn_cul_hl },
- { NULL, -1 },
- };
- for (int i = 0; cattrs[i].dest; i++) {
- if (cattrs[i].hl != 0 && sign->se_priority >= cattrs[i].dest->priority) {
- *cattrs[i].dest = (HlPriId) {
- .hl_id = cattrs[i].hl,
- .priority = sign->se_priority
- };
- }
- }
- }
- return sattr_matches;
-}
-
-/// Delete sign 'id' in group 'group' from buffer 'buf'.
-/// If 'id' is zero, then delete all the signs in group 'group'. Otherwise
-/// delete only the specified sign.
-/// If 'group' is '*', then delete the sign in all the groups. If 'group' is
-/// NULL, then delete the sign in the global group. Otherwise delete the sign in
-/// the specified group.
+/// Delete the specified signs
///
-/// @param buf buffer sign is stored in
-/// @param atlnum sign at this line, 0 - at any line
-/// @param id sign id
+/// @param buf buffer sign is stored in or NULL for all buffers
/// @param group sign group
-///
-/// @return the line number of the deleted sign. If multiple signs are deleted,
-/// then returns the line number of the last sign deleted.
-static linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char *group)
+/// @param id sign id
+/// @param atlnum sign at this line, -1 at any line
+static int buf_delete_signs(buf_T *buf, char *group, int id, linenr_T atlnum)
{
- sign_entry_T **lastp; // pointer to pointer to current sign
- sign_entry_T *sign; // a sign in a b_signlist
- sign_entry_T *next; // the next sign in a b_signlist
- linenr_T lnum; // line number whose sign was deleted
-
- lastp = &buf->b_signlist;
- lnum = 0;
- for (sign = buf->b_signlist; sign != NULL; sign = next) {
- next = sign->se_next;
- if ((id == 0 || sign->se_id == id)
- && (atlnum == 0 || sign->se_lnum == atlnum)
- && sign_in_group(sign, group)) {
- *lastp = next;
- if (next != NULL) {
- next->se_prev = sign->se_prev;
- }
- lnum = sign->se_lnum;
- buf_signcols_del_check(buf, lnum, lnum);
- if (sign->se_group != NULL) {
- sign_group_unref(sign->se_group->sg_name);
- }
- xfree(sign);
- redraw_buf_line_later(buf, lnum, false);
- // Check whether only one sign needs to be deleted
- // If deleting a sign with a specific identifier in a particular
- // group or deleting any sign at a particular line number, delete
- // only one sign.
- if (group == NULL
- || (*group != '*' && id != 0)
- || (*group == '*' && atlnum != 0)) {
- break;
- }
- } else {
- lastp = &sign->se_next;
- }
- }
-
- // When deleting the last sign the cursor position may change, because the
- // sign columns no longer shows. And the 'signcolumn' may be hidden.
- if (buf->b_signlist == NULL) {
- redraw_buf_later(buf, UPD_NOT_VALID);
- changed_line_abv_curs();
+ int64_t ns = group_get_ns(group);
+ if (ns < 0) {
+ return FAIL;
}
- return lnum;
-}
-
-/// Find the line number of the sign with the requested id in group 'group'. If
-/// the sign does not exist, return 0 as the line number. This will still let
-/// the correct file get loaded.
-///
-/// @param buf buffer to store sign in
-/// @param id sign ID
-/// @param group sign group
-static int buf_findsign(buf_T *buf, int id, char *group)
-{
- sign_entry_T *sign; // a sign in the signlist
+ MarkTreeIter itr[1];
+ int row = atlnum > 0 ? atlnum - 1 : 0;
+ kvec_t(MTKey) signs = KV_INITIAL_VALUE;
+ // Store and sort when removing a single sign at a specific line number.
+ if (atlnum > 0) {
+ if (!marktree_itr_get_overlap(buf->b_marktree, row, 0, itr)) {
+ return FAIL;
+ }
- FOR_ALL_SIGNS_IN_BUF(buf, sign) {
- if (sign->se_id == id && sign_in_group(sign, group)) {
- return (int)sign->se_lnum;
+ MTPair pair;
+ while (marktree_itr_step_overlap(buf->b_marktree, itr, &pair)) {
+ if ((ns == UINT32_MAX || ns == pair.start.ns)
+ && pair.start.decor_full && decor_has_sign(pair.start.decor_full)) {
+ kv_push(signs, pair.start);
+ }
}
+ } else {
+ marktree_itr_get(buf->b_marktree, 0, 0, itr);
}
- return 0;
-}
-
-/// Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is
-/// not found at the line. If 'groupname' is NULL, searches in the global group.
-///
-/// @param buf buffer whose sign we are searching for
-/// @param lnum line number of sign
-/// @param groupname sign group name
-static sign_entry_T *buf_getsign_at_line(buf_T *buf, linenr_T lnum, char *groupname)
-{
- sign_entry_T *sign; // a sign in the signlist
-
- FOR_ALL_SIGNS_IN_BUF(buf, sign) {
- if (sign->se_lnum > lnum) {
- // Signs are sorted by line number in the buffer. No need to check
- // for signs after the specified line number 'lnum'.
+ while (itr->x) {
+ MTKey mark = marktree_itr_current(itr);
+ if (row && mark.pos.row > row) {
break;
}
-
- if (sign->se_lnum == lnum && sign_in_group(sign, groupname)) {
- return sign;
+ if (!mt_end(mark) && mark.decor_full && decor_has_sign(mark.decor_full)
+ && (id == 0 || (int)mark.id == id)
+ && (ns == UINT32_MAX || ns == mark.ns)) {
+ if (atlnum > 0) {
+ kv_push(signs, mark);
+ marktree_itr_next(buf->b_marktree, itr);
+ } else {
+ extmark_del(buf, itr, mark, true);
+ }
+ } else {
+ marktree_itr_next(buf->b_marktree, itr);
}
}
- return NULL;
-}
-
-/// Return the identifier of the sign at line number 'lnum' in buffer 'buf'.
-///
-/// @param buf buffer whose sign we are searching for
-/// @param lnum line number of sign
-/// @param groupname sign group name
-static int buf_findsign_id(buf_T *buf, linenr_T lnum, char *groupname)
-{
- sign_entry_T *sign; // a sign in the signlist
-
- sign = buf_getsign_at_line(buf, lnum, groupname);
- if (sign != NULL) {
- return sign->se_id;
+ if (kv_size(signs)) {
+ qsort((void *)&kv_A(signs, 0), kv_size(signs), sizeof(MTKey), sign_cmp);
+ extmark_del_id(buf, kv_A(signs, 0).ns, kv_A(signs, 0).id);
+ kv_destroy(signs);
+ } else if (atlnum > 0) {
+ return FAIL;
}
- return 0;
-}
-
-/// Delete signs in buffer "buf".
-void buf_delete_signs(buf_T *buf, char *group)
-{
- sign_entry_T *sign;
- sign_entry_T **lastp; // pointer to pointer to current sign
- sign_entry_T *next;
-
// When deleting the last sign need to redraw the windows to remove the
// sign column. Not when curwin is NULL (this means we're exiting).
- if (buf->b_signlist != NULL && curwin != NULL) {
+ if (!buf->b_signs_with_text && curwin != NULL) {
changed_line_abv_curs();
}
- lastp = &buf->b_signlist;
- for (sign = buf->b_signlist; sign != NULL; sign = next) {
- next = sign->se_next;
- if (sign_in_group(sign, group)) {
- *lastp = next;
- if (next != NULL) {
- next->se_prev = sign->se_prev;
- }
- if (sign->se_group != NULL) {
- sign_group_unref(sign->se_group->sg_name);
- }
- xfree(sign);
- } else {
- lastp = &sign->se_next;
- }
- }
- buf_signcols_del_check(buf, 1, MAXLNUM);
+ return OK;
}
/// List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
-static void sign_list_placed(buf_T *rbuf, char *sign_group)
+static void sign_list_placed(buf_T *rbuf, char *group)
{
- buf_T *buf;
- sign_entry_T *sign;
char lbuf[MSG_BUF_LEN];
- char group[MSG_BUF_LEN];
+ char namebuf[MSG_BUF_LEN];
+ char groupbuf[MSG_BUF_LEN];
+ buf_T *buf = rbuf ? rbuf : firstbuf;
+ int64_t ns = group_get_ns(group);
msg_puts_title(_("\n--- Signs ---"));
msg_putchar('\n');
- if (rbuf == NULL) {
- buf = firstbuf;
- } else {
- buf = rbuf;
- }
+
while (buf != NULL && !got_int) {
- if (buf->b_signlist != NULL) {
+ if (buf->b_signs) {
vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
msg_puts_attr(lbuf, HL_ATTR(HLF_D));
msg_putchar('\n');
}
- FOR_ALL_SIGNS_IN_BUF(buf, sign) {
- if (got_int) {
- break;
- }
- if (!sign_in_group(sign, sign_group)) {
- continue;
- }
- if (sign->se_group != NULL) {
- vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"),
- sign->se_group->sg_name);
- } else {
- group[0] = '\0';
- }
- vim_snprintf(lbuf, MSG_BUF_LEN,
- _(" line=%" PRIdLINENR " id=%d%s name=%s priority=%d"),
- sign->se_lnum, sign->se_id, group,
- sign_typenr2name(sign->se_typenr), sign->se_priority);
- msg_puts(lbuf);
- msg_putchar('\n');
- }
- if (rbuf != NULL) {
- break;
- }
- buf = buf->b_next;
- }
-}
-
-/// Adjust or delete a placed sign for inserted/deleted lines.
-///
-/// @return the new line number of the sign, or 0 if the sign is in deleted lines.
-static linenr_T sign_adjust_one(const linenr_T se_lnum, linenr_T line1, linenr_T line2,
- linenr_T amount, linenr_T amount_after)
-{
- if (se_lnum < line1) {
- // Ignore changes to lines after the sign
- return se_lnum;
- }
- if (se_lnum > line2) {
- // Lines inserted or deleted before the sign
- return se_lnum + amount_after;
- }
- if (amount == MAXLNUM) { // sign in deleted lines
- return 0;
- }
- return se_lnum + amount;
-}
-
-/// Adjust placed signs for inserted/deleted lines.
-void sign_mark_adjust(buf_T *buf, linenr_T line1, linenr_T line2, linenr_T amount,
- linenr_T amount_after)
-{
- sign_entry_T *sign; // a sign in a b_signlist
- sign_entry_T *next; // the next sign in a b_signlist
- sign_entry_T *last = NULL; // pointer to pointer to current sign
- sign_entry_T **lastp = NULL; // pointer to pointer to current sign
- linenr_T new_lnum; // new line number to assign to sign
- int is_fixed = 0;
- int signcol = curwin->w_buffer == buf ? win_signcol_configured(curwin, &is_fixed) : 0;
- if (amount == MAXLNUM) { // deleting
- buf_signcols_del_check(buf, line1, line2);
- }
+ if (ns >= 0) {
+ MarkTreeIter itr[1];
+ kvec_t(MTKey) signs = KV_INITIAL_VALUE;
+ marktree_itr_get(buf->b_marktree, 0, 0, itr);
- lastp = &buf->b_signlist;
-
- for (sign = buf->b_signlist; sign != NULL; sign = next) {
- next = sign->se_next;
-
- new_lnum = sign_adjust_one(sign->se_lnum, line1, line2, amount, amount_after);
- if (new_lnum == 0) { // sign in deleted lines
- if (!is_fixed || signcol >= 2) {
- *lastp = next;
- if (next) {
- next->se_prev = last;
+ while (itr->x) {
+ MTKey mark = marktree_itr_current(itr);
+ if (!mt_end(mark) && mark.decor_full && decor_has_sign(mark.decor_full)
+ && (ns == UINT32_MAX || ns == mark.ns)) {
+ kv_push(signs, mark);
}
- xfree(sign);
- continue;
+ marktree_itr_next(buf->b_marktree, itr);
}
- } else {
- // If the new sign line number is past the last line in the buffer,
- // then don't adjust the line number. Otherwise, it will always be past
- // the last line and will not be visible.
- if (new_lnum <= buf->b_ml.ml_line_count) {
- sign->se_lnum = new_lnum;
+
+ if (kv_size(signs)) {
+ qsort((void *)&kv_A(signs, 0), kv_size(signs), sizeof(MTKey), sign_cmp);
+
+ for (size_t i = 0; i < kv_size(signs); i++) {
+ namebuf[0] = '\0';
+ groupbuf[0] = '\0';
+ MTKey mark = kv_A(signs, i);
+ if (mark.decor_full->sign_name != NULL) {
+ vim_snprintf(namebuf, MSG_BUF_LEN, _(" name=%s"), sign_get_name(mark));
+ }
+ if (mark.ns != 0) {
+ vim_snprintf(groupbuf, MSG_BUF_LEN, _(" group=%s"), describe_ns((int)mark.ns, ""));
+ }
+ vim_snprintf(lbuf, MSG_BUF_LEN, _(" line=%" PRIdLINENR " id=%u%s%s priority=%d"),
+ mark.pos.row + 1, mark.id, groupbuf, namebuf, mark.decor_full->priority);
+ msg_puts(lbuf);
+ msg_putchar('\n');
+ }
+ kv_destroy(signs);
}
}
- last = sign;
- lastp = &sign->se_next;
- }
-
- new_lnum = sign_adjust_one(buf->b_signcols.sentinel, line1, line2, amount, amount_after);
- if (new_lnum != 0) {
- buf->b_signcols.sentinel = new_lnum;
+ if (rbuf != NULL) {
+ return;
+ }
+ buf = buf->b_next;
}
}
@@ -830,93 +304,22 @@ static int sign_cmd_idx(char *begin_cmd, char *end_cmd)
return idx;
}
-/// Find a sign by name. Also returns pointer to the previous sign.
-static sign_T *sign_find(const char *name, sign_T **sp_prev)
-{
- sign_T *sp;
-
- if (sp_prev != NULL) {
- *sp_prev = NULL;
- }
- for (sp = first_sign; sp != NULL; sp = sp->sn_next) {
- if (strcmp(sp->sn_name, name) == 0) {
- break;
- }
- if (sp_prev != NULL) {
- *sp_prev = sp;
- }
- }
-
- return sp;
-}
-
-/// Allocate a new sign
-static sign_T *alloc_new_sign(char *name)
-{
- sign_T *sp;
- sign_T *lp;
- int start = next_sign_typenr;
-
- // Allocate a new sign.
- sp = xcalloc(1, sizeof(sign_T));
-
- // Check that next_sign_typenr is not already being used.
- // This only happens after wrapping around. Hopefully
- // another one got deleted and we can use its number.
- for (lp = first_sign; lp != NULL;) {
- if (lp->sn_typenr == next_sign_typenr) {
- next_sign_typenr++;
- if (next_sign_typenr == MAX_TYPENR) {
- next_sign_typenr = 1;
- }
- if (next_sign_typenr == start) {
- xfree(sp);
- emsg(_("E612: Too many signs defined"));
- return NULL;
- }
- lp = first_sign; // start all over
- continue;
- }
- lp = lp->sn_next;
- }
-
- sp->sn_typenr = next_sign_typenr;
- if (++next_sign_typenr == MAX_TYPENR) {
- next_sign_typenr = 1; // wrap around
- }
-
- sp->sn_name = xstrdup(name);
-
- return sp;
-}
-
-/// Initialize the icon information for a new sign
-static void sign_define_init_icon(sign_T *sp, char *icon)
-{
- xfree(sp->sn_icon);
- sp->sn_icon = xstrdup(icon);
- backslash_halve(sp->sn_icon);
-}
-
-/// Initialize the text for a new sign
-static int sign_define_init_text(sign_T *sp, char *text)
+/// Initialize the "text" for a new sign and store in "sign_text".
+/// "sp" is NULL for signs added through nvim_buf_set_extmark().
+int init_sign_text(sign_T *sp, char **sign_text, char *text)
{
char *s;
- char *endp;
- int cells;
- size_t len;
+ char *endp = text + (int)strlen(text);
- endp = text + (int)strlen(text);
- for (s = text; s + 1 < endp; s++) {
+ for (s = sp ? text : endp; s + 1 < endp; s++) {
if (*s == '\\') {
- // Remove a backslash, so that it is possible
- // to use a space.
+ // Remove a backslash, so that it is possible to use a space.
STRMOVE(s, s + 1);
endp--;
}
}
// Count cells and check for non-printable chars
- cells = 0;
+ int cells = 0;
for (s = text; s < endp; s += utfc_ptr2len(s)) {
if (!vim_isprintc(utf_ptr2char(s))) {
break;
@@ -925,95 +328,69 @@ static int sign_define_init_text(sign_T *sp, char *text)
}
// Currently must be empty, one or two display cells
if (s != endp || cells > 2) {
- semsg(_("E239: Invalid sign text: %s"), text);
+ if (sp != NULL) {
+ semsg(_("E239: Invalid sign text: %s"), text);
+ }
return FAIL;
}
if (cells < 1) {
- sp->sn_text = NULL;
+ if (sp != NULL) {
+ sp->sn_text = NULL;
+ }
return OK;
}
- xfree(sp->sn_text);
- // Allocate one byte more if we need to pad up
- // with a space.
- len = (size_t)(endp - text + ((cells == 1) ? 1 : 0));
- sp->sn_text = xstrnsave(text, len);
+ if (sp != NULL) {
+ xfree(sp->sn_text);
+ }
+ // Allocate one byte more if we need to pad up with a space.
+ size_t len = (size_t)(endp - text + (cells == 1));
+ *sign_text = xstrnsave(text, len);
if (cells == 1) {
- STRCPY(sp->sn_text + len - 1, " ");
+ STRCPY(*sign_text + len - 1, " ");
}
return OK;
}
/// Define a new sign or update an existing sign
-static int sign_define_by_name(char *name, char *icon, char *linehl, char *text, char *texthl,
+static int sign_define_by_name(char *name, char *icon, char *text, char *linehl, char *texthl,
char *culhl, char *numhl)
{
- sign_T *sp_prev;
- sign_T *sp;
+ cstr_t *key;
+ sign_T **sp = (sign_T **)pmap_put_ref(cstr_t)(&sign_map, name, &key, NULL);
- sp = sign_find(name, &sp_prev);
- if (sp == NULL) {
- sp = alloc_new_sign(name);
- if (sp == NULL) {
- return FAIL;
- }
-
- // add the new sign to the list of signs
- if (sp_prev == NULL) {
- first_sign = sp;
- } else {
- sp_prev->sn_next = sp;
- }
+ if (*sp == NULL) {
+ *key = xstrdup(name);
+ *sp = xcalloc(1, sizeof(sign_T));
+ (*sp)->sn_name = (char *)(*key);
} else {
- // Signs may already exist, a redraw is needed in windows with a
- // non-empty sign list.
+ // Signs may already exist, a redraw is needed in windows with a non-empty sign list.
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer->b_signlist != NULL) {
+ if (wp->w_buffer->b_signs) {
redraw_buf_later(wp->w_buffer, UPD_NOT_VALID);
}
}
}
- // set values for a defined sign.
+ // Set values for a defined sign.
if (icon != NULL) {
- sign_define_init_icon(sp, icon);
+ /// Initialize the icon information for a new sign
+ xfree((*sp)->sn_icon);
+ (*sp)->sn_icon = xstrdup(icon);
+ backslash_halve((*sp)->sn_icon);
}
- if (text != NULL && (sign_define_init_text(sp, text) == FAIL)) {
+ if (text != NULL && (init_sign_text(*sp, &(*sp)->sn_text, text) == FAIL)) {
return FAIL;
}
- if (linehl != NULL) {
- if (*linehl == NUL) {
- sp->sn_line_hl = 0;
- } else {
- sp->sn_line_hl = syn_check_group(linehl, strlen(linehl));
- }
- }
-
- if (texthl != NULL) {
- if (*texthl == NUL) {
- sp->sn_text_hl = 0;
- } else {
- sp->sn_text_hl = syn_check_group(texthl, strlen(texthl));
- }
- }
-
- if (culhl != NULL) {
- if (*culhl == NUL) {
- sp->sn_cul_hl = 0;
- } else {
- sp->sn_cul_hl = syn_check_group(culhl, strlen(culhl));
- }
- }
-
- if (numhl != NULL) {
- if (*numhl == NUL) {
- sp->sn_num_hl = 0;
- } else {
- sp->sn_num_hl = syn_check_group(numhl, strlen(numhl));
+ char *arg[] = { linehl, texthl, culhl, numhl };
+ int *hl[] = { &(*sp)->sn_line_hl, &(*sp)->sn_text_hl, &(*sp)->sn_cul_hl, &(*sp)->sn_num_hl };
+ for (int i = 0; i < 4; i++) {
+ if (arg[i] != NULL) {
+ *hl[i] = *arg[i] ? syn_check_group(arg[i], strlen(arg[i])) : 0;
}
}
@@ -1023,25 +400,47 @@ static int sign_define_by_name(char *name, char *icon, char *linehl, char *text,
/// Free the sign specified by 'name'.
static int sign_undefine_by_name(const char *name)
{
- sign_T *sp_prev;
- sign_T *sp;
-
- sp = sign_find(name, &sp_prev);
+ sign_T *sp = pmap_del(cstr_t)(&sign_map, name, NULL);
if (sp == NULL) {
semsg(_("E155: Unknown sign: %s"), name);
return FAIL;
}
- sign_undefine(sp, sp_prev);
+ xfree(sp->sn_name);
+ xfree(sp->sn_text);
+ xfree(sp->sn_icon);
+ xfree(sp);
return OK;
}
+/// List one sign.
+static void sign_list_defined(sign_T *sp)
+{
+ smsg(0, "sign %s", sp->sn_name);
+ if (sp->sn_icon != NULL) {
+ msg_puts(" icon=");
+ msg_outtrans(sp->sn_icon, 0);
+ msg_puts(_(" (not supported)"));
+ }
+ if (sp->sn_text != NULL) {
+ msg_puts(" text=");
+ msg_outtrans(sp->sn_text, 0);
+ }
+ static char *arg[] = { " linehl=", " texthl=", " culhl=", " numhl=" };
+ int hl[] = { sp->sn_line_hl, sp->sn_text_hl, sp->sn_cul_hl, sp->sn_num_hl };
+ for (int i = 0; i < 4; i++) {
+ if (hl[i] > 0) {
+ msg_puts(arg[i]);
+ const char *p = get_highlight_name_ext(NULL, hl[i] - 1, false);
+ msg_puts(p ? p : "NONE");
+ }
+ }
+}
+
/// List the signs matching 'name'
static void sign_list_by_name(char *name)
{
- sign_T *sp;
-
- sp = sign_find(name, NULL);
+ sign_T *sp = pmap_get(cstr_t)(&sign_map, name);
if (sp != NULL) {
sign_list_defined(sp);
} else {
@@ -1061,103 +460,90 @@ static void may_force_numberwidth_recompute(buf_T *buf, int unplace)
}
/// Place a sign at the specified file location or update a sign.
-static int sign_place(int *sign_id, const char *sign_group, const char *sign_name, buf_T *buf,
- linenr_T lnum, int prio)
+static int sign_place(uint32_t *id, char *group, char *name, buf_T *buf, linenr_T lnum, int prio)
{
- sign_T *sp;
-
// Check for reserved character '*' in group name
- if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0')) {
+ if (group != NULL && (*group == '*' || *group == '\0')) {
return FAIL;
}
- for (sp = first_sign; sp != NULL; sp = sp->sn_next) {
- if (strcmp(sp->sn_name, sign_name) == 0) {
- break;
- }
- }
+ sign_T *sp = pmap_get(cstr_t)(&sign_map, name);
if (sp == NULL) {
- semsg(_("E155: Unknown sign: %s"), sign_name);
+ semsg(_("E155: Unknown sign: %s"), name);
return FAIL;
}
- if (*sign_id == 0) {
- *sign_id = sign_group_get_next_signid(buf, sign_group);
- }
if (lnum > 0) {
- // ":sign place {id} line={lnum} name={name} file={fname}":
- // place a sign
- bool has_text_or_icon = sp->sn_text != NULL || sp->sn_icon != NULL;
- buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr, has_text_or_icon);
+ // ":sign place {id} line={lnum} name={name} file={fname}": place a sign
+ buf_set_sign(buf, id, group, prio, lnum, sp);
} else {
// ":sign place {id} file={fname}": change sign type and/or priority
- lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr, prio);
+ lnum = buf_mod_sign(buf, id, group, prio, sp);
}
if (lnum > 0) {
- redraw_buf_line_later(buf, lnum, false);
-
// When displaying signs in the 'number' column, if the width of the
// number column is less than 2, then force recomputing the width.
may_force_numberwidth_recompute(buf, false);
} else {
- semsg(_("E885: Not possible to change sign %s"), sign_name);
+ semsg(_("E885: Not possible to change sign %s"), name);
return FAIL;
}
return OK;
}
-/// Unplace the specified sign
-static int sign_unplace(int sign_id, char *sign_group, buf_T *buf, linenr_T atlnum)
+static int sign_unplace_inner(buf_T *buf, int id, char *group, linenr_T atlnum)
{
- if (buf->b_signlist == NULL) { // No signs in the buffer
- return OK;
+ if (!buf->b_signs) { // No signs in the buffer
+ return FAIL;
}
- if (sign_id == 0) {
- // Delete all the signs in the specified buffer
- redraw_buf_later(buf, UPD_NOT_VALID);
- buf_delete_signs(buf, sign_group);
- } else {
- linenr_T lnum;
- // Delete only the specified signs
- lnum = buf_delsign(buf, atlnum, sign_id, sign_group);
- if (lnum == 0) {
+ if (id == 0 || atlnum > 0 || (group != NULL && *group == '*')) {
+ // Delete multiple specified signs
+ if (!buf_delete_signs(buf, group, id, atlnum)) {
+ return FAIL;
+ }
+ } else {
+ // Delete only a single sign
+ int64_t ns = group_get_ns(group);
+ if (ns < 0 || !extmark_del_id(buf, (uint32_t)ns, (uint32_t)id)) {
return FAIL;
}
- redraw_buf_line_later(buf, lnum, false);
}
// When all the signs in a buffer are removed, force recomputing the
// number column width (if enabled) in all the windows displaying the
// buffer if 'signcolumn' is set to 'number' in that window.
- if (buf->b_signlist == NULL) {
+ if (!buf->b_signs_with_text) {
may_force_numberwidth_recompute(buf, true);
}
return OK;
}
-/// Unplace the sign at the current cursor line.
-static void sign_unplace_at_cursor(char *groupname)
+/// Unplace the specified sign for a single or all buffers
+static int sign_unplace(buf_T *buf, int id, char *group, linenr_T atlnum)
{
- int id = -1;
-
- id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname);
- if (id > 0) {
- sign_unplace(id, groupname, curwin->w_buffer, curwin->w_cursor.lnum);
+ if (buf != NULL) {
+ return sign_unplace_inner(buf, id, group, atlnum);
} else {
- emsg(_("E159: Missing sign number"));
+ int retval = OK;
+ FOR_ALL_BUFFERS(cbuf) {
+ if (!sign_unplace_inner(cbuf, id, group, atlnum)) {
+ retval = FAIL;
+ }
+ }
+ return retval;
}
}
/// Jump to a sign.
-static linenr_T sign_jump(int sign_id, char *sign_group, buf_T *buf)
+static linenr_T sign_jump(int id, char *group, buf_T *buf)
{
- linenr_T lnum;
+ linenr_T lnum = buf_findsign(buf, id, group);
- if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0) {
- semsg(_("E157: Invalid sign ID: %" PRId64), (int64_t)sign_id);
+ if (lnum <= 0) {
+ semsg(_("E157: Invalid sign ID: %" PRId32), id);
return -1;
}
@@ -1173,8 +559,7 @@ static linenr_T sign_jump(int sign_id, char *sign_group, buf_T *buf)
}
size_t cmdlen = strlen(buf->b_fname) + 24;
char *cmd = xmallocz(cmdlen);
- snprintf(cmd, cmdlen, "e +%" PRId64 " %s",
- (int64_t)lnum, buf->b_fname);
+ snprintf(cmd, cmdlen, "e +%" PRId64 " %s", (int64_t)lnum, buf->b_fname);
do_cmdline_cmd(cmd);
xfree(cmd);
}
@@ -1185,9 +570,8 @@ static linenr_T sign_jump(int sign_id, char *sign_group, buf_T *buf)
}
/// ":sign define {name} ..." command
-static void sign_define_cmd(char *sign_name, char *cmdline)
+static void sign_define_cmd(char *name, char *cmdline)
{
- char *p = cmdline;
char *icon = NULL;
char *text = NULL;
char *linehl = NULL;
@@ -1198,58 +582,38 @@ static void sign_define_cmd(char *sign_name, char *cmdline)
// set values for a defined sign.
while (true) {
- char *arg = skipwhite(p);
- if (*arg == NUL) {
- break;
- }
- p = skiptowhite_esc(arg);
+ char *arg = skipwhite(cmdline);
+ cmdline = skiptowhite_esc(arg);
if (strncmp(arg, "icon=", 5) == 0) {
- arg += 5;
- XFREE_CLEAR(icon);
- icon = xmemdupz(arg, (size_t)(p - arg));
+ icon = arg + 5;
} else if (strncmp(arg, "text=", 5) == 0) {
- arg += 5;
- XFREE_CLEAR(text);
- text = xmemdupz(arg, (size_t)(p - arg));
+ text = arg + 5;
} else if (strncmp(arg, "linehl=", 7) == 0) {
- arg += 7;
- XFREE_CLEAR(linehl);
- linehl = xmemdupz(arg, (size_t)(p - arg));
+ linehl = arg + 7;
} else if (strncmp(arg, "texthl=", 7) == 0) {
- arg += 7;
- XFREE_CLEAR(texthl);
- texthl = xmemdupz(arg, (size_t)(p - arg));
+ texthl = arg + 7;
} else if (strncmp(arg, "culhl=", 6) == 0) {
- arg += 6;
- XFREE_CLEAR(culhl);
- culhl = xmemdupz(arg, (size_t)(p - arg));
+ culhl = arg + 6;
} else if (strncmp(arg, "numhl=", 6) == 0) {
- arg += 6;
- XFREE_CLEAR(numhl);
- numhl = xmemdupz(arg, (size_t)(p - arg));
+ numhl = arg + 6;
} else {
semsg(_(e_invarg2), arg);
failed = true;
break;
}
+ if (*cmdline == NUL) {
+ break;
+ }
+ *cmdline++ = NUL;
}
if (!failed) {
- sign_define_by_name(sign_name, icon, linehl, text,
- texthl, culhl, numhl);
+ sign_define_by_name(name, icon, text, linehl, texthl, culhl, numhl);
}
-
- xfree(icon);
- xfree(text);
- xfree(linehl);
- xfree(texthl);
- xfree(culhl);
- xfree(numhl);
}
/// ":sign place" command
-static void sign_place_cmd(buf_T *buf, linenr_T lnum, char *sign_name, int id, char *group,
- int prio)
+static void sign_place_cmd(buf_T *buf, linenr_T lnum, char *name, int id, char *group, int prio)
{
if (id <= 0) {
// List signs placed in a file/buffer
@@ -1262,74 +626,37 @@ static void sign_place_cmd(buf_T *buf, linenr_T lnum, char *sign_name, int id, c
// :sign place
// :sign place group={group}
// :sign place group=*
- if (lnum >= 0 || sign_name != NULL
- || (group != NULL && *group == '\0')) {
+ if (lnum >= 0 || name != NULL || (group != NULL && *group == '\0')) {
emsg(_(e_invarg));
} else {
sign_list_placed(buf, group);
}
} else {
// Place a new sign
- if (sign_name == NULL || buf == NULL
- || (group != NULL && *group == '\0')) {
+ if (name == NULL || buf == NULL || (group != NULL && *group == '\0')) {
emsg(_(e_invarg));
return;
}
-
- sign_place(&id, group, sign_name, buf, lnum, prio);
+ uint32_t uid = (uint32_t)id;
+ sign_place(&uid, group, name, buf, lnum, prio);
}
}
/// ":sign unplace" command
-static void sign_unplace_cmd(buf_T *buf, linenr_T lnum, const char *sign_name, int id, char *group)
+static void sign_unplace_cmd(buf_T *buf, linenr_T lnum, const char *name, int id, char *group)
{
- if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0')) {
+ if (lnum >= 0 || name != NULL || (group != NULL && *group == '\0')) {
emsg(_(e_invarg));
return;
}
- if (id == -2) {
- if (buf != NULL) {
- // :sign unplace * file={fname}
- // :sign unplace * group={group} file={fname}
- // :sign unplace * group=* file={fname}
- // :sign unplace * buffer={nr}
- // :sign unplace * group={group} buffer={nr}
- // :sign unplace * group=* buffer={nr}
- sign_unplace(0, group, buf, 0);
- } else {
- // :sign unplace *
- // :sign unplace * group={group}
- // :sign unplace * group=*
- FOR_ALL_BUFFERS(cbuf) {
- if (cbuf->b_signlist != NULL) {
- buf_delete_signs(cbuf, group);
- }
- }
- }
- } else {
- if (buf != NULL) {
- // :sign unplace {id} file={fname}
- // :sign unplace {id} group={group} file={fname}
- // :sign unplace {id} group=* file={fname}
- // :sign unplace {id} buffer={nr}
- // :sign unplace {id} group={group} buffer={nr}
- // :sign unplace {id} group=* buffer={nr}
- sign_unplace(id, group, buf, 0);
- } else {
- if (id == -1) {
- // :sign unplace group={group}
- // :sign unplace group=*
- sign_unplace_at_cursor(group);
- } else {
- // :sign unplace {id}
- // :sign unplace {id} group={group}
- // :sign unplace {id} group=*
- FOR_ALL_BUFFERS(cbuf) {
- sign_unplace(id, group, cbuf, 0);
- }
- }
- }
+ if (id == -1) {
+ lnum = curwin->w_cursor.lnum;
+ buf = curwin->w_buffer;
+ }
+
+ if (!sign_unplace(buf, MAX(0, id), group, lnum) && lnum > 0) {
+ emsg(_("E159: Missing sign number"));
}
}
@@ -1338,15 +665,14 @@ static void sign_unplace_cmd(buf_T *buf, linenr_T lnum, const char *sign_name, i
/// :sign jump {id} buffer={nr}
/// :sign jump {id} group={group} file={fname}
/// :sign jump {id} group={group} buffer={nr}
-static void sign_jump_cmd(buf_T *buf, linenr_T lnum, const char *sign_name, int id, char *group)
+static void sign_jump_cmd(buf_T *buf, linenr_T lnum, const char *name, int id, char *group)
{
- if (sign_name == NULL && group == NULL && id == -1) {
+ if (name == NULL && group == NULL && id == -1) {
emsg(_(e_argreq));
return;
}
- if (buf == NULL || (group != NULL && *group == '\0')
- || lnum >= 0 || sign_name != NULL) {
+ if (buf == NULL || (group != NULL && *group == '\0') || lnum >= 0 || name != NULL) {
// File or buffer is not specified or an empty group is used
// or a line number or a sign name is specified.
emsg(_(e_invarg));
@@ -1360,20 +686,18 @@ static void sign_jump_cmd(buf_T *buf, linenr_T lnum, const char *sign_name, int
/// ":sign jump" commands.
/// The supported arguments are: line={lnum} name={name} group={group}
/// priority={prio} and file={fname} or buffer={nr}.
-static int parse_sign_cmd_args(int cmd, char *arg, char **sign_name, int *signid, char **group,
- int *prio, buf_T **buf, linenr_T *lnum)
+static int parse_sign_cmd_args(int cmd, char *arg, char **name, int *id, char **group, int *prio,
+ buf_T **buf, linenr_T *lnum)
{
- char *arg1;
- char *name;
+ char *arg1 = arg;
char *filename = NULL;
int lnum_arg = false;
// first arg could be placed sign id
- arg1 = arg;
if (ascii_isdigit(*arg)) {
- *signid = getdigits_int(&arg, true, 0);
+ *id = getdigits_int(&arg, true, 0);
if (!ascii_iswhite(*arg) && *arg != NUL) {
- *signid = -1;
+ *id = -1;
arg = arg1;
} else {
arg = skipwhite(arg);
@@ -1387,23 +711,23 @@ static int parse_sign_cmd_args(int cmd, char *arg, char **sign_name, int *signid
arg = skiptowhite(arg);
lnum_arg = true;
} else if (strncmp(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE) {
- if (*signid != -1) {
+ if (*id != -1) {
emsg(_(e_invarg));
return FAIL;
}
- *signid = -2;
+ *id = -2;
arg = skiptowhite(arg + 1);
} else if (strncmp(arg, "name=", 5) == 0) {
arg += 5;
- name = arg;
+ char *namep = arg;
arg = skiptowhite(arg);
if (*arg != NUL) {
*arg++ = NUL;
}
- while (name[0] == '0' && name[1] != NUL) {
- name++;
+ while (namep[0] == '0' && namep[1] != NUL) {
+ namep++;
}
- *sign_name = name;
+ *name = namep;
} else if (strncmp(arg, "group=", 6) == 0) {
arg += 6;
*group = arg;
@@ -1442,8 +766,7 @@ static int parse_sign_cmd_args(int cmd, char *arg, char **sign_name, int *signid
// If the filename is not supplied for the sign place or the sign jump
// command, then use the current buffer.
- if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg)
- || cmd == SIGNCMD_JUMP)) {
+ if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg) || cmd == SIGNCMD_JUMP)) {
*buf = curwin->w_buffer;
}
return OK;
@@ -1453,13 +776,10 @@ static int parse_sign_cmd_args(int cmd, char *arg, char **sign_name, int *signid
void ex_sign(exarg_T *eap)
{
char *arg = eap->arg;
- char *p;
- int idx;
- sign_T *sp;
// Parse the subcommand.
- p = skiptowhite(arg);
- idx = sign_cmd_idx(arg, p);
+ char *p = skiptowhite(arg);
+ int idx = sign_cmd_idx(arg, p);
if (idx == SIGNCMD_LAST) {
semsg(_("E160: Unknown sign command: %s"), arg);
return;
@@ -1470,14 +790,13 @@ void ex_sign(exarg_T *eap)
// Define, undefine or list signs.
if (idx == SIGNCMD_LIST && *arg == NUL) {
// ":sign list": list all defined signs
- for (sp = first_sign; sp != NULL && !got_int; sp = sp->sn_next) {
+ sign_T *sp;
+ map_foreach_value(&sign_map, sp, {
sign_list_defined(sp);
- }
+ });
} else if (*arg == NUL) {
emsg(_("E156: Missing sign name"));
} else {
- char *name;
-
// Isolate the sign name. If it's a number skip leading zeroes,
// so that "099" and "99" are the same sign. But keep "0".
p = skiptowhite(arg);
@@ -1487,246 +806,161 @@ void ex_sign(exarg_T *eap)
while (arg[0] == '0' && arg[1] != NUL) {
arg++;
}
- name = xstrdup(arg);
if (idx == SIGNCMD_DEFINE) {
- sign_define_cmd(name, p);
+ sign_define_cmd(arg, p);
} else if (idx == SIGNCMD_LIST) {
// ":sign list {name}"
- sign_list_by_name(name);
+ sign_list_by_name(arg);
} else {
// ":sign undefine {name}"
- sign_undefine_by_name(name);
+ sign_undefine_by_name(arg);
}
- xfree(name);
return;
}
} else {
int id = -1;
linenr_T lnum = -1;
- char *sign_name = NULL;
+ char *name = NULL;
char *group = NULL;
int prio = SIGN_DEF_PRIO;
buf_T *buf = NULL;
// Parse command line arguments
- if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio,
- &buf, &lnum) == FAIL) {
+ if (parse_sign_cmd_args(idx, arg, &name, &id, &group, &prio, &buf, &lnum) == FAIL) {
return;
}
if (idx == SIGNCMD_PLACE) {
- sign_place_cmd(buf, lnum, sign_name, id, group, prio);
+ sign_place_cmd(buf, lnum, name, id, group, prio);
} else if (idx == SIGNCMD_UNPLACE) {
- sign_unplace_cmd(buf, lnum, sign_name, id, group);
+ sign_unplace_cmd(buf, lnum, name, id, group);
} else if (idx == SIGNCMD_JUMP) {
- sign_jump_cmd(buf, lnum, sign_name, id, group);
+ sign_jump_cmd(buf, lnum, name, id, group);
}
}
}
-/// Return information about a specified sign
-static void sign_getinfo(sign_T *sp, dict_T *retdict)
+/// Append dictionary of information for a defined sign "sp", or placed
+/// sign "mark" to "retlist". Either "sp", or "mark" is NULL.
+static void sign_list_append_info(sign_T *sp, MTKey *mark, list_T *retlist)
{
- const char *p;
+ dict_T *d = tv_dict_alloc();
+ tv_list_append_dict(retlist, d);
+
+ tv_dict_add_str(d, S_LEN("name"), sp ? sp->sn_name : sign_get_name(*mark));
+ if (mark != NULL) {
+ tv_dict_add_nr(d, S_LEN("id"), (int)mark->id);
+ tv_dict_add_str(d, S_LEN("group"), describe_ns((int)mark->ns, ""));
+ tv_dict_add_nr(d, S_LEN("lnum"), mark->pos.row + 1);
+ tv_dict_add_nr(d, S_LEN("priority"), mark->decor_full->priority);
+ return;
+ }
- tv_dict_add_str(retdict, S_LEN("name"), sp->sn_name);
if (sp->sn_icon != NULL) {
- tv_dict_add_str(retdict, S_LEN("icon"), sp->sn_icon);
+ tv_dict_add_str(d, S_LEN("icon"), sp->sn_icon);
}
if (sp->sn_text != NULL) {
- tv_dict_add_str(retdict, S_LEN("text"), sp->sn_text);
- }
- if (sp->sn_line_hl > 0) {
- p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, false);
- if (p == NULL) {
- p = "NONE";
- }
- tv_dict_add_str(retdict, S_LEN("linehl"), p);
- }
- if (sp->sn_text_hl > 0) {
- p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, false);
- if (p == NULL) {
- p = "NONE";
- }
- tv_dict_add_str(retdict, S_LEN("texthl"), p);
- }
- if (sp->sn_cul_hl > 0) {
- p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, false);
- if (p == NULL) {
- p = "NONE";
- }
- tv_dict_add_str(retdict, S_LEN("culhl"), p);
+ tv_dict_add_str(d, S_LEN("text"), sp->sn_text);
}
- if (sp->sn_num_hl > 0) {
- p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, false);
- if (p == NULL) {
- p = "NONE";
- }
- tv_dict_add_str(retdict, S_LEN("numhl"), p);
- }
-}
-
-/// If 'name' is NULL, return a list of all the defined signs.
-/// Otherwise, return information about the specified sign.
-static void sign_getlist(const char *name, list_T *retlist)
-{
- sign_T *sp = first_sign;
-
- if (name != NULL) {
- sp = sign_find(name, NULL);
- if (sp == NULL) {
- return;
- }
- }
-
- for (; sp != NULL && !got_int; sp = sp->sn_next) {
- dict_T *dict = tv_dict_alloc();
- tv_list_append_dict(retlist, dict);
- sign_getinfo(sp, dict);
-
- if (name != NULL) { // handle only the specified sign
- break;
+ static char *arg[] = { "linehl", "texthl", "culhl", "numhl" };
+ int hl[] = { sp->sn_line_hl, sp->sn_text_hl, sp->sn_cul_hl, sp->sn_num_hl };
+ for (int i = 0; i < 4; i++) {
+ if (hl[i] > 0) {
+ const char *p = get_highlight_name_ext(NULL, hl[i] - 1, false);
+ tv_dict_add_str(d, arg[i], strlen(arg[i]), p ? p : "NONE");
}
}
}
/// Returns information about signs placed in a buffer as list of dicts.
list_T *get_buffer_signs(buf_T *buf)
- FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+ FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
- sign_entry_T *sign;
list_T *const l = tv_list_alloc(kListLenMayKnow);
+ MarkTreeIter itr[1];
+ marktree_itr_get(buf->b_marktree, 0, 0, itr);
- FOR_ALL_SIGNS_IN_BUF(buf, sign) {
- dict_T *d = sign_get_info(sign);
- tv_list_append_dict(l, d);
+ while (itr->x) {
+ MTKey mark = marktree_itr_current(itr);
+ if (!mt_end(mark) && mark.decor_full && decor_has_sign(mark.decor_full)) {
+ sign_list_append_info(NULL, &mark, l);
+ }
+ marktree_itr_next(buf->b_marktree, itr);
}
+
return l;
}
/// @return information about all the signs placed in a buffer
-static void sign_get_placed_in_buf(buf_T *buf, linenr_T lnum, int sign_id, const char *sign_group,
+static void sign_get_placed_in_buf(buf_T *buf, linenr_T lnum, int sign_id, const char *group,
list_T *retlist)
{
- dict_T *d;
- list_T *l;
- sign_entry_T *sign;
-
- d = tv_dict_alloc();
+ dict_T *d = tv_dict_alloc();
tv_list_append_dict(retlist, d);
tv_dict_add_nr(d, S_LEN("bufnr"), buf->b_fnum);
- l = tv_list_alloc(kListLenMayKnow);
+ list_T *l = tv_list_alloc(kListLenMayKnow);
tv_dict_add_list(d, S_LEN("signs"), l);
- FOR_ALL_SIGNS_IN_BUF(buf, sign) {
- if (!sign_in_group(sign, sign_group)) {
- continue;
+ int64_t ns = group_get_ns(group);
+ if (!buf->b_signs || ns < 0) {
+ return;
+ }
+
+ MarkTreeIter itr[1];
+ kvec_t(MTKey) signs = KV_INITIAL_VALUE;
+ marktree_itr_get(buf->b_marktree, lnum ? lnum - 1 : 0, 0, itr);
+
+ while (itr->x) {
+ MTKey mark = marktree_itr_current(itr);
+ if (lnum && mark.pos.row >= lnum) {
+ break;
+ }
+ if (!mt_end(mark) && mark.decor_full && decor_has_sign(mark.decor_full)
+ && (ns == UINT32_MAX || ns == mark.ns)
+ && ((lnum == 0 && sign_id == 0)
+ || (sign_id == 0 && lnum == mark.pos.row + 1)
+ || (lnum == 0 && sign_id == (int)mark.id)
+ || (lnum == mark.pos.row + 1 && sign_id == (int)mark.id))) {
+ kv_push(signs, mark);
}
- if ((lnum == 0 && sign_id == 0)
- || (sign_id == 0 && lnum == sign->se_lnum)
- || (lnum == 0 && sign_id == sign->se_id)
- || (lnum == sign->se_lnum && sign_id == sign->se_id)) {
- tv_list_append_dict(l, sign_get_info(sign));
+ marktree_itr_next(buf->b_marktree, itr);
+ }
+
+ if (kv_size(signs)) {
+ qsort((void *)&kv_A(signs, 0), kv_size(signs), sizeof(MTKey), sign_cmp);
+ for (size_t i = 0; i < kv_size(signs); i++) {
+ sign_list_append_info(NULL, &kv_A(signs, i), l);
}
+ kv_destroy(signs);
}
}
/// Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the
/// sign placed at the line number. If 'lnum' is zero, return all the signs
/// placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers.
-static void sign_get_placed(buf_T *buf, linenr_T lnum, int sign_id, const char *sign_group,
- list_T *retlist)
+static void sign_get_placed(buf_T *buf, linenr_T lnum, int id, const char *group, list_T *retlist)
{
if (buf != NULL) {
- sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist);
+ sign_get_placed_in_buf(buf, lnum, id, group, retlist);
} else {
FOR_ALL_BUFFERS(cbuf) {
- if (cbuf->b_signlist != NULL) {
- sign_get_placed_in_buf(cbuf, 0, sign_id, sign_group, retlist);
+ if (cbuf->b_signs) {
+ sign_get_placed_in_buf(cbuf, 0, id, group, retlist);
}
}
}
}
-/// List one sign.
-static void sign_list_defined(sign_T *sp)
-{
- smsg(0, "sign %s", sp->sn_name);
- if (sp->sn_icon != NULL) {
- msg_puts(" icon=");
- msg_outtrans(sp->sn_icon, 0);
- msg_puts(_(" (not supported)"));
- }
- if (sp->sn_text != NULL) {
- msg_puts(" text=");
- msg_outtrans(sp->sn_text, 0);
- }
- if (sp->sn_line_hl > 0) {
- msg_puts(" linehl=");
- const char *const p = get_highlight_name_ext(NULL,
- sp->sn_line_hl - 1, false);
- if (p == NULL) {
- msg_puts("NONE");
- } else {
- msg_puts(p);
- }
- }
- if (sp->sn_text_hl > 0) {
- msg_puts(" texthl=");
- const char *const p = get_highlight_name_ext(NULL,
- sp->sn_text_hl - 1, false);
- if (p == NULL) {
- msg_puts("NONE");
- } else {
- msg_puts(p);
- }
- }
- if (sp->sn_cul_hl > 0) {
- msg_puts(" culhl=");
- const char *const p = get_highlight_name_ext(NULL,
- sp->sn_cul_hl - 1, false);
- if (p == NULL) {
- msg_puts("NONE");
- } else {
- msg_puts(p);
- }
- }
- if (sp->sn_num_hl > 0) {
- msg_puts(" numhl=");
- const char *const p = get_highlight_name_ext(NULL,
- sp->sn_num_hl - 1, false);
- if (p == NULL) {
- msg_puts("NONE");
- } else {
- msg_puts(p);
- }
- }
-}
-
-/// Undefine a sign and free its memory.
-static void sign_undefine(sign_T *sp, sign_T *sp_prev)
-{
- xfree(sp->sn_name);
- xfree(sp->sn_icon);
- xfree(sp->sn_text);
- if (sp_prev == NULL) {
- first_sign = sp->sn_next;
- } else {
- sp_prev->sn_next = sp->sn_next;
- }
- xfree(sp);
-}
-
-/// Undefine/free all signs.
void free_signs(void)
{
- while (first_sign != NULL) {
- sign_undefine(first_sign, NULL);
- }
+ cstr_t name;
+ map_foreach_key(&sign_map, name, {
+ sign_undefine_by_name(name);
+ });
}
static enum {
@@ -1744,12 +978,13 @@ static char *get_nth_sign_name(int idx)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
// Complete with name of signs already defined
+ cstr_t name;
int current_idx = 0;
- for (sign_T *sp = first_sign; sp != NULL; sp = sp->sn_next) {
+ map_foreach_key(&sign_map, name, {
if (current_idx++ == idx) {
- return sp->sn_name;
+ return (char *)name;
}
- }
+ });
return NULL;
}
@@ -1757,35 +992,24 @@ static char *get_nth_sign_name(int idx)
static char *get_nth_sign_group_name(int idx)
{
// Complete with name of sign groups already defined
- int current_idx = 0;
- int todo = (int)sg_table.ht_used;
- for (hashitem_T *hi = sg_table.ht_array; todo > 0; hi++) {
- if (!HASHITEM_EMPTY(hi)) {
- todo--;
- if (current_idx++ == idx) {
- signgroup_T *const group = HI2SG(hi);
- return group->sg_name;
- }
- }
+ if (idx < (int)kv_size(sign_ns)) {
+ return (char *)describe_ns((NS)kv_A(sign_ns, idx), "");
}
return NULL;
}
-/// Function given to ExpandGeneric() to obtain the sign command
-/// expansion.
+/// Function given to ExpandGeneric() to obtain the sign command expansion.
char *get_sign_name(expand_T *xp, int idx)
{
switch (expand_what) {
case EXP_SUBCMD:
return cmds[idx];
case EXP_DEFINE: {
- char *define_arg[] = { "culhl=", "icon=", "linehl=", "numhl=", "text=", "texthl=",
- NULL };
+ char *define_arg[] = { "culhl=", "icon=", "linehl=", "numhl=", "text=", "texthl=", NULL };
return define_arg[idx];
}
case EXP_PLACE: {
- char *place_arg[] = { "line=", "name=", "group=", "priority=", "file=",
- "buffer=", NULL };
+ char *place_arg[] = { "line=", "name=", "group=", "priority=", "file=", "buffer=", NULL };
return place_arg[idx];
}
case EXP_LIST: {
@@ -1808,29 +1032,24 @@ char *get_sign_name(expand_T *xp, int idx)
/// Handle command line completion for :sign command.
void set_context_in_sign_cmd(expand_T *xp, char *arg)
{
- char *end_subcmd;
- char *last;
- int cmd_idx;
- char *begin_subcmd_args;
-
// Default: expand subcommands.
xp->xp_context = EXPAND_SIGN;
expand_what = EXP_SUBCMD;
xp->xp_pattern = arg;
- end_subcmd = skiptowhite(arg);
+ char *end_subcmd = skiptowhite(arg);
if (*end_subcmd == NUL) {
// expand subcmd name
// :sign {subcmd}<CTRL-D>
return;
}
- cmd_idx = sign_cmd_idx(arg, end_subcmd);
+ int cmd_idx = sign_cmd_idx(arg, end_subcmd);
// :sign {subcmd} {subcmd_args}
// |
// begin_subcmd_args
- begin_subcmd_args = skipwhite(end_subcmd);
+ char *begin_subcmd_args = skipwhite(end_subcmd);
// Expand last argument of subcmd.
//
@@ -1839,6 +1058,7 @@ void set_context_in_sign_cmd(expand_T *xp, char *arg)
// p
// Loop until reaching last argument.
+ char *last;
char *p = begin_subcmd_args;
do {
p = skipwhite(p);
@@ -1926,63 +1146,44 @@ void set_context_in_sign_cmd(expand_T *xp, char *arg)
/// Define a sign using the attributes in 'dict'. Returns 0 on success and -1 on
/// failure.
-static int sign_define_from_dict(const char *name_arg, dict_T *dict)
+static int sign_define_from_dict(char *name, dict_T *dict)
{
- char *name = NULL;
+ if (name == NULL) {
+ name = tv_dict_get_string(dict, "name", false);
+ if (name == NULL || name[0] == NUL) {
+ return -1;
+ }
+ }
+
char *icon = NULL;
char *linehl = NULL;
char *text = NULL;
char *texthl = NULL;
char *culhl = NULL;
char *numhl = NULL;
- int retval = -1;
- if (name_arg == NULL) {
- if (dict == NULL) {
- return -1;
- }
- name = tv_dict_get_string(dict, "name", true);
- } else {
- name = xstrdup(name_arg);
+ if (dict != NULL) {
+ icon = tv_dict_get_string(dict, "icon", false);
+ linehl = tv_dict_get_string(dict, "linehl", false);
+ text = tv_dict_get_string(dict, "text", false);
+ texthl = tv_dict_get_string(dict, "texthl", false);
+ culhl = tv_dict_get_string(dict, "culhl", false);
+ numhl = tv_dict_get_string(dict, "numhl", false);
}
- if (name == NULL || name[0] == NUL) {
- goto cleanup;
+
+ if (sign_define_by_name(name, icon, text, linehl, texthl, culhl, numhl) == OK) {
+ return 0;
}
- if (dict != NULL) {
- icon = tv_dict_get_string(dict, "icon", true);
- linehl = tv_dict_get_string(dict, "linehl", true);
- text = tv_dict_get_string(dict, "text", true);
- texthl = tv_dict_get_string(dict, "texthl", true);
- culhl = tv_dict_get_string(dict, "culhl", true);
- numhl = tv_dict_get_string(dict, "numhl", true);
- }
-
- if (sign_define_by_name(name, icon, linehl,
- text, texthl, culhl, numhl)
- == OK) {
- retval = 0;
- }
-
-cleanup:
- xfree(name);
- xfree(icon);
- xfree(linehl);
- xfree(text);
- xfree(texthl);
- xfree(culhl);
- xfree(numhl);
-
- return retval;
+
+ return -1;
}
/// Define multiple signs using attributes from list 'l' and store the return
/// values in 'retlist'.
static void sign_define_multiple(list_T *l, list_T *retlist)
{
- int retval;
-
TV_LIST_ITER_CONST(l, li, {
- retval = -1;
+ int retval = -1;
if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT) {
retval = sign_define_from_dict(NULL, TV_LIST_ITEM_TV(li)->vval.v_dict);
} else {
@@ -1995,8 +1196,6 @@ static void sign_define_multiple(list_T *l, list_T *retlist)
/// "sign_define()" function
void f_sign_define(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- const char *name;
-
if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_UNKNOWN) {
// Define multiple signs
tv_list_alloc_ret(rettv, kListLenMayKnow);
@@ -2008,7 +1207,7 @@ void f_sign_define(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
// Define a single sign
rettv->vval.v_number = -1;
- name = tv_get_string_chk(&argvars[0]);
+ char *name = (char *)tv_get_string_chk(&argvars[0]);
if (name == NULL) {
return;
}
@@ -2017,30 +1216,33 @@ void f_sign_define(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
- rettv->vval.v_number = sign_define_from_dict(name,
- argvars[1].v_type ==
- VAR_DICT ? argvars[1].vval.v_dict : NULL);
+ dict_T *d = argvars[1].v_type == VAR_DICT ? argvars[1].vval.v_dict : NULL;
+ rettv->vval.v_number = sign_define_from_dict(name, d);
}
/// "sign_getdefined()" function
void f_sign_getdefined(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- const char *name = NULL;
+ sign_T *sp;
tv_list_alloc_ret(rettv, 0);
- if (argvars[0].v_type != VAR_UNKNOWN) {
- name = tv_get_string(&argvars[0]);
+ if (argvars[0].v_type == VAR_UNKNOWN) {
+ map_foreach_value(&sign_map, sp, {
+ sign_list_append_info(sp, NULL, rettv->vval.v_list);
+ });
+ } else {
+ sp = pmap_get(cstr_t)(&sign_map, tv_get_string(&argvars[0]));
+ if (sp != NULL) {
+ sign_list_append_info(sp, NULL, rettv->vval.v_list);
+ }
}
-
- sign_getlist(name, rettv->vval.v_list);
}
/// "sign_getplaced()" function
void f_sign_getplaced(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
buf_T *buf = NULL;
- dictitem_T *di;
linenr_T lnum = 0;
int sign_id = 0;
const char *group = NULL;
@@ -2059,15 +1261,14 @@ void f_sign_getplaced(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (tv_check_for_nonnull_dict_arg(argvars, 1) == FAIL) {
return;
}
+ dictitem_T *di;
dict_T *dict = argvars[1].vval.v_dict;
if ((di = tv_dict_find(dict, "lnum", -1)) != NULL) {
// get signs placed at this line
- lnum = (linenr_T)tv_get_number_chk(&di->di_tv, &notanum);
- if (notanum) {
+ lnum = tv_get_lnum(&di->di_tv);
+ if (lnum <= 0) {
return;
}
- (void)lnum;
- lnum = tv_get_lnum(&di->di_tv);
}
if ((di = tv_dict_find(dict, "id", -1)) != NULL) {
// get sign placed with this identifier
@@ -2094,103 +1295,81 @@ void f_sign_getplaced(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "sign_jump()" function
void f_sign_jump(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- int sign_id;
- char *sign_group = NULL;
- buf_T *buf;
- bool notanum = false;
-
rettv->vval.v_number = -1;
// Sign identifier
- sign_id = (int)tv_get_number_chk(&argvars[0], &notanum);
+ bool notanum = false;
+ int id = (int)tv_get_number_chk(&argvars[0], &notanum);
if (notanum) {
return;
}
- if (sign_id <= 0) {
+ if (id <= 0) {
emsg(_(e_invarg));
return;
}
// Sign group
- const char *sign_group_chk = tv_get_string_chk(&argvars[1]);
- if (sign_group_chk == NULL) {
+ char *group = (char *)tv_get_string_chk(&argvars[1]);
+ if (group == NULL) {
return;
}
- if (sign_group_chk[0] == '\0') {
- sign_group = NULL; // global sign group
- } else {
- sign_group = xstrdup(sign_group_chk);
+ if (group[0] == NUL) {
+ group = NULL;
}
// Buffer to place the sign
- buf = get_buf_arg(&argvars[2]);
+ buf_T *buf = get_buf_arg(&argvars[2]);
if (buf == NULL) {
- goto cleanup;
+ return;
}
- rettv->vval.v_number = sign_jump(sign_id, sign_group, buf);
-
-cleanup:
- xfree(sign_group);
+ rettv->vval.v_number = sign_jump(id, group, buf);
}
/// Place a new sign using the values specified in dict 'dict'. Returns the sign
-/// identifier if successfully placed, otherwise returns 0.
+/// identifier if successfully placed, otherwise returns -1.
static int sign_place_from_dict(typval_T *id_tv, typval_T *group_tv, typval_T *name_tv,
typval_T *buf_tv, dict_T *dict)
{
- int sign_id = 0;
- char *group = NULL;
- const char *sign_name = NULL;
- buf_T *buf = NULL;
dictitem_T *di;
- linenr_T lnum = 0;
- int prio = SIGN_DEF_PRIO;
- bool notanum = false;
- int ret_sign_id = -1;
- // sign identifier
+ int id = 0;
+ bool notanum = false;
if (id_tv == NULL) {
di = tv_dict_find(dict, "id", -1);
if (di != NULL) {
id_tv = &di->di_tv;
}
}
- if (id_tv == NULL) {
- sign_id = 0;
- } else {
- sign_id = (int)tv_get_number_chk(id_tv, &notanum);
+ if (id_tv != NULL) {
+ id = (int)tv_get_number_chk(id_tv, &notanum);
if (notanum) {
return -1;
}
- if (sign_id < 0) {
+ if (id < 0) {
emsg(_(e_invarg));
return -1;
}
}
- // sign group
+ char *group = NULL;
if (group_tv == NULL) {
di = tv_dict_find(dict, "group", -1);
if (di != NULL) {
group_tv = &di->di_tv;
}
}
- if (group_tv == NULL) {
- group = NULL; // global group
- } else {
+ if (group_tv != NULL) {
group = (char *)tv_get_string_chk(group_tv);
if (group == NULL) {
- goto cleanup;
+ return -1;
}
- if (group[0] == '\0') { // global sign group
+ if (group[0] == NUL) {
group = NULL;
- } else {
- group = xstrdup(group);
}
}
- // sign name
+ char *name = NULL;
if (name_tv == NULL) {
di = tv_dict_find(dict, "name", -1);
if (di != NULL) {
@@ -2198,14 +1377,13 @@ static int sign_place_from_dict(typval_T *id_tv, typval_T *group_tv, typval_T *n
}
}
if (name_tv == NULL) {
- goto cleanup;
+ return -1;
}
- sign_name = tv_get_string_chk(name_tv);
- if (sign_name == NULL) {
- goto cleanup;
+ name = (char *)tv_get_string_chk(name_tv);
+ if (name == NULL) {
+ return -1;
}
- // buffer to place the sign
if (buf_tv == NULL) {
di = tv_dict_find(dict, "buffer", -1);
if (di != NULL) {
@@ -2213,40 +1391,38 @@ static int sign_place_from_dict(typval_T *id_tv, typval_T *group_tv, typval_T *n
}
}
if (buf_tv == NULL) {
- goto cleanup;
+ return -1;
}
- buf = get_buf_arg(buf_tv);
+ buf_T *buf = get_buf_arg(buf_tv);
if (buf == NULL) {
- goto cleanup;
+ return -1;
}
- // line number of the sign
+ linenr_T lnum = 0;
di = tv_dict_find(dict, "lnum", -1);
if (di != NULL) {
lnum = tv_get_lnum(&di->di_tv);
if (lnum <= 0) {
emsg(_(e_invarg));
- goto cleanup;
+ return -1;
}
}
- // sign priority
+ int prio = SIGN_DEF_PRIO;
di = tv_dict_find(dict, "priority", -1);
if (di != NULL) {
prio = (int)tv_get_number_chk(&di->di_tv, &notanum);
if (notanum) {
- goto cleanup;
+ return -1;
}
}
- if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK) {
- ret_sign_id = sign_id;
+ uint32_t uid = (uint32_t)id;
+ if (sign_place(&uid, group, name, buf, lnum, prio) == OK) {
+ return (int)uid;
}
-cleanup:
- xfree(group);
-
- return ret_sign_id;
+ return -1;
}
/// "sign_place()" function
@@ -2263,15 +1439,13 @@ void f_sign_place(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
dict = argvars[4].vval.v_dict;
}
- rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1], &argvars[2], &argvars[3],
- dict);
+ rettv->vval.v_number = sign_place_from_dict(&argvars[0], &argvars[1],
+ &argvars[2], &argvars[3], dict);
}
/// "sign_placelist()" function. Place multiple signs.
void f_sign_placelist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- int sign_id;
-
tv_list_alloc_ret(rettv, kListLenMayKnow);
if (argvars[0].v_type != VAR_LIST) {
@@ -2281,7 +1455,7 @@ void f_sign_placelist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
// Process the List of sign attributes
TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, {
- sign_id = -1;
+ int sign_id = -1;
if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT) {
sign_id = sign_place_from_dict(NULL, NULL, NULL, NULL, TV_LIST_ITEM_TV(li)->vval.v_dict);
} else {
@@ -2294,12 +1468,9 @@ void f_sign_placelist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// Undefine multiple signs
static void sign_undefine_multiple(list_T *l, list_T *retlist)
{
- char *name;
- int retval;
-
TV_LIST_ITER_CONST(l, li, {
- retval = -1;
- name = (char *)tv_get_string_chk(TV_LIST_ITEM_TV(li));
+ int retval = -1;
+ char *name = (char *)tv_get_string_chk(TV_LIST_ITEM_TV(li));
if (name != NULL && (sign_undefine_by_name(name) == OK)) {
retval = 0;
}
@@ -2342,57 +1513,31 @@ void f_sign_undefine(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
static int sign_unplace_from_dict(typval_T *group_tv, dict_T *dict)
{
dictitem_T *di;
- int sign_id = 0;
+ int id = 0;
buf_T *buf = NULL;
- char *group = NULL;
- int retval = -1;
-
- // sign group
- if (group_tv != NULL) {
- group = (char *)tv_get_string(group_tv);
- } else {
- group = tv_dict_get_string(dict, "group", false);
- }
- if (group != NULL) {
- if (group[0] == '\0') { // global sign group
- group = NULL;
- } else {
- group = xstrdup(group);
- }
+ char *group = (group_tv != NULL) ? (char *)tv_get_string(group_tv)
+ : tv_dict_get_string(dict, "group", false);
+ if (group != NULL && group[0] == NUL) {
+ group = NULL;
}
if (dict != NULL) {
if ((di = tv_dict_find(dict, "buffer", -1)) != NULL) {
buf = get_buf_arg(&di->di_tv);
if (buf == NULL) {
- goto cleanup;
+ return -1;
}
}
if (tv_dict_find(dict, "id", -1) != NULL) {
- sign_id = (int)tv_dict_get_number(dict, "id");
- if (sign_id <= 0) {
+ id = (int)tv_dict_get_number(dict, "id");
+ if (id <= 0) {
emsg(_(e_invarg));
- goto cleanup;
+ return -1;
}
}
}
- if (buf == NULL) {
- // Delete the sign in all the buffers
- retval = 0;
- FOR_ALL_BUFFERS(buf2) {
- if (sign_unplace(sign_id, group, buf2, 0) != OK) {
- retval = -1;
- }
- }
- } else if (sign_unplace(sign_id, group, buf, 0) == OK) {
- retval = 0;
- }
-
-cleanup:
- xfree(group);
-
- return retval;
+ return sign_unplace(buf, id, group, 0) ? 0 : -1;
}
/// "sign_unplace()" function
@@ -2417,8 +1562,6 @@ void f_sign_unplace(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "sign_unplacelist()" function
void f_sign_unplacelist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- int retval;
-
tv_list_alloc_ret(rettv, kListLenMayKnow);
if (argvars[0].v_type != VAR_LIST) {
@@ -2427,7 +1570,7 @@ void f_sign_unplacelist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, {
- retval = -1;
+ int retval = -1;
if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT) {
retval = sign_unplace_from_dict(NULL, TV_LIST_ITEM_TV(li)->vval.v_dict);
} else {
diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h
index 65ab7a72f6..6b8deca02f 100644
--- a/src/nvim/sign_defs.h
+++ b/src/nvim/sign_defs.h
@@ -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
-
-// 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;
+/// 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;
+
+#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
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
index 2f989491d0..a99313cc4e 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -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;
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index 9c16f76359..b3b3128fdd 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -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)
diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua
index 7dcd4cff25..31b54ce0a2 100644
--- a/test/functional/ui/sign_spec.lua
+++ b/test/functional/ui/sign_spec.lua
@@ -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 |
diff --git a/test/functional/ui/statuscolumn_spec.lua b/test/functional/ui/statuscolumn_spec.lua
index 9f00c7a5d6..61f6419f8c 100644
--- a/test/functional/ui/statuscolumn_spec.lua
+++ b/test/functional/ui/statuscolumn_spec.lua
@@ -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}|
diff --git a/test/old/testdir/test_signs.vim b/test/old/testdir/test_signs.vim
index 64271c588e..be81ed0077 100644
--- a/test/old/testdir/test_signs.vim
+++ b/test/old/testdir/test_signs.vim
@@ -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())