public class CLASS { CURSOR }
The C template looks like this:
#include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { CURSOR return 0; }
The HTML template looks like this:
<!DOCTYPE html> <html> <head> <title>...</title> </head> <body> CURSOR </body> </html>
A student has asked how I do this. I’m sure I picked up the knowledge from somewhere else, but the provenance data has been lost. So, I’ll share my configuration here and pretend like I was its sole author.
First, I put all my template files in some known location. I have a universal configuration repository that I clone on all the machines I use regularly. My templates sit in a directory within this clone named vim/templates. The template files are named according to the extension I use for that file type. For example, the Java template file is named java, the C++ template file is named cpp, and so on.
Second, I added the following autocommand to my .vimrc file, which I explain in more detail below:
autocmd BufNewFile *
\ let templatefile = expand("/PATH/TO/UNIVERSAL/CONFIGURATION/configs/vim/templates/") . expand("%:e")|
\ if filereadable(templatefile)|
\ execute "silent! 0r " . templatefile|
\ execute "normal Gdd/CURSOR\<CR>dw"|
\ endif|
\ startinsert!
Let’s break this down a bit:
BufNewFile) for all files (*).%:e grabs the extension of the new file.filereadable checks to see if a template file with this extension exists in the known location.r command to read in the template’s contents. The 0r ensures that the contents are placed after line 0—in other words, at the start of the file.Gdd removes the extra blank that appears at the bottom of the file.CURSOR placeholder in each template. /CURSOR\<CR>dw jumps to the CURSOR placeholder and deletes it. The cursor stays there.startinsert command puts us in insert mode. The ! makes this command behave like A, so we start appending text to the line that once held the placeholder.Adding new templates requires only that I put a new template file in the templates directory.
]]>Suppose you’re shelling around:
$ ls
utilities.c main.c
$ vi makefile
In the makefile, you go to type the names of the sources files upon which your executable depends. To minimize spelling errors and time spent typing, starting typing just the first few letters of the filename and then hit <Control-x><Control-f>:
main: u<Control-x><Control-f>
This completes the filename under the cursor:
main: utilities.c
If multiple files match the prefix that’s been typed, cycle through them with <Control-f>.
You’re typing in a document and a need to do some math. While in Insert mode, hit <Control-r>=. You’ll be prompted to enter a Vimscript expression. Simple arithmetic is supported. For example:
The number of pixels is <Control-r>=1024 * 768<CR>.
yields
The number of pixels is 786432.
Suppose you’ve got a literal in an expression that you’d like to pull out into a variable:
double nsamples = nseconds * 22050;
22050 would be better named SAMPLE_RATE. In very few keystrokes, we can rip out the 22050, replace it with a new name, and insert a declaration on the preceding line. Our method will make use of the following truths:
cw deletes the word under the cursor and goes into Insert mode so you may type it’s replacement. Think of it as “change word.”" register.O in normal mode opens a new line above the current one.Control-A in insert mode enters the text entered during the last insert mode.Put your cursor at the beginning of 22050. Then enter cwSAMPLE_RATE<Escape>Oint <Control-a> = <Control-r>";. When the dust settles, you’ve got:
int SAMPLE_RATE = 22050;
int nsamples = nseconds * SAMPLE_RATE;
Eclipse offers a shortcut for generating System.out.println. We simply type sysout and then hit <Control-Space> to expand.
We can do something similar in Vim using abbreviations. In Normal mode, enter :iabbr sysout System.out.println. Now, if we type sysout followed by some non-identifier character like (, Vim will correctly expand our abbreviation.
Whether it’s because you’ve got off-by-one errors or because you’re copying and pasting array element processing code, we often need to increment or decrement integer literals. This can be done quickly in Normal mode by placing the cursor anywhere on or before the number to be altered and hitting <Control-a> or <Control-x>. For example, suppose the cursor is on the first line of the following:
states[1] = "Virginia";
states[2] = "Wyoming";
If you hit <Control-x>j<Control-x>, you’ll end up with:
states[0] = "Virginia";
states[1] = "Wyoming";
Suppose you’ve got this text:
f(param0);
..
f(param0);
..
f(param0);
f(param0);
Suddenly, the signature for f changes. It gets a new parameter, and at each call site, you’d like to add param1. You could do a find/replace, but sometimes that’s too much overhead. Instead, we can add the text at one call site, and repeat at our action at the remaining with .. With the cursor on the first 0, we enter a, param1/0.n.n. and end up with:
f(param0, param1);
..
f(param0, param1);
..
f(param0, param1);
f(param0, param1);
Maybe you’re editing a makefile. Maybe you’re editing a Java source file. It doesn’t matter. At one time or another, you want to open the file associated with the word under the cursor. The word might be a filename, or it might be the name of a class. Hit <Control-w><Control-f> and it opens.
“If you can keep your fingers on the home row,” people will tell you, “then you can type much faster.” I don’t find typing speeds an issue, because usually I have to think as much as I type—and thinking is the real bottleneck. However, I don’t want to strain my body unnecessarily. Let’s keep those fingers on the home row.
The cursor keys are far from the home row. Good news. In normal mode, h is the same as <Left>, l is <Right>, j is <Down>, and k is <Up>. However, old habits die hard. You can wean yourself off of the cursor keys by mapping them to do nothing:
nmap <Left> <Nop>
nmap <Right> <Nop>
nmap <Down> <Nop>
nmap <Up> <Nop>
You cursor is sitting at the beginning of the line. It needs to be at the hyphen between those two words really far away. How do you get there? The mouse? Hardly. Use the f command. In particular, f- will advance to the first comma after the cursor. 2f- to the second. 3f- to the third. And so on. But you don’t have to count if you don’t want to. Just hit f- and then ; to jump to the next match. If you overshoot, hit , to jump to the previous match.
If the cursor appears after the target, use F- instead.
f and F will take you right to the match. If you’d like to stop short at the character right next to the target, use t and T. For example, maybe you’re in the open and close of an XML/HTML element and you want to land on the first character after the opening tag. T> will do the trick.
The commands c, y, and d are used to change, yank, and delete text, respectively. You follow each of these commands with a motion to indicate what text is affected. For example, cw changes a word, dd deletes a line, and y2w yanks two words.
These commands can also be followed by [ fFtT]. To delete from the cursor until the semi-color, hit dt;. To yank through the next closing parenthesis, hit yf). A count can be provided to target the nth character from the cursor.
Vim also supports quick processing of delimited text. To delete everything between parentheses, hit di(. This also works for [], {}, “”, ”. Suppose your delimiters are not single characters, but tags. Use dit. Suppose you want to delete not just the contents of the delimiters, but the delimiters too. Use da(.
Once I needed to create a 50-element array of State objects, each holding its states capital and name. I found a list in this format online:
Madison, Wisconsin
Des Moines, Iowa
Montpelier, Vermont
...
To convert each line into the construction of a State, I first transformed just the first line—but I recorded my keystrokes into register a as I did so using the record macro command q. I entered qaInew State("<Escape>f,i"<Escape>Wi"<Escape>A"),<Escape>j. Then I hit @a to play back the macro I stored in register a on the next line. After seeing that it worked, I hit 48@@ to play back the last register 48 more times.
I ended up with:
new State("Des Moines", "Iowa"),
new State("Madison", "Wisconsin"),
new State("Montpelier", "Vermont"),
...
All that remained was to surround all the states in {} and assign the result to a State[].
Regular expressions would also have worked.
Often I find myself needing to edit text that is vertically aligned. Maybe several lines in a row are assigning values to some array elements, and I need to alter the index expression in some uniform way. Maybe I’ve copied some numbers that are formatted in columns, and I need to separate each column with a comma. I could use a macro, or I could use a regular expression and substitution. But there’s also Visual Block mode.
Say I’ve got the numbers problem. I need to turn these numbers into a CSV file:
0.048909 0.524034 0.394847 0.317906
0.693668 0.608016 0.962821 0.887913
0.224440 0.929916 0.536071 0.421397
0.422353 0.911034 0.282418 0.496423
(If the file contained no the other spaces, :%s/ /,/g would do the trick. Let’s assume the file does contain other spaces.)
With the cursor at the beginning, I hit f<Space> to go to the space separating the first two columns, <Control-v> to go into Visual Block mode, 3j to select the next three lines, s to substitute the selected text and go into Insert mode, , to replace the text on the first line, and <Escape> to apply the substitution to the remaining lines. Once satisfied with my edit, I hit ;. to repeat on the next column and then ;. once more.
Because I care, I am constantly renaming things. Next to typing in Insert mode, finding and replacing is probably my most frequent action I take in Vim. At its simplest, I just type:
:%s/oldname/newname/g
I can shorten my work a little bit by placing the cursor on a word I want to replace and hitting # or *. Then, I can leave the search pattern blank:
:%s//newname/g
I can confine the substitution to just the visually selected lines just by being in Visual mode. The range of operation is automatically set to '<,'> when I press ::
:'<,'>s/oldname/newname/g
Usually I qualify the substitution to confirm each replacement, at least until I’m reasonably sure my pattern doesn’t lead to false positives and the substitution is correct:
:%s/oldname/newname/gc
You are coding and want comment out some lines. We can do this in many text editors by selecting some lines and hitting <Control-/>. We can make Vim do this too.
First, let’s do this without a special keybinding. Select some lines by going into visual line mode with V. Move the cursor to expand your selection. Prefix all the selected lines with // by entering :'<,'>normal i//. Remove the comment characters with :'<,'>normal xx. These two Ex commands apply a normal mode command to each line in the visually selected range.
We don’t want to type those Ex commands all the time. We can map <Space>/ to prepend the comment character with vmap <Space>/ :normal i//<CR>. The logic to toggle comments can be wrapped up inside a function to make this solution better.
Suppose you need to change a function name that’s used in many files. What a pain, right? No, substituting in a new name across multiple files is only mildly more difficult than substituting it in one. The argdo command lets us apply a command to each file that we sent to Vim as a command-line argument. For example:
:argdo %s/oldfunc/newfunc/
This command essentially iterates through the argument list and applies the subsequent command. You or your settings may require that the current file be written before advancing to the next. Simply append a second write command:
:argdo %s/oldfunc/newfunc/ | w
Imagine you’re editing and need to substitute some text. To match the text that needs updating, you must include some context that you don’t plan on changing. For example, maybe you’ve got an HTML file and you want to replace all the anchor tag links with #, because you’re starting to do things with Javascript now. The file has other links embedded in the text, so you need to be careful that you don’t oversubstitute.
One approach is to capture the surrounding context and explicitly reinsert the unaltered context back in with :%s/\(<a href="\)[^"]\+\(>\)/\1#\2/g. Zero-width assertions, however, would let you match the context without trying to substitute over it: :%s/\(<a href="\)\@<=[^"]\+\(>\)\@=/#/g. Who thinks this is ugly? I do.
The \zs and \ze commands achieve the same effect much more simply by allowing you to delimit which portion of the matched text gets substituted: :%s/<a href="\zs[^"]\+\ze">/#/g.
Many settings or keybindings depend on the kind of file one is editing. For instance, I like to map <Space>x to execute the script I’m editing without having to go out to the shell. I can do that laboriously with Ex command :!interpreter %, in which % expands to the path of the current file. Language-specific keybindings make it way better:
autocmd BufEnter *.py
\ map <Space>x :!python %<CR>
autocmd BufEnter *.rb
\ map <Space>x :!ruby %<CR>
autocmd BufEnter *.hs
\ map <Space>x :!runhaskell %<CR>
Our edits to a file are often spatially coherent. If we change a line in a file, save it, and reopen it, we’re likely to make more edits near our last edit. On reopening, however, we find that Vim’s cursor is sitting right at the beginning of the file. It’d be really, really nice if our cursor was automatically placed at our last edit.
It turns out that Vim automatically stores the last cursor position as a mark. We can jump to it manually with `" or automatically using an autocommand:
autocmd BufReadPost *
\ if line("'\"") > 0 && line("'\"") <= line("$") |
\ execute "normal! g`\"" |
\ endif
Say you’ve got a file a.txt. However, this a.txt is on both your hard drive and a USB drive. The modification times are inconclusive. You fear that each may have changes the other does not. How can you compare and possibly merge them? Just use Vimdiff:
vimdiff path/to/one/a.txt path/to/other/a.txt
The two panes will show the two files with differences highlighted and similarities collapsed. Scrolling in one will scroll in the other. [c and ]c will navigate between differences.
It works on the premise that for each type of file you have, you register what the comment characters are for that type of file. The catch-all is the pound sign:
autocmd User,BufEnter *
\ let b:commentStart='#'|
\ let b:commentEnd=''
For HTML and XML, we’ve got:
autocmd User,BufEnter *.htm,*.html,*.xml,*.xsl,*.xsd
\ let b:commentStart='<!--'|
\ let b:commentEnd='-->'
Other languages are defined similarly. Only a few, like C, Haskell, and HTML require b:commentEnd to be a non-empty string.
Next I bind <Space>c to comment and -c to uncomment in both normal and visual mode. Hitting 5<Space>c in normal mode comments out the next five lines from the cursor. Or, I can hit V, select my five lines, and hit <Space>c.
These mappings lead to a couple of Vimscript functions, which do the real work.
map <silent> <Space>c :<C-U>call CommentNLines(1, v:count1)<CR>
map <silent> -c :<C-u>call CommentNLines(0, v:count1)<CR>
" Comments or uncomments the specified number of lines, starting from the line
" on which the cursor currently sits.
function! CommentNLines(wantsComment, nLines)
" There's a companion function that will comment or uncomment a specified
" range of lines, so our work here is just to determine the range and then
" call that helper function.
" We start at the cursor...
let startAtLine = line(".")
" And go nLines further, but going no farther than the end of the file.
let endAtLine = startAtLine + a:nLines - 1
if endAtLine > line("$")
let endAtLine = line("$")
endif
call CommentLines(a:wantsComment, startAtLine, endAtLine)
endfunction
" --------------------------------------------------------------------------- "
vmap <silent> <Space>c :<C-U>call CommentSelectedLines(1)<CR>gv
vmap <silent> -c :<C-u>call CommentSelectedLines(0)<CR>gv
" Comments or uncomments the visually selected lines.
function! CommentSelectedLines(wantsComment)
" There's a companion function that will comment or uncomment a specified
" range of lines, so our work here is just to determine the range. The marks
" '< and '> tell us the bounds of the visually selected range.
silent call CommentLines(a:wantsComment, line("'<"), line("'>"))
endfunction
" --------------------------------------------------------------------------- "
function! CommentLines(wantsComment, startAtLine, endAtLine)
" Assert that comment characters have been registered for this buffer.
if !exists("b:commentStart") || !exists("b:commentEnd")
echo "No comment character registered for this filetype."
return
endif
" We only want to allow a line to be commented once. This is particularly
" true in C, where /* */ comments cannot legally nest. We also don't want to
" try to uncomment a line that hasn't been commented. Let's find a pattern
" that we can use later to ask a line its commented status.
let commentedPattern = '^\(\s*\)\M' . b:commentStart . '\m \(.\{-}\) \M' . b:commentEnd . '\m\s*\(\\\)\?$'
" There's one slightly funny thing in this pattern and the ones below. If I
" have C preprocessor macro split across multiple lines, like this
" #define SQUARE(a) \
" a \
" * \
" a
" I can't legally end the \-suffixed lines with the */ comment character. The
" comment must be part of the macro, so the end comment character must appear
" before the closing slash:
" #define SQUARE(a) \
" /* TODO: parenthesize a someday */\
" a \
" * \
" a
" The patterns take into account closing backslashes and do not try to wrap
" them inside the comment. Probably I should only handle this in C and C++,
" but it's not manifested itself as an issue in other languages.
" Comment and uncomment are handled with the same algorithm; the only
" thing that changes is the pattern and substitution that we perform.
if a:wantsComment
" For a comment, we place the comment character after all leading
" whitespace. I like my comments indented coherently, not up against the
" left margin. \1 holds the leading space, and \2 everything else on
" the line.
let pattern = '^\(\s*\)\(.\{-}\)\(\\\)\?$'
let substitution = '\1' . b:commentStart . ' \2 ' . b:commentEnd . '\3'
else
" For decommenting, we can reuse the comment pattern already defined. The
" leading space is captured in \1, and the text between the comment start
" and comment end is in \2.
let pattern = commentedPattern
let substitution = '\1\2\3'
endif
" Now, we're ready to visit each line and either comment or uncomment it.
let i = a:startAtLine
while i <= a:endAtLine
let line = getline(i)
" Is the line already a comment or a non-comment?
let isAlreadyComment = line =~ commentedPattern
" Don't operate on blank lines and make sure that the line isn't
" already what the caller wants it to be. That is, we don't comment
" a line twice, nor do we uncomment it twice.
if line !~ '^\s*$' && isAlreadyComment != a:wantsComment
call setline(i, substitute(line, pattern, substitution, ''))
endif
let i = i + 1
endwhile
endfunction
Comments with a wriggle of the nose!
]]>As I return to Mutt, I also miss a few things from Thunderbird. The most prominent of these is autocompletion of my recipients. Mutt will tab-complete aliases that you’ve previously registered in its configuration files, but Thunderbird one-ups Mutt by automatically storing the addresses of people you send messages to. I wanted to make Mutt do that too.
I looked around for hooks, but I didn’t see a way for Mutt to automatically record aliases when I sent a message. However, since I was using Vim to edit my messages, I saw that I could create an autocommand that would strip out the recipients’ addresses from my message and append them to my alias file. So, I first wrote a function to do the heavy-lifting:
" This function, which is to be run only on a Mutt email message,
" finds all the addresses in the To, Cc, and Bcc headers. If
" no aliases exist for these addresses, they are added to the
" alias file.
function! AddMuttAliases()
" This is where my alias file lives. Change this to something
" that suits you.
let aliasfile = $UU . '/configs/mutt/aliases'
" Why isn't there a match + matchstr? match only gives me the
" start, which is not enough to extract the substring.
" matchstr doesn't give me the location, which is not enough
" information to advance forward.
" Find all email addresses. My pattern may not be the best yet.
let addresses = []
silent vimgrep /^\(To\|Cc\|Bcc\):/j %
for line in getqflist()
let pattern = '[-A-z._]\+@[-A-z._]\+'
let start = match(line.text, pattern)
let address = matchstr(line.text, pattern, start)
while address != ""
call add(addresses, address)
let start += len(address)
let address = matchstr(line.text, pattern, start)
endwhile
endfor
" Check to see if each is in aliases file. If not, add it.
" Mutt's alias registration command has this form:
"
" alias name address
"
" Usually the name is something short and mnemonic. I prefer
" to continually work with people's addresses so that I'm not
" too crippled when I lose my configuration. So, my aliases
" have this form:
"
" alias address address
"
" Isn't this overly verbose? Yes. What have I gained? Well,
" mutt will tab-complete the addresses for me. Verbosity
" doesn't matter. And that's sweet.
for address in addresses
execute 'silent! vimgrep /' . address . '/j ' . aliasfile
if len(getqflist()) == 0
let aliasline = 'alias ' . address . ' ' . address
silent! execute '!echo ' . aliasline . ' >> ' . aliasfile
endif
endfor
endfunction
To call this function when Mutt messages are written, I registered the following autocommand in my .vimrc file:
autocmd BufWritePost ~/.tmp/mutt-* call AddMuttAliases()
This command assumes Mutt messages are temporarily stored in ~/.tmp, which I set in my .muttrc.
Now everytime I write a message to someone new, I get an alias added to my aliases file. The next time, I can simply tab-complete to enter the person’s address!
]]>" When editing a file, always jump to the last known cursor position.
" Don't do it when the position is invalid or when inside an event handler
" (happens when dropping a file on gvim).
autocmd BufReadPost *
\ if line("'\"") > 0 && line("'\"") <= line("$") |
\ execute "normal g'\"" |
\ endif
Autocommands let you write callbacks for certain events that occur in certain files. Here, we are defining some code that we want to execute after we open (BufReadPost) a file of any type (*). The code itself reads the last known cursor position, which Vim kindly stored for us in a special register named ” (that’s a double-quote). (Information like this is stored in ~/.viminfo, but this doesn’t concern us.) Then we
This autocommand was pulled directly from Vim’s help pages.
]]>In C, which has one global namespace, functions simply can’t be named generically. Add, insert, and get are out of the question. Usually, I add the name of the data structure to the function: vector_add, list_insert, image_get. However, sometimes I get the order swapped in my head, and type in insert_list or free_stack or some such.
It happens often enough that I wrote a Vim macro to turn free_stack into stack_free:
nmap <Space>_ dt_ea_<ESC>pbx
I hit <Space>_ with the cursor in the offending function identifier and the tokens get switched around. Let me break down this command into its steps: