Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sorting words (not lines) in VIM

Tags:

bash

vim

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.

like image 770
drrlvn Avatar asked Aug 25 '09 12:08

drrlvn


People also ask

How do I rearrange lines in Vim?

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.

How do I sort text in Vim?

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.

How do I skip a line in Vim?

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.


2 Answers

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 
like image 128
DrAl Avatar answered Oct 04 '22 06:10

DrAl


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.

like image 38
drrlvn Avatar answered Oct 04 '22 05:10

drrlvn