问题
As far as I know vim's :sort method will sort each line. I have some code that is in groups of 3 lines. How can I sort this? Please ignore the shitty code, it's a legacy app :'(
I would like to sort by the case 'AF' line but ignore (group) the country and break line
case 'AF':
country = 'Afghanistan';
break;,
case 'AL':
country = 'Albania';
break;,
case 'DZ':
country = 'Algeria';
break;,
case 'AS':
country = 'American Samoa';
break;,
case 'AD':
country = 'Andorra';
break;,
case 'AO':
country = 'Angola';
break;,
case 'AI':
country = 'Anguilla';
break;,
case 'AQ':
country = 'Antarctica';
break;,
case 'AG':
country = 'Antigua And Barbuda';
break;,
case 'AR':
country = 'Argentina';
break;,
case 'AM':
country = 'Armenia';
break;,
case 'AW':
country = 'Aruba';
break;,
case 'AU':
country = 'Australia';
break;,
case 'AT':
country = 'Austria';
break;,
case 'AZ':
country = 'Azerbaijan';
break;,
case 'BS':
country = 'Bahamas';
break;,
case 'BH':
country = 'Bahrain';
break;,
case 'BD':
country = 'Bangladesh';
break;,
case 'BB':
country = 'Barbados';
break;,
case 'BY':
country = 'Belarus';
break;
回答1:
My AdvancedSorters plugin implements the algorithm suggested by @Halst and @SatoKatsura (joining, sorting, then unjoining) as a simple custom command:
:SortRangesByHeader /^case/
The plugin implements various other sorting methods, e.g. by folds, ranges, etc.
回答2:
A buzzword-compliant version of the solution suggested by @Halst:
- mark lines
join them on a character that doesn't appear in code:
:'<,'>s/[:;]\zs\n/@/
mark lines again
sort them:
:'<,'>sort
mark lines one last time
split them on
@
::'<,'>s/@/\r/g
You'll need to fix the last term manually. No need for indent
, sort
, or any other external program.
You can also avoid marking lines if you move the relevant code to a scrap buffer and re-format it there.
回答3:
One way to do this is to collapse the statements into one line like this:
case 'AG': country = 'Antigua And Barbuda'; break;
By doing a visual select and replacing newline+indent with space:
:'<,'>s/\n / /g
Then sorting the selection:
:'<,'>!sort
Then running the region of code through some pretty-printer, for example, GNU indent
:
https://www.gnu.org/software/indent/manual/indent.html
This is one advantage of adopting machine-formatted code—you can run a messy transformation (for example a regex) and then fix it all up automatically.
回答4:
I created a command for this :SortGroup
" :[range]SortGroup[!] [n|f|o|b|x] /{pattern}/
" e.g. :SortGroup /^header/
" e.g. :SortGroup n /^header/
" See :h :sort for details
This means you can do :SortGroup /case/
Implementation below:
function! s:sort_by_header(bang, pat) range
let pat = a:pat
let opts = ""
if pat =~ '^\s*[nfxbo]\s'
let opts = matchstr(pat, '^\s*\zs[nfxbo]')
let pat = matchstr(pat, '^\s*[nfxbo]\s*\zs.*')
endif
let pat = substitute(pat, '^\s*', '', '')
let pat = substitute(pat, '\s*$', '', '')
let sep = '/'
if len(pat) > 0 && pat[0] == matchstr(pat, '.$') && pat[0] =~ '\W'
let [sep, pat] = [pat[0], pat[1:-2]]
endif
if pat == ''
let pat = @/
endif
let ranges = []
execute a:firstline . ',' . a:lastline . 'g' . sep . pat . sep . 'call add(ranges, line("."))'
let converters = {
\ 'n': {s-> str2nr(matchstr(s, '-\?\d\+.*'))},
\ 'x': {s-> str2nr(matchstr(s, '-\?\%(0[xX]\)\?\x\+.*'), 16)},
\ 'o': {s-> str2nr(matchstr(s, '-\?\%(0\)\?\x\+.*'), 8)},
\ 'b': {s-> str2nr(matchstr(s, '-\?\%(0[bB]\)\?\x\+.*'), 2)},
\ 'f': {s-> str2float(matchstr(s, '-\?\d\+.*'))},
\ }
let arr = []
for i in range(len(ranges))
let end = max([get(ranges, i+1, a:lastline+1) - 1, ranges[i]])
let line = getline(ranges[i])
let d = {}
let d.key = call(get(converters, opts, {s->s}), [strpart(line, match(line, pat))])
let d.group = getline(ranges[i], end)
call add(arr, d)
endfor
call sort(arr, {a,b -> a.key == b.key ? 0 : (a.key < b.key ? -1 : 1)})
if a:bang
call reverse(arr)
endif
let lines = []
call map(arr, 'extend(lines, v:val.group)')
let start = max([a:firstline, get(ranges, 0, 0)])
call setline(start, lines)
call setpos("'[", start)
call setpos("']", start+len(lines)-1)
endfunction
command! -range=% -bang -nargs=+ SortGroup <line1>,<line2>call <SID>sort_by_header(<bang>0, <q-args>)
Note: this requires Vim 8+ due to use of lambdas
来源:https://stackoverflow.com/questions/46660252/is-it-possible-to-sort-a-groups-of-lines-in-vim