mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
2afed6ecef
* lib/irb.rb: ditto. * lib/irb/**/*.rb: ditto. * lib/shell.rb: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@47266 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
265 lines
6.4 KiB
Ruby
265 lines
6.4 KiB
Ruby
#
|
|
# irb/multi-irb.rb - multiple irb module
|
|
# $Release Version: 0.9.6$
|
|
# $Revision$
|
|
# by Keiju ISHITSUKA(keiju@ruby-lang.org)
|
|
#
|
|
# --
|
|
#
|
|
#
|
|
#
|
|
IRB.fail CantShiftToMultiIrbMode unless defined?(Thread)
|
|
require "thread"
|
|
|
|
module IRB
|
|
class JobManager
|
|
|
|
# Creates a new JobManager object
|
|
def initialize
|
|
@jobs = []
|
|
@current_job = nil
|
|
end
|
|
|
|
# The active irb session
|
|
attr_accessor :current_job
|
|
|
|
# The total number of irb sessions, used to set +irb_name+ of the current
|
|
# Context.
|
|
def n_jobs
|
|
@jobs.size
|
|
end
|
|
|
|
# Returns the thread for the given +key+ object, see #search for more
|
|
# information.
|
|
def thread(key)
|
|
th, = search(key)
|
|
th
|
|
end
|
|
|
|
# Returns the irb session for the given +key+ object, see #search for more
|
|
# information.
|
|
def irb(key)
|
|
_, irb = search(key)
|
|
irb
|
|
end
|
|
|
|
# Returns the top level thread.
|
|
def main_thread
|
|
@jobs[0][0]
|
|
end
|
|
|
|
# Returns the top level irb session.
|
|
def main_irb
|
|
@jobs[0][1]
|
|
end
|
|
|
|
# Add the given +irb+ session to the jobs Array.
|
|
def insert(irb)
|
|
@jobs.push [Thread.current, irb]
|
|
end
|
|
|
|
# Changes the current active irb session to the given +key+ in the jobs
|
|
# Array.
|
|
#
|
|
# Raises an IrbAlreadyDead exception if the given +key+ is no longer alive.
|
|
#
|
|
# If the given irb session is already active, an IrbSwitchedToCurrentThread
|
|
# exception is raised.
|
|
def switch(key)
|
|
th, irb = search(key)
|
|
IRB.fail IrbAlreadyDead unless th.alive?
|
|
IRB.fail IrbSwitchedToCurrentThread if th == Thread.current
|
|
@current_job = irb
|
|
th.run
|
|
Thread.stop
|
|
@current_job = irb(Thread.current)
|
|
end
|
|
|
|
# Terminates the irb sessions specified by the given +keys+.
|
|
#
|
|
# Raises an IrbAlreadyDead exception if one of the given +keys+ is already
|
|
# terminated.
|
|
#
|
|
# See Thread#exit for more information.
|
|
def kill(*keys)
|
|
for key in keys
|
|
th, _ = search(key)
|
|
IRB.fail IrbAlreadyDead unless th.alive?
|
|
th.exit
|
|
end
|
|
end
|
|
|
|
# Returns the associated job for the given +key+.
|
|
#
|
|
# If given an Integer, it will return the +key+ index for the jobs Array.
|
|
#
|
|
# When an instance of Irb is given, it will return the irb session
|
|
# associated with +key+.
|
|
#
|
|
# If given an instance of Thread, it will return the associated thread
|
|
# +key+ using Object#=== on the jobs Array.
|
|
#
|
|
# Otherwise returns the irb session with the same top-level binding as the
|
|
# given +key+.
|
|
#
|
|
# Raises a NoSuchJob exception if no job can be found with the given +key+.
|
|
def search(key)
|
|
job = case key
|
|
when Integer
|
|
@jobs[key]
|
|
when Irb
|
|
@jobs.find{|k, v| v.equal?(key)}
|
|
when Thread
|
|
@jobs.assoc(key)
|
|
else
|
|
@jobs.find{|k, v| v.context.main.equal?(key)}
|
|
end
|
|
IRB.fail NoSuchJob, key if job.nil?
|
|
job
|
|
end
|
|
|
|
# Deletes the job at the given +key+.
|
|
def delete(key)
|
|
case key
|
|
when Integer
|
|
IRB.fail NoSuchJob, key unless @jobs[key]
|
|
@jobs[key] = nil
|
|
else
|
|
catch(:EXISTS) do
|
|
@jobs.each_index do
|
|
|i|
|
|
if @jobs[i] and (@jobs[i][0] == key ||
|
|
@jobs[i][1] == key ||
|
|
@jobs[i][1].context.main.equal?(key))
|
|
@jobs[i] = nil
|
|
throw :EXISTS
|
|
end
|
|
end
|
|
IRB.fail NoSuchJob, key
|
|
end
|
|
end
|
|
until assoc = @jobs.pop; end unless @jobs.empty?
|
|
@jobs.push assoc
|
|
end
|
|
|
|
# Outputs a list of jobs, see the irb command +irb_jobs+, or +jobs+.
|
|
def inspect
|
|
ary = []
|
|
@jobs.each_index do
|
|
|i|
|
|
th, irb = @jobs[i]
|
|
next if th.nil?
|
|
|
|
if th.alive?
|
|
if th.stop?
|
|
t_status = "stop"
|
|
else
|
|
t_status = "running"
|
|
end
|
|
else
|
|
t_status = "exited"
|
|
end
|
|
ary.push format("#%d->%s on %s (%s: %s)",
|
|
i,
|
|
irb.context.irb_name,
|
|
irb.context.main,
|
|
th,
|
|
t_status)
|
|
end
|
|
ary.join("\n")
|
|
end
|
|
end
|
|
|
|
@JobManager = JobManager.new
|
|
|
|
# The current JobManager in the session
|
|
def IRB.JobManager
|
|
@JobManager
|
|
end
|
|
|
|
# The current Context in this session
|
|
def IRB.CurrentContext
|
|
IRB.JobManager.irb(Thread.current).context
|
|
end
|
|
|
|
# Creates a new IRB session, see Irb.new.
|
|
#
|
|
# The optional +file+ argument is given to Context.new, along with the
|
|
# workspace created with the remaining arguments, see WorkSpace.new
|
|
def IRB.irb(file = nil, *main)
|
|
workspace = WorkSpace.new(*main)
|
|
parent_thread = Thread.current
|
|
Thread.start do
|
|
begin
|
|
irb = Irb.new(workspace, file)
|
|
rescue
|
|
print "Subirb can't start with context(self): ", workspace.main.inspect, "\n"
|
|
print "return to main irb\n"
|
|
Thread.pass
|
|
Thread.main.wakeup
|
|
Thread.exit
|
|
end
|
|
@CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
|
|
@JobManager.insert(irb)
|
|
@JobManager.current_job = irb
|
|
begin
|
|
system_exit = false
|
|
catch(:IRB_EXIT) do
|
|
irb.eval_input
|
|
end
|
|
rescue SystemExit
|
|
system_exit = true
|
|
raise
|
|
#fail
|
|
ensure
|
|
unless system_exit
|
|
@JobManager.delete(irb)
|
|
if @JobManager.current_job == irb
|
|
if parent_thread.alive?
|
|
@JobManager.current_job = @JobManager.irb(parent_thread)
|
|
parent_thread.run
|
|
else
|
|
@JobManager.current_job = @JobManager.main_irb
|
|
@JobManager.main_thread.run
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
Thread.stop
|
|
@JobManager.current_job = @JobManager.irb(Thread.current)
|
|
end
|
|
|
|
@CONF[:SINGLE_IRB_MODE] = false
|
|
@JobManager.insert(@CONF[:MAIN_CONTEXT].irb)
|
|
@JobManager.current_job = @CONF[:MAIN_CONTEXT].irb
|
|
|
|
class Irb
|
|
def signal_handle
|
|
unless @context.ignore_sigint?
|
|
print "\nabort!!\n" if @context.verbose?
|
|
exit
|
|
end
|
|
|
|
case @signal_status
|
|
when :IN_INPUT
|
|
print "^C\n"
|
|
IRB.JobManager.thread(self).raise RubyLex::TerminateLineInput
|
|
when :IN_EVAL
|
|
IRB.irb_abort(self)
|
|
when :IN_LOAD
|
|
IRB.irb_abort(self, LoadAbort)
|
|
when :IN_IRB
|
|
# ignore
|
|
else
|
|
# ignore other cases as well
|
|
end
|
|
end
|
|
end
|
|
|
|
trap("SIGINT") do
|
|
@JobManager.current_job.signal_handle
|
|
Thread.stop
|
|
end
|
|
|
|
end
|