Sorting words (not lines) in VIM Sorting words (not lines) in VIM bash bash

Sorting words (not lines) in VIM


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 algorithmvmap ,s :SortWords<CR>" Normal mode one: ,s to select the string and sort itnmap ,s vi",sfunction! 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)    endifendfunction


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.


Here's the equivalent in pure vimscript:

 :call setline('.',join(sort(split(getline('.'),' ')),' '))

It's no shorter or simpler, but if this is something you do often, you can run it across a range of lines:

 :%call setline('.',join(sort(split(getline('.'),' ')),' '))

Or make a command

 :command -nargs=0 -range SortLine <line1>,<line2>call setline('.',join(sort(split(getline('.'),' ')),' '))

Which you can use with

:SortLine:'<,'>SortLine:%SortLine

etc etc