# Readline and conditional tab results based on input

**URL:** <https://www.ruby-forum.com/t/readline-and-conditional-tab-results-based-on-input/143367>\
**Category:** Ruby\
**Created:** [July 17, 2008, 6:49pm UTC](https://www.ruby-forum.com/t/readline-and-conditional-tab-results-based-on-input/143367 "2008-07-17T18:49:23Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![quik77](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/q/dec6dc/32.png) [@quik77](https://www.ruby-forum.com/u/quik77)\
**Post date:** [July 17, 2008, 6:49pm UTC](https://www.ruby-forum.com/t/readline-and-conditional-tab-results-based-on-input/143367/1 "2008-07-17T18:49:23Z")

</div>

Hi there,

is there a way to “find out” what a user already typed in readline?

Reason is, i am fetching user input like so:

user\_input = Readline.readline(’ Input name of a dir, then press  
'+"\n",true)

And with Readline.completion\_proc i have code that will return only dirs  
in this directory. But I would like to invoke this specific code part  
only  
when a user typed in i.e. "cd "

So if i have a directory called “foobar” it should work here:

“cd foo” # completed to foobar

but if the user would type

“blabalblabla foo” # there should be no result

---

<div class="post-metadata">

**Author:** ![quik77](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/q/dec6dc/32.png) [@quik77](https://www.ruby-forum.com/u/quik77)\
**Post date:** [July 18, 2008, 7:27pm UTC](https://www.ruby-forum.com/t/readline-and-conditional-tab-results-based-on-input/143367/2 "2008-07-18T19:27:17Z")

</div>

Hi,

On 17.07.2008, at 18:44, Marc H. wrote:

> is there a way to “find out” what a user already typed in readline?

I think you have to implement a CompletionProc.  
simple example:

irb\> CompletionProc = proc {|input| input}  
=\> #Proc:0x0003f034@:9(irb)  
irb\> Readline.completion\_proc = CompletionProc  
=\> #Proc:0x0003f034@:9(irb)  
irb\> Readline.completion\_proc.call(“req”)  
=\> “req”  
irb\> req # tab clicked  
req # return value

A real world example exists on irb/completion.rb

hth. regards, Sandor  
Szücs

---

<div class="post-metadata">

**Author:** ![quik77](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/q/dec6dc/32.png) [@quik77](https://www.ruby-forum.com/u/quik77)\
**Post date:** [July 18, 2008, 9:20pm UTC](https://www.ruby-forum.com/t/readline-and-conditional-tab-results-based-on-input/143367/3 "2008-07-18T21:20:58Z")

</div>

Hi,

Sorry for my first post, it wasn’t what you not already knew.  
I should better read then write, but I hope on the bottom I found  
something that help.

On 17.07.2008, at 18:44, Marc H. wrote:

> in this directory. But I would like to invoke this specific code part  
> “blabalblabla foo” # there should be no result  
> I tested a bit your scenario and I think you have to set  
> Readline.completer\_word\_break\_characters , but not to " \t\n"  
> '`><=;|&{(" If you set it to "\t\n\"\\'`\>\<=;|&{(", without whitespace, you will  
> get as  
> input in your CompletionProc the whole line.

Then you can do:

when /^cd/  
typed = input.sub(“cd”,"").strip  
…

$ tabcompletion\_test.rb  
cd bl|cd bl| # tab called after ‘cd bl’

$ cat tabcompletion\_test.rb

require ‘readline’

module MYCMD  
ReservedWords = [  
“BEGIN”, “END”, “yield”,  
]

CompletionProc = proc { |input|  
puts("|#{input}|")  
case input  
when /^cd/  
typed = input.sub(“cd”,"")  
if typed==""  
candidates = Dir.entries(".")  
elsif typed == " "  
candidates = Dir.entries(".")  
else  
candidates = [“no”]  
end  
else  
(candidates|ReservedWords).grep(/^#{Regexp.quote(input)}/)  
end  
}

```
 def self.select_message(receiver, message, candidates)
   candidates.grep(/^#{message}/).collect do |e|
   puts("e:#{e}")
   case e
   when /^[a-zA-Z_]/
     receiver + "." + e
   end
 end

```

end  
end

## no ’ ’

Readline.completer\_word\_break\_characters= “\t\n”\’`\>\<=;|&{("  
Readline.completion\_append\_character = nil  
Readline.completion\_proc = MYCMD::CompletionProc

loop do  
cmd = Readline.readline  
eval(cmd.chomp)  
end

regards, Sandor  
Szücs

---

<div class="post-metadata">

**Author:** ![quik77](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/q/dec6dc/32.png) [@quik77](https://www.ruby-forum.com/u/quik77)\
**Post date:** [July 18, 2008, 10:03pm UTC](https://www.ruby-forum.com/t/readline-and-conditional-tab-results-based-on-input/143367/4 "2008-07-18T22:03:06Z")

</div>

I should be even more specific.

On 18.07.2008, at 21:15, Sandor Szücs wrote:

> > “cd foo” # completed to foobar
> > 
> > but if the user would type
> > 
> > “blabalblabla foo” # there should be no result

```auto
require 'readline'

module MYCMD

CompletionProc = proc { |input|
   case input
   when /^cd/
     typed = input.sub("cd","")
     typed.strip!
     save = input.sub(/#{typed}\z/,"")
     fragment = File.basename(typed)
     tmp = Dir.entries(File.dirname(typed))
     candidates = tmp.select {|f| f.match(/^#{Regexp.quote(fragment)}/)}
     candidates.map! {|f| save+File.dirname(typed)+"/"+f+"/"}
   end
}
#Readline.completer_word_break_characters= "\t\n\"\\'`><=;|&{("
Readline.completer_word_break_characters= "\t"
Readline.completion_append_character = nil
Readline.completion_proc = MYCMD::CompletionProc

loop do
   cmd = Readline.readline
   eval(cmd.chomp)
end

```

That was what you have expected, or?

regards, Sandor  
Szücs

---

<div class="post-metadata">

**Author:** ![quik77](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/q/dec6dc/32.png) [@quik77](https://www.ruby-forum.com/u/quik77)\
**Post date:** [August 11, 2008, 6:45am UTC](https://www.ruby-forum.com/t/readline-and-conditional-tab-results-based-on-input/143367/5 "2008-08-11T06:45:40Z")

</div>

Hi, I am the maintainer of Readline module.

> but if the user would type
> 
> “blabalblabla foo” # there should be no result

I want to add some methods that satisfy the Marc H.'s demands.  
The patch contributed to the RubyForge is taken.

[#3212] Readline does not provide enough context to the  
completion\_proc  
[http://rubyforge.org/tracker/index.php?func=detail&aid=3212&group\_id=426&atid=1698](http://rubyforge.org/tracker/index.php?func=detail&aid=3212&group_id=426&atid=1698)

First of all, I will take it into ruby 1.9. Afterwards, backport to 1.8.

Thanks, TAKAO Kouji.

---

<div class="post-metadata">

**Author:** ![quik77](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/q/dec6dc/32.png) [@quik77](https://www.ruby-forum.com/u/quik77)\
**Post date:** [August 12, 2008, 10:21am UTC](https://www.ruby-forum.com/t/readline-and-conditional-tab-results-based-on-input/143367/6 "2008-08-12T10:21:01Z")

</div>

Hi,

On 2008/08/11, at 13:42, Takao K. wrote:

> > but if the user would type
> > 
> > “blabalblabla foo” # there should be no result
> 
> I want to add some methods that satisfy the Marc H.'s demands.  
> The patch contributed to the RubyForge is taken.
> 
> [#3212] Readline does not provide enough context to the completion\_proc  
> [http://rubyforge.org/tracker/index.php?func=detail&aid=3212&group\_id=426&atid=1698](http://rubyforge.org/tracker/index.php?func=detail&aid=3212&group_id=426&atid=1698)
> 
> First of all, I will take it into ruby 1.9. Afterwards, backport to 1.8.

I will add the follows methods.

- line\_buffer: Returns the full line that is being edited.  
(same as rl\_line\_buffer)
- point: Returns the index of the current cursor position  
in Readline.line\_buffer. (same as rl\_point)

The match\_start is computed by subtracting the length of input-string  
from Readline.point.  
The match\_end and Readline.line\_buffer are same.

Attached the patch for ruby 1.9 (r18525).

## Thanks, TAKAO Kouji.

# Index: ext/readline/readline.c

— ext/readline/readline.c (revision 18525)  
+++ ext/readline/readline.c (working copy)  
@@ -367,6 +367,56 @@  
return rb\_attr\_get(mReadline, completion\_case\_fold);  
}

+/\*

- 
  - call-seq:

- 
  - Readline.line\_buffer → string

- 
  - 

- 
  - Returns the full line that is being edited. This is useful from

- 
  - within the complete\_proc for determining the context of the

- 
  - completion request.

- 
  - 

- 
  - The length of +Readline.line\_buffer+ and GNU Readline’s rl\_end are

- 
  - same.

- \*/  
+static VALUE  
+readline\_s\_get\_line\_buffer(VALUE self)  
+{  
+#ifdef HAVE\_RL\_LINE\_BUFFER
- rb\_secure(4);
- if (rl\_line\_buffer == NULL)
- return Qnil;
- return rb\_tainted\_str\_new2(rl\_line\_buffer);  
+#else
- rb\_notimplement();
- return Qnil; /\* not reached _/  
+#endif /_ HAVE\_RL\_LINE\_BUFFER \*/  
+}
- 

+/\*

- 
  - call-seq:

- 
  - Readline.point → int

- 
  - 

- 
  - Returns the index of the current cursor position in

- 
  - +Readline.line\_buffer+.

- 
  - 

- 
  - The index in +Readline.line\_buffer+ which matches the start of

- 
  - input-string passed to completion\_proc is computed by subtracting

- 
  - the length of input-string from +Readline.point+.

- 
  - 

- 
  - start = (the length of input-string) - Readline.point

- \*/  
+static VALUE  
+readline\_s\_get\_point(VALUE self)  
+{  
+#ifdef HAVE\_RL\_POINT
- rb\_secure(4);
- return INT2NUM(rl\_point);  
+#else
- rb\_notimplement();
- return Qnil; /\* not reached _/  
+#endif /_ HAVE\_RL\_POINT \*/  
+}
- 

static char \*\*  
readline\_attempted\_completion\_function(const char \*text, int start, int  
end)  
{  
@@ -1196,6 +1246,10 @@  
readline\_s\_set\_completion\_case\_fold, 1);  
rb\_define\_singleton\_method(mReadline, “completion\_case\_fold”,  
readline\_s\_get\_completion\_case\_fold, 0);

- rb\_define\_singleton\_method(mReadline, “line\_buffer”,
- 

```
        readline_s_get_line_buffer, 0);

```

- rb\_define\_singleton\_method(mReadline, “point”,
- 

```
        readline_s_get_point, 0);

```

rb\_define\_singleton\_method(mReadline, “vi\_editing\_mode”,  
readline\_s\_vi\_editing\_mode, 0);  
rb\_define\_singleton\_method(mReadline, “vi\_editing\_mode?”,  
Index: ext/readline/extconf.rb  
===================================================================  
— ext/readline/extconf.rb (revision 18525)  
+++ ext/readline/extconf.rb (working copy)  
@@ -59,6 +59,8 @@  
have\_readline\_var(“rl\_attempted\_completion\_over”)  
have\_readline\_var(“rl\_library\_version”)  
have\_readline\_var(“rl\_editing\_mode”)  
+have\_readline\_var(“rl\_line\_buffer”)  
+have\_readline\_var(“rl\_point”)

# workaround for native windows.

# /mswin|bccwin|mingw/ !~ RUBY\_PLATFORM && have\_readline\_var(“rl\_event\_hook”) have\_readline\_func(“rl\_cleanup\_after\_signal”) Index: test/readline/test\_readline.rb

— test/readline/test\_readline.rb (revision 18525)  
+++ test/readline/test\_readline.rb (working copy)  
@@ -3,6 +3,8 @@  
=begin  
class \<\< Readline  
[

- 

```
"line_buffer",

```

- 

```
"point",
"vi_editing_mode",
"emacs_editing_mode",
"completion_append_character=",

```

@@ -61,6 +63,8 @@  
[“completer\_quote\_characters”],  
[“filename\_quote\_characters=”, “\”],  
[“filename\_quote\_characters”],

- 

```
  ["line_buffer"],

```

- 

```
  ["point"],
 ]

```

method\_args.each do |method\_name, \*args|  
assert\_raises(SecurityError, NotImplementedError,  
@@ -74,41 +78,83 @@  
end  
end

- def test\_readline
- stdin = Tempfile.new(“test\_readline\_stdin”)
- stdout = Tempfile.new(“test\_readline\_stdout”)
- begin
- 

```
 stdin.write("hello\n")

```

- 

```
 stdin.close

```

- 

```
 stdout.close

```

- 

```
 line = replace_stdio(stdin.path, stdout.path) {

```

- 

```
   Readline.readline("> ", true)

```

- 

```
 }

```

- 

```
 assert_equal("hello", line)

```

- 

```
 assert_equal(true, line.tainted?)

```

- 

```
 stdout.open

```

- 

```
 assert_equal("> ", stdout.read(2))

```

- 

```
 assert_equal(1, Readline::HISTORY.length)

```

- 

```
 assert_equal("hello", Readline::HISTORY[0])

```

- 

```
 assert_raises(SecurityError) do

```

- 

```
   Thread.start {

```

- 

```
     $SAFE = 1

```

- 

```
     replace_stdio(stdin.path, stdout.path) do

```

- 

```
       Readline.readline("> ".taint)

```

- 

```
     end

```

- 

```
   }.join

```

- if !/EditLine/n.match(Readline::VERSION)
- def test\_readline
- 

```
 stdin = Tempfile.new("test_readline_stdin")

```

- 

```
 stdout = Tempfile.new("test_readline_stdout")

```

- 

```
 begin

```

- 

```
   stdin.write("hello\n")

```

- 

```
   stdin.close

```

- 

```
   stdout.close

```

- 

```
   line = replace_stdio(stdin.path, stdout.path) {

```

- 

```
     Readline.readline("> ", true)

```

- 

```
   }

```

- 

```
   assert_equal("hello", line)

```

- 

```
   assert_equal(true, line.tainted?)

```

- 

```
   stdout.open

```

- 

```
   assert_equal("> ", stdout.read(2))

```

- 

```
   assert_equal(1, Readline::HISTORY.length)

```

- 

```
   assert_equal("hello", Readline::HISTORY[0])

```

- 

```
   assert_raises(SecurityError) do

```

- 

```
     Thread.start {

```

- 

```
       $SAFE = 1

```

- 

```
       replace_stdio(stdin.path, stdout.path) do

```

- 

```
         Readline.readline("> ".taint)

```

- 

```
       end

```

- 

```
     }.join

```

- 

```
   end

```

- 

```
   assert_raises(SecurityError) do

```

- 

```
     Thread.start {

```

- 

```
       $SAFE = 4

```

- 

```
       replace_stdio(stdin.path, stdout.path) { 

```

Readline.readline("\> ") }

- 

```
     }.join

```

- 

```
   end

```

- 

```
 ensure

```

- 

```
   stdin.close(true)

```

- 

```
   stdout.close(true)
 end

```

- 

```
 assert_raises(SecurityError) do

```

- 

```
   Thread.start {

```

- 

```
     $SAFE = 4

```

- 

```
     replace_stdio(stdin.path, stdout.path) { Readline.readline("> 

```

") }

- 

```
   }.join

```

- end
- 
- 
# line\_buffer
- 
# point
- def test\_line\_buffer\_\_point
- 

```
 begin

```

- 

```
   Readline.line_buffer

```

- 

```
   Readline.point

```

- 

```
 rescue NotImplementedError

```

- 

```
   return
 end

```

- ensure
- 

```
 stdin.close(true)

```

- 

```
 stdout.close(true)

```

- 
- 

```
 stdin = Tempfile.new("test_readline_stdin")

```

- 

```
 stdout = Tempfile.new("test_readline_stdout")

```

- 

```
 begin

```

- 

```
   actual_text = nil

```

- 

```
   actual_line_buffer = nil

```

- 

```
   actual_point = nil

```

- 

```
   Readline.completion_proc = proc { |text|

```

- 

```
     actual_text = text

```

- 

```
     actual_point = Readline.point

```

- 

```
     actual_buffer_line = Readline.line_buffer

```

- 

```
     stdin.write(" finish\n")

```

- 

```
     stdin.close

```

- 

```
     stdout.close

```

- 

```
     return ["complete"]

```

- 

```
   }

```

- 

```
   stdin.write("first second\t")

```

- 

```
   stdin.flush

```

- 

```
   line = replace_stdio(stdin.path, stdout.path) {

```

- 

```
     Readline.readline("> ", false)

```

- 

```
   }

```

- 

```
   assert_equal("first second", actual_line_buffer)

```

- 

```
   assert_equal(12, actual_point)

```

- 

```
   assert_equal("first complete finish", Readline.line_buffer)

```

- 

```
   assert_equal(21, Readline.point)

```

- 

```
 ensure

```

- 

```
   stdin.close(true)

```

- 

```
   stdout.close(true)

```

- 

```
 end

```

end

- end if !/EditLine/n.match(Readline::VERSION)

- end

---

<div class="post-metadata">

**Author:** ![quik77](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/q/dec6dc/32.png) [@quik77](https://www.ruby-forum.com/u/quik77)\
**Post date:** [July 10, 2009, 2:23pm UTC](https://www.ruby-forum.com/t/readline-and-conditional-tab-results-based-on-input/143367/7 "2009-07-10T14:23:26Z")

</div>

Hi,

Sorry for late reply.

On 2008/08/12, at 17:17, Takao K. wrote:

> Attached the patch for ruby 1.9 (r18525).  
> I commited it in r24019.

Thanks, Kouji.

* * *

TAKAO Kouji [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
blog: [高尾宏治日記 on はてな](http://d.hatena.ne.jp/kouji0625/)  
twitter: takaokouji / projects: ruby, s7-seven

---

<div class="post-metadata">

**Author:** ![quik77](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/q/dec6dc/32.png) [@quik77](https://www.ruby-forum.com/u/quik77)\
**Post date:** [October 17, 2008, 11:34pm UTC](https://www.ruby-forum.com/t/readline-and-conditional-tab-results-based-on-input/143367/8 "2008-10-17T23:34:42Z")

</div>

Hello Takao K.,

Sorry for the late reply.

Thanks a lot!
