6

Many sections of stuff we right is such that it could be automatically formatted by user defined rules. Here are a couple of examples:

  • List of include files in languages such as C. It is customary to sort these alphabetically, and this sorting is also part of style guides such as those of Google.

  • Ditto for usepackage in LaTeX

  • Comments in languages such as BASH

  • Tables, which could be processed by column -t

    • The entire file, from which I would like to automatically remove spaces at eoln, and repeated empty lines.

Doing these in vi is not tough. Just do something like !}sort -u. What's annoying is the need to do this repeatedly. Ideally, I would write:

// AUTO: `!}sort -u | column -t`
#include "z.h"
#include "a.h"
#include "b.h"

and each time I save my file, these specially marked commands will execute. Is this possible to do? Are there any plugins that do something similar?

Inspiration may be drawn from this question: Apply formating with a script, or have ftplugin format particular text based on syntax

EDIT: Here is another concrete example

# To sort: +2,/)/-1!column -t | sort
JUNKFILES = $(wildcard \
*~             \
*.aux          \
*.out          \
*.bbl          \
*.bcf          \
*.blg          \
*blx.bib       \
[dD]elme*      \
DELME*         \
*.dvi          \
*.fdb_latexmk  \
*.fls          \
*.listing      \
*.log          \
$(MAIN).pdf    \
*.o            \
*.out          \
*.run.xml      \
*.synctex.gz   \
)
  • 1
    Note that the include order may matter. For example, in LaTeX, hyperref should be loaded before cleveref if both are used. – muru Feb 1 '16 at 8:09
4

You want to automatically run a command when you write your buffer, and the n save your buffer after the command.. This means you're looking for an autocmd that runs during a BufWrite.

Place something like this into your .vimrc:

autocmd BufWrite *.c call Libsort()
function Libsort()
    normal mfgg}
    let lineNumber = line('.') - 1
    execute '1,' . lineNumber . '!sort -u'
    normal 'f
endfunction

That should do something similar to what you want. When you write your current buffer, if it is a .c file, it marks your current position, goes to the start of the document and sorts the first paragraph, and then returns to the mark. I wrote that function quite quickly, so it's not error checked or anything but should give you an idea for a starting point.

| improve this answer | |
  • Nice idea! Take a look at the other example I added. It would be a nuisance to update my scripts whenever I edit the text in files I use: C files, Makefiles, LaTeX, etc. I am in search for something a bit different. A general script and a keybinding, such that whenever I am in a line with a vim command, it would run this command. – Yossi Gil Feb 1 '16 at 10:30
  • You should learn how to make these macros and functions yourself, then you can just create one on the fly whenever you need it. – fruglemonkey Feb 1 '16 at 10:50
  • What do you mean by "whenever I am in a line with a vim command, it would run this command. "? – fruglemonkey Feb 1 '16 at 10:50
  • 1
    Suppose your cursor is on a line x, then, hitting (say) zp would search for a vi command on that line, and execute it. This command could be marked by a designated prefix, e.g., VIM: !} sort -u. – Yossi Gil Feb 1 '16 at 12:19
  • 1
2

I had similar but different requirements for my #include sorting. While this is tailored to C-like languages different functions with different patterns could be defined for other languages (or the pattern could simply be passed as a parameter).

To hook up the automatic formatting right before saving:

autocmd BufWritePre   *.{cpp,h,c,inl,cg,cgfx,fx}     call SortIncludes()

Then the meat. This sorts blocks of #includes, often I arrange them in sets based on whether they're project local, standard library, system, different dependent libraries... It also returns the cursor and window position to what it was before saving. (This also handles the case where some #includes are part of a set but commented out or indented.)

function! SortIncludes()
    let l:winview = winsaveview()
    call cursor(1, 1)

    let l:done = 0

    " -- find the next line that starts with #include
    while !l:done && search('^\(\s\|//\)*#include "', 'W')
        let l:startline=line('.')
        " -- and the next line that doesn't...
        let l:endline=search('^\(\(\s\|//\)*\)\@>\(#include\)\@!')
        if l:endline < l:startline
            let l:endline = line('$')
            let l:done = 1
        endif
        let l:lines=getline(l:startline, l:endline-1)
        let l:origlines=copy(l:lines)
        call sort(l:lines, 1)
        if l:origlines != l:lines
            call setline(l:startline, l:lines)
        endif
        call cursor(endline, 1)
    endwhile

    call winrestview(l:winview)
endfunction
| improve this answer | |
  • Oh it's worth noting that this actually skips #include <...> lines since those are often (in my experience) order dependent. So mostly this is for project includes, still arranged in sets if necessary that are separated by blank lines or comments or whatever. – dash-tom-bang Feb 24 '16 at 23:02
  • IMHO, include files should NOT be order dependent. If they are, then you should make this explicit by having one .H file include each other. Alphabetical sorting of include files is also part of style guides, e.g., Google's – Yossi Gil Feb 25 '16 at 11:01
  • 1
    That's a nice opinion to have and it's my preference too but I have better things to do than make Adobe, Microsoft, and other corporations bow to my preferences. – dash-tom-bang Feb 25 '16 at 17:27
  • good point @dash-tom-bang – Yossi Gil Feb 25 '16 at 21:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.