Skip to content

Instantly share code, notes, and snippets.

@Raimondi
Forked from inkarkat/SortUnfolded.vim
Last active November 25, 2019 17:15
Show Gist options
  • Save Raimondi/90071c2bce80481efdcdedd3b7725c77 to your computer and use it in GitHub Desktop.
Save Raimondi/90071c2bce80481efdcdedd3b7725c77 to your computer and use it in GitHub Desktop.
A sort function for Vim that keeps folded lines intact.
":[range]SortUnfolded[!] [i][u][r][n][x][o] [/{pattern}/]
" Sort visible lines in [range]. Lines inside closed folds
" are kept intact; sorting is done only on the first line
" of the fold; the other lines inside the fold move with
" it as a unit.
" Copyright: (C) 2018 Israel Chauca
" 2012 Ingo Karkat
" The VIM LICENSE applies to this scriptlet; see ':help copyright'.
" Inspiration:
" http://stackoverflow.com/questions/13554191/sorting-vim-folds
function! s:ErrorMsg( text )
let v:errmsg = a:text
echohl ErrorMsg
echomsg v:errmsg
echohl None
endfunction
function! s:ExceptionMsg( exception )
" v:exception contains what is normally in v:errmsg, but with extra
" exception source info prepended, which we cut away.
call s:ErrorMsg(substitute(a:exception, '^Vim\%((\a\+)\)\=:', '', ''))
endfunction
function! s:REEscape( string )
return escape(a:string, '/\^$~.*[')
endfunction
function! s:GetClosedFolds( startLnum, endLnum )
"******************************************************************************
"* PURPOSE:
" Determine the ranges of closed folds within the passed range.
"* ASSUMPTIONS / PRECONDITIONS:
" None.
"* EFFECTS / POSTCONDITIONS:
" None.
"* INPUTS:
" a:startLnum First line of the range.
" a:endLnum Last line of the range.
"* RETURN VALUES:
" List of [foldStartLnum, foldEndLnum] elements.
"******************************************************************************
let folds = []
let lnum = a:startLnum
while lnum <= a:endLnum
let foldEndLnum = foldclosedend(lnum)
if foldEndLnum == -1
let lnum += 1
else
call add(folds, [lnum, foldEndLnum])
let lnum = foldEndLnum + 1
endif
endwhile
return folds
endfunction
function! s:JoinFolded( startLnum, endLnum, separator )
let folds = s:GetClosedFolds(a:startLnum, a:endLnum)
if empty(folds)
return [0, 0]
endif
let joinCnt = 0
let save_foldenable = &foldenable
set nofoldenable
try
for [foldStartLnum, foldEndLnum] in reverse(folds)
let cnt = foldEndLnum - foldStartLnum
for i in range(cnt)
let cmd = 'silent %ssubstitute/\n/\=a:separator'
execute printf(cmd, foldStartLnum)
endfor
let joinCnt += cnt
endfor
finally
let &foldenable = save_foldenable
endtry
return [len(folds), joinCnt]
endfunction
function! s:SortUnfolded( bang, startLnum, endLnum, sortArgs )
let separator = strtrans(nr2char(1))
let asciiCode = 1
while search(separator, 'wnc')
let asciiCode += 1
let separator .= strtrans(nr2char(asciiCode + 1))
echom printf('separator: %s', separator)
endwhile
let escapedSeparator = s:REEscape(separator)
echom printf('separator: %s', strtrans(escapedSeparator))
let [foldNum, joinCnt] = s:JoinFolded(a:startLnum, a:endLnum, separator)
if empty(foldNum)
call s:ErrorMsg('No folds found')
return
endif
let reducedEndLnum = a:endLnum - joinCnt
try
execute printf('%d,%dsort%s %s', a:startLnum, reducedEndLnum, a:bang, a:sortArgs)
catch /^Vim\%((\a\+)\)\=:E/
call s:ExceptionMsg(v:exception)
finally
silent execute printf('%d,%dsubstitute/%s/\r/g', a:startLnum, reducedEndLnum, escapedSeparator)
endtry
endfunction
command! -bang -range=% -nargs=* SortUnfolded call setline(<line1>, getline(<line1>)) | call s:SortUnfolded('<bang>', <line1>, <line2>, <q-args>)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment