From 9d8b23e03a6e4cc56c43e16c0fd234fa431cf67a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Yngve=20Lerv=C3=A5g?= Date: Mon, 24 Oct 2016 23:00:35 +0200 Subject: [PATCH] Fixes #597: Better command folding implementation --- autoload/vimtex/fold.vim | 247 +++++++++++++++++++++------------------ doc/vimtex.txt | 140 ++++++++++------------ 2 files changed, 197 insertions(+), 190 deletions(-) diff --git a/autoload/vimtex/fold.vim b/autoload/vimtex/fold.vim index 606b4cc..ec7c729 100644 --- a/autoload/vimtex/fold.vim +++ b/autoload/vimtex/fold.vim @@ -27,36 +27,17 @@ function! vimtex#fold#init_options() " {{{1 \ 'subsection', \ 'subsubsection', \ ]) - call vimtex#util#set_default('g:vimtex_fold_documentclass', 0) - - " Fold command pattern 1: \{} - " folded to '\{...}' - call vimtex#util#set_default('g:vimtex_fold_cmd_pattern1', 1) - call vimtex#util#set_default('g:vimtex_fold_cmd_pattern1_list', - \ [ - \ 'hypersetup', - \ 'tikzset', - \ ]) - " Fold command pattern 2: \[]{} - " folded to '\[...]{}' - call vimtex#util#set_default('g:vimtex_fold_cmd_pattern2', 1) - call vimtex#util#set_default('g:vimtex_fold_cmd_pattern2_list', - \ [ - \ 'usepackage', - \ 'includepdf', - \ ]) - " Fold command pattern 3: \{short mandatory arg}[]{}{\n} - " folded to '\{} ...' - " NOTE: final '}' has to be on its own line! - call vimtex#util#set_default('g:vimtex_fold_cmd_pattern3', 1) - call vimtex#util#set_default('g:vimtex_fold_cmd_pattern3_list', - \ [ - \ '%(re)?new%(command|environment)', - \ 'providecommand', - \ 'presetkeys', - \ 'Declare%(Multi|Auto)?CiteCommand', - \ 'Declare%(Index)?%(Field|List|Name)%(Format|Alias)', - \ ]) + call vimtex#util#set_default('g:vimtex_fold_commands_default', { + \ 'hypersetup' : 'single', + \ 'tikzset' : 'single', + \ 'usepackage' : 'single_opt', + \ 'includepdf' : 'single_opt', + \ '%(re)?new%(command|environment)' : 'multi', + \ 'providecommand' : 'multi', + \ 'presetkeys' : 'multi', + \ 'Declare%(Multi|Auto)?CiteCommand' : 'multi', + \ 'Declare%(Index)?%(Field|List|Name)%(Format|Alias)' : 'multi', + \}) " Disable manual mode in vimdiff let g:vimtex_fold_manual = &diff ? 0 : g:vimtex_fold_manual @@ -69,6 +50,21 @@ function! vimtex#fold#init_script() " {{{1 let s:notbslash = '\%(\\\@1' endif - " Fold documentclass - if g:vimtex_fold_documentclass - if line =~# '^\s*\\documentclass\s*\[\s*\%($\|%\)' - let s:documentclass = 1 - return 'a1' - elseif get(s:, 'documentclass', 0) && line =~# '^\s*\]{' - let s:documentclass = 0 - return 's1' - endif - endif - " Never fold \begin{document} if line =~# '^\s*\\begin\s*{\s*document\s*}' return '0' endif - " Fold command pattern 1 (e.g. hypersetup) - if g:vimtex_fold_cmd_pattern1 - if line =~# '^\s*\\\%('.join(g:vimtex_fold_cmd_pattern1_list, '\|').'\)\s*{\s*\%($\|%\)' - let s:cmd_pattern1 = 1 - return 'a1' - elseif get(s:, 'cmd_pattern1', 0) && line =~# '^\s*}' - let s:cmd_pattern1 = 0 - return 's1' - endif - endif - - " Fold command pattern 2 (e.g. usepackages) - if g:vimtex_fold_cmd_pattern2 - if line =~# '^\s*\\\%('.join(g:vimtex_fold_cmd_pattern2_list, '\|').'\)\s*\[\s*\%($\|%\)' - let s:cmd_pattern2 = 1 - return 'a1' - elseif get(s:, 'cmd_pattern2', 0) && line =~# '^\s*\]{' - let s:cmd_pattern2 = 0 - return 's1' - endif - endif - - " Fold command pattern 3 (e.g. newcommands) - if g:vimtex_fold_cmd_pattern3 - if line =~# '\v^\s*\\%('.join(g:vimtex_fold_cmd_pattern3_list, '|').')\*?' - \ && indent(a:lnum+1) > indent(a:lnum) - let s:cmd_pattern3 = indent(a:lnum) - return 'a1' - elseif exists('s:cmd_pattern3') - \ && indent(a:lnum) == s:cmd_pattern3 - \ && line =~# '^\s*}\s*$' - unlet s:cmd_pattern3 - return 's1' - endif - endif + " Fold commands + for l:cmd in s:cmd_types + let l:value = l:cmd.level(line, a:lnum) + if !empty(l:value) | return l:value | endif + endfor " Fold chapters and sections for [part, level] in b:vimtex_fold.parts @@ -277,36 +230,12 @@ function! vimtex#fold#text() " {{{1 return ' ' . matchstr(line, '\v\%\s*\zs.*\ze\{\{\{') endif - " Text for command pattern 1 (e.g. hypersetup) - if g:vimtex_fold_cmd_pattern1 - \ && line =~# '\v^\s*\\%('.join(g:vimtex_fold_cmd_pattern1_list, '|').')' - return matchstr(line, - \ '\v^\s*\\%('.join(g:vimtex_fold_cmd_pattern1_list, '|').')\*?') . '{...}' - endif - - " Text for command pattern 2 (e.g. usepackage) - if g:vimtex_fold_cmd_pattern2 - \ && line =~# '\v^\s*\\%('.join(g:vimtex_fold_cmd_pattern2_list, '|').')' - return matchstr(line, - \ '\v^\s*\\%('.join(g:vimtex_fold_cmd_pattern2_list, '|').')\*?') - \ .'[...]{' - \ . vimtex#cmd#get_at(v:foldstart, 1).args[0].text - \ . '}' - endif - - " Text for command pattern 3 (e.g. newcommand) - if g:vimtex_fold_cmd_pattern3 - \ && line =~# '\v^\s*\\%('.join(g:vimtex_fold_cmd_pattern3_list, '|').')' - return matchstr(line, - \ '\v^\s*\\%('.join(g:vimtex_fold_cmd_pattern3_list, '|').')\*?\{[^}]*\}') . ' ...' - endif - - " Text for documentclass - if g:vimtex_fold_documentclass && line =~# '^\s*\\documentclass' - return '\documentclass[...]{' - \ . vimtex#cmd#get_at(v:foldstart, 1).args[0].text - \ . '}' - endif + " Text for various folded commands + for l:cmd in s:cmd_types + if line =~# l:cmd.re.start + return l:cmd.text(line) + endif + endfor let level = v:foldlevel > 1 \ ? repeat('-', v:foldlevel-2) . g:vimtex_fold_levelmarker @@ -447,6 +376,100 @@ endfunction " }}}1 +function! s:cmd_single(cmds) " {{{1 + let l:re = '\v^\s*\\%(' . join(a:cmds, '|') . ')\*?' + + let l:fold = {} + let l:fold.re = { + \ 'start' : l:re . '\s*\{\s*%($|\%)', + \ 'end' : '^\s*}', + \ 'text' : l:re, + \} + + function! l:fold.level(line, lnum) dict + if a:line =~# self.re.start + let self.opened = 1 + return 'a1' + elseif has_key(self, 'opened') + \ && a:line =~# self.re.end + unlet self.opened + return 's1' + endif + return '' + endfunction + + function! l:fold.text(line) dict + return matchstr(a:line, self.re.text) . '{...}' + endfunction + + return l:fold +endfunction + +" }}}1 +function! s:cmd_single_opt(cmds) " {{{1 + let l:re = '\v^\s*\\%(' . join(a:cmds, '|') . ')\*?' + + let l:fold = {} + let l:fold.re = { + \ 'start' : l:re . '\s*\[\s*%($|\%)', + \ 'end' : '^\s*\]{', + \ 'text' : l:re, + \} + + function! l:fold.level(line, lnum) dict + if a:line =~# self.re.start + let self.opened = 1 + return 'a1' + elseif has_key(self, 'opened') + \ && a:line =~# self.re.end + unlet self.opened + return 's1' + endif + return '' + endfunction + + function! l:fold.text(line) dict + return matchstr(a:line, self.re.text) . '[...]{' + \ . vimtex#cmd#get_at(v:foldstart, 1).args[0].text . '}' + endfunction + + return l:fold +endfunction + +" }}}1 +function! s:cmd_multi(cmds) " {{{1 + let l:re = '\v^\s*\\%(' . join(a:cmds, '|') . ')\*?' + + let l:fold = {} + let l:fold.re = { + \ 'start' : l:re, + \ 'end' : '^\s*}\s*$', + \ 'text' : l:re . '\{[^}]*\}' + \} + + function! l:fold.level(line, lnum) dict + if a:line =~# self.re.start + \ && indent(a:lnum+1) > indent(a:lnum) + let self.indent = indent(a:lnum) + return 'a1' + elseif has_key(self, 'indent') + \ && a:line =~# self.re.end + \ && indent(a:lnum) == self.indent + unlet self.indent + return 's1' + endif + return '' + endfunction + + function! l:fold.text(line) dict + return matchstr(a:line, self.re.text) . ' ...' + endfunction + + return l:fold +endfunction + +" }}}1 + function! s:parse_label() " {{{1 let i = v:foldend while i >= v:foldstart diff --git a/doc/vimtex.txt b/doc/vimtex.txt index aa29cf8..177eb67 100644 --- a/doc/vimtex.txt +++ b/doc/vimtex.txt @@ -444,7 +444,8 @@ Options~ *g:vimtex_fold_preamble* Control whether or not to fold the preamble. - Note: This option can not be used together with |g:vimtex_fold_documentclass|. + Note: If |g:vimtex_fold_commands| contains `documentclass`, then this option + will be automatically disabled. Default value: 1 @@ -482,87 +483,68 @@ Options~ \ 'subsubsection', \ ] < -*g:vimtex_fold_cmd_pattern1* - Use this option to disable/enable folding of commands with a single - mandatory argument written on separate lines. The lines must be - formatted like this for folding to work properly: > +*g:vimtex_fold_commands* +*g:vimtex_fold_commands_default* + Use this option to disable/enable folding of commands. The option is + a dictionary of key-value pairs, where the key is a regex in very magic mode + (see |\v|) that matches the desired command name(s), and the value is the + variant of command folding. There are three variants allowed (see below for + a few examples that may be clarifying): - \hypersetup{ - option 1, - ..., - option n - } + `single` Useful for commands with a single long argument. + `single_opt` Useful for commands that open with a single long optional + argument, then a short "real" argument. + `multi` Useful for commands that start with a short regular argument + and continue with long optional and/or regular arguments. In + order for the folding to work as expected, this style depends + on the indentation to be correct. See the example below for + how to properly format the folded regions. + + The option combines the contents of |g:vimtex_fold_commands| and + |g:vimtex_fold_commands_default|. Entries in the former will have higher + priority than entries in the latter. This way, it is easy to add custom + entries without disturbing the defaults. For those who want to ignore the + defaults, one may simple set |g:vimtex_fold_commands_default| to an empty + dictionary. + + Examples of how command folds work for the different variants: > + + Variant Not folded Folded + ---------------------------------------------------------------------- + 'single' \hypersetup{ \hypersetup{...} + option 1, + ..., + option n + } + + 'single_opt' \usepackage[ \usepackage[...]{name} + option 1, + ..., + option n + ]{name} + + 'multi' \newcommand{\command}[3]{ \newcommand{\command} ... + Hello #1, #2, and #3. + } + ---------------------------------------------------------------------- < - Default value: 1 - -*g:vimtex_fold_cmd_pattern1_list* - Use this option to specify commands which should be folded according - to |g:vimtex_fold_cmd_pattern1|. + Note: |g:vimtex_fold_commands| deprecates the old *g:vimtex_fold_documentclass* + option. To fold the `\documentclass` command, it may be added with the + `single_opt` variant. As before, one must disable |g:vimtex_fold_preamble|. Default value: > - let *g:vimtex_fold_cmd_pattern1_list* = [ - \ 'hypersetup', - \ 'tikzset', - \ ] -< -*g:vimtex_fold_cmd_pattern2* - Use this option to disable/enable folding of commands with many - optional arguments written on separate lines. The lines must be - formatted like this for folding to work properly: > - - \usepackage[ - option 1, - ..., - option n - ]{name} -< - Default value: 1 - -*g:vimtex_fold_cmd_pattern2_list* - Use this option to specify commands which should be folded according - to |g:vimtex_fold_cmd_pattern2|. - - Default value: > - let *g:vimtex_fold_cmd_pattern2_list* = [ - \ 'usepackage', - \ 'includepdf', - \ ] -< -*g:vimtex_fold_cmd_pattern3* - Use this option to disable/enable folding of commands with a short - first mandatory argument and second or more mandatory arguments - written on separate lines. The lines must be formatted like this for - folding to work properly: > - - \[re]newcommand{\command}{ - ..., - } -< - That is, the fold region starts at the command and ends when at the final `}` - at the same indent level as the start of the command. - - Default value: 1 - -*g:vimtex_fold_cmd_pattern3_list* - Use this option to specify commands which should be folded according - to |g:vimtex_fold_cmd_pattern3|. - - Default value: > - let *g:vimtex_fold_cmd_pattern3_list* = [ - \ '%(re)?new%(command|environment)', - \ 'providecommand', - \ 'presetkeys', - \ 'Declare%(Multi|Auto)?CiteCommand', - \ 'Declare%(Index)?%(Field|List|Name)%(Format|Alias)', - \ ] -< -*g:vimtex_fold_documentclass* - Use this option to disable/enable folding of long `\documentclass` lines. This - works similar to |g:vimtex_fold_usepackage|. - - Note: This option can not be used together with |g:vimtex_fold_preamble|. - - Default value: 0 + let g:vimtex_fold_commands = {} + let g:vimtex_fold_commands_default = { + \ 'hypersetup' : 'single', + \ 'tikzset' : 'single', + \ 'usepackage' : 'single_opt', + \ 'includepdf' : 'single_opt', + \ '%(re)?new%(command|environment)' : 'multi', + \ 'providecommand' : 'multi', + \ 'presetkeys' : 'multi', + \ 'Declare%(Multi|Auto)?CiteCommand' : 'multi', + \ 'Declare%(Index)?%(Field|List|Name)%(Format|Alias)' : 'multi', + \} *g:vimtex_imaps_enabled* Use this option to disable/enable the insert mode mappings. @@ -1762,6 +1744,8 @@ Associated settings: |g:vimtex_fold_markers| |g:vimtex_fold_parts| |g:vimtex_fold_sections| + |g:vimtex_fold_commands| + |g:vimtex_fold_commands_default| ============================================================================== INDENTATION *vimtex-indent*