]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.2.0865: syntax foldlevel is taken from the start of the line v8.2.0865
authorBram Moolenaar <Bram@vim.org>
Sun, 31 May 2020 17:48:53 +0000 (19:48 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 31 May 2020 17:48:53 +0000 (19:48 +0200)
Problem:    Syntax foldlevel is taken from the start of the line.
Solution:   Add ":syn foldlevel" to be able to use the minimal foldlevel in
            the line. (Brad King, closes #6087)

runtime/doc/syntax.txt
src/structs.h
src/syntax.c
src/testdir/test_syntax.vim
src/version.c

index ff9be2e5c7a53bdcf2b0e8815d586373bab6cbce..2cff69411aa3243d71f538d5bc8876b27a9c8fd4 100644 (file)
@@ -3636,6 +3636,26 @@ DEFINING CASE                                            *:syn-case* *E390*
 :sy[ntax] case
        Show either "syntax case match" or "syntax case ignore" (translated).
 
+
+DEFINING FOLDLEVEL                                     *:syn-foldlevel*
+
+:sy[ntax] foldlevel [start | minimum]
+       This defines how the foldlevel of a line is computed when using
+       foldmethod=syntax (see |fold-syntax| and |:syn-fold|):
+
+       start:          Use level of item containing start of line.
+       minimum:        Use lowest local-minimum level of items on line.
+
+       The default is 'start'.  Use 'minimum' to search a line horizontally
+       for the lowest level contained on the line that is followed by a
+       higher level.  This produces more natural folds when syntax items
+       may close and open horizontally within a line.
+
+:sy[ntax] foldlevel
+       Show either "syntax foldlevel start" or "syntax foldlevel minimum".
+
+       {not meaningful when Vim was compiled without |+folding| feature}
+
 SPELL CHECKING                                         *:syn-spell*
 
 :sy[ntax] spell [toplevel | notoplevel | default]
@@ -4099,6 +4119,8 @@ This will make each {} block form one fold.
 The fold will start on the line where the item starts, and end where the item
 ends.  If the start and end are within the same line, there is no fold.
 The 'foldnestmax' option limits the nesting of syntax folds.
+See |:syn-foldlevel| to control how the foldlevel of a line is computed
+from its syntax items.
 {not available when Vim was compiled without |+folding| feature}
 
 
index 9968e9205d35fde6e88679e3db325c8ab5dcceaf..8fb14d7598b98ebe131ac8f4fe3ed0fdd6516c90 100644 (file)
@@ -2266,6 +2266,10 @@ typedef struct
 #define SYNSPL_TOP     1       // spell check toplevel text
 #define SYNSPL_NOTOP   2       // don't spell check toplevel text
 
+// values for b_syn_foldlevel: how to compute foldlevel on a line
+#define SYNFLD_START   0       // use level of item at start of line
+#define SYNFLD_MINIMUM 1       // use lowest local minimum level on line
+
 // avoid #ifdefs for when b_spell is not available
 #ifdef FEAT_SPELL
 # define B_SPELL(buf)  ((buf)->b_spell)
@@ -2360,6 +2364,7 @@ typedef struct {
     int                b_syn_slow;             // TRUE when 'redrawtime' reached
 # endif
     int                b_syn_ic;               // ignore case for :syn cmds
+    int                b_syn_foldlevel;        // how to compute foldlevel on a line
     int                b_syn_spell;            // SYNSPL_ values
     garray_T   b_syn_patterns;         // table for syntax patterns
     garray_T   b_syn_clusters;         // table for syntax clusters
index cda1f953b926495a72a4858233bb18de372904cb..f2f74a181a8c4e9472835a2a01731107e21e7f04 100644 (file)
@@ -30,6 +30,8 @@
 static char *(spo_name_tab[SPO_COUNT]) =
            {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
 
+static char e_illegal_arg[] = N_("E390: Illegal argument: %s");
+
 /*
  * The patterns that are being searched for are stored in a syn_pattern.
  * A match item consists of one pattern.
@@ -3340,7 +3342,7 @@ syn_cmd_conceal(exarg_T *eap UNUSED, int syncing UNUSED)
     else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
        curwin->w_s->b_syn_conceal = FALSE;
     else
-       semsg(_("E390: Illegal argument: %s"), arg);
+       semsg(_(e_illegal_arg), arg);
 #endif
 }
 
@@ -3370,7 +3372,49 @@ syn_cmd_case(exarg_T *eap, int syncing UNUSED)
     else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
        curwin->w_s->b_syn_ic = TRUE;
     else
-       semsg(_("E390: Illegal argument: %s"), arg);
+       semsg(_(e_illegal_arg), arg);
+}
+
+/*
+ * Handle ":syntax foldlevel" command.
+ */
+    static void
+syn_cmd_foldlevel(exarg_T *eap, int syncing UNUSED)
+{
+    char_u *arg = eap->arg;
+    char_u *arg_end;
+
+    eap->nextcmd = find_nextcmd(arg);
+    if (eap->skip)
+       return;
+
+    if (*arg == NUL)
+    {
+       switch (curwin->w_s->b_syn_foldlevel)
+       {
+           case SYNFLD_START:   msg(_("syntax foldlevel start"));   break;
+           case SYNFLD_MINIMUM: msg(_("syntax foldlevel minimum")); break;
+           default: break;
+       }
+       return;
+    }
+
+    arg_end = skiptowhite(arg);
+    if (STRNICMP(arg, "start", 5) == 0 && arg_end - arg == 5)
+       curwin->w_s->b_syn_foldlevel = SYNFLD_START;
+    else if (STRNICMP(arg, "minimum", 7) == 0 && arg_end - arg == 7)
+       curwin->w_s->b_syn_foldlevel = SYNFLD_MINIMUM;
+    else
+    {
+       semsg(_(e_illegal_arg), arg);
+       return;
+    }
+
+    arg = skipwhite(arg_end);
+    if (*arg != NUL)
+    {
+       semsg(_(e_illegal_arg), arg);
+    }
 }
 
 /*
@@ -3404,7 +3448,7 @@ syn_cmd_spell(exarg_T *eap, int syncing UNUSED)
        curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
     else
     {
-       semsg(_("E390: Illegal argument: %s"), arg);
+       semsg(_(e_illegal_arg), arg);
        return;
     }
 
@@ -3476,6 +3520,7 @@ syntax_clear(synblock_T *block)
     block->b_syn_slow = FALSE;     // clear previous timeout
 #endif
     block->b_syn_ic = FALSE;       // Use case, by default
+    block->b_syn_foldlevel = SYNFLD_START;
     block->b_syn_spell = SYNSPL_DEFAULT; // default spell checking
     block->b_syn_containedin = FALSE;
 #ifdef FEAT_CONCEAL
@@ -6192,6 +6237,7 @@ static struct subcommand subcommands[] =
     {"cluster",                syn_cmd_cluster},
     {"conceal",                syn_cmd_conceal},
     {"enable",         syn_cmd_enable},
+    {"foldlevel",      syn_cmd_foldlevel},
     {"include",                syn_cmd_include},
     {"iskeyword",      syn_cmd_iskeyword},
     {"keyword",                syn_cmd_keyword},
@@ -6489,6 +6535,18 @@ syn_get_stack_item(int i)
 #endif
 
 #if defined(FEAT_FOLDING) || defined(PROTO)
+    static int
+syn_cur_foldlevel(void)
+{
+    int                level = 0;
+    int                i;
+
+    for (i = 0; i < current_state.ga_len; ++i)
+       if (CUR_STATE(i).si_flags & HL_FOLD)
+           ++level;
+    return level;
+}
+
 /*
  * Function called to get folding level for line "lnum" in window "wp".
  */
@@ -6496,7 +6554,8 @@ syn_get_stack_item(int i)
 syn_get_foldlevel(win_T *wp, long lnum)
 {
     int                level = 0;
-    int                i;
+    int                low_level;
+    int                cur_level;
 
     // Return quickly when there are no fold items at all.
     if (wp->w_s->b_syn_folditems != 0
@@ -6508,9 +6567,25 @@ syn_get_foldlevel(win_T *wp, long lnum)
     {
        syntax_start(wp, lnum);
 
-       for (i = 0; i < current_state.ga_len; ++i)
-           if (CUR_STATE(i).si_flags & HL_FOLD)
-               ++level;
+       // Start with the fold level at the start of the line.
+       level = syn_cur_foldlevel();
+
+       if (wp->w_s->b_syn_foldlevel == SYNFLD_MINIMUM)
+       {
+           // Find the lowest fold level that is followed by a higher one.
+           cur_level = level;
+           low_level = cur_level;
+           while (!current_finished)
+           {
+               (void)syn_current_attr(FALSE, FALSE, NULL, FALSE);
+               cur_level = syn_cur_foldlevel();
+               if (cur_level < low_level)
+                   low_level = cur_level;
+               else if (cur_level > low_level)
+                   level = low_level;
+               ++current_col;
+           }
+       }
     }
     if (level > wp->w_p_fdn)
     {
index 024ba59a405c8fb8897d45388fcc05f95060eb20..bbcd0d88abfc9017ca6315910018afb1d26b7379 100644 (file)
@@ -160,7 +160,7 @@ endfunc
 
 func Test_syntax_completion()
   call feedkeys(":syn \<C-A>\<C-B>\"\<CR>", 'tx')
-  call assert_equal('"syn case clear cluster conceal enable include iskeyword keyword list manual match off on region reset spell sync', @:)
+  call assert_equal('"syn case clear cluster conceal enable foldlevel include iskeyword keyword list manual match off on region reset spell sync', @:)
 
   call feedkeys(":syn case \<C-A>\<C-B>\"\<CR>", 'tx')
   call assert_equal('"syn case ignore match', @:)
@@ -691,4 +691,87 @@ func Test_syntax_after_bufdo()
   call delete('Xddd.c')
 endfunc
 
+func Test_syntax_foldlevel()
+  new
+  call setline(1, [
+   \ 'void f(int a)',
+   \ '{',
+   \ '    if (a == 1) {',
+   \ '        a = 0;',
+   \ '    } else if (a == 2) {',
+   \ '        a = 1;',
+   \ '    } else {',
+   \ '        a = 2;',
+   \ '    }',
+   \ '    if (a > 0) {',
+   \ '        if (a == 1) {',
+   \ '            a = 0;',
+   \ '        } /* missing newline */ } /* end of outer if */ else {',
+   \ '        a = 1;',
+   \ '    }',
+   \ '    if (a == 1)',
+   \ '    {',
+   \ '        a = 0;',
+   \ '    }',
+   \ '    else if (a == 2)',
+   \ '    {',
+   \ '        a = 1;',
+   \ '    }',
+   \ '    else',
+   \ '    {',
+   \ '        a = 2;',
+   \ '    }',
+   \ '}',
+   \ ])
+  setfiletype c
+  syntax on
+  set foldmethod=syntax
+
+  call assert_fails('syn foldlevel start start', 'E390')
+  call assert_fails('syn foldlevel not_an_option', 'E390')
+
+  set foldlevel=1
+
+  syn foldlevel start
+  redir @c
+  syn foldlevel
+  redir END
+  call assert_equal("\nsyntax foldlevel start", @c)
+  syn sync fromstart
+  let a = map(range(3,9), 'foldclosed(v:val)')
+  call assert_equal([3,3,3,3,3,3,3], a) " attached cascade folds together
+  let a = map(range(10,15), 'foldclosed(v:val)')
+  call assert_equal([10,10,10,10,10,10], a) " over-attached 'else' hidden
+  let a = map(range(16,27), 'foldclosed(v:val)')
+  let unattached_results = [-1,17,17,17,-1,21,21,21,-1,25,25,25]
+  call assert_equal(unattached_results, a) " unattached cascade folds separately
+
+  syn foldlevel minimum
+  redir @c
+  syn foldlevel
+  redir END
+  call assert_equal("\nsyntax foldlevel minimum", @c)
+  syn sync fromstart
+  let a = map(range(3,9), 'foldclosed(v:val)')
+  call assert_equal([3,3,5,5,7,7,7], a) " attached cascade folds separately
+  let a = map(range(10,15), 'foldclosed(v:val)')
+  call assert_equal([10,10,10,13,13,13], a) " over-attached 'else' visible
+  let a = map(range(16,27), 'foldclosed(v:val)')
+  call assert_equal(unattached_results, a) " unattached cascade folds separately
+
+  set foldlevel=2
+
+  syn foldlevel start
+  syn sync fromstart
+  let a = map(range(11,14), 'foldclosed(v:val)')
+  call assert_equal([11,11,11,-1], a) " over-attached 'else' hidden
+
+  syn foldlevel minimum
+  syn sync fromstart
+  let a = map(range(11,14), 'foldclosed(v:val)')
+  call assert_equal([11,11,-1,-1], a) " over-attached 'else' visible
+
+  quit!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index 6f77139a346f2d0e0ab82038ec554ce9082ace6d..4fc7c3c796792ca2031aafd3657d190ba97245b9 100644 (file)
@@ -746,6 +746,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    865,
 /**/
     864,
 /**/