1

I'm trying to make a custom Vim command to compile code without having to do :!gcc -Wall %:p -o %:p:r every time. So far, I've been using this:

:command Com w<CR>! gcc -Wall %:p -o %:p:r

But the problem with that is that if I want to compile multiple files, I have to go back to :!. I was thinking of something like this:

:command -nargs=+ Com w | ! gcc -Wall <args> -o compiled
:command -nargs=0 Com w | ! gcc -Wall %:p -o %:p:r

However, that doesn't work, because it throws an error for trying to define the command twice. It does show what I want to do, though, I think.

How can I get different behavior for different numbers of arguments?


I'm new here. Please don't be too angry if I misread or misunderstood Don't Ask and On Topic, or left out some information that's necessary.

8
  • Try to make a single command, that covers both cases.
    – VanLaser
    Nov 17, 2015 at 22:19
  • @VanLaser I have, and failed. I currently have it as two commands, ComC and ComM, for "compile current" and "compile many". Still, it's an interesting question, even if the answer is just "It can't be done".
    – anon
    Nov 17, 2015 at 22:20
  • 1
    How about making a Makefile (that has as targets either the whole binary, or just a single object - so it would solve both of your problems)? You will then just have to run :make from Vim AND you get any errors in the quickfix window.
    – VanLaser
    Nov 17, 2015 at 22:39
  • @VanLaser Hm, that could work. The problem is that then, even for my tiny little test things, I have to make a makefile.
    – anon
    Nov 17, 2015 at 22:45
  • Yes - perhaps have a Makefile template or snippet skeleton ready. There is also this, but only for a single file ...
    – VanLaser
    Nov 17, 2015 at 22:47

1 Answer 1

2

I know nothing about compiling but if I understand you correctly, you want a vim command that can be used without argument to execute a shell command.
And if the same vim command is used with arguments, it should execute a different shell command on them.

I've come up with a template that maybe you could adapt to your specific needs and that seems to do what you want :

function! CompileFiles(...)

    if a:0 == 0
        !echo "Compile without argument"
    else
        execute "!cat " . join(a:000)
    endif

endfunction

command! -nargs=* Com call CompileFiles(<f-args>)

In this template, the Com command is defined at the end and calls the CompileFiles() function that is defined just above.

The command passes the arguments it receives to the function.
Inside the latter you can test the number of arguments with a:0 and access the list of arguments with a:000.

The function tests the number of arguments it has received :

  • if it's 0 it executes the shell command echo "Compile without argument"
  • if it's more, all the arguments are concatenated (with the built-in join() function) into a string which in turn is concatenated at the end of "!cat ".
    The content of the resulting string is then executed.
    For example, if you type :Com foo bar, the function should execute !cat foo bar.

What you want is different.
You want to write the current file (w) and then (|) execute the shell (!) command gcc -Wall %:p -o %:p:r if no argument follows the command, or execute gcc -Wall <args> -o compiled if there are arguments.

I think the previous code could be modified like this :

function! CompileFiles(...)

    if a:0 == 0
        write
        !gcc -Wall %:p -o %:p:r
    else
        write
        execute "!gcc -Wall " . join(a:000) . " -o compiled"
    endif

endfunction

command! -nargs=* Com call CompileFiles(<f-args>)

Not sure it will work though.

One last thing, before passing some arguments to the shell it's often recommended to escape them. There's a built-in function that does it : shellescape()

I know how to use it on a specific argument :

shellescape(a:foo) will escape the argument whose name is foo,
shellescape(a:1) will escape the first argument of the optional group (the one that is symbolized by ... in the previous code), etc.

but I don't know how to escape a list of arguments whose size is unknown.

Edit : I've tried to modify the code a little bit to escape the arguments in case they contain special characters :

function! CompileFiles(...)

    let mylist=[]
    let c = 0
    while c < a:0
        let myarg = shellescape(a:000[c])
        call add(mylist, myarg)
        let c += 1
    endwhile

    if a:0 == 0
        write
        !gcc -Wall %:p -o %:p:r
    else
        write
        execute "!gcc -Wall " . join(mylist) . " -o compiled"
    endif

endfunction

command! -nargs=* Com call CompileFiles(<f-args>)

The only thing that changes is the creation of a mylist variable and a while loop that executes the shellescape() function on each argument received by the main function.

I didn't test it as I don't have anything to compile.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.