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)
Files:      runtime/doc/eval.txt, runtime/doc/usr_41.txt,
            runtime/plugin/matchparen.vim, src/eval.c, src/ex_docmd.c,
            src/proto/window.pro, src/screen.c, src/structs.h,
            src/testdir/test63.in, src/testdir/test63.ok, src/window.c


*** ../vim-7.4.329/runtime/doc/eval.txt 2014-05-28 20:31:37.500292805 +0200
--- runtime/doc/eval.txt        2014-06-17 16:31:35.572453748 +0200
***************
*** 1887,1892 ****
--- 1887,1894 ----
                                Number  position where {pat} matches in {expr}
  matchadd( {group}, {pattern}[, {priority}[, {id}]])
                                Number  highlight {pattern} with {group}
+ matchaddpos( {group}, {list}[, {priority}[, {id}]])
+                               Number  highlight positions with {group}
  matcharg( {nr})                       List    arguments of |:match|
  matchdelete( {id})            Number  delete match identified by {id}
  matchend( {expr}, {pat}[, {start}[, {count}]])
***************
*** 4342,4347 ****
--- 4382,4422 ----
                available from |getmatches()|.  All matches can be deleted in
                one operation by |clearmatches()|.
  
+ matchaddpos({group}, {pos}[, {priority}[, {id}]])             *matchaddpos()*
+               Same as |matchadd()|, but requires a list of positions {pos}
+               instead of a pattern. This command is faster than |matchadd()|
+               because it does not require to handle regular expressions and
+               sets buffer line boundaries to redraw screen. It is supposed
+               to be used when fast match additions and deletions are
+               required, for example to highlight matching parentheses.
+ 
+               The list {pos} can contain one of these items:
+               - A number.  This while line will be highlighted.  The first
+                 line has number 1.
+               - A list with one number, e.g., [23]. The whole line with this
+                 number will be highlighted.
+               - A list with two numbers, e.g., [23, 11]. The first number is
+                 the line number, the second one the column number (first
+                 column is 1).  The character at this position will be
+                 highlighted.
+               - A list with three numbers, e.g., [23, 11, 3]. As above, but
+                 the third number gives the length of the highlight in screen
+                 cells.
+               
+               The maximum number of positions is 8.
+ 
+               Example: >
+                       :highlight MyGroup ctermbg=green guibg=green
+                       :let m = matchaddpos("MyGroup", [[23, 24], 34])
+ <             Deletion of the pattern: >
+                       :call matchdelete(m)
+ 
+ <             Matches added by |matchaddpos()| are returned by
+               |getmatches()| with an entry "pos1", "pos2", etc., with the
+               value a list like the {pos} item.
+               These matches cannot be set via |setmatches()|, however they
+               can still be deleted by |clearmatches()|.
+ 
  matcharg({nr})                                                        
*matcharg()*
                Selects the {nr} match item, as set with a |:match|,
                |:2match| or |:3match| command.
*** ../vim-7.4.329/runtime/doc/usr_41.txt       2014-05-28 18:22:37.872225054 
+0200
--- runtime/doc/usr_41.txt      2014-06-17 14:06:44.836124965 +0200
***************
*** 824,829 ****
--- 827,833 ----
        synconcealed()          get info about concealing
        diff_hlID()             get highlight ID for diff mode at a position
        matchadd()              define a pattern to highlight (a "match")
+       matchaddpos()           define a list of positions to highlight
        matcharg()              get info about |:match| arguments
        matchdelete()           delete a match defined by |matchadd()| or a
                                |:match| command
*** ../vim-7.4.329/runtime/plugin/matchparen.vim        2013-05-08 
05:15:53.000000000 +0200
--- runtime/plugin/matchparen.vim       2014-06-17 14:14:45.712143158 +0200
***************
*** 1,6 ****
  " Vim plugin for showing matching parens
  " Maintainer:  Bram Moolenaar <[email protected]>
! " Last Change: 2013 May 08
  
  " Exit quickly when:
  " - this plugin was already loaded (or disabled)
--- 1,6 ----
  " Vim plugin for showing matching parens
  " Maintainer:  Bram Moolenaar <[email protected]>
! " Last Change: 2014 Jun 17
  
  " Exit quickly when:
  " - this plugin was already loaded (or disabled)
***************
*** 39,45 ****
  function! s:Highlight_Matching_Pair()
    " Remove any previous match.
    if exists('w:paren_hl_on') && w:paren_hl_on
!     3match none
      let w:paren_hl_on = 0
    endif
  
