Fixes this bug / feature request: Allow more than 3 ":match" items. The functions "clearmatches()", "getmatches()", "matchadd()", "matchdelete()", and "setmatches()" are added and documented. The command ":[N]match" and the function "matcharg()" are changed to work transparently with the data structures introduced by the new functions. A small bug in syntax.c is fixed, so newly created highlight groups can have their name resolved correctly from their ID. Created by Martin Toft Bay during Google Summer of Code 2007. *** runtime/doc/eval.txt.orig Mon Jul 16 00:57:57 2007 --- runtime/doc/eval.txt Mon Jul 16 00:31:21 2007 *************** *** 1557,1562 **** --- 1557,1563 ---- changenr() Number current change number char2nr( {expr}) Number ASCII value of first char in {expr} cindent( {lnum}) Number C indent for line {lnum} + clearmatches() None clear all matches col( {expr}) Number column nr of cursor or mark complete({startcol}, {matches}) String set Insert mode completion complete_add( {expr}) Number add completion match *************** *** 1622,1627 **** --- 1623,1629 ---- getline( {lnum}) String line {lnum} of current buffer getline( {lnum}, {end}) List lines {lnum} to {end} of current buffer getloclist({nr}) List list of location list items + getmatches() List list of current matches getpos( {expr}) List position of cursor, mark, etc. getqflist() List list of quickfix items getreg( [{regname} [, 1]]) String contents of register *************** *** 1676,1682 **** --- 1678,1687 ---- String check for mappings matching {name} match( {expr}, {pat}[, {start}[, {count}]]) Number position where {pat} matches in {expr} + matchadd( {group}, {pattern}[, {priority}[, {id}]]) + Number highlight {pattern} with {group} matcharg( {nr}) List arguments of |:match| + matchdelete( {id}) Number delete match identified by {id} matchend( {expr}, {pat}[, {start}[, {count}]]) Number position where {pat} ends in {expr} matchlist( {expr}, {pat}[, {start}[, {count}]]) *************** *** 1731,1736 **** --- 1736,1742 ---- setline( {lnum}, {line}) Number set line {lnum} to {line} setloclist( {nr}, {list}[, {action}]) Number modify location list using {list} + setmatches( {list}) Number restore a list of matches setpos( {expr}, {list}) none set the {expr} position to {list} setqflist( {list}[, {action}]) Number modify quickfix list using {list} setreg( {n}, {v}[, {opt}]) Number set register to value and type *************** *** 2012,2017 **** --- 2018,2027 ---- feature, -1 is returned. See |C-indenting|. + clearmatches() *clearmatches()* + Clears all matches previously defined by |matchadd()| and the + |:match| commands. + *col()* col({expr}) The result is a Number, which is the byte index of the column position given with {expr}. The accepted positions are: *************** *** 2914,2919 **** --- 2924,2951 ---- returned. For an invalid window number {nr}, an empty list is returned. Otherwise, same as getqflist(). + getmatches() *getmatches()* + Returns a list with all matches previously defined by + |matchadd()| and the |:match| commands. |getmatches()| is + useful in combination with |setmatches()|, as |setmatches()| + can restore a list of matches saved by |getmatches()|. + Example: > + :echo getmatches() + < [{'group': 'MyGroup1', 'pattern': 'TODO', + 'priority': 10, 'id': 1}, {'group': 'MyGroup2', + 'pattern': 'FIXME', 'priority': 10, 'id': 2}] > + :let m = getmatches() + :call clearmatches() + :echo getmatches() + < [] > + :call setmatches(m) + :echo getmatches() + < [{'group': 'MyGroup1', 'pattern': 'TODO', + 'priority': 10, 'id': 1}, {'group': 'MyGroup2', + 'pattern': 'FIXME', 'priority': 10, 'id': 2}] > + :unlet m + < + getqflist() *getqflist()* Returns a list with all the current quickfix errors. Each list item is a dictionary with these entries: *************** *** 3618,3624 **** --- 3650,3693 ---- the pattern. 'smartcase' is NOT used. The matching is always done like 'magic' is set and 'cpoptions' is empty. + matchadd({group}, {pattern}[, {priority}[, {id}]]) + Defines a pattern to be highlighted in the current window (a + "match"). It will be highlighted with {group}. Returns an + identification number (ID), which can be used to delete the + match using |matchdelete()|. + The optional {priority} argument assigns a priority to the + match. A match with a high priority will have its + highlighting overrule that of a match with a lower priority. + A priority is specified as an integer (negative numbers are no + exception). If the {priority} argument is not specified, the + default priority is 10. The priority of 'hlsearch' is zero, + hence all matches with a priority greater than zero will + overrule it. Syntax highlighting (see 'syntax') is a separate + mechanism, and regardless of the chosen priority a match will + always overrule syntax highlighting. + + The optional {id} argument allows the request for a specific + match ID. If a specified ID is already taken, an error + message will appear and the match will not be added. An ID + is specified as a positive integer (zero excluded). IDs 1, 2 + and 3 are reserved for |:match|, |:2match| and |:3match|, + respectively. If the {id} argument is not specified, + |matchadd()| automatically chooses a free ID. + + The number of matches is not limited, as it is the case with + the |:match| commands. + + Example: > + :highlight MyGroup ctermbg=green guibg=green + :let m = matchadd("MyGroup", "TODO") + < Deletion of the pattern: > + :matchdelete(m) + + < A list of matches defined by |matchadd()| and |:match| are + available from |getmatches()|. All matches can be deleted in + one operation by |clearmatches()|. + matcharg({nr}) *matcharg()* Selects the {nr} match item, as set with a |:match|, |:2match| or |:3match| command. *************** *** 3627,3634 **** The pattern used. When {nr} is not 1, 2 or 3 returns an empty |List|. When there is no match item set returns ['', '']. ! This is usef to save and restore a |:match|. matchend({expr}, {pat}[, {start}[, {count}]]) *matchend()* Same as match(), but return the index of first character after --- 3696,3710 ---- The pattern used. When {nr} is not 1, 2 or 3 returns an empty |List|. When there is no match item set returns ['', '']. ! This is used to save and restore a |:match|. ! Highlighting matches using the |:match| commands are limited ! to three matches. |matchadd()| does not have this limitation. + matchdelete({id}) *matchdelete()* + Deletes a match with ID {id} previously defined by |matchadd()| + and the |:match| commands. Returns 0 if succesfull, otherwise + -1. See example for |matchadd()|. All matches can be deleted + in one operation by |clearmatches()|. matchend({expr}, {pat}[, {start}[, {count}]]) *matchend()* Same as match(), but return the index of first character after *************** *** 4382,4387 **** --- 4458,4468 ---- list window, the displayed location list is modified. For an invalid window number {nr}, -1 is returned. Otherwise, same as setqflist(). + + setmatches({list}) *setmatches()* + Restores a list of matches saved by |getmatches()|. Returns 0 + if succesfull, otherwise -1. All current matches are cleared + before the list is restored. See example for |getmatches()|. *setpos()* setpos({expr}, {list}) *** runtime/doc/pattern.txt.orig Fri Jul 13 23:27:28 2007 --- runtime/doc/pattern.txt Sun Jul 15 11:54:51 2007 *************** *** 1212,1218 **** {group} must exist at the moment this command is executed. The {group} highlighting still applies when a character is ! to be highlighted for 'hlsearch'. Note that highlighting the last used search pattern with 'hlsearch' is used in all windows, while the pattern defined --- 1212,1221 ---- {group} must exist at the moment this command is executed. The {group} highlighting still applies when a character is ! to be highlighted for 'hlsearch', as the highlighting for ! matches is given higher priority than that of 'hlsearch'. ! Syntax highlighting (see 'syntax') is also overruled by ! matches. Note that highlighting the last used search pattern with 'hlsearch' is used in all windows, while the pattern defined *************** *** 1226,1233 **** display you may get unexpected results. That is because Vim looks for a match in the line where redrawing starts. ! Also see |matcharg()|, it returns the highlight group and ! pattern of a previous :match command. Another example, which highlights all characters in virtual column 72 and more: > --- 1229,1243 ---- display you may get unexpected results. That is because Vim looks for a match in the line where redrawing starts. ! Also see |matcharg()|and |getmatches()|. The former returns ! the highlight group and pattern of a previous |:match| ! command. The latter returns a list with highlight groups and ! patterns defined by both |matchadd()| and |:match|. ! ! Highlighting matches using |:match| are limited to three ! matches (aside from |:match|, |:2match| and |:3match|are ! available). |matchadd()| does not have this limitation and in ! addition makes it possible to prioritize matches. Another example, which highlights all characters in virtual column 72 and more: > *** runtime/doc/usr_41.txt.orig Fri Jul 13 23:27:30 2007 --- runtime/doc/usr_41.txt Fri Jul 13 23:28:15 2007 *************** *** 763,775 **** --- 763,784 ---- foldtextresult() get the text displayed for a closed fold Syntax and highlighting: + clearmatches() clear all matches defined by |matchadd()| and + the |:match| commands + getmatches() get all matches defined by |matchadd()| and + the |:match| commands hlexists() check if a highlight group exists hlID() get ID of a highlight group synID() get syntax ID at a specific position synIDattr() get a specific attribute of a syntax ID synIDtrans() get translated syntax ID diff_hlID() get highlight ID for diff mode at a position + matchadd() define a pattern to highlight (a "match") matcharg() get info about |:match| arguments + matchdelete() delete a match defined by |matchadd()| or a + |:match| command + setmatches() restore a list of matches saved by + |getmatches()| Spelling: spellbadword() locate badly spelled word at or after cursor *** src/eval.c.orig Fri Jul 13 23:27:39 2007 --- src/eval.c Mon Jul 16 01:40:36 2007 *************** *** 475,480 **** --- 475,481 ---- static void f_changenr __ARGS((typval_T *argvars, typval_T *rettv)); static void f_char2nr __ARGS((typval_T *argvars, typval_T *rettv)); static void f_cindent __ARGS((typval_T *argvars, typval_T *rettv)); + static void f_clearmatches __ARGS((typval_T *argvars, typval_T *rettv)); static void f_col __ARGS((typval_T *argvars, typval_T *rettv)); #if defined(FEAT_INS_EXPAND) static void f_complete __ARGS((typval_T *argvars, typval_T *rettv)); *************** *** 529,534 **** --- 530,536 ---- static void f_getftime __ARGS((typval_T *argvars, typval_T *rettv)); static void f_getftype __ARGS((typval_T *argvars, typval_T *rettv)); static void f_getline __ARGS((typval_T *argvars, typval_T *rettv)); + static void f_getmatches __ARGS((typval_T *argvars, typval_T *rettv)); static void f_getpos __ARGS((typval_T *argvars, typval_T *rettv)); static void f_getqflist __ARGS((typval_T *argvars, typval_T *rettv)); static void f_getreg __ARGS((typval_T *argvars, typval_T *rettv)); *************** *** 577,583 **** --- 579,587 ---- static void f_maparg __ARGS((typval_T *argvars, typval_T *rettv)); static void f_mapcheck __ARGS((typval_T *argvars, typval_T *rettv)); static void f_match __ARGS((typval_T *argvars, typval_T *rettv)); + static void f_matchadd __ARGS((typval_T *argvars, typval_T *rettv)); static void f_matcharg __ARGS((typval_T *argvars, typval_T *rettv)); + static void f_matchdelete __ARGS((typval_T *argvars, typval_T *rettv)); static void f_matchend __ARGS((typval_T *argvars, typval_T *rettv)); static void f_matchlist __ARGS((typval_T *argvars, typval_T *rettv)); static void f_matchstr __ARGS((typval_T *argvars, typval_T *rettv)); *************** *** 618,623 **** --- 622,628 ---- static void f_setcmdpos __ARGS((typval_T *argvars, typval_T *rettv)); static void f_setline __ARGS((typval_T *argvars, typval_T *rettv)); static void f_setloclist __ARGS((typval_T *argvars, typval_T *rettv)); + static void f_setmatches __ARGS((typval_T *argvars, typval_T *rettv)); static void f_setpos __ARGS((typval_T *argvars, typval_T *rettv)); static void f_setqflist __ARGS((typval_T *argvars, typval_T *rettv)); static void f_setreg __ARGS((typval_T *argvars, typval_T *rettv)); *************** *** 7043,7048 **** --- 7048,7054 ---- {"changenr", 0, 0, f_changenr}, {"char2nr", 1, 1, f_char2nr}, {"cindent", 1, 1, f_cindent}, + {"clearmatches", 0, 0, f_clearmatches}, {"col", 1, 1, f_col}, #if defined(FEAT_INS_EXPAND) {"complete", 2, 2, f_complete}, *************** *** 7099,7104 **** --- 7105,7111 ---- {"getftype", 1, 1, f_getftype}, {"getline", 1, 2, f_getline}, {"getloclist", 1, 1, f_getqflist}, + {"getmatches", 0, 0, f_getmatches}, {"getpos", 1, 1, f_getpos}, {"getqflist", 0, 0, f_getqflist}, {"getreg", 0, 2, f_getreg}, *************** *** 7149,7155 **** --- 7156,7164 ---- {"maparg", 1, 3, f_maparg}, {"mapcheck", 1, 3, f_mapcheck}, {"match", 2, 4, f_match}, + {"matchadd", 2, 4, f_matchadd}, {"matcharg", 1, 1, f_matcharg}, + {"matchdelete", 1, 1, f_matchdelete}, {"matchend", 2, 4, f_matchend}, {"matchlist", 2, 4, f_matchlist}, {"matchstr", 2, 4, f_matchstr}, *************** *** 7190,7195 **** --- 7199,7205 ---- {"setcmdpos", 1, 1, f_setcmdpos}, {"setline", 2, 2, f_setline}, {"setloclist", 2, 3, f_setloclist}, + {"setmatches", 1, 1, f_setmatches}, {"setpos", 2, 2, f_setpos}, {"setqflist", 1, 2, f_setqflist}, {"setreg", 2, 3, f_setreg}, *************** *** 8240,8245 **** --- 8250,8268 ---- } /* + * "clearmatches()" function + */ + static void + f_clearmatches(argvars, rettv) + typval_T *argvars; + typval_T *rettv; + { + #ifdef FEAT_SEARCH_EXTRA + clear_matches(curwin); + #endif + } + + /* * "col(string)" function */ static void *************** *** 10275,10280 **** --- 10298,10335 ---- } /* + * "getmatches()" function + */ + static void + f_getmatches(argvars, rettv) + typval_T *argvars; + typval_T *rettv; + { + rettv->vval.v_number = 0; + #ifdef FEAT_SEARCH_EXTRA + dict_T *dict; + matchitem_T *cur = curwin->w_match_head; + + if (rettv_list_alloc(rettv) == OK) + { + while (cur != NULL) + { + dict = dict_alloc(); + if (dict == NULL) + return; + ++dict->dv_refcount; + dict_add_nr_str(dict, "group", NULL, syn_id2name(cur->hlg_id)); + dict_add_nr_str(dict, "pattern", NULL, cur->pattern); + dict_add_nr_str(dict, "priority", cur->priority, NULL); + dict_add_nr_str(dict, "id", cur->id, NULL); + list_append_dict(rettv->vval.v_list, dict); + cur = cur->next; + } + } + #endif + } + + /* * "getpos(string)" function */ static void *************** *** 12445,12450 **** --- 12500,12541 ---- } /* + * "matchadd()" function + */ + static void + f_matchadd(argvars, rettv) + typval_T *argvars; + typval_T *rettv; + { + #ifdef FEAT_SEARCH_EXTRA + char_u buf[NUMBUFLEN]; + char_u *grp = get_tv_string_buf_chk(&argvars[0], buf); /* group */ + char_u *pat = get_tv_string_buf_chk(&argvars[1], buf); /* pattern */ + int prio = 10; /* default priority */ + int id = -1; + int error = FALSE; + + rettv->vval.v_number = -1; + + if (grp == NULL || pat == NULL) + return; + 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; + if (id >= 1 && id <= 3) + { + EMSGN("E999: ID is reserved for \":match\": %ld", id); + return; + } + + rettv->vval.v_number = match_add(curwin, grp, pat, prio, id); + #endif + } + + /* * "matcharg()" function */ static void *************** *** 12455,12474 **** if (rettv_list_alloc(rettv) == OK) { #ifdef FEAT_SEARCH_EXTRA ! int mi = get_tv_number(&argvars[0]); ! if (mi >= 1 && mi <= 3) { ! list_append_string(rettv->vval.v_list, ! syn_id2name(curwin->w_match_id[mi - 1]), -1); ! list_append_string(rettv->vval.v_list, ! curwin->w_match_pat[mi - 1], -1); } #endif } } /* * "matchend()" function */ static void --- 12546,12587 ---- if (rettv_list_alloc(rettv) == OK) { #ifdef FEAT_SEARCH_EXTRA ! int id = get_tv_number(&argvars[0]); ! matchitem_T *m; ! if (id >= 1 && id <= 3) { ! if ((m = (matchitem_T *)get_match(curwin, id)) != NULL) ! { ! list_append_string(rettv->vval.v_list, ! syn_id2name(m->hlg_id), -1); ! list_append_string(rettv->vval.v_list, m->pattern, -1); ! } ! else ! { ! list_append_string(rettv->vval.v_list, NUL, -1); ! list_append_string(rettv->vval.v_list, NUL, -1); ! } } #endif } } /* + * "matchdelete()" function + */ + static void + f_matchdelete(argvars, rettv) + typval_T *argvars; + typval_T *rettv; + { + #ifdef FEAT_SEARCH_EXTRA + rettv->vval.v_number = match_delete(curwin, + (matchitem_T *)get_tv_number(&argvars[0]), TRUE); + #endif + } + + /* * "matchend()" function */ static void *************** *** 14503,14508 **** --- 14616,14681 ---- win = find_win_by_nr(&argvars[0], NULL); if (win != NULL) set_qf_ll_list(win, &argvars[1], &argvars[2], rettv); + } + + /* + * "setmatches()" function + */ + static void + f_setmatches(argvars, rettv) + typval_T *argvars; + typval_T *rettv; + { + #ifdef FEAT_SEARCH_EXTRA + list_T *l; + listitem_T *li; + dict_T *d; + + rettv->vval.v_number = -1; + if (argvars[0].v_type != VAR_LIST) + { + EMSG(_(e_listreq)); + return; + } + if ((l = argvars[0].vval.v_list) != NULL) + { + + /* To some extent make sure that we are dealing with a list from + * "getmatches()". */ + li = l->lv_first; + while (li != NULL) + { + if (li->li_tv.v_type != VAR_DICT + || (d = li->li_tv.vval.v_dict) == NULL) + { + EMSG(_(e_invarg)); + return; + } + if (!(dict_find(d, "group", -1) != NULL + && dict_find(d, "pattern", -1) != NULL + && dict_find(d, "priority", -1) != NULL + && dict_find(d, "id", -1) != NULL)) + { + EMSG(_(e_invarg)); + return; + } + li = li->li_next; + } + + clear_matches(curwin); + li = l->lv_first; + while (li != NULL) + { + d = li->li_tv.vval.v_dict; + match_add(curwin, get_dict_string(d, "group", FALSE), + get_dict_string(d, "pattern", FALSE), + get_dict_number(d, "priority"), + get_dict_number(d, "id")); + li = li->li_next; + } + rettv->vval.v_number = 0; + } + #endif } /* *** src/ex_docmd.c.orig Fri Jul 13 23:27:43 2007 --- src/ex_docmd.c Fri Jul 13 23:28:15 2007 *************** *** 10817,10828 **** exarg_T *eap; { char_u *p; char_u *end; int c; ! int mi; if (eap->line2 <= 3) ! mi = eap->line2 - 1; else { EMSG(e_invcmd); --- 10817,10829 ---- exarg_T *eap; { char_u *p; + char_u *g; char_u *end; int c; ! int id; if (eap->line2 <= 3) ! id = eap->line2; else { EMSG(e_invcmd); *************** *** 10831,10843 **** /* First clear any old pattern. */ if (!eap->skip) ! { ! vim_free(curwin->w_match[mi].regprog); ! curwin->w_match[mi].regprog = NULL; ! vim_free(curwin->w_match_pat[mi]); ! curwin->w_match_pat[mi] = NULL; ! redraw_later(SOME_VALID); /* always need a redraw */ ! } if (ends_excmd(*eap->arg)) end = eap->arg; --- 10832,10838 ---- /* First clear any old pattern. */ if (!eap->skip) ! match_delete(curwin, id, FALSE); if (ends_excmd(*eap->arg)) end = eap->arg; *************** *** 10848,10862 **** { p = skiptowhite(eap->arg); if (!eap->skip) ! { ! curwin->w_match_id[mi] = syn_namen2id(eap->arg, ! (int)(p - eap->arg)); ! if (curwin->w_match_id[mi] == 0) ! { ! EMSG2(_(e_nogroup), eap->arg); ! return; ! } ! } p = skipwhite(p); if (*p == NUL) { --- 10843,10849 ---- { p = skiptowhite(eap->arg); if (!eap->skip) ! g = vim_strnsave(eap->arg, (int)(p - eap->arg)); p = skipwhite(p); if (*p == NUL) { *************** *** 10880,10893 **** c = *end; *end = NUL; ! curwin->w_match[mi].regprog = vim_regcomp(p + 1, RE_MAGIC); ! if (curwin->w_match[mi].regprog == NULL) ! { ! EMSG2(_(e_invarg2), p); ! *end = c; ! return; ! } ! curwin->w_match_pat[mi] = vim_strsave(p + 1); *end = c; } } --- 10867,10874 ---- c = *end; *end = NUL; ! match_add(curwin, g, p + 1, 10, id); ! vim_free(g); *end = c; } } *** src/screen.c.orig Fri Jul 13 23:27:47 2007 --- src/screen.c Fri Jul 13 23:28:15 2007 *************** *** 100,126 **** static int screen_cur_row, screen_cur_col; /* last known cursor position */ #ifdef FEAT_SEARCH_EXTRA - /* - * Struct used for highlighting 'hlsearch' matches for the last use search - * pattern or a ":match" item. - * For 'hlsearch' there is one pattern for all windows. For ":match" there is - * a different pattern for each window. - */ - typedef struct - { - regmmatch_T rm; /* points to the regexp program; contains last found - match (may continue in next line) */ - buf_T *buf; /* the buffer to search for a match */ - linenr_T lnum; /* the line to search for a match */ - int attr; /* attributes to be used for a match */ - int attr_cur; /* attributes currently active in win_line() */ - linenr_T first_lnum; /* first lnum to search for multi-line pat */ - colnr_T startcol; /* in win_line() points to char where HL starts */ - colnr_T endcol; /* in win_line() points to char where HL ends */ - } match_T; - static match_T search_hl; /* used for 'hlsearch' highlight matching */ - static match_T match_hl[3]; /* used for ":match" highlight matching */ #endif #ifdef FEAT_FOLDING --- 100,106 ---- *************** *** 155,160 **** --- 135,141 ---- static void redraw_custum_statusline __ARGS((win_T *wp)); #endif #ifdef FEAT_SEARCH_EXTRA + #define SEARCH_HL_PRIORITY 0 static void start_search_hl __ARGS((void)); static void end_search_hl __ARGS((void)); static void prepare_search_hl __ARGS((win_T *wp, linenr_T lnum)); *************** *** 787,792 **** --- 768,774 ---- w_topline got smaller a bit */ #endif #ifdef FEAT_SEARCH_EXTRA + matchitem_T *cur; /* points to the match list */ int top_to_mod = FALSE; /* redraw above mod_top */ #endif *************** *** 848,865 **** #endif #ifdef FEAT_SEARCH_EXTRA ! /* Setup for ":match" and 'hlsearch' highlighting. Disable any previous * match */ ! for (i = 0; i < 3; ++i) { ! match_hl[i].rm = wp->w_match[i]; ! if (wp->w_match_id[i] == 0) ! match_hl[i].attr = 0; else ! match_hl[i].attr = syn_id2attr(wp->w_match_id[i]); ! match_hl[i].buf = buf; ! match_hl[i].lnum = 0; ! match_hl[i].first_lnum = 0; } search_hl.buf = buf; search_hl.lnum = 0; --- 830,849 ---- #endif #ifdef FEAT_SEARCH_EXTRA ! /* Setup for match and 'hlsearch' highlighting. Disable any previous * match */ ! cur = wp->w_match_head; ! while (cur != NULL) { ! cur->hl.rm = cur->match; ! if (cur->hlg_id == 0) ! cur->hl.attr = 0; else ! cur->hl.attr = syn_id2attr(cur->hlg_id); ! cur->hl.buf = buf; ! cur->hl.lnum = 0; ! cur->hl.first_lnum = 0; ! cur = cur->next; } search_hl.buf = buf; search_hl.lnum = 0; *************** *** 923,941 **** * change in one line may make the Search highlighting in a * previous line invalid. Simple solution: redraw all visible * lines above the change. ! * Same for a ":match" pattern. */ if (search_hl.rm.regprog != NULL && re_multiline(search_hl.rm.regprog)) top_to_mod = TRUE; else ! for (i = 0; i < 3; ++i) ! if (match_hl[i].rm.regprog != NULL ! && re_multiline(match_hl[i].rm.regprog)) { top_to_mod = TRUE; break; } #endif } #ifdef FEAT_FOLDING --- 907,931 ---- * change in one line may make the Search highlighting in a * previous line invalid. Simple solution: redraw all visible * lines above the change. ! * Same for a match pattern. */ if (search_hl.rm.regprog != NULL && re_multiline(search_hl.rm.regprog)) top_to_mod = TRUE; else ! { ! cur = wp->w_match_head; ! while (cur != NULL) ! { ! if (cur->match.regprog != NULL ! && re_multiline(cur->match.regprog)) { top_to_mod = TRUE; break; } + cur = cur->next; + } + } #endif } #ifdef FEAT_FOLDING *************** *** 2626,2632 **** int line_attr = 0; /* atrribute for the whole line */ #endif #ifdef FEAT_SEARCH_EXTRA ! match_T *shl; /* points to search_hl or match_hl */ #endif #if defined(FEAT_SEARCH_EXTRA) || defined(FEAT_MBYTE) int i; --- 2616,2628 ---- int line_attr = 0; /* atrribute for the whole line */ #endif #ifdef FEAT_SEARCH_EXTRA ! matchitem_T *cur; /* points to the match list */ ! match_T *shl; /* points to search_hl or a match */ ! int shl_flag; /* flag to indicate whether search_hl ! has been processed or not */ ! int prevcol_hl_flag; /* flag to indicate whether prevcol ! equals startcol of search_hl or one ! of the matches */ #endif #if defined(FEAT_SEARCH_EXTRA) || defined(FEAT_MBYTE) int i; *************** *** 3074,3085 **** #ifdef FEAT_SEARCH_EXTRA /* ! * Handle highlighting the last used search pattern and ":match". ! * Do this for both search_hl and match_hl[3]. */ ! for (i = 3; i >= 0; --i) { ! shl = (i == 3) ? &search_hl : &match_hl[i]; shl->startcol = MAXCOL; shl->endcol = MAXCOL; shl->attr_cur = 0; --- 3070,3089 ---- #ifdef FEAT_SEARCH_EXTRA /* ! * Handle highlighting the last used search pattern and matches. ! * Do this for both search_hl and the match list. */ ! cur = wp->w_match_head; ! shl_flag = FALSE; ! while (cur != NULL || shl_flag == FALSE) { ! if (shl_flag == FALSE) ! { ! shl = &search_hl; ! shl_flag = TRUE; ! } ! else ! shl = &cur->hl; shl->startcol = MAXCOL; shl->endcol = MAXCOL; shl->attr_cur = 0; *************** *** 3122,3127 **** --- 3126,3133 ---- area_highlighting = TRUE; } } + if (shl != &search_hl && cur != NULL) + cur = cur->next; } #endif *************** *** 3388,3400 **** * After end, check for start/end of next match. * When another match, have to check for start again. * Watch out for matching an empty string! ! * Do this first for search_hl, then for match_hl, so that ! * ":match" overrules 'hlsearch'. */ v = (long)(ptr - line); ! for (i = 3; i >= 0; --i) { ! shl = (i == 3) ? &search_hl : &match_hl[i]; while (shl->rm.regprog != NULL) { if (shl->startcol != MAXCOL --- 3394,3417 ---- * After end, check for start/end of next match. * When another match, have to check for start again. * Watch out for matching an empty string! ! * Do this for 'search_hl' and the match list (ordered by ! * priority). */ v = (long)(ptr - line); ! cur = wp->w_match_head; ! shl_flag = FALSE; ! while (cur != NULL || shl_flag == FALSE) { ! if (shl_flag == FALSE ! && ((cur != NULL ! && cur->priority > SEARCH_HL_PRIORITY) ! || cur == NULL)) ! { ! shl = &search_hl; ! shl_flag = TRUE; ! } ! else ! shl = &cur->hl; while (shl->rm.regprog != NULL) { if (shl->startcol != MAXCOL *************** *** 3442,3458 **** } break; } } ! /* ":match" highlighting overrules 'hlsearch' */ ! for (i = 0; i <= 3; ++i) ! if (i == 3) ! search_attr = search_hl.attr_cur; ! else if (match_hl[i].attr_cur != 0) { ! search_attr = match_hl[i].attr_cur; ! break; } } #endif --- 3459,3490 ---- } break; } + if (shl != &search_hl && cur != NULL) + cur = cur->next; } ! /* Use attributes from match with highest priority among ! * 'search_hl' and the match list. */ ! search_attr = search_hl.attr_cur; ! cur = wp->w_match_head; ! shl_flag = FALSE; ! while (cur != NULL || shl_flag == FALSE) ! { ! if (shl_flag == FALSE ! && ((cur != NULL ! && cur->priority > SEARCH_HL_PRIORITY) ! || cur == NULL)) { ! shl = &search_hl; ! shl_flag = TRUE; } + else + shl = &cur->hl; + if (shl->attr_cur != 0) + search_attr = shl->attr_cur; + if (shl != &search_hl && cur != NULL) + cur = cur->next; + } } #endif *************** *** 4256,4269 **** * highlight match at end of line. If it's beyond the last * char on the screen, just overwrite that one (tricky!) Not * needed when a '$' was displayed for 'list'. */ if (lcs_eol == lcs_eol_one && ((area_attr != 0 && vcol == fromcol && c == NUL) #ifdef FEAT_SEARCH_EXTRA /* highlight 'hlsearch' match at end of line */ ! || ((prevcol == (long)search_hl.startcol ! || prevcol == (long)match_hl[0].startcol ! || prevcol == (long)match_hl[1].startcol ! || prevcol == (long)match_hl[2].startcol) # if defined(LINE_ATTR) && did_line_attr <= 1 # endif --- 4288,4316 ---- * highlight match at end of line. If it's beyond the last * char on the screen, just overwrite that one (tricky!) Not * needed when a '$' was displayed for 'list'. */ + #ifdef FEAT_SEARCH_EXTRA + prevcol_hl_flag = FALSE; + if (prevcol == (long)search_hl.startcol) + prevcol_hl_flag = TRUE; + else + { + cur = wp->w_match_head; + while (cur != NULL) + { + if (prevcol == (long)cur->hl.startcol) + { + prevcol_hl_flag = TRUE; + break; + } + cur = cur->next; + } + } + #endif if (lcs_eol == lcs_eol_one && ((area_attr != 0 && vcol == fromcol && c == NUL) #ifdef FEAT_SEARCH_EXTRA /* highlight 'hlsearch' match at end of line */ ! || (prevcol_hl_flag == TRUE # if defined(LINE_ATTR) && did_line_attr <= 1 # endif *************** *** 4304,4318 **** #ifdef FEAT_SEARCH_EXTRA if (area_attr == 0) { ! for (i = 0; i <= 3; ++i) { ! if (i == 3) ! char_attr = search_hl.attr; ! else if ((ptr - line) - 1 == (long)match_hl[i].startcol) { ! char_attr = match_hl[i].attr; ! break; } } } #endif --- 4351,4377 ---- #ifdef FEAT_SEARCH_EXTRA if (area_attr == 0) { ! /* Use attributes from match with highest priority among ! * 'search_hl' and the match list. */ ! char_attr = search_hl.attr; ! cur = wp->w_match_head; ! shl_flag = FALSE; ! while (cur != NULL || shl_flag == FALSE) { ! if (shl_flag == FALSE ! && ((cur != NULL ! && cur->priority > SEARCH_HL_PRIORITY) ! || cur == NULL)) { ! shl = &search_hl; ! shl_flag = TRUE; } + else + shl = &cur->hl; + if ((ptr - line) - 1 == (long)shl->startcol) + char_attr = shl->attr; + if (shl != &search_hl && cur != NULL) + cur = cur->next; } } #endif *************** *** 6320,6326 **** #ifdef FEAT_SEARCH_EXTRA /* ! * Prepare for 'searchhl' highlighting. */ static void start_search_hl() --- 6379,6385 ---- #ifdef FEAT_SEARCH_EXTRA /* ! * Prepare for 'hlsearch' highlighting. */ static void start_search_hl() *************** *** 6333,6339 **** } /* ! * Clean up for 'searchhl' highlighting. */ static void end_search_hl() --- 6392,6398 ---- } /* ! * Clean up for 'hlsearch' highlighting. */ static void end_search_hl() *************** *** 6353,6370 **** win_T *wp; linenr_T lnum; { ! match_T *shl; /* points to search_hl or match_hl */ int n; int i; /* * When using a multi-line pattern, start searching at the top * of the window or just after a closed fold. ! * Do this both for search_hl and match_hl[3]. */ ! for (i = 3; i >= 0; --i) { ! shl = (i == 3) ? &search_hl : &match_hl[i]; if (shl->rm.regprog != NULL && shl->lnum == 0 && re_multiline(shl->rm.regprog)) --- 6412,6440 ---- win_T *wp; linenr_T lnum; { ! matchitem_T *cur; /* points to the match list */ ! match_T *shl; /* points to search_hl or a match */ ! int shl_flag; /* flag to indicate whether search_hl ! has been processed or not */ int n; int i; /* * When using a multi-line pattern, start searching at the top * of the window or just after a closed fold. ! * Do this both for search_hl and the match list. */ ! cur = wp->w_match_head; ! shl_flag = FALSE; ! while (cur != NULL || shl_flag == FALSE) { ! if (shl_flag == FALSE) ! { ! shl = &search_hl; ! shl_flag = TRUE; ! } ! else ! shl = &cur->hl; if (shl->rm.regprog != NULL && shl->lnum == 0 && re_multiline(shl->rm.regprog)) *************** *** 6399,6409 **** } } } } } /* ! * Search for a next 'searchl' or ":match" match. * Uses shl->buf. * Sets shl->lnum and shl->rm contents. * Note: Assumes a previous match is always before "lnum", unless --- 6469,6481 ---- } } } + if (shl != &search_hl && cur != NULL) + cur = cur->next; } } /* ! * Search for a next 'hlsearch' or match. * Uses shl->buf. * Sets shl->lnum and shl->rm contents. * Note: Assumes a previous match is always before "lnum", unless *************** *** 6413,6419 **** static void next_search_hl(win, shl, lnum, mincol) win_T *win; ! match_T *shl; /* points to search_hl or match_hl */ linenr_T lnum; colnr_T mincol; /* minimal column for a match */ { --- 6485,6491 ---- static void next_search_hl(win, shl, lnum, mincol) win_T *win; ! match_T *shl; /* points to search_hl or a match */ linenr_T lnum; colnr_T mincol; /* minimal column for a match */ { *************** *** 6481,6487 **** /* Error while handling regexp: stop using this regexp. */ if (shl == &search_hl) { ! /* don't free the regprog in match_hl[], it's a copy */ vim_free(shl->rm.regprog); no_hlsearch = TRUE; } --- 6553,6559 ---- /* 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_free(shl->rm.regprog); no_hlsearch = TRUE; } *** src/structs.h.orig Fri Jul 13 23:27:48 2007 --- src/structs.h Fri Jul 13 23:28:15 2007 *************** *** 1693,1699 **** --- 1693,1736 ---- #define FR_ROW 1 /* frame with a row of windows */ #define FR_COL 2 /* frame with a column of windows */ + #ifdef FEAT_SEARCH_EXTRA /* + * Struct used for highlighting 'hlsearch' matches for the last use search + * pattern or a ":match" item. + * For 'hlsearch' there is one pattern for all windows. For ":match" there is + * a different pattern for each window. + */ + typedef struct + { + regmmatch_T rm; /* points to the regexp program; contains last found + match (may continue in next line) */ + buf_T *buf; /* the buffer to search for a match */ + linenr_T lnum; /* the line to search for a match */ + int attr; /* attributes to be used for a match */ + int attr_cur; /* attributes currently active in win_line() */ + linenr_T first_lnum; /* first lnum to search for multi-line pat */ + colnr_T startcol; /* in win_line() points to char where HL starts */ + colnr_T endcol; /* in win_line() points to char where HL ends */ + } match_T; + + /* + * matchitem_T provides a linked list for storing match items for ":match" and + * the match functions. + */ + typedef struct matchitem matchitem_T; + struct matchitem + { + int id; /* match ID */ + int priority; /* match priority */ + char_u *pattern; /* pattern to highlight */ + int hlg_id; /* highlight group ID */ + regmmatch_T match; /* regexp program for pattern */ + match_T hl; /* struct for doing the actual highlighting */ + matchitem_T *next; + }; + #endif + + /* * Structure which contains all information that belongs to a window * * All row numbers are relative to the start of the window, except w_winrow. *************** *** 1934,1942 **** #endif #ifdef FEAT_SEARCH_EXTRA ! regmmatch_T w_match[3]; /* regexp programs for ":match" */ ! char_u *(w_match_pat[3]); /* patterns for ":match" */ ! int w_match_id[3]; /* highlight IDs for ":match" */ #endif /* --- 1971,1978 ---- #endif #ifdef FEAT_SEARCH_EXTRA ! matchitem_T *w_match_head; /* head of match list */ ! int w_next_match_id; /* next match ID */ #endif /* *** src/syntax.c.orig Fri Jul 13 23:27:53 2007 --- src/syntax.c Fri Jul 13 23:28:15 2007 *************** *** 8479,8485 **** syn_id2name(id) int id; { ! if (id <= 0 || id >= highlight_ga.ga_len) return (char_u *)""; return HL_TABLE()[id - 1].sg_name; } --- 8479,8485 ---- syn_id2name(id) int id; { ! if (id <= 0 || id > highlight_ga.ga_len) return (char_u *)""; return HL_TABLE()[id - 1].sg_name; } *** src/window.c.orig Fri Jul 13 23:27:55 2007 --- src/window.c Fri Jul 13 23:28:15 2007 *************** *** 78,83 **** --- 78,90 ---- static win_T *win_alloc __ARGS((win_T *after)); static void win_new_height __ARGS((win_T *, int)); + #ifdef FEAT_SEARCH_EXTRA + int match_add __ARGS((win_T *wp, char_u *grp, char_u *pat, int prio, int id)); + int match_delete __ARGS((win_T *wp, int id, int perr)); + void clear_matches __ARGS((win_T *wp)); + matchitem_T *get_match __ARGS((win_T *wp, int id)); + #endif + #define URL_SLASH 1 /* path_is_url() has found "://" */ #define URL_BACKSLASH 2 /* path_is_url() has found ":\\" */ *************** *** 4128,4133 **** --- 4135,4144 ---- #ifdef FEAT_AUTOCMD --autocmd_block; #endif + #ifdef FEAT_SEARCH_EXTRA + newwin->w_match_head = NULL; + newwin->w_next_match_id = 4; + #endif } return newwin; } *************** *** 4185,4195 **** vim_free(wp->w_tagstack[i].tagname); vim_free(wp->w_localdir); #ifdef FEAT_SEARCH_EXTRA ! vim_free(wp->w_match[0].regprog); ! vim_free(wp->w_match[1].regprog); ! vim_free(wp->w_match[2].regprog); #endif #ifdef FEAT_JUMPLIST free_jumplist(wp); #endif --- 4196,4206 ---- vim_free(wp->w_tagstack[i].tagname); vim_free(wp->w_localdir); + #ifdef FEAT_SEARCH_EXTRA ! clear_matches(wp); #endif + #ifdef FEAT_JUMPLIST free_jumplist(wp); #endif *************** *** 6172,6176 **** --- 6183,6358 ---- return TRUE; return FALSE; + } + #endif + + #ifdef FEAT_SEARCH_EXTRA + /* + * Add match to the match list of window 'wp'. The pattern 'pat' will be + * highligted with the group 'grp' with priority 'prio'. + * Optionally, a desired ID 'id' can be specified (greater than or equal to 1). + * If no particular ID is desired, -1 must be specified for 'id'. + * Return ID of added match, -1 on failure. + */ + int + match_add(wp, grp, pat, prio, id) + win_T *wp; + char_u *grp; + char_u *pat; + int prio; + int id; + { + matchitem_T *cur; + matchitem_T *prev; + matchitem_T *m; + int hlg_id; + regmmatch_T match; + + if (*grp == NUL || *pat == NUL) + return -1; + if (id < -1 || id == 0) + { + EMSGN("E999: Invalid ID: %ld (must be greater than or equal to 1)", id); + return -1; + } + if (id != -1) + { + cur = wp->w_match_head; + while (cur != NULL) + { + if (cur->id == id) + { + EMSGN("E999: ID already taken: %ld", id); + return -1; + } + cur = cur->next; + } + } + if ((hlg_id = syn_namen2id(grp, strlen(grp))) == 0) + { + EMSG2(_(e_nogroup), grp); + return -1; + } + if ((match.regprog = vim_regcomp(pat, RE_MAGIC)) == NULL) + { + EMSG2(_(e_invarg2), pat); + return -1; + } + + /* Find available match ID. */ + while (id == -1) + { + cur = wp->w_match_head; + while (cur != NULL && cur->id != wp->w_next_match_id) + cur = cur->next; + if (cur == NULL) + id = wp->w_next_match_id; + wp->w_next_match_id++; + } + + /* Build new match. */ + m = (matchitem_T *)alloc(sizeof(matchitem_T)); + m->id = id; + m->priority = prio; + m->pattern = vim_strsave(pat); + m->hlg_id = hlg_id; + m->match.regprog = match.regprog; + + /* Insert new match. The match list is in ascending order with regard to + * the match priorities. */ + cur = wp->w_match_head; + prev = cur; + while (cur != NULL && prio >= cur->priority) + { + prev = cur; + cur = cur->next; + } + if (cur == prev) + wp->w_match_head = m; + else + prev->next = m; + m->next = cur; + + redraw_later(SOME_VALID); + return id; + } + + /* + * Delete match with ID 'id' in the match list of window 'wp'. + * Print error messages if 'perr' is TRUE. + */ + int + match_delete(wp, id, perr) + win_T *wp; + int id; + int perr; + { + matchitem_T *cur = wp->w_match_head; + matchitem_T *prev = cur; + int i; + + if (id < 1) + { + if (perr == TRUE) + EMSGN("E999: Invalid ID: %ld (must be greater than or equal to 1)", + id); + return -1; + } + while (cur != NULL && cur->id != id) + { + prev = cur; + cur = cur->next; + } + if (cur == NULL) + { + if (perr == TRUE) + EMSGN("E999: ID not found: %ld", id); + return -1; + } + if (cur == prev) + wp->w_match_head = cur->next; + else + prev->next = cur->next; + vim_free(cur->match.regprog); + vim_free(cur->pattern); + vim_free(cur); + redraw_later(SOME_VALID); + return 0; + } + + /* + * Delete all matches in the match list of window 'wp'. + */ + void + clear_matches(wp) + win_T *wp; + { + matchitem_T *m; + + while (wp->w_match_head != NULL) + { + m = wp->w_match_head->next; + vim_free(wp->w_match_head->match.regprog); + vim_free(wp->w_match_head->pattern); + vim_free(wp->w_match_head); + wp->w_match_head = m; + } + redraw_later(SOME_VALID); + } + + /* + * Get match from ID 'id' in window 'wp'. + * Return NULL if match not found. + */ + matchitem_T * + get_match(wp, id) + win_T *wp; + int id; + { + matchitem_T *cur = wp->w_match_head; + + while (cur != NULL && cur->id != id) + cur = cur->next; + return cur; } #endif