The built-in VIM :sort
command sorts lines of text. I want to sort words in a single line, e.g. transform the line
b a d c e f
to
a b c d e f
Currently I accomplish this by selecting the line and then using :!tr ' ' '\n' | sort | tr '\n' ' '
, but I'm sure there's a better, simpler, quicker way. Is there?
Note that I use bash so if there's a shorter and more elegant bash command for doing this it's also fine.
EDIT: My use-case is that I have a line that says SOME_VARIABLE="one two three four etc"
and I want the words in that variable to be sorted, i.e. I want to have SOME_VARIABLE="etc four one three two"
.
The end result should preferably be mappable to a shortcut key as this is something I find myself needing quite often.
In normal mode or in insert mode, press Alt-j to move the current line down, or press Alt-k to move the current line up. After visually selecting a block of lines (for example, by pressing V then moving the cursor down), press Alt-j to move the whole block down, or press Alt-k to move the block up.
Sorting text in Vim is easy! Select the text, then press : , type sort , then hit enter! It'll sort the whole document by default, but you can enter a range too.
If we are in the vim editor, then simply do this, “Press the ENTER key, write the Line number, and press Shift+ g”: Again the output is the same.
In pure vim, you could do this:
call setline('.', join(sort(split(getline('.'), ' ')), " "))
Edit
To do this so that it works over a range that is less than one line is a little more complicated (this allows either sorting multiple lines individually or sorting part of one line, depending on the visual selection):
command! -nargs=0 -range SortWords call SortWords() " Add a mapping, go to your string, then press vi",s " vi" selects everything inside the quotation " ,s calls the sorting algorithm vmap ,s :SortWords<CR> " Normal mode one: ,s to select the string and sort it nmap ,s vi",s function! SortWords() " Get the visual mark points let StartPosition = getpos("'<") let EndPosition = getpos("'>") if StartPosition[0] != EndPosition[0] echoerr "Range spans multiple buffers" elseif StartPosition[1] != EndPosition[1] " This is a multiple line range, probably easiest to work line wise " This could be made a lot more complicated and sort the whole " lot, but that would require thoughts on how many " words/characters on each line, so that can be an exercise for " the reader! for LineNum in range(StartPosition[1], EndPosition[1]) call setline(LineNum, join(sort(split(getline('.'), ' ')), " ")) endfor else " Single line range, sort words let CurrentLine = getline(StartPosition[1]) " Split the line into the prefix, the selected bit and the suffix " The start bit if StartPosition[2] > 1 let StartOfLine = CurrentLine[:StartPosition[2]-2] else let StartOfLine = "" endif " The end bit if EndPosition[2] < len(CurrentLine) let EndOfLine = CurrentLine[EndPosition[2]:] else let EndOfLine = "" endif " The middle bit let BitToSort = CurrentLine[StartPosition[2]-1:EndPosition[2]-1] " Move spaces at the start of the section to variable StartOfLine while BitToSort[0] == ' ' let BitToSort = BitToSort[1:] let StartOfLine .= ' ' endwhile " Move spaces at the end of the section to variable EndOfLine while BitToSort[len(BitToSort)-1] == ' ' let BitToSort = BitToSort[:len(BitToSort)-2] let EndOfLine = ' ' . EndOfLine endwhile " Sort the middle bit let Sorted = join(sort(split(BitToSort, ' ')), ' ') " Reform the line let NewLine = StartOfLine . Sorted . EndOfLine " Write it out call setline(StartPosition[1], NewLine) endif endfunction
Using great ideas from your answers, especially Al's answer, I eventually came up with the following:
:vnoremap <F2> d:execute 'normal i' . join(sort(split(getreg('"'))), ' ')<CR>
This maps the F2 button in visual
mode to delete the selected text, split, sort and join it and then re-insert it. When the selection spans multiple lines this will sort the words in all of them and output one sorted line, which I can quickly fix using gqq
.
I'll be glad to hear suggestions on how this can be further improved.
Many thanks, I've learned a lot :)
EDIT: Changed '<C-R>"'
to getreg('"')
to handle text with the char '
in it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With