Hello.

I've already posted about this bug in syntax matching code. But now I have a fix.

Test case
=========
Having following syntax rules

:syn cluster Top contains=Block,String,Identifier
:syn region Block start=+{+ end=+}+ keepend extend [EMAIL PROTECTED]
:syn region String start=+"+ end=+"+ contains=Identifier
:syn region Identifier start=+\${+ end=+}+ extend

and the following buffer is matched incorrectly

{ "string ${var} string" }

on current vim version (7.0.109) I'm getting following syntax groups:

{ "string ${var} string" }
BBBBBBBBBBBBBBBBBBBBBBBBBB
 SSSSSSSSSSSSSS       SS
         IIIIII

Where B means Block region, S - String region and I - Identifier region.

While I'm expecting to see the following picture:

{ "string ${var} string" }
BBBBBBBBBBBBBBBBBBBBBBBBBB
 SSSSSSSSSSSSSSSSSSSSSS
         IIIIII

Bug description
===============
Bug appears, when one have an extend region inside a normal region inside a keepend region. Currently, when any extend region ends vim checks all the outer keepend regions to see, if theirs ends should be "extended", but it does not checks intermediate normal (non keepend) regions and theirs ends should also be checked otherwise one could end up with a normal region been forcefully ended by enclosing keepend region and this keepend region later be extended by a containing extend region.

Just as it is seen in the first picture in the test case. Block region ends String region on a first '}'. But later when Identifier region consumes that first '}' Block region is extended, while String region is not.

Solution
========
Test intermediate normal region ends along with keepend region ends when an extend region ends.

Here is a patch for syntax.c that does this check. I'm getting correct behavior with this patch for my test case.

--
Ilya Bobir
Index: syntax.c
===================================================================
RCS file: /cvsroot/vim/vim7/src/syntax.c,v
retrieving revision 1.62
diff -c -r1.62 syntax.c
*** syntax.c    26 Apr 2006 23:58:59 -0000      1.62
--- syntax.c    20 Sep 2006 20:58:12 -0000
***************
*** 977,982 ****
--- 977,983 ----
  {
      stateitem_T       *cur_si;
      int               i;
+     int               seen_keepend;
  
      if (startofline)
      {
***************
*** 1002,1008 ****
      /*
       * Need to update the end of a start/skip/end that continues from the
       * previous line.  And regions that have "keepend", because they may
!      * influence contained items.
       * Then check for items ending in column 0.
       */
      i = current_state.ga_len - 1;
--- 1003,1012 ----
      /*
       * Need to update the end of a start/skip/end that continues from the
       * previous line.  And regions that have "keepend", because they may
!      * influence contained items.  If we've just removed "extend" 
!      * (startofline == 0) then we should update ends of normal regions 
!      * contained inside "keepend" because "extend" could have extended 
!      * these "keepend" regions as well as contained normal regions. 
       * Then check for items ending in column 0.
       */
      i = current_state.ga_len - 1;
***************
*** 1010,1019 ****
--- 1014,1026 ----
        for ( ; i > keepend_level; --i)
            if (CUR_STATE(i).si_flags & HL_EXTEND)
                break;
+ 
+     seen_keepend = 0;
      for ( ; i < current_state.ga_len; ++i)
      {
        cur_si = &CUR_STATE(i);
        if ((cur_si->si_flags & HL_KEEPEND)
+                           || (seen_keepend && !startofline)
                            || (i == current_state.ga_len - 1 && startofline))
        {
            cur_si->si_h_startpos.col = 0;      /* start highl. in col 0 */
***************
*** 1021,1026 ****
--- 1028,1036 ----
  
            if (!(cur_si->si_flags & HL_MATCHCONT))
                update_si_end(cur_si, (int)current_col, !startofline);
+ 
+           if(!startofline && (cur_si->si_flags & HL_KEEPEND))
+               seen_keepend = 1;
        }
      }
      check_keepend();

Reply via email to