vim-patch: 7.4.330

Problem:    Using a regexp pattern to highlight a specific position can
be slow.
Solution:   Add matchaddpos() to highlight specific positions
efficiently.
(Alexey Radkov.)

https://code.google.com/p/vim/source/detail?r=f9fa2e506b9f07549cd91074835c5c553db7b3a7
This commit is contained in:
Felipe Morales 2014-08-22 20:45:26 -04:00
parent 057f26f0a6
commit bf3d945798
8 changed files with 370 additions and 69 deletions

View File

@ -880,6 +880,28 @@ typedef struct {
proftime_T tm; /* for a time limit */
} match_T;
/// number of positions supported by matchaddpos()
#define MAXPOSMATCH 8
/// Same as lpos_T, but with additional field len.
typedef struct
{
linenr_T lnum; ///< line number
colnr_T col; ///< column number
int len; ///< length: 0 - to the end of line
} llpos_T;
/// posmatch_T provides an array for storing match items for matchaddpos()
/// function.
typedef struct posmatch posmatch_T;
struct posmatch
{
llpos_T pos[MAXPOSMATCH]; ///< array of positions
int cur; ///< internal position counter
linenr_T toplnum; ///< top buffer line
linenr_T botlnum; ///< bottom buffer line
};
/*
* matchitem_T provides a linked list for storing match items for ":match" and
* the match functions.
@ -892,6 +914,7 @@ struct matchitem {
char_u *pattern; /* pattern to highlight */
int hlg_id; /* highlight group ID */
regmmatch_T match; /* regexp program for pattern */
posmatch_T pos; // position matches
match_T hl; /* struct for doing the actual highlighting */
};

View File