--- 39,45 ----
  function! s:Highlight_Matching_Pair()
    " Remove any previous match.
    if exists('w:paren_hl_on') && w:paren_hl_on
!     silent! call matchdelete(3)
      let w:paren_hl_on = 0
    endif
  
***************
*** 152,165 ****
  
    " If a match is found setup match highlighting.
    if m_lnum > 0 && m_lnum >= stoplinetop && m_lnum <= stoplinebottom 
!     exe '3match MatchParen /\(\%' . c_lnum . 'l\%' . (c_col - before) .
!         \ 'c\)\|\(\%' . m_lnum . 'l\%' . m_col . 'c\)/'
      let w:paren_hl_on = 1
    endif
  endfunction
  
  " Define commands that will disable and enable the plugin.
! command! NoMatchParen windo 3match none | unlet! g:loaded_matchparen |
          \ au! matchparen
  command! DoMatchParen runtime plugin/matchparen.vim | windo doau CursorMoved
  
--- 152,169 ----
  
    " If a match is found setup match highlighting.
    if m_lnum > 0 && m_lnum >= stoplinetop && m_lnum <= stoplinebottom 
!     if exists('*matchaddpos')
!       call matchaddpos('MatchParen', [[c_lnum, c_col - before], [m_lnum, 
m_col]], 10, 3)
!     else
!       exe '3match MatchParen /\(\%' . c_lnum . 'l\%' . (c_col - before) .
!           \ 'c\)\|\(\%' . m_lnum . 'l\%' . m_col . 'c\)/'
!     endif
      let w:paren_hl_on = 1
    endif
  endfunction
  
  " Define commands that will disable and enable the plugin.
! command! NoMatchParen windo silent! call matchdelete(3) | unlet! 
g:loaded_matchparen |
          \ au! matchparen
  command! DoMatchParen runtime plugin/matchparen.vim | windo doau CursorMoved
  
*** ../vim-7.4.329/src/eval.c   2014-06-17 12:51:13.207953527 +0200
--- src/eval.c  2014-06-17 17:02:25.388523729 +0200
***************
*** 622,627 ****
--- 622,628 ----
  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_matchaddpos __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));
***************
*** 8054,8059 ****
--- 8055,8061 ----
      {"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},
***************
*** 11767,11772 ****
--- 11769,11775 ----
  #ifdef FEAT_SEARCH_EXTRA
      dict_T    *dict;
      matchitem_T       *cur = curwin->w_match_head;
+     int               i;
  
      if (rettv_list_alloc(rettv) == OK)
      {
***************
*** 11775,11782 ****
            dict = dict_alloc();
            if (dict == NULL)
                return;
            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);
--- 11778,11813 ----
            dict = dict_alloc();
            if (dict == NULL)
                return;
+           if (cur->match.regprog == NULL)
+           {
+               /* match added with matchaddpos() */
+               for (i = 0; i < MAXPOSMATCH; ++i)
+               {
+                   llpos_T     *llpos;
+                   char        buf[6];
+                   list_T      *l;
+ 
+                   llpos = &cur->pos.pos[i];
+                   if (llpos->lnum == 0)
+                       break;
+                   l = list_alloc();
+                   if (l == NULL)
+                       break;
+                   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, "priority", (long)cur->priority, NULL);
            dict_add_nr_str(dict, "id", (long)cur->id, NULL);
            list_append_dict(rettv->vval.v_list, dict);
***************
*** 14313,14319 ****
        return;
      }
  
!     rettv->vval.v_number = match_add(curwin, grp, pat, prio, id);
  #endif
  }
  
--- 14344,14401 ----
        return;
      }
  
!     rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL);
! #endif
! }
! 
! /*
!  * "matchaddpos()" function
!  */
!     static void
! f_matchaddpos(argvars, rettv)
!     typval_T  *argvars UNUSED;
!     typval_T  *rettv UNUSED;
! {
! #ifdef FEAT_SEARCH_EXTRA
!     char_u    buf[NUMBUFLEN];
!     char_u    *group;
!     int               prio = 10;
!     int               id = -1;
!     int               error = FALSE;
!     list_T    *l;
! 
!     rettv->vval.v_number = -1;
! 
!     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;
!     }
!     l = argvars[1].vval.v_list;
!     if (l == 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;
! 
!     /* id == 3 is ok because matchaddpos() is supposed to substitute :3match 
*/
!     if (id == 1 || id == 2)
!     {
!       EMSGN("E798: ID is reserved for \":match\": %ld", id);
!       return;
!     }
! 
!     rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l);
  #endif
  }
  
