contents/articles/rawline-030.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
-----
title: "RawLine 0.3.0 released â now with Readline emulation"
content-type: article
timestamp: 1235890020
tags: "ruby|opensource|rawline"
-----
<p><a href="/rawline">RawLine</a> 0.3.0 has been <a href="http://rubyforge.org/projects/rawline">released</a>. This new milestones fixes some minor bugs and adds some new functionalities, must notably:</p>
<ul>
<li>Ruby 1.9 support</li>
<li>A filename completion function</li>
<li>A new <span class="caps">API</span> very similar to the one exposed by the Ruby wrapper for <span class="caps">GNU</span> Readline</li>
</ul>
<p>Some of you asked for Readline compatibility/emulation and that was actually not too difficult to implement: all the bricks were already there, I just had to put them together in the right place.</p>
<p>The <code>RawLine</code> module (you can spell it “Rawline” as well, if you wish) now behaves like <code>Readline</code>. This means that you can now use RawLine like this (taken from examples/readline_emulation.rb):</p>
<div class='ruby'><pre><code>include Rawline
puts "*** Readline Emulation Test Shell ***"
puts " * Press CTRL+X to exit"
puts " * Press <TAB> for file completion"
Rawline.editor.bind(:ctrl_x) { puts; puts "Exiting..."; exit }
Dir.chdir '..'
loop do
puts "You typed: [#{readline("=> ", true).chomp!}]"
end</code></pre></div><p>Basically you get a <code>readline</code> method, a <code>HISTORY</code> constant like the one exposed by Readline (Rawline’s is a RawLine::HistoryBuffer object though — much more manageable), and a <code>FILENAME_COMPLETION_PROC</code> constant, which provides basic filename completion. Here it is:</p>
<div class='ruby'><pre><code>def filename_completion_proc
lambda do |word|
dirs = @line.text.split('/')
path = @line.text.match(/^\/|[a-zA-Z]:\//) ? "/" : Dir.pwd+"/"
if dirs.length == 0 then # starting directory
dir = path
else
dirs.delete(dirs.last) unless File.directory?(path+dirs.join('/'))
dir = path+dirs.join('/')
end
Dir.entries(dir).select { |e| (e =~ /^\./ && @match_hidden_files && word == '') || (e =~ /^#{word}/ && e !~ /^\./) }
end
end</code></pre></div><p>You can find this function as part of the <code>RawLine::Editor</code> class. The result is not exactly the same Readline, because completion matches are not displayed underneath the line but inline and can be cycled through — which is one of Readline’s completion modes anyway.</p>
<p>A few methods of the <code>RawLine::Editor</code> class can now be accessed directly from the <code>RawLine</code> module, like with Readline:</p>
<ul>
<li><code>Rawline.completion_proc</code> — the Proc object used for <span class="caps">TAB</span> completion (defaults to FILENAME_COMPLETION_PROC).</li>
<li><code>Rawline.completion_matches</code> — an array of completion matches.</li>
<li><code>Rawline.completion_append_char</code> — a character to append after a successful completion.</li>
<li><code>Rawline.basic_word_break_characters</code> — a String listing all the characters used as word separators.</li>
<li><code>Rawline.completer_word_break_characters</code> — same as above.</li>
<li><code>Rawline.library_version</code> — the current version of the Rawline library.</li>
<li><code>Rawline.clear_history</code> — to clear the current history.</li>
<li><code>Rawline.match_hidden_files</code> — whether FILENAME_COMPLETION_PROC matches hidden files and folders or not.</li>
</ul>
<p>I bet you didn’t know these methods were even in the Readline wrapper, did you? Probably because of lack of documentation.<br />
Anyhow, another very important difference beween Rawline and Readline is <code>Rawline.editor</code>, i.e. the default instance of RawLine::Editor used by the Rawline module itself.</p>
<p>This makes things easier if you want more control over the line which is being edited and the previously-edited lines. Sure, <code>Readline#completion_proc</code> exposes the current <em>word</em> being typed before hitting tab, and so does <code>Rawline#completion_proc</code> the difference is that if you access <code>Rawline.editor.line</code> you get a <code>RawLine::Line</code> object with all the information you could possibly need about the current line: the position of the cursor, the text, the order the characters were entered, etc. etc. <br />
Now you can imagine why it took me a few minutes to write the <code>filename_completion_proc</code> method (and why it will take you even less time to write your own similar method if you wanna do something different): you can access not only the last word being typed but also the current <em>and previous</em> lines (through <code>Rawline.editor.history</code> or just <code>Rawline::HISTORY</code>)!</p>
<p>It must be said, as usual, that Rawline is <em>not</em> a complete replacement for the Readline library yet (and it will probably never be, as Readline is huge!), but it’s a good cross-platform, more Ruby-esque alternative to what’s currently available by the Readline wrapper for Ruby.</p>
<p>It’s not as fast, of course, especially when completing long words, but it’s quite usable. The following libraries are not required but recommended:</p>
<ul>
<li><code>win32console</code> (on Windows)</li>
<li><code>termios</code> (on *nix)</li>
</ul>
<p>They basically make Rawline faster. If you don’t use them, Rawline will fall back on its pure-Ruby implementation to move left and right (i.e. printing backspaces and spaces character codes instead of <span class="caps">ASCII</span> escape codes).</p>
<p>Unfortunately, there’s no <code>vi_editing_mode</code> or <code>emacs_editing_mode</code> yet (for time constraints: they <em>can</em> be implemented!) but patches are very welcome. Also, if you need more features, all you have to do is ask :-)</p>
<p>P.S.: Check out the new <a href="/rawline">Project Page</a> and especially its Resources section!</p>
|