From fb9abd7d993cf2166b73810c7621f432bedd325d Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Tue, 7 May 2019 08:26:09 +0200 Subject: vim-patch:8.1.0614: placing signs can be complicated Problem: Placing signs can be complicated. Solution: Add functions for defining and placing signs. Introduce a group name to avoid different plugins using the same signs. (Yegappan Lakshmanan, closes vim/vim#3652) https://github.com/vim/vim/commit/162b71479bd4dcdb3a2ef9198a1444f6f99e6843 --- src/nvim/buffer.c | 250 ++++++++++++---- src/nvim/eval.c | 326 ++++++++++++++++++++- src/nvim/eval.lua | 6 + src/nvim/ex_cmds.c | 630 +++++++++++++++++++++++++++++++--------- src/nvim/globals.h | 5 + src/nvim/sign_defs.h | 5 + src/nvim/testdir/test_signs.vim | 626 ++++++++++++++++++++++++++++++++++++++- 7 files changed, 1637 insertions(+), 211 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index a5ad1f1a11..da65682fbe 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -805,7 +805,7 @@ free_buffer_stuff( hash_init(&buf->b_vars->dv_hashtab); buf_init_changedtick(buf); uc_clear(&buf->b_ucmds); // clear local user commands - buf_delete_signs(buf); // delete any signs + buf_delete_signs(buf, (char_u *)"*"); // delete any signs bufhl_clear_all(buf); // delete any highligts map_clear_int(buf, MAP_ALL_MODES, true, false); // clear local mappings map_clear_int(buf, MAP_ALL_MODES, true, true); // clear local abbrevs @@ -5256,13 +5256,16 @@ bool find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp) } /* - * Insert the sign into the signlist. + * Insert a new sign into the signlist for buffer 'buf' between the 'prev' and + * 'next' signs. */ static void insert_sign( buf_T *buf, // buffer to store sign in signlist_T *prev, // previous sign entry signlist_T *next, // next sign entry int id, // sign ID + char_u *group, // sign group; NULL for global group + int prio, // sign priority linenr_T lnum, // line number which gets the mark int typenr // typenr of sign we are adding ) @@ -5271,6 +5274,11 @@ static void insert_sign( newsign->id = id; newsign->lnum = lnum; newsign->typenr = typenr; + if (group != NULL) + newsign->group = vim_strsave(group); + else + newsign->group = NULL; + newsign->priority = prio; newsign->next = next; newsign->prev = prev; if (next != NULL) { @@ -5279,8 +5287,8 @@ static void insert_sign( buf->b_signcols_max = -1; if (prev == NULL) { - /* When adding first sign need to redraw the windows to create the - * column for signs. */ + // When adding first sign need to redraw the windows to create the + // column for signs. if (buf->b_signlist == NULL) { redraw_buf_later(buf, NOT_VALID); changed_cline_bef_curs(); @@ -5384,12 +5392,75 @@ int buf_signcols(buf_T *buf) return buf->b_signcols; } +/* + * Insert a new sign sorted by line number and sign priority. + */ +static void insert_sign_by_lnum_prio( + buf_T *buf, // buffer to store sign in + signlist_T *prev, // previous sign entry + int id, // sign ID + char_u *group, // sign group; NULL for global group + int prio, // sign priority + linenr_T lnum, // line number which gets the mark + int typenr // typenr of sign we are adding +) +{ + signlist_T *sign; + + // keep signs sorted by lnum and by priority: insert new sign at + // the proper position in the list for this lnum. + while (prev != NULL && prev->lnum == lnum && prev->priority <= prio) { + prev = prev->prev; + } + if (prev == NULL) { + sign = buf->b_signlist; + } else { + sign = prev->next; + } + + insert_sign(buf, prev, sign, id, group, prio, lnum, typenr); +} + +/* + * Returns 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. + */ +int sign_in_group(signlist_T *sign, char_u *group) +{ + return ((group != NULL && STRCMP(group, "*") == 0) || + (group == NULL && sign->group == NULL) || + (group != NULL && sign->group != NULL && + STRCMP(group, sign->group) == 0)); +} + +/* + * Return information about a sign in a Dict + */ +dict_T * sign_get_info(signlist_T *sign) +{ + dict_T *d; + + if ((d = tv_dict_alloc()) == NULL) { + return NULL; + } + tv_dict_add_nr(d, S_LEN("id"), sign->id); + tv_dict_add_str(d, S_LEN("group"), (sign->group == NULL) ? (char_u *)"" : sign->group); + tv_dict_add_nr(d, S_LEN("lnum"), sign->lnum); + tv_dict_add_str(d, S_LEN("name"), sign_typenr2name(sign->typenr)); + tv_dict_add_nr(d, S_LEN("priority"), sign->priority); + + return d; +} + /* * Add the sign into the signlist. Find the right spot to do it though. */ void buf_addsign( buf_T *buf, // buffer to store sign in int id, // sign ID + char_u *group, // sign group + int prio, // sign priority linenr_T lnum, // line number which gets the mark int typenr // typenr of sign we are adding ) @@ -5399,38 +5470,21 @@ void buf_addsign( signlist_T *prev; // the previous sign prev = NULL; - for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { - if (lnum == sign->lnum && id == sign->id) { - sign->typenr = typenr; - return; + FOR_ALL_SIGNS_IN_BUF(buf) { + if (lnum == sign->lnum && id == sign->id && + sign_in_group(sign, group)) { + // Update an existing sign + sign->typenr = typenr; + return; } else if ((lnum == sign->lnum && id != sign->id) || (id < 0 && lnum < sign->lnum)) { - // keep signs sorted by lnum: insert new sign at head of list for - // this lnum - while (prev != NULL && prev->lnum == lnum) { - prev = prev->prev; - } - if (prev == NULL) { - sign = buf->b_signlist; - } else { - sign = prev->next; - } - insert_sign(buf, prev, sign, id, lnum, typenr); + insert_sign_by_lnum_prio(buf, prev, id, group, prio, lnum, typenr); return; } prev = sign; } - // insert new sign at head of list for this lnum - while (prev != NULL && prev->lnum == lnum) { - prev = prev->prev; - } - if (prev == NULL) { - sign = buf->b_signlist; - } else { - sign = prev->next; - } - insert_sign(buf, prev, sign, id, lnum, typenr); + insert_sign_by_lnum_prio(buf, prev, id, group, prio, lnum, typenr); // Having more than one sign with _the same type_ and on the _same line_ is // unwanted, let's prevent it. @@ -5451,13 +5505,14 @@ void buf_addsign( linenr_T buf_change_sign_type( buf_T *buf, // buffer to store sign in int markId, // sign ID + char_u *group, // sign group int typenr // typenr of sign we are adding ) { signlist_T *sign; // a sign in the signlist - for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { - if (sign->id == markId) { + FOR_ALL_SIGNS_IN_BUF(buf) { + if (sign->id == markId && sign_in_group(sign, group)) { sign->typenr = typenr; return sign->lnum; } @@ -5484,7 +5539,7 @@ int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type, signlist_T *matches[9]; int nr_matches = 0; - for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { + FOR_ALL_SIGNS_IN_BUF(buf) { if (sign->lnum == lnum && (type == SIGN_ANY || (type == SIGN_TEXT @@ -5517,9 +5572,20 @@ int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type, return 0; } +/* + * 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. + * Returns the line number of the deleted sign. If multiple signs are deleted, + * then returns the line number of the last sign deleted. + */ linenr_T buf_delsign( buf_T *buf, // buffer sign is stored in - int id // sign id + int id, // sign id + char_u *group// sign group ) { signlist_T **lastp; // pointer to pointer to current sign @@ -5532,13 +5598,16 @@ linenr_T buf_delsign( lnum = 0; for (sign = buf->b_signlist; sign != NULL; sign = next) { next = sign->next; - if (sign->id == id) { + if ((id == 0 || sign->id == id) && sign_in_group(sign, group)) { *lastp = next; if (next != NULL) { next->prev = sign->prev; } lnum = sign->lnum; + xfree(sign->group); xfree(sign); + // Check whether only one sign needs to be deleted + if (group == NULL || (*group != '*' && id != 0)) break; } else { lastp = &sign->next; @@ -5557,19 +5626,20 @@ linenr_T buf_delsign( /* - * Find the line number of the sign with the requested id. If the sign does - * not exist, return 0 as the line number. This will still let the correct file - * get loaded. + * 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. */ int buf_findsign( buf_T *buf, // buffer to store sign in - int id // sign ID + int id, // sign ID + char_u *group // sign group ) { signlist_T *sign; // a sign in the signlist - for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { - if (sign->id == id) { + FOR_ALL_SIGNS_IN_BUF(buf) { + if (sign->id == id && sign_in_group(sign, group)){ return (int)sign->lnum; } } @@ -5577,6 +5647,49 @@ int buf_findsign( return 0; } +/* + * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is + * not found at the line. + */ +static signlist_T * buf_getsign_at_line( + buf_T *buf, // buffer whose sign we are searching for + linenr_T lnum // line number of sign +) +{ + signlist_T *sign; // a sign in the signlist + + FOR_ALL_SIGNS_IN_BUF(buf) { + if (sign->lnum == lnum) { + return sign; + } + } + + return NULL; +} + +/* + * Return the sign with identifier 'id' in group 'group' placed in buffer 'buf' + */ +signlist_T *buf_getsign_with_id( + buf_T *buf, // buffer whose sign we are searching for + int id, // sign identifier + char_u *group// sign group +) +{ + signlist_T *sign; // a sign in the signlist + + FOR_ALL_SIGNS_IN_BUF(buf) { + if (sign->id == id && sign_in_group(sign, group)) { + return sign; + } + } + + return NULL; +} + +/* + * Return the identifier of the sign at line number 'lnum' in buffer 'buf'. + */ int buf_findsign_id( buf_T *buf, // buffer whose sign we are searching for linenr_T lnum // line number of sign @@ -5584,10 +5697,9 @@ int buf_findsign_id( { signlist_T *sign; // a sign in the signlist - for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { - if (sign->lnum == lnum) { - return sign->id; - } + sign = buf_getsign_at_line(buf, lnum); + if (sign != NULL) { + return sign->id; } return 0; @@ -5597,8 +5709,10 @@ int buf_findsign_id( /* * Delete signs in buffer "buf". */ -void buf_delete_signs(buf_T *buf) +buf_delete_signs(buf_T *buf, char_u *group) { + signlist_T *sign; + signlist_T **lastp; // pointer to pointer to current sign signlist_T *next; // When deleting the last sign need to redraw the windows to remove the @@ -5608,10 +5722,19 @@ void buf_delete_signs(buf_T *buf) changed_cline_bef_curs(); } - while (buf->b_signlist != NULL) { - next = buf->b_signlist->next; - xfree(buf->b_signlist); - buf->b_signlist = next; + lastp = &buf->b_signlist; + for (sign = buf->b_signlist; sign != NULL; sign = next) { + next = sign->next; + if (sign_in_group(sign, group)) { + *lastp = next; + if (next != NULL) { + next->prev = sign->prev; + } + xfree(sign->group); + xfree(sign); + } else { + lastp = &sign->next; + } } buf->b_signcols_max = -1; } @@ -5623,7 +5746,7 @@ void buf_delete_all_signs(void) { FOR_ALL_BUFFERS(buf) { if (buf->b_signlist != NULL) { - buf_delete_signs(buf); + buf_delete_signs(buf, (char_u *)"*"); } } } @@ -5631,11 +5754,12 @@ void buf_delete_all_signs(void) /* * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers. */ -void sign_list_placed(buf_T *rbuf) +void sign_list_placed(buf_T *rbuf, char_u *sign_group) { buf_T *buf; - signlist_T *p; + signlist_T *sign; char lbuf[BUFSIZ]; + char group[BUFSIZ]; MSG_PUTS_TITLE(_("\n--- Signs ---")); msg_putchar('\n'); @@ -5650,11 +5774,21 @@ void sign_list_placed(buf_T *rbuf) MSG_PUTS_ATTR(lbuf, HL_ATTR(HLF_D)); msg_putchar('\n'); } - for (p = buf->b_signlist; p != NULL && !got_int; p = p->next) { - vim_snprintf(lbuf, BUFSIZ, _(" line=%" PRId64 " id=%d name=%s"), - (int64_t)p->lnum, p->id, sign_typenr2name(p->typenr)); - MSG_PUTS(lbuf); - msg_putchar('\n'); + FOR_ALL_SIGNS_IN_BUF(buf) { + if (!sign_in_group(sign, sign_group)) { + continue; + } + if (sign->group != NULL) { + vim_snprintf(group, BUFSIZ, " group=%s", sign->group); + } else { + group[0] = '\0'; + } + vim_snprintf(lbuf, BUFSIZ, _(" line=%ld id=%d%s name=%s " + "priority=%d"), + (long)sign->lnum, sign->id, group, + sign_typenr2name(sign->typenr), sign->priority); + MSG_PUTS(lbuf); + msg_putchar('\n'); } if (rbuf != NULL) { break; @@ -5675,7 +5809,7 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a curbuf->b_signcols_max = -1; lastp = &curbuf->b_signlist; - for (sign = curbuf->b_signlist; sign != NULL; sign = next) { + FOR_ALL_SIGNS_IN_BUF(curbuf) { next = sign->next; if (sign->lnum >= line1 && sign->lnum <= line2) { if (amount == MAXLNUM) { diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 9f56b42fba..b276b570b2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -9290,16 +9290,14 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) static list_T *get_buffer_signs(buf_T *buf) FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { + signlist_T *sign; + dict_T *d; list_T *const l = tv_list_alloc(kListLenMayKnow); - for (signlist_T *sign = buf->b_signlist; sign; sign = sign->next) { - dict_T *const d = tv_dict_alloc(); - - tv_dict_add_nr(d, S_LEN("id"), sign->id); - tv_dict_add_nr(d, S_LEN("lnum"), sign->lnum); - tv_dict_add_str(d, S_LEN("name"), - (const char *)sign_typenr2name(sign->typenr)); - tv_list_append_dict(l, d); + FOR_ALL_SIGNS_IN_BUF(buf) { + if ((d = sign_get_info(sign)) != NULL) { + tv_list_append_dict(l, d); + } } return l; } @@ -15438,6 +15436,318 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = get_sw_value(curbuf); } +/* + * "sign_define()" function + */ + static void +f_sign_define(typval_T *argvars, typval_T *rettv) +{ + char_u *name; + dict_T *dict; + char_u *icon = NULL; + char_u *linehl = NULL; + char_u *text = NULL; + char_u *texthl = NULL; + char_u *numhl = NULL; + + rettv->vval.v_number = -1; + + name = tv_get_string_chk(&argvars[0]); + if (name == NULL) + return; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + if (argvars[1].v_type != VAR_DICT) + { + EMSG(_(e_dictreq)); + return; + } + + // sign attributes + dict = argvars[1].vval.v_dict; + if (tv_dict_find(dict, (char_u *)"icon", -1) != NULL) + icon = tv_dict_get_string(dict, (char_u *)"icon", TRUE); + if (tv_dict_find(dict, (char_u *)"linehl", -1) != NULL) + linehl = tv_dict_get_string(dict, (char_u *)"linehl", TRUE); + if (tv_dict_find(dict, (char_u *)"text", -1) != NULL) + text = tv_dict_get_string(dict, (char_u *)"text", TRUE); + if (tv_dict_find(dict, (char_u *)"texthl", -1) != NULL) + texthl = tv_dict_get_string(dict, (char_u *)"texthl", TRUE); + if (tv_dict_find(dict, (char_u *)"numhl", -1) != NULL) + numhl = tv_dict_get_string(dict, (char_u *)"numhl", TRUE); + } + + if (sign_define_by_name(name, icon, linehl, text, texthl, numhl) == OK) + rettv->vval.v_number = 0; + + xfree(icon); + xfree(linehl); + xfree(text); + xfree(texthl); +} + +/* + * "sign_getdefined()" function + */ + static void +f_sign_getdefined(typval_T *argvars, typval_T *rettv) +{ + char_u *name = NULL; + + tv_list_alloc_ret(rettv, 0); + + if (argvars[0].v_type != VAR_UNKNOWN) + name = tv_get_string(&argvars[0]); + + sign_getlist(name, rettv->vval.v_list); +} + +/* + * "sign_getplaced()" function + */ + static void +f_sign_getplaced(typval_T *argvars, typval_T *rettv) +{ + buf_T *buf = NULL; + dict_T *dict; + dictitem_T *di; + linenr_T lnum = 0; + int sign_id = 0; + char_u *group = NULL; + int notanum = FALSE; + + tv_list_alloc_ret(rettv, 0); + + if (argvars[0].v_type != VAR_UNKNOWN) + { + // get signs placed in this buffer + buf = find_buffer(&argvars[0]); + if (buf == NULL) + { + EMSG2(_("E158: Invalid buffer name: %s"), + tv_get_string(&argvars[0])); + return; + } + + if (argvars[1].v_type != VAR_UNKNOWN) + { + if (argvars[1].v_type != VAR_DICT || + ((dict = argvars[1].vval.v_dict) == NULL)) + { + EMSG(_(e_dictreq)); + return; + } + if ((di = tv_dict_find(dict, (char_u *)"lnum", -1)) != NULL) + { + // get signs placed at this line + (void)tv_get_number_chk(&di->di_tv, ¬anum); + if (notanum) + return; + lnum = tv_get_lnum(&di->di_tv); + } + if ((di = tv_dict_find(dict, (char_u *)"id", -1)) != NULL) + { + // get sign placed with this identifier + sign_id = (int)tv_get_number_chk(&di->di_tv, ¬anum); + if (notanum) + return; + } + if ((di = tv_dict_find(dict, (char_u *)"group", -1)) != NULL) + { + group = tv_get_string_chk(&di->di_tv); + if (group == NULL) + return; + } + } + } + + sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list); +} + +/* + * "sign_place()" function + */ + static void +f_sign_place(typval_T *argvars, typval_T *rettv) +{ + int sign_id; + char_u *group = NULL; + char_u *sign_name; + buf_T *buf; + dict_T *dict; + dictitem_T *di; + linenr_T lnum = 0; + int prio = SIGN_DEF_PRIO; + int notanum = FALSE; + + rettv->vval.v_number = -1; + + // Sign identifer + sign_id = (int)tv_get_number_chk(&argvars[0], ¬anum); + if (notanum) + return; + if (sign_id < 0) + { + EMSG(_(e_invarg)); + return; + } + + // Sign group + group = tv_get_string_chk(&argvars[1]); + if (group == NULL) + return; + if (group[0] == '\0') + group = NULL; // global sign group + else + { + group = vim_strsave(group); + if (group == NULL) + return; + } + + // Sign name + sign_name = tv_get_string_chk(&argvars[2]); + if (sign_name == NULL) + goto cleanup; + + // Buffer to place the sign + buf = find_buffer(&argvars[3]); + if (buf == NULL) + { + EMSG2(_("E158: Invalid buffer name: %s"), tv_get_string(&argvars[2])); + goto cleanup; + } + + if (argvars[4].v_type != VAR_UNKNOWN) + { + if (argvars[4].v_type != VAR_DICT || + ((dict = argvars[4].vval.v_dict) == NULL)) + { + EMSG(_(e_dictreq)); + goto cleanup; + } + + // Line number where the sign is to be placed + if ((di = tv_dict_find(dict, (char_u *)"lnum", -1)) != NULL) + { + (void)tv_get_number_chk(&di->di_tv, ¬anum); + if (notanum) + goto cleanup; + lnum = tv_get_lnum(&di->di_tv); + } + if ((di = tv_dict_find(dict, (char_u *)"priority", -1)) != NULL) + { + // Sign priority + prio = (int)tv_get_number_chk(&di->di_tv, ¬anum); + if (notanum) + goto cleanup; + } + } + + if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK) + rettv->vval.v_number = sign_id; + +cleanup: + xfree(group); +} + +/* + * "sign_undefine()" function + */ + static void +f_sign_undefine(typval_T *argvars, typval_T *rettv) +{ + char_u *name; + + rettv->vval.v_number = -1; + + if (argvars[0].v_type == VAR_UNKNOWN) + { + // Free all the signs + free_signs(); + rettv->vval.v_number = 0; + } + else + { + // Free only the specified sign + name = tv_get_string_chk(&argvars[0]); + if (name == NULL) + return; + + if (sign_undefine_by_name(name) == OK) + rettv->vval.v_number = 0; + } +} + +/* + * "sign_unplace()" function + */ + static void +f_sign_unplace(typval_T *argvars, typval_T *rettv) +{ + dict_T *dict; + dictitem_T *di; + int sign_id = 0; + buf_T *buf = NULL; + char_u *group = NULL; + + rettv->vval.v_number = -1; + + if (argvars[0].v_type != VAR_STRING) + { + EMSG(_(e_invarg)); + return; + } + + group = tv_get_string(&argvars[0]); + if (group[0] == '\0') + group = NULL; // global sign group + else + { + group = vim_strsave(group); + if (group == NULL) + return; + } + + if (argvars[1].v_type != VAR_UNKNOWN) + { + if (argvars[1].v_type != VAR_DICT) + { + EMSG(_(e_dictreq)); + return; + } + dict = argvars[1].vval.v_dict; + + if ((di = tv_dict_find(dict, (char_u *)"buffer", -1)) != NULL) + { + buf = find_buffer(&di->di_tv); + if (buf == NULL) + { + EMSG2(_("E158: Invalid buffer name: %s"), + tv_get_string(&di->di_tv)); + return; + } + } + if (tv_dict_find(dict, (char_u *)"id", -1) != NULL) + sign_id = tv_dict_get_number(dict, (char_u *)"id"); + } + + if (buf == NULL) + { + // Delete the sign in all the buffers + FOR_ALL_BUFFERS(buf) + if (sign_unplace(sign_id, group, buf) == OK) + rettv->vval.v_number = 0; + } + else + { + if (sign_unplace(sign_id, group, buf) == OK) + rettv->vval.v_number = 0; + } + xfree(group); +} + /* * "simplify()" function */ diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index dea00c3edd..f583123025 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -277,6 +277,12 @@ return { sha256={args=1}, shellescape={args={1, 2}}, shiftwidth={}, + sign_define={args={1, 2}}, + sign_getdefined={args={0, 1}}, + sign_getplaced={args={0, 2}}, + sign_place={args={4, 5}}, + sign_undefine={args={0, 1}}, + sign_unplace={args={1, 2}}, simplify={args=1}, sin={args=1, func="float_op_wrapper", data="&sin"}, sinh={args=1, func="float_op_wrapper", data="&sinh"}, diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index e7f4736613..4a589ec6e5 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -5588,6 +5588,275 @@ static int sign_cmd_idx( return idx; } +/* + * Find a sign by name. Also returns pointer to the previous sign. + */ + static sign_T * +sign_find(char_u *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; +} + +/* + * Define a new sign or update an existing sign + */ +int sign_define_by_name( + char_u *name, + char_u *icon, + char_u *linehl, + char_u *text, + char_u *texthl, + char_u *numhl +) +{ + sign_T *sp_prev; + sign_T *sp; + + sp = sign_find(name, &sp_prev); + if (sp == NULL) + { + 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 FAIL; + } + 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 = vim_strsave(name); + + // add the new sign to the list of signs + if (sp_prev == NULL) { + first_sign = sp; + } else { + sp_prev->sn_next = sp; + } + } + + // set values for a defined sign. + if (icon != NULL) + { + xfree(sp->sn_icon); + sp->sn_icon = vim_strsave(icon); + backslash_halve(sp->sn_icon); +# ifdef FEAT_SIGN_ICONS + if (gui.in_use) + { + out_flush(); + if (sp->sn_image != NULL) + gui_mch_destroy_sign(sp->sn_image); + sp->sn_image = gui_mch_register_sign(sp->sn_icon); + } +# endif + } + + if (text != NULL) + { + char_u *s; + char_u *endp; + int cells; + int len; + + endp = text + (int)STRLEN(text); + for (s = text; s + 1 < endp; ++s) { + if (*s == '\\') + { + // 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; + for (s = text; s < endp; s += (*mb_ptr2len)(s)) { + if (!vim_isprintc(utf_ptr2char(s))) { + break; + } + cells += utf_ptr2cells(s); + } + // Currently must be one or two display cells + if (s != endp || cells < 1 || cells > 2) { + EMSG2(_("E239: Invalid sign text: %s"), text); + return FAIL; + } + + xfree(sp->sn_text); + // Allocate one byte more if we need to pad up + // with a space. + len = (int)(endp - text + ((cells == 1) ? 1 : 0)); + sp->sn_text = vim_strnsave(text, len); + + if (cells == 1) + STRCPY(sp->sn_text + len - 1, " "); + } + + if (linehl != NULL) + sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl)); + + if (texthl != NULL) + sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl)); + + if (numhl != NULL) + sp->sn_num_hl = syn_check_group(numhl, (int)STRLEN(numhl)); + + return OK; +} + +/* + * Free the sign specified by 'name'. + */ + int +sign_undefine_by_name(char_u *name) +{ + sign_T *sp_prev; + sign_T *sp; + + sp = sign_find(name, &sp_prev); + if (sp == NULL) + { + EMSG2(_("E155: Unknown sign: %s"), name); + return FAIL; + } + sign_undefine(sp, sp_prev); + + return OK; +} + +/* + * List the signs matching 'name' + */ + static void +sign_list_by_name(char_u *name) +{ + sign_T *sp; + + sp = sign_find(name, NULL); + if (sp != NULL) + sign_list_defined(sp); + else + EMSG2(_("E155: Unknown sign: %s"), name); +} + +/* + * Place a sign at the specifed file location or update a sign. + */ +int sign_place( + int *sign_id, + char_u *sign_group, + char_u *sign_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')) { + return FAIL; + } + + for (sp = first_sign; sp != NULL; sp = sp->sn_next) { + if (STRCMP(sp->sn_name, sign_name) == 0) { + break; + } + } + if (sp == NULL) + { + EMSG2(_("E155: Unknown sign: %s"), sign_name); + return FAIL; + } + if (*sign_id == 0) + { + // Allocate a new sign id + int id = 1; + signlist_T *sign; + + while ((sign = buf_getsign_with_id(buf, id, sign_group)) != NULL) { + id++; + } + + *sign_id = id; + } + + if (lnum > 0) { + // ":sign place {id} line={lnum} name={name} file={fname}": + // place a sign + buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr); + } else { + // ":sign place {id} file={fname}": change sign type + lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr); + } + if (lnum > 0) { + redraw_buf_line_later(buf, lnum); + } else { + EMSG2(_("E885: Not possible to change sign %s"), sign_name); + return FAIL; + } + + return OK; +} + +/* + * Unplace the specified sign + */ + int +sign_unplace(int sign_id, char_u *sign_group, buf_T *buf) +{ + if (sign_id == 0) + { + // Delete all the signs in the specified buffer + redraw_buf_later(buf, NOT_VALID); + buf_delete_signs(buf, sign_group); + } else { + linenr_T lnum; + + // Delete only the specified signs + lnum = buf_delsign(buf, sign_id, sign_group); + if (lnum == 0) { + return FAIL; + } + redraw_buf_line_later(buf, lnum); + } + + return OK; +} + /* * ":sign" command */ @@ -5597,7 +5866,6 @@ void ex_sign(exarg_T *eap) char_u *p; int idx; sign_T *sp; - sign_T *sp_prev; // Parse the subcommand. p = skiptowhite(arg); @@ -5618,6 +5886,13 @@ void ex_sign(exarg_T *eap) } else if (*arg == NUL) { EMSG(_("E156: Missing sign name")); } else { + char_u *name; + char_u *icon = NULL; + char_u *text = NULL; + char_u *linehl = NULL; + char_u *texthl = NULL; + char_u *numhl = NULL; + // 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); @@ -5627,57 +5902,11 @@ void ex_sign(exarg_T *eap) while (arg[0] == '0' && arg[1] != NUL) { arg++; } + name = vim_strsave(arg); - sp_prev = NULL; - for (sp = first_sign; sp != NULL; sp = sp->sn_next) { - if (STRCMP(sp->sn_name, arg) == 0) { - break; - } - sp_prev = sp; - } if (idx == SIGNCMD_DEFINE) { + int failed = FALSE; // ":sign define {name} ...": define a sign - if (sp == NULL) { - 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; - } - 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 = vim_strsave(arg); - - // add the new sign to the list of signs - if (sp_prev == NULL) { - first_sign = sp; - } else { - sp_prev->sn_next = sp; - } - } // set values for a defined sign. for (;;) { @@ -5688,88 +5917,62 @@ void ex_sign(exarg_T *eap) p = skiptowhite_esc(arg); if (STRNCMP(arg, "icon=", 5) == 0) { arg += 5; - xfree(sp->sn_icon); - sp->sn_icon = vim_strnsave(arg, (int)(p - arg)); - backslash_halve(sp->sn_icon); + icon = vim_strnsave(arg, (int)(p - arg)); } else if (STRNCMP(arg, "text=", 5) == 0) { - char_u *s; - int cells; - int len; - arg += 5; - for (s = arg; s + 1 < p; s++) { - if (*s == '\\') { - // Remove a backslash, so that it is possible - // to use a space. - STRMOVE(s, s + 1); - p--; - } - } - - // Count cells and check for non-printable chars - cells = 0; - for (s = arg; s < p; s += utfc_ptr2len(s)) { - if (!vim_isprintc(utf_ptr2char(s))) { - break; - } - cells += utf_ptr2cells(s); - } - // Currently must be one or two display cells - if (s != p || cells < 1 || cells > 2) { - *p = NUL; - EMSG2(_("E239: Invalid sign text: %s"), arg); - return; - } - - xfree(sp->sn_text); - // Allocate one byte more if we need to pad up - // with a space. - len = (int)(p - arg + ((cells == 1) ? 1 : 0)); - sp->sn_text = vim_strnsave(arg, len); - - if (cells == 1) { - STRCPY(sp->sn_text + len - 1, " "); - } + text = vim_strnsave(arg, (int)(p - arg)); } else if (STRNCMP(arg, "linehl=", 7) == 0) { arg += 7; - sp->sn_line_hl = syn_check_group(arg, (int)(p - arg)); + linehl = vim_strnsave(arg, (int)(p - arg)); } else if (STRNCMP(arg, "texthl=", 7) == 0) { arg += 7; - sp->sn_text_hl = syn_check_group(arg, (int)(p - arg)); + texthl = vim_strnsave(arg, (int)(p - arg)); } else if (STRNCMP(arg, "numhl=", 6) == 0) { arg += 6; - sp->sn_num_hl = syn_check_group(arg, (int)(p - arg)); + numhl = vim_strnsave(arg, (int)(p - arg)); } else { EMSG2(_(e_invarg2), arg); - return; + failed = TRUE; + break; } } - } else if (sp == NULL) { - EMSG2(_("E155: Unknown sign: %s"), arg); + + if (!failed) + sign_define_by_name(name, icon, linehl, text, texthl, numhl); + + xfree(icon); + xfree(text); + xfree(linehl); + xfree(texthl); } else if (idx == SIGNCMD_LIST) { // ":sign list {name}" - sign_list_defined(sp); + sign_list_by_name(name); } else { // ":sign undefine {name}" - sign_undefine(sp, sp_prev); + sign_undefine_by_name(name); } + + xfree(name); + return; } } else { int id = -1; linenr_T lnum = -1; char_u *sign_name = NULL; + char_u *group = NULL; + int prio = SIGN_DEF_PRIO; char_u *arg1; + int bufarg = FALSE; if (*arg == NUL) { if (idx == SIGNCMD_PLACE) { // ":sign place": list placed signs in all buffers - sign_list_placed(NULL); + sign_list_placed(NULL, NULL); } else if (idx == SIGNCMD_UNPLACE) { // ":sign unplace": remove placed sign at cursor id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum); if (id > 0) { - buf_delsign(curwin->w_buffer, id); - redraw_buf_line_later(curwin->w_buffer, curwin->w_cursor.lnum); + sign_unplace(id, NULL, curwin->w_buffer); } else { EMSG(_("E159: Missing sign number")); } @@ -5797,9 +6000,7 @@ void ex_sign(exarg_T *eap) if (idx == SIGNCMD_UNPLACE && *arg == NUL) { // ":sign unplace {id}": remove placed sign by number FOR_ALL_BUFFERS(buf) { - if ((lnum = buf_delsign(buf, id)) != 0) { - redraw_buf_line_later(buf, lnum); - } + sign_unplace(id, NULL, buf); } return; } @@ -5810,7 +6011,7 @@ void ex_sign(exarg_T *eap) // Leave "arg" pointing to {fname}. buf_T *buf = NULL; - for (;;) { + while (*arg != NUL) { if (STRNCMP(arg, "line=", 5) == 0) { arg += 5; lnum = atoi((char *)arg); @@ -5832,9 +6033,21 @@ void ex_sign(exarg_T *eap) while (sign_name[0] == '0' && sign_name[1] != NUL) { sign_name++; } + } else if (STRNCMP(arg, "group=", 6) == 0) { + arg += 6; + group = arg; + arg = skiptowhite(arg); + if (*arg != NUL) { + *arg++ = NUL; + } + } else if (STRNCMP(arg, "priority=", 9) == 0) { + arg += 9; + prio = atoi((char *)arg); + arg = skiptowhite(arg); } else if (STRNCMP(arg, "file=", 5) == 0) { arg += 5; - buf = buflist_findname(arg); + buf = buflist_findname_exp(arg); + bufarg = TRUE; break; } else if (STRNCMP(arg, "buffer=", 7) == 0) { arg += 7; @@ -5842,6 +6055,7 @@ void ex_sign(exarg_T *eap) if (*skipwhite(arg) != NUL) { EMSG(_(e_trailing)); } + bufarg = TRUE; break; } else { EMSG(_(e_invarg)); @@ -5850,20 +6064,28 @@ void ex_sign(exarg_T *eap) arg = skipwhite(arg); } - if (buf == NULL) { + if ((!bufarg && group == NULL) || (group != NULL && *group == '\0')) { + // File or buffer is not specified or an empty group is used + EMSG(_(e_invarg)); + return; + } + + if (bufarg && buf == NULL) { EMSG2(_("E158: Invalid buffer name: %s"), arg); } else if (id <= 0 && !(idx == SIGNCMD_UNPLACE && id == -2)) { - if (lnum >= 0 || sign_name != NULL) { + if ((group == NULL) && (lnum >= 0 || sign_name != NULL)) { EMSG(_(e_invarg)); } else { // ":sign place file={fname}": list placed signs in one file - sign_list_placed(buf); + // ":sign place group={group} file={fname}" + // ":sign place group=* file={fname}" + sign_list_placed(buf, group); } } else if (idx == SIGNCMD_JUMP) { // ":sign jump {id} file={fname}" if (lnum >= 0 || sign_name != NULL) { EMSG(_(e_invarg)); - } else if ((lnum = buf_findsign(buf, id)) > 0) { + } else if ((lnum = buf_findsign(buf, id, group)) > 0) { // goto a sign ... if (buf_jump_open_win(buf) != NULL) { // ... in a current window @@ -5892,44 +6114,174 @@ void ex_sign(exarg_T *eap) if (lnum >= 0 || sign_name != NULL) { EMSG(_(e_invarg)); } else if (id == -2) { - // ":sign unplace * file={fname}" - redraw_buf_later(buf, NOT_VALID); - buf_delete_signs(buf); - } else { - // ":sign unplace {id} file={fname}" - lnum = buf_delsign(buf, id); - redraw_buf_line_later(buf, lnum); - } - } else if (sign_name != NULL) { - // idx == SIGNCMD_PLACE - for (sp = first_sign; sp != NULL; sp = sp->sn_next) { - if (STRCMP(sp->sn_name, sign_name) == 0) { - break; + if (buf != NULL) { + // ":sign unplace * file={fname}" + sign_unplace(0, group, buf); + } else { + // ":sign unplace * group=*": remove all placed signs + FOR_ALL_BUFFERS(buf) { + if (buf->b_signlist != NULL) { + buf_delete_signs(buf, group); + } + } } - } - if (sp == NULL) { - EMSG2(_("E155: Unknown sign: %s"), sign_name); - return; - } - if (lnum > 0) { - // ":sign place {id} line={lnum} name={name} file={fname}": - // place a sign - buf_addsign(buf, id, lnum, sp->sn_typenr); - } else { - // ":sign place {id} file={fname}": change sign type - lnum = buf_change_sign_type(buf, id, sp->sn_typenr); - } - if (lnum > 0) { - redraw_buf_line_later(buf, lnum); } else { - EMSG2(_("E885: Not possible to change sign %s"), sign_name); + if (buf != NULL) { + // ":sign unplace {id} file={fname}" + // ":sign unplace {id} group={group} file={fname}" + // ":sign unplace {id} group=* file={fname}" + sign_unplace(id, group, buf); + } else { + // ":sign unplace {id} group={group}": + // ":sign unplace {id} group=*": + // remove all placed signs in this group. + FOR_ALL_BUFFERS(buf) { + if (buf->b_signlist != NULL) { + sign_unplace(id, group, buf); + } + } + } } + } else if (sign_name != NULL && buf != NULL) { + // idx == SIGNCMD_PLACE + sign_place(&id, group, sign_name, buf, lnum, prio); } else { EMSG(_(e_invarg)); } } } +/* + * Return information about a specified sign + */ +static void sign_getinfo(sign_T *sp, dict_T *retdict) +{ + char_u *p; + + tv_dict_add_str(retdict, S_LEN("name"), (char_u *)sp->sn_name); + if (sp->sn_icon != NULL) { + tv_dict_add_str(retdict, S_LEN("icon"), (char_u *)sp->sn_icon); + } + if (sp->sn_text != NULL) { + tv_dict_add_str(retdict, S_LEN("text"), (char_u *)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 = (char_u *)"NONE"; + } + tv_dict_add_str(retdict, S_LEN("linehl"), (char_u *)p); + } + if (sp->sn_text_hl > 0) { + p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE); + if (p == NULL) { + p = (char_u *)"NONE"; + } + tv_dict_add_str(retdict, S_LEN("texthl"), (char_u *)p); + } + if (sp->sn_num_hl > 0) { + p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, FALSE); + if (p == NULL) { + p = (char_u *)"NONE"; + } + tv_dict_add_str(retdict, S_LEN("numhl"), (char_u *)p); + } +} + +/* + * If 'name' is NULL, return a list of all the defined signs. + * Otherwise, return information about the specified sign. + */ +void sign_getlist(char_u *name, list_T *retlist) +{ + sign_T *sp = first_sign; + dict_T *dict; + + if (name != NULL) { + sp = sign_find(name, NULL); + if (sp == NULL) { + return; + } + } + + for (; sp != NULL && !got_int; sp = sp->sn_next) { + if ((dict = tv_dict_alloc()) == NULL) { + return; + } + tv_list_append_dict(retlist, dict); + sign_getinfo(sp, dict); + + if (name != NULL) { // handle only the specified sign + break; + } + } +} + +/* + * 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, + char_u *sign_group, + list_T *retlist) +{ + dict_T *d; + list_T *l; + signlist_T *sign; + dict_T *sdict; + + if ((d = tv_dict_alloc()) == NULL) { + return; + } + tv_list_append_dict(retlist, d); + + tv_dict_add_nr(d, S_LEN("bufnr"), (long)buf->b_fnum); + + if ((l = tv_list_alloc(kListLenMayKnow)) == NULL) { + return; + } + tv_dict_add_list(d, S_LEN("signs"), l); + + FOR_ALL_SIGNS_IN_BUF(buf) { + if (!sign_in_group(sign, sign_group)) { + continue; + } + if ((lnum == 0 && sign_id == 0) || + (sign_id == 0 && lnum == sign->lnum) || + (lnum == 0 && sign_id == sign->id) || + (lnum == sign->lnum && sign_id == sign->id)) { + if ((sdict = sign_get_info(sign)) != NULL) { + tv_list_append_dict(l, sdict); + } + } + } +} + +/* + * 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. + */ +void sign_get_placed( + buf_T *buf, + linenr_T lnum, + int sign_id, + char_u *sign_group, + list_T *retlist) +{ + if (buf != NULL) { + sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist); + } else { + FOR_ALL_BUFFERS(buf) { + if (buf->b_signlist != NULL) { + sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist); + } + } + } +} + /* * List one sign. */ @@ -6050,7 +6402,6 @@ char_u * sign_typenr2name(int typenr) return (char_u *)_("[Deleted]"); } -#if defined(EXITFREE) /* * Undefine/free all signs. */ @@ -6059,7 +6410,6 @@ void free_signs(void) while (first_sign != NULL) sign_undefine(first_sign, NULL); } -#endif static enum { diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 9fa294ba87..9890cba637 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -482,6 +482,11 @@ EXTERN buf_T *curbuf INIT(= NULL); // currently active buffer #define FOR_ALL_BUFFERS_BACKWARDS(buf) \ for (buf_T *buf = lastbuf; buf != NULL; buf = buf->b_prev) +// Iterate through all the signs placed in a buffer +#define FOR_ALL_SIGNS_IN_BUF(buf) \ + for (sign = buf->b_signlist; sign != NULL; sign = sign->next) + + /* * List of files being edited (global argument list). curwin->w_alist points * to this when the window is using the global argument list. diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h index b4f2709d45..313fb330ed 100644 --- a/src/nvim/sign_defs.h +++ b/src/nvim/sign_defs.h @@ -12,10 +12,15 @@ struct signlist int id; // unique identifier for each placed sign linenr_T lnum; // line number which has this sign int typenr; // typenr of sign + char_u *group; // sign group + int priority; // priority for highlighting signlist_T *next; // next signlist entry signlist_T *prev; // previous entry -- for easy reordering }; +// Default sign priority for highlighting +#define SIGN_DEF_PRIO 10 + // type argument for buf_getsigntype() and sign_get_attr() typedef enum { SIGN_ANY, diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index 3960177acd..5e2ac3fb94 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -16,8 +16,8 @@ func Test_sign() try sign define Sign2 text=xy texthl=Title linehl=Error numhl=Number icon=../../pixmaps/stock_vim_find_help.png catch /E255:/ - " ignore error: E255: Couldn't read in sign data! - " This error can happen when running in gui. + " Ignore error: E255: Couldn't read in sign data! + " This error can happen when running in the GUI. " Some gui like Motif do not support the png icon format. endtry @@ -63,7 +63,7 @@ func Test_sign() " Check placed signs let a=execute('sign place') - call assert_equal("\n--- Signs ---\nSigns for [NULL]:\n line=3 id=41 name=Sign1\n", a) + call assert_equal("\n--- Signs ---\nSigns for [NULL]:\n line=3 id=41 name=Sign1 priority=10\n", a) " Unplace the sign and try jumping to it again should fail. sign unplace 41 @@ -112,7 +112,7 @@ func Test_sign() " Only 1 or 2 character text is allowed call assert_fails("sign define Sign4 text=abc linehl=Comment", 'E239:') call assert_fails("sign define Sign4 text= linehl=Comment", 'E239:') - call assert_fails("sign define Sign4 text=\ ab linehl=Comment", 'E239:') + call assert_fails("sign define Sign4 text=\\ ab linehl=Comment", 'E239:') " define sign with whitespace sign define Sign4 text=\ X linehl=Comment @@ -131,6 +131,28 @@ func Test_sign() sign define Sign4 text=\\ linehl=Comment sign undefine Sign4 + " Error cases + call assert_fails("exe 'sign place abc line=3 name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("exe 'sign unplace abc name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("exe 'sign place 1abc line=3 name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("exe 'sign unplace 2abc name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign unplace 2 *", 'E474:') + call assert_fails("exe 'sign place 1 line=3 name=Sign1 buffer=' . bufnr('%') a", 'E488:') + call assert_fails("exe 'sign place name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("exe 'sign place line=10 buffer=' . bufnr('%')", 'E474:') + call assert_fails("exe 'sign unplace 2 line=10 buffer=' . bufnr('%')", 'E474:') + call assert_fails("exe 'sign unplace 2 name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("exe 'sign place 2 line=3 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign place 2", 'E474:') + call assert_fails("sign place abc", 'E474:') + call assert_fails("sign place 5 line=3", 'E474:') + call assert_fails("sign place 5 name=Sign1", 'E474:') + call assert_fails("sign place 5 group=g1", 'E474:') + call assert_fails("sign place 5 group=*", 'E474:') + call assert_fails("sign place 5 priority=10", 'E474:') + call assert_fails("sign place 5 line=3 name=Sign1", 'E474:') + call assert_fails("sign place 5 group=g1 line=3 name=Sign1", 'E474:') + " After undefining the sign, we should no longer be able to place it. sign undefine Sign1 sign undefine Sign2 @@ -152,7 +174,7 @@ func Test_sign_undefine_still_placed() " Listing placed sign should show that sign is deleted. let a=execute('sign place') - call assert_equal("\n--- Signs ---\nSigns for foobar:\n line=1 id=41 name=[Deleted]\n", a) + call assert_equal("\n--- Signs ---\nSigns for foobar:\n line=1 id=41 name=[Deleted] priority=10\n", a) sign unplace 41 let a=execute('sign place') @@ -202,6 +224,8 @@ func Test_sign_completion() endfunc func Test_sign_invalid_commands() + sign define Sign1 text=x + call assert_fails('sign', 'E471:') call assert_fails('sign jump', 'E471:') call assert_fails('sign xxx', 'E160:') @@ -211,6 +235,52 @@ func Test_sign_invalid_commands() call assert_fails('sign list xxx', 'E155:') call assert_fails('sign place 1 buffer=999', 'E158:') call assert_fails('sign define Sign2 text=', 'E239:') + " Non-numeric identifier for :sign place + call assert_fails("exe 'sign place abc line=3 name=Sign1 buffer=' . bufnr('%')", 'E474:') + " Non-numeric identifier for :sign unplace + call assert_fails("exe 'sign unplace abc name=Sign1 buffer=' . bufnr('%')", 'E474:') + " Number followed by an alphabet as sign identifier for :sign place + call assert_fails("exe 'sign place 1abc line=3 name=Sign1 buffer=' . bufnr('%')", 'E474:') + " Number followed by an alphabet as sign identifier for :sign unplace + call assert_fails("exe 'sign unplace 2abc name=Sign1 buffer=' . bufnr('%')", 'E474:') + " Sign identifier and '*' for :sign unplace + call assert_fails("sign unplace 2 *", 'E474:') + " Trailing characters after buffer number for :sign place + call assert_fails("exe 'sign place 1 line=3 name=Sign1 buffer=' . bufnr('%') . 'xxx'", 'E488:') + " Trailing characters after buffer number for :sign unplace + call assert_fails("exe 'sign unplace 1 buffer=' . bufnr('%') . 'xxx'", 'E488:') + call assert_fails("exe 'sign unplace * buffer=' . bufnr('%') . 'xxx'", 'E488:') + call assert_fails("sign unplace 1 xxx", 'E474:') + call assert_fails("sign unplace * xxx", 'E474:') + call assert_fails("sign unplace xxx", 'E474:') + " Placing a sign without line number + call assert_fails("exe 'sign place name=Sign1 buffer=' . bufnr('%')", 'E474:') + " Placing a sign without sign name + call assert_fails("exe 'sign place line=10 buffer=' . bufnr('%')", 'E474:') + " Unplacing a sign with line number + call assert_fails("exe 'sign unplace 2 line=10 buffer=' . bufnr('%')", 'E474:') + " Unplacing a sign with sign name + call assert_fails("exe 'sign unplace 2 name=Sign1 buffer=' . bufnr('%')", 'E474:') + " Placing a sign without sign name + call assert_fails("exe 'sign place 2 line=3 buffer=' . bufnr('%')", 'E474:') + " Placing a sign with only sign identifier + call assert_fails("sign place 2", 'E474:') + " Placing a sign with only a name + call assert_fails("sign place abc", 'E474:') + " Placing a sign with only line number + call assert_fails("sign place 5 line=3", 'E474:') + " Placing a sign with only sign name + call assert_fails("sign place 5 name=Sign1", 'E474:') + " Placing a sign with only sign group + call assert_fails("sign place 5 group=g1", 'E474:') + call assert_fails("sign place 5 group=*", 'E474:') + " Placing a sign with only sign priority + call assert_fails("sign place 5 priority=10", 'E474:') + " Placing a sign without buffer number or file name + call assert_fails("sign place 5 line=3 name=Sign1", 'E474:') + call assert_fails("sign place 5 group=g1 line=3 name=Sign1", 'E474:') + + sign undefine Sign1 endfunc func Test_sign_delete_buffer() @@ -224,3 +294,549 @@ func Test_sign_delete_buffer() sign unplace 61 sign undefine Sign endfunc + +" Test for VimL functions for managing signs +func Test_sign_funcs() + " Remove all the signs + call sign_unplace('*') + call sign_undefine() + + " Tests for sign_define() + let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error'} + call assert_equal(0, sign_define("sign1", attr)) + call assert_equal([{'name' : 'sign1', 'texthl' : 'Error', + \ 'linehl' : 'Search', 'text' : '=>'}], sign_getdefined()) + + " Define a new sign without attributes and then update it + call sign_define("sign2") + let attr = {'text' : '!!', 'linehl' : 'DiffAdd', 'texthl' : 'DiffChange', + \ 'icon' : 'sign2.ico'} + try + call sign_define("sign2", attr) + catch /E255:/ + " ignore error: E255: Couldn't read in sign data! + " This error can happen when running in gui. + endtry + call assert_equal([{'name' : 'sign2', 'texthl' : 'DiffChange', + \ 'linehl' : 'DiffAdd', 'text' : '!!', 'icon' : 'sign2.ico'}], + \ sign_getdefined("sign2")) + + " Test for a sign name with digits + call assert_equal(0, sign_define(0002, {'linehl' : 'StatusLine'})) + call assert_equal([{'name' : '2', 'linehl' : 'StatusLine'}], + \ sign_getdefined(0002)) + call sign_undefine(0002) + + " Tests for invalid arguments to sign_define() + call assert_fails('call sign_define("sign4", {"text" : "===>"})', 'E239:') + call assert_fails('call sign_define("sign5", {"text" : ""})', 'E239:') + call assert_fails('call sign_define([])', 'E730:') + call assert_fails('call sign_define("sign6", [])', 'E715:') + + " Tests for sign_getdefined() + call assert_equal([], sign_getdefined("none")) + call assert_fails('call sign_getdefined({})', 'E731:') + + " Tests for sign_place() + call writefile(repeat(["Sun is shining"], 30), "Xsign") + edit Xsign + + call assert_equal(10, sign_place(10, '', 'sign1', 'Xsign', + \ {'lnum' : 20})) + call assert_equal([{'bufnr' : bufnr(''), 'signs' : + \ [{'id' : 10, 'group' : '', 'lnum' : 20, 'name' : 'sign1', + \ 'priority' : 10}]}], sign_getplaced('Xsign')) + call assert_equal([{'bufnr' : bufnr(''), 'signs' : + \ [{'id' : 10, 'group' : '', 'lnum' : 20, 'name' : 'sign1', + \ 'priority' : 10}]}], + \ sign_getplaced('Xsign', {'lnum' : 20})) + call assert_equal([{'bufnr' : bufnr(''), 'signs' : + \ [{'id' : 10, 'group' : '', 'lnum' : 20, 'name' : 'sign1', + \ 'priority' : 10}]}], + \ sign_getplaced('Xsign', {'id' : 10})) + + " Tests for invalid arguments to sign_place() + call assert_fails('call sign_place([], "", "mySign", 1)', 'E745:') + call assert_fails('call sign_place(5, "", "mySign", -1)', 'E158:') + call assert_fails('call sign_place(-1, "", "sign1", "Xsign", [])', + \ 'E474:') + call assert_fails('call sign_place(-1, "", "sign1", "Xsign", + \ {"lnum" : 30})', 'E474:') + call assert_fails('call sign_place(10, "", "xsign1x", "Xsign", + \ {"lnum" : 30})', 'E155:') + call assert_fails('call sign_place(10, "", "", "Xsign", + \ {"lnum" : 30})', 'E155:') + call assert_fails('call sign_place(10, "", [], "Xsign", + \ {"lnum" : 30})', 'E730:') + call assert_fails('call sign_place(5, "", "sign1", "abcxyz.xxx", + \ {"lnum" : 10})', 'E158:') + call assert_fails('call sign_place(5, "", "sign1", "", {"lnum" : 10})', + \ 'E158:') + call assert_fails('call sign_place(5, "", "sign1", [], {"lnum" : 10})', + \ 'E158:') + call assert_fails('call sign_place(21, "", "sign1", "Xsign", + \ {"lnum" : -1})', 'E885:') + call assert_fails('call sign_place(22, "", "sign1", "Xsign", + \ {"lnum" : 0})', 'E885:') + call assert_equal(-1, sign_place(1, "*", "sign1", "Xsign", {"lnum" : 10})) + + " Tests for sign_getplaced() + call assert_equal([{'bufnr' : bufnr(''), 'signs' : + \ [{'id' : 10, 'group' : '', 'lnum' : 20, 'name' : 'sign1', + \ 'priority' : 10}]}], + \ sign_getplaced(bufnr('Xsign'))) + call assert_equal([{'bufnr' : bufnr(''), 'signs' : + \ [{'id' : 10, 'group' : '', 'lnum' : 20, 'name' : 'sign1', + \ 'priority' : 10}]}], + \ sign_getplaced()) + call assert_fails("call sign_getplaced('dummy.sign')", 'E158:') + call assert_fails('call sign_getplaced("")', 'E158:') + call assert_fails('call sign_getplaced(-1)', 'E158:') + call assert_fails('call sign_getplaced("Xsign", [])', 'E715:') + call assert_equal([{'bufnr' : bufnr(''), 'signs' : []}], + \ sign_getplaced('Xsign', {'lnum' : 1000000})) + call assert_fails("call sign_getplaced('Xsign', {'lnum' : []})", + \ 'E745:') + call assert_equal([{'bufnr' : bufnr(''), 'signs' : []}], + \ sign_getplaced('Xsign', {'id' : 44})) + call assert_fails("call sign_getplaced('Xsign', {'id' : []})", + \ 'E745:') + + " Tests for sign_unplace() + call sign_place(20, '', 'sign2', 'Xsign', {"lnum" : 30}) + call assert_equal(0, sign_unplace('', + \ {'id' : 20, 'buffer' : 'Xsign'})) + call assert_equal(-1, sign_unplace('', + \ {'id' : 30, 'buffer' : 'Xsign'})) + call sign_place(20, '', 'sign2', 'Xsign', {"lnum" : 30}) + call assert_fails("call sign_unplace('', + \ {'id' : 20, 'buffer' : 'buffer.c'})", 'E158:') + call assert_fails("call sign_unplace('', + \ {'id' : 20, 'buffer' : ''})", 'E158:') + call assert_fails("call sign_unplace('', + \ {'id' : 20, 'buffer' : 200})", 'E158:') + call assert_fails("call sign_unplace('', 'mySign')", 'E715:') + + " Tests for sign_undefine() + call assert_equal(0, sign_undefine("sign1")) + call assert_equal([], sign_getdefined("sign1")) + call assert_fails('call sign_undefine("none")', 'E155:') + call assert_fails('call sign_undefine([])', 'E730:') + + call delete("Xsign") + call sign_unplace('*') + call sign_undefine() + enew | only +endfunc + +" Tests for sign groups +func Test_sign_group() + enew | only + " Remove all the signs + call sign_unplace('*') + call sign_undefine() + + call writefile(repeat(["Sun is shining"], 30), "Xsign") + + let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error'} + call assert_equal(0, sign_define("sign1", attr)) + + edit Xsign + let bnum = bufnr('%') + let fname = fnamemodify('Xsign', ':p') + + " Error case + call assert_fails("call sign_place(5, [], 'sign1', 'Xsign', + \ {'lnum' : 30})", 'E730:') + + " place three signs with the same identifier. One in the global group and + " others in the named groups + call assert_equal(5, sign_place(5, '', 'sign1', 'Xsign', + \ {'lnum' : 10})) + call assert_equal(5, sign_place(5, 'g1', 'sign1', bnum, {'lnum' : 20})) + call assert_equal(5, sign_place(5, 'g2', 'sign1', bnum, {'lnum' : 30})) + + " Test for sign_getplaced() with group + let s = sign_getplaced('Xsign') + call assert_equal(1, len(s[0].signs)) + call assert_equal(s[0].signs[0].group, '') + let s = sign_getplaced(bnum, {'group' : 'g2'}) + call assert_equal('g2', s[0].signs[0].group) + let s = sign_getplaced(bnum, {'group' : 'g3'}) + call assert_equal([], s[0].signs) + let s = sign_getplaced(bnum, {'group' : '*'}) + call assert_equal([{'id' : 5, 'group' : '', 'name' : 'sign1', 'lnum' : 10, + \ 'priority' : 10}, + \ {'id' : 5, 'group' : 'g1', 'name' : 'sign1', 'lnum' : 20, + \ 'priority' : 10}, + \ {'id' : 5, 'group' : 'g2', 'name' : 'sign1', 'lnum' : 30, + \ 'priority' : 10}], + \ s[0].signs) + + " Test for sign_getplaced() with id + let s = sign_getplaced(bnum, {'id' : 5}) + call assert_equal([{'id' : 5, 'group' : '', 'name' : 'sign1', 'lnum' : 10, + \ 'priority' : 10}], + \ s[0].signs) + let s = sign_getplaced(bnum, {'id' : 5, 'group' : 'g2'}) + call assert_equal( + \ [{'id' : 5, 'name' : 'sign1', 'lnum' : 30, 'group' : 'g2', + \ 'priority' : 10}], + \ s[0].signs) + let s = sign_getplaced(bnum, {'id' : 5, 'group' : '*'}) + call assert_equal([{'id' : 5, 'group' : '', 'name' : 'sign1', 'lnum' : 10, + \ 'priority' : 10}, + \ {'id' : 5, 'group' : 'g1', 'name' : 'sign1', 'lnum' : 20, + \ 'priority' : 10}, + \ {'id' : 5, 'group' : 'g2', 'name' : 'sign1', 'lnum' : 30, + \ 'priority' : 10}], + \ s[0].signs) + let s = sign_getplaced(bnum, {'id' : 5, 'group' : 'g3'}) + call assert_equal([], s[0].signs) + + " Test for sign_getplaced() with lnum + let s = sign_getplaced(bnum, {'lnum' : 20}) + call assert_equal([], s[0].signs) + let s = sign_getplaced(bnum, {'lnum' : 20, 'group' : 'g1'}) + call assert_equal( + \ [{'id' : 5, 'name' : 'sign1', 'lnum' : 20, 'group' : 'g1', + \ 'priority' : 10}], + \ s[0].signs) + let s = sign_getplaced(bnum, {'lnum' : 30, 'group' : '*'}) + call assert_equal( + \ [{'id' : 5, 'name' : 'sign1', 'lnum' : 30, 'group' : 'g2', + \ 'priority' : 10}], + \ s[0].signs) + let s = sign_getplaced(bnum, {'lnum' : 40, 'group' : '*'}) + call assert_equal([], s[0].signs) + + " Error case + call assert_fails("call sign_getplaced(bnum, {'group' : []})", + \ 'E730:') + + " Clear the sign in global group + call sign_unplace('', {'id' : 5, 'buffer' : bnum}) + let s = sign_getplaced(bnum, {'group' : '*'}) + call assert_equal([ + \ {'id' : 5, 'name' : 'sign1', 'lnum' : 20, 'group' : 'g1', + \ 'priority' : 10}, + \ {'id' : 5, 'name' : 'sign1', 'lnum' : 30, 'group' : 'g2', + \ 'priority' : 10}], + \ s[0].signs) + + " Clear the sign in one of the groups + call sign_unplace('g1', {'buffer' : 'Xsign'}) + let s = sign_getplaced(bnum, {'group' : '*'}) + call assert_equal([ + \ {'id' : 5, 'name' : 'sign1', 'lnum' : 30, 'group' : 'g2', + \ 'priority' : 10}], + \ s[0].signs) + + " Clear all the signs from the buffer + call sign_unplace('*', {'buffer' : bnum}) + call assert_equal([], sign_getplaced(bnum, {'group' : '*'})[0].signs) + + " Clear sign across groups using an identifier + call sign_place(25, '', 'sign1', bnum, {'lnum' : 10}) + call sign_place(25, 'g1', 'sign1', bnum, {'lnum' : 11}) + call sign_place(25, 'g2', 'sign1', bnum, {'lnum' : 12}) + call assert_equal(0, sign_unplace('*', {'id' : 25})) + call assert_equal([], sign_getplaced(bnum, {'group' : '*'})[0].signs) + + " Error case + call assert_fails("call sign_unplace([])", 'E474:') + + " Place a sign in the global group and try to delete it using a group + call assert_equal(5, sign_place(5, '', 'sign1', bnum, {'lnum' : 10})) + call assert_equal(-1, sign_unplace('g1', {'id' : 5})) + + " Place signs in multiple groups and delete all the signs in one of the + " group + call assert_equal(5, sign_place(5, '', 'sign1', bnum, {'lnum' : 10})) + call assert_equal(6, sign_place(6, '', 'sign1', bnum, {'lnum' : 11})) + call assert_equal(5, sign_place(5, 'g1', 'sign1', bnum, {'lnum' : 10})) + call assert_equal(5, sign_place(5, 'g2', 'sign1', bnum, {'lnum' : 10})) + call assert_equal(6, sign_place(6, 'g1', 'sign1', bnum, {'lnum' : 11})) + call assert_equal(6, sign_place(6, 'g2', 'sign1', bnum, {'lnum' : 11})) + call assert_equal(0, sign_unplace('g1')) + let s = sign_getplaced(bnum, {'group' : 'g1'}) + call assert_equal([], s[0].signs) + let s = sign_getplaced(bnum) + call assert_equal(2, len(s[0].signs)) + let s = sign_getplaced(bnum, {'group' : 'g2'}) + call assert_equal('g2', s[0].signs[0].group) + call assert_equal(0, sign_unplace('', {'id' : 5})) + call assert_equal(0, sign_unplace('', {'id' : 6})) + let s = sign_getplaced(bnum, {'group' : 'g2'}) + call assert_equal('g2', s[0].signs[0].group) + call assert_equal(0, sign_unplace('', {'buffer' : bnum})) + + call sign_unplace('*') + + " Test for :sign command and groups + exe 'sign place 5 line=10 name=sign1 file=' . fname + exe 'sign place 5 group=g1 line=10 name=sign1 file=' . fname + exe 'sign place 5 group=g2 line=10 name=sign1 file=' . fname + + " Test for :sign place group={group} file={fname} + let a = execute('sign place file=' . fname) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=10 id=5 name=sign1 priority=10\n", a) + + let a = execute('sign place group=g2 file=' . fname) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=10 id=5 group=g2 name=sign1 priority=10\n", a) + + let a = execute('sign place group=* file=' . fname) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=10 id=5 group=g2 name=sign1 priority=10\n" . + \ " line=10 id=5 group=g1 name=sign1 priority=10\n" . + \ " line=10 id=5 name=sign1 priority=10\n", a) + + let a = execute('sign place group=xyz file=' . fname) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n", a) + + call sign_unplace('*') + + " Test for :sign place group={group} buffer={nr} + let bnum = bufnr('Xsign') + exe 'sign place 5 line=10 name=sign1 buffer=' . bnum + exe 'sign place 5 group=g1 line=11 name=sign1 buffer=' . bnum + exe 'sign place 5 group=g2 line=12 name=sign1 buffer=' . bnum + + let a = execute('sign place buffer=' . bnum) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=10 id=5 name=sign1 priority=10\n", a) + + let a = execute('sign place group=g2 buffer=' . bnum) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=12 id=5 group=g2 name=sign1 priority=10\n", a) + + let a = execute('sign place group=* buffer=' . bnum) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=10 id=5 name=sign1 priority=10\n" . + \ " line=11 id=5 group=g1 name=sign1 priority=10\n" . + \ " line=12 id=5 group=g2 name=sign1 priority=10\n", a) + + let a = execute('sign place group=xyz buffer=' . bnum) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n", a) + + let a = execute('sign place group=*') + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=10 id=5 name=sign1 priority=10\n" . + \ " line=11 id=5 group=g1 name=sign1 priority=10\n" . + \ " line=12 id=5 group=g2 name=sign1 priority=10\n", a) + + " Test for :sign unplace + exe 'sign unplace 5 group=g2 file=' . fname + call assert_equal([], sign_getplaced(bnum, {'group' : 'g2'})[0].signs) + + exe 'sign unplace 5 group=g1 buffer=' . bnum + call assert_equal([], sign_getplaced(bnum, {'group' : 'g1'})[0].signs) + + exe 'sign unplace 5 group=xy file=' . fname + call assert_equal(1, len(sign_getplaced(bnum, {'group' : '*'})[0].signs)) + + " Test for removing all the signs. Place the signs again for this test + exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname + exe 'sign place 5 group=g2 line=12 name=sign1 file=' . fname + exe 'sign place 6 line=20 name=sign1 file=' . fname + exe 'sign place 6 group=g1 line=21 name=sign1 file=' . fname + exe 'sign place 6 group=g2 line=22 name=sign1 file=' . fname + exe 'sign unplace 5 group=* file=' . fname + let a = execute('sign place group=*') + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=20 id=6 name=sign1 priority=10\n" . + \ " line=21 id=6 group=g1 name=sign1 priority=10\n" . + \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) + + " Remove all the signs from the global group + exe 'sign unplace * file=' . fname + let a = execute('sign place group=*') + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=21 id=6 group=g1 name=sign1 priority=10\n" . + \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) + + " Remove all the signs from a particular group + exe 'sign place 5 line=10 name=sign1 file=' . fname + exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname + exe 'sign place 5 group=g2 line=12 name=sign1 file=' . fname + exe 'sign unplace * group=g1 file=' . fname + let a = execute('sign place group=*') + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=10 id=5 name=sign1 priority=10\n" . + \ " line=12 id=5 group=g2 name=sign1 priority=10\n" . + \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) + + " Remove all the signs from all the groups in a file + exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname + exe 'sign place 6 line=20 name=sign1 file=' . fname + exe 'sign place 6 group=g1 line=21 name=sign1 file=' . fname + exe 'sign unplace * group=* file=' . fname + let a = execute('sign place group=*') + call assert_equal("\n--- Signs ---\n", a) + + " Remove a particular sign id in a group from all the files + exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname + sign unplace 5 group=g1 + let a = execute('sign place group=*') + call assert_equal("\n--- Signs ---\n", a) + + " Remove a particular sign id in all the groups from all the files + exe 'sign place 5 line=10 name=sign1 file=' . fname + exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname + exe 'sign place 5 group=g2 line=12 name=sign1 file=' . fname + exe 'sign place 6 line=20 name=sign1 file=' . fname + exe 'sign place 6 group=g1 line=21 name=sign1 file=' . fname + exe 'sign place 6 group=g2 line=22 name=sign1 file=' . fname + sign unplace 5 group=* + let a = execute('sign place group=*') + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=20 id=6 name=sign1 priority=10\n" . + \ " line=21 id=6 group=g1 name=sign1 priority=10\n" . + \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) + + " Remove all the signs from all the groups in all the files + exe 'sign place 5 line=10 name=sign1 file=' . fname + exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname + sign unplace * group=* + let a = execute('sign place group=*') + call assert_equal("\n--- Signs ---\n", a) + + " Error cases + call assert_fails("exe 'sign place 3 group= name=sign1 buffer=' . bnum", 'E474:') + + call delete("Xsign") + call sign_unplace('*') + call sign_undefine() + enew | only +endfunc + +" Tests for auto-generating the sign identifier +func Test_sign_id_autogen() + enew | only + call sign_unplace('*') + call sign_undefine() + + let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error'} + call assert_equal(0, sign_define("sign1", attr)) + + call writefile(repeat(["Sun is shining"], 30), "Xsign") + edit Xsign + + call assert_equal(1, sign_place(0, '', 'sign1', 'Xsign', + \ {'lnum' : 10})) + call assert_equal(2, sign_place(2, '', 'sign1', 'Xsign', + \ {'lnum' : 12})) + call assert_equal(3, sign_place(0, '', 'sign1', 'Xsign', + \ {'lnum' : 14})) + call sign_unplace('', {'buffer' : 'Xsign', 'id' : 2}) + call assert_equal(2, sign_place(0, '', 'sign1', 'Xsign', + \ {'lnum' : 12})) + + call assert_equal(1, sign_place(0, 'g1', 'sign1', 'Xsign', + \ {'lnum' : 11})) + call assert_equal(0, sign_unplace('g1', {'id' : 1})) + call assert_equal(10, + \ sign_getplaced('Xsign', {'id' : 1})[0].signs[0].lnum) + + call delete("Xsign") + call sign_unplace('*') + call sign_undefine() + enew | only +endfunc + +" Test for sign priority +func Test_sign_priority() + enew | only + call sign_unplace('*') + call sign_undefine() + + let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Search'} + call sign_define("sign1", attr) + call sign_define("sign2", attr) + call sign_define("sign3", attr) + + " Place three signs with different priority in the same line + call writefile(repeat(["Sun is shining"], 30), "Xsign") + edit Xsign + let fname = fnamemodify('Xsign', ':p') + + call sign_place(1, 'g1', 'sign1', 'Xsign', + \ {'lnum' : 11, 'priority' : 50}) + call sign_place(2, 'g2', 'sign2', 'Xsign', + \ {'lnum' : 11, 'priority' : 100}) + call sign_place(3, '', 'sign3', 'Xsign', {'lnum' : 11}) + let s = sign_getplaced('Xsign', {'group' : '*'}) + call assert_equal([ + \ {'id' : 2, 'name' : 'sign2', 'lnum' : 11, 'group' : 'g2', + \ 'priority' : 100}, + \ {'id' : 1, 'name' : 'sign1', 'lnum' : 11, 'group' : 'g1', + \ 'priority' : 50}, + \ {'id' : 3, 'name' : 'sign3', 'lnum' : 11, 'group' : '', + \ 'priority' : 10}], + \ s[0].signs) + + " Error case + call assert_fails("call sign_place(1, 'g1', 'sign1', 'Xsign', + \ [])", 'E715:') + call sign_unplace('*') + + " Tests for the :sign place command with priority + sign place 5 line=10 name=sign1 priority=30 file=Xsign + sign place 5 group=g1 line=10 name=sign1 priority=20 file=Xsign + sign place 5 group=g2 line=10 name=sign1 priority=25 file=Xsign + let a = execute('sign place group=*') + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=10 id=5 name=sign1 priority=30\n" . + \ " line=10 id=5 group=g2 name=sign1 priority=25\n" . + \ " line=10 id=5 group=g1 name=sign1 priority=20\n", a) + + " Test for :sign place group={group} + let a = execute('sign place group=g1') + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=10 id=5 group=g1 name=sign1 priority=20\n", a) + + call sign_unplace('*') + call sign_undefine() + enew | only + call delete("Xsign") +endfunc + +" Tests for memory allocation failures in sign functions +func Test_sign_memfailures() + call writefile(repeat(["Sun is shining"], 30), "Xsign") + edit Xsign + + call test_alloc_fail(GetAllocId('sign_getdefined'), 0, 0) + call assert_fails('call sign_getdefined("sign1")', 'E342:') + call test_alloc_fail(GetAllocId('sign_getplaced'), 0, 0) + call assert_fails('call sign_getplaced("Xsign")', 'E342:') + call test_alloc_fail(GetAllocId('sign_define_by_name'), 0, 0) + let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error'} + call assert_fails('call sign_define("sign1", attr)', 'E342:') + + let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error'} + call sign_define("sign1", attr) + call test_alloc_fail(GetAllocId('sign_getlist'), 0, 0) + call assert_fails('call sign_getdefined("sign1")', 'E342:') + + call sign_place(3, 'g1', 'sign1', 'Xsign', {'lnum' : 10}) + call test_alloc_fail(GetAllocId('sign_getplaced_dict'), 0, 0) + call assert_fails('call sign_getplaced("Xsign")', 'E342:') + call test_alloc_fail(GetAllocId('sign_getplaced_list'), 0, 0) + call assert_fails('call sign_getplaced("Xsign")', 'E342:') + + call test_alloc_fail(GetAllocId('insert_sign'), 0, 0) + call assert_fails('call sign_place(4, "g1", "sign1", "Xsign", {"lnum" : 11})', + \ 'E342:') + + call test_alloc_fail(GetAllocId('sign_getinfo'), 0, 0) + call assert_fails('call getbufinfo()', 'E342:') + call sign_place(4, 'g1', 'sign1', 'Xsign', {'lnum' : 11}) + call test_alloc_fail(GetAllocId('sign_getinfo'), 0, 0) + call assert_fails('let binfo=getbufinfo("Xsign")', 'E342:') + call assert_equal([{'lnum': 11, 'id': 4, 'name': 'sign1', + \ 'priority': 10, 'group': 'g1'}], binfo[0].signs) + + call sign_unplace('*') + call sign_undefine() + enew | only + call delete("Xsign") +endfunc -- cgit From 4f844c587c18b12ffb9253f461557a8a8da258af Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Thu, 9 May 2019 17:30:23 +0200 Subject: vim-patch:8.1.0632: using sign group names is inefficient Problem: Using sign group names is inefficient. Solution: Store group names in a hash table and use a reference to them. Also remove unnecessary use of ":exe" from the tests. (Yegappan Lakshmanan, closes vim/vim#3715) https://github.com/vim/vim/commit/7a2d9892b7158edf8dc48e9bcaaae70a40787b37 --- src/nvim/buffer.c | 100 ++++++++++++++++++++++++++++---- src/nvim/ex_cmds.c | 2 +- src/nvim/sign_defs.h | 13 ++++- src/nvim/testdir/test_signs.vim | 124 +++++++++++++++++++++------------------- 4 files changed, 167 insertions(+), 72 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index da65682fbe..3ddd2a7163 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -64,6 +64,7 @@ #include "nvim/quickfix.h" #include "nvim/regexp.h" #include "nvim/screen.h" +#include "nvim/sign.h" #include "nvim/spell.h" #include "nvim/strings.h" #include "nvim/syntax.h" @@ -5255,6 +5256,70 @@ bool find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp) return false; } +static hashtab_T sg_table; // sign group (signgroup_T) hashtable + +/* + * 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(char_u *groupname) +{ + static int initialized = FALSE; + hash_T hash; + hashitem_T *hi; + signgroup_T *group; + + if (!initialized) { + initialized = TRUE; + hash_init(&sg_table); + } + + hash = hash_hash(groupname); + hi = hash_lookup(&sg_table, S_LEN(groupname), hash); + if (HASHITEM_EMPTY(hi)) + { + // new group + group = (signgroup_T *)xmalloc( + (unsigned)(sizeof(signgroup_T) + STRLEN(groupname))); + if (group == NULL) + return NULL; + STRCPY(group->sg_name, groupname); + group->refcount = 1; + hash_add_item(&sg_table, hi, group->sg_name, hash); + } + else + { + // existing group + group = HI2SG(hi); + group->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_u *groupname) +{ + hashitem_T *hi; + signgroup_T *group; + + hi = hash_find(&sg_table, groupname); + if (!HASHITEM_EMPTY(hi)) + { + group = HI2SG(hi); + group->refcount--; + if (group->refcount == 0) + { + // All the signs in this group are removed + hash_remove(&sg_table, hi); + xfree(group); + } + } +} + /* * Insert a new sign into the signlist for buffer 'buf' between the 'prev' and * 'next' signs. @@ -5275,7 +5340,14 @@ static void insert_sign( newsign->lnum = lnum; newsign->typenr = typenr; if (group != NULL) - newsign->group = vim_strsave(group); + { + newsign->group = sign_group_ref(group); + if (newsign->group == NULL) + { + xfree(newsign); + return; + } + } else newsign->group = NULL; newsign->priority = prio; @@ -5431,7 +5503,7 @@ int sign_in_group(signlist_T *sign, char_u *group) return ((group != NULL && STRCMP(group, "*") == 0) || (group == NULL && sign->group == NULL) || (group != NULL && sign->group != NULL && - STRCMP(group, sign->group) == 0)); + STRCMP(group, sign->group->sg_name) == 0)); } /* @@ -5445,7 +5517,7 @@ dict_T * sign_get_info(signlist_T *sign) return NULL; } tv_dict_add_nr(d, S_LEN("id"), sign->id); - tv_dict_add_str(d, S_LEN("group"), (sign->group == NULL) ? (char_u *)"" : sign->group); + tv_dict_add_str(d, S_LEN("group"), (sign->group == NULL) ? (char_u *)"" : sign->group->sg_name); tv_dict_add_nr(d, S_LEN("lnum"), sign->lnum); tv_dict_add_str(d, S_LEN("name"), sign_typenr2name(sign->typenr)); tv_dict_add_nr(d, S_LEN("priority"), sign->priority); @@ -5459,7 +5531,7 @@ dict_T * sign_get_info(signlist_T *sign) void buf_addsign( buf_T *buf, // buffer to store sign in int id, // sign ID - char_u *group, // sign group + char_u *groupname, // sign group int prio, // sign priority linenr_T lnum, // line number which gets the mark int typenr // typenr of sign we are adding @@ -5472,19 +5544,19 @@ void buf_addsign( prev = NULL; FOR_ALL_SIGNS_IN_BUF(buf) { if (lnum == sign->lnum && id == sign->id && - sign_in_group(sign, group)) { + sign_in_group(sign, groupname)) { // Update an existing sign sign->typenr = typenr; return; } else if ((lnum == sign->lnum && id != sign->id) || (id < 0 && lnum < sign->lnum)) { - insert_sign_by_lnum_prio(buf, prev, id, group, prio, lnum, typenr); + insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr); return; } prev = sign; } - insert_sign_by_lnum_prio(buf, prev, id, group, prio, lnum, typenr); + insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr); // Having more than one sign with _the same type_ and on the _same line_ is // unwanted, let's prevent it. @@ -5604,7 +5676,8 @@ linenr_T buf_delsign( next->prev = sign->prev; } lnum = sign->lnum; - xfree(sign->group); + if (sign->group != NULL) + sign_group_unref(sign->group->sg_name); xfree(sign); // Check whether only one sign needs to be deleted if (group == NULL || (*group != '*' && id != 0)) @@ -5709,7 +5782,7 @@ int buf_findsign_id( /* * Delete signs in buffer "buf". */ -buf_delete_signs(buf_T *buf, char_u *group) +void buf_delete_signs(buf_T *buf, char_u *group) { signlist_T *sign; signlist_T **lastp; // pointer to pointer to current sign @@ -5730,7 +5803,9 @@ buf_delete_signs(buf_T *buf, char_u *group) if (next != NULL) { next->prev = sign->prev; } - xfree(sign->group); + if (sign->group != NULL) { + sign_group_unref(sign->group->sg_name); + } xfree(sign); } else { lastp = &sign->next; @@ -5775,11 +5850,14 @@ void sign_list_placed(buf_T *rbuf, char_u *sign_group) msg_putchar('\n'); } FOR_ALL_SIGNS_IN_BUF(buf) { + if (got_int) { + break; + } if (!sign_in_group(sign, sign_group)) { continue; } if (sign->group != NULL) { - vim_snprintf(group, BUFSIZ, " group=%s", sign->group); + vim_snprintf(group, BUFSIZ, " group=%s", sign->group->sg_name); } else { group[0] = '\0'; } diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 4a589ec6e5..82b16a721e 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -6072,7 +6072,7 @@ void ex_sign(exarg_T *eap) if (bufarg && buf == NULL) { EMSG2(_("E158: Invalid buffer name: %s"), arg); - } else if (id <= 0 && !(idx == SIGNCMD_UNPLACE && id == -2)) { + } else if (id <= 0 && idx == SIGNCMD_PLACE) { if ((group == NULL) && (lnum >= 0 || sign_name != NULL)) { EMSG(_(e_invarg)); } else { diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h index 313fb330ed..e3028a80ba 100644 --- a/src/nvim/sign_defs.h +++ b/src/nvim/sign_defs.h @@ -5,6 +5,17 @@ // signs: line annotations +// Sign group +typedef struct signgroup_S +{ + uint16_t refcount; // number of signs in this group + char_u sg_name[1]; // 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 signlist signlist_T; struct signlist @@ -12,7 +23,7 @@ struct signlist int id; // unique identifier for each placed sign linenr_T lnum; // line number which has this sign int typenr; // typenr of sign - char_u *group; // sign group + signgroup_T *group; // sign group int priority; // priority for highlighting signlist_T *next; // next signlist entry signlist_T *prev; // previous entry -- for easy reordering diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index 5e2ac3fb94..a0acc54b5b 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -59,7 +59,7 @@ func Test_sign() redraw " Check that we can't change sign. - call assert_fails("exe 'sign place 40 name=Sign1 buffer=' . bufnr('%')", 'E885:') + call assert_fails("sign place 40 name=Sign1 buffer=" . bufnr('%'), 'E885:') " Check placed signs let a=execute('sign place') @@ -68,7 +68,7 @@ func Test_sign() " Unplace the sign and try jumping to it again should fail. sign unplace 41 1 - call assert_fails("exe 'sign jump 41 buffer=' . bufnr('%')", 'E157:') + call assert_fails("sign jump 41 buffer=" . bufnr('%'), 'E157:') call assert_equal('a', getline('.')) " Unplace sign on current line. @@ -132,17 +132,22 @@ func Test_sign() sign undefine Sign4 " Error cases - call assert_fails("exe 'sign place abc line=3 name=Sign1 buffer=' . bufnr('%')", 'E474:') - call assert_fails("exe 'sign unplace abc name=Sign1 buffer=' . bufnr('%')", 'E474:') - call assert_fails("exe 'sign place 1abc line=3 name=Sign1 buffer=' . bufnr('%')", 'E474:') - call assert_fails("exe 'sign unplace 2abc name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign place abc line=3 name=Sign1 buffer=" . + \ bufnr('%'), 'E474:') + call assert_fails("sign unplace abc name=Sign1 buffer=" . + \ bufnr('%'), 'E474:') + call assert_fails("sign place 1abc line=3 name=Sign1 buffer=" . + \ bufnr('%'), 'E474:') + call assert_fails("sign unplace 2abc name=Sign1 buffer=" . + \ bufnr('%'), 'E474:') call assert_fails("sign unplace 2 *", 'E474:') - call assert_fails("exe 'sign place 1 line=3 name=Sign1 buffer=' . bufnr('%') a", 'E488:') - call assert_fails("exe 'sign place name=Sign1 buffer=' . bufnr('%')", 'E474:') - call assert_fails("exe 'sign place line=10 buffer=' . bufnr('%')", 'E474:') - call assert_fails("exe 'sign unplace 2 line=10 buffer=' . bufnr('%')", 'E474:') - call assert_fails("exe 'sign unplace 2 name=Sign1 buffer=' . bufnr('%')", 'E474:') - call assert_fails("exe 'sign place 2 line=3 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign place 1 line=3 name=Sign1 buffer=" . + \ bufnr('%') . " a", 'E488:') + call assert_fails("sign place name=Sign1 buffer=" . bufnr('%'), 'E474:') + call assert_fails("sign place line=10 buffer=" . bufnr('%'), 'E474:') + call assert_fails("sign unplace 2 line=10 buffer=" . bufnr('%'), 'E474:') + call assert_fails("sign unplace 2 name=Sign1 buffer=" . bufnr('%'), 'E474:') + call assert_fails("sign place 2 line=3 buffer=" . bufnr('%'), 'E474:') call assert_fails("sign place 2", 'E474:') call assert_fails("sign place abc", 'E474:') call assert_fails("sign place 5 line=3", 'E474:') @@ -157,7 +162,8 @@ func Test_sign() sign undefine Sign1 sign undefine Sign2 sign undefine Sign3 - call assert_fails("exe 'sign place 41 line=3 name=Sign1 buffer=' . bufnr('%')", 'E155:') + call assert_fails("sign place 41 line=3 name=Sign1 buffer=" . + \ bufnr('%'), 'E155:') endfunc " Undefining placed sign is not recommended. @@ -236,33 +242,33 @@ func Test_sign_invalid_commands() call assert_fails('sign place 1 buffer=999', 'E158:') call assert_fails('sign define Sign2 text=', 'E239:') " Non-numeric identifier for :sign place - call assert_fails("exe 'sign place abc line=3 name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign place abc line=3 name=Sign1 buffer=" . bufnr('%'), 'E474:') " Non-numeric identifier for :sign unplace - call assert_fails("exe 'sign unplace abc name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign unplace abc name=Sign1 buffer=" . bufnr('%'), 'E474:') " Number followed by an alphabet as sign identifier for :sign place - call assert_fails("exe 'sign place 1abc line=3 name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign place 1abc line=3 name=Sign1 buffer=" . bufnr('%'), 'E474:') " Number followed by an alphabet as sign identifier for :sign unplace - call assert_fails("exe 'sign unplace 2abc name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign unplace 2abc name=Sign1 buffer=" . bufnr('%'), 'E474:') " Sign identifier and '*' for :sign unplace call assert_fails("sign unplace 2 *", 'E474:') " Trailing characters after buffer number for :sign place - call assert_fails("exe 'sign place 1 line=3 name=Sign1 buffer=' . bufnr('%') . 'xxx'", 'E488:') + call assert_fails("sign place 1 line=3 name=Sign1 buffer=" . bufnr('%') . 'xxx', 'E488:') " Trailing characters after buffer number for :sign unplace - call assert_fails("exe 'sign unplace 1 buffer=' . bufnr('%') . 'xxx'", 'E488:') - call assert_fails("exe 'sign unplace * buffer=' . bufnr('%') . 'xxx'", 'E488:') + call assert_fails("sign unplace 1 buffer=" . bufnr('%') . 'xxx', 'E488:') + call assert_fails("sign unplace * buffer=" . bufnr('%') . 'xxx', 'E488:') call assert_fails("sign unplace 1 xxx", 'E474:') call assert_fails("sign unplace * xxx", 'E474:') call assert_fails("sign unplace xxx", 'E474:') " Placing a sign without line number - call assert_fails("exe 'sign place name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign place name=Sign1 buffer=" . bufnr('%'), 'E474:') " Placing a sign without sign name - call assert_fails("exe 'sign place line=10 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign place line=10 buffer=" . bufnr('%'), 'E474:') " Unplacing a sign with line number - call assert_fails("exe 'sign unplace 2 line=10 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign unplace 2 line=10 buffer=" . bufnr('%'), 'E474:') " Unplacing a sign with sign name - call assert_fails("exe 'sign unplace 2 name=Sign1 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign unplace 2 name=Sign1 buffer=" . bufnr('%'), 'E474:') " Placing a sign without sign name - call assert_fails("exe 'sign place 2 line=3 buffer=' . bufnr('%')", 'E474:') + call assert_fails("sign place 2 line=3 buffer=" . bufnr('%'), 'E474:') " Placing a sign with only sign identifier call assert_fails("sign place 2", 'E474:') " Placing a sign with only a name @@ -574,24 +580,24 @@ func Test_sign_group() call sign_unplace('*') " Test for :sign command and groups - exe 'sign place 5 line=10 name=sign1 file=' . fname - exe 'sign place 5 group=g1 line=10 name=sign1 file=' . fname - exe 'sign place 5 group=g2 line=10 name=sign1 file=' . fname + sign place 5 line=10 name=sign1 file=Xsign + sign place 5 group=g1 line=10 name=sign1 file=Xsign + sign place 5 group=g2 line=10 name=sign1 file=Xsign " Test for :sign place group={group} file={fname} - let a = execute('sign place file=' . fname) + let a = execute('sign place file=Xsign') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=10 id=5 name=sign1 priority=10\n", a) - let a = execute('sign place group=g2 file=' . fname) + let a = execute('sign place group=g2 file=Xsign') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=10 id=5 group=g2 name=sign1 priority=10\n", a) - let a = execute('sign place group=* file=' . fname) + let a = execute('sign place group=* file=Xsign') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . \ " line=10 id=5 group=g2 name=sign1 priority=10\n" . \ " line=10 id=5 group=g1 name=sign1 priority=10\n" . \ " line=10 id=5 name=sign1 priority=10\n", a) - let a = execute('sign place group=xyz file=' . fname) + let a = execute('sign place group=xyz file=Xsign') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n", a) call sign_unplace('*') @@ -624,22 +630,22 @@ func Test_sign_group() \ " line=12 id=5 group=g2 name=sign1 priority=10\n", a) " Test for :sign unplace - exe 'sign unplace 5 group=g2 file=' . fname + sign unplace 5 group=g2 file=Xsign call assert_equal([], sign_getplaced(bnum, {'group' : 'g2'})[0].signs) exe 'sign unplace 5 group=g1 buffer=' . bnum call assert_equal([], sign_getplaced(bnum, {'group' : 'g1'})[0].signs) - exe 'sign unplace 5 group=xy file=' . fname + sign unplace 5 group=xy file=Xsign call assert_equal(1, len(sign_getplaced(bnum, {'group' : '*'})[0].signs)) " Test for removing all the signs. Place the signs again for this test - exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname - exe 'sign place 5 group=g2 line=12 name=sign1 file=' . fname - exe 'sign place 6 line=20 name=sign1 file=' . fname - exe 'sign place 6 group=g1 line=21 name=sign1 file=' . fname - exe 'sign place 6 group=g2 line=22 name=sign1 file=' . fname - exe 'sign unplace 5 group=* file=' . fname + sign place 5 group=g1 line=11 name=sign1 file=Xsign + sign place 5 group=g2 line=12 name=sign1 file=Xsign + sign place 6 line=20 name=sign1 file=Xsign + sign place 6 group=g1 line=21 name=sign1 file=Xsign + sign place 6 group=g2 line=22 name=sign1 file=Xsign + sign unplace 5 group=* file=Xsign let a = execute('sign place group=*') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . \ " line=20 id=6 name=sign1 priority=10\n" . @@ -647,17 +653,17 @@ func Test_sign_group() \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) " Remove all the signs from the global group - exe 'sign unplace * file=' . fname + sign unplace * file=Xsign let a = execute('sign place group=*') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . \ " line=21 id=6 group=g1 name=sign1 priority=10\n" . \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) " Remove all the signs from a particular group - exe 'sign place 5 line=10 name=sign1 file=' . fname - exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname - exe 'sign place 5 group=g2 line=12 name=sign1 file=' . fname - exe 'sign unplace * group=g1 file=' . fname + sign place 5 line=10 name=sign1 file=Xsign + sign place 5 group=g1 line=11 name=sign1 file=Xsign + sign place 5 group=g2 line=12 name=sign1 file=Xsign + sign unplace * group=g1 file=Xsign let a = execute('sign place group=*') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . \ " line=10 id=5 name=sign1 priority=10\n" . @@ -665,26 +671,26 @@ func Test_sign_group() \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) " Remove all the signs from all the groups in a file - exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname - exe 'sign place 6 line=20 name=sign1 file=' . fname - exe 'sign place 6 group=g1 line=21 name=sign1 file=' . fname - exe 'sign unplace * group=* file=' . fname + sign place 5 group=g1 line=11 name=sign1 file=Xsign + sign place 6 line=20 name=sign1 file=Xsign + sign place 6 group=g1 line=21 name=sign1 file=Xsign + sign unplace * group=* file=Xsign let a = execute('sign place group=*') call assert_equal("\n--- Signs ---\n", a) " Remove a particular sign id in a group from all the files - exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname + sign place 5 group=g1 line=11 name=sign1 file=Xsign sign unplace 5 group=g1 let a = execute('sign place group=*') call assert_equal("\n--- Signs ---\n", a) " Remove a particular sign id in all the groups from all the files - exe 'sign place 5 line=10 name=sign1 file=' . fname - exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname - exe 'sign place 5 group=g2 line=12 name=sign1 file=' . fname - exe 'sign place 6 line=20 name=sign1 file=' . fname - exe 'sign place 6 group=g1 line=21 name=sign1 file=' . fname - exe 'sign place 6 group=g2 line=22 name=sign1 file=' . fname + sign place 5 line=10 name=sign1 file=Xsign + sign place 5 group=g1 line=11 name=sign1 file=Xsign + sign place 5 group=g2 line=12 name=sign1 file=Xsign + sign place 6 line=20 name=sign1 file=Xsign + sign place 6 group=g1 line=21 name=sign1 file=Xsign + sign place 6 group=g2 line=22 name=sign1 file=Xsign sign unplace 5 group=* let a = execute('sign place group=*') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . @@ -693,14 +699,14 @@ func Test_sign_group() \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) " Remove all the signs from all the groups in all the files - exe 'sign place 5 line=10 name=sign1 file=' . fname - exe 'sign place 5 group=g1 line=11 name=sign1 file=' . fname + sign place 5 line=10 name=sign1 file=Xsign + sign place 5 group=g1 line=11 name=sign1 file=Xsign sign unplace * group=* let a = execute('sign place group=*') call assert_equal("\n--- Signs ---\n", a) " Error cases - call assert_fails("exe 'sign place 3 group= name=sign1 buffer=' . bnum", 'E474:') + call assert_fails("sign place 3 group= name=sign1 buffer=" . bnum, 'E474:') call delete("Xsign") call sign_unplace('*') -- cgit From 3ee55edd2e27dd66c3bf8c319929beb0a6426bb3 Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Wed, 15 May 2019 22:02:10 +0200 Subject: vim-patch:8.1.0644: finding next sign ID is inefficient Problem: Finding next sign ID is inefficient. Solution: Add next_sign_id. (Yegappan Lakshmanan, closes vim/vim#3717) https://github.com/vim/vim/commit/6436cd83f90a0efc326798792e49e8ff96a43dce --- src/nvim/buffer.c | 76 +++++++++++++++++++++++++++++++++-------- src/nvim/eval.c | 4 ++- src/nvim/ex_cmds.c | 14 ++------ src/nvim/globals.h | 2 +- src/nvim/main.c | 2 ++ src/nvim/sign_defs.h | 1 + src/nvim/testdir/test_signs.vim | 4 +-- 7 files changed, 74 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 3ddd2a7163..06207c93f2 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -5257,6 +5257,16 @@ bool find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp) } 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, @@ -5264,16 +5274,10 @@ static hashtab_T sg_table; // sign group (signgroup_T) hashtable */ static signgroup_T * sign_group_ref(char_u *groupname) { - static int initialized = FALSE; hash_T hash; hashitem_T *hi; signgroup_T *group; - if (!initialized) { - initialized = TRUE; - hash_init(&sg_table); - } - hash = hash_hash(groupname); hi = hash_lookup(&sg_table, S_LEN(groupname), hash); if (HASHITEM_EMPTY(hi)) @@ -5285,6 +5289,7 @@ static signgroup_T * sign_group_ref(char_u *groupname) return NULL; STRCPY(group->sg_name, groupname); group->refcount = 1; + group->next_sign_id = 1; hash_add_item(&sg_table, hi, group->sg_name, hash); } else @@ -5320,6 +5325,49 @@ static void sign_group_unref(char_u *groupname) } } +/* + * Get the next free sign identifier in the specified group + */ + int +sign_group_get_next_signid(buf_T *buf, char_u *groupname) +{ + int id = 1; + signgroup_T *group = NULL; + signlist_T *sign; + hashitem_T *hi; + int found = FALSE; + + if (groupname != NULL) + { + hi = hash_find(&sg_table, groupname); + if (HASHITEM_EMPTY(hi)) + return id; + group = HI2SG(hi); + } + + // Search for the next usuable sign identifier + while (!found) + { + if (group == NULL) + id = next_sign_id++; // global group + else + id = group->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->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. @@ -5542,7 +5590,7 @@ void buf_addsign( signlist_T *prev; // the previous sign prev = NULL; - FOR_ALL_SIGNS_IN_BUF(buf) { + FOR_ALL_SIGNS_IN_BUF(buf, sign) { if (lnum == sign->lnum && id == sign->id && sign_in_group(sign, groupname)) { // Update an existing sign @@ -5583,7 +5631,7 @@ linenr_T buf_change_sign_type( { signlist_T *sign; // a sign in the signlist - FOR_ALL_SIGNS_IN_BUF(buf) { + FOR_ALL_SIGNS_IN_BUF(buf, sign) { if (sign->id == markId && sign_in_group(sign, group)) { sign->typenr = typenr; return sign->lnum; @@ -5611,7 +5659,7 @@ int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type, signlist_T *matches[9]; int nr_matches = 0; - FOR_ALL_SIGNS_IN_BUF(buf) { + FOR_ALL_SIGNS_IN_BUF(buf, sign) { if (sign->lnum == lnum && (type == SIGN_ANY || (type == SIGN_TEXT @@ -5711,7 +5759,7 @@ int buf_findsign( { signlist_T *sign; // a sign in the signlist - FOR_ALL_SIGNS_IN_BUF(buf) { + FOR_ALL_SIGNS_IN_BUF(buf, sign) { if (sign->id == id && sign_in_group(sign, group)){ return (int)sign->lnum; } @@ -5731,7 +5779,7 @@ static signlist_T * buf_getsign_at_line( { signlist_T *sign; // a sign in the signlist - FOR_ALL_SIGNS_IN_BUF(buf) { + FOR_ALL_SIGNS_IN_BUF(buf, sign) { if (sign->lnum == lnum) { return sign; } @@ -5751,7 +5799,7 @@ signlist_T *buf_getsign_with_id( { signlist_T *sign; // a sign in the signlist - FOR_ALL_SIGNS_IN_BUF(buf) { + FOR_ALL_SIGNS_IN_BUF(buf, sign) { if (sign->id == id && sign_in_group(sign, group)) { return sign; } @@ -5849,7 +5897,7 @@ void sign_list_placed(buf_T *rbuf, char_u *sign_group) MSG_PUTS_ATTR(lbuf, HL_ATTR(HLF_D)); msg_putchar('\n'); } - FOR_ALL_SIGNS_IN_BUF(buf) { + FOR_ALL_SIGNS_IN_BUF(buf, sign) { if (got_int) { break; } @@ -5887,7 +5935,7 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a curbuf->b_signcols_max = -1; lastp = &curbuf->b_signlist; - FOR_ALL_SIGNS_IN_BUF(curbuf) { + FOR_ALL_SIGNS_IN_BUF(curbuf, sign) { next = sign->next; if (sign->lnum >= line1 && sign->lnum <= line2) { if (amount == MAXLNUM) { diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b276b570b2..de2c9168c2 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -9294,7 +9294,7 @@ static list_T *get_buffer_signs(buf_T *buf) dict_T *d; list_T *const l = tv_list_alloc(kListLenMayKnow); - FOR_ALL_SIGNS_IN_BUF(buf) { + FOR_ALL_SIGNS_IN_BUF(buf, sign) { if ((d = sign_get_info(sign)) != NULL) { tv_list_append_dict(l, d); } @@ -15558,6 +15558,8 @@ f_sign_getplaced(typval_T *argvars, typval_T *rettv) group = tv_get_string_chk(&di->di_tv); if (group == NULL) return; + if (*group == '\0') // empty string means global group + group = NULL; } } } diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 82b16a721e..abfa9ad2e5 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -5803,15 +5803,7 @@ int sign_place( } if (*sign_id == 0) { - // Allocate a new sign id - int id = 1; - signlist_T *sign; - - while ((sign = buf_getsign_with_id(buf, id, sign_group)) != NULL) { - id++; - } - - *sign_id = id; + *sign_id = sign_group_get_next_signid(buf, sign_group); } if (lnum > 0) { @@ -6083,7 +6075,7 @@ void ex_sign(exarg_T *eap) } } else if (idx == SIGNCMD_JUMP) { // ":sign jump {id} file={fname}" - if (lnum >= 0 || sign_name != NULL) { + if (lnum >= 0 || sign_name != NULL || buf == NULL){ EMSG(_(e_invarg)); } else if ((lnum = buf_findsign(buf, id, group)) > 0) { // goto a sign ... @@ -6244,7 +6236,7 @@ static void sign_get_placed_in_buf( } tv_dict_add_list(d, S_LEN("signs"), l); - FOR_ALL_SIGNS_IN_BUF(buf) { + FOR_ALL_SIGNS_IN_BUF(buf, sign) { if (!sign_in_group(sign, sign_group)) { continue; } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 9890cba637..8a6f0accda 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -483,7 +483,7 @@ EXTERN buf_T *curbuf INIT(= NULL); // currently active buffer for (buf_T *buf = lastbuf; buf != NULL; buf = buf->b_prev) // Iterate through all the signs placed in a buffer -#define FOR_ALL_SIGNS_IN_BUF(buf) \ +#define FOR_ALL_SIGNS_IN_BUF(buf, sign) \ for (sign = buf->b_signlist; sign != NULL; sign = sign->next) diff --git a/src/nvim/main.c b/src/nvim/main.c index 4e1c7dff57..93b31d684e 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1337,6 +1337,8 @@ static void init_path(const char *exename) // shipped with Windows package. This also mimics SearchPath(). os_setenv_append_path(exepath); #endif + + init_signs(); } /// Get filename from command line, if any. diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h index e3028a80ba..e9c2312ae9 100644 --- a/src/nvim/sign_defs.h +++ b/src/nvim/sign_defs.h @@ -9,6 +9,7 @@ typedef struct signgroup_S { uint16_t refcount; // number of signs in this group + int next_sign_id; // next sign id for this group char_u sg_name[1]; // sign group name } signgroup_T; diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index a0acc54b5b..b745ee04bd 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -301,7 +301,7 @@ func Test_sign_delete_buffer() sign undefine Sign endfunc -" Test for VimL functions for managing signs +" Test for Vim script functions for managing signs func Test_sign_funcs() " Remove all the signs call sign_unplace('*') @@ -733,7 +733,7 @@ func Test_sign_id_autogen() call assert_equal(3, sign_place(0, '', 'sign1', 'Xsign', \ {'lnum' : 14})) call sign_unplace('', {'buffer' : 'Xsign', 'id' : 2}) - call assert_equal(2, sign_place(0, '', 'sign1', 'Xsign', + call assert_equal(4, sign_place(0, '', 'sign1', 'Xsign', \ {'lnum' : 12})) call assert_equal(1, sign_place(0, 'g1', 'sign1', 'Xsign', -- cgit From 09c236ba5c03732a7d7aa5f14f602d6f130f0057 Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Thu, 16 May 2019 21:27:41 +0200 Subject: vim-patch:8.1.0658: deleting signs and completion for :sign is insufficient Problem: Deleting signs and completion for :sign is insufficient. Solution: Add deleting signs in a specified or any group from the current cursor location. Add group and priority to sign command completion. Add tests for different sign unplace commands. Update help text. Add tests for sign jump with group. Update help for sign jump. (Yegappan Lakshmanan, closes vim/vim#3731) https://github.com/vim/vim/commit/7d83bf4f2b785b46d87c7bc376fc9d0a862af782 --- src/nvim/buffer.c | 100 +++++---- src/nvim/eval.c | 4 +- src/nvim/ex_cmds.c | 60 ++++-- src/nvim/testdir/test_signs.vim | 459 +++++++++++++++++++++++++++++++++------- 4 files changed, 479 insertions(+), 144 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 06207c93f2..9ccbfa4dd1 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -5548,10 +5548,10 @@ static void insert_sign_by_lnum_prio( */ int sign_in_group(signlist_T *sign, char_u *group) { - return ((group != NULL && STRCMP(group, "*") == 0) || - (group == NULL && sign->group == NULL) || - (group != NULL && sign->group != NULL && - STRCMP(group, sign->group->sg_name) == 0)); + return ((group != NULL && STRCMP(group, "*") == 0) + || (group == NULL && sign->group == NULL) + || (group != NULL && sign->group != NULL + && STRCMP(group, sign->group->sg_name) == 0)); } /* @@ -5704,45 +5704,55 @@ int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type, */ linenr_T buf_delsign( buf_T *buf, // buffer sign is stored in + linenr_T atlnum, // sign at this line, 0 - at any line int id, // sign id char_u *group// sign group -) + ) { - signlist_T **lastp; // pointer to pointer to current sign - signlist_T *sign; // a sign in a b_signlist - signlist_T *next; // the next sign in a b_signlist - linenr_T lnum; // line number whose sign was deleted - - buf->b_signcols_max = -1; - lastp = &buf->b_signlist; - lnum = 0; - for (sign = buf->b_signlist; sign != NULL; sign = next) { - next = sign->next; - if ((id == 0 || sign->id == id) && sign_in_group(sign, group)) { - *lastp = next; - if (next != NULL) { - next->prev = sign->prev; - } - lnum = sign->lnum; - if (sign->group != NULL) - sign_group_unref(sign->group->sg_name); - xfree(sign); - // Check whether only one sign needs to be deleted - if (group == NULL || (*group != '*' && id != 0)) - break; - } else { - lastp = &sign->next; - } + signlist_T **lastp; // pointer to pointer to current sign + signlist_T *sign; // a sign in a b_signlist + signlist_T *next; // the next sign in a b_signlist + linenr_T lnum; // line number whose sign was deleted + + buf->b_signcols_max = -1; + lastp = &buf->b_signlist; + lnum = 0; + for (sign = buf->b_signlist; sign != NULL; sign = next) { + next = sign->next; + if ((id == 0 || sign->id == id) && + (atlnum == 0 || sign->lnum == atlnum) && + sign_in_group(sign, group)) { + *lastp = next; + if (next != NULL) { + next->prev = sign->prev; + } + lnum = sign->lnum; + if (sign->group != NULL) + sign_group_unref(sign->group->sg_name); + xfree(sign); + redraw_buf_line_later(buf, lnum); + // Check whether only one sign needs to be deleted + // If deleting a sign with a specific identifer 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->next; } + } - /* When deleted the last sign needs to redraw the windows to remove the - * sign column. */ - if (buf->b_signlist == NULL) { - redraw_buf_later(buf, NOT_VALID); - changed_cline_bef_curs(); - } + /* When deleted the last sign needs to redraw the windows to remove the + * sign column. */ + if (buf->b_signlist == NULL) { + redraw_buf_later(buf, NOT_VALID); + changed_cline_bef_curs(); + } - return lnum; + return lnum; } @@ -5770,17 +5780,18 @@ int buf_findsign( /* * Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is - * not found at the line. + * not found at the line. If 'groupname' is NULL, searches in the global group. */ static signlist_T * buf_getsign_at_line( buf_T *buf, // buffer whose sign we are searching for - linenr_T lnum // line number of sign + linenr_T lnum, // line number of sign + char_u *groupname // sign group name ) { signlist_T *sign; // a sign in the signlist FOR_ALL_SIGNS_IN_BUF(buf, sign) { - if (sign->lnum == lnum) { + if (sign->lnum == lnum && sign_in_group(sign, groupname)) { return sign; } } @@ -5813,12 +5824,13 @@ signlist_T *buf_getsign_with_id( */ int buf_findsign_id( buf_T *buf, // buffer whose sign we are searching for - linenr_T lnum // line number of sign + linenr_T lnum, // line number of sign + char_u *groupname // sign group name ) { signlist_T *sign; // a sign in the signlist - sign = buf_getsign_at_line(buf, lnum); + sign = buf_getsign_at_line(buf, lnum, groupname); if (sign != NULL) { return sign->id; } @@ -5865,11 +5877,11 @@ void buf_delete_signs(buf_T *buf, char_u *group) /* * Delete all signs in all buffers. */ -void buf_delete_all_signs(void) +void buf_delete_all_signs(char_u *groupname) { FOR_ALL_BUFFERS(buf) { if (buf->b_signlist != NULL) { - buf_delete_signs(buf, (char_u *)"*"); + buf_delete_signs(buf, groupname); } } } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index de2c9168c2..cbe2a89b5b 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -15739,12 +15739,12 @@ f_sign_unplace(typval_T *argvars, typval_T *rettv) { // Delete the sign in all the buffers FOR_ALL_BUFFERS(buf) - if (sign_unplace(sign_id, group, buf) == OK) + if (sign_unplace(sign_id, group, buf, 0) == OK) rettv->vval.v_number = 0; } else { - if (sign_unplace(sign_id, group, buf) == OK) + if (sign_unplace(sign_id, group, buf, 0) == OK) rettv->vval.v_number = 0; } xfree(group); diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index abfa9ad2e5..5d9dc7508f 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -5828,7 +5828,7 @@ int sign_place( * Unplace the specified sign */ int -sign_unplace(int sign_id, char_u *sign_group, buf_T *buf) +sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum) { if (sign_id == 0) { @@ -5839,15 +5839,31 @@ sign_unplace(int sign_id, char_u *sign_group, buf_T *buf) linenr_T lnum; // Delete only the specified signs - lnum = buf_delsign(buf, sign_id, sign_group); + lnum = buf_delsign(buf, atlnum, sign_id, sign_group); if (lnum == 0) { return FAIL; + update_debug_sign(buf, lnum); } redraw_buf_line_later(buf, lnum); } return OK; } + +/* + * Unplace the sign at the current cursor line. + */ +static void sign_unplace_at_cursor(char_u *groupname) +{ + 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); + } else { + EMSG(_("E159: Missing sign number")); + } +} /* * ":sign" command @@ -5962,12 +5978,7 @@ void ex_sign(exarg_T *eap) sign_list_placed(NULL, NULL); } else if (idx == SIGNCMD_UNPLACE) { // ":sign unplace": remove placed sign at cursor - id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum); - if (id > 0) { - sign_unplace(id, NULL, curwin->w_buffer); - } else { - EMSG(_("E159: Missing sign number")); - } + sign_unplace_at_cursor(NULL); } else { EMSG(_(e_argreq)); } @@ -5976,7 +5987,7 @@ void ex_sign(exarg_T *eap) if (idx == SIGNCMD_UNPLACE && arg[0] == '*' && arg[1] == NUL) { // ":sign unplace *": remove all placed signs - buf_delete_all_signs(); + buf_delete_all_signs(NULL); return; } @@ -5992,7 +6003,7 @@ void ex_sign(exarg_T *eap) if (idx == SIGNCMD_UNPLACE && *arg == NUL) { // ":sign unplace {id}": remove placed sign by number FOR_ALL_BUFFERS(buf) { - sign_unplace(id, NULL, buf); + sign_unplace(id, NULL, buf, 0); } return; } @@ -6075,6 +6086,7 @@ void ex_sign(exarg_T *eap) } } else if (idx == SIGNCMD_JUMP) { // ":sign jump {id} file={fname}" + // ":sign jump {id} group={group} file={fname}" if (lnum >= 0 || sign_name != NULL || buf == NULL){ EMSG(_(e_invarg)); } else if ((lnum = buf_findsign(buf, id, group)) > 0) { @@ -6108,7 +6120,7 @@ void ex_sign(exarg_T *eap) } else if (id == -2) { if (buf != NULL) { // ":sign unplace * file={fname}" - sign_unplace(0, group, buf); + sign_unplace(0, group, buf, 0); } else { // ":sign unplace * group=*": remove all placed signs FOR_ALL_BUFFERS(buf) { @@ -6122,14 +6134,21 @@ void ex_sign(exarg_T *eap) // ":sign unplace {id} file={fname}" // ":sign unplace {id} group={group} file={fname}" // ":sign unplace {id} group=* file={fname}" - sign_unplace(id, group, buf); + sign_unplace(id, group, buf, 0); } else { - // ":sign unplace {id} group={group}": - // ":sign unplace {id} group=*": - // remove all placed signs in this group. - FOR_ALL_BUFFERS(buf) { - if (buf->b_signlist != NULL) { - sign_unplace(id, group, buf); + if (id == -1) { + // ":sign unplace group={group}": + // ":sign unplace group=*": + // remove sign in the current line in specified group + sign_unplace_at_cursor(group); + } else { + // ":sign unplace {id} group={group}": + // ":sign unplace {id} group=*": + // remove all placed signs in this group. + FOR_ALL_BUFFERS(buf) { + if (buf->b_signlist != NULL) { + sign_unplace(id, group, buf, 0); + } } } } @@ -6426,11 +6445,12 @@ char_u * get_sign_name(expand_T *xp, int idx) return (char_u *)define_arg[idx]; } case EXP_PLACE: { - char *place_arg[] = { "line=", "name=", "file=", "buffer=", NULL }; + char *place_arg[] = { "line=", "name=", "group=", "priority=", "file=", + "buffer=", NULL }; return (char_u *)place_arg[idx]; } case EXP_UNPLACE: { - char *unplace_arg[] = { "file=", "buffer=", NULL }; + char *unplace_arg[] = { "group=", "file=", "buffer=", NULL }; return (char_u *)unplace_arg[idx]; } case EXP_SIGN_NAMES: { diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index b745ee04bd..bb8bfc65d1 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -28,8 +28,8 @@ func Test_sign() let a=execute('sign list Sign1') call assert_equal("\nsign Sign1 text=x ", a) - " Split the window to the bottom to verify sign jump will stay in the current window - " if the buffer is displayed there. + " Split the window to the bottom to verify sign jump will stay in the + " current window if the buffer is displayed there. let bn = bufnr('%') let wn = winnr() exe 'sign place 41 line=3 name=Sign1 buffer=' . bn @@ -211,19 +211,20 @@ func Test_sign_completion() call assert_equal('"sign undefine Sign1 Sign2', @:) call feedkeys(":sign place 1 \\\"\", 'tx') - call assert_equal('"sign place 1 buffer= file= line= name=', @:) + call assert_equal('"sign place 1 buffer= file= group= line= name= priority=', + \ @:) call feedkeys(":sign place 1 name=\\\"\", 'tx') call assert_equal('"sign place 1 name=Sign1 Sign2', @:) call feedkeys(":sign unplace 1 \\\"\", 'tx') - call assert_equal('"sign unplace 1 buffer= file=', @:) + call assert_equal('"sign unplace 1 buffer= file= group=', @:) call feedkeys(":sign list \\\"\", 'tx') call assert_equal('"sign list Sign1 Sign2', @:) call feedkeys(":sign jump 1 \\\"\", 'tx') - call assert_equal('"sign jump 1 buffer= file=', @:) + call assert_equal('"sign jump 1 buffer= file= group=', @:) sign undefine Sign1 sign undefine Sign2 @@ -449,7 +450,6 @@ func Test_sign_group() edit Xsign let bnum = bufnr('%') - let fname = fnamemodify('Xsign', ':p') " Error case call assert_fails("call sign_place(5, [], 'sign1', 'Xsign', @@ -466,6 +466,10 @@ func Test_sign_group() let s = sign_getplaced('Xsign') call assert_equal(1, len(s[0].signs)) call assert_equal(s[0].signs[0].group, '') + let s = sign_getplaced(bnum, {'group' : ''}) + call assert_equal([{'id' : 5, 'group' : '', 'name' : 'sign1', 'lnum' : 10, + \ 'priority' : 10}], s[0].signs) + call assert_equal(1, len(s[0].signs)) let s = sign_getplaced(bnum, {'group' : 'g2'}) call assert_equal('g2', s[0].signs[0].group) let s = sign_getplaced(bnum, {'group' : 'g3'}) @@ -584,134 +588,434 @@ func Test_sign_group() sign place 5 group=g1 line=10 name=sign1 file=Xsign sign place 5 group=g2 line=10 name=sign1 file=Xsign - " Test for :sign place group={group} file={fname} + " Tests for the ':sign place' command + + " :sign place file={fname} let a = execute('sign place file=Xsign') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=10 id=5 name=sign1 priority=10\n", a) + " :sign place group={group} file={fname} let a = execute('sign place group=g2 file=Xsign') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=10 id=5 group=g2 name=sign1 priority=10\n", a) + " :sign place group=* file={fname} let a = execute('sign place group=* file=Xsign') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . \ " line=10 id=5 group=g2 name=sign1 priority=10\n" . \ " line=10 id=5 group=g1 name=sign1 priority=10\n" . \ " line=10 id=5 name=sign1 priority=10\n", a) + " Error case: non-existing group let a = execute('sign place group=xyz file=Xsign') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n", a) call sign_unplace('*') - - " Test for :sign place group={group} buffer={nr} let bnum = bufnr('Xsign') exe 'sign place 5 line=10 name=sign1 buffer=' . bnum exe 'sign place 5 group=g1 line=11 name=sign1 buffer=' . bnum exe 'sign place 5 group=g2 line=12 name=sign1 buffer=' . bnum + " :sign place buffer={fname} let a = execute('sign place buffer=' . bnum) call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=10 id=5 name=sign1 priority=10\n", a) + " :sign place group={group} buffer={fname} let a = execute('sign place group=g2 buffer=' . bnum) call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=12 id=5 group=g2 name=sign1 priority=10\n", a) + " :sign place group=* buffer={fname} let a = execute('sign place group=* buffer=' . bnum) call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . \ " line=10 id=5 name=sign1 priority=10\n" . \ " line=11 id=5 group=g1 name=sign1 priority=10\n" . \ " line=12 id=5 group=g2 name=sign1 priority=10\n", a) + " Error case: non-existing group let a = execute('sign place group=xyz buffer=' . bnum) call assert_equal("\n--- Signs ---\nSigns for Xsign:\n", a) + " :sign place + let a = execute('sign place') + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=10 id=5 name=sign1 priority=10\n", a) + + " :sign place group={group} + let a = execute('sign place group=g1') + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=11 id=5 group=g1 name=sign1 priority=10\n", a) + + " :sign place group=* let a = execute('sign place group=*') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . \ " line=10 id=5 name=sign1 priority=10\n" . \ " line=11 id=5 group=g1 name=sign1 priority=10\n" . \ " line=12 id=5 group=g2 name=sign1 priority=10\n", a) - " Test for :sign unplace - sign unplace 5 group=g2 file=Xsign - call assert_equal([], sign_getplaced(bnum, {'group' : 'g2'})[0].signs) + " Test for ':sign jump' command with groups + sign jump 5 group=g1 file=Xsign + call assert_equal(11, line('.')) + call assert_equal('Xsign', bufname('')) + sign jump 5 group=g2 file=Xsign + call assert_equal(12, line('.')) - exe 'sign unplace 5 group=g1 buffer=' . bnum - call assert_equal([], sign_getplaced(bnum, {'group' : 'g1'})[0].signs) + " Error cases + call assert_fails("sign place 3 group= name=sign1 buffer=" . bnum, 'E474:') - sign unplace 5 group=xy file=Xsign - call assert_equal(1, len(sign_getplaced(bnum, {'group' : '*'})[0].signs)) + call delete("Xsign") + call sign_unplace('*') + call sign_undefine() + enew | only +endfunc - " Test for removing all the signs. Place the signs again for this test - sign place 5 group=g1 line=11 name=sign1 file=Xsign - sign place 5 group=g2 line=12 name=sign1 file=Xsign - sign place 6 line=20 name=sign1 file=Xsign - sign place 6 group=g1 line=21 name=sign1 file=Xsign - sign place 6 group=g2 line=22 name=sign1 file=Xsign - sign unplace 5 group=* file=Xsign - let a = execute('sign place group=*') - call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . - \ " line=20 id=6 name=sign1 priority=10\n" . - \ " line=21 id=6 group=g1 name=sign1 priority=10\n" . - \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) +" Place signs used for ":sign unplace" command test +func Place_signs_for_test() + call sign_unplace('*') - " Remove all the signs from the global group - sign unplace * file=Xsign - let a = execute('sign place group=*') - call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . - \ " line=21 id=6 group=g1 name=sign1 priority=10\n" . - \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) + sign place 3 line=10 name=sign1 file=Xsign1 + sign place 3 group=g1 line=11 name=sign1 file=Xsign1 + sign place 3 group=g2 line=12 name=sign1 file=Xsign1 + sign place 4 line=15 name=sign1 file=Xsign1 + sign place 4 group=g1 line=16 name=sign1 file=Xsign1 + sign place 4 group=g2 line=17 name=sign1 file=Xsign1 + sign place 5 line=20 name=sign1 file=Xsign2 + sign place 5 group=g1 line=21 name=sign1 file=Xsign2 + sign place 5 group=g2 line=22 name=sign1 file=Xsign2 + sign place 6 line=25 name=sign1 file=Xsign2 + sign place 6 group=g1 line=26 name=sign1 file=Xsign2 + sign place 6 group=g2 line=27 name=sign1 file=Xsign2 +endfunc - " Remove all the signs from a particular group - sign place 5 line=10 name=sign1 file=Xsign - sign place 5 group=g1 line=11 name=sign1 file=Xsign - sign place 5 group=g2 line=12 name=sign1 file=Xsign - sign unplace * group=g1 file=Xsign - let a = execute('sign place group=*') - call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . - \ " line=10 id=5 name=sign1 priority=10\n" . - \ " line=12 id=5 group=g2 name=sign1 priority=10\n" . - \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) - - " Remove all the signs from all the groups in a file - sign place 5 group=g1 line=11 name=sign1 file=Xsign - sign place 6 line=20 name=sign1 file=Xsign - sign place 6 group=g1 line=21 name=sign1 file=Xsign - sign unplace * group=* file=Xsign - let a = execute('sign place group=*') - call assert_equal("\n--- Signs ---\n", a) +" Place multiple signs in a single line for test +func Place_signs_at_line_for_test() + call sign_unplace('*') + sign place 3 line=13 name=sign1 file=Xsign1 + sign place 3 group=g1 line=13 name=sign1 file=Xsign1 + sign place 3 group=g2 line=13 name=sign1 file=Xsign1 + sign place 4 line=13 name=sign1 file=Xsign1 + sign place 4 group=g1 line=13 name=sign1 file=Xsign1 + sign place 4 group=g2 line=13 name=sign1 file=Xsign1 +endfunc - " Remove a particular sign id in a group from all the files - sign place 5 group=g1 line=11 name=sign1 file=Xsign - sign unplace 5 group=g1 - let a = execute('sign place group=*') - call assert_equal("\n--- Signs ---\n", a) +" Tests for the ':sign unplace' command +func Test_sign_unplace() + enew | only + " Remove all the signs + call sign_unplace('*') + call sign_undefine() - " Remove a particular sign id in all the groups from all the files - sign place 5 line=10 name=sign1 file=Xsign - sign place 5 group=g1 line=11 name=sign1 file=Xsign - sign place 5 group=g2 line=12 name=sign1 file=Xsign - sign place 6 line=20 name=sign1 file=Xsign - sign place 6 group=g1 line=21 name=sign1 file=Xsign - sign place 6 group=g2 line=22 name=sign1 file=Xsign + " Create two files and define signs + call writefile(repeat(["Sun is shining"], 30), "Xsign1") + call writefile(repeat(["It is beautiful"], 30), "Xsign2") + + let attr = {'text' : '=>', 'linehl' : 'Search', 'texthl' : 'Error'} + call sign_define("sign1", attr) + + edit Xsign1 + let bnum1 = bufnr('%') + split Xsign2 + let bnum2 = bufnr('%') + + let signs1 = [{'id' : 3, 'name' : 'sign1', 'lnum' : 10, 'group' : '', + \ 'priority' : 10}, + \ {'id' : 3, 'name' : 'sign1', 'lnum' : 11, 'group' : 'g1', + \ 'priority' : 10}, + \ {'id' : 3, 'name' : 'sign1', 'lnum' : 12, 'group' : 'g2', + \ 'priority' : 10}, + \ {'id' : 4, 'name' : 'sign1', 'lnum' : 15, 'group' : '', + \ 'priority' : 10}, + \ {'id' : 4, 'name' : 'sign1', 'lnum' : 16, 'group' : 'g1', + \ 'priority' : 10}, + \ {'id' : 4, 'name' : 'sign1', 'lnum' : 17, 'group' : 'g2', + \ 'priority' : 10},] + let signs2 = [{'id' : 5, 'name' : 'sign1', 'lnum' : 20, 'group' : '', + \ 'priority' : 10}, + \ {'id' : 5, 'name' : 'sign1', 'lnum' : 21, 'group' : 'g1', + \ 'priority' : 10}, + \ {'id' : 5, 'name' : 'sign1', 'lnum' : 22, 'group' : 'g2', + \ 'priority' : 10}, + \ {'id' : 6, 'name' : 'sign1', 'lnum' : 25, 'group' : '', + \ 'priority' : 10}, + \ {'id' : 6, 'name' : 'sign1', 'lnum' : 26, 'group' : 'g1', + \ 'priority' : 10}, + \ {'id' : 6, 'name' : 'sign1', 'lnum' : 27, 'group' : 'g2', + \ 'priority' : 10},] + + " Test for :sign unplace {id} file={fname} + call Place_signs_for_test() + sign unplace 3 file=Xsign1 + sign unplace 6 file=Xsign2 + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.id != 3 || val.group != ''}), + \ sign_getplaced('Xsign1', {'group' : '*'})[0].signs) + call assert_equal( + \ filter(copy(signs2), + \ {idx, val -> val.id != 6 || val.group != ''}), + \ sign_getplaced('Xsign2', {'group' : '*'})[0].signs) + + " Test for :sign unplace {id} group={group} file={fname} + call Place_signs_for_test() + sign unplace 4 group=g1 file=Xsign1 + sign unplace 5 group=g2 file=Xsign2 + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.id != 4 || val.group != 'g1'}), + \ sign_getplaced('Xsign1', {'group' : '*'})[0].signs) + call assert_equal( + \ filter(copy(signs2), + \ {idx, val -> val.id != 5 || val.group != 'g2'}), + \ sign_getplaced('Xsign2', {'group' : '*'})[0].signs) + + " Test for :sign unplace {id} group=* file={fname} + call Place_signs_for_test() + sign unplace 3 group=* file=Xsign1 + sign unplace 6 group=* file=Xsign2 + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.id != 3}), + \ sign_getplaced('Xsign1', {'group' : '*'})[0].signs) + call assert_equal( + \ filter(copy(signs2), + \ {idx, val -> val.id != 6}), + \ sign_getplaced('Xsign2', {'group' : '*'})[0].signs) + + " Test for :sign unplace * file={fname} + call Place_signs_for_test() + sign unplace * file=Xsign1 + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.group != ''}), + \ sign_getplaced('Xsign1', {'group' : '*'})[0].signs) + call assert_equal(signs2, sign_getplaced('Xsign2', {'group' : '*'})[0].signs) + + " Test for :sign unplace * group={group} file={fname} + call Place_signs_for_test() + sign unplace * group=g1 file=Xsign1 + sign unplace * group=g2 file=Xsign2 + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.group != 'g1'}), + \ sign_getplaced('Xsign1', {'group' : '*'})[0].signs) + call assert_equal( + \ filter(copy(signs2), + \ {idx, val -> val.group != 'g2'}), + \ sign_getplaced('Xsign2', {'group' : '*'})[0].signs) + + " Test for :sign unplace * group=* file={fname} + call Place_signs_for_test() + sign unplace * group=* file=Xsign2 + call assert_equal(signs1, sign_getplaced('Xsign1', {'group' : '*'})[0].signs) + call assert_equal([], sign_getplaced('Xsign2', {'group' : '*'})[0].signs) + + " Test for :sign unplace {id} buffer={nr} + call Place_signs_for_test() + exe 'sign unplace 3 buffer=' . bnum1 + exe 'sign unplace 6 buffer=' . bnum2 + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.id != 3 || val.group != ''}), + \ sign_getplaced(bnum1, {'group' : '*'})[0].signs) + call assert_equal( + \ filter(copy(signs2), + \ {idx, val -> val.id != 6 || val.group != ''}), + \ sign_getplaced(bnum2, {'group' : '*'})[0].signs) + + " Test for :sign unplace {id} group={group} buffer={nr} + call Place_signs_for_test() + exe 'sign unplace 4 group=g1 buffer=' . bnum1 + exe 'sign unplace 5 group=g2 buffer=' . bnum2 + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.id != 4 || val.group != 'g1'}), + \ sign_getplaced(bnum1, {'group' : '*'})[0].signs) + call assert_equal( + \ filter(copy(signs2), + \ {idx, val -> val.id != 5 || val.group != 'g2'}), + \ sign_getplaced(bnum2, {'group' : '*'})[0].signs) + + " Test for :sign unplace {id} group=* buffer={nr} + call Place_signs_for_test() + exe 'sign unplace 3 group=* buffer=' . bnum1 + exe 'sign unplace 6 group=* buffer=' . bnum2 + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.id != 3}), + \ sign_getplaced(bnum1, {'group' : '*'})[0].signs) + call assert_equal( + \ filter(copy(signs2), + \ {idx, val -> val.id != 6}), + \ sign_getplaced(bnum2, {'group' : '*'})[0].signs) + + " Test for :sign unplace * buffer={nr} + call Place_signs_for_test() + exe 'sign unplace * buffer=' . bnum1 + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.group != ''}), + \ sign_getplaced(bnum1, {'group' : '*'})[0].signs) + call assert_equal(signs2, sign_getplaced(bnum2, {'group' : '*'})[0].signs) + + " Test for :sign unplace * group={group} buffer={nr} + call Place_signs_for_test() + exe 'sign unplace * group=g1 buffer=' . bnum1 + exe 'sign unplace * group=g2 buffer=' . bnum2 + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.group != 'g1'}), + \ sign_getplaced(bnum1, {'group' : '*'})[0].signs) + call assert_equal( + \ filter(copy(signs2), + \ {idx, val -> val.group != 'g2'}), + \ sign_getplaced(bnum2, {'group' : '*'})[0].signs) + + " Test for :sign unplace * group=* buffer={nr} + call Place_signs_for_test() + exe 'sign unplace * group=* buffer=' . bnum2 + call assert_equal(signs1, sign_getplaced(bnum1, {'group' : '*'})[0].signs) + call assert_equal([], sign_getplaced(bnum2, {'group' : '*'})[0].signs) + + " Test for :sign unplace {id} + call Place_signs_for_test() + sign unplace 4 + sign unplace 6 + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.id != 4 || val.group != ''}), + \ sign_getplaced('Xsign1', {'group' : '*'})[0].signs) + call assert_equal( + \ filter(copy(signs2), + \ {idx, val -> val.id != 6 || val.group != ''}), + \ sign_getplaced('Xsign2', {'group' : '*'})[0].signs) + + " Test for :sign unplace {id} group={group} + call Place_signs_for_test() + sign unplace 4 group=g1 + sign unplace 6 group=g2 + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.id != 4 || val.group != 'g1'}), + \ sign_getplaced('Xsign1', {'group' : '*'})[0].signs) + call assert_equal( + \ filter(copy(signs2), + \ {idx, val -> val.id != 6 || val.group != 'g2'}), + \ sign_getplaced('Xsign2', {'group' : '*'})[0].signs) + + " Test for :sign unplace {id} group=* + call Place_signs_for_test() + sign unplace 3 group=* sign unplace 5 group=* - let a = execute('sign place group=*') - call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . - \ " line=20 id=6 name=sign1 priority=10\n" . - \ " line=21 id=6 group=g1 name=sign1 priority=10\n" . - \ " line=22 id=6 group=g2 name=sign1 priority=10\n", a) + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.id != 3}), + \ sign_getplaced('Xsign1', {'group' : '*'})[0].signs) + call assert_equal( + \ filter(copy(signs2), + \ {idx, val -> val.id != 5}), + \ sign_getplaced('Xsign2', {'group' : '*'})[0].signs) - " Remove all the signs from all the groups in all the files - sign place 5 line=10 name=sign1 file=Xsign - sign place 5 group=g1 line=11 name=sign1 file=Xsign + " Test for :sign unplace * + call Place_signs_for_test() + sign unplace * + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.group != ''}), + \ sign_getplaced('Xsign1', {'group' : '*'})[0].signs) + call assert_equal( + \ filter(copy(signs2), + \ {idx, val -> val.group != ''}), + \ sign_getplaced('Xsign2', {'group' : '*'})[0].signs) + + " Test for :sign unplace * group={group} + call Place_signs_for_test() + sign unplace * group=g1 + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.group != 'g1'}), + \ sign_getplaced('Xsign1', {'group' : '*'})[0].signs) + call assert_equal( + \ filter(copy(signs2), + \ {idx, val -> val.group != 'g1'}), + \ sign_getplaced('Xsign2', {'group' : '*'})[0].signs) + + " Test for :sign unplace * group=* + call Place_signs_for_test() sign unplace * group=* - let a = execute('sign place group=*') - call assert_equal("\n--- Signs ---\n", a) + call assert_equal([], sign_getplaced('Xsign1', {'group' : '*'})[0].signs) + call assert_equal([], sign_getplaced('Xsign2', {'group' : '*'})[0].signs) - " Error cases - call assert_fails("sign place 3 group= name=sign1 buffer=" . bnum, 'E474:') + " Negative test cases + call Place_signs_for_test() + sign unplace 3 group=xy file=Xsign1 + sign unplace * group=xy file=Xsign1 + silent! sign unplace * group=* file=FileNotPresent + call assert_equal(signs1, sign_getplaced('Xsign1', {'group' : '*'})[0].signs) + call assert_equal(signs2, sign_getplaced('Xsign2', {'group' : '*'})[0].signs) + + " Tests for removing sign at the current cursor position + + " Test for ':sign unplace' + let signs1 = [{'id' : 4, 'name' : 'sign1', 'lnum' : 13, 'group' : 'g2', + \ 'priority' : 10}, + \ {'id' : 4, 'name' : 'sign1', 'lnum' : 13, 'group' : 'g1', + \ 'priority' : 10}, + \ {'id' : 4, 'name' : 'sign1', 'lnum' : 13, 'group' : '', + \ 'priority' : 10}, + \ {'id' : 3, 'name' : 'sign1', 'lnum' : 13, 'group' : 'g2', + \ 'priority' : 10}, + \ {'id' : 3, 'name' : 'sign1', 'lnum' : 13, 'group' : 'g1', + \ 'priority' : 10}, + \ {'id' : 3, 'name' : 'sign1', 'lnum' : 13, 'group' : '', + \ 'priority' : 10},] + exe bufwinnr('Xsign1') . 'wincmd w' + call cursor(13, 1) + + " Should remove only one sign in the global group + call Place_signs_at_line_for_test() + sign unplace + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.id != 4 || val.group != ''}), + \ sign_getplaced('Xsign1', {'group' : '*'})[0].signs) + " Should remove the second sign in the global group + sign unplace + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.group != ''}), + \ sign_getplaced('Xsign1', {'group' : '*'})[0].signs) + + " Test for ':sign unplace group={group}' + call Place_signs_at_line_for_test() + " Should remove only one sign in group g1 + sign unplace group=g1 + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.id != 4 || val.group != 'g1'}), + \ sign_getplaced('Xsign1', {'group' : '*'})[0].signs) + sign unplace group=g2 + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.id != 4 || val.group == ''}), + \ sign_getplaced('Xsign1', {'group' : '*'})[0].signs) + + " Test for ':sign unplace group=*' + call Place_signs_at_line_for_test() + sign unplace group=* + sign unplace group=* + sign unplace group=* + call assert_equal( + \ filter(copy(signs1), + \ {idx, val -> val.id != 4}), + \ sign_getplaced('Xsign1', {'group' : '*'})[0].signs) + sign unplace group=* + sign unplace group=* + sign unplace group=* + call assert_equal([], sign_getplaced('Xsign1', {'group' : '*'})[0].signs) - call delete("Xsign") call sign_unplace('*') call sign_undefine() enew | only + call delete("Xsign1") + call delete("Xsign2") endfunc " Tests for auto-generating the sign identifier @@ -762,7 +1066,6 @@ func Test_sign_priority() " Place three signs with different priority in the same line call writefile(repeat(["Sun is shining"], 30), "Xsign") edit Xsign - let fname = fnamemodify('Xsign', ':p') call sign_place(1, 'g1', 'sign1', 'Xsign', \ {'lnum' : 11, 'priority' : 50}) -- cgit From 88720c626eadc7626ad64ff31eeefcdc69ed176a Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Fri, 17 May 2019 12:20:22 +0200 Subject: vim-patch:8.1.0660: sign_cleanup() may leak memory Problem: sign_cleanup() may leak memory. Solution: Free the group name before returning. Add a few more tests. (Yegappan Lakshmanan) https://github.com/vim/vim/commit/1ea88a3e12e5f6afc5353cd0d6e6d4f4a89ced94 --- src/nvim/eval.c | 6 ++++-- src/nvim/testdir/test_signs.vim | 21 ++++++++++++++------- 2 files changed, 18 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index cbe2a89b5b..ed47d27613 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -15717,7 +15717,7 @@ f_sign_unplace(typval_T *argvars, typval_T *rettv) if (argvars[1].v_type != VAR_DICT) { EMSG(_(e_dictreq)); - return; + goto cleanup; } dict = argvars[1].vval.v_dict; @@ -15728,7 +15728,7 @@ f_sign_unplace(typval_T *argvars, typval_T *rettv) { EMSG2(_("E158: Invalid buffer name: %s"), tv_get_string(&di->di_tv)); - return; + goto cleanup; } } if (tv_dict_find(dict, (char_u *)"id", -1) != NULL) @@ -15747,6 +15747,8 @@ f_sign_unplace(typval_T *argvars, typval_T *rettv) if (sign_unplace(sign_id, group, buf, 0) == OK) rettv->vval.v_number = 0; } + +cleanup: xfree(group); } diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index bb8bfc65d1..dc76812d88 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -385,6 +385,8 @@ func Test_sign_funcs() \ {"lnum" : -1})', 'E885:') call assert_fails('call sign_place(22, "", "sign1", "Xsign", \ {"lnum" : 0})', 'E885:') + call assert_fails('call sign_place(22, "", "sign1", "Xsign", + \ {"lnum" : []})', 'E745:') call assert_equal(-1, sign_place(1, "*", "sign1", "Xsign", {"lnum" : 10})) " Tests for sign_getplaced() @@ -420,9 +422,9 @@ func Test_sign_funcs() \ {'id' : 20, 'buffer' : 'buffer.c'})", 'E158:') call assert_fails("call sign_unplace('', \ {'id' : 20, 'buffer' : ''})", 'E158:') - call assert_fails("call sign_unplace('', + call assert_fails("call sign_unplace('g1', \ {'id' : 20, 'buffer' : 200})", 'E158:') - call assert_fails("call sign_unplace('', 'mySign')", 'E715:') + call assert_fails("call sign_unplace('g1', 'mySign')", 'E715:') " Tests for sign_undefine() call assert_equal(0, sign_undefine("sign1")) @@ -664,7 +666,7 @@ func Test_sign_group() call delete("Xsign") call sign_unplace('*') call sign_undefine() - enew | only + enew | only endfunc " Place signs used for ":sign unplace" command test @@ -1013,7 +1015,7 @@ func Test_sign_unplace() call sign_unplace('*') call sign_undefine() - enew | only + enew | only call delete("Xsign1") call delete("Xsign2") endfunc @@ -1042,6 +1044,9 @@ func Test_sign_id_autogen() call assert_equal(1, sign_place(0, 'g1', 'sign1', 'Xsign', \ {'lnum' : 11})) + " Check for the next generated sign id in this group + call assert_equal(2, sign_place(0, 'g1', 'sign1', 'Xsign', + \ {'lnum' : 12})) call assert_equal(0, sign_unplace('g1', {'id' : 1})) call assert_equal(10, \ sign_getplaced('Xsign', {'id' : 1})[0].signs[0].lnum) @@ -1049,7 +1054,7 @@ func Test_sign_id_autogen() call delete("Xsign") call sign_unplace('*') call sign_undefine() - enew | only + enew | only endfunc " Test for sign priority @@ -1085,6 +1090,8 @@ func Test_sign_priority() " Error case call assert_fails("call sign_place(1, 'g1', 'sign1', 'Xsign', \ [])", 'E715:') + call assert_fails("call sign_place(1, 'g1', 'sign1', 'Xsign', + \ {'priority' : []})", 'E745:') call sign_unplace('*') " Tests for the :sign place command with priority @@ -1104,7 +1111,7 @@ func Test_sign_priority() call sign_unplace('*') call sign_undefine() - enew | only + enew | only call delete("Xsign") endfunc @@ -1146,6 +1153,6 @@ func Test_sign_memfailures() call sign_unplace('*') call sign_undefine() - enew | only + enew | only call delete("Xsign") endfunc -- cgit From ce021baea069df6aae89e0225834b1be7b26c989 Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Fri, 17 May 2019 12:34:50 +0200 Subject: vim-patch:8.1.0669: the ex_sign() function is too long Problem: The ex_sign() function is too long. Solution: Refactor the function. Add a bit more testing. (Yegappan Lakshmanan, closes vim/vim#3745) https://github.com/vim/vim/commit/a355652ea5b0c1633e8126ad9af2d970e05f4e1a --- src/nvim/ex_cmds.c | 528 +++++++++++++++++++++++----------------- src/nvim/testdir/test_signs.vim | 30 +++ 2 files changed, 341 insertions(+), 217 deletions(-) (limited to 'src') diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 5d9dc7508f..212aeb4c27 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -5830,6 +5830,9 @@ int sign_place( int sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum) { + if (buf->b_signlist == NULL) { // No signs in the buffer + return OK; + } if (sign_id == 0) { // Delete all the signs in the specified buffer @@ -5866,298 +5869,389 @@ static void sign_unplace_at_cursor(char_u *groupname) } /* - * ":sign" command + * sign define command + * ":sign define {name} ..." */ -void ex_sign(exarg_T *eap) +static void sign_define_cmd(char_u *sign_name, char_u *cmdline) { - char_u *arg = eap->arg; - char_u *p; - int idx; - sign_T *sp; + char_u *arg; + char_u *p = cmdline; + char_u *icon = NULL; + char_u *text = NULL; + char_u *linehl = NULL; + char_u *texthl = NULL; + char_u *numhl = NULL; + int failed = FALSE; + + // set values for a defined sign. + for (;;) { + arg = skipwhite(p); + if (*arg == NUL) { + break; + } + p = skiptowhite_esc(arg); + if (STRNCMP(arg, "icon=", 5) == 0) { + arg += 5; + icon = vim_strnsave(arg, (int)(p - arg)); + } else if (STRNCMP(arg, "text=", 5) == 0) { + arg += 5; + text = vim_strnsave(arg, (int)(p - arg)); + } else if (STRNCMP(arg, "linehl=", 7) == 0) { + arg += 7; + linehl = vim_strnsave(arg, (int)(p - arg)); + } else if (STRNCMP(arg, "texthl=", 7) == 0) { + arg += 7; + texthl = vim_strnsave(arg, (int)(p - arg)); + } else if (STRNCMP(arg, "numhl=", 6) == 0) { + arg += 6; + numhl = vim_strnsave(arg, (int)(p - arg)); + } else { + EMSG2(_(e_invarg2), arg); + failed = TRUE; + break; + } + } - // Parse the subcommand. - p = skiptowhite(arg); - idx = sign_cmd_idx(arg, p); - if (idx == SIGNCMD_LAST) { - EMSG2(_("E160: Unknown sign command: %s"), arg); - return; + if (!failed) { + sign_define_by_name(sign_name, icon, linehl, text, texthl, numhl); } - arg = skipwhite(p); - if (idx <= SIGNCMD_LIST) { - // 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_list_defined(sp); - } - } else if (*arg == NUL) { - EMSG(_("E156: Missing sign name")); + xfree(icon); + xfree(text); + xfree(linehl); + xfree(texthl); + xfree(numhl); +} + +/* + * :sign place command + */ +static void sign_place_cmd( + buf_T *buf, + linenr_T lnum, + char_u *sign_name, + int id, + char_u *group, + int prio + ) +{ + if (id <= 0) + { + // List signs placed in a file/buffer + // :sign place file={fname} + // :sign place group={group} file={fname} + // :sign place group=* file={fname} + // :sign place buffer={nr} + // :sign place group={group} buffer={nr} + // :sign place group=* buffer={nr} + // :sign place + // :sign place group={group} + // :sign place group=* + if (lnum >= 0 || sign_name != NULL || + (group != NULL && *group == '\0')) { + EMSG(_(e_invarg)); } else { - char_u *name; - char_u *icon = NULL; - char_u *text = NULL; - char_u *linehl = NULL; - char_u *texthl = NULL; - char_u *numhl = NULL; + sign_list_placed(buf, group); + } + } else { + // Place a new sign + if (sign_name == NULL || buf == NULL || + (group != NULL && *group == '\0')) { + EMSG(_(e_invarg)); + return; + } - // 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); - if (*p != NUL) { - *p++ = NUL; - } - while (arg[0] == '0' && arg[1] != NUL) { - arg++; - } - name = vim_strsave(arg); + sign_place(&id, group, sign_name, buf, lnum, prio); + } +} - if (idx == SIGNCMD_DEFINE) { - int failed = FALSE; - // ":sign define {name} ...": define a sign +/* + * :sign unplace command + */ +static void sign_unplace_cmd( + buf_T *buf, + linenr_T lnum, + char_u *sign_name, + int id, + char_u *group +) +{ + if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0')) { + EMSG(_(e_invarg)); + return; + } - // set values for a defined sign. - for (;;) { - arg = skipwhite(p); - if (*arg == NUL) { - break; + 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(buf) { + if (buf->b_signlist != NULL) { + buf_delete_signs(buf, group); } - p = skiptowhite_esc(arg); - if (STRNCMP(arg, "icon=", 5) == 0) { - arg += 5; - icon = vim_strnsave(arg, (int)(p - arg)); - } else if (STRNCMP(arg, "text=", 5) == 0) { - arg += 5; - text = vim_strnsave(arg, (int)(p - arg)); - } else if (STRNCMP(arg, "linehl=", 7) == 0) { - arg += 7; - linehl = vim_strnsave(arg, (int)(p - arg)); - } else if (STRNCMP(arg, "texthl=", 7) == 0) { - arg += 7; - texthl = vim_strnsave(arg, (int)(p - arg)); - } else if (STRNCMP(arg, "numhl=", 6) == 0) { - arg += 6; - numhl = vim_strnsave(arg, (int)(p - arg)); - } else { - EMSG2(_(e_invarg2), arg); - failed = TRUE; - break; + } + } + } 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(buf) { + sign_unplace(id, group, buf, 0); } } + } + } +} - if (!failed) - sign_define_by_name(name, icon, linehl, text, texthl, numhl); +/* + * Jump to a placed sign + * :sign jump {id} file={fname} + * :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, + char_u *sign_name, + int id, + char_u *group + ) +{ + if (buf == NULL && sign_name == NULL && group == NULL && id == -1) { + EMSG(_(e_argreq)); + return; + } - xfree(icon); - xfree(text); - xfree(linehl); - xfree(texthl); - } else if (idx == SIGNCMD_LIST) { - // ":sign list {name}" - sign_list_by_name(name); - } else { - // ":sign undefine {name}" - sign_undefine_by_name(name); - } + if (buf == NULL || (group != NULL && *group == '\0') || + lnum >= 0 || sign_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)); + return; + } - xfree(name); - return; - } - } else { - int id = -1; - linenr_T lnum = -1; - char_u *sign_name = NULL; - char_u *group = NULL; - int prio = SIGN_DEF_PRIO; - char_u *arg1; - int bufarg = FALSE; + if ((lnum = buf_findsign(buf, id, group)) <= 0) { + EMSGN(_("E157: Invalid sign ID: %ld"), id); + return; + } - if (*arg == NUL) { - if (idx == SIGNCMD_PLACE) { - // ":sign place": list placed signs in all buffers - sign_list_placed(NULL, NULL); - } else if (idx == SIGNCMD_UNPLACE) { - // ":sign unplace": remove placed sign at cursor - sign_unplace_at_cursor(NULL); - } else { - EMSG(_(e_argreq)); - } + // goto a sign ... + if (buf_jump_open_win(buf) != NULL) + { // ... in a current window + curwin->w_cursor.lnum = lnum; + check_cursor_lnum(); + beginline(BL_WHITE); + } else { // ... not currently in a window + if (buf->b_fname == NULL) { + EMSG(_("E934: Cannot jump to a buffer that does not have a name")); return; } + size_t cmdlen = STRLEN(buf->b_fname) + 24; + char *cmd = xmallocz(cmdlen); + snprintf(cmd, cmdlen, "e +%" PRId64 " %s", + (int64_t)lnum, buf->b_fname); + do_cmdline_cmd(cmd); + xfree(cmd); + } - if (idx == SIGNCMD_UNPLACE && arg[0] == '*' && arg[1] == NUL) { - // ":sign unplace *": remove all placed signs - buf_delete_all_signs(NULL); - return; - } + foldOpenCursor(); +} + +/* + * Parse the command line arguments for the ":sign place", ":sign unplace" and + * ":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_u *arg, + char_u **sign_name, + int *signid, + char_u **group, + int *prio, + buf_T **buf, + linenr_T *lnum +) +{ + char_u *arg1; + char_u *name; + char_u *filename = NULL; // first arg could be placed sign id arg1 = arg; if (ascii_isdigit(*arg)) { - id = getdigits_int(&arg); + *signid = getdigits_int(&arg); if (!ascii_iswhite(*arg) && *arg != NUL) { - id = -1; + *signid = -1; arg = arg1; } else { arg = skipwhite(arg); - if (idx == SIGNCMD_UNPLACE && *arg == NUL) { - // ":sign unplace {id}": remove placed sign by number - FOR_ALL_BUFFERS(buf) { - sign_unplace(id, NULL, buf, 0); - } - return; - } } } - // Check for line={lnum} name={name} and file={fname} or buffer={nr}. - // Leave "arg" pointing to {fname}. - - buf_T *buf = NULL; while (*arg != NUL) { if (STRNCMP(arg, "line=", 5) == 0) { arg += 5; - lnum = atoi((char *)arg); + *lnum = atoi((char *)arg); arg = skiptowhite(arg); - } else if (STRNCMP(arg, "*", 1) == 0 && idx == SIGNCMD_UNPLACE) { - if (id != -1) { + } else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE) { + if (*signid != -1) { EMSG(_(e_invarg)); - return; + return FAIL; } - id = -2; + *signid = -2; arg = skiptowhite(arg + 1); } else if (STRNCMP(arg, "name=", 5) == 0) { arg += 5; - sign_name = arg; + name = arg; arg = skiptowhite(arg); if (*arg != NUL) { *arg++ = NUL; } - while (sign_name[0] == '0' && sign_name[1] != NUL) { - sign_name++; + while (name[0] == '0' && name[1] != NUL) { + ++name; } + *sign_name = name; } else if (STRNCMP(arg, "group=", 6) == 0) { arg += 6; - group = arg; + *group = arg; arg = skiptowhite(arg); if (*arg != NUL) { *arg++ = NUL; } } else if (STRNCMP(arg, "priority=", 9) == 0) { arg += 9; - prio = atoi((char *)arg); + *prio = atoi((char *)arg); arg = skiptowhite(arg); } else if (STRNCMP(arg, "file=", 5) == 0) { arg += 5; - buf = buflist_findname_exp(arg); - bufarg = TRUE; + filename = arg; + *buf = buflist_findname_exp(arg); break; } else if (STRNCMP(arg, "buffer=", 7) == 0) { arg += 7; - buf = buflist_findnr(getdigits_int(&arg)); + filename = arg; + *buf = buflist_findnr(getdigits_int(&arg)); if (*skipwhite(arg) != NUL) { EMSG(_(e_trailing)); } - bufarg = TRUE; break; } else { EMSG(_(e_invarg)); - return; + return FAIL; } arg = skipwhite(arg); } - if ((!bufarg && group == NULL) || (group != NULL && *group == '\0')) { - // File or buffer is not specified or an empty group is used - EMSG(_(e_invarg)); - return; + if (filename != NULL && *buf == NULL) { + EMSG2(_("E158: Invalid buffer name: %s"), filename); + return FAIL; } - if (bufarg && buf == NULL) { - EMSG2(_("E158: Invalid buffer name: %s"), arg); - } else if (id <= 0 && idx == SIGNCMD_PLACE) { - if ((group == NULL) && (lnum >= 0 || sign_name != NULL)) { - EMSG(_(e_invarg)); - } else { - // ":sign place file={fname}": list placed signs in one file - // ":sign place group={group} file={fname}" - // ":sign place group=* file={fname}" - sign_list_placed(buf, group); + return OK; +} + +/* + * ":sign" command + */ +void ex_sign(exarg_T *eap) +{ + char_u *arg = eap->arg; + char_u *p; + int idx; + sign_T *sp; + + // Parse the subcommand. + p = skiptowhite(arg); + idx = sign_cmd_idx(arg, p); + if (idx == SIGNCMD_LAST) { + EMSG2(_("E160: Unknown sign command: %s"), arg); + return; + } + arg = skipwhite(p); + + if (idx <= SIGNCMD_LIST) { + // 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_list_defined(sp); } - } else if (idx == SIGNCMD_JUMP) { - // ":sign jump {id} file={fname}" - // ":sign jump {id} group={group} file={fname}" - if (lnum >= 0 || sign_name != NULL || buf == NULL){ - EMSG(_(e_invarg)); - } else if ((lnum = buf_findsign(buf, id, group)) > 0) { - // goto a sign ... - if (buf_jump_open_win(buf) != NULL) { - // ... in a current window - curwin->w_cursor.lnum = lnum; - check_cursor_lnum(); - beginline(BL_WHITE); - } else { - // ... not currently in a window - if (buf->b_fname == NULL) { - EMSG(_("E934: Cannot jump to a buffer that does not have a name")); - return; - } - size_t cmdlen = STRLEN(buf->b_fname) + 24; - char *cmd = xmallocz(cmdlen); - snprintf(cmd, cmdlen, "e +%" PRId64 " %s", - (int64_t)lnum, buf->b_fname); - do_cmdline_cmd(cmd); - xfree(cmd); - } + } else if (*arg == NUL) { + EMSG(_("E156: Missing sign name")); + } else { + char_u *name; - foldOpenCursor(); - } else { - EMSGN(_("E157: Invalid sign ID: %" PRId64), id); + // 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); + if (*p != NUL) { + *p++ = NUL; } - } else if (idx == SIGNCMD_UNPLACE) { - if (lnum >= 0 || sign_name != NULL) { - EMSG(_(e_invarg)); - } else if (id == -2) { - if (buf != NULL) { - // ":sign unplace * file={fname}" - sign_unplace(0, group, buf, 0); - } else { - // ":sign unplace * group=*": remove all placed signs - FOR_ALL_BUFFERS(buf) { - if (buf->b_signlist != NULL) { - buf_delete_signs(buf, group); - } - } - } + while (arg[0] == '0' && arg[1] != NUL) { + arg++; + } + name = vim_strsave(arg); + + if (idx == SIGNCMD_DEFINE) { + sign_define_cmd(name, p); + } else if (idx == SIGNCMD_LIST) { + // ":sign list {name}" + sign_list_by_name(name); } 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, group, buf, 0); - } else { - if (id == -1) { - // ":sign unplace group={group}": - // ":sign unplace group=*": - // remove sign in the current line in specified group - sign_unplace_at_cursor(group); - } else { - // ":sign unplace {id} group={group}": - // ":sign unplace {id} group=*": - // remove all placed signs in this group. - FOR_ALL_BUFFERS(buf) { - if (buf->b_signlist != NULL) { - sign_unplace(id, group, buf, 0); - } - } - } - } + // ":sign undefine {name}" + sign_undefine_by_name(name); } - } else if (sign_name != NULL && buf != NULL) { - // idx == SIGNCMD_PLACE - sign_place(&id, group, sign_name, buf, lnum, prio); - } else { - EMSG(_(e_invarg)); + + xfree(name); + return; + } + } else { + int id = -1; + linenr_T lnum = -1; + char_u *sign_name = NULL; + char_u *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) + return; + + if (idx == SIGNCMD_PLACE) { + sign_place_cmd(buf, lnum, sign_name, id, group, prio); + } else if (idx == SIGNCMD_UNPLACE) { + sign_unplace_cmd(buf, lnum, sign_name, id, group); + } else if (idx == SIGNCMD_JUMP) { + sign_jump_cmd(buf, lnum, sign_name, id, group); } } } diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index dc76812d88..e3cbd465ce 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -104,6 +104,16 @@ func Test_sign() exe 'sign jump 43 file=' . fn call assert_equal('B', getline('.')) + " Check for jumping to a sign in a hidden buffer + enew! | only! + edit foo + call setline(1, ['A', 'B', 'C', 'D']) + let fn = expand('%:p') + exe 'sign place 21 line=3 name=Sign3 file=' . fn + hide edit bar + exe 'sign jump 21 file=' . fn + call assert_equal('C', getline('.')) + " can't define a sign with a non-printable character as text call assert_fails("sign define Sign4 text=\e linehl=Comment", 'E239:') call assert_fails("sign define Sign4 text=a\e linehl=Comment", 'E239:') @@ -131,6 +141,18 @@ func Test_sign() sign define Sign4 text=\\ linehl=Comment sign undefine Sign4 + " define a sign with a leading 0 in the name + sign unplace * + sign define 004 text=#> linehl=Comment + let a = execute('sign list 4') + call assert_equal("\nsign 4 text=#> linehl=Comment", a) + exe 'sign place 20 line=3 name=004 buffer=' . bufnr('') + let a = execute('sign place') + call assert_equal("\n--- Signs ---\nSigns for foo:\n line=3 id=20 name=4 priority=10\n", a) + exe 'sign unplace 20 buffer=' . bufnr('') + sign undefine 004 + call assert_fails('sign list 4', 'E155:') + " Error cases call assert_fails("sign place abc line=3 name=Sign1 buffer=" . \ bufnr('%'), 'E474:') @@ -241,6 +263,14 @@ func Test_sign_invalid_commands() call assert_fails('sign undefine', 'E156:') call assert_fails('sign list xxx', 'E155:') call assert_fails('sign place 1 buffer=999', 'E158:') + call assert_fails('sign place 1 name=Sign1 buffer=999', 'E158:') + call assert_fails('sign place buffer=999', 'E158:') + call assert_fails('sign jump buffer=999', 'E158:') + call assert_fails('sign jump 1 file=', 'E158:') + call assert_fails('sign jump 1 group=', 'E474:') + call assert_fails('sign jump 1 name=', 'E474:') + call assert_fails('sign jump 1 name=Sign1', 'E474:') + call assert_fails('sign jump 1 line=100', '474:') call assert_fails('sign define Sign2 text=', 'E239:') " Non-numeric identifier for :sign place call assert_fails("sign place abc line=3 name=Sign1 buffer=" . bufnr('%'), 'E474:') -- cgit From 83025f0028ee5b1b6e09340fa419999643e3f8f6 Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Sat, 18 May 2019 15:35:33 +0200 Subject: vim-patch:8.1.0673: functionality for signs is spread out over several files Problem: Functionality for signs is spread out over several files. Solution: Move most of the sign functionality into sign.c. (Yegappan Lakshmanan, closes vim/vim#3751) https://github.com/vim/vim/commit/bbea47075cc4e7826e9f8c203e4272ba023ed7b0 --- src/nvim/buffer.c | 618 ------------------ src/nvim/eval.c | 17 +- src/nvim/ex_cmds.c | 1150 -------------------------------- src/nvim/ex_docmd.c | 1 + src/nvim/ex_getln.c | 1 + src/nvim/screen.c | 1 + src/nvim/sign.c | 1814 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/nvim/sign.h | 10 + src/nvim/syntax.c | 1 + 9 files changed, 1829 insertions(+), 1784 deletions(-) create mode 100644 src/nvim/sign.c create mode 100644 src/nvim/sign.h (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 9ccbfa4dd1..1f4a1e0cd1 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -5256,172 +5256,6 @@ bool find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp) return false; } -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(char_u *groupname) -{ - hash_T hash; - hashitem_T *hi; - signgroup_T *group; - - hash = hash_hash(groupname); - hi = hash_lookup(&sg_table, S_LEN(groupname), hash); - if (HASHITEM_EMPTY(hi)) - { - // new group - group = (signgroup_T *)xmalloc( - (unsigned)(sizeof(signgroup_T) + STRLEN(groupname))); - if (group == NULL) - return NULL; - STRCPY(group->sg_name, groupname); - group->refcount = 1; - group->next_sign_id = 1; - hash_add_item(&sg_table, hi, group->sg_name, hash); - } - else - { - // existing group - group = HI2SG(hi); - group->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_u *groupname) -{ - hashitem_T *hi; - signgroup_T *group; - - hi = hash_find(&sg_table, groupname); - if (!HASHITEM_EMPTY(hi)) - { - group = HI2SG(hi); - group->refcount--; - if (group->refcount == 0) - { - // All the signs in this group are removed - hash_remove(&sg_table, hi); - xfree(group); - } - } -} - -/* - * Get the next free sign identifier in the specified group - */ - int -sign_group_get_next_signid(buf_T *buf, char_u *groupname) -{ - int id = 1; - signgroup_T *group = NULL; - signlist_T *sign; - hashitem_T *hi; - int found = FALSE; - - if (groupname != NULL) - { - hi = hash_find(&sg_table, groupname); - if (HASHITEM_EMPTY(hi)) - return id; - group = HI2SG(hi); - } - - // Search for the next usuable sign identifier - while (!found) - { - if (group == NULL) - id = next_sign_id++; // global group - else - id = group->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->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. - */ -static void insert_sign( - buf_T *buf, // buffer to store sign in - signlist_T *prev, // previous sign entry - signlist_T *next, // next sign entry - int id, // sign ID - char_u *group, // sign group; NULL for global group - int prio, // sign priority - linenr_T lnum, // line number which gets the mark - int typenr // typenr of sign we are adding -) -{ - signlist_T *newsign = xmalloc(sizeof(signlist_T)); - newsign->id = id; - newsign->lnum = lnum; - newsign->typenr = typenr; - if (group != NULL) - { - newsign->group = sign_group_ref(group); - if (newsign->group == NULL) - { - xfree(newsign); - return; - } - } - else - newsign->group = NULL; - newsign->priority = prio; - newsign->next = next; - newsign->prev = prev; - if (next != NULL) { - next->prev = newsign; - } - buf->b_signcols_max = -1; - - 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, NOT_VALID); - changed_cline_bef_curs(); - } - - // first sign in signlist - buf->b_signlist = newsign; - } - else { - prev->next = newsign; - } -} - static int sign_compare(const void *a1, const void *a2) { const signlist_T *s1 = *(const signlist_T **)a1; @@ -5512,458 +5346,6 @@ int buf_signcols(buf_T *buf) return buf->b_signcols; } -/* - * Insert a new sign sorted by line number and sign priority. - */ -static void insert_sign_by_lnum_prio( - buf_T *buf, // buffer to store sign in - signlist_T *prev, // previous sign entry - int id, // sign ID - char_u *group, // sign group; NULL for global group - int prio, // sign priority - linenr_T lnum, // line number which gets the mark - int typenr // typenr of sign we are adding -) -{ - signlist_T *sign; - - // keep signs sorted by lnum and by priority: insert new sign at - // the proper position in the list for this lnum. - while (prev != NULL && prev->lnum == lnum && prev->priority <= prio) { - prev = prev->prev; - } - if (prev == NULL) { - sign = buf->b_signlist; - } else { - sign = prev->next; - } - - insert_sign(buf, prev, sign, id, group, prio, lnum, typenr); -} - -/* - * Returns 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. - */ -int sign_in_group(signlist_T *sign, char_u *group) -{ - return ((group != NULL && STRCMP(group, "*") == 0) - || (group == NULL && sign->group == NULL) - || (group != NULL && sign->group != NULL - && STRCMP(group, sign->group->sg_name) == 0)); -} - -/* - * Return information about a sign in a Dict - */ -dict_T * sign_get_info(signlist_T *sign) -{ - dict_T *d; - - if ((d = tv_dict_alloc()) == NULL) { - return NULL; - } - tv_dict_add_nr(d, S_LEN("id"), sign->id); - tv_dict_add_str(d, S_LEN("group"), (sign->group == NULL) ? (char_u *)"" : sign->group->sg_name); - tv_dict_add_nr(d, S_LEN("lnum"), sign->lnum); - tv_dict_add_str(d, S_LEN("name"), sign_typenr2name(sign->typenr)); - tv_dict_add_nr(d, S_LEN("priority"), sign->priority); - - return d; -} - -/* - * Add the sign into the signlist. Find the right spot to do it though. - */ -void buf_addsign( - buf_T *buf, // buffer to store sign in - int id, // sign ID - char_u *groupname, // sign group - int prio, // sign priority - linenr_T lnum, // line number which gets the mark - int typenr // typenr of sign we are adding -) -{ - signlist_T **lastp; // pointer to pointer to current sign - signlist_T *sign; // a sign in the signlist - signlist_T *prev; // the previous sign - - prev = NULL; - FOR_ALL_SIGNS_IN_BUF(buf, sign) { - if (lnum == sign->lnum && id == sign->id && - sign_in_group(sign, groupname)) { - // Update an existing sign - sign->typenr = typenr; - return; - } else if ((lnum == sign->lnum && id != sign->id) - || (id < 0 && lnum < sign->lnum)) { - insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr); - return; - } - prev = sign; - } - - insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr); - - // Having more than one sign with _the same type_ and on the _same line_ is - // unwanted, let's prevent it. - - lastp = &buf->b_signlist; - for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { - if (lnum == sign->lnum && sign->typenr == typenr && id != sign->id) { - *lastp = sign->next; - xfree(sign); - } else { - lastp = &sign->next; - } - } -} - -// 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. -linenr_T buf_change_sign_type( - buf_T *buf, // buffer to store sign in - int markId, // sign ID - char_u *group, // sign group - int typenr // typenr of sign we are adding -) -{ - signlist_T *sign; // a sign in the signlist - - FOR_ALL_SIGNS_IN_BUF(buf, sign) { - if (sign->id == markId && sign_in_group(sign, group)) { - sign->typenr = typenr; - return sign->lnum; - } - } - - return (linenr_T)0; -} - - -/// Gets a sign from a given line. -/// -/// @param buf Buffer in which to search -/// @param lnum Line in which to search -/// @param type Type of sign to look for -/// @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 Identifier of the matching sign, or 0 -int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type, - int idx, int max_signs) -{ - signlist_T *sign; // a sign in a b_signlist - signlist_T *matches[9]; - int nr_matches = 0; - - FOR_ALL_SIGNS_IN_BUF(buf, sign) { - if (sign->lnum == lnum - && (type == SIGN_ANY - || (type == SIGN_TEXT - && sign_get_text(sign->typenr) != NULL) - || (type == SIGN_LINEHL - && sign_get_attr(sign->typenr, SIGN_LINEHL) != 0) - || (type == SIGN_NUMHL - && sign_get_attr(sign->typenr, SIGN_NUMHL) != 0))) { - matches[nr_matches] = sign; - nr_matches++; - - if (nr_matches == ARRAY_SIZE(matches)) { - break; - } - } - } - - if (nr_matches > 0) { - if (nr_matches > max_signs) { - idx += nr_matches - max_signs; - } - - if (idx >= nr_matches) { - return 0; - } - - return matches[idx]->typenr; - } - - return 0; -} - -/* - * 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. - * Returns the line number of the deleted sign. If multiple signs are deleted, - * then returns the line number of the last sign deleted. - */ -linenr_T buf_delsign( - buf_T *buf, // buffer sign is stored in - linenr_T atlnum, // sign at this line, 0 - at any line - int id, // sign id - char_u *group// sign group - ) -{ - signlist_T **lastp; // pointer to pointer to current sign - signlist_T *sign; // a sign in a b_signlist - signlist_T *next; // the next sign in a b_signlist - linenr_T lnum; // line number whose sign was deleted - - buf->b_signcols_max = -1; - lastp = &buf->b_signlist; - lnum = 0; - for (sign = buf->b_signlist; sign != NULL; sign = next) { - next = sign->next; - if ((id == 0 || sign->id == id) && - (atlnum == 0 || sign->lnum == atlnum) && - sign_in_group(sign, group)) { - *lastp = next; - if (next != NULL) { - next->prev = sign->prev; - } - lnum = sign->lnum; - if (sign->group != NULL) - sign_group_unref(sign->group->sg_name); - xfree(sign); - redraw_buf_line_later(buf, lnum); - // Check whether only one sign needs to be deleted - // If deleting a sign with a specific identifer 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->next; - } - } - - /* When deleted the last sign needs to redraw the windows to remove the - * sign column. */ - if (buf->b_signlist == NULL) { - redraw_buf_later(buf, NOT_VALID); - changed_cline_bef_curs(); - } - - 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. - */ -int buf_findsign( - buf_T *buf, // buffer to store sign in - int id, // sign ID - char_u *group // sign group -) -{ - signlist_T *sign; // a sign in the signlist - - FOR_ALL_SIGNS_IN_BUF(buf, sign) { - if (sign->id == id && sign_in_group(sign, group)){ - return (int)sign->lnum; - } - } - - 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. - */ -static signlist_T * buf_getsign_at_line( - buf_T *buf, // buffer whose sign we are searching for - linenr_T lnum, // line number of sign - char_u *groupname // sign group name -) -{ - signlist_T *sign; // a sign in the signlist - - FOR_ALL_SIGNS_IN_BUF(buf, sign) { - if (sign->lnum == lnum && sign_in_group(sign, groupname)) { - return sign; - } - } - - return NULL; -} - -/* - * Return the sign with identifier 'id' in group 'group' placed in buffer 'buf' - */ -signlist_T *buf_getsign_with_id( - buf_T *buf, // buffer whose sign we are searching for - int id, // sign identifier - char_u *group// sign group -) -{ - signlist_T *sign; // a sign in the signlist - - FOR_ALL_SIGNS_IN_BUF(buf, sign) { - if (sign->id == id && sign_in_group(sign, group)) { - return sign; - } - } - - return NULL; -} - -/* - * Return the identifier of the sign at line number 'lnum' in buffer 'buf'. - */ -int buf_findsign_id( - buf_T *buf, // buffer whose sign we are searching for - linenr_T lnum, // line number of sign - char_u *groupname // sign group name -) -{ - signlist_T *sign; // a sign in the signlist - - sign = buf_getsign_at_line(buf, lnum, groupname); - if (sign != NULL) { - return sign->id; - } - - return 0; -} - - -/* - * Delete signs in buffer "buf". - */ -void buf_delete_signs(buf_T *buf, char_u *group) -{ - signlist_T *sign; - signlist_T **lastp; // pointer to pointer to current sign - signlist_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){ - redraw_buf_later(buf, NOT_VALID); - changed_cline_bef_curs(); - } - - lastp = &buf->b_signlist; - for (sign = buf->b_signlist; sign != NULL; sign = next) { - next = sign->next; - if (sign_in_group(sign, group)) { - *lastp = next; - if (next != NULL) { - next->prev = sign->prev; - } - if (sign->group != NULL) { - sign_group_unref(sign->group->sg_name); - } - xfree(sign); - } else { - lastp = &sign->next; - } - } - buf->b_signcols_max = -1; -} - -/* - * Delete all signs in all buffers. - */ -void buf_delete_all_signs(char_u *groupname) -{ - FOR_ALL_BUFFERS(buf) { - if (buf->b_signlist != NULL) { - buf_delete_signs(buf, groupname); - } - } -} - -/* - * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers. - */ -void sign_list_placed(buf_T *rbuf, char_u *sign_group) -{ - buf_T *buf; - signlist_T *sign; - char lbuf[BUFSIZ]; - char group[BUFSIZ]; - - 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) { - vim_snprintf(lbuf, BUFSIZ, _("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->group != NULL) { - vim_snprintf(group, BUFSIZ, " group=%s", sign->group->sg_name); - } else { - group[0] = '\0'; - } - vim_snprintf(lbuf, BUFSIZ, _(" line=%ld id=%d%s name=%s " - "priority=%d"), - (long)sign->lnum, sign->id, group, - sign_typenr2name(sign->typenr), sign->priority); - MSG_PUTS(lbuf); - msg_putchar('\n'); - } - if (rbuf != NULL) { - break; - } - buf = buf->b_next; - } -} - -/* - * Adjust a placed sign for inserted/deleted lines. - */ -void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after) -{ - signlist_T *sign; // a sign in a b_signlist - signlist_T *next; // the next sign in a b_signlist - signlist_T **lastp; // pointer to pointer to current sign - - curbuf->b_signcols_max = -1; - lastp = &curbuf->b_signlist; - - FOR_ALL_SIGNS_IN_BUF(curbuf, sign) { - next = sign->next; - if (sign->lnum >= line1 && sign->lnum <= line2) { - if (amount == MAXLNUM) { - *lastp = next; - xfree(sign); - continue; - } else { - sign->lnum += amount; - } - } else if (sign->lnum > line2) { - sign->lnum += amount_after; - } - lastp = &sign->next; - } -} - // bufhl: plugin highlights associated with a buffer /// Get reference to line in kbtree_t diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ed47d27613..076dbcf487 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -68,6 +68,7 @@ #include "nvim/screen.h" #include "nvim/search.h" #include "nvim/sha256.h" +#include "nvim/sign.h" #include "nvim/spell.h" #include "nvim/state.h" #include "nvim/strings.h" @@ -9286,22 +9287,6 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } -/// Returns information about signs placed in a buffer as list of dicts. -static list_T *get_buffer_signs(buf_T *buf) - FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT -{ - signlist_T *sign; - dict_T *d; - list_T *const l = tv_list_alloc(kListLenMayKnow); - - FOR_ALL_SIGNS_IN_BUF(buf, sign) { - if ((d = sign_get_info(sign)) != NULL) { - tv_list_append_dict(l, d); - } - } - return l; -} - /// Returns buffer options, variables and other attributes in a dictionary. static dict_T *get_buffer_info(buf_T *buf) { diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 212aeb4c27..8722c03204 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -67,10 +67,6 @@ #include "nvim/os/input.h" #include "nvim/os/time.h" -/* - * Struct to hold the sign properties. - */ -typedef struct sign sign_T; /// Case matching style to use for :substitute typedef enum { @@ -5521,21 +5517,6 @@ void ex_helptags(exarg_T *eap) } } -struct sign -{ - sign_T *sn_next; // next sign in list - int sn_typenr; // type number of sign - char_u *sn_name; // name of sign - char_u *sn_icon; // name of pixmap - char_u *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_num_hl; // highlight ID for line number -}; - -static sign_T *first_sign = NULL; -static int next_sign_typenr = 1; - /* * ":helpclose": Close one help window */ @@ -5549,1137 +5530,6 @@ void ex_helpclose(exarg_T *eap) } } -static char *cmds[] = { - "define", -#define SIGNCMD_DEFINE 0 - "undefine", -#define SIGNCMD_UNDEFINE 1 - "list", -#define SIGNCMD_LIST 2 - "place", -#define SIGNCMD_PLACE 3 - "unplace", -#define SIGNCMD_UNPLACE 4 - "jump", -#define SIGNCMD_JUMP 5 - NULL -#define SIGNCMD_LAST 6 -}; - -/* - * Find index of a ":sign" subcmd from its name. - * "*end_cmd" must be writable. - */ -static int sign_cmd_idx( - char_u *begin_cmd, /* begin of sign subcmd */ - char_u *end_cmd /* just after sign subcmd */ - ) -{ - int idx; - char save = *end_cmd; - - *end_cmd = NUL; - for (idx = 0; ; ++idx) { - if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0) { - break; - } - } - *end_cmd = save; - return idx; -} - -/* - * Find a sign by name. Also returns pointer to the previous sign. - */ - static sign_T * -sign_find(char_u *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; -} - -/* - * Define a new sign or update an existing sign - */ -int sign_define_by_name( - char_u *name, - char_u *icon, - char_u *linehl, - char_u *text, - char_u *texthl, - char_u *numhl -) -{ - sign_T *sp_prev; - sign_T *sp; - - sp = sign_find(name, &sp_prev); - if (sp == NULL) - { - 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 FAIL; - } - 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 = vim_strsave(name); - - // add the new sign to the list of signs - if (sp_prev == NULL) { - first_sign = sp; - } else { - sp_prev->sn_next = sp; - } - } - - // set values for a defined sign. - if (icon != NULL) - { - xfree(sp->sn_icon); - sp->sn_icon = vim_strsave(icon); - backslash_halve(sp->sn_icon); -# ifdef FEAT_SIGN_ICONS - if (gui.in_use) - { - out_flush(); - if (sp->sn_image != NULL) - gui_mch_destroy_sign(sp->sn_image); - sp->sn_image = gui_mch_register_sign(sp->sn_icon); - } -# endif - } - - if (text != NULL) - { - char_u *s; - char_u *endp; - int cells; - int len; - - endp = text + (int)STRLEN(text); - for (s = text; s + 1 < endp; ++s) { - if (*s == '\\') - { - // 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; - for (s = text; s < endp; s += (*mb_ptr2len)(s)) { - if (!vim_isprintc(utf_ptr2char(s))) { - break; - } - cells += utf_ptr2cells(s); - } - // Currently must be one or two display cells - if (s != endp || cells < 1 || cells > 2) { - EMSG2(_("E239: Invalid sign text: %s"), text); - return FAIL; - } - - xfree(sp->sn_text); - // Allocate one byte more if we need to pad up - // with a space. - len = (int)(endp - text + ((cells == 1) ? 1 : 0)); - sp->sn_text = vim_strnsave(text, len); - - if (cells == 1) - STRCPY(sp->sn_text + len - 1, " "); - } - - if (linehl != NULL) - sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl)); - - if (texthl != NULL) - sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl)); - - if (numhl != NULL) - sp->sn_num_hl = syn_check_group(numhl, (int)STRLEN(numhl)); - - return OK; -} - -/* - * Free the sign specified by 'name'. - */ - int -sign_undefine_by_name(char_u *name) -{ - sign_T *sp_prev; - sign_T *sp; - - sp = sign_find(name, &sp_prev); - if (sp == NULL) - { - EMSG2(_("E155: Unknown sign: %s"), name); - return FAIL; - } - sign_undefine(sp, sp_prev); - - return OK; -} - -/* - * List the signs matching 'name' - */ - static void -sign_list_by_name(char_u *name) -{ - sign_T *sp; - - sp = sign_find(name, NULL); - if (sp != NULL) - sign_list_defined(sp); - else - EMSG2(_("E155: Unknown sign: %s"), name); -} - -/* - * Place a sign at the specifed file location or update a sign. - */ -int sign_place( - int *sign_id, - char_u *sign_group, - char_u *sign_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')) { - return FAIL; - } - - for (sp = first_sign; sp != NULL; sp = sp->sn_next) { - if (STRCMP(sp->sn_name, sign_name) == 0) { - break; - } - } - if (sp == NULL) - { - EMSG2(_("E155: Unknown sign: %s"), sign_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 - buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr); - } else { - // ":sign place {id} file={fname}": change sign type - lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr); - } - if (lnum > 0) { - redraw_buf_line_later(buf, lnum); - } else { - EMSG2(_("E885: Not possible to change sign %s"), sign_name); - return FAIL; - } - - return OK; -} - -/* - * Unplace the specified sign - */ - int -sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum) -{ - if (buf->b_signlist == NULL) { // No signs in the buffer - return OK; - } - if (sign_id == 0) - { - // Delete all the signs in the specified buffer - redraw_buf_later(buf, 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) { - return FAIL; - update_debug_sign(buf, lnum); - } - redraw_buf_line_later(buf, lnum); - } - - return OK; -} - -/* - * Unplace the sign at the current cursor line. - */ -static void sign_unplace_at_cursor(char_u *groupname) -{ - 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); - } else { - EMSG(_("E159: Missing sign number")); - } -} - -/* - * sign define command - * ":sign define {name} ..." - */ -static void sign_define_cmd(char_u *sign_name, char_u *cmdline) -{ - char_u *arg; - char_u *p = cmdline; - char_u *icon = NULL; - char_u *text = NULL; - char_u *linehl = NULL; - char_u *texthl = NULL; - char_u *numhl = NULL; - int failed = FALSE; - - // set values for a defined sign. - for (;;) { - arg = skipwhite(p); - if (*arg == NUL) { - break; - } - p = skiptowhite_esc(arg); - if (STRNCMP(arg, "icon=", 5) == 0) { - arg += 5; - icon = vim_strnsave(arg, (int)(p - arg)); - } else if (STRNCMP(arg, "text=", 5) == 0) { - arg += 5; - text = vim_strnsave(arg, (int)(p - arg)); - } else if (STRNCMP(arg, "linehl=", 7) == 0) { - arg += 7; - linehl = vim_strnsave(arg, (int)(p - arg)); - } else if (STRNCMP(arg, "texthl=", 7) == 0) { - arg += 7; - texthl = vim_strnsave(arg, (int)(p - arg)); - } else if (STRNCMP(arg, "numhl=", 6) == 0) { - arg += 6; - numhl = vim_strnsave(arg, (int)(p - arg)); - } else { - EMSG2(_(e_invarg2), arg); - failed = TRUE; - break; - } - } - - if (!failed) { - sign_define_by_name(sign_name, icon, linehl, text, texthl, numhl); - } - - xfree(icon); - xfree(text); - xfree(linehl); - xfree(texthl); - xfree(numhl); -} - -/* - * :sign place command - */ -static void sign_place_cmd( - buf_T *buf, - linenr_T lnum, - char_u *sign_name, - int id, - char_u *group, - int prio - ) -{ - if (id <= 0) - { - // List signs placed in a file/buffer - // :sign place file={fname} - // :sign place group={group} file={fname} - // :sign place group=* file={fname} - // :sign place buffer={nr} - // :sign place group={group} buffer={nr} - // :sign place group=* buffer={nr} - // :sign place - // :sign place group={group} - // :sign place group=* - if (lnum >= 0 || sign_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')) { - EMSG(_(e_invarg)); - return; - } - - sign_place(&id, group, sign_name, buf, lnum, prio); - } -} - -/* - * :sign unplace command - */ -static void sign_unplace_cmd( - buf_T *buf, - linenr_T lnum, - char_u *sign_name, - int id, - char_u *group -) -{ - if (lnum >= 0 || sign_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(buf) { - if (buf->b_signlist != NULL) { - buf_delete_signs(buf, 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(buf) { - sign_unplace(id, group, buf, 0); - } - } - } - } -} - -/* - * Jump to a placed sign - * :sign jump {id} file={fname} - * :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, - char_u *sign_name, - int id, - char_u *group - ) -{ - if (buf == NULL && sign_name == NULL && group == NULL && id == -1) { - EMSG(_(e_argreq)); - return; - } - - if (buf == NULL || (group != NULL && *group == '\0') || - lnum >= 0 || sign_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)); - return; - } - - if ((lnum = buf_findsign(buf, id, group)) <= 0) { - EMSGN(_("E157: Invalid sign ID: %ld"), id); - return; - } - - // goto a sign ... - if (buf_jump_open_win(buf) != NULL) - { // ... in a current window - curwin->w_cursor.lnum = lnum; - check_cursor_lnum(); - beginline(BL_WHITE); - } else { // ... not currently in a window - if (buf->b_fname == NULL) { - EMSG(_("E934: Cannot jump to a buffer that does not have a name")); - return; - } - size_t cmdlen = STRLEN(buf->b_fname) + 24; - char *cmd = xmallocz(cmdlen); - snprintf(cmd, cmdlen, "e +%" PRId64 " %s", - (int64_t)lnum, buf->b_fname); - do_cmdline_cmd(cmd); - xfree(cmd); - } - - foldOpenCursor(); -} - -/* - * Parse the command line arguments for the ":sign place", ":sign unplace" and - * ":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_u *arg, - char_u **sign_name, - int *signid, - char_u **group, - int *prio, - buf_T **buf, - linenr_T *lnum -) -{ - char_u *arg1; - char_u *name; - char_u *filename = NULL; - - // first arg could be placed sign id - arg1 = arg; - if (ascii_isdigit(*arg)) { - *signid = getdigits_int(&arg); - if (!ascii_iswhite(*arg) && *arg != NUL) { - *signid = -1; - arg = arg1; - } else { - arg = skipwhite(arg); - } - } - - while (*arg != NUL) { - if (STRNCMP(arg, "line=", 5) == 0) { - arg += 5; - *lnum = atoi((char *)arg); - arg = skiptowhite(arg); - } else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE) { - if (*signid != -1) { - EMSG(_(e_invarg)); - return FAIL; - } - *signid = -2; - arg = skiptowhite(arg + 1); - } else if (STRNCMP(arg, "name=", 5) == 0) { - arg += 5; - name = arg; - arg = skiptowhite(arg); - if (*arg != NUL) { - *arg++ = NUL; - } - while (name[0] == '0' && name[1] != NUL) { - ++name; - } - *sign_name = name; - } else if (STRNCMP(arg, "group=", 6) == 0) { - arg += 6; - *group = arg; - arg = skiptowhite(arg); - if (*arg != NUL) { - *arg++ = NUL; - } - } else if (STRNCMP(arg, "priority=", 9) == 0) { - arg += 9; - *prio = atoi((char *)arg); - arg = skiptowhite(arg); - } else if (STRNCMP(arg, "file=", 5) == 0) { - arg += 5; - filename = arg; - *buf = buflist_findname_exp(arg); - break; - } else if (STRNCMP(arg, "buffer=", 7) == 0) { - arg += 7; - filename = arg; - *buf = buflist_findnr(getdigits_int(&arg)); - if (*skipwhite(arg) != NUL) { - EMSG(_(e_trailing)); - } - break; - } else { - EMSG(_(e_invarg)); - return FAIL; - } - arg = skipwhite(arg); - } - - if (filename != NULL && *buf == NULL) { - EMSG2(_("E158: Invalid buffer name: %s"), filename); - return FAIL; - } - - return OK; -} - -/* - * ":sign" command - */ -void ex_sign(exarg_T *eap) -{ - char_u *arg = eap->arg; - char_u *p; - int idx; - sign_T *sp; - - // Parse the subcommand. - p = skiptowhite(arg); - idx = sign_cmd_idx(arg, p); - if (idx == SIGNCMD_LAST) { - EMSG2(_("E160: Unknown sign command: %s"), arg); - return; - } - arg = skipwhite(p); - - if (idx <= SIGNCMD_LIST) { - // 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_list_defined(sp); - } - } else if (*arg == NUL) { - EMSG(_("E156: Missing sign name")); - } else { - char_u *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); - if (*p != NUL) { - *p++ = NUL; - } - while (arg[0] == '0' && arg[1] != NUL) { - arg++; - } - name = vim_strsave(arg); - - if (idx == SIGNCMD_DEFINE) { - sign_define_cmd(name, p); - } else if (idx == SIGNCMD_LIST) { - // ":sign list {name}" - sign_list_by_name(name); - } else { - // ":sign undefine {name}" - sign_undefine_by_name(name); - } - - xfree(name); - return; - } - } else { - int id = -1; - linenr_T lnum = -1; - char_u *sign_name = NULL; - char_u *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) - return; - - if (idx == SIGNCMD_PLACE) { - sign_place_cmd(buf, lnum, sign_name, id, group, prio); - } else if (idx == SIGNCMD_UNPLACE) { - sign_unplace_cmd(buf, lnum, sign_name, id, group); - } else if (idx == SIGNCMD_JUMP) { - sign_jump_cmd(buf, lnum, sign_name, id, group); - } - } -} - -/* - * Return information about a specified sign - */ -static void sign_getinfo(sign_T *sp, dict_T *retdict) -{ - char_u *p; - - tv_dict_add_str(retdict, S_LEN("name"), (char_u *)sp->sn_name); - if (sp->sn_icon != NULL) { - tv_dict_add_str(retdict, S_LEN("icon"), (char_u *)sp->sn_icon); - } - if (sp->sn_text != NULL) { - tv_dict_add_str(retdict, S_LEN("text"), (char_u *)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 = (char_u *)"NONE"; - } - tv_dict_add_str(retdict, S_LEN("linehl"), (char_u *)p); - } - if (sp->sn_text_hl > 0) { - p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE); - if (p == NULL) { - p = (char_u *)"NONE"; - } - tv_dict_add_str(retdict, S_LEN("texthl"), (char_u *)p); - } - if (sp->sn_num_hl > 0) { - p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, FALSE); - if (p == NULL) { - p = (char_u *)"NONE"; - } - tv_dict_add_str(retdict, S_LEN("numhl"), (char_u *)p); - } -} - -/* - * If 'name' is NULL, return a list of all the defined signs. - * Otherwise, return information about the specified sign. - */ -void sign_getlist(char_u *name, list_T *retlist) -{ - sign_T *sp = first_sign; - dict_T *dict; - - if (name != NULL) { - sp = sign_find(name, NULL); - if (sp == NULL) { - return; - } - } - - for (; sp != NULL && !got_int; sp = sp->sn_next) { - if ((dict = tv_dict_alloc()) == NULL) { - return; - } - tv_list_append_dict(retlist, dict); - sign_getinfo(sp, dict); - - if (name != NULL) { // handle only the specified sign - break; - } - } -} - -/* - * 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, - char_u *sign_group, - list_T *retlist) -{ - dict_T *d; - list_T *l; - signlist_T *sign; - dict_T *sdict; - - if ((d = tv_dict_alloc()) == NULL) { - return; - } - tv_list_append_dict(retlist, d); - - tv_dict_add_nr(d, S_LEN("bufnr"), (long)buf->b_fnum); - - if ((l = tv_list_alloc(kListLenMayKnow)) == NULL) { - return; - } - tv_dict_add_list(d, S_LEN("signs"), l); - - FOR_ALL_SIGNS_IN_BUF(buf, sign) { - if (!sign_in_group(sign, sign_group)) { - continue; - } - if ((lnum == 0 && sign_id == 0) || - (sign_id == 0 && lnum == sign->lnum) || - (lnum == 0 && sign_id == sign->id) || - (lnum == sign->lnum && sign_id == sign->id)) { - if ((sdict = sign_get_info(sign)) != NULL) { - tv_list_append_dict(l, sdict); - } - } - } -} - -/* - * 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. - */ -void sign_get_placed( - buf_T *buf, - linenr_T lnum, - int sign_id, - char_u *sign_group, - list_T *retlist) -{ - if (buf != NULL) { - sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist); - } else { - FOR_ALL_BUFFERS(buf) { - if (buf->b_signlist != NULL) { - sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist); - } - } - } -} - -/* - * List one sign. - */ -static void sign_list_defined(sign_T *sp) -{ - smsg("sign %s", sp->sn_name); - if (sp->sn_icon != NULL) { - msg_puts(" icon="); - msg_outtrans(sp->sn_icon); - msg_puts(_(" (not supported)")); - } - if (sp->sn_text != NULL) { - msg_puts(" text="); - msg_outtrans(sp->sn_text); - } - 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_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); -} - -/// Gets highlighting attribute for sign "typenr" corresponding to "type". -int sign_get_attr(int typenr, SignType type) -{ - sign_T *sp; - int sign_hl = 0; - - for (sp = first_sign; sp != NULL; sp = sp->sn_next) { - if (sp->sn_typenr == typenr) { - switch (type) { - case SIGN_TEXT: - sign_hl = sp->sn_text_hl; - break; - case SIGN_LINEHL: - sign_hl = sp->sn_line_hl; - break; - case SIGN_NUMHL: - sign_hl = sp->sn_num_hl; - break; - default: - abort(); - } - if (sign_hl > 0) { - return syn_id2attr(sign_hl); - } - break; - } - } - return 0; -} - -/* - * Get text mark for sign "typenr". - * Returns NULL if there isn't one. - */ -char_u * sign_get_text(int typenr) -{ - sign_T *sp; - - for (sp = first_sign; sp != NULL; sp = sp->sn_next) - if (sp->sn_typenr == typenr) - return sp->sn_text; - return NULL; -} - - -/* - * Get the name of a sign by its typenr. - */ -char_u * 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 (char_u *)_("[Deleted]"); -} - -/* - * Undefine/free all signs. - */ -void free_signs(void) -{ - while (first_sign != NULL) - sign_undefine(first_sign, NULL); -} - -static enum -{ - EXP_SUBCMD, /* expand :sign sub-commands */ - EXP_DEFINE, /* expand :sign define {name} args */ - EXP_PLACE, /* expand :sign place {id} args */ - EXP_UNPLACE, /* expand :sign unplace" */ - EXP_SIGN_NAMES /* expand with name of placed signs */ -} expand_what; - -/// Function given to ExpandGeneric() to obtain the sign command -/// expansion. -char_u * get_sign_name(expand_T *xp, int idx) -{ - switch (expand_what) - { - case EXP_SUBCMD: - return (char_u *)cmds[idx]; - case EXP_DEFINE: { - char *define_arg[] = { "icon=", "linehl=", "text=", "texthl=", "numhl=", - NULL }; - return (char_u *)define_arg[idx]; - } - case EXP_PLACE: { - char *place_arg[] = { "line=", "name=", "group=", "priority=", "file=", - "buffer=", NULL }; - return (char_u *)place_arg[idx]; - } - case EXP_UNPLACE: { - char *unplace_arg[] = { "group=", "file=", "buffer=", NULL }; - return (char_u *)unplace_arg[idx]; - } - case EXP_SIGN_NAMES: { - // Complete with name of signs already defined - int current_idx = 0; - for (sign_T *sp = first_sign; sp != NULL; sp = sp->sn_next) { - if (current_idx++ == idx) { - return sp->sn_name; - } - } - } - return NULL; - default: - return NULL; - } -} - -/* - * Handle command line completion for :sign command. - */ -void set_context_in_sign_cmd(expand_T *xp, char_u *arg) -{ - char_u *p; - char_u *end_subcmd; - char_u *last; - int cmd_idx; - char_u *begin_subcmd_args; - - /* Default: expand subcommands. */ - xp->xp_context = EXPAND_SIGN; - expand_what = EXP_SUBCMD; - xp->xp_pattern = arg; - - end_subcmd = skiptowhite(arg); - if (*end_subcmd == NUL) - /* expand subcmd name - * :sign {subcmd}*/ - return; - - cmd_idx = sign_cmd_idx(arg, end_subcmd); - - // :sign {subcmd} {subcmd_args} - // | - // begin_subcmd_args - begin_subcmd_args = skipwhite(end_subcmd); - p = skiptowhite(begin_subcmd_args); - if (*p == NUL) - { - /* - * Expand first argument of subcmd when possible. - * For ":jump {id}" and ":unplace {id}", we could - * possibly expand the ids of all signs already placed. - */ - xp->xp_pattern = begin_subcmd_args; - switch (cmd_idx) - { - case SIGNCMD_LIST: - case SIGNCMD_UNDEFINE: - /* :sign list - * :sign undefine */ - expand_what = EXP_SIGN_NAMES; - break; - default: - xp->xp_context = EXPAND_NOTHING; - } - return; - } - - // Expand last argument of subcmd. - // - // :sign define {name} {args}... - // | - // p - - // Loop until reaching last argument. - do - { - p = skipwhite(p); - last = p; - p = skiptowhite(p); - } while (*p != NUL); - - p = vim_strchr(last, '='); - - // :sign define {name} {args}... {last}= - // | | - // last p - if (p == NULL) { - // Expand last argument name (before equal sign). - xp->xp_pattern = last; - switch (cmd_idx) - { - case SIGNCMD_DEFINE: - expand_what = EXP_DEFINE; - break; - case SIGNCMD_PLACE: - expand_what = EXP_PLACE; - break; - case SIGNCMD_JUMP: - case SIGNCMD_UNPLACE: - expand_what = EXP_UNPLACE; - break; - default: - xp->xp_context = EXPAND_NOTHING; - } - } - else - { - /* Expand last argument value (after equal sign). */ - xp->xp_pattern = p + 1; - switch (cmd_idx) - { - case SIGNCMD_DEFINE: - if (STRNCMP(last, "texthl", p - last) == 0 - || STRNCMP(last, "linehl", p - last) == 0 - || STRNCMP(last, "numhl", p - last) == 0) { - xp->xp_context = EXPAND_HIGHLIGHT; - } else if (STRNCMP(last, "icon", p - last) == 0) { - xp->xp_context = EXPAND_FILES; - } else { - xp->xp_context = EXPAND_NOTHING; - } - break; - case SIGNCMD_PLACE: - if (STRNCMP(last, "name", p - last) == 0) - expand_what = EXP_SIGN_NAMES; - else - xp->xp_context = EXPAND_NOTHING; - break; - default: - xp->xp_context = EXPAND_NOTHING; - } - } -} - /// Shows the effects of the :substitute command being typed ('inccommand'). /// If inccommand=split, shows a preview window and later restores the layout. static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index aa76355bad..527120ae9f 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -53,6 +53,7 @@ #include "nvim/regexp.h" #include "nvim/screen.h" #include "nvim/search.h" +#include "nvim/sign.h" #include "nvim/spell.h" #include "nvim/spellfile.h" #include "nvim/strings.h" diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index beac3cd9ec..b2d40089e8 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -54,6 +54,7 @@ #include "nvim/regexp.h" #include "nvim/screen.h" #include "nvim/search.h" +#include "nvim/sign.h" #include "nvim/strings.h" #include "nvim/state.h" #include "nvim/syntax.h" diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 81ddbbfb74..31386ebd42 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -103,6 +103,7 @@ #include "nvim/quickfix.h" #include "nvim/regexp.h" #include "nvim/search.h" +#include "nvim/sign.h" #include "nvim/spell.h" #include "nvim/state.h" #include "nvim/strings.h" diff --git a/src/nvim/sign.c b/src/nvim/sign.c new file mode 100644 index 0000000000..237d1eb621 --- /dev/null +++ b/src/nvim/sign.c @@ -0,0 +1,1814 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +/* + * sign.c: functions for managing signs + */ + +#include "nvim/vim.h" +#include "nvim/sign.h" +#include "nvim/ascii.h" +#include "nvim/buffer.h" +#include "nvim/charset.h" +#include "nvim/cursor.h" +#include "nvim/ex_docmd.h" +#include "nvim/edit.h" +#include "nvim/fold.h" +#include "nvim/move.h" +#include "nvim/screen.h" +#include "nvim/syntax.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_u *sn_name; // name of sign + char_u *sn_icon; // name of pixmap +# ifdef FEAT_SIGN_ICONS + void *sn_image; /* icon image */ +# endif + char_u *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_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 char *cmds[] = { + "define", +#define SIGNCMD_DEFINE 0 + "undefine", +#define SIGNCMD_UNDEFINE 1 + "list", +#define SIGNCMD_LIST 2 + "place", +#define SIGNCMD_PLACE 3 + "unplace", +#define SIGNCMD_UNPLACE 4 + "jump", +#define SIGNCMD_JUMP 5 + NULL +#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(char_u *groupname) +{ + hash_T hash; + hashitem_T *hi; + signgroup_T *group; + + hash = hash_hash(groupname); + hi = hash_lookup(&sg_table, S_LEN(groupname), hash); + if (HASHITEM_EMPTY(hi)) + { + // new group + group = (signgroup_T *)xmalloc( + (unsigned)(sizeof(signgroup_T) + STRLEN(groupname))); + if (group == NULL) + return NULL; + STRCPY(group->sg_name, groupname); + group->refcount = 1; + group->next_sign_id = 1; + hash_add_item(&sg_table, hi, group->sg_name, hash); + } + else + { + // existing group + group = HI2SG(hi); + group->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_u *groupname) +{ + hashitem_T *hi; + signgroup_T *group; + + hi = hash_find(&sg_table, groupname); + if (!HASHITEM_EMPTY(hi)) + { + group = HI2SG(hi); + group->refcount--; + if (group->refcount == 0) + { + // All the signs in this group are removed + hash_remove(&sg_table, hi); + xfree(group); + } + } +} + +/* + * Returns 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. + */ +int sign_in_group(signlist_T *sign, char_u *group) +{ + return ((group != NULL && STRCMP(group, "*") == 0) + || (group == NULL && sign->group == NULL) + || (group != NULL && sign->group != NULL + && STRCMP(group, sign->group->sg_name) == 0)); +} + +/* + * Get the next free sign identifier in the specified group + */ + int +sign_group_get_next_signid(buf_T *buf, char_u *groupname) +{ + int id = 1; + signgroup_T *group = NULL; + signlist_T *sign; + hashitem_T *hi; + int found = FALSE; + + if (groupname != NULL) + { + hi = hash_find(&sg_table, groupname); + if (HASHITEM_EMPTY(hi)) + return id; + group = HI2SG(hi); + } + + // Search for the next usuable sign identifier + while (!found) + { + if (group == NULL) + id = next_sign_id++; // global group + else + id = group->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->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. + */ +static void insert_sign( + buf_T *buf, // buffer to store sign in + signlist_T *prev, // previous sign entry + signlist_T *next, // next sign entry + int id, // sign ID + char_u *group, // sign group; NULL for global group + int prio, // sign priority + linenr_T lnum, // line number which gets the mark + int typenr // typenr of sign we are adding +) +{ + signlist_T *newsign = xmalloc(sizeof(signlist_T)); + newsign->id = id; + newsign->lnum = lnum; + newsign->typenr = typenr; + if (group != NULL) + { + newsign->group = sign_group_ref(group); + if (newsign->group == NULL) + { + xfree(newsign); + return; + } + } + else + newsign->group = NULL; + newsign->priority = prio; + newsign->next = next; + newsign->prev = prev; + if (next != NULL) { + next->prev = newsign; + } + buf->b_signcols_max = -1; + + 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, NOT_VALID); + changed_cline_bef_curs(); + } + + // first sign in signlist + buf->b_signlist = newsign; + } + else { + prev->next = newsign; + } +} + +/* + * Insert a new sign sorted by line number and sign priority. + */ +static void insert_sign_by_lnum_prio( + buf_T *buf, // buffer to store sign in + signlist_T *prev, // previous sign entry + int id, // sign ID + char_u *group, // sign group; NULL for global group + int prio, // sign priority + linenr_T lnum, // line number which gets the mark + int typenr // typenr of sign we are adding +) +{ + signlist_T *sign; + + // keep signs sorted by lnum and by priority: insert new sign at + // the proper position in the list for this lnum. + while (prev != NULL && prev->lnum == lnum && prev->priority <= prio) { + prev = prev->prev; + } + if (prev == NULL) { + sign = buf->b_signlist; + } else { + sign = prev->next; + } + + insert_sign(buf, prev, sign, id, group, prio, lnum, typenr); +} + +/* + * Get the name of a sign by its typenr. + */ +char_u * 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 (char_u *)_("[Deleted]"); +} + +/* + * Return information about a sign in a Dict + */ +dict_T * sign_get_info(signlist_T *sign) +{ + dict_T *d; + + if ((d = tv_dict_alloc()) == NULL) { + return NULL; + } + tv_dict_add_nr(d, S_LEN("id"), sign->id); + tv_dict_add_str(d, S_LEN("group"), (sign->group == NULL) ? (char_u *)"" : sign->group->sg_name); + tv_dict_add_nr(d, S_LEN("lnum"), sign->lnum); + tv_dict_add_str(d, S_LEN("name"), sign_typenr2name(sign->typenr)); + tv_dict_add_nr(d, S_LEN("priority"), sign->priority); + + return d; +} + +/* + * Add the sign into the signlist. Find the right spot to do it though. + */ +void buf_addsign( + buf_T *buf, // buffer to store sign in + int id, // sign ID + char_u *groupname, // sign group + int prio, // sign priority + linenr_T lnum, // line number which gets the mark + int typenr // typenr of sign we are adding +) +{ + signlist_T **lastp; // pointer to pointer to current sign + signlist_T *sign; // a sign in the signlist + signlist_T *prev; // the previous sign + + prev = NULL; + FOR_ALL_SIGNS_IN_BUF(buf, sign) { + if (lnum == sign->lnum && id == sign->id && + sign_in_group(sign, groupname)) { + // Update an existing sign + sign->typenr = typenr; + return; + } else if ((lnum == sign->lnum && id != sign->id) + || (id < 0 && lnum < sign->lnum)) { + insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr); + return; + } + prev = sign; + } + + insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr); + + // Having more than one sign with _the same type_ and on the _same line_ is + // unwanted, let's prevent it. + + lastp = &buf->b_signlist; + for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { + if (lnum == sign->lnum && sign->typenr == typenr && id != sign->id) { + *lastp = sign->next; + xfree(sign); + } else { + lastp = &sign->next; + } + } +} + +// 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. +linenr_T buf_change_sign_type( + buf_T *buf, // buffer to store sign in + int markId, // sign ID + char_u *group, // sign group + int typenr // typenr of sign we are adding +) +{ + signlist_T *sign; // a sign in the signlist + + FOR_ALL_SIGNS_IN_BUF(buf, sign) { + if (sign->id == markId && sign_in_group(sign, group)) { + sign->typenr = typenr; + return sign->lnum; + } + } + + return (linenr_T)0; +} + +/// Gets a sign from a given line. +/// +/// @param buf Buffer in which to search +/// @param lnum Line in which to search +/// @param type Type of sign to look for +/// @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 Identifier of the matching sign, or 0 +int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type, + int idx, int max_signs) +{ + signlist_T *sign; // a sign in a b_signlist + signlist_T *matches[9]; + int nr_matches = 0; + + FOR_ALL_SIGNS_IN_BUF(buf, sign) { + if (sign->lnum == lnum + && (type == SIGN_ANY + || (type == SIGN_TEXT + && sign_get_text(sign->typenr) != NULL) + || (type == SIGN_LINEHL + && sign_get_attr(sign->typenr, SIGN_LINEHL) != 0) + || (type == SIGN_NUMHL + && sign_get_attr(sign->typenr, SIGN_NUMHL) != 0))) { + matches[nr_matches] = sign; + nr_matches++; + + if (nr_matches == ARRAY_SIZE(matches)) { + break; + } + } + } + + if (nr_matches > 0) { + if (nr_matches > max_signs) { + idx += nr_matches - max_signs; + } + + if (idx >= nr_matches) { + return 0; + } + + return matches[idx]->typenr; + } + + return 0; +} + +/* + * 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. + * Returns the line number of the deleted sign. If multiple signs are deleted, + * then returns the line number of the last sign deleted. + */ +linenr_T buf_delsign( + buf_T *buf, // buffer sign is stored in + linenr_T atlnum, // sign at this line, 0 - at any line + int id, // sign id + char_u *group// sign group + ) +{ + signlist_T **lastp; // pointer to pointer to current sign + signlist_T *sign; // a sign in a b_signlist + signlist_T *next; // the next sign in a b_signlist + linenr_T lnum; // line number whose sign was deleted + + buf->b_signcols_max = -1; + lastp = &buf->b_signlist; + lnum = 0; + for (sign = buf->b_signlist; sign != NULL; sign = next) { + next = sign->next; + if ((id == 0 || sign->id == id) && + (atlnum == 0 || sign->lnum == atlnum) && + sign_in_group(sign, group)) { + *lastp = next; + if (next != NULL) { + next->prev = sign->prev; + } + lnum = sign->lnum; + if (sign->group != NULL) + sign_group_unref(sign->group->sg_name); + xfree(sign); + redraw_buf_line_later(buf, lnum); + // Check whether only one sign needs to be deleted + // If deleting a sign with a specific identifer 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->next; + } + } + + /* When deleted the last sign needs to redraw the windows to remove the + * sign column. */ + if (buf->b_signlist == NULL) { + redraw_buf_later(buf, NOT_VALID); + changed_cline_bef_curs(); + } + + 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. + */ +int buf_findsign( + buf_T *buf, // buffer to store sign in + int id, // sign ID + char_u *group // sign group +) +{ + signlist_T *sign; // a sign in the signlist + + FOR_ALL_SIGNS_IN_BUF(buf, sign) { + if (sign->id == id && sign_in_group(sign, group)){ + return (int)sign->lnum; + } + } + + 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. + */ +static signlist_T * buf_getsign_at_line( + buf_T *buf, // buffer whose sign we are searching for + linenr_T lnum, // line number of sign + char_u *groupname // sign group name +) +{ + signlist_T *sign; // a sign in the signlist + + FOR_ALL_SIGNS_IN_BUF(buf, sign) { + if (sign->lnum == lnum && sign_in_group(sign, groupname)) { + return sign; + } + } + + return NULL; +} + +/* + * Return the identifier of the sign at line number 'lnum' in buffer 'buf'. + */ +int buf_findsign_id( + buf_T *buf, // buffer whose sign we are searching for + linenr_T lnum, // line number of sign + char_u *groupname // sign group name +) +{ + signlist_T *sign; // a sign in the signlist + + sign = buf_getsign_at_line(buf, lnum, groupname); + if (sign != NULL) { + return sign->id; + } + + return 0; +} + +/* + * Delete signs in buffer "buf". + */ +void buf_delete_signs(buf_T *buf, char_u *group) +{ + signlist_T *sign; + signlist_T **lastp; // pointer to pointer to current sign + signlist_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){ + redraw_buf_later(buf, NOT_VALID); + changed_cline_bef_curs(); + } + + lastp = &buf->b_signlist; + for (sign = buf->b_signlist; sign != NULL; sign = next) { + next = sign->next; + if (sign_in_group(sign, group)) { + *lastp = next; + if (next != NULL) { + next->prev = sign->prev; + } + if (sign->group != NULL) { + sign_group_unref(sign->group->sg_name); + } + xfree(sign); + } else { + lastp = &sign->next; + } + } + buf->b_signcols_max = -1; +} + +/* + * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers. + */ +void sign_list_placed(buf_T *rbuf, char_u *sign_group) +{ + buf_T *buf; + signlist_T *sign; + char lbuf[BUFSIZ]; + char group[BUFSIZ]; + + 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) { + vim_snprintf(lbuf, BUFSIZ, _("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->group != NULL) { + vim_snprintf(group, BUFSIZ, " group=%s", sign->group->sg_name); + } else { + group[0] = '\0'; + } + vim_snprintf(lbuf, BUFSIZ, _(" line=%ld id=%d%s name=%s " + "priority=%d"), + (long)sign->lnum, sign->id, group, + sign_typenr2name(sign->typenr), sign->priority); + MSG_PUTS(lbuf); + msg_putchar('\n'); + } + if (rbuf != NULL) { + break; + } + buf = buf->b_next; + } +} + +/* + * Adjust a placed sign for inserted/deleted lines. + */ +void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after) +{ + signlist_T *sign; // a sign in a b_signlist + signlist_T *next; // the next sign in a b_signlist + signlist_T **lastp; // pointer to pointer to current sign + + curbuf->b_signcols_max = -1; + lastp = &curbuf->b_signlist; + + FOR_ALL_SIGNS_IN_BUF(curbuf, sign) { + next = sign->next; + if (sign->lnum >= line1 && sign->lnum <= line2) { + if (amount == MAXLNUM) { + *lastp = next; + xfree(sign); + continue; + } else { + sign->lnum += amount; + } + } else if (sign->lnum > line2) { + sign->lnum += amount_after; + } + lastp = &sign->next; + } +} + +/* + * Find index of a ":sign" subcmd from its name. + * "*end_cmd" must be writable. + */ +static int sign_cmd_idx( + char_u *begin_cmd, /* begin of sign subcmd */ + char_u *end_cmd /* just after sign subcmd */ + ) +{ + int idx; + char save = *end_cmd; + + *end_cmd = NUL; + for (idx = 0; ; ++idx) { + if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0) { + break; + } + } + *end_cmd = save; + return idx; +} + +/* + * Find a sign by name. Also returns pointer to the previous sign. + */ + static sign_T * +sign_find(char_u *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; +} + +/* + * Define a new sign or update an existing sign + */ +int sign_define_by_name( + char_u *name, + char_u *icon, + char_u *linehl, + char_u *text, + char_u *texthl, + char_u *numhl +) +{ + sign_T *sp_prev; + sign_T *sp; + + sp = sign_find(name, &sp_prev); + if (sp == NULL) + { + 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 FAIL; + } + 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 = vim_strsave(name); + + // add the new sign to the list of signs + if (sp_prev == NULL) { + first_sign = sp; + } else { + sp_prev->sn_next = sp; + } + } + + // set values for a defined sign. + if (icon != NULL) + { + xfree(sp->sn_icon); + sp->sn_icon = vim_strsave(icon); + backslash_halve(sp->sn_icon); +# ifdef FEAT_SIGN_ICONS + if (gui.in_use) + { + out_flush(); + if (sp->sn_image != NULL) + gui_mch_destroy_sign(sp->sn_image); + sp->sn_image = gui_mch_register_sign(sp->sn_icon); + } +# endif + } + + if (text != NULL) + { + char_u *s; + char_u *endp; + int cells; + int len; + + endp = text + (int)STRLEN(text); + for (s = text; s + 1 < endp; ++s) { + if (*s == '\\') + { + // 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; + for (s = text; s < endp; s += (*mb_ptr2len)(s)) { + if (!vim_isprintc(utf_ptr2char(s))) { + break; + } + cells += utf_ptr2cells(s); + } + // Currently must be one or two display cells + if (s != endp || cells < 1 || cells > 2) { + EMSG2(_("E239: Invalid sign text: %s"), text); + return FAIL; + } + + xfree(sp->sn_text); + // Allocate one byte more if we need to pad up + // with a space. + len = (int)(endp - text + ((cells == 1) ? 1 : 0)); + sp->sn_text = vim_strnsave(text, len); + + if (cells == 1) + STRCPY(sp->sn_text + len - 1, " "); + } + + if (linehl != NULL) + sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl)); + + if (texthl != NULL) + sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl)); + + if (numhl != NULL) + sp->sn_num_hl = syn_check_group(numhl, (int)STRLEN(numhl)); + + return OK; +} + +/* + * Free the sign specified by 'name'. + */ + int +sign_undefine_by_name(char_u *name) +{ + sign_T *sp_prev; + sign_T *sp; + + sp = sign_find(name, &sp_prev); + if (sp == NULL) + { + EMSG2(_("E155: Unknown sign: %s"), name); + return FAIL; + } + sign_undefine(sp, sp_prev); + + return OK; +} + +/* + * List the signs matching 'name' + */ + static void +sign_list_by_name(char_u *name) +{ + sign_T *sp; + + sp = sign_find(name, NULL); + if (sp != NULL) + sign_list_defined(sp); + else + EMSG2(_("E155: Unknown sign: %s"), name); +} + + +/* + * Place a sign at the specifed file location or update a sign. + */ +int sign_place( + int *sign_id, + char_u *sign_group, + char_u *sign_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')) { + return FAIL; + } + + for (sp = first_sign; sp != NULL; sp = sp->sn_next) { + if (STRCMP(sp->sn_name, sign_name) == 0) { + break; + } + } + if (sp == NULL) + { + EMSG2(_("E155: Unknown sign: %s"), sign_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 + buf_addsign(buf, *sign_id, sign_group, prio, lnum, sp->sn_typenr); + } else { + // ":sign place {id} file={fname}": change sign type + lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr); + } + if (lnum > 0) { + redraw_buf_line_later(buf, lnum); + } else { + EMSG2(_("E885: Not possible to change sign %s"), sign_name); + return FAIL; + } + + return OK; +} + +/* + * Unplace the specified sign + */ + int +sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum) +{ + if (buf->b_signlist == NULL) { // No signs in the buffer + return OK; + } + if (sign_id == 0) + { + // Delete all the signs in the specified buffer + redraw_buf_later(buf, 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) { + return FAIL; + } + redraw_buf_line_later(buf, lnum); + } + + return OK; +} + +/* + * Unplace the sign at the current cursor line. + */ +static void sign_unplace_at_cursor(char_u *groupname) +{ + 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); + } else { + EMSG(_("E159: Missing sign number")); + } +} + +/* + * sign define command + * ":sign define {name} ..." + */ +static void sign_define_cmd(char_u *sign_name, char_u *cmdline) +{ + char_u *arg; + char_u *p = cmdline; + char_u *icon = NULL; + char_u *text = NULL; + char_u *linehl = NULL; + char_u *texthl = NULL; + char_u *numhl = NULL; + int failed = FALSE; + + // set values for a defined sign. + for (;;) { + arg = skipwhite(p); + if (*arg == NUL) { + break; + } + p = skiptowhite_esc(arg); + if (STRNCMP(arg, "icon=", 5) == 0) { + arg += 5; + icon = vim_strnsave(arg, (int)(p - arg)); + } else if (STRNCMP(arg, "text=", 5) == 0) { + arg += 5; + text = vim_strnsave(arg, (int)(p - arg)); + } else if (STRNCMP(arg, "linehl=", 7) == 0) { + arg += 7; + linehl = vim_strnsave(arg, (int)(p - arg)); + } else if (STRNCMP(arg, "texthl=", 7) == 0) { + arg += 7; + texthl = vim_strnsave(arg, (int)(p - arg)); + } else if (STRNCMP(arg, "numhl=", 6) == 0) { + arg += 6; + numhl = vim_strnsave(arg, (int)(p - arg)); + } else { + EMSG2(_(e_invarg2), arg); + failed = TRUE; + break; + } + } + + if (!failed) { + sign_define_by_name(sign_name, icon, linehl, text, texthl, numhl); + } + + xfree(icon); + xfree(text); + xfree(linehl); + xfree(texthl); + xfree(numhl); +} + +/* + * :sign place command + */ +static void sign_place_cmd( + buf_T *buf, + linenr_T lnum, + char_u *sign_name, + int id, + char_u *group, + int prio + ) +{ + if (id <= 0) + { + // List signs placed in a file/buffer + // :sign place file={fname} + // :sign place group={group} file={fname} + // :sign place group=* file={fname} + // :sign place buffer={nr} + // :sign place group={group} buffer={nr} + // :sign place group=* buffer={nr} + // :sign place + // :sign place group={group} + // :sign place group=* + if (lnum >= 0 || sign_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')) { + EMSG(_(e_invarg)); + return; + } + + sign_place(&id, group, sign_name, buf, lnum, prio); + } +} + +/* + * :sign unplace command + */ +static void sign_unplace_cmd( + buf_T *buf, + linenr_T lnum, + char_u *sign_name, + int id, + char_u *group +) +{ + if (lnum >= 0 || sign_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(buf) { + if (buf->b_signlist != NULL) { + buf_delete_signs(buf, 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(buf) { + sign_unplace(id, group, buf, 0); + } + } + } + } +} + +/* + * Jump to a placed sign + * :sign jump {id} file={fname} + * :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, + char_u *sign_name, + int id, + char_u *group + ) +{ + if (buf == NULL && sign_name == NULL && group == NULL && id == -1) { + EMSG(_(e_argreq)); + return; + } + + if (buf == NULL || (group != NULL && *group == '\0') || + lnum >= 0 || sign_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)); + return; + } + + if ((lnum = buf_findsign(buf, id, group)) <= 0) { + EMSGN(_("E157: Invalid sign ID: %ld"), id); + return; + } + + // goto a sign ... + if (buf_jump_open_win(buf) != NULL) + { // ... in a current window + curwin->w_cursor.lnum = lnum; + check_cursor_lnum(); + beginline(BL_WHITE); + } else { // ... not currently in a window + if (buf->b_fname == NULL) { + EMSG(_("E934: Cannot jump to a buffer that does not have a name")); + return; + } + size_t cmdlen = STRLEN(buf->b_fname) + 24; + char *cmd = xmallocz(cmdlen); + snprintf(cmd, cmdlen, "e +%" PRId64 " %s", + (int64_t)lnum, buf->b_fname); + do_cmdline_cmd(cmd); + xfree(cmd); + } + + foldOpenCursor(); +} + +/* + * Parse the command line arguments for the ":sign place", ":sign unplace" and + * ":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_u *arg, + char_u **sign_name, + int *signid, + char_u **group, + int *prio, + buf_T **buf, + linenr_T *lnum +) +{ + char_u *arg1; + char_u *name; + char_u *filename = NULL; + + // first arg could be placed sign id + arg1 = arg; + if (ascii_isdigit(*arg)) { + *signid = getdigits_int(&arg); + if (!ascii_iswhite(*arg) && *arg != NUL) { + *signid = -1; + arg = arg1; + } else { + arg = skipwhite(arg); + } + } + + while (*arg != NUL) { + if (STRNCMP(arg, "line=", 5) == 0) { + arg += 5; + *lnum = atoi((char *)arg); + arg = skiptowhite(arg); + } else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE) { + if (*signid != -1) { + EMSG(_(e_invarg)); + return FAIL; + } + *signid = -2; + arg = skiptowhite(arg + 1); + } else if (STRNCMP(arg, "name=", 5) == 0) { + arg += 5; + name = arg; + arg = skiptowhite(arg); + if (*arg != NUL) { + *arg++ = NUL; + } + while (name[0] == '0' && name[1] != NUL) { + ++name; + } + *sign_name = name; + } else if (STRNCMP(arg, "group=", 6) == 0) { + arg += 6; + *group = arg; + arg = skiptowhite(arg); + if (*arg != NUL) { + *arg++ = NUL; + } + } else if (STRNCMP(arg, "priority=", 9) == 0) { + arg += 9; + *prio = atoi((char *)arg); + arg = skiptowhite(arg); + } else if (STRNCMP(arg, "file=", 5) == 0) { + arg += 5; + filename = arg; + *buf = buflist_findname_exp(arg); + break; + } else if (STRNCMP(arg, "buffer=", 7) == 0) { + arg += 7; + filename = arg; + *buf = buflist_findnr(getdigits_int(&arg)); + if (*skipwhite(arg) != NUL) { + EMSG(_(e_trailing)); + } + break; + } else { + EMSG(_(e_invarg)); + return FAIL; + } + arg = skipwhite(arg); + } + + if (filename != NULL && *buf == NULL) { + EMSG2(_("E158: Invalid buffer name: %s"), filename); + return FAIL; + } + + return OK; +} + +/* + * ":sign" command + */ +void ex_sign(exarg_T *eap) +{ + char_u *arg = eap->arg; + char_u *p; + int idx; + sign_T *sp; + + // Parse the subcommand. + p = skiptowhite(arg); + idx = sign_cmd_idx(arg, p); + if (idx == SIGNCMD_LAST) { + EMSG2(_("E160: Unknown sign command: %s"), arg); + return; + } + arg = skipwhite(p); + + if (idx <= SIGNCMD_LIST) { + // 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_list_defined(sp); + } + } else if (*arg == NUL) { + EMSG(_("E156: Missing sign name")); + } else { + char_u *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); + if (*p != NUL) { + *p++ = NUL; + } + while (arg[0] == '0' && arg[1] != NUL) { + arg++; + } + name = vim_strsave(arg); + + if (idx == SIGNCMD_DEFINE) { + sign_define_cmd(name, p); + } else if (idx == SIGNCMD_LIST) { + // ":sign list {name}" + sign_list_by_name(name); + } else { + // ":sign undefine {name}" + sign_undefine_by_name(name); + } + + xfree(name); + return; + } + } else { + int id = -1; + linenr_T lnum = -1; + char_u *sign_name = NULL; + char_u *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) + return; + + if (idx == SIGNCMD_PLACE) { + sign_place_cmd(buf, lnum, sign_name, id, group, prio); + } else if (idx == SIGNCMD_UNPLACE) { + sign_unplace_cmd(buf, lnum, sign_name, id, group); + } else if (idx == SIGNCMD_JUMP) { + sign_jump_cmd(buf, lnum, sign_name, id, group); + } + } +} + +/* + * Return information about a specified sign + */ +static void sign_getinfo(sign_T *sp, dict_T *retdict) +{ + char_u *p; + + tv_dict_add_str(retdict, S_LEN("name"), (char_u *)sp->sn_name); + if (sp->sn_icon != NULL) { + tv_dict_add_str(retdict, S_LEN("icon"), (char_u *)sp->sn_icon); + } + if (sp->sn_text != NULL) { + tv_dict_add_str(retdict, S_LEN("text"), (char_u *)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 = (char_u *)"NONE"; + } + tv_dict_add_str(retdict, S_LEN("linehl"), (char_u *)p); + } + if (sp->sn_text_hl > 0) { + p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE); + if (p == NULL) { + p = (char_u *)"NONE"; + } + tv_dict_add_str(retdict, S_LEN("texthl"), (char_u *)p); + } + if (sp->sn_num_hl > 0) { + p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, FALSE); + if (p == NULL) { + p = (char_u *)"NONE"; + } + tv_dict_add_str(retdict, S_LEN("numhl"), (char_u *)p); + } +} + +/* + * If 'name' is NULL, return a list of all the defined signs. + * Otherwise, return information about the specified sign. + */ +void sign_getlist(char_u *name, list_T *retlist) +{ + sign_T *sp = first_sign; + dict_T *dict; + + if (name != NULL) { + sp = sign_find(name, NULL); + if (sp == NULL) { + return; + } + } + + for (; sp != NULL && !got_int; sp = sp->sn_next) { + if ((dict = tv_dict_alloc()) == NULL) { + return; + } + tv_list_append_dict(retlist, dict); + sign_getinfo(sp, dict); + + if (name != NULL) { // handle only the specified sign + break; + } + } +} + +/// 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 +{ + signlist_T *sign; + dict_T *d; + list_T *const l = tv_list_alloc(kListLenMayKnow); + + FOR_ALL_SIGNS_IN_BUF(buf, sign) { + if ((d = sign_get_info(sign)) != NULL) { + tv_list_append_dict(l, d); + } + } + 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, + char_u *sign_group, + list_T *retlist) +{ + dict_T *d; + list_T *l; + signlist_T *sign; + dict_T *sdict; + + if ((d = tv_dict_alloc()) == NULL) { + return; + } + tv_list_append_dict(retlist, d); + + tv_dict_add_nr(d, S_LEN("bufnr"), (long)buf->b_fnum); + + if ((l = tv_list_alloc(kListLenMayKnow)) == NULL) { + return; + } + tv_dict_add_list(d, S_LEN("signs"), l); + + FOR_ALL_SIGNS_IN_BUF(buf, sign) { + if (!sign_in_group(sign, sign_group)) { + continue; + } + if ((lnum == 0 && sign_id == 0) || + (sign_id == 0 && lnum == sign->lnum) || + (lnum == 0 && sign_id == sign->id) || + (lnum == sign->lnum && sign_id == sign->id)) { + if ((sdict = sign_get_info(sign)) != NULL) { + tv_list_append_dict(l, sdict); + } + } + } +} + +/* + * 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. + */ +void sign_get_placed( + buf_T *buf, + linenr_T lnum, + int sign_id, + char_u *sign_group, + list_T *retlist) +{ + if (buf != NULL) { + sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist); + } else { + FOR_ALL_BUFFERS(buf) { + if (buf->b_signlist != NULL) { + sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist); + } + } + } +} + +# if defined(FEAT_SIGN_ICONS) || defined(PROTO) +/* + * Allocate the icons. Called when the GUI has started. Allows defining + * signs before it starts. + */ + void +sign_gui_started(void) +{ + sign_T *sp; + + for (sp = first_sign; sp != NULL; sp = sp->sn_next) + if (sp->sn_icon != NULL) + sp->sn_image = gui_mch_register_sign(sp->sn_icon); +} +# endif + +/* + * List one sign. + */ +static void sign_list_defined(sign_T *sp) +{ + smsg("sign %s", sp->sn_name); + if (sp->sn_icon != NULL) { + msg_puts(" icon="); + msg_outtrans(sp->sn_icon); + msg_puts(_(" (not supported)")); + } + if (sp->sn_text != NULL) { + msg_puts(" text="); + msg_outtrans(sp->sn_text); + } + 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_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); +} + +/// Gets highlighting attribute for sign "typenr" corresponding to "type". +int sign_get_attr(int typenr, SignType type) +{ + sign_T *sp; + int sign_hl = 0; + + for (sp = first_sign; sp != NULL; sp = sp->sn_next) { + if (sp->sn_typenr == typenr) { + switch (type) { + case SIGN_TEXT: + sign_hl = sp->sn_text_hl; + break; + case SIGN_LINEHL: + sign_hl = sp->sn_line_hl; + break; + case SIGN_NUMHL: + sign_hl = sp->sn_num_hl; + break; + default: + abort(); + } + if (sign_hl > 0) { + return syn_id2attr(sign_hl); + } + break; + } + } + return 0; +} + +/* + * Get text mark for sign "typenr". + * Returns NULL if there isn't one. + */ +char_u * sign_get_text(int typenr) +{ + sign_T *sp; + + for (sp = first_sign; sp != NULL; sp = sp->sn_next) + if (sp->sn_typenr == typenr) + return sp->sn_text; + return NULL; +} + +# if defined(FEAT_SIGN_ICONS) || defined(PROTO) + void * +sign_get_image( + int typenr) /* the attribute which may have a sign */ +{ + sign_T *sp; + + for (sp = first_sign; sp != NULL; sp = sp->sn_next) + if (sp->sn_typenr == typenr) + return sp->sn_image; + return NULL; +} +# endif + +/* + * Undefine/free all signs. + */ +void free_signs(void) +{ + while (first_sign != NULL) { + sign_undefine(first_sign, NULL); + } +} + +static enum +{ + EXP_SUBCMD, /* expand :sign sub-commands */ + EXP_DEFINE, /* expand :sign define {name} args */ + EXP_PLACE, /* expand :sign place {id} args */ + EXP_UNPLACE, /* expand :sign unplace" */ + EXP_SIGN_NAMES /* expand with name of placed signs */ +} expand_what; + +/// Function given to ExpandGeneric() to obtain the sign command +/// expansion. +char_u * get_sign_name(expand_T *xp, int idx) +{ + switch (expand_what) + { + case EXP_SUBCMD: + return (char_u *)cmds[idx]; + case EXP_DEFINE: { + char *define_arg[] = { "icon=", "linehl=", "text=", "texthl=", "numhl=", + NULL }; + return (char_u *)define_arg[idx]; + } + case EXP_PLACE: { + char *place_arg[] = { "line=", "name=", "group=", "priority=", "file=", + "buffer=", NULL }; + return (char_u *)place_arg[idx]; + } + case EXP_UNPLACE: { + char *unplace_arg[] = { "group=", "file=", "buffer=", NULL }; + return (char_u *)unplace_arg[idx]; + } + case EXP_SIGN_NAMES: { + // Complete with name of signs already defined + int current_idx = 0; + for (sign_T *sp = first_sign; sp != NULL; sp = sp->sn_next) { + if (current_idx++ == idx) { + return sp->sn_name; + } + } + } + return NULL; + default: + return NULL; + } +} + +/* + * Handle command line completion for :sign command. + */ +void set_context_in_sign_cmd(expand_T *xp, char_u *arg) +{ + char_u *p; + char_u *end_subcmd; + char_u *last; + int cmd_idx; + char_u *begin_subcmd_args; + + /* Default: expand subcommands. */ + xp->xp_context = EXPAND_SIGN; + expand_what = EXP_SUBCMD; + xp->xp_pattern = arg; + + end_subcmd = skiptowhite(arg); + if (*end_subcmd == NUL) + /* expand subcmd name + * :sign {subcmd}*/ + return; + + cmd_idx = sign_cmd_idx(arg, end_subcmd); + + // :sign {subcmd} {subcmd_args} + // | + // begin_subcmd_args + begin_subcmd_args = skipwhite(end_subcmd); + p = skiptowhite(begin_subcmd_args); + if (*p == NUL) + { + /* + * Expand first argument of subcmd when possible. + * For ":jump {id}" and ":unplace {id}", we could + * possibly expand the ids of all signs already placed. + */ + xp->xp_pattern = begin_subcmd_args; + switch (cmd_idx) + { + case SIGNCMD_LIST: + case SIGNCMD_UNDEFINE: + /* :sign list + * :sign undefine */ + expand_what = EXP_SIGN_NAMES; + break; + default: + xp->xp_context = EXPAND_NOTHING; + } + return; + } + + // Expand last argument of subcmd. + // + // :sign define {name} {args}... + // | + // p + + // Loop until reaching last argument. + do + { + p = skipwhite(p); + last = p; + p = skiptowhite(p); + } while (*p != NUL); + + p = vim_strchr(last, '='); + + // :sign define {name} {args}... {last}= + // | | + // last p + if (p == NULL) { + // Expand last argument name (before equal sign). + xp->xp_pattern = last; + switch (cmd_idx) + { + case SIGNCMD_DEFINE: + expand_what = EXP_DEFINE; + break; + case SIGNCMD_PLACE: + expand_what = EXP_PLACE; + break; + case SIGNCMD_JUMP: + case SIGNCMD_UNPLACE: + expand_what = EXP_UNPLACE; + break; + default: + xp->xp_context = EXPAND_NOTHING; + } + } + else + { + /* Expand last argument value (after equal sign). */ + xp->xp_pattern = p + 1; + switch (cmd_idx) + { + case SIGNCMD_DEFINE: + if (STRNCMP(last, "texthl", p - last) == 0 + || STRNCMP(last, "linehl", p - last) == 0 + || STRNCMP(last, "numhl", p - last) == 0) { + xp->xp_context = EXPAND_HIGHLIGHT; + } else if (STRNCMP(last, "icon", p - last) == 0) { + xp->xp_context = EXPAND_FILES; + } else { + xp->xp_context = EXPAND_NOTHING; + } + break; + case SIGNCMD_PLACE: + if (STRNCMP(last, "name", p - last) == 0) + expand_what = EXP_SIGN_NAMES; + else + xp->xp_context = EXPAND_NOTHING; + break; + default: + xp->xp_context = EXPAND_NOTHING; + } + } +} + diff --git a/src/nvim/sign.h b/src/nvim/sign.h new file mode 100644 index 0000000000..6b1ee7e30e --- /dev/null +++ b/src/nvim/sign.h @@ -0,0 +1,10 @@ +#ifndef NVIM_SIGN_H +#define NVIM_SIGN_H + +#include + + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "sign.h.generated.h" +#endif +#endif // NVIM_SIGN_H diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 4460c13ac6..418756abc4 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -38,6 +38,7 @@ #include "nvim/macros.h" #include "nvim/regexp.h" #include "nvim/screen.h" +#include "nvim/sign.h" #include "nvim/strings.h" #include "nvim/syntax_defs.h" #include "nvim/terminal.h" -- cgit From 54c4567564b788f211b55c0bdcee3f2cd3d982e8 Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Sun, 19 May 2019 18:38:25 +0200 Subject: vim-patch:8.1.0679: sign functions do not take buffer argument as documented Problem: Sign functions do not take buffer argument as documented. Solution: Use get_buf_tv(). (Yegappan Lakshmanan, closes vim/vim#3755) https://github.com/vim/vim/commit/2cbc1a02cb72916dfdbd0d307512c7c3fb766edf --- src/nvim/eval.c | 6 ++-- src/nvim/testdir/test_signs.vim | 73 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 69 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 076dbcf487..6d32382497 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -15507,7 +15507,7 @@ f_sign_getplaced(typval_T *argvars, typval_T *rettv) if (argvars[0].v_type != VAR_UNKNOWN) { // get signs placed in this buffer - buf = find_buffer(&argvars[0]); + buf = tv_get_buf(&argvars[0], FALSE); if (buf == NULL) { EMSG2(_("E158: Invalid buffer name: %s"), @@ -15599,7 +15599,7 @@ f_sign_place(typval_T *argvars, typval_T *rettv) goto cleanup; // Buffer to place the sign - buf = find_buffer(&argvars[3]); + buf = tv_get_buf(&argvars[3], FALSE); if (buf == NULL) { EMSG2(_("E158: Invalid buffer name: %s"), tv_get_string(&argvars[2])); @@ -15708,7 +15708,7 @@ f_sign_unplace(typval_T *argvars, typval_T *rettv) if ((di = tv_dict_find(dict, (char_u *)"buffer", -1)) != NULL) { - buf = find_buffer(&di->di_tv); + buf = tv_get_buf(&di->di_tv, FALSE); if (buf == NULL) { EMSG2(_("E158: Invalid buffer name: %s"), diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index e3cbd465ce..5666292bf6 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -32,7 +32,7 @@ func Test_sign() " current window if the buffer is displayed there. let bn = bufnr('%') let wn = winnr() - exe 'sign place 41 line=3 name=Sign1 buffer=' . bn + exe 'sign place 41 line=3 name=Sign1 buffer=' . bn 1 bot split exe 'sign jump 41 buffer=' . bufnr('%') @@ -77,7 +77,7 @@ func Test_sign() sign unplace let a=execute('sign place') call assert_equal("\n--- Signs ---\n", a) - + " Try again to unplace sign on current line, it should fail this time. call assert_fails('sign unplace', 'E159:') @@ -386,11 +386,11 @@ func Test_sign_funcs() call assert_equal([{'bufnr' : bufnr(''), 'signs' : \ [{'id' : 10, 'group' : '', 'lnum' : 20, 'name' : 'sign1', \ 'priority' : 10}]}], - \ sign_getplaced('Xsign', {'lnum' : 20})) + \ sign_getplaced('%', {'lnum' : 20})) call assert_equal([{'bufnr' : bufnr(''), 'signs' : \ [{'id' : 10, 'group' : '', 'lnum' : 20, 'name' : 'sign1', \ 'priority' : 10}]}], - \ sign_getplaced('Xsign', {'id' : 10})) + \ sign_getplaced('', {'id' : 10})) " Tests for invalid arguments to sign_place() call assert_fails('call sign_place([], "", "mySign", 1)', 'E745:') @@ -407,7 +407,7 @@ func Test_sign_funcs() \ {"lnum" : 30})', 'E730:') call assert_fails('call sign_place(5, "", "sign1", "abcxyz.xxx", \ {"lnum" : 10})', 'E158:') - call assert_fails('call sign_place(5, "", "sign1", "", {"lnum" : 10})', + call assert_fails('call sign_place(5, "", "sign1", "@", {"lnum" : 10})', \ 'E158:') call assert_fails('call sign_place(5, "", "sign1", [], {"lnum" : 10})', \ 'E158:') @@ -429,7 +429,7 @@ func Test_sign_funcs() \ 'priority' : 10}]}], \ sign_getplaced()) call assert_fails("call sign_getplaced('dummy.sign')", 'E158:') - call assert_fails('call sign_getplaced("")', 'E158:') + call assert_fails('call sign_getplaced("&")', 'E158:') call assert_fails('call sign_getplaced(-1)', 'E158:') call assert_fails('call sign_getplaced("Xsign", [])', 'E715:') call assert_equal([{'bufnr' : bufnr(''), 'signs' : []}], @@ -451,7 +451,7 @@ func Test_sign_funcs() call assert_fails("call sign_unplace('', \ {'id' : 20, 'buffer' : 'buffer.c'})", 'E158:') call assert_fails("call sign_unplace('', - \ {'id' : 20, 'buffer' : ''})", 'E158:') + \ {'id' : 20, 'buffer' : '&'})", 'E158:') call assert_fails("call sign_unplace('g1', \ {'id' : 20, 'buffer' : 200})", 'E158:') call assert_fails("call sign_unplace('g1', 'mySign')", 'E715:') @@ -1186,3 +1186,62 @@ func Test_sign_memfailures() enew | only call delete("Xsign") endfunc + +" Test for auto-adjusting the line number of a placed sign. +func Test_sign_lnum_adjust() + enew! | only! + + sign define sign1 text=#> linehl=Comment + call setline(1, ['A', 'B', 'C', 'D']) + exe 'sign place 5 line=3 name=sign1 buffer=' . bufnr('') + let l = sign_getplaced(bufnr('')) + call assert_equal(3, l[0].signs[0].lnum) + + " Add some lines before the sign and check the sign line number + call append(2, ['AA', 'AB', 'AC']) + let l = sign_getplaced(bufnr('')) + call assert_equal(6, l[0].signs[0].lnum) + + " Delete some lines before the sign and check the sign line number + call deletebufline('%', 1, 2) + let l = sign_getplaced(bufnr('')) + call assert_equal(4, l[0].signs[0].lnum) + + sign unplace * group=* + sign undefine sign1 + enew! +endfunc + +" Test for changing the type of a placed sign +func Test_sign_change_type() + enew! | only! + + sign define sign1 text=#> linehl=Comment + sign define sign2 text=@@ linehl=Comment + + call setline(1, ['A', 'B', 'C', 'D']) + exe 'sign place 4 line=3 name=sign1 buffer=' . bufnr('') + let l = sign_getplaced(bufnr('')) + call assert_equal('sign1', l[0].signs[0].name) + exe 'sign place 4 name=sign2 buffer=' . bufnr('') + let l = sign_getplaced(bufnr('')) + call assert_equal('sign2', l[0].signs[0].name) + call sign_place(4, '', 'sign1', '') + let l = sign_getplaced(bufnr('')) + call assert_equal('sign1', l[0].signs[0].name) + + exe 'sign place 4 group=g1 line=4 name=sign1 buffer=' . bufnr('') + let l = sign_getplaced(bufnr(''), {'group' : 'g1'}) + call assert_equal('sign1', l[0].signs[0].name) + exe 'sign place 4 group=g1 name=sign2 buffer=' . bufnr('') + let l = sign_getplaced(bufnr(''), {'group' : 'g1'}) + call assert_equal('sign2', l[0].signs[0].name) + call sign_place(4, 'g1', 'sign1', '') + let l = sign_getplaced(bufnr(''), {'group' : 'g1'}) + call assert_equal('sign1', l[0].signs[0].name) + + sign unplace * group=* + sign undefine sign1 + sign undefine sign2 + enew! +endfunc -- cgit From e70609cfac9c64bdf221fa902bba788883e97540 Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Sun, 19 May 2019 21:14:30 +0200 Subject: vim-patch:8.1.0697: ":sign place" requires the buffer argument Problem: ":sign place" requires the buffer argument. Solution: Make the argument optional. Also update the help and clean up the sign test. (Yegappan Lakshmanan, closes vim/vim#3767) https://github.com/vim/vim/commit/b328cca2548936c5f68fff683049a929882f5011 --- src/nvim/sign.c | 7 ++- src/nvim/testdir/test_signs.vim | 94 ++++++++++++++++++++--------------------- 2 files changed, 52 insertions(+), 49 deletions(-) (limited to 'src') diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 237d1eb621..fac47e6fce 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -1142,7 +1142,7 @@ static void sign_jump_cmd( char_u *group ) { - if (buf == NULL && sign_name == NULL && group == NULL && id == -1) { + if (sign_name == NULL && group == NULL && id == -1) { EMSG(_(e_argreq)); return; } @@ -1274,6 +1274,11 @@ static int parse_sign_cmd_args( return FAIL; } + // 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 || cmd == SIGNCMD_JUMP)) { + *buf = curwin->w_buffer; + } return OK; } diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index 5666292bf6..b05c97f347 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -14,7 +14,8 @@ func Test_sign() " the icon name when listing signs. sign define Sign1 text=x try - sign define Sign2 text=xy texthl=Title linehl=Error numhl=Number icon=../../pixmaps/stock_vim_find_help.png + sign define Sign2 text=xy texthl=Title linehl=Error + \ icon=../../pixmaps/stock_vim_find_help.png catch /E255:/ " Ignore error: E255: Couldn't read in sign data! " This error can happen when running in the GUI. @@ -23,7 +24,9 @@ func Test_sign() " Test listing signs. let a=execute('sign list') - call assert_match("^\nsign Sign1 text=x \nsign Sign2 icon=../../pixmaps/stock_vim_find_help.png .*text=xy linehl=Error texthl=Title numhl=Number$", a) + call assert_match('^\nsign Sign1 text=x \nsign Sign2 ' . + \ 'icon=../../pixmaps/stock_vim_find_help.png .*text=xy ' . + \ 'linehl=Error texthl=Title$', a) let a=execute('sign list Sign1') call assert_equal("\nsign Sign1 text=x ", a) @@ -63,7 +66,8 @@ func Test_sign() " Check placed signs let a=execute('sign place') - call assert_equal("\n--- Signs ---\nSigns for [NULL]:\n line=3 id=41 name=Sign1 priority=10\n", a) + call assert_equal("\n--- Signs ---\nSigns for [NULL]:\n" . + \ " line=3 id=41 name=Sign1 priority=10\n", a) " Unplace the sign and try jumping to it again should fail. sign unplace 41 @@ -87,12 +91,20 @@ func Test_sign() let a=execute('sign place') call assert_equal("\n--- Signs ---\n", a) + " Place a sign without specifying the filename or buffer + sign place 77 line=9 name=Sign2 + let a=execute('sign place') + call assert_equal("\n--- Signs ---\nSigns for [NULL]:\n" . + \ " line=9 id=77 name=Sign2 priority=10\n", a) + sign unplace * + " Check :jump with file=... edit foo call setline(1, ['A', 'B', 'C', 'D']) try - sign define Sign3 text=y texthl=DoesNotExist linehl=DoesNotExist icon=doesnotexist.xpm + sign define Sign3 text=y texthl=DoesNotExist linehl=DoesNotExist + \ icon=doesnotexist.xpm catch /E255:/ " ignore error: E255: it can happens for guis. endtry @@ -148,38 +160,12 @@ func Test_sign() call assert_equal("\nsign 4 text=#> linehl=Comment", a) exe 'sign place 20 line=3 name=004 buffer=' . bufnr('') let a = execute('sign place') - call assert_equal("\n--- Signs ---\nSigns for foo:\n line=3 id=20 name=4 priority=10\n", a) + call assert_equal("\n--- Signs ---\nSigns for foo:\n" . + \ " line=3 id=20 name=4 priority=10\n", a) exe 'sign unplace 20 buffer=' . bufnr('') sign undefine 004 call assert_fails('sign list 4', 'E155:') - " Error cases - call assert_fails("sign place abc line=3 name=Sign1 buffer=" . - \ bufnr('%'), 'E474:') - call assert_fails("sign unplace abc name=Sign1 buffer=" . - \ bufnr('%'), 'E474:') - call assert_fails("sign place 1abc line=3 name=Sign1 buffer=" . - \ bufnr('%'), 'E474:') - call assert_fails("sign unplace 2abc name=Sign1 buffer=" . - \ bufnr('%'), 'E474:') - call assert_fails("sign unplace 2 *", 'E474:') - call assert_fails("sign place 1 line=3 name=Sign1 buffer=" . - \ bufnr('%') . " a", 'E488:') - call assert_fails("sign place name=Sign1 buffer=" . bufnr('%'), 'E474:') - call assert_fails("sign place line=10 buffer=" . bufnr('%'), 'E474:') - call assert_fails("sign unplace 2 line=10 buffer=" . bufnr('%'), 'E474:') - call assert_fails("sign unplace 2 name=Sign1 buffer=" . bufnr('%'), 'E474:') - call assert_fails("sign place 2 line=3 buffer=" . bufnr('%'), 'E474:') - call assert_fails("sign place 2", 'E474:') - call assert_fails("sign place abc", 'E474:') - call assert_fails("sign place 5 line=3", 'E474:') - call assert_fails("sign place 5 name=Sign1", 'E474:') - call assert_fails("sign place 5 group=g1", 'E474:') - call assert_fails("sign place 5 group=*", 'E474:') - call assert_fails("sign place 5 priority=10", 'E474:') - call assert_fails("sign place 5 line=3 name=Sign1", 'E474:') - call assert_fails("sign place 5 group=g1 line=3 name=Sign1", 'E474:') - " After undefining the sign, we should no longer be able to place it. sign undefine Sign1 sign undefine Sign2 @@ -202,7 +188,8 @@ func Test_sign_undefine_still_placed() " Listing placed sign should show that sign is deleted. let a=execute('sign place') - call assert_equal("\n--- Signs ---\nSigns for foobar:\n line=1 id=41 name=[Deleted] priority=10\n", a) + call assert_equal("\n--- Signs ---\nSigns for foobar:\n" . + \ " line=1 id=41 name=[Deleted] priority=10\n", a) sign unplace 41 let a=execute('sign place') @@ -220,7 +207,8 @@ func Test_sign_completion() call assert_equal('"sign define Sign icon= linehl= numhl= text= texthl=', @:) call feedkeys(":sign define Sign linehl=Spell\\\"\", 'tx') - call assert_equal('"sign define Sign linehl=SpellBad SpellCap SpellLocal SpellRare', @:) + call assert_equal('"sign define Sign linehl=SpellBad SpellCap ' . + \ 'SpellLocal SpellRare', @:) call writefile(['foo'], 'XsignOne') call writefile(['bar'], 'XsignTwo') @@ -273,17 +261,22 @@ func Test_sign_invalid_commands() call assert_fails('sign jump 1 line=100', '474:') call assert_fails('sign define Sign2 text=', 'E239:') " Non-numeric identifier for :sign place - call assert_fails("sign place abc line=3 name=Sign1 buffer=" . bufnr('%'), 'E474:') + call assert_fails("sign place abc line=3 name=Sign1 buffer=" . bufnr(''), + \ 'E474:') " Non-numeric identifier for :sign unplace - call assert_fails("sign unplace abc name=Sign1 buffer=" . bufnr('%'), 'E474:') + call assert_fails("sign unplace abc name=Sign1 buffer=" . bufnr(''), + \ 'E474:') " Number followed by an alphabet as sign identifier for :sign place - call assert_fails("sign place 1abc line=3 name=Sign1 buffer=" . bufnr('%'), 'E474:') + call assert_fails("sign place 1abc line=3 name=Sign1 buffer=" . bufnr(''), + \ 'E474:') " Number followed by an alphabet as sign identifier for :sign unplace - call assert_fails("sign unplace 2abc name=Sign1 buffer=" . bufnr('%'), 'E474:') + call assert_fails("sign unplace 2abc name=Sign1 buffer=" . bufnr(''), + \ 'E474:') " Sign identifier and '*' for :sign unplace call assert_fails("sign unplace 2 *", 'E474:') " Trailing characters after buffer number for :sign place - call assert_fails("sign place 1 line=3 name=Sign1 buffer=" . bufnr('%') . 'xxx', 'E488:') + call assert_fails("sign place 1 line=3 name=Sign1 buffer=" . + \ bufnr('%') . 'xxx', 'E488:') " Trailing characters after buffer number for :sign unplace call assert_fails("sign unplace 1 buffer=" . bufnr('%') . 'xxx', 'E488:') call assert_fails("sign unplace * buffer=" . bufnr('%') . 'xxx', 'E488:') @@ -306,16 +299,11 @@ func Test_sign_invalid_commands() call assert_fails("sign place abc", 'E474:') " Placing a sign with only line number call assert_fails("sign place 5 line=3", 'E474:') - " Placing a sign with only sign name - call assert_fails("sign place 5 name=Sign1", 'E474:') " Placing a sign with only sign group call assert_fails("sign place 5 group=g1", 'E474:') call assert_fails("sign place 5 group=*", 'E474:') " Placing a sign with only sign priority call assert_fails("sign place 5 priority=10", 'E474:') - " Placing a sign without buffer number or file name - call assert_fails("sign place 5 line=3 name=Sign1", 'E474:') - call assert_fails("sign place 5 group=g1 line=3 name=Sign1", 'E474:') sign undefine Sign1 endfunc @@ -624,11 +612,13 @@ func Test_sign_group() " :sign place file={fname} let a = execute('sign place file=Xsign') - call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=10 id=5 name=sign1 priority=10\n", a) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=10 id=5 name=sign1 priority=10\n", a) " :sign place group={group} file={fname} let a = execute('sign place group=g2 file=Xsign') - call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=10 id=5 group=g2 name=sign1 priority=10\n", a) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=10 id=5 group=g2 name=sign1 priority=10\n", a) " :sign place group=* file={fname} let a = execute('sign place group=* file=Xsign') @@ -649,11 +639,13 @@ func Test_sign_group() " :sign place buffer={fname} let a = execute('sign place buffer=' . bnum) - call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=10 id=5 name=sign1 priority=10\n", a) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=10 id=5 name=sign1 priority=10\n", a) " :sign place group={group} buffer={fname} let a = execute('sign place group=g2 buffer=' . bnum) - call assert_equal("\n--- Signs ---\nSigns for Xsign:\n line=12 id=5 group=g2 name=sign1 priority=10\n", a) + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=12 id=5 group=g2 name=sign1 priority=10\n", a) " :sign place group=* buffer={fname} let a = execute('sign place group=* buffer=' . bnum) @@ -690,6 +682,12 @@ func Test_sign_group() sign jump 5 group=g2 file=Xsign call assert_equal(12, line('.')) + " Test for :sign jump command without the filename or buffer + sign jump 5 + call assert_equal(10, line('.')) + sign jump 5 group=g1 + call assert_equal(11, line('.')) + " Error cases call assert_fails("sign place 3 group= name=sign1 buffer=" . bnum, 'E474:') -- cgit From 35fbb4e1ca94acf9d7c6e6be979a1548f1d58bbd Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Mon, 20 May 2019 08:57:49 +0200 Subject: vim-patch:8.1.0701: sign message not translated and inconsistent spacing Problem: Sign message not translated and inconsistent spacing. Solution: Add _() for translation. Add a space. (Ken Takata) Also use MSG_BUF_LEN instead of BUFSIZ. https://github.com/vim/vim/commit/d730c8e2974609034016ca66db09d2ef78784343 --- src/nvim/sign.c | 17 ++++++++------- src/nvim/testdir/test_signs.vim | 46 ++++++++++++++++++++--------------------- 2 files changed, 32 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/nvim/sign.c b/src/nvim/sign.c index fac47e6fce..5d6c3d3326 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -594,8 +594,8 @@ void sign_list_placed(buf_T *rbuf, char_u *sign_group) { buf_T *buf; signlist_T *sign; - char lbuf[BUFSIZ]; - char group[BUFSIZ]; + char lbuf[MSG_BUF_LEN]; + char group[MSG_BUF_LEN]; MSG_PUTS_TITLE(_("\n--- Signs ---")); msg_putchar('\n'); @@ -606,7 +606,7 @@ void sign_list_placed(buf_T *rbuf, char_u *sign_group) } while (buf != NULL && !got_int) { if (buf->b_signlist != NULL) { - vim_snprintf(lbuf, BUFSIZ, _("Signs for %s:"), buf->b_fname); + vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname); MSG_PUTS_ATTR(lbuf, HL_ATTR(HLF_D)); msg_putchar('\n'); } @@ -618,14 +618,15 @@ void sign_list_placed(buf_T *rbuf, char_u *sign_group) continue; } if (sign->group != NULL) { - vim_snprintf(group, BUFSIZ, " group=%s", sign->group->sg_name); + vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"), + sign->group->sg_name); } else { group[0] = '\0'; } - vim_snprintf(lbuf, BUFSIZ, _(" line=%ld id=%d%s name=%s " - "priority=%d"), - (long)sign->lnum, sign->id, group, - sign_typenr2name(sign->typenr), sign->priority); + vim_snprintf(lbuf, MSG_BUF_LEN, + _(" line=%ld id=%d%s name=%s priority=%d"), + (long)sign->lnum, sign->id, group, + sign_typenr2name(sign->typenr), sign->priority); MSG_PUTS(lbuf); msg_putchar('\n'); } diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index b05c97f347..ae16c49474 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -67,7 +67,7 @@ func Test_sign() " Check placed signs let a=execute('sign place') call assert_equal("\n--- Signs ---\nSigns for [NULL]:\n" . - \ " line=3 id=41 name=Sign1 priority=10\n", a) + \ " line=3 id=41 name=Sign1 priority=10\n", a) " Unplace the sign and try jumping to it again should fail. sign unplace 41 @@ -95,7 +95,7 @@ func Test_sign() sign place 77 line=9 name=Sign2 let a=execute('sign place') call assert_equal("\n--- Signs ---\nSigns for [NULL]:\n" . - \ " line=9 id=77 name=Sign2 priority=10\n", a) + \ " line=9 id=77 name=Sign2 priority=10\n", a) sign unplace * " Check :jump with file=... @@ -161,7 +161,7 @@ func Test_sign() exe 'sign place 20 line=3 name=004 buffer=' . bufnr('') let a = execute('sign place') call assert_equal("\n--- Signs ---\nSigns for foo:\n" . - \ " line=3 id=20 name=4 priority=10\n", a) + \ " line=3 id=20 name=4 priority=10\n", a) exe 'sign unplace 20 buffer=' . bufnr('') sign undefine 004 call assert_fails('sign list 4', 'E155:') @@ -189,7 +189,7 @@ func Test_sign_undefine_still_placed() " Listing placed sign should show that sign is deleted. let a=execute('sign place') call assert_equal("\n--- Signs ---\nSigns for foobar:\n" . - \ " line=1 id=41 name=[Deleted] priority=10\n", a) + \ " line=1 id=41 name=[Deleted] priority=10\n", a) sign unplace 41 let a=execute('sign place') @@ -613,19 +613,19 @@ func Test_sign_group() " :sign place file={fname} let a = execute('sign place file=Xsign') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . - \ " line=10 id=5 name=sign1 priority=10\n", a) + \ " line=10 id=5 name=sign1 priority=10\n", a) " :sign place group={group} file={fname} let a = execute('sign place group=g2 file=Xsign') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . - \ " line=10 id=5 group=g2 name=sign1 priority=10\n", a) + \ " line=10 id=5 group=g2 name=sign1 priority=10\n", a) " :sign place group=* file={fname} let a = execute('sign place group=* file=Xsign') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . - \ " line=10 id=5 group=g2 name=sign1 priority=10\n" . - \ " line=10 id=5 group=g1 name=sign1 priority=10\n" . - \ " line=10 id=5 name=sign1 priority=10\n", a) + \ " line=10 id=5 group=g2 name=sign1 priority=10\n" . + \ " line=10 id=5 group=g1 name=sign1 priority=10\n" . + \ " line=10 id=5 name=sign1 priority=10\n", a) " Error case: non-existing group let a = execute('sign place group=xyz file=Xsign') @@ -640,19 +640,19 @@ func Test_sign_group() " :sign place buffer={fname} let a = execute('sign place buffer=' . bnum) call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . - \ " line=10 id=5 name=sign1 priority=10\n", a) + \ " line=10 id=5 name=sign1 priority=10\n", a) " :sign place group={group} buffer={fname} let a = execute('sign place group=g2 buffer=' . bnum) call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . - \ " line=12 id=5 group=g2 name=sign1 priority=10\n", a) + \ " line=12 id=5 group=g2 name=sign1 priority=10\n", a) " :sign place group=* buffer={fname} let a = execute('sign place group=* buffer=' . bnum) call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . - \ " line=10 id=5 name=sign1 priority=10\n" . - \ " line=11 id=5 group=g1 name=sign1 priority=10\n" . - \ " line=12 id=5 group=g2 name=sign1 priority=10\n", a) + \ " line=10 id=5 name=sign1 priority=10\n" . + \ " line=11 id=5 group=g1 name=sign1 priority=10\n" . + \ " line=12 id=5 group=g2 name=sign1 priority=10\n", a) " Error case: non-existing group let a = execute('sign place group=xyz buffer=' . bnum) @@ -661,19 +661,19 @@ func Test_sign_group() " :sign place let a = execute('sign place') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . - \ " line=10 id=5 name=sign1 priority=10\n", a) + \ " line=10 id=5 name=sign1 priority=10\n", a) " :sign place group={group} let a = execute('sign place group=g1') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . - \ " line=11 id=5 group=g1 name=sign1 priority=10\n", a) + \ " line=11 id=5 group=g1 name=sign1 priority=10\n", a) " :sign place group=* let a = execute('sign place group=*') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . - \ " line=10 id=5 name=sign1 priority=10\n" . - \ " line=11 id=5 group=g1 name=sign1 priority=10\n" . - \ " line=12 id=5 group=g2 name=sign1 priority=10\n", a) + \ " line=10 id=5 name=sign1 priority=10\n" . + \ " line=11 id=5 group=g1 name=sign1 priority=10\n" . + \ " line=12 id=5 group=g2 name=sign1 priority=10\n", a) " Test for ':sign jump' command with groups sign jump 5 group=g1 file=Xsign @@ -1128,14 +1128,14 @@ func Test_sign_priority() sign place 5 group=g2 line=10 name=sign1 priority=25 file=Xsign let a = execute('sign place group=*') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . - \ " line=10 id=5 name=sign1 priority=30\n" . - \ " line=10 id=5 group=g2 name=sign1 priority=25\n" . - \ " line=10 id=5 group=g1 name=sign1 priority=20\n", a) + \ " line=10 id=5 name=sign1 priority=30\n" . + \ " line=10 id=5 group=g2 name=sign1 priority=25\n" . + \ " line=10 id=5 group=g1 name=sign1 priority=20\n", a) " Test for :sign place group={group} let a = execute('sign place group=g1') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . - \ " line=10 id=5 group=g1 name=sign1 priority=20\n", a) + \ " line=10 id=5 group=g1 name=sign1 priority=20\n", a) call sign_unplace('*') call sign_undefine() -- cgit From f43900f6865cb9f7b70621b090194448920d66e5 Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Mon, 20 May 2019 11:04:58 +0200 Subject: vim-patch:8.1.0702: ":sign place" only uses the current buffer Problem: ":sign place" only uses the current buffer. Solution: List signs for all buffers when there is no buffer argument. Fix error message for invalid buffer name in sign_place(). (Yegappan Lakshmanan, closes vim/vim#3774) https://github.com/vim/vim/commit/b589f95b38ddd779d7e696abb0ea011dc92ea903 --- src/nvim/eval.c | 2 +- src/nvim/sign.c | 5 ++++- src/nvim/testdir/test_signs.vim | 12 ++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 6d32382497..7298f2e9a8 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -15602,7 +15602,7 @@ f_sign_place(typval_T *argvars, typval_T *rettv) buf = tv_get_buf(&argvars[3], FALSE); if (buf == NULL) { - EMSG2(_("E158: Invalid buffer name: %s"), tv_get_string(&argvars[2])); + EMSG2(_("E158: Invalid buffer name: %s"), tv_get_string(&argvars[3])); goto cleanup; } diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 5d6c3d3326..70d32f687d 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -1203,6 +1203,7 @@ static int parse_sign_cmd_args( char_u *arg1; char_u *name; char_u *filename = NULL; + int lnum_arg = FALSE; // first arg could be placed sign id arg1 = arg; @@ -1221,6 +1222,7 @@ static int parse_sign_cmd_args( arg += 5; *lnum = atoi((char *)arg); arg = skiptowhite(arg); + lnum_arg = TRUE; } else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE) { if (*signid != -1) { EMSG(_(e_invarg)); @@ -1277,7 +1279,8 @@ static int parse_sign_cmd_args( // 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 || cmd == SIGNCMD_JUMP)) { + if (filename == NULL && ((cmd == SIGNCMD_PLACE && lnum_arg) + || cmd == SIGNCMD_JUMP)) { *buf = curwin->w_buffer; } return OK; diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index ae16c49474..96f60a2ac8 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -663,6 +663,18 @@ func Test_sign_group() call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . \ " line=10 id=5 name=sign1 priority=10\n", a) + " Place signs in more than one buffer and list the signs + split foo + set buftype=nofile + sign place 25 line=76 name=sign1 priority=99 file=foo + let a = execute('sign place') + call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . + \ " line=10 id=5 name=sign1 priority=10\n" . + \ "Signs for foo:\n" . + \ " line=76 id=25 name=sign1 priority=99\n", a) + close + bwipe foo + " :sign place group={group} let a = execute('sign place group=g1') call assert_equal("\n--- Signs ---\nSigns for Xsign:\n" . -- cgit From e09f3baed8abc5fb821fb9b6226ad232d8e2f4c7 Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Mon, 20 May 2019 11:19:22 +0200 Subject: vim-patch:8.1.0709: windows are updated for every added/deleted sign Problem: Windows are updated for every added/deleted sign. Solution: Do not call update_debug_sign(). Only redraw when the line with the sign is visible. (idea from neovim vim/vim#9479) https://github.com/vim/vim/commit/27a472c32ed5b5298bca50864570a4a71ec1d204 --- src/nvim/screen.c | 3 ++- src/nvim/sign.c | 35 +++++++++++++++++------------------ 2 files changed, 19 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 31386ebd42..0f29ef09ae 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -221,7 +221,8 @@ void redraw_buf_later(buf_T *buf, int type) void redraw_buf_line_later(buf_T *buf, linenr_T line) { FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_buffer == buf) { + if (wp->w_buffer == buf + && line >= wp->w_topline && line < wp->w_botline) { redrawWinline(wp, line); } } diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 70d32f687d..069f841d63 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -326,8 +326,8 @@ void buf_addsign( prev = NULL; FOR_ALL_SIGNS_IN_BUF(buf, sign) { - if (lnum == sign->lnum && id == sign->id && - sign_in_group(sign, groupname)) { + if (lnum == sign->lnum && id == sign->id + && sign_in_group(sign, groupname)) { // Update an existing sign sign->typenr = typenr; return; @@ -453,9 +453,9 @@ linenr_T buf_delsign( lnum = 0; for (sign = buf->b_signlist; sign != NULL; sign = next) { next = sign->next; - if ((id == 0 || sign->id == id) && - (atlnum == 0 || sign->lnum == atlnum) && - sign_in_group(sign, group)) { + if ((id == 0 || sign->id == id) + && (atlnum == 0 || sign->lnum == atlnum) + && sign_in_group(sign, group)) { *lastp = next; if (next != NULL) { next->prev = sign->prev; @@ -564,7 +564,6 @@ void buf_delete_signs(buf_T *buf, char_u *group) // 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){ - redraw_buf_later(buf, NOT_VALID); changed_cline_bef_curs(); } @@ -1047,16 +1046,16 @@ static void sign_place_cmd( // :sign place // :sign place group={group} // :sign place group=* - if (lnum >= 0 || sign_name != NULL || - (group != NULL && *group == '\0')) { + if (lnum >= 0 || sign_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 (sign_name == NULL || buf == NULL + || (group != NULL && *group == '\0')) { EMSG(_(e_invarg)); return; } @@ -1148,8 +1147,8 @@ static void sign_jump_cmd( return; } - if (buf == NULL || (group != NULL && *group == '\0') || - lnum >= 0 || sign_name != NULL) { + if (buf == NULL || (group != NULL && *group == '\0') + || lnum >= 0 || sign_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)); @@ -1280,8 +1279,8 @@ static int parse_sign_cmd_args( // 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)) { - *buf = curwin->w_buffer; + || cmd == SIGNCMD_JUMP)) { + *buf = curwin->w_buffer; } return OK; } @@ -1477,10 +1476,10 @@ static void sign_get_placed_in_buf( if (!sign_in_group(sign, sign_group)) { continue; } - if ((lnum == 0 && sign_id == 0) || - (sign_id == 0 && lnum == sign->lnum) || - (lnum == 0 && sign_id == sign->id) || - (lnum == sign->lnum && sign_id == sign->id)) { + if ((lnum == 0 && sign_id == 0) + || (sign_id == 0 && lnum == sign->lnum) + || (lnum == 0 && sign_id == sign->id) + || (lnum == sign->lnum && sign_id == sign->id)) { if ((sdict = sign_get_info(sign)) != NULL) { tv_list_append_dict(l, sdict); } -- cgit From fa07cc215d6c7e86e3b4b7a83d856c017a655933 Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Mon, 20 May 2019 13:12:30 +0200 Subject: vim-patch:8.1.0717: there is no function for the ":sign jump" command Problem: There is no function for the ":sign jump" command. Solution: Add the sign_jump() function. (Yegappan Lakshmanan, closes vim/vim#3780) https://github.com/vim/vim/commit/6b7b7190aa9e5c4f51bceaebf9275aa5097cfea1 --- src/nvim/eval.c | 83 ++++++++++++++++++++++++------ src/nvim/eval.lua | 1 + src/nvim/sign.c | 111 ++++++++++++++++++++++------------------ src/nvim/testdir/test_signs.vim | 45 ++++++++++++++++ 4 files changed, 175 insertions(+), 65 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 7298f2e9a8..790f1081b5 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -7203,6 +7203,23 @@ static buf_T *tv_get_buf(typval_T *tv, int curtab_only) return buf; } +/* + * Get the buffer from "arg" and give an error and return NULL if it is not + * valid. + */ + static buf_T * +get_buf_arg(typval_T *arg) +{ + buf_T *buf; + + ++emsg_off; + buf = tv_get_buf(arg, FALSE); + --emsg_off; + if (buf == NULL) + EMSG2(_("E158: Invalid buffer name: %s"), tv_get_string(arg)); + return buf; +} + /* * "bufname(expr)" function */ @@ -15506,14 +15523,10 @@ f_sign_getplaced(typval_T *argvars, typval_T *rettv) if (argvars[0].v_type != VAR_UNKNOWN) { - // get signs placed in this buffer - buf = tv_get_buf(&argvars[0], FALSE); + // get signs placed in the specified buffer + buf = get_buf_arg(&argvars[0]); if (buf == NULL) - { - EMSG2(_("E158: Invalid buffer name: %s"), - tv_get_string(&argvars[0])); return; - } if (argvars[1].v_type != VAR_UNKNOWN) { @@ -15552,6 +15565,53 @@ f_sign_getplaced(typval_T *argvars, typval_T *rettv) sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list); } +/* + * "sign_jump()" function + */ + static void +f_sign_jump(typval_T *argvars, typval_T *rettv) +{ + int sign_id; + char_u *sign_group = NULL; + buf_T *buf; + int notanum = FALSE; + + rettv->vval.v_number = -1; + + // Sign identifer + sign_id = (int)tv_get_number_chk(&argvars[0], ¬anum); + if (notanum) + return; + if (sign_id <= 0) + { + EMSG(_(e_invarg)); + return; + } + + // Sign group + sign_group = tv_get_string_chk(&argvars[1]); + if (sign_group == NULL) + return; + if (sign_group[0] == '\0') + sign_group = NULL; // global sign group + else + { + sign_group = vim_strsave(sign_group); + if (sign_group == NULL) + return; + } + + // Buffer to place the sign + buf = get_buf_arg(&argvars[2]); + if (buf == NULL) + goto cleanup; + + rettv->vval.v_number = sign_jump(sign_id, sign_group, buf); + +cleanup: + xfree(sign_group); +} + /* * "sign_place()" function */ @@ -15599,12 +15659,9 @@ f_sign_place(typval_T *argvars, typval_T *rettv) goto cleanup; // Buffer to place the sign - buf = tv_get_buf(&argvars[3], FALSE); + buf = get_buf_arg(&argvars[3]); if (buf == NULL) - { - EMSG2(_("E158: Invalid buffer name: %s"), tv_get_string(&argvars[3])); goto cleanup; - } if (argvars[4].v_type != VAR_UNKNOWN) { @@ -15708,13 +15765,9 @@ f_sign_unplace(typval_T *argvars, typval_T *rettv) if ((di = tv_dict_find(dict, (char_u *)"buffer", -1)) != NULL) { - buf = tv_get_buf(&di->di_tv, FALSE); + buf = get_buf_arg(&di->di_tv); if (buf == NULL) - { - EMSG2(_("E158: Invalid buffer name: %s"), - tv_get_string(&di->di_tv)); goto cleanup; - } } if (tv_dict_find(dict, (char_u *)"id", -1) != NULL) sign_id = tv_dict_get_number(dict, (char_u *)"id"); diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index f583123025..15f392d8f1 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -280,6 +280,7 @@ return { sign_define={args={1, 2}}, sign_getdefined={args={0, 1}}, sign_getplaced={args={0, 2}}, + sign_jump={args={3, 3}}, sign_place={args={4, 5}}, sign_undefine={args={0, 1}}, sign_unplace={args={1, 2}}, diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 069f841d63..24ebd12e6a 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -36,7 +36,7 @@ struct sign char_u *sn_name; // name of sign char_u *sn_icon; // name of pixmap # ifdef FEAT_SIGN_ICONS - void *sn_image; /* icon image */ + void *sn_image; // icon image # endif char_u *sn_text; // text used instead of pixmap int sn_line_hl; // highlight ID for line @@ -670,8 +670,8 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a * "*end_cmd" must be writable. */ static int sign_cmd_idx( - char_u *begin_cmd, /* begin of sign subcmd */ - char_u *end_cmd /* just after sign subcmd */ + char_u *begin_cmd, // begin of sign subcmd + char_u *end_cmd // just after sign subcmd ) { int idx; @@ -968,8 +968,43 @@ static void sign_unplace_at_cursor(char_u *groupname) } /* - * sign define command - * ":sign define {name} ..." + * Jump to a sign. + */ +linenr_T sign_jump(int sign_id, char_u *sign_group, buf_T *buf) +{ + linenr_T lnum; + + if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0) { + EMSGN(_("E157: Invalid sign ID: %ld"), sign_id); + return -1; + } + + // goto a sign ... + if (buf_jump_open_win(buf) != NULL) + { // ... in a current window + curwin->w_cursor.lnum = lnum; + check_cursor_lnum(); + beginline(BL_WHITE); + } else { // ... not currently in a window + if (buf->b_fname == NULL) { + EMSG(_("E934: Cannot jump to a buffer that does not have a name")); + return -1; + } + size_t cmdlen = STRLEN(buf->b_fname) + 24; + char *cmd = xmallocz(cmdlen); + snprintf(cmd, cmdlen, "e +%" PRId64 " %s", + (int64_t)lnum, buf->b_fname); + do_cmdline_cmd(cmd); + xfree(cmd); + } + + foldOpenCursor(); + + return lnum; +} + +/* + * ":sign define {name} ..." command */ static void sign_define_cmd(char_u *sign_name, char_u *cmdline) { @@ -1023,7 +1058,7 @@ static void sign_define_cmd(char_u *sign_name, char_u *cmdline) } /* - * :sign place command + * ":sign place" command */ static void sign_place_cmd( buf_T *buf, @@ -1065,7 +1100,7 @@ static void sign_place_cmd( } /* - * :sign unplace command + * ":sign unplace" command */ static void sign_unplace_cmd( buf_T *buf, @@ -1128,7 +1163,7 @@ static void sign_unplace_cmd( } /* - * Jump to a placed sign + * Jump to a placed sign commands: * :sign jump {id} file={fname} * :sign jump {id} buffer={nr} * :sign jump {id} group={group} file={fname} @@ -1155,31 +1190,7 @@ static void sign_jump_cmd( return; } - if ((lnum = buf_findsign(buf, id, group)) <= 0) { - EMSGN(_("E157: Invalid sign ID: %ld"), id); - return; - } - - // goto a sign ... - if (buf_jump_open_win(buf) != NULL) - { // ... in a current window - curwin->w_cursor.lnum = lnum; - check_cursor_lnum(); - beginline(BL_WHITE); - } else { // ... not currently in a window - if (buf->b_fname == NULL) { - EMSG(_("E934: Cannot jump to a buffer that does not have a name")); - return; - } - size_t cmdlen = STRLEN(buf->b_fname) + 24; - char *cmd = xmallocz(cmdlen); - snprintf(cmd, cmdlen, "e +%" PRId64 " %s", - (int64_t)lnum, buf->b_fname); - do_cmdline_cmd(cmd); - xfree(cmd); - } - - foldOpenCursor(); + (void)sign_jump(id, group, buf); } /* @@ -1635,7 +1646,7 @@ char_u * sign_get_text(int typenr) # if defined(FEAT_SIGN_ICONS) || defined(PROTO) void * sign_get_image( - int typenr) /* the attribute which may have a sign */ + int typenr) // the attribute which may have a sign { sign_T *sp; @@ -1658,11 +1669,11 @@ void free_signs(void) static enum { - EXP_SUBCMD, /* expand :sign sub-commands */ - EXP_DEFINE, /* expand :sign define {name} args */ - EXP_PLACE, /* expand :sign place {id} args */ - EXP_UNPLACE, /* expand :sign unplace" */ - EXP_SIGN_NAMES /* expand with name of placed signs */ + EXP_SUBCMD, // expand :sign sub-commands + EXP_DEFINE, // expand :sign define {name} args + EXP_PLACE, // expand :sign place {id} args + EXP_UNPLACE, // expand :sign unplace" + EXP_SIGN_NAMES // expand with name of placed signs } expand_what; /// Function given to ExpandGeneric() to obtain the sign command @@ -1713,15 +1724,15 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) int cmd_idx; char_u *begin_subcmd_args; - /* Default: expand subcommands. */ + // Default: expand subcommands. xp->xp_context = EXPAND_SIGN; expand_what = EXP_SUBCMD; xp->xp_pattern = arg; end_subcmd = skiptowhite(arg); if (*end_subcmd == NUL) - /* expand subcmd name - * :sign {subcmd}*/ + // expand subcmd name + // :sign {subcmd} return; cmd_idx = sign_cmd_idx(arg, end_subcmd); @@ -1733,18 +1744,18 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) p = skiptowhite(begin_subcmd_args); if (*p == NUL) { - /* - * Expand first argument of subcmd when possible. - * For ":jump {id}" and ":unplace {id}", we could - * possibly expand the ids of all signs already placed. - */ + // + // Expand first argument of subcmd when possible. + // For ":jump {id}" and ":unplace {id}", we could + // possibly expand the ids of all signs already placed. + // xp->xp_pattern = begin_subcmd_args; switch (cmd_idx) { case SIGNCMD_LIST: case SIGNCMD_UNDEFINE: - /* :sign list - * :sign undefine */ + // :sign list + // :sign undefine expand_what = EXP_SIGN_NAMES; break; default: @@ -1793,7 +1804,7 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) } else { - /* Expand last argument value (after equal sign). */ + // Expand last argument value (after equal sign). xp->xp_pattern = p + 1; switch (cmd_idx) { diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index 96f60a2ac8..3d6990f136 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -1255,3 +1255,48 @@ func Test_sign_change_type() sign undefine sign2 enew! endfunc + +" Test for the sign_jump() function +func Test_sign_jump_func() + enew! | only! + + sign define sign1 text=#> linehl=Comment + + edit foo + set buftype=nofile + call setline(1, ['A', 'B', 'C', 'D', 'E']) + call sign_place(5, '', 'sign1', '', {'lnum' : 2}) + call sign_place(5, 'g1', 'sign1', '', {'lnum' : 3}) + call sign_place(6, '', 'sign1', '', {'lnum' : 4}) + call sign_place(6, 'g1', 'sign1', '', {'lnum' : 5}) + split bar + set buftype=nofile + call setline(1, ['P', 'Q', 'R', 'S', 'T']) + call sign_place(5, '', 'sign1', '', {'lnum' : 2}) + call sign_place(5, 'g1', 'sign1', '', {'lnum' : 3}) + call sign_place(6, '', 'sign1', '', {'lnum' : 4}) + call sign_place(6, 'g1', 'sign1', '', {'lnum' : 5}) + + let r = sign_jump(5, '', 'foo') + call assert_equal(2, r) + call assert_equal(2, line('.')) + let r = sign_jump(6, 'g1', 'foo') + call assert_equal(5, r) + call assert_equal(5, line('.')) + let r = sign_jump(5, '', 'bar') + call assert_equal(2, r) + call assert_equal(2, line('.')) + + " Error cases + call assert_fails("call sign_jump(99, '', 'bar')", 'E157:') + call assert_fails("call sign_jump(0, '', 'foo')", 'E474:') + call assert_fails("call sign_jump(5, 'g5', 'foo')", 'E157:') + call assert_fails('call sign_jump([], "", "foo")', 'E745:') + call assert_fails('call sign_jump(2, [], "foo")', 'E730:') + call assert_fails('call sign_jump(2, "", {})', 'E158:') + call assert_fails('call sign_jump(2, "", "baz")', 'E158:') + + sign unplace * group=* + sign undefine sign1 + enew! | only! +endfunc -- cgit From c5f3dbab3549bdf6765aaf9d123cedbf8de46e34 Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Mon, 20 May 2019 17:29:22 +0200 Subject: vim-patch:8.1.0750: when the last sign is deleted the signcolumn may remain Problem: When the last sign is deleted the signcolumn may not be removed even though 'signcolumn' is "auto". Solution: When deleting the last sign redraw the buffer. (Dominique Pelle, closes vim/vim#3803, closes vim/vim#3804) https://github.com/vim/vim/commit/8144acbec33b751788a7912e2d880c083c6cfe93 --- src/nvim/sign.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 24ebd12e6a..9750101ac4 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -378,6 +378,9 @@ linenr_T buf_change_sign_type( /// Gets a sign from a given line. /// +/// Return the type number of the sign at line number 'lnum' in buffer 'buf' +/// which has the attribute specified by 'type'. Returns 0 if a sign is not +/// found at the line number or it doesn't have the specified attribute. /// @param buf Buffer in which to search /// @param lnum Line in which to search /// @param type Type of sign to look for @@ -466,7 +469,7 @@ linenr_T buf_delsign( xfree(sign); redraw_buf_line_later(buf, lnum); // Check whether only one sign needs to be deleted - // If deleting a sign with a specific identifer in a particular + // 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 @@ -479,8 +482,8 @@ linenr_T buf_delsign( } } - /* When deleted the last sign needs to redraw the windows to remove the - * sign column. */ + // When deleted the last sign needs to redraw the windows to remove the + // sign column. if (buf->b_signlist == NULL) { redraw_buf_later(buf, NOT_VALID); changed_cline_bef_curs(); @@ -873,7 +876,7 @@ sign_list_by_name(char_u *name) /* - * Place a sign at the specifed file location or update a sign. + * Place a sign at the specified file location or update a sign. */ int sign_place( int *sign_id, -- cgit From fb4cf05e446baf2aa13f0499f4c2a1999ed2d153 Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Mon, 20 May 2019 20:04:47 +0200 Subject: vim-patch:8.1.0767: when deleting lines at the bottom signs are misplaced Problem: When deleting lines at the bottom signs are misplaced. Solution: Properly update the line number of signs at the end of a buffer after a delete/undo operation. (Yegappan Lakshmanan, closes vim/vim#3798) https://github.com/vim/vim/commit/c771bf901622064dc27421b04853e16b6914a295 --- src/nvim/sign.c | 12 ++++++++++-- src/nvim/testdir/test_signs.vim | 42 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 9750101ac4..7c55742d4d 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -647,23 +647,31 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a signlist_T *sign; // a sign in a b_signlist signlist_T *next; // the next sign in a b_signlist signlist_T **lastp; // pointer to pointer to current sign + linenr_T new_lnum; // new line number to assign to sign curbuf->b_signcols_max = -1; lastp = &curbuf->b_signlist; FOR_ALL_SIGNS_IN_BUF(curbuf, sign) { next = sign->next; + new_lnum = sign->lnum; if (sign->lnum >= line1 && sign->lnum <= line2) { if (amount == MAXLNUM) { *lastp = next; xfree(sign); continue; } else { - sign->lnum += amount; + new_lnum += amount; } } else if (sign->lnum > line2) { - sign->lnum += amount_after; + new_lnum += amount_after; } + // 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 (sign->lnum >= line1 && new_lnum <= curbuf->b_ml.ml_line_count) { + sign->lnum = new_lnum; + } lastp = &sign->next; } } diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index 3d6990f136..3edb4b4ae0 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -1202,13 +1202,13 @@ func Test_sign_lnum_adjust() enew! | only! sign define sign1 text=#> linehl=Comment - call setline(1, ['A', 'B', 'C', 'D']) + call setline(1, ['A', 'B', 'C', 'D', 'E']) exe 'sign place 5 line=3 name=sign1 buffer=' . bufnr('') let l = sign_getplaced(bufnr('')) call assert_equal(3, l[0].signs[0].lnum) " Add some lines before the sign and check the sign line number - call append(2, ['AA', 'AB', 'AC']) + call append(2, ['BA', 'BB', 'BC']) let l = sign_getplaced(bufnr('')) call assert_equal(6, l[0].signs[0].lnum) @@ -1217,6 +1217,44 @@ func Test_sign_lnum_adjust() let l = sign_getplaced(bufnr('')) call assert_equal(4, l[0].signs[0].lnum) + " Insert some lines after the sign and check the sign line number + call append(5, ['DA', 'DB']) + let l = sign_getplaced(bufnr('')) + call assert_equal(4, l[0].signs[0].lnum) + + " Delete some lines after the sign and check the sign line number + call deletebufline('', 6, 7) + let l = sign_getplaced(bufnr('')) + call assert_equal(4, l[0].signs[0].lnum) + + " Break the undo. Otherwise the undo operation below will undo all the + " changes made by this function. + let &undolevels=&undolevels + + " 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) + + " Break the undo + let &undolevels=&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) + + " 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) + sign unplace * group=* sign undefine sign1 enew! -- cgit From 93e18e698e4d0251bec6e47c7f2ad0ca8d47c849 Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Tue, 21 May 2019 20:20:55 +0200 Subject: vim-patch:8.1.0772: the sign_define_by_name() function is too long Problem: The sign_define_by_name() function is too long. Solution: Split it into smaller functions. (Yegappan Lakshmanan, closes vim/vim#3819) https://github.com/vim/vim/commit/0314236aabcb2ca9d0b74074dadecf68d7c7ed5f --- src/nvim/sign.c | 196 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 115 insertions(+), 81 deletions(-) (limited to 'src') diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 7c55742d4d..35605f8d23 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -719,6 +719,115 @@ sign_find(char_u *name, sign_T **sp_prev) return sp; } +/* + * Allocate a new sign + */ + static sign_T * +alloc_new_sign(char_u *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 = vim_strsave(name); + + return sp; +} + +/* + * Initialize the icon information for a new sign + */ + static void +sign_define_init_icon(sign_T *sp, char_u *icon) +{ + xfree(sp->sn_icon); + sp->sn_icon = vim_strsave(icon); + backslash_halve(sp->sn_icon); +# ifdef FEAT_SIGN_ICONS + if (gui.in_use) + { + out_flush(); + if (sp->sn_image != NULL) + gui_mch_destroy_sign(sp->sn_image); + sp->sn_image = gui_mch_register_sign(sp->sn_icon); + } +# endif +} + +/* + * Initialize the text for a new sign + */ + static int +sign_define_init_text(sign_T *sp, char_u *text) +{ + char_u *s; + char_u *endp; + int cells; + int len; + + endp = text + (int)STRLEN(text); + for (s = text; s + 1 < endp; ++s) { + if (*s == '\\') + { + // 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; + for (s = text; s < endp; s += (*mb_ptr2len)(s)) { + if (!vim_isprintc(utf_ptr2char(s))) { + break; + } + cells += utf_ptr2cells(s); + } + // Currently must be one or two display cells + if (s != endp || cells < 1 || cells > 2) { + EMSG2(_("E239: Invalid sign text: %s"), text); + return FAIL; + } + + xfree(sp->sn_text); + // Allocate one byte more if we need to pad up + // with a space. + len = (int)(endp - text + ((cells == 1) ? 1 : 0)); + sp->sn_text = vim_strnsave(text, len); + + if (cells == 1) + STRCPY(sp->sn_text + len - 1, " "); + + return OK; +} + /* * Define a new sign or update an existing sign */ @@ -737,38 +846,11 @@ int sign_define_by_name( sp = sign_find(name, &sp_prev); if (sp == NULL) { - 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 FAIL; - } - lp = first_sign; // start all over - continue; - } - lp = lp->sn_next; + sp = alloc_new_sign(name); + if (sp == NULL) { + return FAIL; } - sp->sn_typenr = next_sign_typenr; - if (++next_sign_typenr == MAX_TYPENR) - next_sign_typenr = 1; // wrap around - - sp->sn_name = vim_strsave(name); - // add the new sign to the list of signs if (sp_prev == NULL) { first_sign = sp; @@ -780,59 +862,11 @@ int sign_define_by_name( // set values for a defined sign. if (icon != NULL) { - xfree(sp->sn_icon); - sp->sn_icon = vim_strsave(icon); - backslash_halve(sp->sn_icon); -# ifdef FEAT_SIGN_ICONS - if (gui.in_use) - { - out_flush(); - if (sp->sn_image != NULL) - gui_mch_destroy_sign(sp->sn_image); - sp->sn_image = gui_mch_register_sign(sp->sn_icon); - } -# endif + sign_define_init_icon(sp, icon); } - if (text != NULL) - { - char_u *s; - char_u *endp; - int cells; - int len; - - endp = text + (int)STRLEN(text); - for (s = text; s + 1 < endp; ++s) { - if (*s == '\\') - { - // 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; - for (s = text; s < endp; s += (*mb_ptr2len)(s)) { - if (!vim_isprintc(utf_ptr2char(s))) { - break; - } - cells += utf_ptr2cells(s); - } - // Currently must be one or two display cells - if (s != endp || cells < 1 || cells > 2) { - EMSG2(_("E239: Invalid sign text: %s"), text); - return FAIL; - } - - xfree(sp->sn_text); - // Allocate one byte more if we need to pad up - // with a space. - len = (int)(endp - text + ((cells == 1) ? 1 : 0)); - sp->sn_text = vim_strnsave(text, len); - - if (cells == 1) - STRCPY(sp->sn_text + len - 1, " "); + if (text != NULL && (sign_define_init_text(sp, text) == FAIL)) { + return FAIL; } if (linehl != NULL) -- cgit From f2341164c661b007cf4fa3fa6605beb4579c5e8f Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Wed, 22 May 2019 09:14:10 +0200 Subject: Changed sign_mark_adjust behaviour to match vim Vim does not delete/free signs if they are placed on lines which get deleted. In case of undo it does make a difference as in vim the sign will be still available. --- src/nvim/sign.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'src') diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 35605f8d23..b2f235d0be 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -656,11 +656,7 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a next = sign->next; new_lnum = sign->lnum; if (sign->lnum >= line1 && sign->lnum <= line2) { - if (amount == MAXLNUM) { - *lastp = next; - xfree(sign); - continue; - } else { + if (amount != MAXLNUM) { new_lnum += amount; } } else if (sign->lnum > line2) { -- cgit From 7d43943e4ecf5bd82ffc041b1039e0b2db4d6d60 Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Wed, 22 May 2019 22:41:05 +0200 Subject: Fixed ordering of signs to align vim and neovim behaviour --- src/nvim/buffer.c | 12 +++++++++--- src/nvim/sign.c | 34 +++++++++++++++------------------- 2 files changed, 24 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 1f4a1e0cd1..39242b0575 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -5261,7 +5261,7 @@ static int sign_compare(const void *a1, const void *a2) const signlist_T *s1 = *(const signlist_T **)a1; const signlist_T *s2 = *(const signlist_T **)a2; - // Sort by line number and the by id + // Sort by line number, priority and id if (s1->lnum > s2->lnum) { return 1; @@ -5269,12 +5269,18 @@ static int sign_compare(const void *a1, const void *a2) if (s1->lnum < s2->lnum) { return -1; } - if (s1->id > s2->id) { + if (s1->priority > s2->priority) { + return -1; + } + if (s1->priority < s2->priority) { return 1; } - if (s1->id < s2->id) { + if (s1->id > s2->id) { return -1; } + if (s1->id < s2->id) { + return 1; + } return 0; } diff --git a/src/nvim/sign.c b/src/nvim/sign.c index b2f235d0be..dc4c322a19 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -212,18 +212,18 @@ static void insert_sign( newsign->id = id; newsign->lnum = lnum; newsign->typenr = typenr; - if (group != NULL) + if (group != NULL) + { + newsign->group = sign_group_ref(group); + if (newsign->group == NULL) { - newsign->group = sign_group_ref(group); - if (newsign->group == NULL) - { - xfree(newsign); - return; - } + xfree(newsign); + return; } - else - newsign->group = NULL; - newsign->priority = prio; + } + else + newsign->group = NULL; + newsign->priority = prio; newsign->next = next; newsign->prev = prev; if (next != NULL) { @@ -331,8 +331,7 @@ void buf_addsign( // Update an existing sign sign->typenr = typenr; return; - } else if ((lnum == sign->lnum && id != sign->id) - || (id < 0 && lnum < sign->lnum)) { + } else if (lnum < sign->lnum) { insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr); return; } @@ -407,23 +406,20 @@ int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type, && sign_get_attr(sign->typenr, SIGN_NUMHL) != 0))) { matches[nr_matches] = sign; nr_matches++; - - if (nr_matches == ARRAY_SIZE(matches)) { + // signlist is sorted with most important (priority, id), thus we + // may stop as soon as we have max_signs matches + if (nr_matches == ARRAY_SIZE(matches) || nr_matches >= max_signs) { break; } } } if (nr_matches > 0) { - if (nr_matches > max_signs) { - idx += nr_matches - max_signs; - } - if (idx >= nr_matches) { return 0; } - return matches[idx]->typenr; + return matches[nr_matches - idx -1]->typenr; } return 0; -- cgit From 51563b70d7689321411b9393bb0c85d3f2cdecdd Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Wed, 22 May 2019 23:27:46 +0200 Subject: Allow multiple signs of same type in one line (matching vim behaviour) This partly rolls back 36762a00a8010c5e14ad4347ab8287d1e8e7e064, but it matches vim behaviour (covered with recent tests - oldtest). --- src/nvim/sign.c | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'src') diff --git a/src/nvim/sign.c b/src/nvim/sign.c index dc4c322a19..01f6c275af 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -339,19 +339,6 @@ void buf_addsign( } insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr); - - // Having more than one sign with _the same type_ and on the _same line_ is - // unwanted, let's prevent it. - - lastp = &buf->b_signlist; - for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { - if (lnum == sign->lnum && sign->typenr == typenr && id != sign->id) { - *lastp = sign->next; - xfree(sign); - } else { - lastp = &sign->next; - } - } } // For an existing, placed sign "markId" change the type to "typenr". -- cgit From 8df9213d1b6ed804fb883073e012b82d26080963 Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Fri, 24 May 2019 22:10:19 +0200 Subject: Resolved compile warnings & fixed lot of style related to sign api --- src/nvim/eval.c | 474 +++++++++++++++++++++++++++--------------------------- src/nvim/main.c | 1 + src/nvim/mark.c | 1 + src/nvim/memory.c | 1 + src/nvim/sign.c | 124 +++++++------- 5 files changed, 301 insertions(+), 300 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 790f1081b5..f3c2e34d80 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -15441,16 +15441,15 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) /* * "sign_define()" function */ - static void -f_sign_define(typval_T *argvars, typval_T *rettv) +static void f_sign_define(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *name; + const char *name; dict_T *dict; - char_u *icon = NULL; - char_u *linehl = NULL; - char_u *text = NULL; - char_u *texthl = NULL; - char_u *numhl = NULL; + char *icon = NULL; + char *linehl = NULL; + char *text = NULL; + char *texthl = NULL; + char *numhl = NULL; rettv->vval.v_number = -1; @@ -15468,21 +15467,24 @@ f_sign_define(typval_T *argvars, typval_T *rettv) // sign attributes dict = argvars[1].vval.v_dict; - if (tv_dict_find(dict, (char_u *)"icon", -1) != NULL) - icon = tv_dict_get_string(dict, (char_u *)"icon", TRUE); - if (tv_dict_find(dict, (char_u *)"linehl", -1) != NULL) - linehl = tv_dict_get_string(dict, (char_u *)"linehl", TRUE); - if (tv_dict_find(dict, (char_u *)"text", -1) != NULL) - text = tv_dict_get_string(dict, (char_u *)"text", TRUE); - if (tv_dict_find(dict, (char_u *)"texthl", -1) != NULL) - texthl = tv_dict_get_string(dict, (char_u *)"texthl", TRUE); - if (tv_dict_find(dict, (char_u *)"numhl", -1) != NULL) - numhl = tv_dict_get_string(dict, (char_u *)"numhl", TRUE); + if (tv_dict_find(dict, "icon", -1) != NULL) + icon = tv_dict_get_string(dict, "icon", TRUE); + if (tv_dict_find(dict, "linehl", -1) != NULL) + linehl = tv_dict_get_string(dict, "linehl", TRUE); + if (tv_dict_find(dict, "text", -1) != NULL) + text = tv_dict_get_string(dict, "text", TRUE); + if (tv_dict_find(dict, "texthl", -1) != NULL) + texthl = tv_dict_get_string(dict, "texthl", TRUE); + if (tv_dict_find(dict, "numhl", -1) != NULL) + numhl = tv_dict_get_string(dict, "numhl", TRUE); + } + + if (sign_define_by_name((char_u *)name, (char_u *)icon, (char_u *)linehl, + (char_u *)text, (char_u *)texthl, (char_u *)numhl) + == OK) { + rettv->vval.v_number = 0; } - if (sign_define_by_name(name, icon, linehl, text, texthl, numhl) == OK) - rettv->vval.v_number = 0; - xfree(icon); xfree(linehl); xfree(text); @@ -15493,301 +15495,303 @@ f_sign_define(typval_T *argvars, typval_T *rettv) * "sign_getdefined()" function */ static void -f_sign_getdefined(typval_T *argvars, typval_T *rettv) +f_sign_getdefined(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *name = NULL; + const char *name = NULL; tv_list_alloc_ret(rettv, 0); if (argvars[0].v_type != VAR_UNKNOWN) - name = tv_get_string(&argvars[0]); + name = tv_get_string(&argvars[0]); - sign_getlist(name, rettv->vval.v_list); + sign_getlist((const char_u *)name, rettv->vval.v_list); } /* * "sign_getplaced()" function */ - static void -f_sign_getplaced(typval_T *argvars, typval_T *rettv) +static void f_sign_getplaced(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - buf_T *buf = NULL; - dict_T *dict; - dictitem_T *di; - linenr_T lnum = 0; - int sign_id = 0; - char_u *group = NULL; - int notanum = FALSE; + buf_T *buf = NULL; + dict_T *dict; + dictitem_T *di; + linenr_T lnum = 0; + int sign_id = 0; + const char *group = NULL; + bool notanum = FALSE; - tv_list_alloc_ret(rettv, 0); + tv_list_alloc_ret(rettv, 0); - if (argvars[0].v_type != VAR_UNKNOWN) - { - // get signs placed in the specified buffer - buf = get_buf_arg(&argvars[0]); - if (buf == NULL) - return; + if (argvars[0].v_type != VAR_UNKNOWN) + { + // get signs placed in the specified buffer + buf = get_buf_arg(&argvars[0]); + if (buf == NULL) { + return; + } - if (argvars[1].v_type != VAR_UNKNOWN) - { - if (argvars[1].v_type != VAR_DICT || - ((dict = argvars[1].vval.v_dict) == NULL)) - { - EMSG(_(e_dictreq)); - return; - } - if ((di = tv_dict_find(dict, (char_u *)"lnum", -1)) != NULL) - { - // get signs placed at this line - (void)tv_get_number_chk(&di->di_tv, ¬anum); - if (notanum) - return; - lnum = tv_get_lnum(&di->di_tv); - } - if ((di = tv_dict_find(dict, (char_u *)"id", -1)) != NULL) - { - // get sign placed with this identifier - sign_id = (int)tv_get_number_chk(&di->di_tv, ¬anum); - if (notanum) - return; - } - if ((di = tv_dict_find(dict, (char_u *)"group", -1)) != NULL) - { - group = tv_get_string_chk(&di->di_tv); - if (group == NULL) - return; - if (*group == '\0') // empty string means global group - group = NULL; - } - } + if (argvars[1].v_type != VAR_UNKNOWN) { + if (argvars[1].v_type != VAR_DICT || + ((dict = argvars[1].vval.v_dict) == NULL)) { + EMSG(_(e_dictreq)); + return; + } + 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, ¬anum); + if (notanum) { + return; + } + lnum = tv_get_lnum(&di->di_tv); + } + if ((di = tv_dict_find(dict, "id", -1)) != NULL) { + // get sign placed with this identifier + sign_id = (int)tv_get_number_chk(&di->di_tv, ¬anum); + if (notanum) { + return; + } + } + if ((di = tv_dict_find(dict, "group", -1)) != NULL) + { + group = tv_get_string_chk(&di->di_tv); + if (group == NULL) + return; + if (*group == '\0') // empty string means global group + group = NULL; + } } + } - sign_get_placed(buf, lnum, sign_id, group, rettv->vval.v_list); + sign_get_placed(buf, lnum, sign_id, (const char_u*)group, rettv->vval.v_list); } /* * "sign_jump()" function */ static void -f_sign_jump(typval_T *argvars, typval_T *rettv) +f_sign_jump(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int sign_id; - char_u *sign_group = NULL; - buf_T *buf; - int notanum = FALSE; + int sign_id; + char *sign_group = NULL; + buf_T *buf; + bool notanum = FALSE; - rettv->vval.v_number = -1; + rettv->vval.v_number = -1; - // Sign identifer - sign_id = (int)tv_get_number_chk(&argvars[0], ¬anum); - if (notanum) - return; - if (sign_id <= 0) - { - EMSG(_(e_invarg)); - return; - } + // Sign identifer + sign_id = (int)tv_get_number_chk(&argvars[0], ¬anum); + if (notanum) { + return; + } + if (sign_id <= 0) + { + EMSG(_(e_invarg)); + return; + } - // Sign group - sign_group = tv_get_string_chk(&argvars[1]); - if (sign_group == NULL) - return; - if (sign_group[0] == '\0') - sign_group = NULL; // global sign group - else - { - sign_group = vim_strsave(sign_group); - if (sign_group == NULL) - return; + // Sign group + const char* sign_group_chk = tv_get_string_chk(&argvars[1]); + if (sign_group_chk == NULL) + return; + if (sign_group_chk[0] == '\0') { + sign_group = NULL; // global sign group + } else { + sign_group = xstrdup(sign_group_chk); + if (sign_group == NULL) { + return; } + } - // Buffer to place the sign - buf = get_buf_arg(&argvars[2]); - if (buf == NULL) - goto cleanup; + // Buffer to place the sign + buf = get_buf_arg(&argvars[2]); + if (buf == NULL) + goto cleanup; - rettv->vval.v_number = sign_jump(sign_id, sign_group, buf); + rettv->vval.v_number = sign_jump(sign_id, (char_u*)sign_group, buf); cleanup: - xfree(sign_group); + xfree(sign_group); } /* * "sign_place()" function */ - static void -f_sign_place(typval_T *argvars, typval_T *rettv) +static void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int sign_id; - char_u *group = NULL; - char_u *sign_name; - buf_T *buf; - dict_T *dict; - dictitem_T *di; - linenr_T lnum = 0; - int prio = SIGN_DEF_PRIO; - int notanum = FALSE; + int sign_id; + char_u *group = NULL; + const char *sign_name; + buf_T *buf; + dict_T *dict; + dictitem_T *di; + linenr_T lnum = 0; + int prio = SIGN_DEF_PRIO; + bool notanum = FALSE; - rettv->vval.v_number = -1; + rettv->vval.v_number = -1; - // Sign identifer - sign_id = (int)tv_get_number_chk(&argvars[0], ¬anum); - if (notanum) - return; - if (sign_id < 0) - { - EMSG(_(e_invarg)); - return; - } + // Sign identifer + sign_id = (int)tv_get_number_chk(&argvars[0], ¬anum); + if (notanum) { + return; + } + if (sign_id < 0) { + EMSG(_(e_invarg)); + return; + } - // Sign group - group = tv_get_string_chk(&argvars[1]); - if (group == NULL) - return; - if (group[0] == '\0') - group = NULL; // global sign group - else - { - group = vim_strsave(group); - if (group == NULL) - return; + // Sign group + const char *group_chk = tv_get_string_chk(&argvars[1]); + if (group_chk == NULL) { + return; + } + if (group_chk[0] == '\0') { + group = NULL; // global sign group + } else { + group = vim_strsave((const char_u*)group_chk); + if (group == NULL) { + return; } + } - // Sign name - sign_name = tv_get_string_chk(&argvars[2]); - if (sign_name == NULL) - goto cleanup; + // Sign name + sign_name = tv_get_string_chk(&argvars[2]); + if (sign_name == NULL) { + goto cleanup; + } - // Buffer to place the sign - buf = get_buf_arg(&argvars[3]); - if (buf == NULL) - goto cleanup; + // Buffer to place the sign + buf = get_buf_arg(&argvars[3]); + if (buf == NULL) { + goto cleanup; + } - if (argvars[4].v_type != VAR_UNKNOWN) - { - if (argvars[4].v_type != VAR_DICT || - ((dict = argvars[4].vval.v_dict) == NULL)) - { - EMSG(_(e_dictreq)); - goto cleanup; - } + if (argvars[4].v_type != VAR_UNKNOWN) { + if (argvars[4].v_type != VAR_DICT || + ((dict = argvars[4].vval.v_dict) == NULL)) { + EMSG(_(e_dictreq)); + goto cleanup; + } - // Line number where the sign is to be placed - if ((di = tv_dict_find(dict, (char_u *)"lnum", -1)) != NULL) - { - (void)tv_get_number_chk(&di->di_tv, ¬anum); - if (notanum) - goto cleanup; - lnum = tv_get_lnum(&di->di_tv); - } - if ((di = tv_dict_find(dict, (char_u *)"priority", -1)) != NULL) - { - // Sign priority - prio = (int)tv_get_number_chk(&di->di_tv, ¬anum); - if (notanum) - goto cleanup; - } + // Line number where the sign is to be placed + if ((di = tv_dict_find(dict, "lnum", -1)) != NULL) { + lnum = (linenr_T)tv_get_number_chk(&di->di_tv, ¬anum); + if (notanum) { + goto cleanup; + } + lnum = tv_get_lnum(&di->di_tv); } + if ((di = tv_dict_find(dict, "priority", -1)) != NULL) { + // Sign priority + prio = (int)tv_get_number_chk(&di->di_tv, ¬anum); + if (notanum) { + goto cleanup; + } + } + } - if (sign_place(&sign_id, group, sign_name, buf, lnum, prio) == OK) - rettv->vval.v_number = sign_id; + if (sign_place(&sign_id, group, (const char_u*)sign_name, buf, lnum, prio) == OK) { + rettv->vval.v_number = sign_id; + } cleanup: - xfree(group); + xfree(group); } /* * "sign_undefine()" function */ static void -f_sign_undefine(typval_T *argvars, typval_T *rettv) +f_sign_undefine(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - char_u *name; + const char *name; - rettv->vval.v_number = -1; + rettv->vval.v_number = -1; - if (argvars[0].v_type == VAR_UNKNOWN) - { - // Free all the signs - free_signs(); - rettv->vval.v_number = 0; - } - else - { - // Free only the specified sign - name = tv_get_string_chk(&argvars[0]); - if (name == NULL) - return; + if (argvars[0].v_type == VAR_UNKNOWN) + { + // Free all the signs + free_signs(); + rettv->vval.v_number = 0; + } + else + { + // Free only the specified sign + name = tv_get_string_chk(&argvars[0]); + if (name == NULL) + return; - if (sign_undefine_by_name(name) == OK) - rettv->vval.v_number = 0; + if (sign_undefine_by_name((const char_u*)name) == OK) { + rettv->vval.v_number = 0; } + } } /* * "sign_unplace()" function */ - static void -f_sign_unplace(typval_T *argvars, typval_T *rettv) +static void f_sign_unplace(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - dict_T *dict; - dictitem_T *di; - int sign_id = 0; - buf_T *buf = NULL; - char_u *group = NULL; + dict_T *dict; + dictitem_T *di; + int sign_id = 0; + buf_T *buf = NULL; + char_u *group = NULL; - rettv->vval.v_number = -1; + rettv->vval.v_number = -1; - if (argvars[0].v_type != VAR_STRING) - { - EMSG(_(e_invarg)); - return; + if (argvars[0].v_type != VAR_STRING) + { + EMSG(_(e_invarg)); + return; + } + + const char* group_chk = tv_get_string(&argvars[0]); + if (group_chk[0] == '\0') { + group = NULL; // global sign group + } else { + group = vim_strsave((const char_u*)group_chk); + if (group == NULL) { + return; } + } - group = tv_get_string(&argvars[0]); - if (group[0] == '\0') - group = NULL; // global sign group - else + if (argvars[1].v_type != VAR_UNKNOWN) + { + if (argvars[1].v_type != VAR_DICT) { - group = vim_strsave(group); - if (group == NULL) - return; + EMSG(_(e_dictreq)); + goto cleanup; } + dict = argvars[1].vval.v_dict; - if (argvars[1].v_type != VAR_UNKNOWN) + if ((di = tv_dict_find(dict, "buffer", -1)) != NULL) { - if (argvars[1].v_type != VAR_DICT) - { - EMSG(_(e_dictreq)); - goto cleanup; - } - dict = argvars[1].vval.v_dict; - - if ((di = tv_dict_find(dict, (char_u *)"buffer", -1)) != NULL) - { - buf = get_buf_arg(&di->di_tv); - if (buf == NULL) - goto cleanup; - } - if (tv_dict_find(dict, (char_u *)"id", -1) != NULL) - sign_id = tv_dict_get_number(dict, (char_u *)"id"); + buf = get_buf_arg(&di->di_tv); + if (buf == NULL) + goto cleanup; + } + if (tv_dict_find(dict, "id", -1) != NULL) { + sign_id = tv_dict_get_number(dict, "id"); } + } - if (buf == NULL) - { - // Delete the sign in all the buffers - FOR_ALL_BUFFERS(buf) - if (sign_unplace(sign_id, group, buf, 0) == OK) - rettv->vval.v_number = 0; + if (buf == NULL) + { + // Delete the sign in all the buffers + FOR_ALL_BUFFERS(cbuf) { + if (sign_unplace(sign_id, group, cbuf, 0) == OK) { + rettv->vval.v_number = 0; + } } - else - { - if (sign_unplace(sign_id, group, buf, 0) == OK) - rettv->vval.v_number = 0; + } else { + if (sign_unplace(sign_id, group, buf, 0) == OK) { + rettv->vval.v_number = 0; } + } cleanup: - xfree(group); + xfree(group); } /* diff --git a/src/nvim/main.c b/src/nvim/main.c index 93b31d684e..ed8788af60 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -49,6 +49,7 @@ #include "nvim/popupmnu.h" #include "nvim/quickfix.h" #include "nvim/screen.h" +#include "nvim/sign.h" #include "nvim/state.h" #include "nvim/strings.h" #include "nvim/syntax.h" diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 7809b6814f..2f2f2a7d74 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -29,6 +29,7 @@ #include "nvim/path.h" #include "nvim/quickfix.h" #include "nvim/search.h" +#include "nvim/sign.h" #include "nvim/strings.h" #include "nvim/ui.h" #include "nvim/os/os.h" diff --git a/src/nvim/memory.c b/src/nvim/memory.c index b8a29070ce..dced03f3d5 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -16,6 +16,7 @@ #include "nvim/message.h" #include "nvim/misc1.h" #include "nvim/ui.h" +#include "nvim/sign.h" #include "nvim/api/vim.h" #ifdef UNIT_TESTING diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 01f6c275af..5d482700d0 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -84,14 +84,14 @@ init_signs(void) * 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(char_u *groupname) +static signgroup_T * sign_group_ref(const char_u *groupname) { hash_T hash; hashitem_T *hi; signgroup_T *group; hash = hash_hash(groupname); - hi = hash_lookup(&sg_table, S_LEN(groupname), hash); + hi = hash_lookup(&sg_table, (char*)S_LEN(groupname), hash); if (HASHITEM_EMPTY(hi)) { // new group @@ -142,7 +142,7 @@ static void sign_group_unref(char_u *groupname) * 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. */ -int sign_in_group(signlist_T *sign, char_u *group) +int sign_in_group(signlist_T *sign, const char_u *group) { return ((group != NULL && STRCMP(group, "*") == 0) || (group == NULL && sign->group == NULL) @@ -154,7 +154,7 @@ int sign_in_group(signlist_T *sign, char_u *group) * Get the next free sign identifier in the specified group */ int -sign_group_get_next_signid(buf_T *buf, char_u *groupname) +sign_group_get_next_signid(buf_T *buf, const char_u *groupname) { int id = 1; signgroup_T *group = NULL; @@ -202,7 +202,7 @@ static void insert_sign( signlist_T *prev, // previous sign entry signlist_T *next, // next sign entry int id, // sign ID - char_u *group, // sign group; NULL for global group + const char_u *group, // sign group; NULL for global group int prio, // sign priority linenr_T lnum, // line number which gets the mark int typenr // typenr of sign we are adding @@ -254,7 +254,7 @@ static void insert_sign_by_lnum_prio( buf_T *buf, // buffer to store sign in signlist_T *prev, // previous sign entry int id, // sign ID - char_u *group, // sign group; NULL for global group + const char_u *group, // sign group; NULL for global group int prio, // sign priority linenr_T lnum, // line number which gets the mark int typenr // typenr of sign we are adding @@ -300,9 +300,10 @@ dict_T * sign_get_info(signlist_T *sign) return NULL; } tv_dict_add_nr(d, S_LEN("id"), sign->id); - tv_dict_add_str(d, S_LEN("group"), (sign->group == NULL) ? (char_u *)"" : sign->group->sg_name); + tv_dict_add_str(d, S_LEN("group"), + (sign->group == NULL) ? (char*)"" : (char*)sign->group->sg_name); tv_dict_add_nr(d, S_LEN("lnum"), sign->lnum); - tv_dict_add_str(d, S_LEN("name"), sign_typenr2name(sign->typenr)); + tv_dict_add_str(d, S_LEN("name"), (char*)sign_typenr2name(sign->typenr)); tv_dict_add_nr(d, S_LEN("priority"), sign->priority); return d; @@ -314,13 +315,12 @@ dict_T * sign_get_info(signlist_T *sign) void buf_addsign( buf_T *buf, // buffer to store sign in int id, // sign ID - char_u *groupname, // sign group + const char_u *groupname, // sign group int prio, // sign priority linenr_T lnum, // line number which gets the mark int typenr // typenr of sign we are adding ) { - signlist_T **lastp; // pointer to pointer to current sign signlist_T *sign; // a sign in the signlist signlist_T *prev; // the previous sign @@ -346,7 +346,7 @@ void buf_addsign( linenr_T buf_change_sign_type( buf_T *buf, // buffer to store sign in int markId, // sign ID - char_u *group, // sign group + const char_u *group, // sign group int typenr // typenr of sign we are adding ) { @@ -628,15 +628,11 @@ void sign_list_placed(buf_T *rbuf, char_u *sign_group) void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after) { signlist_T *sign; // a sign in a b_signlist - signlist_T *next; // the next sign in a b_signlist - signlist_T **lastp; // pointer to pointer to current sign linenr_T new_lnum; // new line number to assign to sign curbuf->b_signcols_max = -1; - lastp = &curbuf->b_signlist; FOR_ALL_SIGNS_IN_BUF(curbuf, sign) { - next = sign->next; new_lnum = sign->lnum; if (sign->lnum >= line1 && sign->lnum <= line2) { if (amount != MAXLNUM) { @@ -651,7 +647,6 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a if (sign->lnum >= line1 && new_lnum <= curbuf->b_ml.ml_line_count) { sign->lnum = new_lnum; } - lastp = &sign->next; } } @@ -665,9 +660,9 @@ static int sign_cmd_idx( ) { int idx; - char save = *end_cmd; + char_u save = *end_cmd; - *end_cmd = NUL; + *end_cmd = (char_u)NUL; for (idx = 0; ; ++idx) { if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0) { break; @@ -680,8 +675,7 @@ static int sign_cmd_idx( /* * Find a sign by name. Also returns pointer to the previous sign. */ - static sign_T * -sign_find(char_u *name, sign_T **sp_prev) +static sign_T * sign_find(const char_u *name, sign_T **sp_prev) { sign_T *sp; @@ -769,7 +763,7 @@ sign_define_init_text(sign_T *sp, char_u *text) char_u *s; char_u *endp; int cells; - int len; + size_t len; endp = text + (int)STRLEN(text); for (s = text; s + 1 < endp; ++s) { @@ -798,7 +792,7 @@ sign_define_init_text(sign_T *sp, char_u *text) xfree(sp->sn_text); // Allocate one byte more if we need to pad up // with a space. - len = (int)(endp - text + ((cells == 1) ? 1 : 0)); + len = (size_t)(endp - text + ((cells == 1) ? 1 : 0)); sp->sn_text = vim_strnsave(text, len); if (cells == 1) @@ -864,7 +858,7 @@ int sign_define_by_name( * Free the sign specified by 'name'. */ int -sign_undefine_by_name(char_u *name) +sign_undefine_by_name(const char_u *name) { sign_T *sp_prev; sign_T *sp; @@ -900,15 +894,15 @@ sign_list_by_name(char_u *name) * Place a sign at the specified file location or update a sign. */ int sign_place( - int *sign_id, - char_u *sign_group, - char_u *sign_name, - buf_T *buf, - linenr_T lnum, - int prio + int *sign_id, + const char_u *sign_group, + const char_u *sign_name, + buf_T *buf, + linenr_T lnum, + int prio ) { - sign_T *sp; + sign_T *sp; // Check for reserved character '*' in group name if (sign_group != NULL && (*sign_group == '*' || *sign_group == '\0')) { @@ -999,7 +993,7 @@ linenr_T sign_jump(int sign_id, char_u *sign_group, buf_T *buf) linenr_T lnum; if ((lnum = buf_findsign(buf, sign_id, sign_group)) <= 0) { - EMSGN(_("E157: Invalid sign ID: %ld"), sign_id); + EMSGN(_("E157: Invalid sign ID: %" PRId64), sign_id); return -1; } @@ -1050,19 +1044,19 @@ static void sign_define_cmd(char_u *sign_name, char_u *cmdline) p = skiptowhite_esc(arg); if (STRNCMP(arg, "icon=", 5) == 0) { arg += 5; - icon = vim_strnsave(arg, (int)(p - arg)); + icon = vim_strnsave(arg, (size_t)(p - arg)); } else if (STRNCMP(arg, "text=", 5) == 0) { arg += 5; - text = vim_strnsave(arg, (int)(p - arg)); + text = vim_strnsave(arg, (size_t)(p - arg)); } else if (STRNCMP(arg, "linehl=", 7) == 0) { arg += 7; - linehl = vim_strnsave(arg, (int)(p - arg)); + linehl = vim_strnsave(arg, (size_t)(p - arg)); } else if (STRNCMP(arg, "texthl=", 7) == 0) { arg += 7; - texthl = vim_strnsave(arg, (int)(p - arg)); + texthl = vim_strnsave(arg, (size_t)(p - arg)); } else if (STRNCMP(arg, "numhl=", 6) == 0) { arg += 6; - numhl = vim_strnsave(arg, (int)(p - arg)); + numhl = vim_strnsave(arg, (size_t)(p - arg)); } else { EMSG2(_(e_invarg2), arg); failed = TRUE; @@ -1153,9 +1147,9 @@ static void sign_unplace_cmd( // :sign unplace * // :sign unplace * group={group} // :sign unplace * group=* - FOR_ALL_BUFFERS(buf) { - if (buf->b_signlist != NULL) { - buf_delete_signs(buf, group); + FOR_ALL_BUFFERS(cbuf) { + if (cbuf->b_signlist != NULL) { + buf_delete_signs(cbuf, group); } } } @@ -1178,8 +1172,8 @@ static void sign_unplace_cmd( // :sign unplace {id} // :sign unplace {id} group={group} // :sign unplace {id} group=* - FOR_ALL_BUFFERS(buf) { - sign_unplace(id, group, buf, 0); + FOR_ALL_BUFFERS(cbuf) { + sign_unplace(id, group, cbuf, 0); } } } @@ -1403,35 +1397,35 @@ void ex_sign(exarg_T *eap) */ static void sign_getinfo(sign_T *sp, dict_T *retdict) { - char_u *p; + const char *p; - tv_dict_add_str(retdict, S_LEN("name"), (char_u *)sp->sn_name); + tv_dict_add_str(retdict, S_LEN("name"), (char *)sp->sn_name); if (sp->sn_icon != NULL) { - tv_dict_add_str(retdict, S_LEN("icon"), (char_u *)sp->sn_icon); + tv_dict_add_str(retdict, S_LEN("icon"), (char *)sp->sn_icon); } if (sp->sn_text != NULL) { - tv_dict_add_str(retdict, S_LEN("text"), (char_u *)sp->sn_text); + tv_dict_add_str(retdict, S_LEN("text"), (char *)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 = (char_u *)"NONE"; + p = "NONE"; } - tv_dict_add_str(retdict, S_LEN("linehl"), (char_u *)p); + tv_dict_add_str(retdict, S_LEN("linehl"), (char *)p); } if (sp->sn_text_hl > 0) { p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE); if (p == NULL) { - p = (char_u *)"NONE"; + p = "NONE"; } - tv_dict_add_str(retdict, S_LEN("texthl"), (char_u *)p); + tv_dict_add_str(retdict, S_LEN("texthl"), (char *)p); } if (sp->sn_num_hl > 0) { p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, FALSE); if (p == NULL) { - p = (char_u *)"NONE"; + p = "NONE"; } - tv_dict_add_str(retdict, S_LEN("numhl"), (char_u *)p); + tv_dict_add_str(retdict, S_LEN("numhl"), (char *)p); } } @@ -1439,7 +1433,7 @@ static void sign_getinfo(sign_T *sp, dict_T *retdict) * If 'name' is NULL, return a list of all the defined signs. * Otherwise, return information about the specified sign. */ -void sign_getlist(char_u *name, list_T *retlist) +void sign_getlist(const char_u *name, list_T *retlist) { sign_T *sp = first_sign; dict_T *dict; @@ -1484,11 +1478,11 @@ list_T *get_buffer_signs(buf_T *buf) * 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, - char_u *sign_group, - list_T *retlist) + buf_T *buf, + linenr_T lnum, + int sign_id, + const char_u *sign_group, + list_T *retlist) { dict_T *d; list_T *l; @@ -1528,18 +1522,18 @@ static void sign_get_placed_in_buf( * placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers. */ void sign_get_placed( - buf_T *buf, - linenr_T lnum, - int sign_id, - char_u *sign_group, - list_T *retlist) + buf_T *buf, + linenr_T lnum, + int sign_id, + const char_u *sign_group, + list_T *retlist) { if (buf != NULL) { sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist); } else { - FOR_ALL_BUFFERS(buf) { - if (buf->b_signlist != NULL) { - sign_get_placed_in_buf(buf, 0, sign_id, sign_group, retlist); + FOR_ALL_BUFFERS(cbuf) { + if (cbuf->b_signlist != NULL) { + sign_get_placed_in_buf(cbuf, 0, sign_id, sign_group, retlist); } } } -- cgit From 237cecd81b83dcbdac05b208ea6adc3357039858 Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Tue, 21 May 2019 21:50:59 +0200 Subject: vim-patch:8.1.0039: cannot easily delete lines in another buffer Problem: Cannot easily delete lines in another buffer. Solution: Add deletebufline(). https://github.com/vim/vim/commit/d79a26219d7161e9211fd144f0e874aa5f6d251e --- src/nvim/eval.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/nvim/eval.lua | 1 + 2 files changed, 102 insertions(+) (limited to 'src') diff --git a/src/nvim/eval.c b/src/nvim/eval.c index f3c2e34d80..dded1a07f3 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1114,6 +1114,24 @@ static void restore_vimvar(int idx, typval_T *save_tv) } } +/* + * If there is a window for "curbuf", make it the current window. + */ + static void +find_win_for_curbuf(void) +{ + wininfo_T *wip; + + for (wip = curbuf->b_wininfo; wip != NULL; wip = wip->wi_next) + { + if (wip->wi_win != NULL) + { + curwin = wip->wi_win; + break; + } + } +} + /* * Evaluate an expression to a list with suggestions. * For the "expr:" part of 'spellsuggest'. @@ -8017,6 +8035,89 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) callback_free(&callback); } +/* + * "deletebufline()" function + */ + static void +f_deletebufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + buf_T *buf; + linenr_T first, last; + linenr_T lnum; + long count; + int is_curbuf; + buf_T *curbuf_save = NULL; + win_T *curwin_save = NULL; + + buf = tv_get_buf(&argvars[0], FALSE); + if (buf == NULL) + { + rettv->vval.v_number = 1; /* FAIL */ + return; + } + is_curbuf = buf == curbuf; + + first = tv_get_lnum_buf(&argvars[1], buf); + if (argvars[2].v_type != VAR_UNKNOWN) + last = tv_get_lnum_buf(&argvars[2], buf); + else + last = first; + + if (buf->b_ml.ml_mfp == NULL || first < 1 + || first > buf->b_ml.ml_line_count || last < first) + { + rettv->vval.v_number = 1; /* FAIL */ + return; + } + + if (!is_curbuf) + { + curbuf_save = curbuf; + curwin_save = curwin; + curbuf = buf; + find_win_for_curbuf(); + } + if (last > curbuf->b_ml.ml_line_count) + last = curbuf->b_ml.ml_line_count; + count = last - first + 1; + + // When coming here from Insert mode, sync undo, so that this can be + // undone separately from what was previously inserted. + if (u_sync_once == 2) + { + u_sync_once = 1; // notify that u_sync() was called + u_sync(TRUE); + } + + if (u_save(first - 1, last + 1) == FAIL) + { + rettv->vval.v_number = 1; /* FAIL */ + return; + } + + for (lnum = first; lnum <= last; ++lnum) + ml_delete(first, TRUE); + + FOR_ALL_TAB_WINDOWS(tp, wp) + if (wp->w_buffer == buf) + { + if (wp->w_cursor.lnum > last) + wp->w_cursor.lnum -= count; + else if (wp->w_cursor.lnum> first) + wp->w_cursor.lnum = first; + if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) + wp->w_cursor.lnum = wp->w_buffer->b_ml.ml_line_count; + } + check_cursor_col(); + deleted_lines_mark(first, count); + + if (!is_curbuf) + { + curbuf = curbuf_save; + curwin = curwin_save; + } +} + /* * "did_filetype()" function */ diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 15f392d8f1..aff1ff2a74 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -75,6 +75,7 @@ return { cursor={args={1, 3}}, deepcopy={args={1, 2}}, delete={args={1,2}}, + deletebufline={args={2,3}}, dictwatcheradd={args=3}, dictwatcherdel={args=3}, did_filetype={}, -- cgit From 897431c486bfe9ba7dbb52c8520df5d82fb63f01 Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Sat, 25 May 2019 11:51:13 +0200 Subject: Fix out of bounds read in sign_group_ref --- src/nvim/sign.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 5d482700d0..68d013f298 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -91,7 +91,7 @@ static signgroup_T * sign_group_ref(const char_u *groupname) signgroup_T *group; hash = hash_hash(groupname); - hi = hash_lookup(&sg_table, (char*)S_LEN(groupname), hash); + hi = hash_lookup(&sg_table, (char*)groupname, STRLEN(groupname), hash); if (HASHITEM_EMPTY(hi)) { // new group -- cgit From 0b9e57aca1ec12f35c2a2531be9769eff076c6b2 Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Sat, 25 May 2019 13:45:27 +0200 Subject: Skipping Test_sign_memfailures (unsupported memory checks) --- src/nvim/testdir/test_signs.vim | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index 3edb4b4ae0..ef4b227215 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -1157,6 +1157,7 @@ endfunc " Tests for memory allocation failures in sign functions func Test_sign_memfailures() + throw 'skipped: Nvim does not support test_alloc_fail()' call writefile(repeat(["Sun is shining"], 30), "Xsign") edit Xsign -- cgit From 80f40f0203f5af167f8c77bf6b9f22a4d1abd6da Mon Sep 17 00:00:00 2001 From: Andrej Zieger Date: Sun, 26 May 2019 17:06:50 +0200 Subject: lint --- src/nvim/buffer.c | 6 +- src/nvim/buffer_defs.h | 28 +- src/nvim/edit.c | 11 +- src/nvim/eval.c | 439 +++++++-------- src/nvim/fileio.c | 92 ++-- src/nvim/globals.h | 2 +- src/nvim/if_cscope.c | 23 +- src/nvim/mbyte.c | 38 +- src/nvim/memline.c | 57 +- src/nvim/message.c | 55 +- src/nvim/normal.c | 8 +- src/nvim/search.c | 14 +- src/nvim/sign.c | 1411 ++++++++++++++++++++++-------------------------- src/nvim/sign.h | 3 + src/nvim/sign_defs.h | 25 +- 15 files changed, 1033 insertions(+), 1179 deletions(-) (limited to 'src') diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 39242b0575..078d4fe782 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -805,9 +805,9 @@ free_buffer_stuff( vars_clear(&buf->b_vars->dv_hashtab); // free all internal variables hash_init(&buf->b_vars->dv_hashtab); buf_init_changedtick(buf); - uc_clear(&buf->b_ucmds); // clear local user commands - buf_delete_signs(buf, (char_u *)"*"); // delete any signs - bufhl_clear_all(buf); // delete any highligts + uc_clear(&buf->b_ucmds); // clear local user commands + buf_delete_signs(buf, (char_u *)"*"); // delete any signs + bufhl_clear_all(buf); // delete any highligts map_clear_int(buf, MAP_ALL_MODES, true, false); // clear local mappings map_clear_int(buf, MAP_ALL_MODES, true, true); // clear local abbrevs XFREE_CLEAR(buf->b_start_fenc); diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 5e28a7b513..255aeb82b6 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -769,15 +769,15 @@ struct file_buffer { * spell buffer - used for spell info, never displayed and doesn't have a * file name. */ - bool b_help; /* TRUE for help file buffer (when set b_p_bt - is "help") */ - bool b_spell; /* True for a spell file buffer, most fields - are not used! Use the B_SPELL macro to - access b_spell without #ifdef. */ + bool b_help; // TRUE for help file buffer (when set b_p_bt + // is "help") + bool b_spell; // True for a spell file buffer, most fields + // are not used! Use the B_SPELL macro to + // access b_spell without #ifdef. - synblock_T b_s; /* Info related to syntax highlighting. w_s - * normally points to this, but some windows - * may use a different synblock_T. */ + synblock_T b_s; // Info related to syntax highlighting. w_s + // normally points to this, but some windows + // may use a different synblock_T. signlist_T *b_signlist; // list of signs to draw int b_signcols_max; // cached maximum number of sign columns @@ -1144,12 +1144,12 @@ struct window_S { int w_cline_row; /* starting row of the cursor line */ - colnr_T w_virtcol; /* column number of the cursor in the - buffer line, as opposed to the column - number we're at on the screen. This - makes a difference on lines which span - more than one screen line or when - w_leftcol is non-zero */ + colnr_T w_virtcol; // column number of the cursor in the + // buffer line, as opposed to the column + // number we're at on the screen. This + // makes a difference on lines which span + // more than one screen line or when + // w_leftcol is non-zero /* * w_wrow and w_wcol specify the cursor position in the window. diff --git a/src/nvim/edit.c b/src/nvim/edit.c index a8dd115074..bfd63aab7a 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -179,9 +179,9 @@ static int compl_no_insert = FALSE; /* FALSE: select & insert static int compl_no_select = FALSE; /* FALSE: select & insert TRUE: noselect */ -static int compl_used_match; /* Selected one of the matches. When - FALSE the match was edited or using - the longest common string. */ +static int compl_used_match; // Selected one of the matches. When + // FALSE the match was edited or using + // the longest common string. static int compl_was_interrupted = FALSE; /* didn't finish finding completions. */ @@ -2599,8 +2599,9 @@ void ins_compl_show_pum(void) do { if ((compl->cp_flags & ORIGINAL_TEXT) == 0 && (compl_leader == NULL - || ins_compl_equal(compl, compl_leader, lead_len))) - ++compl_match_arraysize; + || ins_compl_equal(compl, compl_leader, lead_len))) { + compl_match_arraysize++; + } compl = compl->cp_next; } while (compl != NULL && compl != compl_first_match); if (compl_match_arraysize == 0) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index dded1a07f3..1d15e04218 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1114,22 +1114,17 @@ static void restore_vimvar(int idx, typval_T *save_tv) } } -/* - * If there is a window for "curbuf", make it the current window. - */ - static void -find_win_for_curbuf(void) +/// If there is a window for "curbuf", make it the current window. +static void find_win_for_curbuf(void) { - wininfo_T *wip; + wininfo_T *wip; - for (wip = curbuf->b_wininfo; wip != NULL; wip = wip->wi_next) - { - if (wip->wi_win != NULL) - { - curwin = wip->wi_win; - break; - } + for (wip = curbuf->b_wininfo; wip != NULL; wip = wip->wi_next) { + if (wip->wi_win != NULL) { + curwin = wip->wi_win; + break; } + } } /* @@ -1144,7 +1139,7 @@ list_T *eval_spell_expr(char_u *badword, char_u *expr) list_T *list = NULL; char_u *p = skipwhite(expr); - /* Set "v:val" to the bad word. */ + // Set "v:val" to the bad word. prepare_vimvar(VV_VAL, &save_val); vimvars[VV_VAL].vv_type = VAR_STRING; vimvars[VV_VAL].vv_str = badword; @@ -7202,7 +7197,7 @@ static buf_T *tv_get_buf(typval_T *tv, int curtab_only) if (name[0] == '$' && name[1] == NUL) return lastbuf; - /* Ignore 'magic' and 'cpoptions' here to make scripts portable */ + // Ignore 'magic' and 'cpoptions' here to make scripts portable save_magic = p_magic; p_magic = TRUE; save_cpo = p_cpo; @@ -7214,28 +7209,27 @@ static buf_T *tv_get_buf(typval_T *tv, int curtab_only) p_magic = save_magic; p_cpo = save_cpo; - /* If not found, try expanding the name, like done for bufexists(). */ - if (buf == NULL) + // If not found, try expanding the name, like done for bufexists(). + if (buf == NULL) { buf = find_buffer(tv); + } return buf; } -/* - * Get the buffer from "arg" and give an error and return NULL if it is not - * valid. - */ - static buf_T * -get_buf_arg(typval_T *arg) +/// Get the buffer from "arg" and give an error and return NULL if it is not +/// valid. +static buf_T * get_buf_arg(typval_T *arg) { - buf_T *buf; + buf_T *buf; - ++emsg_off; - buf = tv_get_buf(arg, FALSE); - --emsg_off; - if (buf == NULL) - EMSG2(_("E158: Invalid buffer name: %s"), tv_get_string(arg)); - return buf; + emsg_off++; + buf = tv_get_buf(arg, false); + emsg_off--; + if (buf == NULL) { + EMSG2(_("E158: Invalid buffer name: %s"), tv_get_string(arg)); + } + return buf; } /* @@ -8035,87 +8029,83 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr) callback_free(&callback); } -/* - * "deletebufline()" function - */ - static void -f_deletebufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) +/// "deletebufline()" function +static void f_deletebufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - buf_T *buf; - linenr_T first, last; - linenr_T lnum; - long count; - int is_curbuf; - buf_T *curbuf_save = NULL; - win_T *curwin_save = NULL; + buf_T *buf; + linenr_T first, last; + linenr_T lnum; + long count; + int is_curbuf; + buf_T *curbuf_save = NULL; + win_T *curwin_save = NULL; - buf = tv_get_buf(&argvars[0], FALSE); - if (buf == NULL) - { - rettv->vval.v_number = 1; /* FAIL */ - return; - } - is_curbuf = buf == curbuf; + buf = tv_get_buf(&argvars[0], false); + if (buf == NULL) { + rettv->vval.v_number = 1; // FAIL + return; + } + is_curbuf = buf == curbuf; - first = tv_get_lnum_buf(&argvars[1], buf); - if (argvars[2].v_type != VAR_UNKNOWN) - last = tv_get_lnum_buf(&argvars[2], buf); - else - last = first; + first = tv_get_lnum_buf(&argvars[1], buf); + if (argvars[2].v_type != VAR_UNKNOWN) { + last = tv_get_lnum_buf(&argvars[2], buf); + } else { + last = first; + } - if (buf->b_ml.ml_mfp == NULL || first < 1 - || first > buf->b_ml.ml_line_count || last < first) - { - rettv->vval.v_number = 1; /* FAIL */ - return; - } + if (buf->b_ml.ml_mfp == NULL || first < 1 + || first > buf->b_ml.ml_line_count || last < first) { + rettv->vval.v_number = 1; // FAIL + return; + } - if (!is_curbuf) - { - curbuf_save = curbuf; - curwin_save = curwin; - curbuf = buf; - find_win_for_curbuf(); - } - if (last > curbuf->b_ml.ml_line_count) - last = curbuf->b_ml.ml_line_count; - count = last - first + 1; - - // When coming here from Insert mode, sync undo, so that this can be - // undone separately from what was previously inserted. - if (u_sync_once == 2) - { - u_sync_once = 1; // notify that u_sync() was called - u_sync(TRUE); - } + if (!is_curbuf) { + curbuf_save = curbuf; + curwin_save = curwin; + curbuf = buf; + find_win_for_curbuf(); + } + if (last > curbuf->b_ml.ml_line_count) { + last = curbuf->b_ml.ml_line_count; + } + count = last - first + 1; - if (u_save(first - 1, last + 1) == FAIL) - { - rettv->vval.v_number = 1; /* FAIL */ - return; - } - - for (lnum = first; lnum <= last; ++lnum) - ml_delete(first, TRUE); - - FOR_ALL_TAB_WINDOWS(tp, wp) - if (wp->w_buffer == buf) - { - if (wp->w_cursor.lnum > last) - wp->w_cursor.lnum -= count; - else if (wp->w_cursor.lnum> first) - wp->w_cursor.lnum = first; - if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) - wp->w_cursor.lnum = wp->w_buffer->b_ml.ml_line_count; - } - check_cursor_col(); - deleted_lines_mark(first, count); - - if (!is_curbuf) - { - curbuf = curbuf_save; - curwin = curwin_save; + // When coming here from Insert mode, sync undo, so that this can be + // undone separately from what was previously inserted. + if (u_sync_once == 2) { + u_sync_once = 1; // notify that u_sync() was called + u_sync(true); + } + + if (u_save(first - 1, last + 1) == FAIL) { + rettv->vval.v_number = 1; // FAIL + return; + } + + for (lnum = first; lnum <= last; lnum++) { + ml_delete(first, true); + } + + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->w_buffer == buf) { + if (wp->w_cursor.lnum > last) { + wp->w_cursor.lnum -= count; + } else if (wp->w_cursor.lnum> first) { + wp->w_cursor.lnum = first; + } + if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) { + wp->w_cursor.lnum = wp->w_buffer->b_ml.ml_line_count; + } } + } + check_cursor_col(); + deleted_lines_mark(first, count); + + if (!is_curbuf) { + curbuf = curbuf_save; + curwin = curwin_save; + } } /* @@ -15539,92 +15529,89 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = get_sw_value(curbuf); } -/* - * "sign_define()" function - */ +/// "sign_define()" function static void f_sign_define(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - const char *name; - dict_T *dict; - char *icon = NULL; - char *linehl = NULL; - char *text = NULL; - char *texthl = NULL; - char *numhl = NULL; + const char *name; + dict_T *dict; + char *icon = NULL; + char *linehl = NULL; + char *text = NULL; + char *texthl = NULL; + char *numhl = NULL; - rettv->vval.v_number = -1; + rettv->vval.v_number = -1; - name = tv_get_string_chk(&argvars[0]); - if (name == NULL) - return; + name = tv_get_string_chk(&argvars[0]); + if (name == NULL) { + return; + } - if (argvars[1].v_type != VAR_UNKNOWN) - { - if (argvars[1].v_type != VAR_DICT) - { - EMSG(_(e_dictreq)); - return; - } - - // sign attributes - dict = argvars[1].vval.v_dict; - if (tv_dict_find(dict, "icon", -1) != NULL) - icon = tv_dict_get_string(dict, "icon", TRUE); - if (tv_dict_find(dict, "linehl", -1) != NULL) - linehl = tv_dict_get_string(dict, "linehl", TRUE); - if (tv_dict_find(dict, "text", -1) != NULL) - text = tv_dict_get_string(dict, "text", TRUE); - if (tv_dict_find(dict, "texthl", -1) != NULL) - texthl = tv_dict_get_string(dict, "texthl", TRUE); - if (tv_dict_find(dict, "numhl", -1) != NULL) - numhl = tv_dict_get_string(dict, "numhl", TRUE); - } - - if (sign_define_by_name((char_u *)name, (char_u *)icon, (char_u *)linehl, - (char_u *)text, (char_u *)texthl, (char_u *)numhl) - == OK) { - rettv->vval.v_number = 0; + if (argvars[1].v_type != VAR_UNKNOWN) { + if (argvars[1].v_type != VAR_DICT) { + EMSG(_(e_dictreq)); + return; + } + + // sign attributes + dict = argvars[1].vval.v_dict; + if (tv_dict_find(dict, "icon", -1) != NULL) { + icon = tv_dict_get_string(dict, "icon", true); + } + if (tv_dict_find(dict, "linehl", -1) != NULL) { + linehl = tv_dict_get_string(dict, "linehl", true); + } + if (tv_dict_find(dict, "text", -1) != NULL) { + text = tv_dict_get_string(dict, "text", true); + } + if (tv_dict_find(dict, "texthl", -1) != NULL) { + texthl = tv_dict_get_string(dict, "texthl", true); + } + if (tv_dict_find(dict, "numhl", -1) != NULL) { + numhl = tv_dict_get_string(dict, "numhl", true); } + } + + if (sign_define_by_name((char_u *)name, (char_u *)icon, (char_u *)linehl, + (char_u *)text, (char_u *)texthl, (char_u *)numhl) + == OK) { + rettv->vval.v_number = 0; + } - xfree(icon); - xfree(linehl); - xfree(text); - xfree(texthl); + xfree(icon); + xfree(linehl); + xfree(text); + xfree(texthl); } -/* - * "sign_getdefined()" function - */ - static void -f_sign_getdefined(typval_T *argvars, typval_T *rettv, FunPtr fptr) +/// "sign_getdefined()" function +static void f_sign_getdefined(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - const char *name = NULL; + const char *name = NULL; - tv_list_alloc_ret(rettv, 0); + 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) { + name = tv_get_string(&argvars[0]); + } - sign_getlist((const char_u *)name, rettv->vval.v_list); + sign_getlist((const char_u *)name, rettv->vval.v_list); } -/* - * "sign_getplaced()" function - */ +/// "sign_getplaced()" function static void f_sign_getplaced(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - buf_T *buf = NULL; - dict_T *dict; - dictitem_T *di; - linenr_T lnum = 0; + buf_T *buf = NULL; + dict_T *dict; + dictitem_T *di; + linenr_T lnum = 0; int sign_id = 0; const char *group = NULL; - bool notanum = FALSE; + bool notanum = false; tv_list_alloc_ret(rettv, 0); - if (argvars[0].v_type != VAR_UNKNOWN) - { + if (argvars[0].v_type != VAR_UNKNOWN) { // get signs placed in the specified buffer buf = get_buf_arg(&argvars[0]); if (buf == NULL) { @@ -15632,8 +15619,8 @@ static void f_sign_getplaced(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (argvars[1].v_type != VAR_UNKNOWN) { - if (argvars[1].v_type != VAR_DICT || - ((dict = argvars[1].vval.v_dict) == NULL)) { + if (argvars[1].v_type != VAR_DICT + || ((dict = argvars[1].vval.v_dict) == NULL)) { EMSG(_(e_dictreq)); return; } @@ -15652,30 +15639,29 @@ static void f_sign_getplaced(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } } - if ((di = tv_dict_find(dict, "group", -1)) != NULL) - { + if ((di = tv_dict_find(dict, "group", -1)) != NULL) { group = tv_get_string_chk(&di->di_tv); - if (group == NULL) + if (group == NULL) { return; - if (*group == '\0') // empty string means global group + } + if (*group == '\0') { // empty string means global group group = NULL; + } } } } - sign_get_placed(buf, lnum, sign_id, (const char_u*)group, rettv->vval.v_list); + sign_get_placed(buf, lnum, sign_id, (const char_u *)group, + rettv->vval.v_list); } -/* - * "sign_jump()" function - */ - static void -f_sign_jump(typval_T *argvars, typval_T *rettv, FunPtr fptr) +/// "sign_jump()" function +static void f_sign_jump(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int sign_id; + int sign_id; char *sign_group = NULL; buf_T *buf; - bool notanum = FALSE; + bool notanum = false; rettv->vval.v_number = -1; @@ -15684,18 +15670,18 @@ f_sign_jump(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (notanum) { return; } - if (sign_id <= 0) - { + if (sign_id <= 0) { EMSG(_(e_invarg)); return; } // Sign group - const char* sign_group_chk = tv_get_string_chk(&argvars[1]); - if (sign_group_chk == NULL) + const char * sign_group_chk = tv_get_string_chk(&argvars[1]); + if (sign_group_chk == NULL) { return; + } if (sign_group_chk[0] == '\0') { - sign_group = NULL; // global sign group + sign_group = NULL; // global sign group } else { sign_group = xstrdup(sign_group_chk); if (sign_group == NULL) { @@ -15705,18 +15691,17 @@ f_sign_jump(typval_T *argvars, typval_T *rettv, FunPtr fptr) // Buffer to place the sign buf = get_buf_arg(&argvars[2]); - if (buf == NULL) + if (buf == NULL) { goto cleanup; + } - rettv->vval.v_number = sign_jump(sign_id, (char_u*)sign_group, buf); + rettv->vval.v_number = sign_jump(sign_id, (char_u *)sign_group, buf); cleanup: xfree(sign_group); } -/* - * "sign_place()" function - */ +/// "sign_place()" function static void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int sign_id; @@ -15727,7 +15712,7 @@ static void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr) dictitem_T *di; linenr_T lnum = 0; int prio = SIGN_DEF_PRIO; - bool notanum = FALSE; + bool notanum = false; rettv->vval.v_number = -1; @@ -15747,9 +15732,9 @@ static void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } if (group_chk[0] == '\0') { - group = NULL; // global sign group + group = NULL; // global sign group } else { - group = vim_strsave((const char_u*)group_chk); + group = vim_strsave((const char_u *)group_chk); if (group == NULL) { return; } @@ -15768,8 +15753,8 @@ static void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr) } if (argvars[4].v_type != VAR_UNKNOWN) { - if (argvars[4].v_type != VAR_DICT || - ((dict = argvars[4].vval.v_dict) == NULL)) { + if (argvars[4].v_type != VAR_DICT + || ((dict = argvars[4].vval.v_dict) == NULL)) { EMSG(_(e_dictreq)); goto cleanup; } @@ -15791,7 +15776,8 @@ static void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - if (sign_place(&sign_id, group, (const char_u*)sign_name, buf, lnum, prio) == OK) { + if (sign_place(&sign_id, group, (const char_u *)sign_name, buf, lnum, prio) + == OK) { rettv->vval.v_number = sign_id; } @@ -15799,86 +15785,75 @@ cleanup: xfree(group); } -/* - * "sign_undefine()" function - */ - static void -f_sign_undefine(typval_T *argvars, typval_T *rettv, FunPtr fptr) +/// "sign_undefine()" function +static void f_sign_undefine(typval_T *argvars, typval_T *rettv, FunPtr fptr) { const char *name; rettv->vval.v_number = -1; - if (argvars[0].v_type == VAR_UNKNOWN) - { + if (argvars[0].v_type == VAR_UNKNOWN) { // Free all the signs free_signs(); rettv->vval.v_number = 0; - } - else - { + } else { // Free only the specified sign name = tv_get_string_chk(&argvars[0]); - if (name == NULL) + if (name == NULL) { return; + } - if (sign_undefine_by_name((const char_u*)name) == OK) { + if (sign_undefine_by_name((const char_u *)name) == OK) { rettv->vval.v_number = 0; } } } -/* - * "sign_unplace()" function - */ +/// "sign_unplace()" function static void f_sign_unplace(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - dict_T *dict; - dictitem_T *di; - int sign_id = 0; - buf_T *buf = NULL; - char_u *group = NULL; + dict_T *dict; + dictitem_T *di; + int sign_id = 0; + buf_T *buf = NULL; + char_u *group = NULL; rettv->vval.v_number = -1; - if (argvars[0].v_type != VAR_STRING) - { + if (argvars[0].v_type != VAR_STRING) { EMSG(_(e_invarg)); return; } - const char* group_chk = tv_get_string(&argvars[0]); + const char *group_chk = tv_get_string(&argvars[0]); if (group_chk[0] == '\0') { - group = NULL; // global sign group + group = NULL; // global sign group } else { - group = vim_strsave((const char_u*)group_chk); + group = vim_strsave((const char_u *)group_chk); if (group == NULL) { return; } } - if (argvars[1].v_type != VAR_UNKNOWN) - { - if (argvars[1].v_type != VAR_DICT) - { + if (argvars[1].v_type != VAR_UNKNOWN) { + if (argvars[1].v_type != VAR_DICT) { EMSG(_(e_dictreq)); goto cleanup; } dict = argvars[1].vval.v_dict; - if ((di = tv_dict_find(dict, "buffer", -1)) != NULL) - { + if ((di = tv_dict_find(dict, "buffer", -1)) != NULL) { buf = get_buf_arg(&di->di_tv); - if (buf == NULL) + if (buf == NULL) { goto cleanup; + } } if (tv_dict_find(dict, "id", -1) != NULL) { sign_id = tv_dict_get_number(dict, "id"); } } - if (buf == NULL) - { + if (buf == NULL) { // Delete the sign in all the buffers FOR_ALL_BUFFERS(cbuf) { if (sign_unplace(sign_id, group, cbuf, 0) == OK) { diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 3c45a1ad2e..f97ae8778d 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -2833,13 +2833,12 @@ buf_write ( // XFREE_CLEAR(backup); // no backup file to delete } else if (!p_bk) { - /* - * We are not going to keep the backup file, so don't - * delete an existing one, and try to use another name instead. - * Change one character, just before the extension. - */ + // We are not going to keep the backup file, so don't + // delete an existing one, and try to use another name instead. + // Change one character, just before the extension. + // wp = backup + STRLEN(backup) - 1 - STRLEN(backup_ext); - if (wp < backup) { /* empty file name ??? */ + if (wp < backup) { // empty file name ??? wp = backup; } *wp = 'z'; @@ -2847,7 +2846,7 @@ buf_write ( && os_fileinfo((char *)backup, &file_info_new)) { --*wp; } - /* They all exist??? Must be something wrong. */ + // They all exist??? Must be something wrong. if (*wp == 'a') { XFREE_CLEAR(backup); } @@ -2969,18 +2968,17 @@ nobackup: } } if (backup != NULL) { - /* - * Delete any existing backup and move the current version - * to the backup. For safety, we don't remove the backup - * until the write has finished successfully. And if the - * 'backup' option is set, leave it around. - */ - /* - * If the renaming of the original file to the backup file - * works, quit here. - */ - if (vim_rename(fname, backup) == 0) + // Delete any existing backup and move the current version + // to the backup. For safety, we don't remove the backup + // until the write has finished successfully. And if the + // 'backup' option is set, leave it around. + + // If the renaming of the original file to the backup file + // works, quit here. + /// + if (vim_rename(fname, backup) == 0) { break; + } XFREE_CLEAR(backup); // don't do the rename below } @@ -2993,7 +2991,7 @@ nobackup: } #if defined(UNIX) - /* When using ":w!" and the file was read-only: make it writable */ + // When using ":w!" and the file was read-only: make it writable if (forceit && perm >= 0 && !(perm & 0200) && file_info_old.stat.st_uid == getuid() && vim_strchr(p_cpo, CPO_FWRITE) == NULL) { @@ -3003,12 +3001,12 @@ nobackup: } #endif - /* When using ":w!" and writing to the current file, 'readonly' makes no - * sense, reset it, unless 'Z' appears in 'cpoptions'. */ + // When using ":w!" and writing to the current file, 'readonly' makes no + // sense, reset it, unless 'Z' appears in 'cpoptions'. if (forceit && overwriting && vim_strchr(p_cpo, CPO_KEEPRO) == NULL) { - buf->b_p_ro = FALSE; - need_maketitle = TRUE; /* set window title later */ - status_redraw_all(); /* redraw status lines later */ + buf->b_p_ro = false; + need_maketitle = true; // set window title later + status_redraw_all(); // redraw status lines later } if (end > buf->b_ml.ml_line_count) @@ -3016,13 +3014,11 @@ nobackup: if (buf->b_ml.ml_flags & ML_EMPTY) start = end + 1; - /* - * If the original file is being overwritten, there is a small chance that - * we crash in the middle of writing. Therefore the file is preserved now. - * This makes all block numbers positive so that recovery does not need - * the original file. - * Don't do this if there is a backup file and we are exiting. - */ + // If the original file is being overwritten, there is a small chance that + // we crash in the middle of writing. Therefore the file is preserved now. + // This makes all block numbers positive so that recovery does not need + // the original file. + // Don't do this if there is a backup file and we are exiting. if (reset_changed && !newfile && overwriting && !(exiting && backup != NULL)) { ml_preserve(buf, false, !!p_fs); @@ -3033,36 +3029,34 @@ nobackup: } - /* Default: write the file directly. May write to a temp file for - * multi-byte conversion. */ + // Default: write the file directly. May write to a temp file for + // multi-byte conversion. wfname = fname; - /* Check for forced 'fileencoding' from "++opt=val" argument. */ + // Check for forced 'fileencoding' from "++opt=val" argument. if (eap != NULL && eap->force_enc != 0) { fenc = eap->cmd + eap->force_enc; fenc = enc_canonize(fenc); fenc_tofree = fenc; - } else + } else { fenc = buf->b_p_fenc; + } - /* - * Check if the file needs to be converted. - */ + // Check if the file needs to be converted. converted = need_conversion(fenc); - /* - * Check if UTF-8 to UCS-2/4 or Latin1 conversion needs to be done. Or - * Latin1 to Unicode conversion. This is handled in buf_write_bytes(). - * Prepare the flags for it and allocate bw_conv_buf when needed. - */ + // Check if UTF-8 to UCS-2/4 or Latin1 conversion needs to be done. Or + // Latin1 to Unicode conversion. This is handled in buf_write_bytes(). + // Prepare the flags for it and allocate bw_conv_buf when needed. if (converted && (enc_utf8 || STRCMP(p_enc, "latin1") == 0)) { wb_flags = get_fio_flags(fenc); if (wb_flags & (FIO_UCS2 | FIO_UCS4 | FIO_UTF16 | FIO_UTF8)) { - /* Need to allocate a buffer to translate into. */ - if (wb_flags & (FIO_UCS2 | FIO_UTF16 | FIO_UTF8)) + // Need to allocate a buffer to translate into. + if (wb_flags & (FIO_UCS2 | FIO_UTF16 | FIO_UTF8)) { write_info.bw_conv_buflen = bufsize * 2; - else /* FIO_UCS4 */ + } else { // FIO_UCS4 write_info.bw_conv_buflen = bufsize * 4; + } write_info.bw_conv_buf = verbose_try_malloc(write_info.bw_conv_buflen); if (!write_info.bw_conv_buf) { end = 0; @@ -3074,10 +3068,8 @@ nobackup: if (converted && wb_flags == 0) { # ifdef USE_ICONV - /* - * Use iconv() conversion when conversion is needed and it's not done - * internally. - */ + // Use iconv() conversion when conversion is needed and it's not done + // internally. write_info.bw_iconv_fd = (iconv_t)my_iconv_open(fenc, enc_utf8 ? (char_u *)"utf-8" : p_enc); if (write_info.bw_iconv_fd != (iconv_t)-1) { diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 8a6f0accda..3597467766 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -484,7 +484,7 @@ EXTERN buf_T *curbuf INIT(= NULL); // currently active buffer // 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->next) + for (sign = buf->b_signlist; sign != NULL; sign = sign->next) // NOLINT /* diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index 84721ee96c..a3eabed8a0 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -1524,12 +1524,10 @@ static void cs_fill_results(char *tagstr, size_t totmatches, int *nummatches_a, } totsofar++; - - } /* for all matches */ + } // for all matches (void)cs_read_prompt(i); - - } /* for all cscope connections */ + } // for all cscope connections if (totsofar == 0) { // No matches, free the arrays and return NULL in "*matches_p". @@ -1541,20 +1539,25 @@ static void cs_fill_results(char *tagstr, size_t totmatches, int *nummatches_a, *cntxts_p = cntxts; xfree(buf); -} /* cs_fill_results */ +} // cs_fill_results /* get the requested path components */ static char *cs_pathcomponents(char *path) { - if (p_cspc == 0) + if (p_cspc == 0) { return path; + } char *s = path + strlen(path) - 1; - for (int i = 0; i < p_cspc; ++i) - while (s > path && *--s != '/') continue; - if ((s > path && *s == '/')) - ++s; + for (int i = 0; i < p_cspc; i++) { + while (s > path && *--s != '/') { + continue; + } + } + if ((s > path && *s == '/')) { + s++; + } return s; } diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index c161bad66f..683087bd7b 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -2085,31 +2085,31 @@ static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, to = (char *)result + done; tolen = len - done - 2; - /* Avoid a warning for systems with a wrong iconv() prototype by - * casting the second argument to void *. */ + // Avoid a warning for systems with a wrong iconv() prototype by + // casting the second argument to void *. if (iconv(vcp->vc_fd, (void *)&from, &fromlen, &to, &tolen) != SIZE_MAX) { - /* Finished, append a NUL. */ + // Finished, append a NUL. *to = NUL; break; } - /* Check both ICONV_EINVAL and EINVAL, because the dynamically loaded - * iconv library may use one of them. */ + // Check both ICONV_EINVAL and EINVAL, because the dynamically loaded + // iconv library may use one of them. if (!vcp->vc_fail && unconvlenp != NULL && (ICONV_ERRNO == ICONV_EINVAL || ICONV_ERRNO == EINVAL)) { - /* Handle an incomplete sequence at the end. */ + // Handle an incomplete sequence at the end. *to = NUL; *unconvlenp = fromlen; break; - } - /* Check both ICONV_EILSEQ and EILSEQ, because the dynamically loaded - * iconv library may use one of them. */ - else if (!vcp->vc_fail - && (ICONV_ERRNO == ICONV_EILSEQ || ICONV_ERRNO == EILSEQ - || ICONV_ERRNO == ICONV_EINVAL || ICONV_ERRNO == EINVAL)) { - /* Can't convert: insert a '?' and skip a character. This assumes - * conversion from 'encoding' to something else. In other - * situations we don't know what to skip anyway. */ + } else if (!vcp->vc_fail + && (ICONV_ERRNO == ICONV_EILSEQ || ICONV_ERRNO == EILSEQ + || ICONV_ERRNO == ICONV_EINVAL || ICONV_ERRNO == EINVAL)) { + // Check both ICONV_EILSEQ and EILSEQ, because the dynamically loaded + // iconv library may use one of them. + + // Can't convert: insert a '?' and skip a character. This assumes + // conversion from 'encoding' to something else. In other + // situations we don't know what to skip anyway. *to++ = '?'; if (utf_ptr2cells((char_u *)from) > 1) { *to++ = '?'; @@ -2122,7 +2122,7 @@ static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, XFREE_CLEAR(result); break; } - /* Not enough room or skipping illegal sequence. */ + // Not enough room or skipping illegal sequence. done = to - (char *)result; } @@ -2132,11 +2132,9 @@ static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, } # if defined(DYNAMIC_ICONV) -/* - * Dynamically load the "iconv.dll" on Win32. - */ +// Dynamically load the "iconv.dll" on Win32. -#ifndef DYNAMIC_ICONV /* just generating prototypes */ +#ifndef DYNAMIC_ICONV // just generating prototypes # define HINSTANCE int #endif static HINSTANCE hIconvDLL = 0; diff --git a/src/nvim/memline.c b/src/nvim/memline.c index a4d2feb5e3..a69669f680 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -3356,47 +3356,38 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, break; } - /* - * A file name equal to old_fname is OK to use. - */ - if (old_fname != NULL && fnamecmp(fname, old_fname) == 0) + // A file name equal to old_fname is OK to use. + if (old_fname != NULL && fnamecmp(fname, old_fname) == 0) { break; + } - /* - * get here when file already exists - */ - if (fname[n - 2] == 'w' && fname[n - 1] == 'p') { /* first try */ - /* - * If we get here the ".swp" file really exists. - * Give an error message, unless recovering, no file name, we are - * viewing a help file or when the path of the file is different - * (happens when all .swp files are in one directory). - */ + // get here when file already exists + if (fname[n - 2] == 'w' && fname[n - 1] == 'p') { // first try + // If we get here the ".swp" file really exists. + // Give an error message, unless recovering, no file name, we are + // viewing a help file or when the path of the file is different + // (happens when all .swp files are in one directory). if (!recoverymode && buf_fname != NULL && !buf->b_help && !(buf->b_flags & BF_DUMMY)) { int fd; struct block0 b0; int differ = FALSE; - /* - * Try to read block 0 from the swap file to get the original - * file name (and inode number). - */ + // Try to read block 0 from the swap file to get the original + // file name (and inode number). fd = os_open(fname, O_RDONLY, 0); if (fd >= 0) { if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) { - /* - * If the swapfile has the same directory as the - * buffer don't compare the directory names, they can - * have a different mountpoint. - */ + // If the swapfile has the same directory as the + // buffer don't compare the directory names, they can + // have a different mountpoint. if (b0.b0_flags & B0_SAME_DIR) { if (fnamecmp(path_tail(buf->b_ffname), path_tail(b0.b0_fname)) != 0 - || !same_directory((char_u *) fname, buf->b_ffname)) { - /* Symlinks may point to the same file even - * when the name differs, need to check the - * inode too. */ + || !same_directory((char_u *)fname, buf->b_ffname)) { + // Symlinks may point to the same file even + // when the name differs, need to check the + // inode too. expand_env(b0.b0_fname, NameBuff, MAXPATHL); if (fnamecmp_ino(buf->b_ffname, NameBuff, char_to_long(b0.b0_ino))) { @@ -3404,10 +3395,8 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, } } } else { - /* - * The name in the swap file may be - * "~user/path/file". Expand it first. - */ + // The name in the swap file may be + // "~user/path/file". Expand it first. expand_env(b0.b0_fname, NameBuff, MAXPATHL); if (fnamecmp_ino(buf->b_ffname, NameBuff, char_to_long(b0.b0_ino))) { @@ -3418,9 +3407,9 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, close(fd); } - /* give the ATTENTION message when there is an old swap file - * for the current file, and the buffer was not recovered. */ - if (differ == FALSE && !(curbuf->b_flags & BF_RECOVERED) + // give the ATTENTION message when there is an old swap file + // for the current file, and the buffer was not recovered. */ + if (differ == false && !(curbuf->b_flags & BF_RECOVERED) && vim_strchr(p_shm, SHM_ATTENTION) == NULL) { int choice = 0; diff --git a/src/nvim/message.c b/src/nvim/message.c index 077c28eb2c..190a268be7 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1082,16 +1082,14 @@ void wait_return(int redraw) /* Put the character back in the typeahead buffer. Don't use the * stuff buffer, because lmaps wouldn't work. */ ins_char_typebuf(c); - do_redraw = TRUE; /* need a redraw even though there is - typeahead */ + do_redraw = true; // need a redraw even though there is + // typeahead } } - redir_off = FALSE; + redir_off = false; - /* - * If the user hits ':', '?' or '/' we get a command line from the next - * line. - */ + // If the user hits ':', '?' or '/' we get a command line from the next + // line. if (c == ':' || c == '?' || c == '/') { if (!exmode_active) cmdline_row = msg_row; @@ -1100,19 +1098,17 @@ void wait_return(int redraw) msg_ext_keep_after_cmdline = true; } - /* - * If the window size changed set_shellsize() will redraw the screen. - * Otherwise the screen is only redrawn if 'redraw' is set and no ':' - * typed. - */ + // If the window size changed set_shellsize() will redraw the screen. + // Otherwise the screen is only redrawn if 'redraw' is set and no ':' + // typed. tmpState = State; - State = oldState; /* restore State before set_shellsize */ + State = oldState; // restore State before set_shellsize setmouse(); msg_check(); - need_wait_return = FALSE; - did_wait_return = TRUE; - emsg_on_display = FALSE; /* can delete error message now */ - lines_left = -1; /* reset lines_left at next msg_start() */ + need_wait_return = false; + did_wait_return = true; + emsg_on_display = false; // can delete error message now + lines_left = -1; // reset lines_left at next msg_start() reset_last_sourcing(); if (keep_msg != NULL && vim_strsize(keep_msg) >= (Rows - cmdline_row - 1) * Columns + sc_col) { @@ -1183,25 +1179,25 @@ void msg_ext_set_kind(const char *msg_kind) */ void msg_start(void) { - int did_return = FALSE; + int did_return = false; if (!msg_silent) { XFREE_CLEAR(keep_msg); // don't display old message now } if (need_clr_eos) { - /* Halfway an ":echo" command and getting an (error) message: clear - * any text from the command. */ - need_clr_eos = FALSE; + // Halfway an ":echo" command and getting an (error) message: clear + // any text from the command. + need_clr_eos = false; msg_clr_eos(); } - if (!msg_scroll && full_screen) { /* overwrite last message */ + if (!msg_scroll && full_screen) { // overwrite last message msg_row = cmdline_row; msg_col = cmdmsg_rl ? Columns - 1 : 0; - } else if (msg_didout) { /* start message on next line */ + } else if (msg_didout) { // start message on next line msg_putchar('\n'); did_return = TRUE; if (exmode_active != EXMODE_NORMAL) @@ -1210,7 +1206,7 @@ void msg_start(void) if (!msg_didany || lines_left < 0) msg_starthere(); if (msg_silent == 0) { - msg_didout = FALSE; /* no output on current line yet */ + msg_didout = false; // no output on current line yet } if (ui_has(kUIMessages)) { @@ -2990,12 +2986,13 @@ int verbose_open(void) */ void give_warning(char_u *message, bool hl) FUNC_ATTR_NONNULL_ARG(1) { - /* Don't do this for ":silent". */ - if (msg_silent != 0) + // Don't do this for ":silent". + if (msg_silent != 0) { return; + } - /* Don't want a hit-enter prompt here. */ - ++no_wait_return; + // Don't want a hit-enter prompt here. + no_wait_return++; set_vim_var_string(VV_WARNINGMSG, (char *)message, -1); XFREE_CLEAR(keep_msg); @@ -3013,7 +3010,7 @@ void give_warning(char_u *message, bool hl) FUNC_ATTR_NONNULL_ARG(1) msg_nowait = true; // Don't wait for this message. msg_col = 0; - --no_wait_return; + no_wait_return--; } void give_warning2(char_u *const message, char_u *const a1, bool hl) diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 50abd226fc..d6e78d9ce4 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -4213,10 +4213,10 @@ dozet: set_fraction(curwin); break; - /* "z^", "z-" and "zb": put cursor at bottom of screen */ - case '^': /* Strange Vi behavior: z^ finds line at top of window - * when is at bottom of window, and puts that one at - * bottom of window. */ + // "z^", "z-" and "zb": put cursor at bottom of screen + case '^': // Strange Vi behavior: z^ finds line at top of window + // when is at bottom of window, and puts that one at + // bottom of window. if (cap->count0 != 0) { scroll_cursor_bot(0, true); curwin->w_cursor.lnum = curwin->w_topline; diff --git a/src/nvim/search.c b/src/nvim/search.c index 3bd222b3de..e64233985b 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -87,13 +87,13 @@ static struct spat spats[2] = static int last_idx = 0; /* index in spats[] for RE_LAST */ -static char_u lastc[2] = {NUL, NUL}; /* last character searched for */ -static int lastcdir = FORWARD; /* last direction of character search */ -static int last_t_cmd = TRUE; /* last search t_cmd */ +static char_u lastc[2] = { NUL, NUL }; // last character searched for +static int lastcdir = FORWARD; // last direction of character search +static int last_t_cmd = true; // last search t_cmd static char_u lastc_bytes[MB_MAXBYTES + 1]; -static int lastc_bytelen = 1; /* >1 for multi-byte char */ +static int lastc_bytelen = 1; // >1 for multi-byte char -/* copy of spats[], for keeping the search patterns while executing autocmds */ +// copy of spats[], for keeping the search patterns while executing autocmds static struct spat saved_spats[2]; // copy of spats[RE_SEARCH], for keeping the search patterns while incremental // searching @@ -101,8 +101,8 @@ static struct spat saved_last_search_spat; static int saved_last_idx = 0; static int saved_no_hlsearch = 0; -static char_u *mr_pattern = NULL; /* pattern used by search_regcomp() */ -static int mr_pattern_alloced = FALSE; /* mr_pattern was allocated */ +static char_u *mr_pattern = NULL; // pattern used by search_regcomp() +static int mr_pattern_alloced = false; // mr_pattern was allocated /* * Type used by find_pattern_in_path() to remember which included files have diff --git a/src/nvim/sign.c b/src/nvim/sign.c index 68d013f298..ac26fd0137 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -1,15 +1,10 @@ -/* vi:set ts=8 sts=4 sw=4 noet: - * - * VIM - Vi IMproved by Bram Moolenaar - * - * Do ":help uganda" in Vim to read copying and usage conditions. - * Do ":help credits" in Vim to see a list of people who contributed. - * See README.txt for an overview of the Vim source code. - */ - -/* - * sign.c: functions for managing signs - */ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +// +// sign.c: functions for managing with signs +// + #include "nvim/vim.h" #include "nvim/sign.h" @@ -24,9 +19,7 @@ #include "nvim/screen.h" #include "nvim/syntax.h" -/* - * Struct to hold the sign properties. - */ +/// Struct to hold the sign properties. typedef struct sign sign_T; struct sign @@ -36,7 +29,7 @@ struct sign char_u *sn_name; // name of sign char_u *sn_icon; // name of pixmap # ifdef FEAT_SIGN_ICONS - void *sn_image; // icon image + void *sn_image; // icon image # endif char_u *sn_text; // text used instead of pixmap int sn_line_hl; // highlight ID for line @@ -44,68 +37,62 @@ struct sign int sn_num_hl; // highlight ID for line number }; -static sign_T *first_sign = NULL; -static int next_sign_typenr = 1; +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 char *cmds[] = { - "define", -#define SIGNCMD_DEFINE 0 - "undefine", + "define", +#define SIGNCMD_DEFINE 0 + "undefine", #define SIGNCMD_UNDEFINE 1 - "list", -#define SIGNCMD_LIST 2 - "place", -#define SIGNCMD_PLACE 3 - "unplace", -#define SIGNCMD_UNPLACE 4 - "jump", -#define SIGNCMD_JUMP 5 - NULL -#define SIGNCMD_LAST 6 + "list", +#define SIGNCMD_LIST 2 + "place", +#define SIGNCMD_PLACE 3 + "unplace", +#define SIGNCMD_UNPLACE 4 + "jump", +#define SIGNCMD_JUMP 5 + NULL +#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 +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) +/// Initialize data needed for managing signs +void init_signs(void) { - hash_init(&sg_table); // sign group hash table + 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. - */ +/// 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_u *groupname) { - hash_T hash; - hashitem_T *hi; - signgroup_T *group; + hash_T hash; + hashitem_T *hi; + signgroup_T *group; hash = hash_hash(groupname); - hi = hash_lookup(&sg_table, (char*)groupname, STRLEN(groupname), hash); - if (HASHITEM_EMPTY(hi)) - { + hi = hash_lookup(&sg_table, (char *)groupname, STRLEN(groupname), hash); + if (HASHITEM_EMPTY(hi)) { // new group group = (signgroup_T *)xmalloc( (unsigned)(sizeof(signgroup_T) + STRLEN(groupname))); - if (group == NULL) + if (group == NULL) { return NULL; + } STRCPY(group->sg_name, groupname); group->refcount = 1; - group->next_sign_id = 1; + group->next_sign_id = 1; hash_add_item(&sg_table, hi, group->sg_name, hash); - } - else - { + } else { // existing group group = HI2SG(hi); group->refcount++; @@ -114,22 +101,18 @@ static signgroup_T * sign_group_ref(const char_u *groupname) return group; } -/* - * A sign in group 'groupname' is removed. If all the signs in this group are - * removed, then remove the 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_u *groupname) { - hashitem_T *hi; - signgroup_T *group; + hashitem_T *hi; + signgroup_T *group; hi = hash_find(&sg_table, groupname); - if (!HASHITEM_EMPTY(hi)) - { + if (!HASHITEM_EMPTY(hi)) { group = HI2SG(hi); group->refcount--; - if (group->refcount == 0) - { + if (group->refcount == 0) { // All the signs in this group are removed hash_remove(&sg_table, hi); xfree(group); @@ -137,66 +120,57 @@ static void sign_group_unref(char_u *groupname) } } -/* - * Returns 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. - */ +/// Returns 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. int sign_in_group(signlist_T *sign, const char_u *group) { return ((group != NULL && STRCMP(group, "*") == 0) - || (group == NULL && sign->group == NULL) - || (group != NULL && sign->group != NULL - && STRCMP(group, sign->group->sg_name) == 0)); + || (group == NULL && sign->group == NULL) + || (group != NULL && sign->group != NULL + && STRCMP(group, sign->group->sg_name) == 0)); } -/* - * Get the next free sign identifier in the specified group - */ - int -sign_group_get_next_signid(buf_T *buf, const char_u *groupname) +/// Get the next free sign identifier in the specified group +int sign_group_get_next_signid(buf_T *buf, const char_u *groupname) { - int id = 1; - signgroup_T *group = NULL; - signlist_T *sign; - hashitem_T *hi; - int found = FALSE; - - if (groupname != NULL) - { - hi = hash_find(&sg_table, groupname); - if (HASHITEM_EMPTY(hi)) - return id; - group = HI2SG(hi); - } - - // Search for the next usuable sign identifier - while (!found) - { - if (group == NULL) - id = next_sign_id++; // global group - else - id = group->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->id && sign_in_group(sign, groupname)) - { - found = FALSE; // sign identifier is in use - break; - } - } - } - - return id; + int id = 1; + signgroup_T *group = NULL; + signlist_T *sign; + hashitem_T *hi; + int found = false; + + if (groupname != NULL) { + hi = hash_find(&sg_table, groupname); + if (HASHITEM_EMPTY(hi)) { + return id; + } + group = HI2SG(hi); + } + + // Search for the next usuable sign identifier + while (!found) { + if (group == NULL) { + id = next_sign_id++; // global group + } else { + id = group->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->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. - */ +/// Insert a new sign into the signlist for buffer 'buf' between the 'prev' and +/// 'next' signs. static void insert_sign( buf_T *buf, // buffer to store sign in signlist_T *prev, // previous sign entry @@ -208,59 +182,54 @@ static void insert_sign( int typenr // typenr of sign we are adding ) { - signlist_T *newsign = xmalloc(sizeof(signlist_T)); - newsign->id = id; - newsign->lnum = lnum; - newsign->typenr = typenr; - if (group != NULL) - { - newsign->group = sign_group_ref(group); - if (newsign->group == NULL) - { - xfree(newsign); - return; - } - } - else - newsign->group = NULL; - newsign->priority = prio; - newsign->next = next; - newsign->prev = prev; - if (next != NULL) { - next->prev = newsign; + signlist_T *newsign = xmalloc(sizeof(signlist_T)); + newsign->id = id; + newsign->lnum = lnum; + newsign->typenr = typenr; + if (group != NULL) { + newsign->group = sign_group_ref(group); + if (newsign->group == NULL) { + xfree(newsign); + return; } - buf->b_signcols_max = -1; - - 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, NOT_VALID); - changed_cline_bef_curs(); - } + } else { + newsign->group = NULL; + } + newsign->priority = prio; + newsign->next = next; + newsign->prev = prev; + if (next != NULL) { + next->prev = newsign; + } + buf->b_signcols_max = -1; - // first sign in signlist - buf->b_signlist = newsign; - } - else { - prev->next = 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, NOT_VALID); + changed_cline_bef_curs(); } + + // first sign in signlist + buf->b_signlist = newsign; + } else { + prev->next = newsign; + } } -/* - * Insert a new sign sorted by line number and sign priority. - */ +/// Insert a new sign sorted by line number and sign priority. static void insert_sign_by_lnum_prio( - buf_T *buf, // buffer to store sign in - signlist_T *prev, // previous sign entry - int id, // sign ID - const char_u *group, // sign group; NULL for global group - int prio, // sign priority - linenr_T lnum, // line number which gets the mark - int typenr // typenr of sign we are adding + buf_T *buf, // buffer to store sign in + signlist_T *prev, // previous sign entry + int id, // sign ID + const char_u *group, // sign group; NULL for global group + int prio, // sign priority + linenr_T lnum, // line number which gets the mark + int typenr // typenr of sign we are adding ) { - signlist_T *sign; + signlist_T *sign; // keep signs sorted by lnum and by priority: insert new sign at // the proper position in the list for this lnum. @@ -276,69 +245,66 @@ static void insert_sign_by_lnum_prio( insert_sign(buf, prev, sign, id, group, prio, lnum, typenr); } -/* - * Get the name of a sign by its typenr. - */ +/// Get the name of a sign by its typenr. char_u * sign_typenr2name(int typenr) { sign_T *sp; - for (sp = first_sign; sp != NULL; sp = sp->sn_next) - if (sp->sn_typenr == typenr) + for (sp = first_sign; sp != NULL; sp = sp->sn_next) { + if (sp->sn_typenr == typenr) { return sp->sn_name; + } + } return (char_u *)_("[Deleted]"); } -/* - * Return information about a sign in a Dict - */ +/// Return information about a sign in a Dict dict_T * sign_get_info(signlist_T *sign) { - dict_T *d; + dict_T *d; if ((d = tv_dict_alloc()) == NULL) { return NULL; } tv_dict_add_nr(d, S_LEN("id"), sign->id); - tv_dict_add_str(d, S_LEN("group"), - (sign->group == NULL) ? (char*)"" : (char*)sign->group->sg_name); + tv_dict_add_str(d, S_LEN("group"), ((sign->group == NULL) + ? (char *)"" + : (char *)sign->group->sg_name)); tv_dict_add_nr(d, S_LEN("lnum"), sign->lnum); - tv_dict_add_str(d, S_LEN("name"), (char*)sign_typenr2name(sign->typenr)); + tv_dict_add_str(d, S_LEN("name"), (char *)sign_typenr2name(sign->typenr)); tv_dict_add_nr(d, S_LEN("priority"), sign->priority); return d; } -/* - * Add the sign into the signlist. Find the right spot to do it though. - */ +/// Add the sign into the signlist. Find the right spot to do it though. void buf_addsign( buf_T *buf, // buffer to store sign in int id, // sign ID - const char_u *groupname, // sign group + const char_u *groupname, // sign group int prio, // sign priority linenr_T lnum, // line number which gets the mark int typenr // typenr of sign we are adding ) { - signlist_T *sign; // a sign in the signlist - signlist_T *prev; // the previous sign + signlist_T *sign; // a sign in the signlist + signlist_T *prev; // the previous sign - prev = NULL; - FOR_ALL_SIGNS_IN_BUF(buf, sign) { - if (lnum == sign->lnum && id == sign->id - && sign_in_group(sign, groupname)) { - // Update an existing sign - sign->typenr = typenr; - return; - } else if (lnum < sign->lnum) { - insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr); - return; - } - prev = sign; + prev = NULL; + FOR_ALL_SIGNS_IN_BUF(buf, sign) { + if (lnum == sign->lnum && id == sign->id + && sign_in_group(sign, groupname)) { + // Update an existing sign + sign->typenr = typenr; + return; + } else if (lnum < sign->lnum) { + insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr); + return; } + prev = sign; + } - insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr); + insert_sign_by_lnum_prio(buf, prev, id, groupname, prio, lnum, typenr); } // For an existing, placed sign "markId" change the type to "typenr". @@ -384,13 +350,13 @@ int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type, FOR_ALL_SIGNS_IN_BUF(buf, sign) { if (sign->lnum == lnum - && (type == SIGN_ANY - || (type == SIGN_TEXT - && sign_get_text(sign->typenr) != NULL) - || (type == SIGN_LINEHL - && sign_get_attr(sign->typenr, SIGN_LINEHL) != 0) - || (type == SIGN_NUMHL - && sign_get_attr(sign->typenr, SIGN_NUMHL) != 0))) { + && (type == SIGN_ANY + || (type == SIGN_TEXT + && sign_get_text(sign->typenr) != NULL) + || (type == SIGN_LINEHL + && sign_get_attr(sign->typenr, SIGN_LINEHL) != 0) + || (type == SIGN_NUMHL + && sign_get_attr(sign->typenr, SIGN_NUMHL) != 0))) { matches[nr_matches] = sign; nr_matches++; // signlist is sorted with most important (priority, id), thus we @@ -412,22 +378,20 @@ int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type, return 0; } -/* - * 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. - * Returns the line number of the deleted sign. If multiple signs are deleted, - * then returns the line number of the last sign deleted. - */ +/// 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. +/// Returns the line number of the deleted sign. If multiple signs are deleted, +/// then returns the line number of the last sign deleted. linenr_T buf_delsign( - buf_T *buf, // buffer sign is stored in - linenr_T atlnum, // sign at this line, 0 - at any line - int id, // sign id - char_u *group// sign group - ) + buf_T *buf, // buffer sign is stored in + linenr_T atlnum, // sign at this line, 0 - at any line + int id, // sign id + char_u *group // sign group +) { signlist_T **lastp; // pointer to pointer to current sign signlist_T *sign; // a sign in a b_signlist @@ -447,12 +411,13 @@ linenr_T buf_delsign( next->prev = sign->prev; } lnum = sign->lnum; - if (sign->group != NULL) + if (sign->group != NULL) { sign_group_unref(sign->group->sg_name); + } xfree(sign); redraw_buf_line_later(buf, lnum); // Check whether only one sign needs to be deleted - // If deleting a sign with a specific identifier in a particular + // 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 @@ -476,11 +441,9 @@ linenr_T buf_delsign( } -/* - * 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. - */ +/// 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. int buf_findsign( buf_T *buf, // buffer to store sign in int id, // sign ID @@ -490,7 +453,7 @@ int buf_findsign( signlist_T *sign; // a sign in the signlist FOR_ALL_SIGNS_IN_BUF(buf, sign) { - if (sign->id == id && sign_in_group(sign, group)){ + if (sign->id == id && sign_in_group(sign, group)) { return (int)sign->lnum; } } @@ -498,17 +461,15 @@ int buf_findsign( 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. - */ +/// 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. static signlist_T * buf_getsign_at_line( - buf_T *buf, // buffer whose sign we are searching for - linenr_T lnum, // line number of sign - char_u *groupname // sign group name + buf_T *buf, // buffer whose sign we are searching for + linenr_T lnum, // line number of sign + char_u *groupname // sign group name ) { - signlist_T *sign; // a sign in the signlist + signlist_T *sign; // a sign in the signlist FOR_ALL_SIGNS_IN_BUF(buf, sign) { if (sign->lnum == lnum && sign_in_group(sign, groupname)) { @@ -519,9 +480,7 @@ static signlist_T * buf_getsign_at_line( return NULL; } -/* - * Return the identifier of the sign at line number 'lnum' in buffer 'buf'. - */ +/// Return the identifier of the sign at line number 'lnum' in buffer 'buf'. int buf_findsign_id( buf_T *buf, // buffer whose sign we are searching for linenr_T lnum, // line number of sign @@ -538,18 +497,16 @@ int buf_findsign_id( return 0; } -/* - * Delete signs in buffer "buf". - */ +/// Delete signs in buffer "buf". void buf_delete_signs(buf_T *buf, char_u *group) { - signlist_T *sign; - signlist_T **lastp; // pointer to pointer to current sign + signlist_T *sign; + signlist_T **lastp; // pointer to pointer to current sign signlist_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_signlist != NULL && curwin != NULL) { changed_cline_bef_curs(); } @@ -572,98 +529,97 @@ void buf_delete_signs(buf_T *buf, char_u *group) buf->b_signcols_max = -1; } -/* - * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers. - */ +/// List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers. void sign_list_placed(buf_T *rbuf, char_u *sign_group) { - buf_T *buf; - signlist_T *sign; - char lbuf[MSG_BUF_LEN]; - char group[MSG_BUF_LEN]; - - MSG_PUTS_TITLE(_("\n--- Signs ---")); - msg_putchar('\n'); - if (rbuf == NULL) { - buf = firstbuf; - } else { - buf = rbuf; + buf_T *buf; + signlist_T *sign; + char lbuf[MSG_BUF_LEN]; + char group[MSG_BUF_LEN]; + + 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) { + vim_snprintf(lbuf, MSG_BUF_LEN, _("Signs for %s:"), buf->b_fname); + MSG_PUTS_ATTR(lbuf, HL_ATTR(HLF_D)); + msg_putchar('\n'); } - while (buf != NULL && !got_int) { - if (buf->b_signlist != NULL) { - 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->group != NULL) { - vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"), - sign->group->sg_name); - } else { - group[0] = '\0'; - } - vim_snprintf(lbuf, MSG_BUF_LEN, - _(" line=%ld id=%d%s name=%s priority=%d"), - (long)sign->lnum, sign->id, group, - sign_typenr2name(sign->typenr), sign->priority); - MSG_PUTS(lbuf); - msg_putchar('\n'); - } - if (rbuf != NULL) { - break; - } - buf = buf->b_next; + FOR_ALL_SIGNS_IN_BUF(buf, sign) { + if (got_int) { + break; + } + if (!sign_in_group(sign, sign_group)) { + continue; + } + if (sign->group != NULL) { + vim_snprintf(group, MSG_BUF_LEN, _(" group=%s"), + sign->group->sg_name); + } else { + group[0] = '\0'; + } + vim_snprintf(lbuf, MSG_BUF_LEN, + _(" line=%ld id=%d%s name=%s priority=%d"), + (long)sign->lnum, sign->id, group, + sign_typenr2name(sign->typenr), sign->priority); + MSG_PUTS(lbuf); + msg_putchar('\n'); + } + if (rbuf != NULL) { + break; } + buf = buf->b_next; + } } -/* - * Adjust a placed sign for inserted/deleted lines. - */ -void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after) +/// Adjust a placed sign for inserted/deleted lines. +void sign_mark_adjust( + linenr_T line1, + linenr_T line2, + long amount, + long amount_after +) { - signlist_T *sign; // a sign in a b_signlist - linenr_T new_lnum; // new line number to assign to sign + signlist_T *sign; // a sign in a b_signlist + linenr_T new_lnum; // new line number to assign to sign - curbuf->b_signcols_max = -1; + curbuf->b_signcols_max = -1; - FOR_ALL_SIGNS_IN_BUF(curbuf, sign) { - new_lnum = sign->lnum; - if (sign->lnum >= line1 && sign->lnum <= line2) { - if (amount != MAXLNUM) { - new_lnum += amount; - } - } else if (sign->lnum > line2) { - new_lnum += amount_after; - } - // 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 (sign->lnum >= line1 && new_lnum <= curbuf->b_ml.ml_line_count) { - sign->lnum = new_lnum; - } + FOR_ALL_SIGNS_IN_BUF(curbuf, sign) { + new_lnum = sign->lnum; + if (sign->lnum >= line1 && sign->lnum <= line2) { + if (amount != MAXLNUM) { + new_lnum += amount; + } + } else if (sign->lnum > line2) { + new_lnum += amount_after; + } + // 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 (sign->lnum >= line1 && new_lnum <= curbuf->b_ml.ml_line_count) { + sign->lnum = new_lnum; } + } } -/* - * Find index of a ":sign" subcmd from its name. - * "*end_cmd" must be writable. - */ +/// Find index of a ":sign" subcmd from its name. +/// "*end_cmd" must be writable. static int sign_cmd_idx( char_u *begin_cmd, // begin of sign subcmd char_u *end_cmd // just after sign subcmd - ) +) { int idx; char_u save = *end_cmd; *end_cmd = (char_u)NUL; - for (idx = 0; ; ++idx) { + for (idx = 0; ; idx++) { if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0) { break; } @@ -672,234 +628,217 @@ static int sign_cmd_idx( return idx; } -/* - * Find a sign by name. Also returns pointer to the previous sign. - */ +/// Find a sign by name. Also returns pointer to the previous sign. static sign_T * sign_find(const char_u *name, sign_T **sp_prev) { - sign_T *sp; + 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; + 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; + return sp; } -/* - * Allocate a new sign - */ - static sign_T * -alloc_new_sign(char_u *name) +/// Allocate a new sign +static sign_T * alloc_new_sign(char_u *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 = vim_strsave(name); - - return sp; + 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 = vim_strsave(name); + + return sp; } -/* - * Initialize the icon information for a new sign - */ - static void -sign_define_init_icon(sign_T *sp, char_u *icon) +/// Initialize the icon information for a new sign +static void sign_define_init_icon(sign_T *sp, char_u *icon) { - xfree(sp->sn_icon); - sp->sn_icon = vim_strsave(icon); - backslash_halve(sp->sn_icon); + xfree(sp->sn_icon); + sp->sn_icon = vim_strsave(icon); + backslash_halve(sp->sn_icon); # ifdef FEAT_SIGN_ICONS - if (gui.in_use) - { - out_flush(); - if (sp->sn_image != NULL) - gui_mch_destroy_sign(sp->sn_image); - sp->sn_image = gui_mch_register_sign(sp->sn_icon); + if (gui.in_use) { + out_flush(); + if (sp->sn_image != NULL) { + gui_mch_destroy_sign(sp->sn_image); } + sp->sn_image = gui_mch_register_sign(sp->sn_icon); + } # endif } -/* - * Initialize the text for a new sign - */ - static int -sign_define_init_text(sign_T *sp, char_u *text) +/// Initialize the text for a new sign +static int sign_define_init_text(sign_T *sp, char_u *text) { - char_u *s; - char_u *endp; - int cells; - size_t len; - - endp = text + (int)STRLEN(text); - for (s = text; s + 1 < endp; ++s) { - if (*s == '\\') - { - // 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; - for (s = text; s < endp; s += (*mb_ptr2len)(s)) { - if (!vim_isprintc(utf_ptr2char(s))) { - break; - } - cells += utf_ptr2cells(s); - } - // Currently must be one or two display cells - if (s != endp || cells < 1 || cells > 2) { - EMSG2(_("E239: Invalid sign text: %s"), text); - return FAIL; - } - - 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 = vim_strnsave(text, len); - - if (cells == 1) - STRCPY(sp->sn_text + len - 1, " "); + char_u *s; + char_u *endp; + int cells; + size_t len; + + endp = text + (int)STRLEN(text); + for (s = text; s + 1 < endp; s++) { + if (*s == '\\') { + // 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; + for (s = text; s < endp; s += (*mb_ptr2len)(s)) { + if (!vim_isprintc(utf_ptr2char(s))) { + break; + } + cells += utf_ptr2cells(s); + } + // Currently must be one or two display cells + if (s != endp || cells < 1 || cells > 2) { + EMSG2(_("E239: Invalid sign text: %s"), text); + return FAIL; + } - 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 = vim_strnsave(text, len); + + if (cells == 1) { + STRCPY(sp->sn_text + len - 1, " "); + } + + return OK; } -/* - * Define a new sign or update an existing sign - */ +/// Define a new sign or update an existing sign int sign_define_by_name( - char_u *name, - char_u *icon, - char_u *linehl, - char_u *text, - char_u *texthl, - char_u *numhl + char_u *name, + char_u *icon, + char_u *linehl, + char_u *text, + char_u *texthl, + char_u *numhl ) { - sign_T *sp_prev; - sign_T *sp; - - sp = sign_find(name, &sp_prev); - if (sp == NULL) - { - sp = alloc_new_sign(name); - if (sp == NULL) { - return FAIL; - } + sign_T *sp_prev; + sign_T *sp; - // add the new sign to the list of signs - if (sp_prev == NULL) { - first_sign = sp; - } else { - sp_prev->sn_next = sp; - } + sp = sign_find(name, &sp_prev); + if (sp == NULL) { + sp = alloc_new_sign(name); + if (sp == NULL) { + return FAIL; } - // set values for a defined sign. - if (icon != NULL) - { - sign_define_init_icon(sp, icon); + // add the new sign to the list of signs + if (sp_prev == NULL) { + first_sign = sp; + } else { + sp_prev->sn_next = sp; } + } - if (text != NULL && (sign_define_init_text(sp, text) == FAIL)) { - return FAIL; - } + // set values for a defined sign. + if (icon != NULL) { + sign_define_init_icon(sp, icon); + } + + if (text != NULL && (sign_define_init_text(sp, text) == FAIL)) { + return FAIL; + } - if (linehl != NULL) - sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl)); + if (linehl != NULL) { + sp->sn_line_hl = syn_check_group(linehl, (int)STRLEN(linehl)); + } - if (texthl != NULL) - sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl)); + if (texthl != NULL) { + sp->sn_text_hl = syn_check_group(texthl, (int)STRLEN(texthl)); + } - if (numhl != NULL) - sp->sn_num_hl = syn_check_group(numhl, (int)STRLEN(numhl)); + if (numhl != NULL) { + sp->sn_num_hl = syn_check_group(numhl, (int)STRLEN(numhl)); + } - return OK; + return OK; } -/* - * Free the sign specified by 'name'. - */ - int -sign_undefine_by_name(const char_u *name) +/// Free the sign specified by 'name'. +int sign_undefine_by_name(const char_u *name) { - sign_T *sp_prev; - sign_T *sp; + sign_T *sp_prev; + sign_T *sp; - sp = sign_find(name, &sp_prev); - if (sp == NULL) - { - EMSG2(_("E155: Unknown sign: %s"), name); - return FAIL; - } - sign_undefine(sp, sp_prev); + sp = sign_find(name, &sp_prev); + if (sp == NULL) { + EMSG2(_("E155: Unknown sign: %s"), name); + return FAIL; + } + sign_undefine(sp, sp_prev); - return OK; + return OK; } -/* - * List the signs matching 'name' - */ - static void -sign_list_by_name(char_u *name) +/// List the signs matching 'name' +static void sign_list_by_name(char_u *name) { - sign_T *sp; + sign_T *sp; - sp = sign_find(name, NULL); - if (sp != NULL) - sign_list_defined(sp); - else - EMSG2(_("E155: Unknown sign: %s"), name); + sp = sign_find(name, NULL); + if (sp != NULL) { + sign_list_defined(sp); + } else { + EMSG2(_("E155: Unknown sign: %s"), name); + } } -/* - * Place a sign at the specified file location or update a sign. - */ +/// Place a sign at the specified file location or update a sign. int sign_place( - int *sign_id, - const char_u *sign_group, - const char_u *sign_name, - buf_T *buf, - linenr_T lnum, - int prio + int *sign_id, + const char_u *sign_group, + const char_u *sign_name, + buf_T *buf, + linenr_T lnum, + int prio ) { sign_T *sp; @@ -914,13 +853,11 @@ int sign_place( break; } } - if (sp == NULL) - { + if (sp == NULL) { EMSG2(_("E155: Unknown sign: %s"), sign_name); return FAIL; } - if (*sign_id == 0) - { + if (*sign_id == 0) { *sign_id = sign_group_get_next_signid(buf, sign_group); } @@ -942,22 +879,18 @@ int sign_place( return OK; } -/* - * Unplace the specified sign - */ - int -sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum) +/// Unplace the specified sign +int sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum) { - if (buf->b_signlist == NULL) { // No signs in the buffer - return OK; - } - if (sign_id == 0) - { + if (buf->b_signlist == NULL) { // No signs in the buffer + return OK; + } + if (sign_id == 0) { // Delete all the signs in the specified buffer redraw_buf_later(buf, NOT_VALID); buf_delete_signs(buf, sign_group); } else { - linenr_T lnum; + linenr_T lnum; // Delete only the specified signs lnum = buf_delsign(buf, atlnum, sign_id, sign_group); @@ -969,13 +902,11 @@ sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum) return OK; } - -/* - * Unplace the sign at the current cursor line. - */ + +/// Unplace the sign at the current cursor line. static void sign_unplace_at_cursor(char_u *groupname) { - int id = -1; + int id = -1; id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname); if (id > 0) { @@ -985,9 +916,7 @@ static void sign_unplace_at_cursor(char_u *groupname) } } -/* - * Jump to a sign. - */ +/// Jump to a sign. linenr_T sign_jump(int sign_id, char_u *sign_group, buf_T *buf) { linenr_T lnum; @@ -998,12 +927,11 @@ linenr_T sign_jump(int sign_id, char_u *sign_group, buf_T *buf) } // goto a sign ... - if (buf_jump_open_win(buf) != NULL) - { // ... in a current window + if (buf_jump_open_win(buf) != NULL) { // ... in a current window curwin->w_cursor.lnum = lnum; check_cursor_lnum(); beginline(BL_WHITE); - } else { // ... not currently in a window + } else { // ... not currently in a window if (buf->b_fname == NULL) { EMSG(_("E934: Cannot jump to a buffer that does not have a name")); return -1; @@ -1011,7 +939,7 @@ linenr_T sign_jump(int sign_id, char_u *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); + (int64_t)lnum, buf->b_fname); do_cmdline_cmd(cmd); xfree(cmd); } @@ -1021,19 +949,17 @@ linenr_T sign_jump(int sign_id, char_u *sign_group, buf_T *buf) return lnum; } -/* - * ":sign define {name} ..." command - */ +/// ":sign define {name} ..." command static void sign_define_cmd(char_u *sign_name, char_u *cmdline) { - char_u *arg; - char_u *p = cmdline; - char_u *icon = NULL; - char_u *text = NULL; - char_u *linehl = NULL; - char_u *texthl = NULL; - char_u *numhl = NULL; - int failed = FALSE; + char_u *arg; + char_u *p = cmdline; + char_u *icon = NULL; + char_u *text = NULL; + char_u *linehl = NULL; + char_u *texthl = NULL; + char_u *numhl = NULL; + int failed = false; // set values for a defined sign. for (;;) { @@ -1059,7 +985,7 @@ static void sign_define_cmd(char_u *sign_name, char_u *cmdline) numhl = vim_strnsave(arg, (size_t)(p - arg)); } else { EMSG2(_(e_invarg2), arg); - failed = TRUE; + failed = true; break; } } @@ -1075,20 +1001,17 @@ static void sign_define_cmd(char_u *sign_name, char_u *cmdline) xfree(numhl); } -/* - * ":sign place" command - */ +/// ":sign place" command static void sign_place_cmd( - buf_T *buf, - linenr_T lnum, - char_u *sign_name, - int id, - char_u *group, - int prio - ) + buf_T *buf, + linenr_T lnum, + char_u *sign_name, + int id, + char_u *group, + int prio +) { - if (id <= 0) - { + if (id <= 0) { // List signs placed in a file/buffer // :sign place file={fname} // :sign place group={group} file={fname} @@ -1117,15 +1040,13 @@ static void sign_place_cmd( } } -/* - * ":sign unplace" command - */ +/// ":sign unplace" command static void sign_unplace_cmd( - buf_T *buf, - linenr_T lnum, - char_u *sign_name, - int id, - char_u *group + buf_T *buf, + linenr_T lnum, + char_u *sign_name, + int id, + char_u *group ) { if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0')) { @@ -1133,8 +1054,7 @@ static void sign_unplace_cmd( return; } - if (id == -2) - { + if (id == -2) { if (buf != NULL) { // :sign unplace * file={fname} // :sign unplace * group={group} file={fname} @@ -1163,8 +1083,7 @@ static void sign_unplace_cmd( // :sign unplace {id} group=* buffer={nr} sign_unplace(id, group, buf, 0); } else { - if (id == -1) - { + if (id == -1) { // :sign unplace group={group} // :sign unplace group=* sign_unplace_at_cursor(group); @@ -1180,20 +1099,18 @@ static void sign_unplace_cmd( } } -/* - * Jump to a placed sign commands: - * :sign jump {id} file={fname} - * :sign jump {id} buffer={nr} - * :sign jump {id} group={group} file={fname} - * :sign jump {id} group={group} buffer={nr} - */ +/// Jump to a placed sign commands: +/// :sign jump {id} file={fname} +/// :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, - char_u *sign_name, - int id, - char_u *group - ) + buf_T *buf, + linenr_T lnum, + char_u *sign_name, + int id, + char_u *group +) { if (sign_name == NULL && group == NULL && id == -1) { EMSG(_(e_argreq)); @@ -1211,112 +1128,108 @@ static void sign_jump_cmd( (void)sign_jump(id, group, buf); } -/* - * Parse the command line arguments for the ":sign place", ":sign unplace" and - * ":sign jump" commands. - * The supported arguments are: line={lnum} name={name} group={group} - * priority={prio} and file={fname} or buffer={nr}. - */ +/// Parse the command line arguments for the ":sign place", ":sign unplace" and +/// ":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_u *arg, - char_u **sign_name, - int *signid, - char_u **group, - int *prio, - buf_T **buf, - linenr_T *lnum + int cmd, + char_u *arg, + char_u **sign_name, + int *signid, + char_u **group, + int *prio, + buf_T **buf, + linenr_T *lnum ) { - char_u *arg1; - char_u *name; - char_u *filename = NULL; - int lnum_arg = FALSE; - - // first arg could be placed sign id - arg1 = arg; - if (ascii_isdigit(*arg)) { - *signid = getdigits_int(&arg); - if (!ascii_iswhite(*arg) && *arg != NUL) { - *signid = -1; - arg = arg1; - } else { - arg = skipwhite(arg); - } + char_u *arg1; + char_u *name; + char_u *filename = NULL; + int lnum_arg = false; + + // first arg could be placed sign id + arg1 = arg; + if (ascii_isdigit(*arg)) { + *signid = getdigits_int(&arg); + if (!ascii_iswhite(*arg) && *arg != NUL) { + *signid = -1; + arg = arg1; + } else { + arg = skipwhite(arg); } + } - while (*arg != NUL) { - if (STRNCMP(arg, "line=", 5) == 0) { - arg += 5; - *lnum = atoi((char *)arg); - arg = skiptowhite(arg); - lnum_arg = TRUE; - } else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE) { - if (*signid != -1) { - EMSG(_(e_invarg)); - return FAIL; - } - *signid = -2; - arg = skiptowhite(arg + 1); - } else if (STRNCMP(arg, "name=", 5) == 0) { - arg += 5; - name = arg; - arg = skiptowhite(arg); - if (*arg != NUL) { - *arg++ = NUL; - } - while (name[0] == '0' && name[1] != NUL) { - ++name; - } - *sign_name = name; - } else if (STRNCMP(arg, "group=", 6) == 0) { - arg += 6; - *group = arg; - arg = skiptowhite(arg); - if (*arg != NUL) { - *arg++ = NUL; - } - } else if (STRNCMP(arg, "priority=", 9) == 0) { - arg += 9; - *prio = atoi((char *)arg); - arg = skiptowhite(arg); - } else if (STRNCMP(arg, "file=", 5) == 0) { - arg += 5; - filename = arg; - *buf = buflist_findname_exp(arg); - break; - } else if (STRNCMP(arg, "buffer=", 7) == 0) { - arg += 7; - filename = arg; - *buf = buflist_findnr(getdigits_int(&arg)); - if (*skipwhite(arg) != NUL) { - EMSG(_(e_trailing)); - } - break; - } else { + while (*arg != NUL) { + if (STRNCMP(arg, "line=", 5) == 0) { + arg += 5; + *lnum = atoi((char *)arg); + arg = skiptowhite(arg); + lnum_arg = true; + } else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE) { + if (*signid != -1) { EMSG(_(e_invarg)); return FAIL; } - arg = skipwhite(arg); - } - - if (filename != NULL && *buf == NULL) { - EMSG2(_("E158: Invalid buffer name: %s"), filename); + *signid = -2; + arg = skiptowhite(arg + 1); + } else if (STRNCMP(arg, "name=", 5) == 0) { + arg += 5; + name = arg; + arg = skiptowhite(arg); + if (*arg != NUL) { + *arg++ = NUL; + } + while (name[0] == '0' && name[1] != NUL) { + name++; + } + *sign_name = name; + } else if (STRNCMP(arg, "group=", 6) == 0) { + arg += 6; + *group = arg; + arg = skiptowhite(arg); + if (*arg != NUL) { + *arg++ = NUL; + } + } else if (STRNCMP(arg, "priority=", 9) == 0) { + arg += 9; + *prio = atoi((char *)arg); + arg = skiptowhite(arg); + } else if (STRNCMP(arg, "file=", 5) == 0) { + arg += 5; + filename = arg; + *buf = buflist_findname_exp(arg); + break; + } else if (STRNCMP(arg, "buffer=", 7) == 0) { + arg += 7; + filename = arg; + *buf = buflist_findnr(getdigits_int(&arg)); + if (*skipwhite(arg) != NUL) { + EMSG(_(e_trailing)); + } + break; + } else { + EMSG(_(e_invarg)); return FAIL; } + arg = skipwhite(arg); + } - // 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)) { - *buf = curwin->w_buffer; - } - return OK; + if (filename != NULL && *buf == NULL) { + EMSG2(_("E158: Invalid buffer name: %s"), filename); + return FAIL; + } + + // 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)) { + *buf = curwin->w_buffer; + } + return OK; } -/* - * ":sign" command - */ +/// ":sign" command void ex_sign(exarg_T *eap) { char_u *arg = eap->arg; @@ -1343,7 +1256,7 @@ void ex_sign(exarg_T *eap) } else if (*arg == NUL) { EMSG(_("E156: Missing sign name")); } else { - char_u *name; + char_u *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". @@ -1379,8 +1292,9 @@ void ex_sign(exarg_T *eap) // Parse command line arguments if (parse_sign_cmd_args(idx, arg, &sign_name, &id, &group, &prio, - &buf, &lnum) == FAIL) + &buf, &lnum) == FAIL) { return; + } if (idx == SIGNCMD_PLACE) { sign_place_cmd(buf, lnum, sign_name, id, group, prio); @@ -1392,9 +1306,7 @@ void ex_sign(exarg_T *eap) } } -/* - * Return information about a specified sign - */ +/// Return information about a specified sign static void sign_getinfo(sign_T *sp, dict_T *retdict) { const char *p; @@ -1407,21 +1319,21 @@ static void sign_getinfo(sign_T *sp, dict_T *retdict) tv_dict_add_str(retdict, S_LEN("text"), (char *)sp->sn_text); } if (sp->sn_line_hl > 0) { - p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE); + 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"), (char *)p); } if (sp->sn_text_hl > 0) { - p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE); + 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"), (char *)p); } if (sp->sn_num_hl > 0) { - p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, FALSE); + p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, false); if (p == NULL) { p = "NONE"; } @@ -1429,14 +1341,12 @@ static void sign_getinfo(sign_T *sp, dict_T *retdict) } } -/* - * If 'name' is NULL, return a list of all the defined signs. - * Otherwise, return information about the specified sign. - */ +/// If 'name' is NULL, return a list of all the defined signs. +/// Otherwise, return information about the specified sign. void sign_getlist(const char_u *name, list_T *retlist) { - sign_T *sp = first_sign; - dict_T *dict; + sign_T *sp = first_sign; + dict_T *dict; if (name != NULL) { sp = sign_find(name, NULL); @@ -1452,7 +1362,7 @@ void sign_getlist(const char_u *name, list_T *retlist) tv_list_append_dict(retlist, dict); sign_getinfo(sp, dict); - if (name != NULL) { // handle only the specified sign + if (name != NULL) { // handle only the specified sign break; } } @@ -1462,8 +1372,8 @@ void sign_getlist(const char_u *name, list_T *retlist) list_T *get_buffer_signs(buf_T *buf) FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - signlist_T *sign; - dict_T *d; + signlist_T *sign; + dict_T *d; list_T *const l = tv_list_alloc(kListLenMayKnow); FOR_ALL_SIGNS_IN_BUF(buf, sign) { @@ -1474,9 +1384,7 @@ list_T *get_buffer_signs(buf_T *buf) return l; } -/* - * Return information about all the signs placed in a buffer - */ +/// Return information about all the signs placed in a buffer static void sign_get_placed_in_buf( buf_T *buf, linenr_T lnum, @@ -1484,10 +1392,10 @@ static void sign_get_placed_in_buf( const char_u *sign_group, list_T *retlist) { - dict_T *d; - list_T *l; - signlist_T *sign; - dict_T *sdict; + dict_T *d; + list_T *l; + signlist_T *sign; + dict_T *sdict; if ((d = tv_dict_alloc()) == NULL) { return; @@ -1516,17 +1424,16 @@ static void sign_get_placed_in_buf( } } -/* - * 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. - */ +/// 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. void sign_get_placed( buf_T *buf, linenr_T lnum, int sign_id, const char_u *sign_group, - list_T *retlist) + list_T *retlist +) { if (buf != NULL) { sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist); @@ -1540,24 +1447,21 @@ void sign_get_placed( } # if defined(FEAT_SIGN_ICONS) || defined(PROTO) -/* - * Allocate the icons. Called when the GUI has started. Allows defining - * signs before it starts. - */ - void -sign_gui_started(void) +/// Allocate the icons. Called when the GUI has started. Allows defining +/// signs before it starts. +void sign_gui_started(void) { - sign_T *sp; + sign_T *sp; - for (sp = first_sign; sp != NULL; sp = sp->sn_next) - if (sp->sn_icon != NULL) - sp->sn_image = gui_mch_register_sign(sp->sn_icon); + for (sp = first_sign; sp != NULL; sp = sp->sn_next) { + if (sp->sn_icon != NULL) { + sp->sn_image = gui_mch_register_sign(sp->sn_icon); + } + } } # endif -/* - * List one sign. - */ +/// List one sign. static void sign_list_defined(sign_T *sp) { smsg("sign %s", sp->sn_name); @@ -1602,18 +1506,17 @@ static void sign_list_defined(sign_T *sp) } } -/* - * Undefine a sign and free its memory. - */ +/// 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) + if (sp_prev == NULL) { first_sign = sp->sn_next; - else + } else { sp_prev->sn_next = sp->sn_next; + } xfree(sp); } @@ -1647,37 +1550,37 @@ int sign_get_attr(int typenr, SignType type) return 0; } -/* - * Get text mark for sign "typenr". - * Returns NULL if there isn't one. - */ +/// Get text mark for sign "typenr". +/// Returns NULL if there isn't one. char_u * sign_get_text(int typenr) { - sign_T *sp; + sign_T *sp; - for (sp = first_sign; sp != NULL; sp = sp->sn_next) - if (sp->sn_typenr == typenr) - return sp->sn_text; - return NULL; + for (sp = first_sign; sp != NULL; sp = sp->sn_next) { + if (sp->sn_typenr == typenr) { + return sp->sn_text; + } + } + return NULL; } # if defined(FEAT_SIGN_ICONS) || defined(PROTO) - void * -sign_get_image( - int typenr) // the attribute which may have a sign +void * sign_get_image( + int typenr // the attribute which may have a sign +) { - sign_T *sp; + sign_T *sp; - for (sp = first_sign; sp != NULL; sp = sp->sn_next) - if (sp->sn_typenr == typenr) - return sp->sn_image; - return NULL; + for (sp = first_sign; sp != NULL; sp = sp->sn_next) { + if (sp->sn_typenr == typenr) { + return sp->sn_image; + } + } + return NULL; } # endif -/* - * Undefine/free all signs. - */ +/// Undefine/free all signs. void free_signs(void) { while (first_sign != NULL) { @@ -1687,19 +1590,18 @@ void free_signs(void) static enum { - EXP_SUBCMD, // expand :sign sub-commands - EXP_DEFINE, // expand :sign define {name} args - EXP_PLACE, // expand :sign place {id} args - EXP_UNPLACE, // expand :sign unplace" - EXP_SIGN_NAMES // expand with name of placed signs + EXP_SUBCMD, // expand :sign sub-commands + EXP_DEFINE, // expand :sign define {name} args + EXP_PLACE, // expand :sign place {id} args + EXP_UNPLACE, // expand :sign unplace" + EXP_SIGN_NAMES // expand with name of placed signs } expand_what; /// Function given to ExpandGeneric() to obtain the sign command /// expansion. char_u * get_sign_name(expand_T *xp, int idx) { - switch (expand_what) - { + switch (expand_what) { case EXP_SUBCMD: return (char_u *)cmds[idx]; case EXP_DEFINE: { @@ -1731,9 +1633,7 @@ char_u * get_sign_name(expand_T *xp, int idx) } } -/* - * Handle command line completion for :sign command. - */ +/// Handle command line completion for :sign command. void set_context_in_sign_cmd(expand_T *xp, char_u *arg) { char_u *p; @@ -1748,10 +1648,11 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) xp->xp_pattern = arg; end_subcmd = skiptowhite(arg); - if (*end_subcmd == NUL) + if (*end_subcmd == NUL) { // expand subcmd name // :sign {subcmd} return; + } cmd_idx = sign_cmd_idx(arg, end_subcmd); @@ -1760,16 +1661,14 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) // begin_subcmd_args begin_subcmd_args = skipwhite(end_subcmd); p = skiptowhite(begin_subcmd_args); - if (*p == NUL) - { + if (*p == NUL) { // // Expand first argument of subcmd when possible. // For ":jump {id}" and ":unplace {id}", we could // possibly expand the ids of all signs already placed. // xp->xp_pattern = begin_subcmd_args; - switch (cmd_idx) - { + switch (cmd_idx) { case SIGNCMD_LIST: case SIGNCMD_UNDEFINE: // :sign list @@ -1789,8 +1688,7 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) // p // Loop until reaching last argument. - do - { + do { p = skipwhite(p); last = p; p = skiptowhite(p); @@ -1804,8 +1702,7 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) if (p == NULL) { // Expand last argument name (before equal sign). xp->xp_pattern = last; - switch (cmd_idx) - { + switch (cmd_idx) { case SIGNCMD_DEFINE: expand_what = EXP_DEFINE; break; @@ -1819,13 +1716,10 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) default: xp->xp_context = EXPAND_NOTHING; } - } - else - { - // Expand last argument value (after equal sign). + } else { + // Expand last argument value (after equal sign). xp->xp_pattern = p + 1; - switch (cmd_idx) - { + switch (cmd_idx) { case SIGNCMD_DEFINE: if (STRNCMP(last, "texthl", p - last) == 0 || STRNCMP(last, "linehl", p - last) == 0 @@ -1838,10 +1732,11 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) } break; case SIGNCMD_PLACE: - if (STRNCMP(last, "name", p - last) == 0) + if (STRNCMP(last, "name", p - last) == 0) { expand_what = EXP_SIGN_NAMES; - else + } else { xp->xp_context = EXPAND_NOTHING; + } break; default: xp->xp_context = EXPAND_NOTHING; diff --git a/src/nvim/sign.h b/src/nvim/sign.h index 6b1ee7e30e..e3a5a27c74 100644 --- a/src/nvim/sign.h +++ b/src/nvim/sign.h @@ -2,6 +2,9 @@ #define NVIM_SIGN_H #include +#include "nvim/buffer_defs.h" +#include "nvim/ex_cmds_defs.h" +#include "nvim/sign_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h index e9c2312ae9..687c15bbd6 100644 --- a/src/nvim/sign_defs.h +++ b/src/nvim/sign_defs.h @@ -2,36 +2,37 @@ #define NVIM_SIGN_DEFS_H #include "nvim/pos.h" +#include "nvim/types.h" // signs: line annotations // Sign group typedef struct signgroup_S { - uint16_t refcount; // number of signs in this group + uint16_t refcount; // number of signs in this group int next_sign_id; // next sign id for this group - char_u sg_name[1]; // sign group name + char_u sg_name[1]; // 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)) +#define SGN_KEY_OFF offsetof(signgroup_T, sg_name) +#define HI2SG(hi) ((signgroup_T *)((hi)->hi_key - SGN_KEY_OFF)) typedef struct signlist signlist_T; struct signlist { - int id; // unique identifier for each placed sign - linenr_T lnum; // line number which has this sign - int typenr; // typenr of sign - signgroup_T *group; // sign group - int priority; // priority for highlighting - signlist_T *next; // next signlist entry - signlist_T *prev; // previous entry -- for easy reordering + int id; // unique identifier for each placed sign + linenr_T lnum; // line number which has this sign + int typenr; // typenr of sign + signgroup_T *group; // sign group + int priority; // priority for highlighting + signlist_T *next; // next signlist entry + signlist_T *prev; // previous entry -- for easy reordering }; // Default sign priority for highlighting -#define SIGN_DEF_PRIO 10 +#define SIGN_DEF_PRIO 10 // type argument for buf_getsigntype() and sign_get_attr() typedef enum { -- cgit