Ruby readline - completion based on already-entered commands

B

Bill Atkins

How can I use the builtin readline library to complete arguments to
commands? I have basic completion working now, so that

%wmi > st [TAB]

will show valid commands starting with "st" (in this case "start" and
"stop"), but how do I complete arguments specific to that command?

For instance, if the user has already typed "start Te" and then hits
TAB, it should show completions specific to the start command (in this
case "TestDevice" and "TerminalServer"), but I'm not clear on how to
do this.

--=20
Bill Atkins
 
J

James Edward Gray II

How can I use the builtin readline library to complete arguments to
commands? I have basic completion working now, so that

%wmi > st [TAB]

will show valid commands starting with "st" (in this case "start" and
"stop"), but how do I complete arguments specific to that command?

Do you have a Pickaxe (second edition)? It's demoed on the page for
the Readline library.

Let me know if you don't and I'll post an example...

James Edward Gray II
 
B

Bill Atkins

I do. I have the basics of readline working; I'm just trying to
figure out how to complete input differently based on already entered
commands.

An analogy would be shell completion. You type "mpla [TAB]" and
"mplayer" gets completed, but any subsequent completion is for paths
only.

On Aug 11, 2005, at 9:44 AM, Bill Atkins wrote:
=20
How can I use the builtin readline library to complete arguments to
commands? I have basic completion working now, so that

%wmi > st [TAB]

will show valid commands starting with "st" (in this case "start" and
"stop"), but how do I complete arguments specific to that command?
=20
Do you have a Pickaxe (second edition)? It's demoed on the page for
the Readline library.
=20
Let me know if you don't and I'll post an example...
=20
James Edward Gray II
=20
=20
=20


--=20
Bill Atkins
 
J

James Edward Gray II

An analogy would be shell completion. You type "mpla [TAB]" and
"mplayer" gets completed, but any subsequent completion is for paths
only.

Oops, sorry. I misunderstood the question... and it's a great one!
I too await the answer...

James Edward Gray II
 
B

Bill Atkins

No problem.

On Aug 11, 2005, at 10:00 AM, Bill Atkins wrote:
=20
An analogy would be shell completion. You type "mpla [TAB]" and
"mplayer" gets completed, but any subsequent completion is for paths
only.
=20
Oops, sorry. I misunderstood the question... and it's a great one!
I too await the answer...
=20
James Edward Gray II
=20
=20


--=20
Bill Atkins
 
B

Brian McCallister

You'd have to have the completion function parse the line "so far" so
you'd have it match against commands until there was only one command
to match ("mplayer") then match completions against file names,
returning options concatenating the command and file name options.

-Brian

I do. I have the basics of readline working; I'm just trying to
figure out how to complete input differently based on already entered
commands.

An analogy would be shell completion. You type "mpla [TAB]" and
"mplayer" gets completed, but any subsequent completion is for paths
only.

How can I use the builtin readline library to complete arguments to
commands? I have basic completion working now, so that

%wmi > st [TAB]

will show valid commands starting with "st" (in this case "start"
and
"stop"), but how do I complete arguments specific to that command?

Do you have a Pickaxe (second edition)? It's demoed on the page for
the Readline library.

Let me know if you don't and I'll post an example...

James Edward Gray II
 
D

Dave Baldwin

How can I use the builtin readline library to complete arguments to
commands? I have basic completion working now, so that

%wmi > st [TAB]

will show valid commands starting with "st" (in this case "start" and
"stop"), but how do I complete arguments specific to that command?

For instance, if the user has already typed "start Te" and then hits
TAB, it should show completions specific to the start command (in this
case "TestDevice" and "TerminalServer"), but I'm not clear on how to
do this.

Unfortunately the ruby readline bindings are missing some key
functions to make this easy. Even raw access to the line buffer
would make this trivial, but this is not available.

I used the following code to augment the ruby library 'Cmd' (http://
code.vernix.org/cmd) to implement what you are asking for (the
missing functions in the code below will be found in this library,
but they just do what their name implies). The main problem to solve
is how to gain access to the whole command line and not just the last
token being completed (this is all readline give you with the Ruby
API). The Ruby API doesn't provide this directly but you can change
the word split character to be an unused character (I use bell
character). This gets you the complete line to make your completion
decision on, but you need to also include the earlier characters on
the line in the completion list you pass back to readline. This
unfortunately causes <tab><tab> to prepend the whole line on every
possible completion in the presented completion list.

You also have to worry about completing on an empty set inserting
spaces into the line buffer (more cosmetic than anything else if I
recall). The space is fixed by turning off the auto space on
complete in readline and adding this yourself. I also output the
bell when necessary. Still not perfect behaviour as <tab> <tab> on
an empty set should just beep but not display anything while in this
case it will display the line so far.

# Configure readline
set_completion_proc:)readline_completion_handler)
Readline.completer_word_break_characters = 7.chr # bell
character
Readline.completion_case_fold = true
# Our empty completion sets return the original line so
readline thinks we have sucessfully completed
# and will append the following character. We need to stop it
from doing this.
Readline.completion_append_character = ''


def readline_completion_handler(line)
tokens = line.split
tokens << "" if line =~ /^\w.* $/ # starting new completion set
if tokens.length == 0
command_list
else
# Get completion set for token.last. See if we have a
valid single command with a completion proc defined.
completion_proc = nil
commands = completion_grep(command_list, tokens.first)
if commands.size == 1
cmd = commands.first
completion_proc = complete_method(cmd) if
collect_complete.include?(cmd)
end
if tokens.length == 1
matches = completion_grep(command_list, tokens.first)
matches = matches.map {|m| m + ' '} if
matches.length == 1
matches
else
if completion_proc
tokens.shift # don't need to send the command
matches = self.send(completion_proc, tokens)
if matches.empty?
print "\a" # beep
[line] # empty completion set so pass
back original line
else
# Prepend the command line up to the last
token we have been completing on to all the match values.
line.sub!(Regexp.new("#{tokens.last}$"), "")
matches = matches.map {|m| line + m}

# If match is complete add space to move
onto next token
matches = matches.map {|m| m + ' '} if
matches.length == 1
matches
end
else
print "\a" # beep
[line] # empty completion set so pass
back original line
end
end
end
end


Hope this helps,

Dave.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top