diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2014-03-28 02:09:08 -0400 |
---|---|---|
committer | Thiago de Arruda <tpadilha84@gmail.com> | 2014-03-29 20:47:39 -0300 |
commit | d9f5cd6290755b0fc571c89db5f213844cbe9eef (patch) | |
tree | ae8e325f1cc2efbfc79cf59c7cc7f61e8ed38db8 | |
parent | 28b3659955d154d04ac8e531b54f1ec32f3136a7 (diff) | |
download | rneovim-d9f5cd6290755b0fc571c89db5f213844cbe9eef.tar.gz rneovim-d9f5cd6290755b0fc571c89db5f213844cbe9eef.tar.bz2 rneovim-d9f5cd6290755b0fc571c89db5f213844cbe9eef.zip |
Re-integrate FEAT_SIGNS code, close #383
- omit FEAT_NETBEANS_INTG and FEAT_SIGN_ICONS
- omit FEAT_GUI blocks
-rw-r--r-- | src/buffer.c | 269 | ||||
-rw-r--r-- | src/buffer_defs.h | 5 | ||||
-rw-r--r-- | src/edit.c | 5 | ||||
-rw-r--r-- | src/eval.c | 1 | ||||
-rw-r--r-- | src/ex_cmds.c | 758 | ||||
-rw-r--r-- | src/ex_docmd.c | 5 | ||||
-rw-r--r-- | src/globals.h | 1 | ||||
-rw-r--r-- | src/mark.c | 3 | ||||
-rw-r--r-- | src/misc2.c | 1 | ||||
-rw-r--r-- | src/move.c | 1 | ||||
-rw-r--r-- | src/screen.c | 158 | ||||
-rw-r--r-- | src/sign_defs.h | 24 | ||||
-rw-r--r-- | src/syntax.c | 5 | ||||
-rw-r--r-- | src/version.c | 2 |
14 files changed, 1234 insertions, 4 deletions
diff --git a/src/buffer.c b/src/buffer.c index 90f965f42d..4486611540 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -91,6 +91,7 @@ static void clear_wininfo(buf_T *buf); # define dev_T unsigned #endif +static void insert_sign(buf_T *buf, signlist_T *prev, signlist_T *next, int id, linenr_T lnum, int typenr); static char *msg_loclist = N_("[Location List]"); static char *msg_qflist = N_("[Quickfix List]"); @@ -568,6 +569,7 @@ free_buffer_stuff ( vars_clear(&buf->b_vars->dv_hashtab); /* free all internal variables */ hash_init(&buf->b_vars->dv_hashtab); uc_clear(&buf->b_ucmds); /* clear local user commands */ + buf_delete_signs(buf); /* delete any signs */ map_clear_int(buf, MAP_ALL_MODES, TRUE, FALSE); /* clear local mappings */ map_clear_int(buf, MAP_ALL_MODES, TRUE, TRUE); /* clear local abbrevs */ vim_free(buf->b_start_fenc); @@ -4410,6 +4412,273 @@ win_found: } #endif +/* + * Insert the sign into the signlist. + */ +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 */ + linenr_T lnum, /* line number which gets the mark */ + int typenr /* typenr of sign we are adding */ + ) +{ + signlist_T *newsign; + + newsign = (signlist_T *)lalloc((long_u)sizeof(signlist_T), FALSE); + if (newsign != NULL) { + newsign->id = id; + newsign->lnum = lnum; + newsign->typenr = typenr; + newsign->next = next; + + 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; + } + } +} + +/* + * 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 */ + 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 */ + + prev = NULL; + for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { + if (lnum == sign->lnum && id == sign->id) { + sign->typenr = typenr; + return; + } + else if (id < 0 /* keep signs sorted by lnum */ + && lnum < sign->lnum) + { + insert_sign(buf, prev, sign, id, lnum, typenr); + return; + } + prev = sign; + } + insert_sign(buf, prev, sign, id, lnum, typenr); + + return; +} + +linenr_T buf_change_sign_type( + buf_T *buf, /* buffer to store sign in */ + int markId, /* sign ID */ + 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) { + sign->typenr = typenr; + return sign->lnum; + } + } + + return (linenr_T)0; +} + +int buf_getsigntype( + buf_T *buf, + linenr_T lnum, + int type /* SIGN_ICON, SIGN_TEXT, SIGN_ANY, SIGN_LINEHL */ + ) +{ + signlist_T *sign; /* a sign in a b_signlist */ + + for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { + if (sign->lnum == lnum + && (type == SIGN_ANY + || (type == SIGN_TEXT + && sign_get_text(sign->typenr) != NULL) + || (type == SIGN_LINEHL + && sign_get_attr(sign->typenr, TRUE) != 0))) { + return sign->typenr; + } + } + return 0; +} + + +linenr_T buf_delsign( + buf_T *buf, /* buffer sign is stored in */ + int id /* sign id */ + ) +{ + 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 */ + + lastp = &buf->b_signlist; + lnum = 0; + for (sign = buf->b_signlist; sign != NULL; sign = next) { + next = sign->next; + if (sign->id == id) { + *lastp = next; + lnum = sign->lnum; + vim_free(sign); + 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. 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 */ + ) +{ + signlist_T *sign; /* a sign in the signlist */ + + for (sign = buf->b_signlist; sign != NULL; sign = sign->next) { + if (sign->id == id) { + return sign->lnum; + } + } + + return 0; +} + +int buf_findsign_id( + 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 (sign = buf->b_signlist; sign != NULL; sign = sign->next) { + if (sign->lnum == lnum) { + return sign->id; + } + } + + return 0; +} + + +/* + * Delete signs in buffer "buf". + */ +void buf_delete_signs(buf_T *buf) +{ + signlist_T *next; + + while (buf->b_signlist != NULL) { + next = buf->b_signlist->next; + vim_free(buf->b_signlist); + buf->b_signlist = next; + } +} + +/* + * Delete all signs in all buffers. + */ +void buf_delete_all_signs() +{ + buf_T *buf; /* buffer we are checking for signs */ + + for (buf = firstbuf; buf != NULL; buf = buf->b_next) { + if (buf->b_signlist != NULL) { + /* Need to redraw the windows to remove the sign column. */ + redraw_buf_later(buf, NOT_VALID); + buf_delete_signs(buf); + } + } +} + +/* + * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers. + */ +void sign_list_placed(buf_T *rbuf) +{ + buf_T *buf; + signlist_T *p; + char lbuf[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 (p = buf->b_signlist; p != NULL && !got_int; p = p->next) { + vim_snprintf(lbuf, BUFSIZ, _(" line=%ld id=%d name=%s"), + (long)p->lnum, p->id, sign_typenr2name(p->typenr)); + 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 */ + + for (sign = curbuf->b_signlist; sign != NULL; sign = sign->next) { + if (sign->lnum >= line1 && sign->lnum <= line2) { + if (amount == MAXLNUM) { + sign->lnum = line1; + } else { + sign->lnum += amount; + } + } + else if (sign->lnum > line2) + sign->lnum += amount_after; + } +} /* * Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed. diff --git a/src/buffer_defs.h b/src/buffer_defs.h index 9d5e75b609..346a402fc2 100644 --- a/src/buffer_defs.h +++ b/src/buffer_defs.h @@ -38,6 +38,9 @@ typedef struct memfile memfile_T; // for synstate_T (needs reg_extmatch_T, win_T and buf_T) #include "syntax_defs.h" +// for signlist_T +#include "sign_defs.h" + /* * The taggy struct is used to store the information about a :tag command. */ @@ -700,6 +703,8 @@ struct file_buffer { 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 */ }; /* diff --git a/src/edit.c b/src/edit.c index f41af9347b..873d5b5898 100644 --- a/src/edit.c +++ b/src/edit.c @@ -5643,6 +5643,11 @@ comp_textwidth ( if (cmdwin_type != 0) textwidth -= 1; textwidth -= curwin->w_p_fdc; + + if (curwin->w_buffer->b_signlist != NULL) { + textwidth -= 1; + } + if (curwin->w_p_nu || curwin->w_p_rnu) textwidth -= 8; } diff --git a/src/eval.c b/src/eval.c index 858ae6179e..bf208b3fde 100644 --- a/src/eval.c +++ b/src/eval.c @@ -10305,6 +10305,7 @@ static void f_has(typval_T *argvars, typval_T *rettv) "scrollbind", "showcmd", "cmdline_info", + "signs", "smartindent", #ifdef STARTUPTIME "startuptime", diff --git a/src/ex_cmds.c b/src/ex_cmds.c index 4dc9931e0c..cb4c24737b 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -5686,4 +5686,762 @@ helptags_one ( fclose(fd_tags); /* there is no check for an error... */ } +/* + * 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 */ + 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 */ +}; + +static sign_T *first_sign = NULL; +static int next_sign_typenr = 1; + +static int sign_cmd_idx (char_u *begin_cmd, char_u *end_cmd); +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 +}; + +/* + * 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; +} + +/* + * ":sign" command + */ +void ex_sign(exarg_T *eap) +{ + char_u *arg = eap->arg; + char_u *p; + int idx; + sign_T *sp; + sign_T *sp_prev; + buf_T *buf; + + /* 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 + { + /* 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; + + 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) + { + /* ":sign define {name} ...": define a sign */ + if (sp == NULL) + { + sign_T *lp; + int start = next_sign_typenr; + + /* Allocate a new sign. */ + sp = (sign_T *)alloc_clear((unsigned)sizeof(sign_T)); + if (sp == NULL) + return; + + /* 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) + { + vim_free(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); + if (sp->sn_name == NULL) /* out of memory */ + { + vim_free(sp); + return; + } + + /* 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 (;;) + { + arg = skipwhite(p); + if (*arg == NUL) + break; + p = skiptowhite_esc(arg); + if (STRNCMP(arg, "icon=", 5) == 0) + { + arg += 5; + vim_free(sp->sn_icon); + sp->sn_icon = vim_strnsave(arg, (int)(p - arg)); + backslash_halve(sp->sn_icon); + } + else if (STRNCMP(arg, "text=", 5) == 0) + { + char_u *s; + int cells; + int len; + + arg += 5; + + /* Count cells and check for non-printable chars */ + if (has_mbyte) + { + cells = 0; + for (s = arg; s < p; s += (*mb_ptr2len)(s)) + { + if (!vim_isprintc((*mb_ptr2char)(s))) + break; + cells += (*mb_ptr2cells)(s); + } + } + else + + { + for (s = arg; s < p; ++s) + if (!vim_isprintc(*s)) + break; + cells = (int)(s - arg); + } + /* 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; + } + + vim_free(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 (sp->sn_text != NULL && cells == 1) + STRCPY(sp->sn_text + len - 1, " "); + } + else if (STRNCMP(arg, "linehl=", 7) == 0) + { + arg += 7; + sp->sn_line_hl = syn_check_group(arg, (int)(p - arg)); + } + else if (STRNCMP(arg, "texthl=", 7) == 0) + { + arg += 7; + sp->sn_text_hl = syn_check_group(arg, (int)(p - arg)); + } + else + { + EMSG2(_(e_invarg2), arg); + return; + } + } + } + else if (sp == NULL) + EMSG2(_("E155: Unknown sign: %s"), arg); + else if (idx == SIGNCMD_LIST) + /* ":sign list {name}" */ + sign_list_defined(sp); + else + /* ":sign undefine {name}" */ + sign_undefine(sp, sp_prev); + } + } + else + { + int id = -1; + linenr_T lnum = -1; + char_u *sign_name = NULL; + char_u *arg1; + + if (*arg == NUL) + { + if (idx == SIGNCMD_PLACE) + { + /* ":sign place": list placed signs in all buffers */ + sign_list_placed(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); + update_debug_sign(curwin->w_buffer, curwin->w_cursor.lnum); + } + else + EMSG(_("E159: Missing sign number")); + } + else + EMSG(_(e_argreq)); + return; + } + + if (idx == SIGNCMD_UNPLACE && arg[0] == '*' && arg[1] == NUL) + { + /* ":sign unplace *": remove all placed signs */ + buf_delete_all_signs(); + return; + } + + /* first arg could be placed sign id */ + arg1 = arg; + if (VIM_ISDIGIT(*arg)) + { + id = getdigits(&arg); + if (!vim_iswhite(*arg) && *arg != NUL) + { + id = -1; + arg = arg1; + } + else + { + arg = skipwhite(arg); + if (idx == SIGNCMD_UNPLACE && *arg == NUL) + { + /* ":sign unplace {id}": remove placed sign by number */ + for (buf = firstbuf; buf != NULL; buf = buf->b_next) + if ((lnum = buf_delsign(buf, id)) != 0) + update_debug_sign(buf, lnum); + return; + } + } + } + + /* + * Check for line={lnum} name={name} and file={fname} or buffer={nr}. + * Leave "arg" pointing to {fname}. + */ + for (;;) + { + if (STRNCMP(arg, "line=", 5) == 0) + { + arg += 5; + lnum = atoi((char *)arg); + arg = skiptowhite(arg); + } + else if (STRNCMP(arg, "*", 1) == 0 && idx == SIGNCMD_UNPLACE) + { + if (id != -1) + { + EMSG(_(e_invarg)); + return; + } + id = -2; + arg = skiptowhite(arg + 1); + } + else if (STRNCMP(arg, "name=", 5) == 0) + { + arg += 5; + sign_name = arg; + arg = skiptowhite(arg); + if (*arg != NUL) + *arg++ = NUL; + while (sign_name[0] == '0' && sign_name[1] != NUL) + ++sign_name; + } + else if (STRNCMP(arg, "file=", 5) == 0) + { + arg += 5; + buf = buflist_findname(arg); + break; + } + else if (STRNCMP(arg, "buffer=", 7) == 0) + { + arg += 7; + buf = buflist_findnr((int)getdigits(&arg)); + if (*skipwhite(arg) != NUL) + EMSG(_(e_trailing)); + break; + } + else + { + EMSG(_(e_invarg)); + return; + } + arg = skipwhite(arg); + } + + if (buf == NULL) + { + EMSG2(_("E158: Invalid buffer name: %s"), arg); + } + else if (id <= 0 && !(idx == SIGNCMD_UNPLACE && id == -2)) + { + if (lnum >= 0 || sign_name != NULL) + EMSG(_(e_invarg)); + else + /* ":sign place file={fname}": list placed signs in one file */ + sign_list_placed(buf); + } + 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) + { /* 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 */ + char_u *cmd; + + cmd = alloc((unsigned)STRLEN(buf->b_fname) + 25); + if (cmd == NULL) + return; + sprintf((char *)cmd, "e +%ld %s", (long)lnum, buf->b_fname); + do_cmdline_cmd(cmd); + vim_free(cmd); + } + + foldOpenCursor(); + } + else + EMSGN(_("E157: Invalid sign ID: %ld"), id); + } + else if (idx == SIGNCMD_UNPLACE) + { + 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); + update_debug_sign(buf, lnum); + } + } + /* idx == SIGNCMD_PLACE */ + else if (sign_name != NULL) + { + 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; + } + 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); + update_debug_sign(buf, lnum); + } + else + EMSG(_(e_invarg)); + } +} + +/* + * List one sign. + */ + static void +sign_list_defined(sp) + sign_T *sp; +{ + char_u *p; + + smsg((char_u *)"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="); + p = get_highlight_name(NULL, sp->sn_line_hl - 1); + if (p == NULL) + MSG_PUTS("NONE"); + else + msg_puts(p); + } + if (sp->sn_text_hl > 0) + { + MSG_PUTS(" texthl="); + p = get_highlight_name(NULL, sp->sn_text_hl - 1); + if (p == NULL) + MSG_PUTS("NONE"); + else + msg_puts(p); + } +} + +/* + * Undefine a sign and free its memory. + */ + static void +sign_undefine(sp, sp_prev) + sign_T *sp; + sign_T *sp_prev; +{ + vim_free(sp->sn_name); + vim_free(sp->sn_icon); + vim_free(sp->sn_text); + if (sp_prev == NULL) + first_sign = sp->sn_next; + else + sp_prev->sn_next = sp->sn_next; + vim_free(sp); +} + +/* + * Get highlighting attribute for sign "typenr". + * If "line" is TRUE: line highl, if FALSE: text highl. + */ + int +sign_get_attr(typenr, line) + int typenr; + int line; +{ + sign_T *sp; + + for (sp = first_sign; sp != NULL; sp = sp->sn_next) + if (sp->sn_typenr == typenr) + { + if (line) + { + if (sp->sn_line_hl > 0) + return syn_id2attr(sp->sn_line_hl); + } + else + { + if (sp->sn_text_hl > 0) + return syn_id2attr(sp->sn_text_hl); + } + break; + } + return 0; +} + +/* + * Get text mark for sign "typenr". + * Returns NULL if there isn't one. + */ + char_u * +sign_get_text(typenr) + 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(typenr) + 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]"); +} + +#if defined(EXITFREE) || defined(PROTO) +/* + * Undefine/free all signs. + */ + void +free_signs() +{ + while (first_sign != NULL) + sign_undefine(first_sign, NULL); +} +#endif + +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) +{ + sign_T *sp; + int current_idx; + + switch (expand_what) + { + case EXP_SUBCMD: + return (char_u *)cmds[idx]; + case EXP_DEFINE: + { + char *define_arg[] = + { + "icon=", "linehl=", "text=", "texthl=", NULL + }; + return (char_u *)define_arg[idx]; + } + case EXP_PLACE: + { + char *place_arg[] = + { + "line=", "name=", "file=", "buffer=", NULL + }; + return (char_u *)place_arg[idx]; + } + case EXP_UNPLACE: + { + char *unplace_arg[] = { "file=", "buffer=", NULL }; + return (char_u *)unplace_arg[idx]; + } + case EXP_SIGN_NAMES: + /* Complete with name of signs already defined */ + current_idx = 0; + for (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(xp, arg) + 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}<CTRL-D>*/ + 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 <CTRL-D> + * :sign undefine <CTRL-D> */ + 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 == NUL) + { + /* 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) + 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/ex_docmd.c b/src/ex_docmd.c index 8f29dbfaab..30279ff595 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -259,7 +259,6 @@ static void ex_folddo(exarg_T *eap); #ifndef HAVE_WORKING_LIBINTL # define ex_language ex_ni #endif -# define ex_sign ex_ni # define ex_wsverb ex_ni # define ex_nbclose ex_ni # define ex_nbkey ex_ni @@ -3102,6 +3101,9 @@ set_one_cmd_context ( case CMD_scscope: set_context_in_cscope_cmd(xp, arg, ea.cmdidx); break; + case CMD_sign: + set_context_in_sign_cmd(xp, arg); + break; case CMD_bdelete: case CMD_bwipeout: case CMD_bunload: @@ -4460,6 +4462,7 @@ static struct { {EXPAND_SYNTIME, "syntime"}, {EXPAND_SETTINGS, "option"}, {EXPAND_SHELLCMD, "shellcmd"}, + {EXPAND_SIGN, "sign"}, {EXPAND_TAGS, "tag"}, {EXPAND_TAGS_LISTFILES, "tag_listfiles"}, {EXPAND_USER, "user"}, diff --git a/src/globals.h b/src/globals.h index 56d2a24235..eb87aa90b1 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1081,6 +1081,7 @@ EXTERN char_u e_screenmode[] INIT(= N_( #endif EXTERN char_u e_scroll[] INIT(= N_("E49: Invalid scroll size")); EXTERN char_u e_shellempty[] INIT(= N_("E91: 'shell' option is empty")); +EXTERN char_u e_signdata[] INIT(= N_("E255: Couldn't read in sign data!")); EXTERN char_u e_swapclose[] INIT(= N_("E72: Close error on swap file")); EXTERN char_u e_tagstack[] INIT(= N_("E73: tag stack empty")); EXTERN char_u e_toocompl[] INIT(= N_("E74: Command too complex")); diff --git a/src/mark.c b/src/mark.c index 72d290f802..1f113dd957 100644 --- a/src/mark.c +++ b/src/mark.c @@ -942,8 +942,9 @@ void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after) qf_mark_adjust(NULL, line1, line2, amount, amount_after); /* location lists */ FOR_ALL_TAB_WINDOWS(tab, win) - qf_mark_adjust(win, line1, line2, amount, amount_after); + qf_mark_adjust(win, line1, line2, amount, amount_after); + sign_mark_adjust(line1, line2, amount, amount_after); } /* previous context mark */ diff --git a/src/misc2.c b/src/misc2.c index 6942c2fe02..21d942e370 100644 --- a/src/misc2.c +++ b/src/misc2.c @@ -786,6 +786,7 @@ void free_all_mem(void) free_regexp_stuff(); free_tag_stuff(); free_cd_dir(); + free_signs(); set_expr_line(NULL); diff_clear(curtab); clear_sb_text(); /* free any scrollback text */ diff --git a/src/move.c b/src/move.c index 26be5698e5..3d0060fac8 100644 --- a/src/move.c +++ b/src/move.c @@ -679,6 +679,7 @@ int win_col_off(win_T *wp) return ((wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) + 1 : 0) + (cmdwin_type == 0 || wp != curwin ? 0 : 1) + wp->w_p_fdc + + (wp->w_buffer->b_signlist != NULL ? 2 : 0) ; } diff --git a/src/screen.c b/src/screen.c index d12c53177a..7cbe287538 100644 --- a/src/screen.c +++ b/src/screen.c @@ -96,6 +96,7 @@ #include "charset.h" #include "diff.h" #include "eval.h" +#include "ex_cmds.h" #include "ex_cmds2.h" #include "ex_getln.h" #include "farsi.h" @@ -192,6 +193,11 @@ static int fillchar_vsep(int *attr); static void win_redr_custom(win_T *wp, int draw_ruler); static void win_redr_ruler(win_T *wp, int always); +//signs column +static void update_prepare(void); +static void update_finish(void); +static int draw_signcolumn (win_T *wp); + /* Ugly global: overrule attribute used by screen_char() */ static int screen_char_attr = 0; @@ -678,7 +684,87 @@ void update_single_line(win_T *wp, linenr_T lnum) } +/* + * Prepare for updating one or more windows. + * Caller must check for "updating_screen" already set to avoid recursiveness. + */ +static void update_prepare() +{ + cursor_off(); + updating_screen = TRUE; + start_search_hl(); +} + +/* + * Finish updating one or more windows. + */ +static void update_finish() +{ + if (redraw_cmdline) { + showmode(); + } + + end_search_hl(); + updating_screen = FALSE; +} + +void update_debug_sign(buf_T *buf, linenr_T lnum) +{ + win_T *wp; + int doit = FALSE; + win_foldinfo.fi_level = 0; + + /* update/delete a specific mark */ + FOR_ALL_WINDOWS(wp) + { + if (buf != NULL && lnum > 0) { + if (wp->w_buffer == buf && lnum >= wp->w_topline + && lnum < wp->w_botline) + { + if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) { + wp->w_redraw_top = lnum; + } + if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) { + wp->w_redraw_bot = lnum; + } + redraw_win_later(wp, VALID); + } + } else { + redraw_win_later(wp, VALID); + } + if (wp->w_redr_type != 0) { + doit = TRUE; + } + } + + /* Return when there is nothing to do, screen updating is already + * happening (recursive call) or still starting up. */ + if (!doit || updating_screen || starting) { + return; + } + + /* update all windows that need updating */ + update_prepare(); + + for (wp = firstwin; wp; wp = wp->w_next) { + if (wp->w_redr_type != 0) { + win_update(wp); + } + if (wp->w_redr_status) { + win_redr_status(wp); + } + } + update_finish(); +} + +/* + * Return TRUE when window "wp" has a column to draw signs in. + */ +static int draw_signcolumn(win_T *wp) +{ + return (wp->w_buffer->b_signlist != NULL); +} /* @@ -1676,6 +1762,20 @@ static void win_draw_end(win_T *wp, int c1, int c2, int row, int endrow, hlf_T h W_ENDCOL(wp) - n, (int)W_ENDCOL(wp), ' ', ' ', hl_attr(HLF_FC)); } + + if (draw_signcolumn(wp)) { + int nn = n + 2; + + /* draw the sign column left of the fold column */ + if (nn > W_WIDTH(wp)) { + nn = W_WIDTH(wp); + } + screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow, + W_ENDCOL(wp) - nn, (int)W_ENDCOL(wp) - n, + ' ', ' ', hl_attr(HLF_SC)); + n = nn; + } + screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow, W_WINCOL(wp), W_ENDCOL(wp) - 1 - FDC_OFF, c2, c2, hl_attr(hl)); @@ -1703,6 +1803,21 @@ static void win_draw_end(win_T *wp, int c1, int c2, int row, int endrow, hlf_T h ' ', ' ', hl_attr(HLF_FC)); n = nn; } + + if (draw_signcolumn(wp)) + { + int nn = n + 2; + + /* draw the sign column after the fold column */ + if (nn > W_WIDTH(wp)) { + nn = W_WIDTH(wp); + } + screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow, + W_WINCOL(wp) + n, (int)W_WINCOL(wp) + nn, + ' ', ' ', hl_attr(HLF_SC)); + n = nn; + } + screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + endrow, W_WINCOL(wp) + FDC_OFF, (int)W_ENDCOL(wp), c1, c2, hl_attr(hl)); @@ -1792,6 +1907,17 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T * text */ RL_MEMSET(col, hl_attr(HLF_FL), W_WIDTH(wp) - col); + /* If signs are being displayed, add two spaces. */ + if (draw_signcolumn(wp)) { + len = W_WIDTH(wp) - col; + if (len > 0) { + if (len > 2) { + len = 2; + } + copy_text_attr(off + col, (char_u *)" ", len, hl_attr(HLF_FL)); + col += len; + } + } /* * 3. Add the 'number' or 'relativenumber' column @@ -2211,7 +2337,7 @@ win_line ( #define WL_START 0 /* nothing done yet */ # define WL_CMDLINE WL_START + 1 /* cmdline window column */ # define WL_FOLD WL_CMDLINE + 1 /* 'foldcolumn' */ -# define WL_SIGN WL_FOLD /* column for signs */ +# define WL_SIGN WL_FOLD + 1 /* column for signs */ #define WL_NR WL_SIGN + 1 /* line number */ # define WL_SBR WL_NR + 1 /* 'showbreak' or 'diff' */ #define WL_LINE WL_SBR + 1 /* text in the line */ @@ -2407,6 +2533,11 @@ win_line ( filler_todo = filler_lines; #ifdef LINE_ATTR + /* If this line has a sign with line highlighting set line_attr. */ + v = buf_getsigntype(wp->w_buffer, lnum, SIGN_LINEHL); + if (v != 0) + line_attr = sign_get_attr((int)v, TRUE); + /* Highlight the current line in the quickfix window. */ if (bt_quickfix(wp->w_buffer) && qf_current_entry(wp) == lnum) line_attr = hl_attr(HLF_L); @@ -2667,6 +2798,31 @@ win_line ( } } + //sign column + if (draw_state == WL_SIGN - 1 && n_extra == 0) { + draw_state = WL_SIGN; + /* Show the sign column when there are any signs in this + * buffer or when using Netbeans. */ + if (draw_signcolumn(wp) && filler_todo <= 0) { + int text_sign; + /* Draw two cells with the sign value or blank. */ + c_extra = ' '; + char_attr = hl_attr(HLF_SC); + n_extra = 2; + + if (row == startrow) { + text_sign = buf_getsigntype(wp->w_buffer, lnum, SIGN_TEXT); + if (text_sign != 0) { + p_extra = sign_get_text(text_sign); + if (p_extra != NULL) { + c_extra = NUL; + n_extra = (int)STRLEN(p_extra); + } + char_attr = sign_get_attr(text_sign, FALSE); + } + } + } + } if (draw_state == WL_NR - 1 && n_extra == 0) { draw_state = WL_NR; diff --git a/src/sign_defs.h b/src/sign_defs.h new file mode 100644 index 0000000000..be33a57fff --- /dev/null +++ b/src/sign_defs.h @@ -0,0 +1,24 @@ +#ifndef NEOVIM_SIGN_DEFS_H +#define NEOVIM_SIGN_DEFS_H + +// signs: line annotations + +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 */ + signlist_T *next; /* next signlist entry */ +}; + +/* type argument for buf_getsigntype() */ +#define SIGN_ANY 0 +#define SIGN_LINEHL 1 +#define SIGN_ICON 2 +#define SIGN_TEXT 3 + + + +#endif // NEOVIM_SIGN_DEFS_H diff --git a/src/syntax.c b/src/syntax.c index c863f4605a..d58fdbd289 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -6014,6 +6014,8 @@ static char *(highlight_init_light[]) = CENT( "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue", "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"), + CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue", + "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"), CENT("Visual term=reverse", "Visual term=reverse guibg=LightGrey"), CENT("DiffAdd term=bold ctermbg=LightBlue", @@ -6084,6 +6086,8 @@ static char *(highlight_init_dark[]) = CENT( "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan", "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"), + CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan", + "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"), CENT("Visual term=reverse", "Visual term=reverse guibg=DarkGrey"), CENT("DiffAdd term=bold ctermbg=DarkBlue", @@ -7847,6 +7851,7 @@ static void highlight_list_two(int cnt, int attr) */ char_u *get_highlight_name(expand_T *xp, int idx) { + //TODO: 'xp' is unused if (idx == highlight_ga.ga_len && include_none != 0) return (char_u *)"none"; if (idx == highlight_ga.ga_len + include_none && include_default != 0) diff --git a/src/version.c b/src/version.c index 18f9fddc44..3f1fd25ae3 100644 --- a/src/version.c +++ b/src/version.c @@ -156,7 +156,7 @@ static char *(features[]) = { "+rightleft", "-ruby", "+scrollbind", - "-signs", + "+signs", "+smartindent", "-sniff", #ifdef STARTUPTIME |