41

Often I just want to sort all my #include's at the top of my source and header files by their length.

vim allows me to sort alphanumerically in a similar manner with :{range} sort u.

In vim, how do you sort a range of lines by the length of the line? Such that shorter lines are followed by longer lines.

Searching the internet, I found this:

:% s/.*/\=printf("%03d", len(submatch(0)))."|".submatch(0)/ | sor n | %s/..../

But that only works to sort the entire file, and is black magic to me anyway. I'm trying to figure out how to do that same sort with a range such as from line 4 to 18, as in :4,18 s/... Do you have any ideas?

Qiau
  • 5,976
  • 3
  • 29
  • 40
Cory Klein
  • 51,188
  • 43
  • 183
  • 243

3 Answers3

41

Filter Visual Selection with Awk

One way to do this in vim is by filtering the visual selection with awk's length() function before sorting. For example:

:'<,'> ! awk '{ print length(), $0 | "sort -n | cut -d\\  -f2-" }'
Todd A. Jacobs
  • 81,402
  • 15
  • 141
  • 199
  • 2
    @CoryKlein That *is* a range. The example just specifies it using vim's visual selection (lines selected with `SHIFT-V + `). If you want line addresses, just replace `'<,'>` with any other valid address specification (e.g. `5,12`). – Todd A. Jacobs Jul 18 '12 at 14:40
  • Awesome, thanks! I didn't really understand the syntax of `'<,'>`, and it's good to know that this is just another way to specify a range, and that this is how you specify the range of the visual selection. – Cory Klein Jul 18 '12 at 18:44
  • Even with awk installed on my Windows system this still fails for me – icc97 Sep 05 '17 at 07:01
8

One way, neither elegant nor efficient, but it works:

Add following function to your vimrc file. It inserts at the beginning of each line its number of characters, sort them numerically and deletes the numbers.

function! SortLines() range
    execute a:firstline . "," . a:lastline . 's/^\(.*\)$/\=strdisplaywidth( submatch(0) ) . " " . submatch(0)/'
    execute a:firstline . "," . a:lastline . 'sort n'
    execute a:firstline . "," . a:lastline . 's/^\d\+\s//'
endfunction

Call it with a range of numbers, like

:4,18call SortLines()

or in Visual mode using V, like:

:'<,'>call SortLines()

EDIT: Ops, now I realised that this solution is very similar to yours. It was fine, only that % means the complete buffer instead :4,18 or :'<,:'> that selects specific lines.

Birei
  • 35,723
  • 2
  • 77
  • 82
  • 4
    Attention! `strlen()` counts the number of bytes used to represent the character, so you may get unexpected results for non-ASCII characters. Better use `strchars()` for the number of characters, or `strdisplaywidth()` if you want to sort by the displayed length (which can differ when and double-width characters are involved). – Ingo Karkat Jul 18 '12 at 06:49
  • @IngoKarkat: Thank you very much, +1. I modified the answer to use `strdisplaywidth()`. I liked it more but the OP should read your comment to choose his best. – Birei Jul 18 '12 at 09:04
  • @Birei I'm giving the answer to you because although the solution uses more lines of code, you have put it into a function, making it easier for me to use in the future, whereas I won't be able to remember (nor want to type) CodeGnome's solution, and I'm not familiar enough with `vim` functions to make his answer into a function. – Cory Klein Jul 18 '12 at 14:39
  • This answer purely relies on Vim and so works cross platform – icc97 Sep 05 '17 at 07:00
  • @Birei Will it also work if I put the function in to ~\.gvimrc file? – anishjp Nov 04 '21 at 14:31
8

I've written the AdvancedSorters plugin to deal with these complex sorting requirements.

Like in @Birei's answer, this plugin offers extension commands that evaluate an expression per line, put that number in front of the line, do a numerical sort, and then remove the temporary number again. Specializations handle the common sort by number of characters and by the line's display width, so you could just use:

:SortByWidth
Ingo Karkat
  • 167,457
  • 16
  • 250
  • 324
  • That's nice! Could you please update your repository on github: https://github.com/vim-scripts/AdvancedSorters so I can install it with vim-plug? – TornaxO7 Apr 18 '21 at 00:17
  • @TornaxO7: That is not my repository, it's a mirror of all vim.org plugins that has been discontinued. Through the [vim.org plugin page](http://www.vim.org/scripts/script.php?script_id=4958) you can reach my own [GitHub repository](https://github.com/inkarkat/vim-AdvancedSorters); this contains the latest published 1.30 version. – Ingo Karkat Apr 19 '21 at 10:46
  • @IngoKarkat: Does this plugin works with gVim 8+. I copied the AdvancedSorters.vim file to my $HOME/vimfiles/plugin/ and it gave me a lot of errors when I restarted gVim. – anishjp Jun 28 '22 at 09:30
  • @anishjp You also need to install my [ingo-library plugin](http://www.vim.org/scripts/script.php?script_id=4433); also available on [GitHub](https://github.com/inkarkat/vim-ingo-library). It's listed as a DEPENDENCY in the plugin's INSTALLATION section. – Ingo Karkat Jun 28 '22 at 17:22