aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/sign.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/sign.c')
-rw-r--r--src/nvim/sign.c142
1 files changed, 86 insertions, 56 deletions
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index d05c708d2c..4e6672c5dd 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -18,22 +18,27 @@
#include "nvim/cmdexpand_defs.h"
#include "nvim/cursor.h"
#include "nvim/decoration.h"
+#include "nvim/decoration_defs.h"
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval/funcs.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/extmark.h"
#include "nvim/fold.h"
-#include "nvim/func_attr.h"
-#include "nvim/gettext.h"
+#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
+#include "nvim/grid.h"
+#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
+#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/macros_defs.h"
#include "nvim/map_defs.h"
#include "nvim/marktree.h"
+#include "nvim/marktree_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
@@ -46,8 +51,12 @@
#include "nvim/vim_defs.h"
#include "nvim/window.h"
-static PMap(cstr_t) sign_map INIT( = MAP_INIT);
-static kvec_t(Integer) sign_ns INIT( = MAP_INIT);
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "sign.c.generated.h"
+#endif
+
+static PMap(cstr_t) sign_map = MAP_INIT;
+static kvec_t(Integer) sign_ns = KV_INITIAL_VALUE;
static char *cmds[] = {
"define",
@@ -75,7 +84,7 @@ static int64_t group_get_ns(const char *group)
return UINT32_MAX; // All namespaces
}
// Specific or non-existing namespace
- int ns = map_get(String, int)(&namespace_ids, cstr_as_string((char *)group));
+ int ns = map_get(String, int)(&namespace_ids, cstr_as_string(group));
return ns ? ns : -1;
}
@@ -103,7 +112,7 @@ static void buf_set_sign(buf_T *buf, uint32_t *id, char *group, int prio, linenr
DecorSignHighlight sign = DECOR_SIGN_HIGHLIGHT_INIT;
sign.flags |= kSHIsSign;
- sign.text.ptr = sp->sn_text ? xstrdup(sp->sn_text) : NULL;
+ memcpy(sign.text, sp->sn_text, SIGN_WIDTH * sizeof(schar_T));
sign.sign_name = xstrdup(sp->sn_name);
sign.hl_id = sp->sn_text_hl;
sign.line_hl_id = sp->sn_line_hl;
@@ -112,12 +121,12 @@ static void buf_set_sign(buf_T *buf, uint32_t *id, char *group, int prio, linenr
sign.priority = (DecorPriority)prio;
bool has_hl = (sp->sn_line_hl || sp->sn_num_hl || sp->sn_cul_hl);
- uint16_t decor_flags = (sp->sn_text ? MT_FLAG_DECOR_SIGNTEXT : 0)
+ uint16_t decor_flags = (sp->sn_text[0] ? MT_FLAG_DECOR_SIGNTEXT : 0)
| (has_hl ? MT_FLAG_DECOR_SIGNHL : 0);
DecorInline decor = { .ext = true, .data.ext = { .vt = NULL, .sh_idx = decor_put_sh(sign) } };
extmark_set(buf, ns, id, lnum - 1, 0, -1, -1, decor, decor_flags, true,
- false, true, true, NULL);
+ false, true, true, false, NULL);
}
/// For an existing, placed sign with "id", modify the sign, group or priority.
@@ -159,24 +168,22 @@ static int buf_findsign(buf_T *buf, int id, char *group)
}
/// qsort() function to sort signs by line number, priority, id and recency.
-int sign_cmp(const void *p1, const void *p2)
+static int sign_row_cmp(const void *p1, const void *p2)
{
const MTKey *s1 = (MTKey *)p1;
const MTKey *s2 = (MTKey *)p2;
- int n = s1->pos.row - s2->pos.row;
- if (n) {
- return n;
+ if (s1->pos.row != s2->pos.row) {
+ return s1->pos.row > s2->pos.row ? 1 : -1;
}
DecorSignHighlight *sh1 = decor_find_sign(mt_decor(*s1));
DecorSignHighlight *sh2 = decor_find_sign(mt_decor(*s2));
assert(sh1 && sh2);
+ SignItem si1 = { sh1, s1->id };
+ SignItem si2 = { sh2, s2->id };
- n = sh2->priority - sh1->priority;
-
- return n ? n : (n = (int)(s2->id - s1->id))
- ? n : (sh2->sign_add_id - sh1->sign_add_id);
+ return sign_item_cmp(&si1, &si2);
}
/// Delete the specified sign(s)
@@ -232,7 +239,7 @@ static int buf_delete_signs(buf_T *buf, char *group, int id, linenr_T atlnum)
// Sort to remove the highest priority sign at a specific line number.
if (kv_size(signs)) {
- qsort((void *)&kv_A(signs, 0), kv_size(signs), sizeof(MTKey), sign_cmp);
+ qsort((void *)&kv_A(signs, 0), kv_size(signs), sizeof(MTKey), sign_row_cmp);
extmark_del_id(buf, kv_A(signs, 0).ns, kv_A(signs, 0).id);
kv_destroy(signs);
} else if (atlnum > 0) {
@@ -241,13 +248,18 @@ static int buf_delete_signs(buf_T *buf, char *group, int id, linenr_T atlnum)
// 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_signs_with_text && curwin != NULL) {
+ if (!buf_meta_total(buf, kMTMetaSignText) && curwin != NULL) {
changed_line_abv_curs();
}
return OK;
}
+bool buf_has_signs(const buf_T *buf)
+{
+ return (buf_meta_total(buf, kMTMetaSignHL) + buf_meta_total(buf, kMTMetaSignText));
+}
+
/// List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers.
static void sign_list_placed(buf_T *rbuf, char *group)
{
@@ -261,7 +273,7 @@ static void sign_list_placed(buf_T *rbuf, char *group)
msg_putchar('\n');
while (buf != NULL && !got_int) {
- if (buf->b_signs) {
+ if (buf_has_signs(buf)) {
vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname);
msg_puts_attr(lbuf, HL_ATTR(HLF_D));
msg_putchar('\n');
@@ -282,7 +294,7 @@ static void sign_list_placed(buf_T *rbuf, char *group)
}
if (kv_size(signs)) {
- qsort((void *)&kv_A(signs, 0), kv_size(signs), sizeof(MTKey), sign_cmp);
+ qsort((void *)&kv_A(signs, 0), kv_size(signs), sizeof(MTKey), sign_row_cmp);
for (size_t i = 0; i < kv_size(signs); i++) {
namebuf[0] = '\0';
@@ -332,9 +344,24 @@ static int sign_cmd_idx(char *begin_cmd, char *end_cmd)
return idx;
}
+/// buf must be SIGN_WIDTH * MAX_SCHAR_SIZE (no extra +1 needed)
+size_t describe_sign_text(char *buf, schar_T *sign_text)
+{
+ size_t p = 0;
+ for (int i = 0; i < SIGN_WIDTH; i++) {
+ schar_get(buf + p, sign_text[i]);
+ size_t len = strlen(buf + p);
+ if (len == 0) {
+ break;
+ }
+ p += len;
+ }
+ return p;
+}
+
/// 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)
+int init_sign_text(sign_T *sp, schar_T *sign_text, char *text)
{
char *s;
char *endp = text + (int)strlen(text);
@@ -349,34 +376,29 @@ int init_sign_text(sign_T *sp, char **sign_text, char *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))) {
+ int c;
+ sign_text[cells] = utfc_ptr2schar(s, &c);
+ if (!vim_isprintc(c)) {
break;
}
- cells += utf_ptr2cells(s);
+ int width = utf_char2cells(c);
+ if (width == 2) {
+ sign_text[cells + 1] = 0;
+ }
+ cells += width;
}
// Currently must be empty, one or two display cells
- if (s != endp || cells > 2) {
+ if (s != endp || cells > SIGN_WIDTH) {
if (sp != NULL) {
semsg(_("E239: Invalid sign text: %s"), text);
}
return FAIL;
}
- if (cells < 1) {
- if (sp != NULL) {
- sp->sn_text = NULL;
- }
- return OK;
- }
- 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(*sign_text + len - 1, " ");
+ if (cells < 1) {
+ sign_text[0] = 0;
+ } else if (cells == 1) {
+ sign_text[1] = schar_from_ascii(' ');
}
return OK;
@@ -396,7 +418,7 @@ static int sign_define_by_name(char *name, char *icon, char *text, char *linehl,
} else {
// 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_signs) {
+ if (buf_has_signs(wp->w_buffer)) {
redraw_buf_later(wp->w_buffer, UPD_NOT_VALID);
}
}
@@ -410,7 +432,7 @@ static int sign_define_by_name(char *name, char *icon, char *text, char *linehl,
backslash_halve((*sp)->sn_icon);
}
- if (text != NULL && (init_sign_text(*sp, &(*sp)->sn_text, text) == FAIL)) {
+ if (text != NULL && (init_sign_text(*sp, (*sp)->sn_text, text) == FAIL)) {
return FAIL;
}
@@ -435,7 +457,6 @@ static int sign_undefine_by_name(const char *name)
}
xfree(sp->sn_name);
- xfree(sp->sn_text);
xfree(sp->sn_icon);
xfree(sp);
return OK;
@@ -450,9 +471,11 @@ static void sign_list_defined(sign_T *sp)
msg_outtrans(sp->sn_icon, 0);
msg_puts(_(" (not supported)"));
}
- if (sp->sn_text != NULL) {
+ if (sp->sn_text[0]) {
msg_puts(" text=");
- msg_outtrans(sp->sn_text, 0);
+ char buf[SIGN_WIDTH * MAX_SCHAR_SIZE];
+ describe_sign_text(buf, sp->sn_text);
+ msg_outtrans(buf, 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 };
@@ -522,7 +545,7 @@ static int sign_place(uint32_t *id, char *group, char *name, buf_T *buf, linenr_
static int sign_unplace_inner(buf_T *buf, int id, char *group, linenr_T atlnum)
{
- if (!buf->b_signs) { // No signs in the buffer
+ if (!buf_has_signs(buf)) { // No signs in the buffer
return FAIL;
}
@@ -542,7 +565,7 @@ static int sign_unplace_inner(buf_T *buf, int id, char *group, linenr_T atlnum)
// 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_signs_with_text) {
+ if (!buf_meta_total(buf, kMTMetaSignText)) {
may_force_numberwidth_recompute(buf, true);
}
@@ -706,7 +729,7 @@ static void sign_jump_cmd(buf_T *buf, linenr_T lnum, const char *name, int id, c
return;
}
- (void)sign_jump(id, group, buf);
+ sign_jump(id, group, buf);
}
/// Parse the command line arguments for the ":sign place", ":sign unplace" and
@@ -718,7 +741,7 @@ static int parse_sign_cmd_args(int cmd, char *arg, char **name, int *id, char **
{
char *arg1 = arg;
char *filename = NULL;
- int lnum_arg = false;
+ bool lnum_arg = false;
// first arg could be placed sign id
if (ascii_isdigit(*arg)) {
@@ -787,7 +810,7 @@ static int parse_sign_cmd_args(int cmd, char *arg, char **name, int *id, char **
}
if (filename != NULL && *buf == NULL) {
- semsg(_("E158: Invalid buffer name: %s"), filename);
+ semsg(_(e_invalid_buffer_name_str), filename);
return FAIL;
}
@@ -879,8 +902,10 @@ static dict_T *sign_get_info_dict(sign_T *sp)
if (sp->sn_icon != NULL) {
tv_dict_add_str(d, S_LEN("icon"), sp->sn_icon);
}
- if (sp->sn_text != NULL) {
- tv_dict_add_str(d, S_LEN("text"), sp->sn_text);
+ if (sp->sn_text[0]) {
+ char buf[SIGN_WIDTH * MAX_SCHAR_SIZE];
+ describe_sign_text(buf, sp->sn_text);
+ tv_dict_add_str(d, S_LEN("text"), buf);
}
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 };
@@ -910,7 +935,7 @@ static dict_T *sign_get_placed_info_dict(MTKey mark)
/// 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
{
list_T *const l = tv_list_alloc(kListLenMayKnow);
MarkTreeIter itr[1];
@@ -940,7 +965,7 @@ static void sign_get_placed_in_buf(buf_T *buf, linenr_T lnum, int sign_id, const
tv_dict_add_list(d, S_LEN("signs"), l);
int64_t ns = group_get_ns(group);
- if (!buf->b_signs || ns < 0) {
+ if (!buf_has_signs(buf) || ns < 0) {
return;
}
@@ -967,7 +992,7 @@ static void sign_get_placed_in_buf(buf_T *buf, linenr_T lnum, int sign_id, const
}
if (kv_size(signs)) {
- qsort((void *)&kv_A(signs, 0), kv_size(signs), sizeof(MTKey), sign_cmp);
+ qsort((void *)&kv_A(signs, 0), kv_size(signs), sizeof(MTKey), sign_row_cmp);
for (size_t i = 0; i < kv_size(signs); i++) {
tv_list_append_dict(l, sign_get_placed_info_dict(kv_A(signs, i)));
}
@@ -984,7 +1009,7 @@ static void sign_get_placed(buf_T *buf, linenr_T lnum, int id, const char *group
sign_get_placed_in_buf(buf, lnum, id, group, retlist);
} else {
FOR_ALL_BUFFERS(cbuf) {
- if (cbuf->b_signs) {
+ if (buf_has_signs(cbuf)) {
sign_get_placed_in_buf(cbuf, 0, id, group, retlist);
}
}
@@ -994,9 +1019,14 @@ static void sign_get_placed(buf_T *buf, linenr_T lnum, int id, const char *group
void free_signs(void)
{
cstr_t name;
+ kvec_t(cstr_t) names = KV_INITIAL_VALUE;
map_foreach_key(&sign_map, name, {
- sign_undefine_by_name(name);
+ kv_push(names, name);
});
+ for (size_t i = 0; i < kv_size(names); i++) {
+ sign_undefine_by_name(kv_A(names, i));
+ }
+ kv_destroy(names);
}
static enum {