Readline has the powerful concept of a macro but there are few examples that seem to do anything other than insert text. We’ll look at using a Readline macro to trigger Ruby code in a Ruby shell. Since Ruby has Bond to easily define Readline completions, we can use a macro to change completion behavior with the flick of a key combo.
Intro
First off, you should be aware that what follows can be done with any readline, non-ruby completion setup that allows painless redefinition of a completion. This post will focus on doing this within a Ruby shell.
Redefinition of an existing completion is easy in a Ruby shell with Bond and Bond.recomplete(). Simply use the original completion condition and call Bond.recomplete instead of Bond.complete with the new completion definition:
If you use the console enough, readline is your friend. You’ve probably read the docs and made your own ~/.inputrc. What we care about here is readline’s macros. With a macro, you can map a keystroke combo to any number of keystrokes. These mapped keystrokes can even include keystrokes that trigger other readline macros or that hit enter to execute something in a shell! Just for fun, let’s execute some ruby code in irb with a keystroke combo. Drop this in your ~/.inputrc:
# Only defined when using ruby's readline library i.e. in irb
$if Ruby
"\C-x\C-p": "puts \"So many keystrokes. So little time.\"\n"
$endif
Start irb and press Control-X Control-P:
bash> irb
>> [C-xC-p]
>> puts "So many keystrokes. So little time."
So many keystrokes. So little time.
=> nil
Ruby code executed at the flick of a key combo!
Ruby Readline Example
I like method completion to autocomplete any method an objects responds to … most of the time. But if I’m using a new gem and it’s unfamiliar objects I usually just want to try out its functionality. Sure I can call self.class.instance_methods(false) on an object but why do that every time when I can just change an object’s completion behavior with a key combo? Here’s the following method I use to toggle object completion for bond/completion :
This method checks the current toggle state and switches between the two completion definitions per call. Notice that two completions are redefined since bond/completion uses two completion definitions to control an object’s method completion.
Now let’s map this to method to a key combo in ~/.inputrc:
Sweet. With a simple key combo, we can now alter method completion for any ruby object! One shortcoming you can see is that the key combo must be executed when in a blank line. Otherwise you’ll end up executing what’s already in the line as well. It’s possible to bypass this if anyone ever gets around to writing a Ruby binding for setting readline’s line buffer with rl_replace_line(). Maybe I’ll get around to brushing up on my C one of these days…
Outro
There are two cool ideas I hope you got from this. First, that changing completion behavior with a key combo isn’t limited to a ruby shell. With bash, you could map a key combo to call a bash function that redefines completions with bash’s complete(). Second, when in a ruby shell, you can execute any ruby code with a simple key combo. Although I only looked at changing completion behavior with key combos, there are an endless number of possibilities to be played with. I’m curious to see what others will come up with. If you’re hungering for more readline configuration tips, check outmy inputrc.