Merge pull request #12133 from bradking/syntax-fold
This commit is contained in:
commit
225f0bcd98
|
@ -3522,6 +3522,24 @@ DEFINING CASE *:syn-case* *E390*
|
||||||
:sy[ntax] case
|
:sy[ntax] case
|
||||||
Show either "syntax case match" or "syntax case ignore" (translated).
|
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*
|
SPELL CHECKING *:syn-spell*
|
||||||
|
|
||||||
:sy[ntax] spell [toplevel | notoplevel | default]
|
: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
|
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.
|
ends. If the start and end are within the same line, there is no fold.
|
||||||
The 'foldnestmax' option limits the nesting of syntax folds.
|
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*
|
*:syn-contains* *E405* *E406* *E407* *E408* *E409*
|
||||||
|
|
|
@ -373,6 +373,10 @@ struct stl_hlrec {
|
||||||
#define SYNSPL_TOP 1 // spell check toplevel text
|
#define SYNSPL_TOP 1 // spell check toplevel text
|
||||||
#define SYNSPL_NOTOP 2 // don't 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
|
// avoid #ifdefs for when b_spell is not available
|
||||||
# define B_SPELL(buf) ((buf)->b_spell)
|
# define B_SPELL(buf) ((buf)->b_spell)
|
||||||
|
|
||||||
|
@ -398,6 +402,7 @@ typedef struct {
|
||||||
int b_syn_error; // TRUE when error occurred in HL
|
int b_syn_error; // TRUE when error occurred in HL
|
||||||
bool b_syn_slow; // true when 'redrawtime' reached
|
bool b_syn_slow; // true when 'redrawtime' reached
|
||||||
int b_syn_ic; // ignore case for :syn cmds
|
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
|
int b_syn_spell; // SYNSPL_ values
|
||||||
garray_T b_syn_patterns; // table for syntax patterns
|
garray_T b_syn_patterns; // table for syntax patterns
|
||||||
garray_T b_syn_clusters; // table for syntax clusters
|
garray_T b_syn_clusters; // table for syntax clusters
|
||||||
|
|
|
@ -121,6 +121,8 @@ static int hl_attr_table[] =
|
||||||
{ HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE,
|
{ HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE,
|
||||||
HL_INVERSE, HL_STRIKETHROUGH, HL_NOCOMBINE, 0 };
|
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.
|
// The patterns that are being searched for are stored in a syn_pattern.
|
||||||
// A match item consists of one pattern.
|
// A match item consists of one pattern.
|
||||||
// A start/end item consists of n start patterns and m end patterns.
|
// 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) {
|
} else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3) {
|
||||||
curwin->w_s->b_syn_conceal = false;
|
curwin->w_s->b_syn_conceal = false;
|
||||||
} else {
|
} 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) {
|
} else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6) {
|
||||||
curwin->w_s->b_syn_ic = true;
|
curwin->w_s->b_syn_ic = true;
|
||||||
} else {
|
} 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) {
|
} else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7) {
|
||||||
curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
|
curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
|
||||||
} else {
|
} else {
|
||||||
EMSG2(_("E390: Illegal argument: %s"), arg);
|
EMSG2(_(e_illegal_arg), arg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3161,6 +3198,7 @@ void syntax_clear(synblock_T *block)
|
||||||
block->b_syn_error = false; // clear previous error
|
block->b_syn_error = false; // clear previous error
|
||||||
block->b_syn_slow = false; // clear previous timeout
|
block->b_syn_slow = false; // clear previous timeout
|
||||||
block->b_syn_ic = false; // Use case, by default
|
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_spell = SYNSPL_DEFAULT; // default spell checking
|
||||||
block->b_syn_containedin = false;
|
block->b_syn_containedin = false;
|
||||||
block->b_syn_conceal = false;
|
block->b_syn_conceal = false;
|
||||||
|
@ -5485,6 +5523,7 @@ static struct subcommand subcommands[] =
|
||||||
{ "cluster", syn_cmd_cluster },
|
{ "cluster", syn_cmd_cluster },
|
||||||
{ "conceal", syn_cmd_conceal },
|
{ "conceal", syn_cmd_conceal },
|
||||||
{ "enable", syn_cmd_enable },
|
{ "enable", syn_cmd_enable },
|
||||||
|
{ "foldlevel", syn_cmd_foldlevel },
|
||||||
{ "include", syn_cmd_include },
|
{ "include", syn_cmd_include },
|
||||||
{ "iskeyword", syn_cmd_iskeyword },
|
{ "iskeyword", syn_cmd_iskeyword },
|
||||||
{ "keyword", syn_cmd_keyword },
|
{ "keyword", syn_cmd_keyword },
|
||||||
|
@ -5763,6 +5802,17 @@ int syn_get_stack_item(int i)
|
||||||
return CUR_STATE(i).si_id;
|
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".
|
* 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) {
|
&& !wp->w_s->b_syn_slow) {
|
||||||
syntax_start(wp, lnum);
|
syntax_start(wp, lnum);
|
||||||
|
|
||||||
for (int i = 0; i < current_state.ga_len; ++i) {
|
// Start with the fold level at the start of the line.
|
||||||
if (CUR_STATE(i).si_flags & HL_FOLD) {
|
level = syn_cur_foldlevel();
|
||||||
++level;
|
|
||||||
|
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++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,7 @@ endfunc
|
||||||
|
|
||||||
func Test_syntax_completion()
|
func Test_syntax_completion()
|
||||||
call feedkeys(":syn \<C-A>\<C-B>\"\<CR>", 'tx')
|
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 feedkeys(":syn case \<C-A>\<C-B>\"\<CR>", 'tx')
|
||||||
call assert_equal('"syn case ignore match', @:)
|
call assert_equal('"syn case ignore match', @:)
|
||||||
|
@ -579,3 +579,86 @@ func Test_syntax_hangs()
|
||||||
set redrawtime&
|
set redrawtime&
|
||||||
bwipe!
|
bwipe!
|
||||||
endfunc
|
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
|
||||||
|
|
Loading…
Reference in New Issue