You know how some people keep track of words they've looked up in the dictionary by highlighting them (most famously in Say Anything)? I was curious what things I was looking up in vim's help, in part because I know there are things I wanted to write about in this blog, and I just can't remember what they are. If only I could look at my history of
:h's! And then of course, because it's a fun exercise and maybe moderately useful, at the very least for writing a blog, I decided to write something that would do it. I'll present it first and then maybe make some sparse comments about it and the process of writing it:
let s:helps_file = $HOME . "/.vim/help-queries.txt" function! s:SaveHelpQueries() abort if getcmdtype() == ':' let line = getcmdline() if line =~ '^h ' || line =~ '^help ' let entry = join(split(line, ' ')[1:], ' ') call writefile([entry], s:helps_file, "a") endif endif endfunction function! s:OpenHelp() abort let pos = getcurpos() normal ^"ay$ call setpos('.', pos) exec "noautocmd h" @a endfunction augroup TrackHelp au! au CmdlineLeave * call s:SaveHelpQueries() exec "au BufEnter" s:helps_file "nnoremap <buffer> K :call <SID>OpenHelp()\<CR>" augroup END function! s:ShowHelpers(...) abort let type = a:0 == 0 ? "e" : a:1 let bang = a:0 == 2 ? a:2 : 0 if !bang exec type s:helps_file else if (type == 'vsp' && &splitbelow) || (type == 'sp' && &splitright) exec "abo" type s:helps_file else exec "bel" tpye s:helps_file endif endif if type != 'e' augroup AutocloseHelp au! exec "au BufLeave" s:helps_file "q | au! AutocloseHelp BufLeave" augroup END endif endfunction command! -nargs=0 Helps :call s:ShowHelpers() command! -nargs=0 -bang VHelps :call s:ShowHelpers('vsp', <bang>0) command! -nargs=0 -bang SHelps :call s:ShowHelpers('sp', <bang>0) command! -nargs=0 THelps :call s:ShowHelpers('tabe')
I wrote the
TrackHelp auto group and the
SaveHelpQueries functions first. I'll admit I had to Google (and ultimately read some questions on stack overflow) to remember
CmdlineLeave, which is what I needed to record the command line right before it's executed. In the function called from there, I first check that the type of command being executed is a
: command. Other types of commands trigger this auto event too, such as searching or running a macro. See
:h getcmdtype for a list. The rest is pretty straight forward: if it is a
: command, and the first part of that command is
help, I get the rest of the command and append it to the help file.
After finishing this I thought, "That's nice, but it would be useful if it was easy to view this collection too. Perhaps I should add a command to open the file?" Of course, it wouldn't be that hard to type something like
:vsp ~/.vim/help-queries.txt, especially with tab completion, but since this is a vimscript exercise already, I decided to add a
Helps command (as well as variants for opening the file in other views). This was, on the whole, pretty straight-forward: just a command that takes no args and calls a function with the view type ('vsp', 'sp', or 'tabe' . . . I decided to default to 'e', the universal default view) that opens the file in that view. Then I remembered the
-bang argument to
command, which I haven't really had a need to use before, and as long as I'm in an exercise, I might as well use some new things.
-bang to a command allows the user to add '!' when they execute the command (in this case
:SHelps!). Passing it to the function as
<bang>0 is the equivalent (in other languages) of
!false. If bang is supplied, a true value (literally 1) is passed, if not, 0 is passed. The obvious use for a bang (in my opinion) was to reverse the existing
splitright option. If splits normally open right, bang makes them open left. If they normally open below, bang makes them open above. And vice versa.
Then, I figured it would be nice if the help-queries file auto closed when you left it, so I added an autocommand for
BufLeave (except when
e is used, since that doesn't make much sense . . . there's nothing to leave in that case, and executing
q will actually exit vim). When the user leaves the help-queries buffer, that buffer is closed and the
BufLeave autocmd is disarmed. Disarming the autocmd prevents weirdness if you happen to open that file back up in some other way (like
vsp), in which case you want the buffer to behave like a normal buffer and not auto close.
Finally, I added an autocommand to make
K reopen the help for each entry in the help-queries file. I played at first with using
setlocal keywordprg=:help for this, but strangely, this didn't work if the cursor was on
keywordprg (of all things), although it did work on some other entries. The other problem is that
K operates on the word under cursor, and the concept of a word in vim means that using
g; opens the help for
g instead of for
g;. So I overwrote the
K mapping in this buffer instead and did it by hand.
OpenHelp which records the current cursor position, captures the current line into the
a register, restores the cursor position, and then calls
help with the contents of the
a register, but without triggering autoevents. That's because, in this case, we don't need the help entry added to the help-queries file because we're already looking at it.
And that's it. Once I think of a clever name, maybe I'll publish it as a plugin, but for now, if you want to use it, just copy it somewhere in your vim setup (vimrc or something sourced therein). It's working great, and I've already got 13 entries in my help-queries file. This should give me plenty of material for future vim posts. I know . . . the excitement is palpable! Check back soon!