Create Your Own Vim Commands

If you have used Vim for any length of time you have run across Ex commands. Hell, you use an Ex command to exit Vim; :quit, or just :q. There are Ex commands available to do pretty much anything that can be done in Vim.

You can make your own Ex commands as well. Many Vim plugins add their own Ex commands as part of the plugin. You might think it is hard to create a new Vim command, but it isn't. I'll show you the basics here, and where to look for more information.

Open a Reference File

I find it much easier to learn things if there is something I want to build, so this is the command that will be built.

There is a particular code editing situation that I find myself in very often. I'm working on a bit of code, say an HTML page, and I need to add a class name that is defined in one of the project's CSS files. I can't remember the class name though. I want to open the CSS file in a split window so I can see it, but I don't need to edit it.

This is easy enough to do in Vim, simply :vsplit [path_to_css_file] and the CSS file is available in a split window.

Ahh, but there is a problem. Using :vsplit opens the CSS file in a new Vim window, makes it the active window and moves the HTML file I'm editing to the right. What I really want is to open the CSS file in a window on the right and continue editing the HTML file.

I just need the CSS file for reference.

I'll actually create two commands in this post. One to open a reference file in a vertical window to the right and leave the current window active, and a horizontal version of the same command that opens the reference file in the bottom window.

Along the way I will describe the Vim :command command and its options so you can create your own Vim commands if you need to.

The :command Command

That's right, the Vim command to create user defined commands is :command. You can use :command on its own in Vim to get a list of user defined commands. It is handy if you ever forget a command name you created or one you loaded from a plugin. When creating a user command :command takes the following form:

:command [args] Name actions

The new command you create has to have a name, of course, otherwise how could you call it. The name must start with a capital letter. This is a requirement of Vim and is used to keep user defined commands separate from internal Vim commands. There is one exception though, it can not be named :Next. Apparently Vim broke its own rule.

Once the command is working you can use it in Vim by entering :Name, but, you know, replacing Name with the actual command name.

I'll name these commands :Ref and :Vref to stick to the Vim convention of :split for a horizontal window and :vsplit for a vertical window. So the commands so far are:

:command Ref
:command Vref

Optional Args

The :command command takes zero or more optional arguments. The most important of these is the -nargs= argument. This lets Vim know if the command takes any arguments. The possible values it can be set to are:

  • 0 : The command takes no arguments. This is the default, so it can be omitted entirely.
  • [1-9]: The command takes the specified number of arguments.
  • *: The command takes zero or more arguments.
  • +: The command takes one or more arguments.
  • ?: The command take zero or one argument.

The commands I want to build will take a single argument, the path to the reference file to open, so I'll be using -nargs=1.

You can access the arguments in a number of ways. First, just using <args> somewhere in the actions part of the command. Second, if you need to make sure the arguments are properly escaped for use in an expression you can use <q-args. Finally, if you are going to pass the arguments to a Vim function you can use <f-args>.

Another argument that will be used in these commands will be -complete=. This argument lets Vim know what kind of tab completion is available for the command. There are over thirty different values that this can be set to. Far too many to cover here, but some of the values are buffer to complete buffer names, dir to complete directory names, help to complete help topic name and file to complete file and directory names.

The last argument that will be needed is the -bar arguments. This tells :command that the actions can contain a vertical bar | followed by additional Ex commands, or a double quote " followed by comments. I'll need two different Ex commands in these new commands, so they will need to be separated with a vertical bar.

Some of the other arguments you can supply to :command are:

  • -range allows the command can accept a range of lines in the current buffer to operate on.
  • -count allows you to supply a repeat count for the command.
  • -register allows you to specify a register other than the unnamed register.

For more on the available :command arguments look at :h user-commands.

With the arguments defined for the new commands we end up with:

:command -nargs=1 -complete=file -bar Ref
:command -nargs=1 -complete-file -bar Vref

All that is left is to create the actions to take when the commands are executed.

Command Action

The Vim documentation actually calls this the replacement, because it replaces your command name with the command or commands you specify here. I find actions to be a bit more intuitive. In any case, this is what you want to have happen when you enter your command into Vim.

The user command documentation doesn't say anything else about the actions, I suppose because all of the other documentation is applicable. Any Ex command can be used here to achieve the desired effect.

For my case I want to open a new window to the right or bottom with the file specified in the command, but leave the current window active. The built in :split and :vsplit Ex commands don't have the behavior I want.

I looked at the Vim documentation for :split, thinking that perhaps there was an argument that would give me the behavior I wanted. It does take a couple of arguments, but they are related to the file format, and cursor positioning when the file opens. Not really what I was looking for.

A little further down in the documentation for :split I found the splitbelow and splitright settings. If you :set splitbelow then new windows created with :split are created below the active window instead of above. The same goes for :set splitright; new windows will be created to the right.

That looked like exactly what I wanted. I can set those values in my .vimrc right before I define the commands and everything will work like I want.

While that would work I wasn't in love with the solution. That will change the default for every split window. I don't like changing the default settings if I don't have to. A little more digging the the Vim documentation near the :split docs and I found the exact right thing.

There are command modifiers for the split window commands called :rightbelow, :leftabove, :topleft and :botright. These are Ex commands, but are called modifiers because they only take another Ex command as an argument and override the splitbelow and splitright settings.

The :rightbelow modifier just executes whatever command you supply as an argument. If the supplied argument opens a split window it will open it to the right, or below, depending on the argument. So, now the commands are:

:command -nargs=1 -complete=file -bar Ref :rightbelow :split <args>
:command -nargs=1 -complete=file -bar Vref :rightbelow :vsplit <args>

The commands are almost complete. The only issue left to deal with is that the new window that is created is still made active. That is simple enough to fix, I can just add a window movement command to make the previous window active again.

My initial thought was to use the Vim window movement commands; <C-w>k to move to the upper window and <C-w>h to move to the left window. Vim didn't parse that correctly though. Apparently all of the commands have to be Ex commands.

I had to dig through Vim help topics for a bit but I found what I needed. All of the Vim window commands can be activated through the :wincmd Ex command. So, whenever you type <C-w>x you could also use :wincmd x.

The final commands, then, are as follows.

:command -nargs=1 -complete=file -bar Ref :rightbelow :split <args> | :wincmd k
:command -nargs=1 -complete=file -bar Vref :rightbelow :vsplit <args> | :wincmd l

There you have it; two brand new Vim commands. They take one argument, the path to a file, which can be <Tab> completed. They open the file in a new split window, either to the right or below the currently active window and then move to the previously active window.

I hope you enjoyed this little trip through Vim commands. There is so much flexibility available in Vim. With a little bit of effort and digging through the help files you can build your own Vim Commands to do pretty much anything you need.