***************
*** 16816,16822 ****
            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"));
            li = li->li_next;
        }
        rettv->vval.v_number = 0;
--- 16898,16904 ----
            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"), NULL);
            li = li->li_next;
        }
        rettv->vval.v_number = 0;
*** ../vim-7.4.329/src/ex_docmd.c       2014-05-28 18:22:37.876225054 +0200
--- src/ex_docmd.c      2014-06-17 14:06:44.844124966 +0200
***************
*** 11489,11495 ****
  
            c = *end;
            *end = NUL;
!           match_add(curwin, g, p + 1, 10, id);
            vim_free(g);
            *end = c;
        }
--- 11489,11495 ----
  
            c = *end;
            *end = NUL;
!           match_add(curwin, g, p + 1, 10, id, NULL);
            vim_free(g);
            *end = c;
        }
*** ../vim-7.4.329/src/proto/window.pro 2013-08-14 17:11:14.000000000 +0200
--- src/proto/window.pro        2014-06-17 14:06:44.844124966 +0200
***************
*** 75,81 ****
  void switch_buffer __ARGS((buf_T **save_curbuf, buf_T *buf));
  void restore_buffer __ARGS((buf_T *save_curbuf));
  int win_hasvertsplit __ARGS((void));
! 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));
--- 75,81 ----
  void switch_buffer __ARGS((buf_T **save_curbuf, buf_T *buf));
  void restore_buffer __ARGS((buf_T *save_curbuf));
  int win_hasvertsplit __ARGS((void));
! int match_add __ARGS((win_T *wp, char_u *grp, char_u *pat, int prio, int id, 
list_T *pos));
  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));
*** ../vim-7.4.329/src/screen.c 2014-05-28 21:40:47.092329130 +0200
--- src/screen.c        2014-06-17 17:04:08.064527614 +0200
***************
*** 144,150 ****
  static void end_search_hl __ARGS((void));
  static void init_search_hl __ARGS((win_T *wp));
  static void prepare_search_hl __ARGS((win_T *wp, linenr_T lnum));
! static void next_search_hl __ARGS((win_T *win, match_T *shl, linenr_T lnum, 
colnr_T mincol));
  #endif
  static void screen_start_highlight __ARGS((int attr));
  static void screen_char __ARGS((unsigned off, int row, int col));
--- 144,151 ----
  static void end_search_hl __ARGS((void));
  static void init_search_hl __ARGS((win_T *wp));
  static void prepare_search_hl __ARGS((win_T *wp, linenr_T lnum));
! static void next_search_hl __ARGS((win_T *win, match_T *shl, linenr_T lnum, 
colnr_T mincol, matchitem_T *cur));
! static int next_search_hl_pos __ARGS((match_T *shl, linenr_T lnum, posmatch_T 
*pos, colnr_T mincol));
  #endif
  static void screen_start_highlight __ARGS((int attr));
  static void screen_char __ARGS((unsigned off, int row, int col));
***************
*** 2929,2934 ****
--- 2930,2937 ----
      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               pos_inprogress;         /* marks that position match 
search is
+                                          in progress */
      int               prevcol_hl_flag;        /* flag to indicate whether 
prevcol
                                           equals startcol of search_hl or one
                                           of the matches */
***************
*** 3439,3482 ****
        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);
! 
!           /* 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)
-               {
  #ifdef FEAT_MBYTE
!                   if (has_mbyte && line[shl->endcol] != NUL)
!                       shl->endcol += (*mb_ptr2len)(line + shl->endcol);
!                   else
  #endif
!                       ++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;
--- 3442,3484 ----
        shl->startcol = MAXCOL;
        shl->endcol = MAXCOL;
        shl->attr_cur = 0;
!       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;
  
!       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)
            {
  #ifdef FEAT_MBYTE
!               if (has_mbyte && line[shl->endcol] != NUL)
!                   shl->endcol += (*mb_ptr2len)(line + shl->endcol);
!               else
  #endif
!                   ++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;
***************
*** 3488,3494 ****
       * 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))
      {
        line_attr = hl_attr(HLF_CUL);
        area_highlighting = TRUE;
--- 3490,3496 ----
       * 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))
      {
        line_attr = hl_attr(HLF_CUL);
        area_highlighting = TRUE;
***************
*** 3792,3798 ****
                    }
                    else
                        shl = &cur->hl;
!                   while (shl->rm.regprog != NULL)
                    {
                        if (shl->startcol != MAXCOL
                                && v >= (long)shl->startcol
--- 3794,3804 ----
                    }
                    else
                        shl = &cur->hl;
!                   if (cur != NULL)
!                       cur->pos.cur = 0;
!                   pos_inprogress = TRUE;
!                   while (shl->rm.regprog != NULL
!                                          || (cur != NULL && pos_inprogress))
                    {
                        if (shl->startcol != MAXCOL
                                && v >= (long)shl->startcol
***************
*** 3803,3810 ****
                        else if (v == (long)shl->endcol)
                        {
                            shl->attr_cur = 0;
! 
!                           next_search_hl(wp, shl, lnum, (colnr_T)v);
  
                            /* Need to get the line again, a multi-line regexp
                             * may have made it invalid. */
