Skip to content

Instantly share code, notes, and snippets.

@Fmajor
Created April 4, 2021 15:41
Show Gist options
  • Save Fmajor/f124b8223530da20c03cbd58c1063f10 to your computer and use it in GitHub Desktop.
Save Fmajor/f124b8223530da20c03cbd58c1063f10 to your computer and use it in GitHub Desktop.
custom vim folding
"#<== custom highlights
" highligh enpty end of each line
highlight WhitespaceEOL ctermbg=red guibg=red
match WhitespaceEOL /\s\+$/
highlight NoneText ctermfg=8
highlight SpecialKey ctermfg=8
" highlight link name1 name2
highlight foldStart ctermfg=7 ctermbg=23
highlight foldStartStr ctermfg=11 ctermbg=23
call matchadd("foldStart", '^\s*.\?#<==*\ \(.*\)$\|^\s*.\?.\?#region:\ \(.*\)$')
call matchadd("foldStartStr", '^\s*.\?#<==*\ \zs\(.*\)\ze$\|^\s*.\?.\?#region:\ \zs\(.*\)\ze$')
highlight foldEnd ctermfg=0 ctermbg=13
highlight foldEndStr ctermfg=190 ctermbg=13
call matchadd("foldEnd", '^\s*.\?#==\+>\ \(.*\)$\|^\s*.\?.\?#endregion:\ \(.*\)$')
call matchadd("foldEndStr", '^\s*.\?#==\+>\ \zs\(.*\)\ze$\|^\s*.\?.\?#endregion:\ \zs\(.*\)\ze$')
"syntax region foldKeys start=/^\s*.\{1}#<==* / end=/$/ oneline contains=vimLineComment
highlight myComment cterm=bold ctermfg=11 ctermbg=22
call matchadd("myComment", '.\?!!\+\ \(.*\)$')
highlight Folded term=standout ctermfg=14 ctermbg=242 guifg=Cyan guibg=DarkGrey
"!! myComment hight
"#==> custom highlights
"#<== custom folding method
" reload vimrc
nnoremap ,re :source ~/.vimrc<CR>
" This costom functions enable using both the 'indent' folding
" and the custom '#<==' '#==>' 'Marker' folding
" To improve the performance, i (on purpose) simplify the algorithms
" sometimes you will get WRONG folding layout when you edit the
" first or last line of each block
" then use `,re` to reload vimrc, this will fix the WRONG folding layout
"nnoremap <space> @=((foldclosed(line('.')) < 0) ? 'zc' :'zo')<CR>
nnoremap <silent> <space> za:redraw!<CR>
set foldenable
"set foldmethod=indent foldignore=
set foldmethod=expr
set foldexpr=MyFold(v:lnum)
"inBlock count the stack depth of my maker pair
let b:inBlock=0
let b:lastLineNum=0
let b:lastLevel=0
let b:lastGoodLine=0
let b:lastGoodBlock=0
let b:startFoldingMark='^\s*.\?#<==*\|^.*#<==$\|^\s*.\?.\?#region'
let b:endFoldingMark='^\s*.\?#=*=>\|^.*#==>$\|^\s*.\?.\?#endregion'
" experiment function..
let b:docStringGroupName='pythonDocstring'
function! DocStrStatus(linenum, linetext)
" to add additional fold level for docstring
let syntaxGroupName = map(synstack(a:linenum, 1), 'synIDattr(v:val, "name")')
let b:inDocStrFlag = index(syntaxGroupName, b:docStringGroupName) >= 0
return b:inDocStrFlag
endfunction
function! MyFold(linenum)
let linetext = getline(a:linenum)
let level = indent(a:linenum) / &shiftwidth
"let l:inDocStr = DocStrStatus(a:linenum, linetext)
let l:inDocStr = 0
"the first line have 0 fold level
if (a:linenum == 1)
let l:inDocStr = 0
if linetext =~ b:startFoldingMark
let b:inBlock = 1
let b:lastLineNum=a:linenum
let b:lastGoodLine=0
let b:lastGoodBlock=0
let b:lastLevel=level
return level + l:inDocStr
endif
let b:inBlock=0
let b:lastInBlock=0
let b:lastLineNum=a:linenum
let b:lastGoodLine=0
let b:lastGoodBlock=b:inBlock
"echom a:linenum ':' level+b:inBlock '=' level '+' b:inBlock
let b:lastLevel=level + b:inBlock
return level + b:inBlock + l:inDocStr
endif
" not calculate line by line from last line, recovery good fold level from previous lines
if ((b:lastLineNum+1) != a:linenum)
"let l:inDocStr = 0
let level = indent(a:linenum) / &shiftwidth
let lastGoodNum = a:linenum-1
" skip black lines
silent echom 'recovery searching begin from: ' a:linenum ':' linetext ', lastLineNum:' b:lastLineNum
while (lastGoodNum>1 && getline(lastGoodNum) =~? '\v^\s*$' )
silent echom 'recovery searching: lastGoodNum:' lastGoodNum ':' foldlevel(lastGoodNum) ": content:" getline(lastGoodNum)
silent echom 'recovery searching: lastGoodNum-1:' lastGoodNum-1 ':' foldlevel(lastGoodNum-1) ": content:" getline(lastGoodNum-1) ", go on"
let lastGoodNum -= 1
endwhile
silent echom 'recovery search ok: lastGoodNum:' lastGoodNum ", content:" getline(lastGoodNum) ', infos: lastGoodLine' b:lastGoodLine 'lastGoodBlock:' b:lastGoodBlock
if (foldlevel(lastGoodNum)==-1)
let b:inBlock=b:lastGoodBlock
silent echom 'recovery inBlock with lastGoodBlock: ' b:inBlock
else
let lastlevel = indent(lastGoodNum) / &shiftwidth
let lastlinetext = getline(lastGoodNum)
let lastlinelevel = foldlevel(lastGoodNum)
if lastlinetext =~ b:startFoldingMark
let b:inBlock = lastlinelevel - lastlevel + 1
silent echom 'recovery meet startFoldingMark ' b:inBlock
elseif lastlinetext =~ b:endFoldingMark
let b:inBlock = lastlinelevel - lastlevel - 1
silent echom 'recovery meet endFoldingMark' b:inBlock
else
let b:inBlock = lastlinelevel - lastlevel
endif
silent echom 'recovery inBlock with line ' lastGoodNum 'to be: ' b:inBlock '=' lastlinelevel '-' lastlevel
endif
endif
"blank lines have undefined fold level
if getline(a:linenum) =~? '\v^\s*$'
"echom '(' b:lastLineNum ':' foldlevel(b:lastLineNum) ')' a:linenum ':' -1
let b:lastLineNum=a:linenum
let b:lastLevel=-1
return -1
endif
" fold calculate line by line, lastnumber = linumber - 1
" b:inBlock is the right value
"if next line is a start of new marker block, inBlock ++
if linetext =~ b:startFoldingMark
let b:inBlock = b:inBlock + 1
"echom '(' b:lastLineNum ':' foldlevel(b:lastLineNum) ')' a:linenum ':' level '|' b:inBlock '(+=1)'
let b:lastLineNum=a:linenum
if (b:lastLevel != -1)
let b:lastGoodLine=a:linenum
let b:lastGoodBlock=b:inBlock
endif
let b:lastLevel=level + b:inBlock - 1
return level + b:inBlock - 1 " start fold in the next line
"if next line is an end of new marker block, inBlock -
elseif linetext =~ b:endFoldingMark
let b:inBlock = b:inBlock - 1
"echom '(' b:lastLineNum ':' foldlevel(b:lastLineNum) ')' a:linenum ':' level+b:inBlock+1 "|" b:inBlock '(-=1)'
let b:lastLineNum=a:linenum
"if (b:lastLevel != -1)
let b:lastGoodLine=a:linenum
let b:lastGoodBlock=b:inBlock
"endif
let b:lastLevel=level + b:inBlock + 1
return level + b:inBlock + 1 "the end is still folded
endif
"echom '(' b:lastLineNum ':' foldlevel(b:lastLineNum) ')' a:linenum ':' level+b:inBlock '=' level '+' b:inBlock
" for normal lines not in start and end folding mark list
let b:lastLineNum=a:linenum
"if (b:lastLevel != -1)
let b:lastGoodLine=a:linenum
let b:lastGoodBlock=b:inBlock
"endif
let b:lastLevel=level + b:inBlock
return level+b:inBlock + l:inDocStr
endfunction
" function for debug, leave it
nnoremap <F2> :call Do_debug_folding()<CR>
function! Do_debug_folding()
exec "echom foldlevel(line('.')) b:lastLineNum"
endfunction
set foldcolumn=0
nnoremap ,fc :call Set_foldcolumn()<CR>
function! Set_foldcolumn()
if &foldcolumn=="0"
set foldcolumn=1
echo "foldcolumn set to 3"
else
set foldcolumn=0
echo "foldcolumn set to 0"
endif
endfunction
function! IndentLevel(lnum)
return indent(a:lnum) / &shiftwidth
endfunction
" ftplugin
" .vim/after/ftplugin/markdown/folding.vim
"!! fold text
function! MyFoldText()
let foldsize = (v:foldend-v:foldstart)
return getline(v:foldstart).' ('.foldsize.' lines)'
endfunction
set foldtext=MyFoldText()
"!! special folding method for some ft
"
"#<== Fold python docstring
" \| because map need a \, the origin exp not have that \
" have problem for one line docstring with content after docstring
"nnoremap ,docf :%s/\v(\s*(class\|def).*)(#\<\=\=)@<!(\n\s*("""\|''').*)%(\5.*\5.*)@<!(\n\_.{-1,}\5)/\1 #<==\4\6 #==>/g<CR>
"nnoremap ,docuf :%s/\v(\s*(class\|def).*)( #\<\=\=)(\n\s*("""\|''').*)%(\5.*\5.*)@<!(\n\_.{-1,}\5)( #\=\=\>)/\1\4\6/g<CR>
"#==>
"#==> custom folding method
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment