diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2014-09-05 20:57:41 -0400 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2014-09-05 20:57:41 -0400 |
commit | 3f5482d3251b3bbdc7c4df8a8e64eb3af7e5922c (patch) | |
tree | c3703170cc71c81da50b25fd0a8ff24af387d594 | |
parent | d66cd61b79d6d592436c4552dbdddb904dd1c22e (diff) | |
parent | d860ba45e25231ae54c6fd12ecd00dc936780184 (diff) | |
download | rneovim-3f5482d3251b3bbdc7c4df8a8e64eb3af7e5922c.tar.gz rneovim-3f5482d3251b3bbdc7c4df8a8e64eb3af7e5922c.tar.bz2 rneovim-3f5482d3251b3bbdc7c4df8a8e64eb3af7e5922c.zip |
Merge pull request #1107 from fmoralesc/matchaddpos
Add matchaddpos()-related vim patches. [vim-patch: 7.4.330, 7.4.334, 7.4.343, 7.4.344, 7.4.362]
-rw-r--r-- | src/nvim/buffer_defs.h | 23 | ||||
-rw-r--r-- | src/nvim/eval.c | 74 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 2 | ||||
-rw-r--r-- | src/nvim/screen.c | 176 | ||||
-rw-r--r-- | src/nvim/testdir/test63.in | 25 | ||||
-rw-r--r-- | src/nvim/testdir/test63.ok | 3 | ||||
-rw-r--r-- | src/nvim/version.c | 10 | ||||
-rw-r--r-- | src/nvim/window.c | 128 |
8 files changed, 369 insertions, 72 deletions
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 3b12925119..de1b0985bb 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -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 */ }; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index a9570ecc84..a61f082e59 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -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}, @@ -9301,12 +9302,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); @@ -11105,7 +11128,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); } /* @@ -12927,7 +12995,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; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index ab7add1c5b..2dc5ad436d 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -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; } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 811f265902..91db983525 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -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) + 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 + } 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; } + // 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,15 +2946,22 @@ 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) { shl->attr_cur = shl->attr; - } else if (v == (long)shl->endcol) { + } 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,22 @@ 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 +5743,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; diff --git a/src/nvim/testdir/test63.in b/src/nvim/testdir/test63.in index 74339c3e35..ea66ee6dea 100644 --- a/src/nvim/testdir/test63.in +++ b/src/nvim/testdir/test63.in @@ -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 diff --git a/src/nvim/testdir/test63.ok b/src/nvim/testdir/test63.ok index 14973985eb..f804b693ac 100644 --- a/src/nvim/testdir/test63.ok +++ b/src/nvim/testdir/test63.ok @@ -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 diff --git a/src/nvim/version.c b/src/nvim/version.c index b164f94bb1..75b4ac4b4d 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -233,7 +233,7 @@ static int included_patches[] = { //365, //364, //363, - //362, + 362, //361, //360, //359, @@ -251,8 +251,8 @@ static int included_patches[] = { //347, 346, //345, - //344, - //343, + 344, + 343, //342 NA //341, //340 NA @@ -261,11 +261,11 @@ static int included_patches[] = { //337, //336, 335, - //334, + 334, //333 NA //332 NA 331, - //330, + 330, 329, 328, 327, diff --git a/src/nvim/window.c b/src/nvim/window.c index 3499b14688..becc21be89 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -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,105 @@ 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->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; li != NULL && 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 = false; + + 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; + } + if (lnum == 0) { + --i; + continue; + } + m->pos.pos[i].lnum = lnum; + 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) { + --i; + 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 + 1; + } + } + + // 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 +5351,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 +5367,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 +5391,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; } |