How do I sort multiple blocks of text by the first line in each block in Vim?

11

3

I have multiple blocks of text, or in other words, multiple functions like this:

def ==(other)
  ...
end

def to_s(full=false)
  ...
end

def to_a
  ...
end

def to_hash
  ...
end

def inspect
  ...
end

I want to sort the functions alphabetically by the function signature. How can this most easily be done in Vim?

Hubro

Posted 2014-05-10T20:53:50.603

Reputation: 4 846

define what you mean by function signature please. What type of functions are these? Also, on what platform are you working under OS, distribution, and version number would be VERY helpful. – mdpc – 2014-05-11T00:03:31.957

1why does this have to be done in VIM? there are excellent builtin unix/linux utilities that can be easily cobbled together for these type of things....awk comes to mind. – mdpc – 2014-05-11T00:04:38.030

@mdpc: By the function signature I just mean the line containing def .... I'm on Linux, so any of those utilities will work for me. – Hubro – 2014-05-11T14:47:35.210

Answers

12

It's relatively simple (and, I believe, close to what Jason had in mind) :

  1. turn all your functions into one liners by replacing all newlines with some fancy character:

    :g/def/,/end/s/\n/§
    
  2. sort those one liners with:

    :%sort
    
  3. expand all your functions back to their initial individual state:

    :g/def/s/§/\r
    

romainl

Posted 2014-05-10T20:53:50.603

Reputation: 19 227

1Similarly, if the blank lines can be considered to be unique separators, then it could be done with :g/./,/^$/-s/\n/§/g, and then :%sort, and then s/§/\r/g – ArtBIT – 2019-01-11T15:50:59.897

3

I've written the AdvancedSorters plugin to simplify the three separate steps given in @romainl's answer into a single command:

:SortRangesByRange /^def\>/,/^end\>\_s*\zs$/

The pattern here is slightly more complex to also properly include the separating empty lines.

Ingo Karkat

Posted 2014-05-10T20:53:50.603

Reputation: 19 513

Note: You'll currently need a fairly new Vim 7.4.218 for that; I'll provide an update that also supports older Vim versions soon. – Ingo Karkat – 2014-06-11T09:41:47.150

2

The best way I can think of doing this without writing a function that parses the definitions, would be to substitute the line delimiters not preceded by end for another unique delimiter (+EOL+?), and then :sort, and re-substitute the line delimiters. It could probably be recorded to a macro.

A function might be better though, in the case that you want to use visual selection.

Jason

Posted 2014-05-10T20:53:50.603

Reputation: 1 010

2

Try the vissort plugin. It supports a block sorting facility:

  • :'< '>BS nextblock endblock findtag tagpat tagsub
  • :[range]call BlockSort(nextblock,endblock,findtag,tagpat,tagsub)

If any arguments are missing, BlockSort() will query you for them. The nextblock/endblock patterns delimit a block, the findtag pattern is used to find a line containing a "tag" which will be used for sorting; the tagpat and tabsub are used in a substitute to extract the sorting tag from the tag-containing line.

You may get vissort.vim from http://www.drchip.org/astronaut/vim/index.html#VISSORT

user21497

Posted 2014-05-10T20:53:50.603

Reputation: 216