patch 9.1.1077: included syntax items do not understand contains=TOP

Commit: 
https://github.com/vim/vim/commit/f50d5364d790619a3b982a3ad3658b5a10daf511
Author: Theodore Dubois <tbl...@icloud.com>
Date:   Wed Feb 5 23:59:25 2025 +0100

    patch 9.1.1077: included syntax items do not understand contains=TOP
    
    Problem:  Syntax engine interpreted contains=TOP as matching nothing
              inside included files, since :syn-include forces HL_CONTAINED
              on for every included item. After 8.2.2761, interprets
              contains=TOP as contains=@INCLUDED, which is also not correct
              since it doesn't respect exclusions, and doesn't work if there
              is no @INCLUDED cluster.
    Solution: revert patch 8.2.2761, instead track groups that have had
              HL_CONTAINED forced, and interpret contains=TOP and
              contains=CONTAINED using this. (Theodore Dubois)
    
    fixes: #11277
    closes: #16571
    
    Signed-off-by: Theodore Dubois <tbl...@icloud.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/syntax.c b/src/syntax.c
index 6940aa124..34563aad9 100644
--- a/src/syntax.c
+++ b/src/syntax.c
@@ -299,7 +299,7 @@ static void update_si_attr(int idx);
 static void check_keepend(void);
 static void update_si_end(stateitem_T *sip, int startcol, int force);
 static short *copy_id_list(short *list);
-static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, 
int contained);
+static int in_id_list(stateitem_T *item, short *cont_list, struct sp_syn *ssp, 
int flags);
 static int push_current_state(int idx);
 static void pop_current_state(void);
 #ifdef FEAT_PROFILE
@@ -1943,7 +1943,7 @@ syn_current_attr(
                                        ? !(spp->sp_flags & HL_CONTAINED)
                                        : in_id_list(cur_si,
                                            cur_si->si_cont_list, &spp->sp_syn,
-                                           spp->sp_flags & HL_CONTAINED))))
+                                           spp->sp_flags))))
                        {
                            int r;
 
@@ -3269,7 +3269,7 @@ check_keyword_id(
                        : (cur_si == NULL
                            ? !(kp->flags & HL_CONTAINED)
                            : in_id_list(cur_si, cur_si->si_cont_list,
-                                     &kp->k_syn, kp->flags & HL_CONTAINED)))
+                                     &kp->k_syn, kp->flags)))
                {
                    *endcolp = startcol + kwlen;
                    *flagsp = kp->flags;
@@ -4681,7 +4681,7 @@ syn_incl_toplevel(int id, int *flagsp)
 {
     if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
        return;
-    *flagsp |= HL_CONTAINED;
+    *flagsp |= HL_CONTAINED | HL_INCLUDED_TOPLEVEL;
     if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
     {
        // We have to alloc this, because syn_combine_list() will free it.
@@ -5969,17 +5969,12 @@ get_id_list(
                    break;
                }
                if (name[1] == 'A')
-                   id = SYNID_ALLBUT + current_syn_inc_tag;
+                   id = SYNID_ALLBUT;
                else if (name[1] == 'T')
-               {
-                   if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
-                       id = curwin->w_s->b_syn_topgrp;
-                   else
-                       id = SYNID_TOP + current_syn_inc_tag;
-               }
+                   id = SYNID_TOP;
                else
-                   id = SYNID_CONTAINED + current_syn_inc_tag;
-
+                   id = SYNID_CONTAINED;
+               id += current_syn_inc_tag;
            }
            else if (name[1] == '@')
            {
@@ -6127,7 +6122,7 @@ in_id_list(
     stateitem_T        *cur_si,        // current item or NULL
     short      *list,          // id list
     struct sp_syn *ssp,                // group id and ":syn include" tag of 
group
-    int                contained)      // group id is contained
+    int                flags)          // group flags
 {
     int                retval;
     short      *scl_list;
@@ -6135,6 +6130,7 @@ in_id_list(
     short      id = ssp->id;
     static int depth = 0;
     int                r;
+    int                toplevel;
 
     // If ssp has a "containedin" list and "cur_si" is in it, return TRUE.
     if (cur_si != NULL && ssp->cont_in_list != NULL
@@ -6148,7 +6144,7 @@ in_id_list(
        // cur_si->si_idx is -1 for keywords, these never contain anything.
        if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
                &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
-                 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
+                 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags))
            return TRUE;
     }
 
@@ -6160,7 +6156,14 @@ in_id_list(
      * inside anything.  Only allow not-contained groups.
      */
     if (list == ID_LIST_ALL)
-       return !contained;
+       return !(flags & HL_CONTAINED);
+
+    /*
+     * Is this top-level (i.e. not 'contained') in the file it was declared in?
+     * For included files, this is different from HL_CONTAINED, which is set
+     * unconditionally.
+     */
+    toplevel = !(flags & HL_CONTAINED) || (flags & HL_INCLUDED_TOPLEVEL);
 
     /*
      * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
@@ -6179,13 +6182,13 @@ in_id_list(
        else if (item < SYNID_CONTAINED)
        {
            // TOP: accept all not-contained groups in the same file
-           if (item - SYNID_TOP != ssp->inc_tag || contained)
+           if (item - SYNID_TOP != ssp->inc_tag || !toplevel)
                return FALSE;
        }
        else
        {
            // CONTAINED: accept all contained groups in the same file
-           if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
+           if (item - SYNID_CONTAINED != ssp->inc_tag || toplevel)
                return FALSE;
        }
        item = *++list;
@@ -6209,7 +6212,7 @@ in_id_list(
            if (scl_list != NULL && depth < 30)
            {
                ++depth;
-               r = in_id_list(NULL, scl_list, ssp, contained);
+               r = in_id_list(NULL, scl_list, ssp, flags);
                --depth;
                if (r)
                    return retval;
diff --git a/src/testdir/test_syntax.vim b/src/testdir/test_syntax.vim
index aeb2c51c5..748c2bd7b 100644
--- a/src/testdir/test_syntax.vim
+++ b/src/testdir/test_syntax.vim
@@ -949,7 +949,7 @@ func Test_syn_contained_transparent()
 endfunc
 
 func Test_syn_include_contains_TOP()
-  let l:case = "TOP in included syntax means its group list name"
+  let l:case = "TOP in included syntax refers to top level of that included 
syntax"
   new
   syntax include @INCLUDED syntax/c.vim
   syntax region FencedCodeBlockC start=/```c/ end=/```/ contains=@INCLUDED
@@ -964,6 +964,18 @@ func Test_syn_include_contains_TOP()
   bw!
 endfunc
 
+func Test_syn_include_contains_TOP_excluding()
+  new
+  syntax include @INCLUDED syntax/c.vim
+  syntax region FencedCodeBlockC start=/```c/ end=/```/ contains=@INCLUDED
+
+  call setline(1,  ['```c', '#if 0', 'int', '#else', 'int', '#if', '#endif', 
'```' ])
+  let l:expected = ["cCppOutElse", "cConditional"]
+  eval AssertHighlightGroups(6, 1, l:expected, 1)
+  syntax clear
+  bw!
+endfunc
+
 " This was using freed memory
 func Test_WinEnter_synstack_synID()
   autocmd WinEnter * call synstack(line("."), col("."))
diff --git a/src/version.c b/src/version.c
index d71d4d170..c51a07daa 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1077,
 /**/
     1076,
 /**/
diff --git a/src/vim.h b/src/vim.h
index 1cf764821..eb312172b 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -953,6 +953,7 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring);
 # define HL_TRANS_CONT 0x10000 // transparent item without contains arg
 # define HL_CONCEAL    0x20000 // can be concealed
 # define HL_CONCEALENDS        0x40000 // can be concealed
+# define HL_INCLUDED_TOPLEVEL 0x80000 // toplevel item in included syntax, 
allowed by contains=TOP
 #endif
 
 // Values for 'options' argument in do_search() and searchit()

-- 
-- 
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 vim_dev+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1tfobv-008t4C-2S%40256bit.org.

Raspunde prin e-mail lui