--- 3809,3817 ----
                        else if (v == (long)shl->endcol)
                        {
                            shl->attr_cur = 0;
!                           next_search_hl(wp, shl, lnum, (colnr_T)v, cur);
!                           pos_inprogress = cur == NULL || cur->pos.cur == 0
!                                                             ? FALSE : TRUE;
  
                            /* Need to get the line again, a multi-line regexp
                             * may have made it invalid. */
***************
*** 7277,7282 ****
--- 7284,7291 ----
      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               pos_inprogress; /* marks that position match search is
+                                  in progress */
      int               n;
  
      /*
***************
*** 7311,7320 ****
                shl->first_lnum = wp->w_topline;
  # endif
            }
            n = 0;
!           while (shl->first_lnum < lnum && shl->rm.regprog != NULL)
            {
!               next_search_hl(wp, shl, shl->first_lnum, (colnr_T)n);
                if (shl->lnum != 0)
                {
                    shl->first_lnum = shl->lnum
--- 7320,7335 ----
                shl->first_lnum = wp->w_topline;
  # endif
            }
+           if (cur != NULL)
+               cur->pos.cur = 0;
+           pos_inprogress = TRUE;
            n = 0;
!           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
!                                                             ? FALSE : TRUE;
                if (shl->lnum != 0)
                {
                    shl->first_lnum = shl->lnum
***************
*** 7343,7353 ****
   * Careful: Any pointers for buffer lines will become invalid.
   */
      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 */
  {
      linenr_T  l;
      colnr_T   matchcol;
--- 7358,7369 ----
   * Careful: Any pointers for buffer lines will become invalid.
   */
      static void
! next_search_hl(win, shl, lnum, mincol, cur)
!     win_T         *win;
!     match_T       *shl;       /* points to search_hl or a match */
!     linenr_T      lnum;
!     colnr_T       mincol;     /* minimal column for a match */
!     matchitem_T           *cur;       /* to retrieve match postions if any */
  {
      linenr_T  l;
      colnr_T   matchcol;
***************
*** 7415,7440 ****
            matchcol = shl->rm.endpos[0].col;
  
        shl->lnum = lnum;
!       nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum, matchcol,
  #ifdef FEAT_RELTIME
!               &(shl->tm)
  #else
!               NULL
  #endif
!               );
!       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;
        }
        if (nmatched == 0)
        {
--- 7431,7465 ----
            matchcol = shl->rm.endpos[0].col;
  
        shl->lnum = lnum;
!       if (shl->rm.regprog != NULL)
!       {
!           nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
!                   matchcol,
  #ifdef FEAT_RELTIME
!                   &(shl->tm)
  #else
!                   NULL
  #endif
!                   );
!           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;
            }
!       }
!       else if (cur != NULL)
!       {
!           nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
        }
        if (nmatched == 0)
        {
***************
*** 7453,7458 ****
--- 7478,7539 ----
  }
  #endif
  
+     static int
+ next_search_hl_pos(shl, lnum, posmatch, mincol)
+     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(attr)
        int     attr;
*** ../vim-7.4.329/src/structs.h        2014-05-28 18:22:37.876225054 +0200
--- src/structs.h       2014-06-17 17:00:55.524520330 +0200
***************
*** 1927,1932 ****
--- 1927,1958 ----
  #endif
  } 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.
***************
*** 1940,1945 ****
--- 1966,1972 ----
      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 */
  };
  
*** ../vim-7.4.329/src/testdir/test63.in        2010-05-15 13:04:10.000000000 
+0200
--- src/testdir/test63.in       2014-06-17 16:29:36.056449227 +0200
***************
*** 1,5 ****
  Test for ":match", ":2match", ":3match", "clearmatches()", "getmatches()",
