Merge pull request #12133 from bradking/syntax-fold

This commit is contained in:
James McCoy 2020-06-24 13:09:30 -04:00 committed by GitHub
commit 225f0bcd98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 178 additions and 7 deletions

View File

@ -3522,6 +3522,24 @@ 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".
SPELL CHECKING *:syn-spell*
:sy[ntax] spell [toplevel | notoplevel | default]
@ -3985,6 +4003,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.
*:syn-contains* *E405* *E406* *E407* *E408* *E409*

View File

@ -373,6 +373,10 @@ struct stl_hlrec {
#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
# define B_SPELL(buf) ((buf)->b_spell)
@ -398,6 +402,7 @@ typedef struct {
int b_syn_error; // TRUE when error occurred in HL
bool b_syn_slow; // true when 'redrawtime' reached
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

View File

@ -121,6 +121,8 @@ static int hl_attr_table[] =
{ HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE,
HL_INVERSE, HL_STRIKETHROUGH, HL_NOCOMBINE, 0 };
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.
// A start/end item consists of n start patterns and m end patterns.
@ -3045,7 +3047,7 @@ static void syn_cmd_conceal(exarg_T *eap, int syncing)
} else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3) {
curwin->w_s->b_syn_conceal = false;
} else {
EMSG2(_("E390: Illegal argument: %s"), arg);
EMSG2(_(e_illegal_arg), arg);
}
}
@ -3073,7 +3075,42 @@ static void syn_cmd_case(exarg_T *eap, int syncing)
} else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6) {
curwin->w_s->b_syn_ic = true;
} else {
EMSG2(_("E390: Illegal argument: %s"), arg);
EMSG2(_(e_illegal_arg), arg);
}
}
/// Handle ":syntax foldlevel" command.
static void syn_cmd_foldlevel(exarg_T *eap, int syncing)
{
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 {
EMSG2(_(e_illegal_arg), arg);
return;
}
arg = skipwhite(arg_end);
if (*arg != NUL) {
EMSG2(_(e_illegal_arg), arg);
}
}
@ -3105,7 +3142,7 @@ static void syn_cmd_spell(exarg_T *eap, int syncing)
} else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7) {
curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
} else {
EMSG2(_("E390: Illegal argument: %s"), arg);
EMSG2(_(e_illegal_arg), arg);
return;
}
@ -3161,6 +3198,7 @@ void syntax_clear(synblock_T *block)
block->b_syn_error = false; // clear previous error
block->b_syn_slow = false; // clear previous timeout
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;
block->b_syn_conceal = false;
@ -5485,6 +5523,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 },
@ -5763,6 +5802,17 @@ int syn_get_stack_item(int i)
return CUR_STATE(i).si_id;
}
static int syn_cur_foldlevel(void)
{
int level = 0;
for (int 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".
*/
@ -5776,9 +5826,22 @@ int syn_get_foldlevel(win_T *wp, long lnum)
&& !wp->w_s->b_syn_slow) {
syntax_start(wp, lnum);
for (int 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.
int cur_level = level;
int 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++;
}
}
}

View File

@ -153,7 +153,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', @:)
@ -579,3 +579,86 @@ func Test_syntax_hangs()
set redrawtime&
bwipe!
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