@ -6456,6 +6456,7 @@ static struct fst {
{"mapcheck", 1, 3, f_mapcheck},
{"match", 2, 4, f_match},
{"matchadd", 2, 4, f_matchadd},
{"matchaddpos", 2, 4, f_matchaddpos},
{"matcharg", 1, 1, f_matcharg},
{"matchdelete", 1, 1, f_matchdelete},
{"matchend", 2, 4, f_matchend},
@ -9300,12 +9301,34 @@ static void f_getline(typval_T *argvars, typval_T *rettv)
static void f_getmatches(typval_T *argvars, typval_T *rettv)
{
matchitem_T *cur = curwin->w_match_head;
int i;
rettv_list_alloc(rettv);
while (cur != NULL) {
dict_T *dict = dict_alloc();
if (cur->match.regprog == NULL) {
// match added with matchaddpos()
for (i = 0; i < MAXPOSMATCH; ++i) {
llpos_T *llpos;
char buf[6];
llpos = &cur->pos.pos[i];
if (llpos->lnum == 0) {
break;
}
list_T *l = list_alloc();
list_append_number(l, (varnumber_T)llpos->lnum);
if (llpos->col > 0) {
list_append_number(l, (varnumber_T)llpos->col);
list_append_number(l, (varnumber_T)llpos->len);
}
sprintf(buf, "pos%d", i + 1);
dict_add_list(dict, buf, l);
}
} else {
dict_add_nr_str(dict, "pattern", 0L, cur->pattern);
}
dict_add_nr_str(dict, "group", 0L, syn_id2name(cur->hlg_id));
dict_add_nr_str(dict, "pattern", 0L, cur->pattern);
dict_add_nr_str(dict, "priority", (long)cur->priority, NULL);
dict_add_nr_str(dict, "id", (long)cur->id, NULL);
list_append_dict(rettv->vval.v_list, dict);
@ -11104,7 +11127,52 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv)
return;
}
rettv->vval.v_number = match_add(curwin, grp, pat, prio, id);
rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL);
}
static void f_matchaddpos(typval_T *argvars, typval_T *rettv) FUNC_ATTR_NONNULL_ALL
{
rettv->vval.v_number = -1;
char_u buf[NUMBUFLEN];
char_u *group;
group = get_tv_string_buf_chk(&argvars[0], buf);
if (group == NULL) {
return;
}
if (argvars[1].v_type != VAR_LIST) {
EMSG2(_(e_listarg), "matchaddpos()");
return;
}
list_T *l;
l = argvars[1].vval.v_list;
if (l == NULL) {
return;
}
int error = false;
int prio = 10;
int id = -1;
if (argvars[2].v_type != VAR_UNKNOWN) {
prio = get_tv_number_chk(&argvars[2], &error);
if (argvars[3].v_type != VAR_UNKNOWN) {
id = get_tv_number_chk(&argvars[3], &error);
}
}
if (error == true) {
return;
}
// id == 3 is ok because matchaddpos() is supposed to substitute :3match
if (id == 1 || id == 2) {
EMSGN("E798: ID is reserved for \"match\": %" PRId64, id);
return;
}
rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l);
}
/*
@ -12914,7 +12982,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv)
match_add(curwin, get_dict_string(d, (char_u *)"group", FALSE),
get_dict_string(d, (char_u *)"pattern", FALSE),
(int)get_dict_number(d, (char_u *)"priority"),
(int)get_dict_number(d, (char_u *)"id"));
(int)get_dict_number(d, (char_u *)"id"), NULL);
li = li->li_next;
}
rettv->vval.v_number = 0;

View File

@ -8859,7 +8859,7 @@ static void ex_match(exarg_T *eap)
c = *end;
*end = NUL;
match_add(curwin, g, p + 1, 10, id);
match_add(curwin, g, p + 1, 10, id, NULL);
free(g);
*end = c;
}

View File

@ -2658,38 +2658,42 @@ win_line (
shl->startcol = MAXCOL;
shl->endcol = MAXCOL;
shl->attr_cur = 0;
if (shl->rm.regprog != NULL) {
v = (long)(ptr - line);
next_search_hl(wp, shl, lnum, (colnr_T)v);
v = (long)(ptr - line);
if (cur != NULL) {
cur->pos.cur = 0;
}
next_search_hl(wp, shl, lnum, (colnr_T)v, cur);
/* Need to get the line again, a multi-line regexp may have made it
* invalid. */
line = ml_get_buf(wp->w_buffer, lnum, FALSE);
ptr = line + v;
// Need to get the line again, a multi-line regexp may have made it
// invalid.
line = ml_get_buf(wp->w_buffer, lnum, false);
ptr = line + v;
if (shl->lnum != 0 && shl->lnum <= lnum) {
if (shl->lnum == lnum)
shl->startcol = shl->rm.startpos[0].col;
else
shl->startcol = 0;
if (lnum == shl->lnum + shl->rm.endpos[0].lnum
- shl->rm.startpos[0].lnum)
shl->endcol = shl->rm.endpos[0].col;
else
shl->endcol = MAXCOL;
/* Highlight one character for an empty match. */
if (shl->startcol == shl->endcol) {
if (has_mbyte && line[shl->endcol] != NUL)
shl->endcol += (*mb_ptr2len)(line + shl->endcol);
else
++shl->endcol;
}
if ((long)shl->startcol < v) { /* match at leftcol */
shl->attr_cur = shl->attr;
search_attr = shl->attr;
}
area_highlighting = TRUE;
if (shl->lnum != 0 && shl->lnum <= lnum) {
if (shl->lnum == lnum) {
shl->startcol = shl->rm.startpos[0].col;
} else {
shl->startcol = 0;
}
if (lnum == shl->lnum + shl->rm.endpos[0].lnum
- shl->rm.startpos[0].lnum) {
shl->endcol = shl->rm.endpos[0].col;
} else {
shl->endcol = MAXCOL;
}
// Highlight one character for an empty match.
if (shl->startcol == shl->endcol) {
if (has_mbyte && line[shl->endcol] != NUL) {
shl->endcol += (*mb_ptr2len)(line + shl->endcol);
} else {
++shl->endcol;
}
}
if ((long)shl->startcol < v) { // match at leftcol
shl->attr_cur = shl->attr;
search_attr = shl->attr;
}
area_highlighting = true;
}
if (shl != &search_hl && cur != NULL)
cur = cur->next;
@ -2699,9 +2703,9 @@ win_line (
* when Visual mode is active, because it's not clear what is selected
* then. */
if (wp->w_p_cul && lnum == wp->w_cursor.lnum
&& !(wp == curwin && VIsual_active)) {
&& !(wp == curwin && VIsual_active)) {
line_attr = hl_attr(HLF_CUL);
area_highlighting = TRUE;
area_highlighting = true;
}
off = (unsigned)(current_ScreenLine - ScreenLines);
@ -2942,7 +2946,13 @@ win_line (
shl_flag = TRUE;
} else
shl = &cur->hl;
while (shl->rm.regprog != NULL) {
if (cur != NULL) {
cur->pos.cur = 0;
}
bool pos_inprogress = true; // mark that a position match search is
// in progress
while (shl->rm.regprog != NULL
|| (cur != NULL && pos_inprogress)) {
if (shl->startcol != MAXCOL
&& v >= (long)shl->startcol
&& v < (long)shl->endcol) {
@ -2950,7 +2960,8 @@ win_line (
} else if (v == (long)shl->endcol) {
shl->attr_cur = 0;
next_search_hl(wp, shl, lnum, (colnr_T)v);
next_search_hl(wp, shl, lnum, (colnr_T)v, cur);
pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
/* Need to get the line again, a multi-line regexp
* may have made it invalid. */
@ -5602,9 +5613,16 @@ static void prepare_search_hl(win_T *wp, linenr_T lnum)
NULL, NULL, TRUE, NULL))
break;
}
if (cur != NULL) {
cur->pos.cur = 0;
}
bool pos_inprogress = true; // mark that a position match search is
// in progress
n = 0;
while (shl->first_lnum < lnum && shl->rm.regprog != NULL) {
next_search_hl(wp, shl, shl->first_lnum, (colnr_T)n);
while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
|| (cur != NULL && pos_inprogress))) {
next_search_hl(wp, shl, shl->first_lnum, (colnr_T)n, cur);
pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
if (shl->lnum != 0) {
shl->first_lnum = shl->lnum
+ shl->rm.endpos[0].lnum
@ -5634,12 +5652,13 @@ next_search_hl (
win_T *win,
match_T *shl, /* points to search_hl or a match */
linenr_T lnum,
colnr_T mincol /* minimal column for a match */
colnr_T mincol, /* minimal column for a match */
matchitem_T *cur /* to retrieve match positions if any */
)
{
linenr_T l;
colnr_T matchcol;
long nmatched;
long nmatched = 0;
if (shl->lnum != 0) {
/* Check for three situations:
@ -5674,8 +5693,8 @@ next_search_hl (
if (shl->lnum == 0)
matchcol = 0;
else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
|| (shl->rm.endpos[0].lnum == 0
&& shl->rm.endpos[0].col <= shl->rm.startpos[0].col)) {
|| (shl->rm.endpos[0].lnum == 0
&& shl->rm.endpos[0].col <= shl->rm.startpos[0].col)) {
char_u *ml;
matchcol = shl->rm.startpos[0].col;
@ -5693,20 +5712,23 @@ next_search_hl (
matchcol = shl->rm.endpos[0].col;
shl->lnum = lnum;
nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol,
&(shl->tm)
);
if (called_emsg || got_int) {
/* Error while handling regexp: stop using this regexp. */
if (shl == &search_hl) {
/* don't free regprog in the match list, it's a copy */
vim_regfree(shl->rm.regprog);
SET_NO_HLSEARCH(TRUE);
if (shl->rm.regprog != NULL) {
nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol, &(shl->tm));
if (called_emsg || got_int) {
// Error while handling regexp: stop using this regexp.
if (shl == &search_hl) {
// don't free regprog in the match list, it's a copy
vim_regfree(shl->rm.regprog);
SET_NO_HLSEARCH(TRUE);
}
shl->rm.regprog = NULL;
shl->lnum = 0;
got_int = FALSE; /* avoid the "Type :quit to exit Vim"
message */
break;
}
shl->rm.regprog = NULL;
shl->lnum = 0;
got_int = FALSE; /* avoid the "Type :quit to exit Vim" message */
break;
} else if (cur != NULL) {
nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
}
if (nmatched == 0) {
shl->lnum = 0; /* no match found */
@ -5722,6 +5744,59 @@ next_search_hl (
}
}
static int
next_search_hl_pos(
match_T *shl, // points to a match
linenr_T lnum,
posmatch_T *posmatch, // match positions
colnr_T mincol // minimal column for a match
)
{
int i;
int bot = -1;
shl->lnum = 0;
for (i = posmatch->cur; i < MAXPOSMATCH; i++) {
if (posmatch->pos[i].lnum == 0) {
break;
}
if (posmatch->pos[i].col < mincol) {
continue;
}
if (posmatch->pos[i].lnum == lnum) {
if (shl->lnum == lnum) {
// partially sort positions by column numbers
// on the same line
if (posmatch->pos[i].col < posmatch->pos[bot].col) {
llpos_T tmp = posmatch->pos[i];
posmatch->pos[i] = posmatch->pos[bot];
posmatch->pos[bot] = tmp;
}
} else {
bot = i;
shl->lnum = lnum;
}
}
}
posmatch->cur = 0;
if (shl->lnum == lnum) {
colnr_T start = posmatch->pos[bot].col == 0
? 0: posmatch->pos[bot].col - 1;
colnr_T end = posmatch->pos[bot].col == 0
? MAXCOL : start + posmatch->pos[bot].len;
shl->rm.startpos[0].lnum = 0;
shl->rm.startpos[0].col = start;
shl->rm.endpos[0].lnum = 0;
shl->rm.endpos[0].col = end;
posmatch->cur = bot + 1;
return true;
}
return false;
}
static void screen_start_highlight(int attr)
{
attrentry_T *aep = NULL;

View File

@ -1,5 +1,5 @@
Test for ":match", ":2match", ":3match", "clearmatches()", "getmatches()",
"matchadd()", "matcharg()", "matchdelete()", and "setmatches()".
"matchadd()", "matchaddpos()", "matcharg()", "matchdelete()", and "setmatches()".
STARTTEST
:so small.vim
@ -147,9 +147,26 @@ STARTTEST
:unlet rf1
:unlet rf2
:unlet rf3
:highlight clear MyGroup1
:highlight clear MyGroup2
:highlight clear MyGroup3
:" --- Check that "matchaddpos()" positions matches correctly
:let @r .= "*** Test 11:\n"
:set nolazyredraw
:call setline(1, 'abcdefghijklmnopq')
:call matchaddpos("MyGroup1", [[1, 5], [1, 8, 3]], 10, 3)
:1
:redraw!
:let v1 = screenattr(1, 1)
:let v5 = screenattr(1, 5)
:let v6 = screenattr(1, 6)
:let v8 = screenattr(1, 8)
:let v10 = screenattr(1, 10)
:let v11 = screenattr(1, 11)
:let @r .= string(getmatches())."\n"
:if v1 != v5 && v6 == v1 && v8 == v5 && v10 == v5 && v11 == v1
: let @r .= "OK\n"
:else
: let @r .= "FAILED\n"
:endif
:call clearmatches()
G"rp
:/^Results/,$wq! test.out
ENDTEST

View File

@ -9,3 +9,6 @@ Results of test63:
*** Test 8: OK
*** Test 9: OK
*** Test 10: OK
*** Test 11:
[{'group': 'MyGroup1', 'id': 3, 'priority': 10, 'pos1': [1, 5, 1], 'pos2': [1, 8, 3]}]
OK

View File

@ -265,7 +265,7 @@ static int included_patches[] = {
//333 NA
//332 NA
331,
//330,
330,
329,
328,
327,

View File

@ -5190,16 +5190,18 @@ void restore_buffer(buf_T *save_curbuf)
* If no particular ID is desired, -1 must be specified for 'id'.
* Return ID of added match, -1 on failure.
*/
int match_add(win_T *wp, char_u *grp, char_u *pat, int prio, int id)
int match_add(win_T *wp, char_u *grp, char_u *pat, int prio, int id, list_T *pos_list)
{
matchitem_T *cur;
matchitem_T *prev;
matchitem_T *m;
int hlg_id;
regprog_T *regprog;
regprog_T *regprog = NULL;
int rtype = SOME_VALID;
if (*grp == NUL || *pat == NUL)
if (*grp == NUL || (pat != NULL && *pat == NUL)) {
return -1;
}
if (id < -1 || id == 0) {
EMSGN("E799: Invalid ID: %" PRId64
" (must be greater than or equal to 1)",
@ -5220,7 +5222,7 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, int prio, int id)
EMSG2(_(e_nogroup), grp);
return -1;
}
if ((regprog = vim_regcomp(pat, RE_MAGIC)) == NULL) {
if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL) {
EMSG2(_(e_invarg2), pat);
return -1;
}
@ -5236,15 +5238,108 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, int prio, int id)
}
/* Build new match. */
m = xmalloc(sizeof(matchitem_T));
m = xcalloc(1, sizeof(matchitem_T));
m->id = id;
m->priority = prio;
m->pattern = vim_strsave(pat);
m->pattern = pat == NULL ? NULL: vim_strsave(pat);
m->pos.cur = 0;
m->hlg_id = hlg_id;
m->match.regprog = regprog;
m->match.rmm_ic = FALSE;
m->match.rmm_maxcol = 0;
// Set up position matches
if (pos_list != NULL)
{
linenr_T toplnum = 0;
linenr_T botlnum = 0;
listitem_T *li;
int i;
for (i = 0, li = pos_list->lv_first; i < MAXPOSMATCH;
i++, li = li->li_next) {
linenr_T lnum = 0;
colnr_T col = 0;
int len = 1;
list_T *subl;
listitem_T *subli;
int error;
if (li == NULL) {
m->pos.pos[i].lnum = 0;
break;
}
if (li->li_tv.v_type == VAR_LIST) {
subl = li->li_tv.vval.v_list;
if (subl == NULL) {
goto fail;
}
subli = subl->lv_first;
if (subli == NULL) {
goto fail;
}
lnum = get_tv_number_chk(&subli->li_tv, &error);
if (error == TRUE) {
goto fail;
}
m->pos.pos[i].lnum = lnum;
if (lnum == 0) {
--i;
continue;
}
subli = subli->li_next;
if (subli != NULL) {
col = get_tv_number_chk(&subli->li_tv, &error);
if (error == TRUE)
goto fail;
subli = subli->li_next;
if (subli != NULL) {
len = get_tv_number_chk(&subli->li_tv, &error);
if (error == true) {
goto fail;
}
}
}
m->pos.pos[i].col = col;
m->pos.pos[i].len = len;
} else if (li->li_tv.v_type == VAR_NUMBER) {
if (li->li_tv.vval.v_number == 0)
continue;
m->pos.pos[i].lnum = li->li_tv.vval.v_number;
m->pos.pos[i].col = 0;
m->pos.pos[i].len = 0;
} else {
EMSG(_("List or number required"));
goto fail;
}
if (toplnum == 0 || lnum < toplnum) {
toplnum = lnum;
}
if (botlnum == 0 || lnum > botlnum) {
botlnum = lnum;
}
}
// Calculate top and bottom lines for redrawing area
if (toplnum != 0){
if (wp->w_buffer->b_mod_set) {
if (wp->w_buffer->b_mod_top > toplnum) {
wp->w_buffer->b_mod_top = toplnum;
}
if (wp->w_buffer->b_mod_bot < botlnum) {
wp->w_buffer->b_mod_bot = botlnum;
}
} else {
wp->w_buffer->b_mod_top = toplnum;
wp->w_buffer->b_mod_bot = botlnum;
}
m->pos.toplnum = toplnum;
m->pos.botlnum = botlnum;
wp->w_buffer->b_mod_set = TRUE;
rtype = VALID;
}
}
/* Insert new match. The match list is in ascending order with regard to
* the match priorities. */
cur = wp->w_match_head;
@ -5259,8 +5354,12 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, int prio, int id)
prev->next = m;
m->next = cur;
redraw_later(SOME_VALID);
redraw_later(rtype);
return id;
fail:
free(m);
return -1;
}
/*
@ -5271,6 +5370,7 @@ int match_delete(win_T *wp, int id, int perr)
{
matchitem_T *cur = wp->w_match_head;
matchitem_T *prev = cur;
int rtype = SOME_VALID;
if (id < 1) {
if (perr == TRUE)
@ -5294,8 +5394,23 @@ int match_delete(win_T *wp, int id, int perr)
prev->next = cur->next;
vim_regfree(cur->match.regprog);
free(cur->pattern);
if (cur->pos.toplnum != 0) {
if (wp->w_buffer->b_mod_set) {
if (wp->w_buffer->b_mod_top > cur->pos.toplnum) {
wp->w_buffer->b_mod_top = cur->pos.toplnum;
}
if (wp->w_buffer->b_mod_bot < cur->pos.botlnum) {
wp->w_buffer->b_mod_bot = cur->pos.botlnum;
}
} else {
wp->w_buffer->b_mod_top = cur->pos.toplnum;
wp->w_buffer->b_mod_bot = cur->pos.botlnum;
}
wp->w_buffer->b_mod_set = TRUE;
rtype = VALID;
}
free(cur);
redraw_later(SOME_VALID);
redraw_later(rtype);
return 0;
}