! "matchadd()", "matcharg()", "matchdelete()", and "setmatches()".
  
  STARTTEST
  :so small.vim
--- 1,5 ----
  Test for ":match", ":2match", ":3match", "clearmatches()", "getmatches()",
! "matchadd()", "matchaddpos", "matcharg()", "matchdelete()", and 
"setmatches()".
  
  STARTTEST
  :so small.vim
***************
*** 147,155 ****
  :unlet rf1
  :unlet rf2
  :unlet rf3
! :highlight clear MyGroup1
! :highlight clear MyGroup2
! :highlight clear MyGroup3
  G"rp
  :/^Results/,$wq! test.out
  ENDTEST
--- 147,172 ----
  :unlet rf1
  :unlet rf2
  :unlet rf3
! :" --- 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
*** ../vim-7.4.329/src/testdir/test63.ok        2010-05-15 13:04:10.000000000 
+0200
--- src/testdir/test63.ok       2014-06-17 17:32:57.036593023 +0200
***************
*** 9,11 ****
--- 9,14 ----
  *** 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
*** ../vim-7.4.329/src/window.c 2014-06-17 13:52:35.868092848 +0200
--- src/window.c        2014-06-17 17:04:51.060529240 +0200
***************
*** 6751,6770 ****
   * 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;
!     regprog_T *regprog;
  
!     if (*grp == NUL || *pat == NUL)
        return -1;
      if (id < -1 || id == 0)
      {
--- 6751,6772 ----
   * Return ID of added match, -1 on failure.
   */
      int
! match_add(wp, grp, pat, prio, id, pos_list)
      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 = NULL;
!     int               rtype = SOME_VALID;
  
!     if (*grp == NUL || (pat != NULL && *pat == NUL))
        return -1;
      if (id < -1 || id == 0)
      {
***************
*** 6789,6795 ****
        EMSG2(_(e_nogroup), grp);
        return -1;
      }
!     if ((regprog = vim_regcomp(pat, RE_MAGIC)) == NULL)
      {
        EMSG2(_(e_invarg2), pat);
        return -1;
--- 6791,6797 ----
        EMSG2(_(e_nogroup), grp);
        return -1;
      }
!     if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL)
      {
        EMSG2(_(e_invarg2), pat);
        return -1;
***************
*** 6810,6821 ****
      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 = regprog;
      m->match.rmm_ic = FALSE;
      m->match.rmm_maxcol = 0;
  
      /* Insert new match.  The match list is in ascending order with regard to
       * the match priorities. */
      cur = wp->w_match_head;
--- 6812,6922 ----
      m = (matchitem_T *)alloc(sizeof(matchitem_T));
      m->id = id;
      m->priority = prio;
!     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;
***************
*** 6831,6838 ****
        prev->next = m;
      m->next = cur;
  
!     redraw_later(SOME_VALID);
      return id;
  }
  
  /*
--- 6932,6943 ----
        prev->next = m;
      m->next = cur;
  
!     redraw_later(rtype);
      return id;
+ 
+ fail:
+     vim_free(m);
+     return -1;
  }
  
  /*
***************
*** 6845,6852 ****
      int               id;
      int               perr;
  {
!     matchitem_T *cur = wp->w_match_head;
!     matchitem_T *prev = cur;
  
      if (id < 1)
      {
--- 6950,6958 ----
      int               id;
      int               perr;
  {
!     matchitem_T       *cur = wp->w_match_head;
!     matchitem_T       *prev = cur;
!     int               rtype = SOME_VALID;
  
      if (id < 1)
      {
***************
*** 6872,6879 ****
        prev->next = cur->next;
      vim_regfree(cur->match.regprog);
      vim_free(cur->pattern);
      vim_free(cur);
!     redraw_later(SOME_VALID);
      return 0;
  }
  
--- 6978,7002 ----
        prev->next = cur->next;
      vim_regfree(cur->match.regprog);
      vim_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;
+     }
      vim_free(cur);
!     redraw_later(rtype);
      return 0;
  }
  
*** ../vim-7.4.329/src/version.c        2014-06-17 13:52:35.868092848 +0200
--- src/version.c       2014-06-17 14:11:53.764136653 +0200
***************
*** 736,737 ****
--- 736,739 ----
  {   /* Add new patch number below this line */
+ /**/
+     330,
  /**/

-- 
I'd like to meet the man who invented sex and see what he's working on now.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui