Browse files

Add remote tab completion

  • Loading branch information...
1 parent 684919f commit 6e7018c1ba5cdd185ddfefc31ee81361236c1694 Jennifer Hickey committed Jan 27, 2012
Showing with 36 additions and 8 deletions.
  1. +9 −2 doc/README
  2. +27 −6 lib/live_console.rb
View
11 doc/README
@@ -92,8 +92,15 @@ only works for the TCP socket mode.
== Bugs
-LiveConsole lacks many of the niceties of IRB on the console, like Readline
-support. For this, you can actually use the wonderful rlwrap program, which
+LiveConsole lacks many of the niceties of IRB on the console, like full Readline
+support. LiveConsole does offer tab completion if you write a client that sends
+the phrase to complete, ending with a tab and newline. LiveConsole will activate the IRB
+completion proc and send back the results, terminated by newline.
+
+Readline history support can be added if anyone finds it useful to manage history
+remotely.
+
+For full Readline support, you can actually use the wonderful rlwrap program, which
wraps an arbitrary interactive program in readline. For example, to connect to
a LiveConsole on localhost:3333, use
rlwrap netcat localhost 3333
View
33 lib/live_console.rb
@@ -6,20 +6,20 @@
require 'irb'
require 'irb/frame'
require 'socket'
-
+require 'irb/completion'
# LiveConsole provides a socket that can be connected to via netcat or telnet
# to use to connect to an IRB session inside a running process. It listens on the
# specified address/port or Unix Domain Socket path,
# and presents connecting clients with an IRB shell. Using this, you can
# execute code on a running instance of a Ruby process to inspect the state or
-# even patch code on the fly. There is currently no readline support.
+# even patch code on the fly.
class LiveConsole
include Socket::Constants
autoload :IOMethods, 'live_console/io_methods'
- attr_accessor :io_method, :io, :thread, :bind, :authenticate
- private :io_method=, :io=, :thread=, :authenticate=
+ attr_accessor :io_method, :io, :thread, :bind, :authenticate, :readline
+ private :io_method=, :io=, :thread=, :authenticate=, :readline=
# call-seq:
# # Bind a LiveConsole to localhost:3030 (only allow clients on this
@@ -48,6 +48,7 @@ def initialize(io_method, opts = {})
self.io_method = io_method.to_sym
self.bind = opts.delete :bind
self.authenticate = opts.delete :authenticate
+ self.readline = opts.delete :readline
unless IOMethods::List.include?(self.io_method)
raise ArgumentError, "Unknown IO method: #{io_method}"
end
@@ -209,10 +210,11 @@ class GenericIOMethod < IRB::StdioInputMethod
#
# Creates a GenericIOMethod, using either a single object for both input
# and output, or one object for input and another for output.
- def initialize(input, output = nil)
+ def initialize(input, output = nil, readline=false)
@input, @output = input, output
@line = []
@line_no = 0
+ @readline = readline
end
attr_reader :input
@@ -223,7 +225,15 @@ def output
def gets
output.print @prompt
output.flush
- @line[@line_no += 1] = input.gets
+ line = input.gets
+ if @readline
+ #Return tab completion data as long as we receive input that ends in '\t'
+ while line.chomp =~ /\t\z/n
+ run_completion_proc line.chomp.chop
+ line = input.gets
+ end
+ end
+ @line[@line_no += 1] = line
@line[@line_no]
end
@@ -252,4 +262,15 @@ def close
input.close
output.close if @output
end
+
+ private
+ # Runs the IRB CompletionProc with the given argument and writes the array to output stream as
+ # a comma-separated String, terminated by newline (so clients know when all data received)
+ def run_completion_proc(line)
+ opts = IRB::InputCompletor::CompletionProc.call(line)
+ opts.compact!
+ optstrng = opts.join(",") + "\n"
+ output.print optstrng
+ output.flush
+ end
end

0 comments on commit 6e7018c

Please sign in to comment.