Re-integrate FEAT_SIGNS code, close #383

- omit FEAT_NETBEANS_INTG and FEAT_SIGN_ICONS
- omit FEAT_GUI blocks
This commit is contained in:
Justin M. Keyes
2014-03-28 02:09:08 -04:00
committed by Thiago de Arruda
parent 28b3659955
commit d9f5cd6290
14 changed files with 1234 additions and 4 deletions

View File

@@ -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;
}
}
}