├── test ├── javadoc_c.vader ├── mediawiki.vader ├── header.vader ├── userfold.vader ├── markdown.vader └── javadoc.vader ├── autoload └── flexagon │ ├── userfold.vim │ └── folds.vim ├── doc └── flexagon.txt ├── plugin └── flexagon.vim └── README.md /test/javadoc_c.vader: -------------------------------------------------------------------------------- 1 | Given c (javadoc style c): 2 | #include 3 | /* normal comment 2 */ 4 | /** simple header 3 */ 5 | /*! bang header 4 */ 6 | 7 | Execute (c normal comment): 8 | AssertEqual flexagon#folds#javadoc(2), '=' 9 | Execute (c normal header): 10 | AssertEqual flexagon#folds#javadoc(3), '>1' 11 | Execute (c normal header): 12 | AssertEqual flexagon#folds#javadoc(3), '>1' 13 | Execute (c bang header): 14 | AssertEqual flexagon#folds#javadoc(3), '>1' 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/mediawiki.vader: -------------------------------------------------------------------------------- 1 | Given sh (mediawiki comments): 2 | # = H-1 ============= 3 | This is not a comment 4 | # == H-2 ============= 5 | # == no closing mark 6 | # === leading spaces 7 | 8 | Execute (test mediawiki H1): 9 | AssertEqual flexagon#folds#comment_marker(1, '='), '>1' 10 | Execute (test mediawiki non-comment): 11 | AssertEqual flexagon#folds#comment_marker(2, '='), '=' 12 | Execute (test mediawiki H2): 13 | AssertEqual flexagon#folds#comment_marker(3, '='), '>2' 14 | Execute (test mediawiki H2 no closing): 15 | AssertEqual flexagon#folds#comment_marker(4, '='), '>2' 16 | Execute (test leading indents work): 17 | AssertEqual flexagon#folds#comment_marker(5, '='), '>3' 18 | 19 | 20 | -------------------------------------------------------------------------------- /test/header.vader: -------------------------------------------------------------------------------- 1 | Given sh (header): 2 | # 1 ============= 3 | # 2 This is not a comment 4 | # 3 ============= 5 | # 4test 6 | # 5 ----------------- 7 | # 6test 8 | # 7++++++++++++++++ 9 | # 10 | 11 | Execute (test flowerpot header line): 12 | AssertEqual flexagon#folds#header(1), '=' 13 | AssertEqual flexagon#folds#header(2), '>1' 14 | AssertEqual flexagon#folds#header(3), '=' 15 | AssertEqual flexagon#folds#header(4), '>1' 16 | AssertEqual flexagon#folds#header(5), '=' 17 | AssertEqual flexagon#folds#header(6), '>1' 18 | 19 | Given c (header): 20 | #include 'stdio.h'; 21 | /********************* 22 | * this is a comment 3 23 | */ 24 | #include 'stdio.h'; 25 | 26 | Execute (test C flowerpot header line): 27 | AssertEqual flexagon#folds#header(1), '=' 28 | AssertEqual flexagon#folds#header(2), '=' 29 | AssertEqual flexagon#folds#header(3), '>1' 30 | AssertEqual flexagon#folds#header(4), '=' 31 | AssertEqual flexagon#folds#header(5), '=' 32 | 33 | -------------------------------------------------------------------------------- /autoload/flexagon/userfold.vim: -------------------------------------------------------------------------------- 1 | 2 | let s:user_folds = { } 3 | function! flexagon#userfold#register(name, function) abort 4 | let s:user_folds[a:name] = a:function 5 | endfunction 6 | 7 | function! flexagon#userfold#list() abort 8 | return s:user_folds 9 | endfunction 10 | function! flexagon#userfold#keys() abort 11 | return keys(s:user_folds) 12 | endfunction 13 | 14 | function! flexagon#userfold#defined(name) abort 15 | return has_key( s:user_folds, a:name ) 16 | endfunction 17 | 18 | let s:selected_user_fold = '' 19 | function! flexagon#userfold#set_user_fold( fold ) abort 20 | let s:selected_user_fold = a:fold 21 | endfunction 22 | function! flexagon#userfold#get_user_fold( ) abort 23 | return s:selected_user_fold 24 | endfunction 25 | 26 | function! flexagon#userfold#user_fold(lnum) abort 27 | let l:falias = flexagon#userfold#get_user_fold() 28 | if len(l:falias) == 0 29 | echo "no fold alias defined" 30 | return 31 | endif 32 | let l:fname = s:user_folds[l:falias] 33 | let l:Func = function(l:fname) 34 | return l:Func( a:lnum ) 35 | endfunction 36 | -------------------------------------------------------------------------------- /test/userfold.vader: -------------------------------------------------------------------------------- 1 | Given text (simple fold text): 2 | Lorem ipsum dolor sit amet, consectetur 3 | adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna 4 | aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi 5 | ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in 6 | voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint 7 | occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim 8 | id est laborum. 9 | 10 | Execute (setup a user fold): 11 | function! Modulo(lnum) abort 12 | if a:lnum % 3 == 0 13 | return '>1' 14 | else 15 | return '=' 16 | endif 17 | endfunction 18 | call flexagon#userfold#register( 'modulo', 'Modulo' ) 19 | AssertEqual flexagon#userfold#defined( 'modulo' ), 1 20 | AssertEqual flexagon#userfold#defined( 'bogus' ), 0 21 | 22 | Execute (setup a user fold): 23 | call flexagon#userfold#set_user_fold( 'modulo' ) 24 | AssertEqual flexagon#userfold#user_fold( 1 ), '=' 25 | AssertEqual flexagon#userfold#user_fold( 2 ), '=' 26 | AssertEqual flexagon#userfold#user_fold( 3 ), '>1' 27 | -------------------------------------------------------------------------------- /test/markdown.vader: -------------------------------------------------------------------------------- 1 | Given sh (markdown comments): 2 | # Normal comment 3 | This is not a comment 4 | ## H1 5 | ### H3 6 | 7 | Execute (test markdown shell comments H1): 8 | AssertEqual flexagon#folds#comment_marker(1, "#"), '=' 9 | Execute (test markdown shell comments non-comment): 10 | AssertEqual flexagon#folds#comment_marker(2, "#"), '=' 11 | Execute (test markdown shell comments H2): 12 | AssertEqual flexagon#folds#markdown(3), '>1' 13 | Execute (test markdown shell comments H3): 14 | AssertEqual flexagon#folds#markdown(4), '>2' 15 | 16 | Given markdown (regular markdown): 17 | # H-1 ============= 18 | This is not a comment 19 | ## H-2 ============= 20 | ## no closing mark 21 | ### no closing mark 22 | 23 | Execute (test regular markdown H1): 24 | AssertEqual flexagon#folds#markdown(1), '>1' 25 | Execute (test regular markdown not comment): 26 | AssertEqual flexagon#folds#markdown(2), '=' 27 | Execute (test regular markdown H2): 28 | AssertEqual flexagon#folds#markdown(3), '>2' 29 | Execute (test regular markdown H2, no closing bar): 30 | AssertEqual flexagon#folds#markdown(4), '>2' 31 | Execute (test regular markdown H3): 32 | AssertEqual flexagon#folds#markdown(5), '>3' 33 | 34 | 35 | -------------------------------------------------------------------------------- /test/javadoc.vader: -------------------------------------------------------------------------------- 1 | Given sh (javadoc style shell): 2 | # normal comment 3 | echo some stuff 4 | 5 | ## H1 6 | echo some more stuff 7 | 8 | Execute (normal comment): 9 | AssertEqual flexagon#folds#javadoc(1), '=' 10 | Execute (normal not comment): 11 | AssertEqual flexagon#folds#javadoc(2), '=' 12 | Execute (blank): 13 | AssertEqual flexagon#folds#javadoc(3), '=' 14 | Execute (doubled header): 15 | AssertEqual flexagon#folds#javadoc(4), '>1' 16 | Execute (normal code): 17 | AssertEqual flexagon#folds#javadoc(5), '=' 18 | 19 | Given vim (javadoc style vim): 20 | " normal comment 21 | echo some stuff 2 22 | 23 | "" H1 4 24 | echo some more stuff 5 25 | "" H1 6 26 | 27 | Execute (vim normal comment): 28 | AssertEqual flexagon#folds#javadoc(1), '=' 29 | Execute (vim normal not comment): 30 | AssertEqual flexagon#folds#javadoc(2), '=' 31 | Execute (vim blank): 32 | AssertEqual flexagon#folds#javadoc(3), '=' 33 | Execute (vim doubled header): 34 | AssertEqual flexagon#folds#javadoc(4), '>1' 35 | Execute (vim normal code): 36 | AssertEqual flexagon#folds#javadoc(5), '=' 37 | Execute (vim whitespace plus header): 38 | AssertEqual flexagon#folds#javadoc(6), '>1' 39 | 40 | Given php (javadoc style php): 41 | 1' 59 | Execute (php normal code): 60 | AssertEqual flexagon#folds#javadoc(6), '=' 61 | Execute (php more commands): 62 | AssertEqual flexagon#folds#javadoc(7), '=' 63 | Execute (php leader triple slash): 64 | AssertEqual flexagon#folds#javadoc(8), '>1' 65 | Execute (php space leader triple slash): 66 | AssertEqual flexagon#folds#javadoc(8), '>1' 67 | -------------------------------------------------------------------------------- /doc/flexagon.txt: -------------------------------------------------------------------------------- 1 | *vim-flexagon* Easy access to a variety of expression folds 2 | 3 | ================================================================================ 4 | CONTENTS *FlexagonContents* 5 | 6 | 1. Introduction.................|FlexagonIntro| 7 | 2. Commands.....................|FlexagonCommands| 8 | 3. Folds........................|FlexagonFolds| 9 | 4. Settings.....................|FlexagonSettings| 10 | 5. Mappings.....................|FlexagonMapppins| 11 | 12 | ================================================================================ 13 | 1. INTRODUCTION *FlexagonIntro* 14 | 15 | Folding is a dark corner of vim, but potentially quite powerful. It's a 16 | tool to hide "unimportant" text, but that skirts the question, "what is 17 | unimportant". This plugin attempts to ease unlocking this power. 18 | 19 | This plugin is (mostly) a wrapper around |foldexpr| allowing you to 20 | easily specify a high level "hiding strategy". It exposes one command 21 | *Fold:* which takes a folding argument. The options are exposed via tab 22 | complete. 23 | 24 | 25 | ================================================================================ 26 | 2. COMMANDS *FlexagonCommands* 27 | 28 | *:Fold* 29 | :Fold [foldtype] Choose a fold type with tab auto-complete 30 | 31 | ================================================================================ 32 | 3. FOLDS *FlexagonFolds* 33 | 34 | wiki Look for Mediawiki style headers ( = HEADER ) embedded in 35 | comments. Up to 4 levels are recognized. 36 | 37 | markdown Similar to wiki header, but with markdown hash style 38 | headers ( # HEADER ). This also recognizes standard header 39 | levels. This can be applied against standard markdown and 40 | will work where hash comment markers are valid. 41 | 42 | bar comment with long string of =, -, or _ 43 | single level fold 44 | 45 | doxygen Doxygen/javadoc style "doubled up" comments, "/**" for c, 46 | '//' for php/cpp. This is supports two layers 47 | 48 | header Look for flowerpot style headers. One level of fold. 49 | Recognizes "bars" of various sorts and attempts to a 50 | relevant comment to fold on. 51 | 52 | space Use blank lines as section/fold separators 53 | As a first order fold this works very well on a wide variety 54 | of files 55 | 56 | comment fold non-commented code. This is useful approximation of 57 | Donald Knuth's "Literate" code. 58 | 59 | code fold comments 60 | 61 | braces Use { and } as markers. For most C inspired code this is an 62 | easy first level fold 63 | 64 | ini Fold on INI style headers 65 | 66 | html Fold on H tags 67 | 68 | manual Reset to vim manual folding 69 | 70 | indent Reset to vim indent folding 71 | 72 | php fold php code embedded in between markers. Adding a 73 | "bang" (!) reverses the orientation, folding non-embedded 74 | code. 75 | 76 | asp Fold asp code embedded in between <% %> markers. Works for 77 | ASP as well as languages like JSP. 78 | 79 | ================================================================================ 80 | 4. SETTINGS *FlexagonSettings* 81 | 82 | *flexagon_disable_foldtext* 83 | Flexagon has it's own foldtext formatting. It's enabled by default. It 84 | shouldn't be too annoying and features a simple but useful search for 85 | "relevant" fold text that compliments the included folds. 86 | 87 | To disabled it: 88 | 89 | let g:flexagon_disable_foldtext = 1 90 | 91 | Please note, the folding function tries to skip "empty" text in an 92 | effort to make the fold text more useful. 93 | 94 | If you like the tab complete Fold, but need a fold not supplied, 95 | Flexagon allows you to register your own fold. 96 | 97 | :call flexgon#userfold#register('fold_alias', 'FunctionName') 98 | 99 | ================================================================================ 100 | 4. MAPPINGS *FlexagonFolds* 101 | 102 | Flexagon provides a utility "bubble" function to walk up and down top 103 | lecel folds. By default there are mappings for ZJ and ZJ mirroring vim's 104 | native zj and zk mappings. These two mapping are meant to walk through 105 | folded code, expanding the next or last fold and closing the prior fold 106 | The mappings use to avoid stepping on personal mappings. 107 | There's no current vim mapping for those mappings. There are two 108 | mappings 109 | 110 | BubbleDown 111 | BubbleUp 112 | -------------------------------------------------------------------------------- /plugin/flexagon.vim: -------------------------------------------------------------------------------- 1 | if exists('g:loaded_flexagon') 2 | finish 3 | endif 4 | let g:loaded_flexagon = 1 5 | 6 | let s:save_cpo = &cpoptions 7 | set cpoptions&vim 8 | 9 | command! -nargs=* FlexagonRegisterFold :call flexagon#userfold#register() 10 | 11 | let s:folds_list = [ ] 12 | 13 | function! s:register_fold( fold ) abort 14 | call add( s:folds_list, a:fold ) 15 | endfunction 16 | 17 | function! s:has_included_fold(foldname) abort 18 | if index( s:folds_list, a:foldname ) != -1 19 | return 1 20 | endif 21 | return 0 22 | endfunction 23 | 24 | call s:register_fold( 'asp' ) 25 | call s:register_fold( 'bar' ) 26 | call s:register_fold( 'braces' ) 27 | call s:register_fold( 'code' ) 28 | call s:register_fold( 'comment' ) 29 | call s:register_fold( 'doxygen' ) 30 | call s:register_fold( 'function' ) 31 | call s:register_fold( 'header' ) 32 | call s:register_fold( 'html' ) 33 | call s:register_fold( 'indent' ) 34 | call s:register_fold( 'ini' ) 35 | call s:register_fold( 'manual' ) 36 | call s:register_fold( 'markdown' ) 37 | call s:register_fold( 'php' ) 38 | call s:register_fold( 'space' ) 39 | call s:register_fold( 'wiki' ) 40 | 41 | function! s:fold_complete(...) abort 42 | let l:fold_completions = extend( s:folds_list , flexagon#userfold#keys() ) 43 | return join( l:fold_completions, "\n" ) 44 | endfunction 45 | command! -bang -nargs=1 -complete=custom,fold_complete Fold call custom_fold("", "") 46 | 47 | " let s:user_selected_fold = '' 48 | " function! FlexagonUserFunction(lnum) abort 49 | " let l:func_name = s:user_folds[s:user_selected_fold] 50 | " let l:Func = function( l:func_name ) 51 | " return l:Func( a:lnum ) 52 | " endfunction 53 | 54 | function! s:custom_fold(fold, bang) abort 55 | if a:fold ==# 'manual' 56 | call set_stock_fold('manual') 57 | return 58 | endif 59 | if a:fold ==# 'indent' 60 | call set_stock_fold('indent') 61 | return 62 | endif 63 | if s:has_included_fold( a:fold ) 64 | setlocal foldmethod=expr 65 | execute 'setlocal foldexpr=flexagon#folds#' . a:fold . '(v:lnum)' 66 | endif 67 | if flexagon#userfold#defined( a:fold ) 68 | call flexagon#userfold#set_user_fold( a:fold ) 69 | setlocal foldmethod=expr 70 | setlocal foldexpr=flexagon#userfold#user_fold(v:lnum) 71 | endif 72 | if a:fold ==# 'braces' 73 | setlocal foldmethod=marker 74 | setlocal foldmarker={,} 75 | setlocal foldnestmax=1 76 | endif 77 | if a:fold ==# 'php' 78 | setlocal foldmethod=marker 79 | if a:bang ==# "!" 80 | setlocal foldmarker=?>, 83 | endif 84 | setlocal foldnestmax=1 85 | endif 86 | if a:fold ==# 'asp' 87 | setlocal foldmethod=marker 88 | if a:bang ==# "!" 89 | setlocal foldmarker=%>,<% 90 | else 91 | setlocal foldmarker=<%,%> 92 | endif 93 | setlocal foldnestmax=1 94 | endif 95 | call set_fold_settings() 96 | normal! zz 97 | if foldlevel('.') == 0 98 | normal! zj 99 | endif 100 | normal! zA 101 | endfunction 102 | 103 | function! s:set_stock_fold(method) abort 104 | execute 'setlocal foldmethod=' . a:method 105 | call set_fold_settings() 106 | endfunction 107 | 108 | function! s:set_fold_settings() abort 109 | setlocal foldcolumn=3 110 | setlocal foldlevel=0 111 | setlocal foldenable 112 | setlocal foldtext=FoldText() 113 | endfunction 114 | 115 | function! FoldText() abort 116 | let l:foldsize = (v:foldend-v:foldstart) 117 | let l:line = getline(v:foldstart) 118 | " use a line with info 119 | if len(l:line) <= 4 120 | let l:next_line = getline(v:foldstart + 1) 121 | if len(l:next_line) > 4 122 | let l:line = l:next_line 123 | endif 124 | endif 125 | let l:line = l:line[0:60] 126 | let l:line_leader = '+-' . repeat( '---', v:foldlevel - 1 ) 127 | let l:text_length= strlen(l:line . l:line_leader) 128 | let l:fillerlength = winwidth(0) - l:text_length 129 | let l:line_count_text = repeat('+', v:foldlevel) . l:foldsize . ' lines ' 130 | let l:line_count_filler = repeat( ' ', 15 - len(l:line_count_text)) 131 | let l:line_count_text = l:line_count_filler . l:line_count_text 132 | let l:fillerlength = l:fillerlength - 35 133 | return l:line_leader . '> ' . l:line . repeat( '-', l:fillerlength) . l:line_count_text 134 | endfunction 135 | 136 | if ! exists('flexagon_disable_foldtext') 137 | set foldtext=FoldText() 138 | endif 139 | 140 | function! s:bubble_fold(direction) abort 141 | if foldlevel('.') != 0 142 | normal! zC 143 | endif 144 | if a:direction ==# 'up' 145 | normal! zk 146 | else 147 | normal! zj 148 | endif 149 | normal! zO 150 | normal! [z 151 | normal! zt 152 | endfunction 153 | 154 | nmap BubbleDown :call bubble_fold("down") 155 | if mapcheck( 'ZJ', 'n') ==# '' 156 | nmap ZJ BubbleDown 157 | endif 158 | 159 | nmap BubbleUp :call bubble_fold("up") 160 | if mapcheck( 'ZK', 'n') ==# '' 161 | nmap ZK BubbleUp 162 | endif 163 | 164 | let &cpoptions = s:save_cpo 165 | unlet s:save_cpo 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flexagon, A Vim fold method method manager 2 | 3 | A wrapper around vims folding to easily switch between various folding schemes 4 | and some useful pre-packaged schemes, all designed to be easily discovered and 5 | easily switched. 6 | 7 | Flexagon also provides some custom fold presentation and a "bubble" Plug 8 | mapping to walk through folds. 9 | 10 | # Overview 11 | 12 | Vim offers several folding methods, each with trade-offs, none to my mind fully 13 | satisfying. Further, I've found folds useful for code visualization, a purpose 14 | that's not a primary aim of vim's folding method. 15 | 16 | Rather then striving to find the perfect fold method, I decided to embrace the 17 | diversity and make it easy to add and change folding methods 18 | 19 | # Inspiration 20 | 21 | An interesting and short comment thread on Reddit, in particular user 22 | ``/u/]interiot`` preference for using mediawiki syntax over vim's native 23 | folding 24 | https://www.reddit.com/r/vim/comments/1hnh8v/question_what_foldmethod_are_you_guys_using_and/ 25 | 26 | Wiki header syntax is intuitive, natural and clean. 27 | It also has a natural and unobtrusive way to signal levels. 28 | 29 | Some other natural schemes along this line: 30 | 31 | * Markdown 32 | 33 | * Javadoc/Doxygen 34 | 35 | * "Flower pot" header bars 36 | 37 | # Visualization 38 | 39 | Folding offers a way for a text based editor to visualize "shape". Selectively 40 | hiding code offers a way to emphasize other code. Flexagon offers two several 41 | folds that help visualize code structure: 42 | 43 | * Hide comments ( ``:Fold: code`` ) 44 | 45 | * Hide non-comments ( ``:Fold: comments`` ) 46 | 47 | * Blank and or all whitespace lines ( ``:Fold: space`` ) 48 | 49 | Each offers a different emphasis on the code, which I've found surprisingly 50 | revealing and useful. 51 | 52 | # Levels 53 | 54 | One level of folding offers most of the benefit of folding, as folding 55 | visibility is binary (visible or not). There is benefit in several levels, but 56 | each additional level offers less. In particular each higher level fold 57 | requires working through more mental details Many of the packaged folds in 58 | Flexagon are conceived and implemented with only one fold level in mind. 59 | 60 | As in vim's native marker folds, multiple levels are likely better when 61 | deliberate, as the higher levels are more likely to be related with each other. 62 | For example, a layer 4 indent in one block may have no relationship to a layer 63 | 4 indent in another block, but the intent of a ``{{`` marker in one block most 64 | likely is highly related to a ``{{`` in another block. 65 | 66 | # Commands 67 | 68 | There is one command **''Fold:''** (shadowing vim's native ``:fold`` commeand). 69 | It offers tab completion of the various folding methods. 70 | 71 | 72 | ## Custom Folds 73 | 74 | Currently supported folding arguments: 75 | 76 | * ***wiki*** Use embedded wiki ``=`` heading markers. Given this code: 77 | 78 | #!/usr/bin/perl 79 | # = this is a first level header 80 | some_code_here(); 81 | # == this is a second level header 82 | some_more_code_here(); 83 | # === this is a third level header 84 | even_more_code(); 85 | 86 | ``:Fold wiki`` will create nested folds as indicated. 87 | 88 | Leading comments are expected. 89 | 90 | * ***markdown*** similar to wiki, but use hash header. Also works on regular 91 | markdown 92 | 93 | * ***doxygen*** Not a true/full javadoc/doxygen fold, but a useful approximation. 94 | Fold when an extra comment character is present, i.e.: 95 | * ``/**`` in ``c`` 96 | * ``##`` in shell, perl etc. 97 | * ``///`` ( or ``##``) in ``php`` 98 | Most of these expect ``commentstring`` to be set. 99 | 100 | * ***space*** space separated sections (i.e. "paragraphs") 101 | Since spaces often separate "ideas" this Fold provides an overview of the 102 | ideas in the code. 103 | 104 | * ***comment*** hide code and show comments. This approximates "literate" code 105 | (Donald Knuth's interesting idea). 106 | 107 | * ***code*** This is the inverse of comment above, showing only code. This is 108 | useful in code with excess comments (yes this exists :-) ) 109 | 110 | * ***braces*** Fold on brace characters. I found this in Steve Losh's .vimrc 111 | and immediately wondered why this wasn't a standard. It's a very good fit for 112 | any C language family code, roughly the equivalent of ``indent`` as a python 113 | fold. 114 | 115 | * **header** "Flower Pot" headers (not particularly sophisticated) 116 | 117 | * ***ini*** fold on DOS INI section headers with second folds on breaks and 118 | comments 119 | 120 | * ***html*** A not entirely successful attempt to fold HTML 121 | 122 | ## Vim Native Folds 123 | Since I've shadowed vim's native fold command it's only polite to allow 124 | switching to those native methods 125 | * ***manual*** 126 | * ***indent*** 127 | 128 | # Discussion 129 | 130 | Folding with more then one level deep is complicated, not only in 131 | implementation, but in conception. Syntax folding offers fine control, in a way 132 | the seems logical, but in practice this has not worked well for me. Let alone 133 | resource drag, my practical experience changing the fold level expects more 134 | knowledge of which folding level I want to see. One level is easy to 135 | understand, and two levels provides most of the utility. 136 | 137 | -------------------------------------------------------------------------------- /autoload/flexagon/folds.vim: -------------------------------------------------------------------------------- 1 | " Public Folding calls for flexagon 2 | 3 | " # HELPER_FUNCTIONS ======================================================= 4 | " 5 | " test vim syntax notions of comment 6 | function! flexagon#folds#iscomment(lnum) abort 7 | return flexagon#folds#match_line_syntax(a:lnum, 'comment') 8 | endfunction 9 | 10 | function! flexagon#folds#is_function(lnum) abort 11 | return flexagon#folds#match_line_syntax(a:lnum, '\vfunckey|function') 12 | endfunction 13 | 14 | function! flexagon#folds#match_line_syntax(lnum, matchstring) abort 15 | let l:start = match(getline(a:lnum), '\v\s*\zs\S') + 1 16 | let l:syn_id = synID( a:lnum, l:start, 0) 17 | let l:syn_name = synIDattr( l:syn_id, 'name') 18 | if l:syn_name =~? a:matchstring 19 | return 1 20 | else 21 | return 0 22 | endif 23 | endfunction 24 | 25 | " # MAIN FOLDS ================================================================ 26 | 27 | function! flexagon#folds#function(lnum) abort 28 | if flexagon#folds#is_function(a:lnum) 29 | return '>1' 30 | else 31 | return '=' 32 | endif 33 | endfunction 34 | 35 | " Fold by mediawiki header 36 | " http://www.reddit.com/r/vim/comments/1hnh8v/question_what_foldmethod_are_you_guys_using_and/ 37 | function! flexagon#folds#wiki(lnum) abort 38 | return flexagon#folds#comment_marker(a:lnum, '=') 39 | endfunction 40 | 41 | " similar idea with markdown headers in comments 42 | function! flexagon#folds#markdown(lnum) abort 43 | if &filetype =~# '\vmarkdown|pandoc' 44 | return flexagon#folds#markdown_filetype(a:lnum) 45 | else 46 | return flexagon#folds#comment_marker(a:lnum, '#') 47 | endif 48 | endfunction 49 | 50 | function! flexagon#folds#markdown_filetype(lnum) abort 51 | if flexagon#folds#iscomment(a:lnum) 52 | return '=' 53 | endif 54 | let l:cline = getline(a:lnum) 55 | for l:i in [1, 2, 3, 4] 56 | if match( l:cline, '\v^[#]{' . l:i . '}[^#]' ) != -1 57 | return '>' . l:i 58 | endif 59 | endfor 60 | return '=' 61 | endfunction 62 | 63 | " factor out comment style 64 | function! flexagon#folds#comment_marker(lnum, leader_char ) abort 65 | if ! flexagon#folds#iscomment(a:lnum) 66 | return '=' 67 | endif 68 | let l:cline = strpart( getline(a:lnum), 0, 15 ) 69 | for l:i in [ 1, 2, 3, 4] 70 | let l:match_wiki_header = '\v^[^' . a:leader_char . ']*[' . a:leader_char . ']{' . l:i . '}[^' . a:leader_char . ']' 71 | if match( l:cline, l:match_wiki_header ) != -1 72 | if a:leader_char ==# '#' && &commentstring =~# '#' 73 | if l:i == 1 74 | continue 75 | else 76 | return '>' . ( l:i - 1 ) 77 | endif 78 | else 79 | return '>' . l:i 80 | endif 81 | endif 82 | endfor 83 | return '=' 84 | endfunction 85 | 86 | " Fold non-space 87 | function! flexagon#folds#space(lnum) abort 88 | let l:cline = getline(a:lnum ) 89 | if l:cline =~# '^\s*$' 90 | return '<1' 91 | else 92 | return '1' 93 | endif 94 | endfunction 95 | 96 | " fold non-comment 97 | function! flexagon#folds#comment(lnum) abort 98 | let l:cline = getline(a:lnum) 99 | if flexagon#folds#iscomment(a:lnum) 100 | return '0' 101 | elseif l:cline =~# '^\w*$' 102 | return '=' 103 | else 104 | return '1' 105 | endif 106 | endfunction 107 | 108 | " fold non code 109 | function! flexagon#folds#code(lnum) abort 110 | let l:cline = getline(a:lnum) 111 | if flexagon#folds#iscomment(a:lnum) 112 | return '1' 113 | elseif l:cline =~# '^\w*$' 114 | return '=' 115 | else 116 | return '0' 117 | endif 118 | endfunction 119 | 120 | " basic folding ini files 121 | function! flexagon#folds#ini(lnum) abort 122 | if flexagon#folds#iscomment(a:lnum) 123 | return '=' 124 | endif 125 | let l:line = getline(a:lnum) 126 | let l:prev_line = getline(a:lnum - 1) 127 | if l:line =~# '^\s*\[[^]]\+\]' 128 | return '>1' 129 | elseif l:line =~# '^\s*;' 130 | return '>2' 131 | elseif l:prev_line =~# '^\s*$' 132 | return '>2' 133 | endif 134 | return '=' 135 | endfunction 136 | 137 | function! flexagon#folds#bar(lnum) abort 138 | if ! flexagon#folds#iscomment(a:lnum) 139 | if getline(a:lnum) =~# '\v[=-_]{4,}' 140 | return '>1' 141 | endif 142 | endif 143 | return '=' 144 | endfunction 145 | 146 | " flowerpot style headers 147 | function! flexagon#folds#header(lnum) abort 148 | if ! flexagon#folds#iscomment(a:lnum) 149 | return '=' 150 | endif 151 | " has some content 152 | if getline(a:lnum) =~# '\v\S{2,}' 153 | " if before or after is flowerpot 154 | if getline( a:lnum - 1 ) =~# '\v[#*+=_-]{5,}' 155 | \ || getline( a:lnum + 1 ) =~# '\v[#*+=_-]{5,}' 156 | return '>1' 157 | else 158 | return '=' 159 | endif 160 | endif 161 | return '=' 162 | endfunction 163 | 164 | function! flexagon#folds#doxygen(lnum) abort 165 | if ! flexagon#folds#iscomment(a:lnum) 166 | return '=' 167 | endif 168 | let l:cline = getline(a:lnum) 169 | if &filetype ==# 'php' 170 | if match( l:cline, '\v^\s*[/]{3,}' ) != -1 171 | return '>1' 172 | elseif match( l:cline, '\v^\s*[#]{2,}' ) != -1 173 | return '>1' 174 | endif 175 | endif 176 | if &filetype ==# 'c' 177 | if match( l:cline, '\v^\s*[/][*][*!]' ) != -1 178 | return '>1' 179 | endif 180 | endif 181 | if len( &commentstring ) == 3 && strcharpart( &commentstring, 1, 3) ==# '%s' 182 | let l:comment_char = strcharpart( &commentstring, 0, 1 ) 183 | if match( l:cline, '\v^\s*[' . l:comment_char . ']{3}' ) != -1 184 | return '>2' 185 | elseif match( l:cline, '\v^\s*[' . l:comment_char . ']{2}' ) != -1 186 | return '>1' 187 | endif 188 | endif 189 | if len( &commentstring ) == 4 && strcharpart( &commentstring, 2, 4) ==# '%s' 190 | let l:comment_char = strcharpart( &commentstring, 0, 1 ) 191 | if match( l:cline, '\v^\s*[' . l:comment_char . ']{4}' ) != -1 192 | return '>2' 193 | elseif match( l:cline, '\v^\s*[' . l:comment_char . ']{3}' ) != -1 194 | return '>1' 195 | endif 196 | endif 197 | return '=' 198 | endfunction 199 | 200 | " FIXME partially working HTML structuring folds 201 | function! flexagon#folds#html(lnum) abort 202 | if getline( a:lnum ) =~# '.*1' 204 | elseif getline( a:lnum +1 ) =~# '.*>.*' 207 | return '>1' 208 | elseif getline( a:lnum ) =~# '.*.*' 209 | return '<1' 210 | elseif getline( a:lnum ) =~# '.*.*>.*.*' 211 | return '=' 212 | elseif getline( a:lnum ) =~# '.*.*>.*' 213 | return 'a1' 214 | elseif getline( a:lnum ) =~# '.*.*>.*' 215 | return 's1' 216 | elseif getline( a:lnum ) =~# '.*.*>.*' 217 | return 's1' 218 | elseif getline( a:lnum ) =~# '.*.*.*' 219 | return '=' 220 | elseif getline( a:lnum ) =~# '.*.*' 221 | return 'a1' 222 | elseif getline( a:lnum ) =~# '.*.*' 223 | return 's1' 224 | elseif getline( a:lnum ) =~# '.*.*.*' 225 | return '=' 226 | elseif getline( a:lnum ) =~# '.*.*' 227 | return 'a1' 228 | elseif getline( a:lnum ) =~# '.*.*' 229 | return 's1' 230 | elseif getline( a:lnum ) =~# '.*.*.*' 231 | return '=' 232 | elseif getline( a:lnum ) =~# '.*.*' 233 | return 'a1' 234 | elseif getline( a:lnum ) =~# '.*.*' 235 | return 's1' 236 | elseif getline( a:lnum ) =~# '.*<[ou]l\>.*' 237 | return 'a1' 238 | elseif getline( a:lnum ) =~# '.*<\/[ou]\>.*' 239 | return 's1' 240 | elseif getline( a:lnum ) =~# '.*.*>.*.*>.*' 241 | return '=' 242 | elseif getline( a:lnum ) =~# '.*.*>.*' 243 | return 'a1' 244 | elseif getline( a:lnum ) =~# '.*.*>.*' 245 | return 's1' 246 | else 247 | return '=' 248 | endif 249 | endfunction 250 | 251 | --------------------------------------------------------------------------------