Autocompleting xchat channel log filenames in zsh
Sometimes zsh is a little too smart for its own good.
Something I do surprisingly often is to complete the filenames for my local channel logs in xchat. Xchat gives its logs crazy filenames like /home/akkana/.xchat2/xchatlogs/FreeNode-#ubuntu-us-ca.log. They're hard to autocomplete -- I have to type something like: ~/.xc<tab>xc<tab>l<tab>Fr<tab>\#ub<tab>us<tab> Even with autocompletion, that's a lot of typing!
Bug zsh makes it even worse: I have to put that backslash in front of
the hash, \#
, or else zsh will see it either as a comment
(unless I unsetopt interactivecomments, in which case I can't
paste functions from my zshrc when I'm testing them);
or as an extended regular expression
(unless I unsetopt extendedglob).
I don't want to unset either of those options: I use both of them.
Tonight I was fiddling with something else related to extendedglob, and was moved to figure out another solution to the xchat completion problem. Why not get zsh's smart zle editor to insert most of that annoying, not easily autocompletable string for me?
The easy solution was to bind it to a function key. I picked F8 for
testing, and figured out its escape sequence by typing
echo
, then Ctrl-V, then hitting F8.
It turns out to insert <ESC>[20~
. So I made a binding:
bindkey -s '\e[20~' '~/.xchat2/xchatlogs/ \\\#^B^B^B'
When I press F8, that inserts the following string:
~/.xchat2/xchatlogs/ \# ↑ (cursor ends up here)... moving the cursor back three characters, so it's right before the space. The space is there so I can autocomplete the server name by typing something like
Fr<TAB>
for FreeNode.
Then I delete the space (Ctrl-D), go to the end of the line (Ctrl-E),
and start typing my channel name, like ubu<TAB>us<TAB>.
I don't have to worry about typing the rest of the path,
or the escaped hash sign.
That's pretty cool. But I wished I could bind it to a character
sequence, like maybe .xc, rather than using a function key.
(I could use my Crikey
program to do that at the X level, but that's cheating; I wanted to do
it within zsh.) You can't just use
bindkey -s '.xch' '~/.xchat2/xchatlogs/ \\\#^B^B^B'
because it's recursive: as soon as zsh inserts the ~/.xc part,
that expands too, and you end up with
~/~/.xchat2/xchatlogs/hat2/xchatlogs/ \# \#.
The solution, though it's a lot more lines, is to use the special variables LBUFFER and RBUFFER. LBUFFER is everything left of the cursor position, and RBUFFER everything right of it. So I define a function to set those, then set a zle "widget" to that function, then finally bindkey to that widget:
function autoxchat() { LBUFFER+="~/.xchat2/xchatlogs/" RBUFFER=" \\#$RBUFFER" } zle -N autoxchat bindkey ".xc" autoxchat
Pretty cool! The only down side: now that I've gone this far in zle bindings, I'm probably an addict and will waste a lot more time tweaking them.
[ 21:31 Jun 15, 2013 More linux/cmdline | permalink to this entry | ]