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 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
----- 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> |