Skip to content

Commit

Permalink
Vim fold sections (#708)
Browse files Browse the repository at this point in the history
* Add section movement to snakefiles

Added [section
movements](https://learnvimscriptthehardway.stevelosh.com/chapters/50.html)
to snakefiles to navigate between rules, checkpoints, and functions.

The move to closings, `][` and `[]` assume rules are separated by at
least a blank line.

* Add folding to snakefiles

Create folds for:
- The preamble (top of file until first rule)
- Each top-level rule, checkpoint, function and class
- Loops with anonymous rules

So the following will have folds indicated with '<'
```
configfile: 'config.yaml'  # <

rule all:  # <
    input: SAMPLES

for s in SAMPLES:  # NO FOLD HERE
    print(s)

checkpoint test:  # <
    # ...

print('Here')  # <
for s in SAMPLES:
    rule:
        # ...
print('Not here')
```

Commands following rules will fold with their predecessor rule, unless
the next rule is indented.
  • Loading branch information
troycomi committed Oct 29, 2020
1 parent c39ed35 commit cfb8348
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 0 deletions.
61 changes: 61 additions & 0 deletions misc/vim/ftplugin/snakemake/folding.vim
@@ -0,0 +1,61 @@
setlocal foldmethod=expr
setlocal foldexpr=GetSnakemakeFold(v:lnum)

function! GetSnakemakeFold(lnum)
" fold preamble
if a:lnum == 1
return '>1'
endif

let thisline = getline(a:lnum)

" blank lines end folds
if thisline =~? '\v^\s*$'
return '-1'
" start fold on top level rules or python objects
elseif thisline =~? '\v^(rule|def|checkpoint|class)'
return ">1"
elseif thisline =~? '\v^\S'
if PreviousLineIndented(a:lnum) && NextRuleIndented(a:lnum)
return ">1"
endif
endif

return "="

endfunction

function! NextRuleIndented(lnum)
let numlines = line('$')
let current = a:lnum + 1

while current <= numlines
let thisline = getline(current)
if thisline =~? '\v^(rule|def|checkpoint|class)'
return 0
elseif thisline =~? '\v^\s+(rule|checkpoint)'
return 1
endif

let current += 1
endwhile

return 0
endfunction

function! PreviousLineIndented(lnum)
let current = a:lnum - 1

while current >= 1
let thisline = getline(current)
if thisline =~? '\v^\S'
return 0
elseif thisline =~? '\v^\s+\S'
return 1
endif

let current -= 1
endwhile

return 0
endfunction
43 changes: 43 additions & 0 deletions misc/vim/ftplugin/snakemake/sections.vim
@@ -0,0 +1,43 @@
function! s:NextSection(type, backwards, visual)
if a:visual
normal! gv
endif

if a:type == 1
let pattern = '\v(^rule|^checkpoint|^def|%^)'
elseif a:type == 2
let pattern = '\v\n\zs\n^(rule|checkpoint|def)'
endif

if a:backwards
let dir = '?'
else
let dir = '/'
endif

execute 'silent normal! ' . dir . pattern . dir . "\r"
endfunction

noremap <script> <buffer> <silent> ]]
\ :call <SID>NextSection(1, 0, 0)<cr>
noremap <script> <buffer> <silent> [[
\ :call <SID>NextSection(1, 1, 0)<cr>
noremap <script> <buffer> <silent> ][
\ :call <SID>NextSection(2, 0, 0)<cr>
noremap <script> <buffer> <silent> []
\ :call <SID>NextSection(2, 1, 0)<cr>
vnoremap <script> <buffer> <silent> ]]
\ :<c-u>call <SID>NextSection(1, 0, 1)<cr>
vnoremap <script> <buffer> <silent> [[
\ :<c-u>call <SID>NextSection(1, 1, 1)<cr>
vnoremap <script> <buffer> <silent> ][
\ :<c-u>call <SID>NextSection(2, 0, 1)<cr>
vnoremap <script> <buffer> <silent> []
\ :<c-u>call <SID>NextSection(2, 1, 1)<cr>

0 comments on commit cfb8348

Please sign in to comment.