1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* lib/irb.rb, lib/irb/*: Documentation for IRB

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38515 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
zzak 2012-12-21 05:45:50 +00:00
parent 4f7a6aafa5
commit 7e9eb32669
21 changed files with 611 additions and 60 deletions

View file

@ -1,3 +1,7 @@
Fri Dec 21 14:45:00 2012 Zachary Scott <zachary@zacharyscott.net>
* lib/irb.rb, lib/irb/*: Documentation for IRB
Fri Dec 21 11:31:02 2012 Eric Hodel <drbrain@segment7.net>
* lib/rake/*: Updated to rake 0.9.6

View file

@ -149,6 +149,52 @@ STDOUT.sync = true
# :RETURN => "%s\n" # used to printf
# }
#
# irb comes with a number of available modes:
#
# # :NULL:
# # :PROMPT_I:
# # :PROMPT_N:
# # :PROMPT_S:
# # :PROMPT_C:
# # :RETURN: |
# # %s
# # :DEFAULT:
# # :PROMPT_I: ! '%N(%m):%03n:%i> '
# # :PROMPT_N: ! '%N(%m):%03n:%i> '
# # :PROMPT_S: ! '%N(%m):%03n:%i%l '
# # :PROMPT_C: ! '%N(%m):%03n:%i* '
# # :RETURN: |
# # => %s
# # :CLASSIC:
# # :PROMPT_I: ! '%N(%m):%03n:%i> '
# # :PROMPT_N: ! '%N(%m):%03n:%i> '
# # :PROMPT_S: ! '%N(%m):%03n:%i%l '
# # :PROMPT_C: ! '%N(%m):%03n:%i* '
# # :RETURN: |
# # %s
# # :SIMPLE:
# # :PROMPT_I: ! '>> '
# # :PROMPT_N: ! '>> '
# # :PROMPT_S:
# # :PROMPT_C: ! '?> '
# # :RETURN: |
# # => %s
# # :INF_RUBY:
# # :PROMPT_I: ! '%N(%m):%03n:%i> '
# # :PROMPT_N:
# # :PROMPT_S:
# # :PROMPT_C:
# # :RETURN: |
# # %s
# # :AUTO_INDENT: true
# # :XMP:
# # :PROMPT_I:
# # :PROMPT_N:
# # :PROMPT_S:
# # :PROMPT_C:
# # :RETURN: |2
# # ==>%s
#
# == Restrictions
#
# Because irb evaluates input immediately after it is syntactically complete,
@ -185,7 +231,8 @@ STDOUT.sync = true
# A few commands for loading files within the session are also available:
#
# +source+::
# Loads a given file in the current session, see IrbLoader#source_file
# Loads a given file in the current session and displays the source lines,
# see IrbLoader#source_file
# +irb_load+::
# Loads the given file similarly to Kernel#load, see IrbLoader#irb_load
# +irb_require+::
@ -279,6 +326,7 @@ STDOUT.sync = true
module IRB
@RCS_ID='-$Id$-'
# An exception raised by IRB.irb_abort
class Abort < Exception;end
@CONF = {}
@ -287,11 +335,14 @@ module IRB
# Displays current configuration.
#
# Modifing the configuration is achieved by sending a message to IRB.conf.
#
# See IRB@Configuration for more information.
def IRB.conf
@CONF
end
# IRB version method
# Returns the current version of IRB, including release version and last
# updated date.
def IRB.version
if v = @CONF[:VERSION] then return v end
@ -300,11 +351,16 @@ module IRB
@CONF[:VERSION] = format("irb %s(%s)", rv, @LAST_UPDATE_DATE)
end
# The current IRB::Context of the session, see IRB.conf
#
# irb
# irb(main):001:0> IRB.CurrentContext.irb_name = "foo"
# foo(main):002:0> IRB.conf[:MAIN_CONTEXT].irb_name #=> "foo"
def IRB.CurrentContext
IRB.conf[:MAIN_CONTEXT]
end
# initialize IRB and start TOP_LEVEL irb
# Initializes IRB and creates a new Irb.irb object at the +TOPLEVEL_BINDING+
def IRB.start(ap_path = nil)
$0 = File::basename(ap_path, ".rb") if ap_path
@ -333,7 +389,7 @@ module IRB
# print "\n"
end
# Calls each of the IRB.conf[:AT_EXIT] hooks when the current session quits.
# Calls each event hook of IRB.conf[:AT_EXIT] when the current session quits.
def IRB.irb_at_exit
@CONF[:AT_EXIT].each{|hook| hook.call}
end
@ -343,6 +399,9 @@ module IRB
throw :IRB_EXIT, ret
end
# Aborts then interrupts irb.
#
# Will raise an Abort exception, or the given +exception+.
def IRB.irb_abort(irb, exception = Abort)
if defined? Thread
irb.context.thread.raise exception, "abort then interrupt!"
@ -351,8 +410,8 @@ module IRB
end
end
# irb interpreter main routine
class Irb
# Creates a new irb session
def initialize(workspace = nil, input_method = nil, output_method = nil)
@context = Context.new(self, workspace, input_method, output_method)
@context.main.extend ExtendCommandBundle
@ -361,9 +420,12 @@ module IRB
@scanner = RubyLex.new
@scanner.exception_on_syntax_error = false
end
# Returns the current context of this irb session
attr_reader :context
# The lexer used by this irb session
attr_accessor :scanner
# Evaluates input for this session.
def eval_input
@scanner.set_prompt do
|ltype, indent, continue, line_no|
@ -462,6 +524,11 @@ module IRB
end
end
# Evaluates the given block using the given +path+ as the Context#irb_path
# and +name+ as the Context#irb_name.
#
# Used by the irb command +source+, see IRB@IRB+Sessions for more
# information.
def suspend_name(path = nil, name = nil)
@context.irb_path, back_path = path, @context.irb_path if path
@context.irb_name, back_name = name, @context.irb_name if name
@ -473,6 +540,11 @@ module IRB
end
end
# Evaluates the given block using the given +workspace+ as the
# Context#workspace.
#
# Used by the irb command +irb_load+, see IRB@IRB+Sessions for more
# information.
def suspend_workspace(workspace)
@context.workspace, back_workspace = workspace, @context.workspace
begin
@ -482,6 +554,11 @@ module IRB
end
end
# Evaluates the given block using the given +input_method+ as the
# Context#io.
#
# Used by the irb commands +source+ and +irb_load+, see IRB@IRB+Sessions
# for more information.
def suspend_input_method(input_method)
back_io = @context.io
@context.instance_eval{@io = input_method}
@ -492,6 +569,7 @@ module IRB
end
end
# Evaluates the given block using the given +context+ as the Context.
def suspend_context(context)
@context, back_context = context, @context
begin
@ -501,6 +579,7 @@ module IRB
end
end
# Handler for the signal SIGINT, see Kernel#trap for more information.
def signal_handle
unless @context.ignore_sigint?
print "\nabort!\n" if @context.verbose?
@ -522,6 +601,7 @@ module IRB
end
end
# Evaluates the given block using the given +status+.
def signal_status(status)
return yield if @signal_status == :IN_LOAD
@ -534,7 +614,7 @@ module IRB
end
end
def prompt(prompt, ltype, indent, line_no)
def prompt(prompt, ltype, indent, line_no) # :nodoc:
p = prompt.dup
p.gsub!(/%([0-9]+)?([a-zA-Z])/) do
case $2
@ -565,10 +645,12 @@ module IRB
p
end
def output_value
def output_value # :nodoc:
printf @context.return_format, @context.inspect_last_value
end
# Outputs the local variables to this current session, including
# #signal_status and #context, using IRB::Locale.
def inspect
ary = []
for iv in instance_variables
@ -585,7 +667,6 @@ module IRB
end
end
# Singleton method
def @CONF.inspect
IRB.version unless self[:VERSION]

View file

@ -9,10 +9,12 @@
require "readline"
module IRB
module InputCompletor
module InputCompletor # :nodoc:
@RCS_ID='-$Id$-'
# Set of reserved words used by Ruby, you should not use these for
# constants or variables
ReservedWords = [
"BEGIN", "END",
"alias", "and",
@ -208,6 +210,7 @@ module IRB
end
}
# Set of available operators in Ruby
Operators = ["%", "&", "*", "**", "+", "-", "/",
"<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>",
"[]", "[]=", "^", "!", "!=", "!~"]

View file

@ -12,6 +12,8 @@ require "irb/workspace"
require "irb/inspector"
module IRB
# A class that wraps the current state of the irb session, including the
# configuration of IRB.conf.
class Context
# Creates a new IRB context.
#
@ -101,28 +103,49 @@ module IRB
@debug_level = IRB.conf[:DEBUG_LEVEL]
end
# The top-level workspace, see WorkSpace#main
def main
@workspace.main
end
# The toplevel workspace, see #home_workspace
attr_reader :workspace_home
# WorkSpace in the current context
attr_accessor :workspace
# The current thread in this context
attr_reader :thread
# The current input method
#
# Can be either StdioInputMethod, ReadlineInputMethod, FileInputMethod or
# other specified when the context is created. See ::new for more
# information on +input_method+.
attr_accessor :io
# Current irb session
attr_accessor :irb
# A copy of the default <code>IRB.conf[:AP_NAME]</code>
attr_accessor :ap_name
# A copy of the default <code>IRB.conf[:RC]</code>
attr_accessor :rc
# A copy of the default <code>IRB.conf[:LOAD_MODULES]</code>
attr_accessor :load_modules
# Can be either name from <code>IRB.conf[:IRB_NAME]</code>, or the number of
# the current job set by JobManager, such as <code>irb#2</code>
attr_accessor :irb_name
# Can be either the #irb_name surrounded by parenthesis, or the
# +input_method+ passed to Context.new
attr_accessor :irb_path
# Whether +Readline+ is enabled or not.
#
# A copy of the default <code>IRB.conf[:USE_READLINE]</code>
#
# See #use_readline= for more information.
attr_reader :use_readline
# A copy of the default <code>IRB.conf[:INSPECT_MODE]</code>
attr_reader :inspect_mode
# A copy of the default <code>IRB.conf[:PROMPT_MODE]</code>
attr_reader :prompt_mode
# Standard IRB prompt
#
@ -138,7 +161,11 @@ module IRB
attr_accessor :prompt_c
# See IRB@Customizing+the+IRB+Prompt for more information.
attr_accessor :prompt_n
# Can be either the deafult <code>IRB.conf[:AUTO_INDENT]</code>, or the
# mode set by #prompt_mode=
attr_accessor :auto_indent_mode
# The format of the return statement, set by #prompt_mode= using the
# +:RETURN+ of the +mode+ passed to set the current #prompt_mode.
attr_accessor :return_format
# Whether <code>^C</code> (+control-c+) will be ignored or not.
@ -154,8 +181,20 @@ module IRB
#
# If set to +false+, <code>^D</code> will quit irb.
attr_accessor :ignore_eof
# Whether to echo the return value to output or not.
#
# Uses IRB.conf[:ECHO] if available, or defaults to +true+.
#
# puts "hello"
# # hello
# #=> nil
# IRB.CurrentContext.echo = false
# puts "omg"
# # omg
attr_accessor :echo
# Whether verbose messages are displayed or not.
#
# A copy of the default <code>IRB.conf[:VERBOSE]</code>
attr_accessor :verbose
# The debug level of irb
#
@ -194,18 +233,26 @@ module IRB
end
end
# Whether #verbose? is +true+, and +input_method+ is either
# StdioInputMethod or ReadlineInputMethod, see #io for more information.
def prompting?
verbose? || (STDIN.tty? && @io.kind_of?(StdioInputMethod) ||
(defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod)))
end
# The return value of the last statement evaluated.
attr_reader :last_value
# Sets the return value from the last statement evaluated in this context
# to #last_value.
def set_last_value(value)
@last_value = value
@workspace.evaluate self, "_ = IRB.CurrentContext.last_value"
end
# Sets the +mode+ of the prompt in this context.
#
# See IRB@Customizing+the+IRB+Prompt for more information.
def prompt_mode=(mode)
@prompt_mode = mode
pconf = IRB.conf[:PROMPT][mode]
@ -221,10 +268,13 @@ module IRB
end
end
# Whether #inspect_mode is set or not, see #inspect_mode= for more detail.
def inspect?
@inspect_mode.nil? or @inspect_mode
end
# Whether #io uses a File for the +input_method+ passed when creating the
# current context, see ::new
def file_input?
@io.class == FileInputMethod
end
@ -236,6 +286,8 @@ module IRB
# +nil+:: inspect mode in non-math mode,
# non-inspect mode in math mode
#
# See IRB::INSPECTORS for more information.
#
# Can also be set using the +--inspect+ and +--noinspect+ command line
# options.
#
@ -311,18 +363,19 @@ module IRB
SLex.debug_level = value
end
# Whether or not debug mode is enabled, see #debug_level=.
def debug?
@debug_level > 0
end
def evaluate(line, line_no)
def evaluate(line, line_no) # :nodoc:
@line_no = line_no
set_last_value(@workspace.evaluate(self, line, irb_path, line_no))
# @workspace.evaluate("_ = IRB.conf[:MAIN_CONTEXT]._")
# @_ = @workspace.evaluate(line, irb_path, line_no)
end
def inspect_last_value
def inspect_last_value # :nodoc:
@inspect_method.inspect_value(@last_value)
end
@ -332,12 +385,12 @@ module IRB
IRB.irb_exit(@irb, ret)
end
NOPRINTING_IVARS = ["@last_value"]
NO_INSPECTING_IVARS = ["@irb", "@io"]
IDNAME_IVARS = ["@prompt_mode"]
NOPRINTING_IVARS = ["@last_value"] # :nodoc:
NO_INSPECTING_IVARS = ["@irb", "@io"] # :nodoc:
IDNAME_IVARS = ["@prompt_mode"] # :nodoc:
alias __inspect__ inspect
def inspect
def inspect # :nodoc:
array = []
for ivar in instance_variables.sort{|e1, e2| e1 <=> e2}
ivar = ivar.to_s

View file

@ -12,6 +12,7 @@
module IRB # :nodoc:
class Context
# Inherited from +TOPLEVEL_BINDING+.
def home_workspace
if defined? @home_workspace
@home_workspace

View file

@ -15,6 +15,7 @@ module IRB # :nodoc:
NOPRINTING_IVARS.push "@eval_history_values"
# See #set_last_value
alias _set_last_value set_last_value
def set_last_value(value)
@ -57,7 +58,7 @@ module IRB # :nodoc:
end
end
class History
class History # :nodoc:
@RCS_ID='-$Id$-'
def initialize(size = 16)

View file

@ -14,12 +14,16 @@ module IRB # :nodoc:
# Raised in the event of an exception in a file loaded from an Irb session
class LoadAbort < Exception;end
# Provides a few commands for loading files within an irb session.
#
# See ExtendCommandBundle for more information.
module IrbLoader
@RCS_ID='-$Id$-'
alias ruby_load load
alias ruby_require require
# Loads the given file similarly to Kernel#load
def irb_load(fn, priv = nil)
path = search_file_from_ruby_path(fn)
raise LoadError, "No such file to load -- #{fn}" unless path
@ -27,7 +31,7 @@ module IRB # :nodoc:
load_file(path, priv)
end
def search_file_from_ruby_path(fn)
def search_file_from_ruby_path(fn) # :nodoc:
if /^#{Regexp.quote(File::Separator)}/ =~ fn
return fn if File.exist?(fn)
return nil
@ -41,6 +45,9 @@ module IRB # :nodoc:
return nil
end
# Loads a given file in the current session and displays the source lines
#
# See Irb#suspend_input_method for more information.
def source_file(path)
irb.suspend_name(path, File.basename(path)) do
irb.suspend_input_method(FileInputMethod.new(path)) do
@ -60,6 +67,9 @@ module IRB # :nodoc:
end
end
# Loads the given file in the current session's context and evaluates it.
#
# See Irb#suspend_input_method for more information.
def load_file(path, priv = nil)
irb.suspend_name(path, File.basename(path)) do
@ -88,7 +98,7 @@ module IRB # :nodoc:
end
end
def old
def old # :nodoc:
back_io = @io
back_path = @irb_path
back_name = @irb_name

View file

@ -12,44 +12,61 @@ IRB.fail CantShiftToMultiIrbMode unless defined?(Thread)
require "thread"
module IRB
# job management class
class JobManager
@RCS_ID='-$Id$-'
# Creates a new JobManager object
def initialize
# @jobs = [[thread, irb],...]
@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?
@ -60,6 +77,12 @@ module IRB
@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)
@ -68,6 +91,20 @@ module IRB
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
@ -83,6 +120,7 @@ module IRB
job
end
# Deletes the job at the given +key+.
def delete(key)
case key
when Integer
@ -106,6 +144,7 @@ module IRB
@jobs.push assoc
end
# Outputs a list of jobs, see the irb command +irb_jobs+, or +jobs+.
def inspect
ary = []
@jobs.each_index do
@ -135,10 +174,12 @@ module IRB
@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

View file

@ -11,21 +11,24 @@
require "readline"
module IRB
module HistorySavingAbility
module HistorySavingAbility # :nodoc:
@RCS_ID='-$Id$-'
end
class Context
def init_save_history
def init_save_history# :nodoc:
unless (class<<@io;self;end).include?(HistorySavingAbility)
@io.extend(HistorySavingAbility)
end
end
# A copy of the default <code>IRB.conf[:SAVE_HISTORY]</code>
def save_history
IRB.conf[:SAVE_HISTORY]
end
# Sets <code>IRB.conf[:SAVE_HISTORY]</code> to the given +val+ and calls
# #init_save_history with this context.
def save_history=(val)
IRB.conf[:SAVE_HISTORY] = val
if val
@ -35,16 +38,18 @@ module IRB
end
end
# A copy of the default <code>IRB.conf[:HISTORY_FILE]</code>
def history_file
IRB.conf[:HISTORY_FILE]
end
# Set <code>IRB.conf[:HISTORY_FILE]</code> to the given +hist+.
def history_file=(hist)
IRB.conf[:HISTORY_FILE] = hist
end
end
module HistorySavingAbility
module HistorySavingAbility # :nodoc:
include Readline
# def HistorySavingAbility.create_finalizer

View file

@ -23,9 +23,16 @@ module IRB
end
class Context
# Whether Tracer is used when evaluating statements in this context.
#
# See +lib/tracer.rb+ for more information.
attr_reader :use_tracer
alias use_tracer? use_tracer
# Sets whether or not to use the Tracer library when evaluating statements
# in this context.
#
# See +lib/tracer.rb+ for more information.
def use_tracer=(opt)
if opt
Tracer.set_get_line_procs(@irb_path) {
@ -41,6 +48,10 @@ module IRB
class WorkSpace
alias __evaluate__ evaluate
# Evaluate the context of this workspace and use the Tracer library to
# output the exact lines of code are being executed in chronological order.
#
# See +lib/tracer.rb+ for more information.
def evaluate(context, statements, file = nil, line = nil)
if context.use_tracer? && file != nil && line != nil
Tracer.on

View file

@ -18,16 +18,16 @@ class Object
end
module IRB
# :stopdoc:
module ExtendCommandBundle
# Loads the given file similarly to Kernel#load, see IrbLoader#irb_load
def irb_load(*opts, &b)
ExtendCommand::Load.execute(irb_context, *opts, &b)
end
# Loads the given file similarly to Kernel#require
def irb_require(*opts, &b)
ExtendCommand::Require.execute(irb_context, *opts, &b)
end
end
# :startdoc:
class Context

View file

@ -12,11 +12,12 @@
module IRB # :nodoc:
class Context
# Size of the current WorkSpace stack
def irb_level
workspace_stack.size
end
# Workspaces in the current stack
# WorkSpaces in the current stack
def workspaces
if defined? @workspaces
@workspaces

View file

@ -9,16 +9,22 @@
#
#
module IRB # :nodoc:
#
# IRB extended command
#
# Installs the default irb extensions command bundle.
module ExtendCommandBundle
EXCB = ExtendCommandBundle
EXCB = ExtendCommandBundle # :nodoc:
# See #install_alias_method.
NO_OVERRIDE = 0
# See #install_alias_method.
OVERRIDE_PRIVATE_ONLY = 0x01
# See #install_alias_method.
OVERRIDE_ALL = 0x02
# Quits the current irb context
#
# +ret+ is the optional signal or message to send to Context#exit
#
# Same as <code>IRB.CurrentContext.exit</code>.
def irb_exit(ret = 0)
irb_context.exit(ret)
end
@ -108,13 +114,33 @@ module IRB # :nodoc:
]
# Installs the default irb commands:
#
# +irb_current_working_workspace+:: Context#main
# +irb_change_workspace+:: Context#change_workspace
# +irb_workspaces+:: Context#workspaces
# +irb_push_workspace+:: Context#push_workspace
# +irb_pop_workspace+:: Context#pop_workspace
# +irb_load+:: #irb_load
# +irb_require+:: #irb_require
# +irb_source+:: IrbLoader#source_file
# +irb+:: IRB.irb
# +irb_jobs+:: JobManager
# +irb_fg+:: JobManager#switch
# +irb_kill+:: JobManager#kill
# +irb_help+:: IRB@Command+line+options
def self.install_extend_commands
for args in @EXTEND_COMMANDS
def_extend_command(*args)
end
end
# aliases = [commands_alias, flag], ...
# Evaluate the given +cmd_name+ on the given +cmd_class+ Class.
#
# Will also define any given +aliases+ for the method.
#
# The optional +load_file+ parameter will be required within the method
# definition.
def self.def_extend_command(cmd_name, cmd_class, load_file = nil, *aliases)
case cmd_class
when Symbol
@ -154,7 +180,8 @@ module IRB # :nodoc:
end
end
# override = {NO_OVERRIDE, OVERRIDE_PRIVATE_ONLY, OVERRIDE_ALL}
# Installs alias methods for the default irb commands, see
# ::install_extend_commands.
def install_alias_method(to, from, override = NO_OVERRIDE)
to = to.id2name unless to.kind_of?(String)
from = from.id2name unless from.kind_of?(String)
@ -175,10 +202,12 @@ module IRB # :nodoc:
end
end
def self.irb_original_method_name(method_name)
def self.irb_original_method_name(method_name) # :nodoc:
"irb_" + method_name + "_org"
end
# Installs alias methods for the default irb commands on the given object
# using #install_alias_method.
def self.extend_object(obj)
unless (class << obj; ancestors; end).include?(EXCB)
super
@ -191,9 +220,9 @@ module IRB # :nodoc:
install_extend_commands
end
# extension support for Context
# Extends methods for the Context module
module ContextExtender
CE = ContextExtender
CE = ContextExtender # :nodoc:
@EXTEND_COMMANDS = [
[:eval_history=, "irb/ext/history.rb"],
@ -203,12 +232,23 @@ module IRB # :nodoc:
[:save_history=, "irb/ext/save-history.rb"],
]
# Installs the default context extensions as irb commands:
#
# Context#eval_history=:: +irb/ext/history.rb+
# Context#use_tracer=:: +irb/ext/tracer.rb+
# Context#math_mode=:: +irb/ext/math-mode.rb+
# Context#use_loader=:: +irb/ext/use-loader.rb+
# Context#save_history=:: +irb/ext/save-history.rb+
def self.install_extend_commands
for args in @EXTEND_COMMANDS
def_extend_command(*args)
end
end
# Evaluate the given +command+ from the given +load_file+ on the Context
# module.
#
# Will also define any given +aliases+ for the method.
def self.def_extend_command(cmd_name, load_file, *aliases)
line = __LINE__; Context.module_eval %[
def #{cmd_name}(*opts, &b)
@ -225,7 +265,10 @@ module IRB # :nodoc:
CE.install_extend_commands
end
# A convenience module for extending Ruby methods.
module MethodExtender
# Extends the given +base_method+ with a prefix call to the given
# +extend_method+.
def def_pre_proc(base_method, extend_method)
base_method = base_method.to_s
extend_method = extend_method.to_s
@ -240,6 +283,8 @@ module IRB # :nodoc:
]
end
# Extends the given +base_method+ with a postfix call to the given
# +extend_method+.
def def_post_proc(base_method, extend_method)
base_method = base_method.to_s
extend_method = extend_method.to_s
@ -254,7 +299,13 @@ module IRB # :nodoc:
]
end
# return #{prefix}#{name}#{postfix}<num>
# Returns a unique method name to use as an alias for the given +name+.
#
# Usually returns <code>#{prefix}#{name}#{postfix}<num></code>, example:
#
# new_alias_name('foo') #=> __alias_of__foo__
# def bar; end
# new_alias_name('bar') #=> __alias_of__bar__2
def new_alias_name(name, prefix = "__alias_of__", postfix = "__")
base_name = "#{prefix}#{name}#{postfix}"
all_methods = instance_methods(true) + private_instance_methods(true)

View file

@ -17,13 +17,17 @@ module IRB
def_exception :FrameOverflow, "frame overflow"
def_exception :FrameUnderflow, "frame underflow"
# Default number of stack frames
INIT_STACK_TIMES = 3
# Default number of frames offset
CALL_STACK_OFFSET = 3
# Creates a new stack frame
def initialize
@frames = [TOPLEVEL_BINDING] * INIT_STACK_TIMES
end
# Used by Kernel#set_trace_func to register each event in the call stack
def trace_func(event, file, line, id, binding)
case event
when 'call', 'class'
@ -33,27 +37,37 @@ module IRB
end
end
# Returns the +n+ number of frames on the call stack from the last frame
# initialized.
#
# Raises FrameUnderflow if there are no frames in the given stack range.
def top(n = 0)
bind = @frames[-(n + CALL_STACK_OFFSET)]
Fail FrameUnderflow unless bind
bind
end
# Returns the +n+ number of frames on the call stack from the first frame
# initialized.
#
# Raises FrameOverflow if there are no frames in the given stack range.
def bottom(n = 0)
bind = @frames[n]
Fail FrameOverflow unless bind
bind
end
# singleton functions
# Convenience method for Frame#bottom
def Frame.bottom(n = 0)
@backtrace.bottom(n)
end
# Convenience method for Frame#top
def Frame.top(n = 0)
@backtrace.top(n)
end
# Returns the binding context of the caller from the last frame initialized
def Frame.sender
eval "self", @backtrace.top
end

View file

@ -12,6 +12,7 @@
require 'irb/magic-file'
module IRB
# Outputs the irb help message, see IRB@Command+line+options.
def IRB.print_usage
lc = IRB.conf[:LC_MESSAGES]
path = lc.find("irb/help-message")

View file

@ -12,34 +12,39 @@ require 'irb/src_encoding'
require 'irb/magic-file'
module IRB
#
# InputMethod
# StdioInputMethod
# FileInputMethod
# (ReadlineInputMethod)
#
STDIN_FILE_NAME = "(line)"
STDIN_FILE_NAME = "(line)" # :nodoc:
class InputMethod
@RCS_ID='-$Id$-'
# Creates a new input method object
def initialize(file = STDIN_FILE_NAME)
@file_name = file
end
# The file name of this input method, usually given during initialization.
attr_reader :file_name
# The irb prompt associated with this input method
attr_accessor :prompt
# Reads the next line from this input method.
#
# See IO#gets for more information.
def gets
IRB.fail NotImplementedError, "gets"
end
public :gets
# Whether this input method is still readable when there is no more data to
# read.
#
# See IO#eof for more information.
def readable_atfer_eof?
false
end
end
class StdioInputMethod < InputMethod
# Creates a new input method object
def initialize
super
@line_no = 0
@ -48,40 +53,67 @@ module IRB
@stdout = IO.open(STDOUT.to_i, 'w', :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-")
end
# Reads the next line from this input method.
#
# See IO#gets for more information.
def gets
print @prompt
line = @stdin.gets
@line[@line_no += 1] = line
end
# Whether the end of this input method has been reached, returns +true+ if
# there is no more data to read.
#
# See IO#eof? for more information.
def eof?
@stdin.eof?
end
# Whether this input method is still readable when there is no more data to
# read.
#
# See IO#eof for more information.
def readable_atfer_eof?
true
end
# Returns the current line number for #io.
#
# #line counts the number of times #gets is called.
#
# See IO#lineno for more information.
def line(line_no)
@line[line_no]
end
# The external encoding for standard input.
def encoding
@stdin.external_encoding
end
end
# Use a File for IO with irb, see InputMethod
class FileInputMethod < InputMethod
# Creates a new input method object
def initialize(file)
super
@io = IRB::MagicFile.open(file)
end
# The file name of this input method, usually given during initialization.
attr_reader :file_name
# Whether the end of this input method has been reached, returns +true+ if
# there is no more data to read.
#
# See IO#eof? for more information.
def eof?
@io.eof?
end
# Reads the next line from this input method.
#
# See IO#gets for more information.
def gets
print @prompt
l = @io.gets
@ -89,6 +121,7 @@ module IRB
l
end
# The external encoding for standard input.
def encoding
@io.external_encoding
end
@ -98,6 +131,7 @@ module IRB
require "readline"
class ReadlineInputMethod < InputMethod
include Readline
# Creates a new input method object using Readline
def initialize
super
@ -109,6 +143,9 @@ module IRB
@stdout = IO.open(STDOUT.to_i, 'w', :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-")
end
# Reads the next line from this input method.
#
# See IO#gets for more information.
def gets
Readline.input = @stdin
Readline.output = @stdout
@ -121,18 +158,32 @@ module IRB
end
end
# Whether the end of this input method has been reached, returns +true+
# if there is no more data to read.
#
# See IO#eof? for more information.
def eof?
@eof
end
# Whether this input method is still readable when there is no more data to
# read.
#
# See IO#eof for more information.
def readable_atfer_eof?
true
end
# Returns the current line number for #io.
#
# #line counts the number of times #gets is called.
#
# See IO#lineno for more information.
def line(line_no)
@line[line_no]
end
# The external encoding for standard input.
def encoding
@stdin.external_encoding
end

View file

@ -12,37 +12,73 @@
module IRB # :nodoc:
# Convenience method to create a new Inspector, using the given +inspect+
# proc, and optional +init+ proc and passes them to Inspector.new
#
# irb(main):001:0> ins = IRB::Inspector(proc{ |v| "omg! #{v}" })
# irb(main):001:0> IRB.CurrentContext.inspect_mode = ins # => omg! #<IRB::Inspector:0x007f46f7ba7d28>
# irb(main):001:0> "what?" #=> omg! what?
#
def IRB::Inspector(inspect, init = nil)
Inspector.new(inspect, init)
end
# An irb inspector
#
# In order to create your own custom inspector there are two things you
# should be aware of:
#
# Inspector uses #inspect_value, or +inspect_proc+, for output of return values.
#
# This also allows for an optional #init+, or +init_proc+, which is called
# when the inspector is activated.
#
# Knowing this, you can create a rudimentary inspector as follows:
#
# irb(main):001:0> ins = IRB::Inspector.new(proc{ |v| "omg! #{v}" })
# irb(main):001:0> IRB.CurrentContext.inspect_mode = ins # => omg! #<IRB::Inspector:0x007f46f7ba7d28>
# irb(main):001:0> "what?" #=> omg! what?
#
class Inspector
# Creates a new inspector object, using the given +inspect_proc+ when
# output return values in irb.
def initialize(inspect_proc, init_proc = nil)
@init = init_proc
@inspect = inspect_proc
end
# Proc to call when the inspector is activated, good for requiring
# dependant libraries.
def init
@init.call if @init
end
# Proc to call when the input is evaluated and output in irb.
def inspect_value(v)
@inspect.call(v)
end
end
# Default inspectors available to irb, this includes:
#
# +:pp+:: Using Kernel#pretty_inspect
# +:yaml+:: Using YAML.dump
# +:marshal+:: Using Marshal.dump
INSPECTORS = {}
# Determines the inspector to use where +inspector+ is one of the keys passed
# during inspector definition.
def INSPECTORS.keys_with_inspector(inspector)
select{|k,v| v == inspector}.collect{|k, v| k}
end
# ex)
# INSPECTORS.def_inspector(key, init_p=nil){|v| v.inspect}
# INSPECTORS.def_inspector([key1,..], init_p=nil){|v| v.inspect}
# INSPECTORS.def_inspector(key, inspector)
# INSPECTORS.def_inspector([key1,...], inspector)
# Example
#
# INSPECTORS.def_inspector(key, init_p=nil){|v| v.inspect}
# INSPECTORS.def_inspector([key1,..], init_p=nil){|v| v.inspect}
# INSPECTORS.def_inspector(key, inspector)
# INSPECTORS.def_inspector([key1,...], inspector)
def INSPECTORS.def_inspector(key, arg=nil, &block)
# if !block_given?
# case arg

View file

@ -13,6 +13,7 @@ require "e2mmap"
require "irb/output-method"
module IRB
# An output formatter used internally by the lexer.
module Notifier
extend Exception2MessageMapper
def_exception :ErrUndefinedNotifier,
@ -20,59 +21,100 @@ module IRB
def_exception :ErrUnrecognizedLevel,
"unrecognized notifier level: %s is specified"
# Define a new Notifier output source, returning a new CompositeNotifier
# with the given +prefix+ and +output_method+.
#
# The optional +prefix+ will be appended to all objects being inspected
# during output, using the given +output_method+ as the output source. If
# no +output_method+ is given, StdioOuputMethod will be used, and all
# expressions will be sent directly to STDOUT without any additional
# formatting.
def def_notifier(prefix = "", output_method = StdioOutputMethod.new)
CompositeNotifier.new(prefix, output_method)
end
module_function :def_notifier
# An abstract class, or superclass, for CompositeNotifier and
# LeveledNotifier to inherit. It provides several wrapper methods for the
# OutputMethod object used by the Notifier.
class AbstractNotifier
# Creates a new Notifier object
def initialize(prefix, base_notifier)
@prefix = prefix
@base_notifier = base_notifier
end
# The +prefix+ for this Notifier, which is appended to all objects being
# inspected during output.
attr_reader :prefix
# A wrapper method used to determine whether notifications are enabled.
#
# Defaults to +true+.
def notify?
true
end
# See OutputMethod#print for more detail.
def print(*opts)
@base_notifier.print prefix, *opts if notify?
end
# See OutputMethod#printn for more detail.
def printn(*opts)
@base_notifier.printn prefix, *opts if notify?
end
# See OutputMethod#printf for more detail.
def printf(format, *opts)
@base_notifier.printf(prefix + format, *opts) if notify?
end
# See OutputMethod#puts for more detail.
def puts(*objs)
if notify?
@base_notifier.puts(*objs.collect{|obj| prefix + obj.to_s})
end
end
# Same as #ppx, except it uses the #prefix given during object
# initialization.
# See OutputMethod#ppx for more detail.
def pp(*objs)
if notify?
@base_notifier.ppx @prefix, *objs
end
end
# Same as #pp, except it concatenates the given +prefix+ with the #prefix
# given during object initialization.
#
# See OutputMethod#ppx for more detail.
def ppx(prefix, *objs)
if notify?
@base_notifier.ppx @prefix+prefix, *objs
end
end
# Execute the given block if notifications are enabled.
def exec_if
yield(@base_notifier) if notify?
end
end
# A class that can be used to create a group of notifier objects with the
# intent of representing a leveled notification system for irb.
#
# This class will allow you to generate other notifiers, and assign them
# the appropriate level for output.
#
# The Notifier class provides a class-method Notifier.def_notifier to
# create a new composite notifier. Using the first composite notifier
# object you create, sibling notifiers can be initialized with
# #def_notifier.
class CompositeNotifier<AbstractNotifier
# Create a new composite notifier object with the given +prefix+, and
# +base_notifier+ to use for output.
def initialize(prefix, base_notifier)
super
@ -80,17 +122,39 @@ module IRB
@level_notifier = D_NOMSG
end
# List of notifiers in the group
attr_reader :notifiers
# Creates a new LeveledNotifier in the composite #notifiers group.
#
# The given +prefix+ will be assigned to the notifier, and +level+ will
# be used as the index of the #notifiers Array.
#
# This method returns the newly created instance.
def def_notifier(level, prefix = "")
notifier = LeveledNotifier.new(self, level, prefix)
@notifiers[level] = notifier
notifier
end
# Returns the leveled notifier for this object
attr_reader :level_notifier
alias level level_notifier
# Sets the leveled notifier for this object.
#
# When the given +value+ is an instance of AbstractNotifier,
# #level_notifier is set to the given object.
#
# When an Integer is given, #level_notifier is set to the notifier at the
# index +value+ in the #notifiers Array.
#
# If no notifier exists at the index +value+ in the #notifiers Array, an
# ErrUndefinedNotifier exception is raised.
#
# An ErrUnrecognizedLevel exception is raised if the given +value+ is not
# found in the existing #notifiers Array, or an instance of
# AbstractNotifier
def level_notifier=(value)
case value
when AbstractNotifier
@ -107,38 +171,61 @@ module IRB
alias level= level_notifier=
end
# A leveled notifier is comparable to the composite group from
# CompositeNotifier#notifiers.
class LeveledNotifier<AbstractNotifier
include Comparable
# Create a new leveled notifier with the given +base+, and +prefix+ to
# send to AbstractNotifier.new
#
# The given +level+ is used to compare other leveled notifiers in the
# CompositeNotifier group to determine whether or not to output
# notifications.
def initialize(base, level, prefix)
super(prefix, base)
@level = level
end
# The current level of this notifier object
attr_reader :level
# Compares the level of this notifier object with the given +other+
# notifier.
#
# See the Comparable module for more information.
def <=>(other)
@level <=> other.level
end
# Whether to output messages to the output method, depending on the level
# of this notifier object.
def notify?
@base_notifier.level >= self
end
end
# NoMsgNotifier is a LeveledNotifier that's used as the default notifier
# when creating a new CompositeNotifier.
#
# This notifier is used as the +zero+ index, or level +0+, for
# CompositeNotifier#notifiers, and will not output messages of any sort.
class NoMsgNotifier<LeveledNotifier
# Creates a new notifier that should not be used to output messages.
def initialize
@base_notifier = nil
@level = 0
@prefix = ""
end
# Ensures notifications are ignored, see AbstractNotifier#notify? for
# more information.
def notify?
false
end
end
D_NOMSG = NoMsgNotifier.new
D_NOMSG = NoMsgNotifier.new # :nodoc:
end
end

View file

@ -12,21 +12,25 @@
require "e2mmap"
module IRB
# OutputMethod
# StdioOutputMethod
# An abstract output class for IO in irb. This is mainly used internally by
# IRB::Notifier. You can define your own output method to use with Irb.new,
# or Context.new
class OutputMethod
@RCS_ID='-$Id$-'
# Open this method to implement your own output method, raises a
# NotImplementedError if you don't define #print in your own class.
def print(*opts)
IRB.fail NotImplementedError, "print"
end
# Prints the given +opts+, with a newline delimiter.
def printn(*opts)
print opts.join(" "), "\n"
end
# extend printf
# Extends IO#printf to format the given +opts+ for Kernel#sprintf using
# #parse_printf_format
def printf(format, *opts)
if /(%*)%I/ =~ format
format, opts = parse_printf_format(format, opts)
@ -34,16 +38,22 @@ module IRB
print sprintf(format, *opts)
end
# %
# <flag> [#0- +]
# <minimum field width> (\*|\*[1-9][0-9]*\$|[1-9][0-9]*)
# <precision>.(\*|\*[1-9][0-9]*\$|[1-9][0-9]*|)?
# #<length modifier>(hh|h|l|ll|L|q|j|z|t)
# <conversion specifier>[diouxXeEfgGcsb%]
# Returns an array of the given +format+ and +opts+ to be used by
# Kernel#sprintf, if there was a successful Regexp match in the given
# +format+ from #printf
#
# %
# <flag> [#0- +]
# <minimum field width> (\*|\*[1-9][0-9]*\$|[1-9][0-9]*)
# <precision>.(\*|\*[1-9][0-9]*\$|[1-9][0-9]*|)?
# #<length modifier>(hh|h|l|ll|L|q|j|z|t)
# <conversion specifier>[diouxXeEfgGcsb%]
def parse_printf_format(format, opts)
return format, opts if $1.size % 2 == 1
end
# Calls #print on each element in the given +objs+, followed by a newline
# character.
def puts(*objs)
for obj in objs
print(*obj)
@ -51,17 +61,27 @@ module IRB
end
end
# Prints the given +objs+ calling Object#inspect on each.
#
# See #puts for more detail.
def pp(*objs)
puts(*objs.collect{|obj| obj.inspect})
end
# Prints the given +objs+ calling Object#inspect on each and appending the
# given +prefix+.
#
# See #puts for more detail.
def ppx(prefix, *objs)
puts(*objs.collect{|obj| prefix+obj.inspect})
end
end
# A standard output printer
class StdioOutputMethod<OutputMethod
# Prints the given +opts+ to standard output, see IO#print for more
# information.
def print(*opts)
STDOUT.print(*opts)
end

View file

@ -75,9 +75,13 @@ EOF
eval("_=nil", @binding)
end
# The Binding of this workspace
attr_reader :binding
# The top-level workspace of this context, also available as
# <code>IRB.conf[:__MAIN__]</code>
attr_reader :main
# Evaluate the given +statements+ within the context of this workspace.
def evaluate(context, statements, file = __FILE__, line = __LINE__)
eval(statements, @binding, file, line)
end

View file

@ -12,9 +12,46 @@
require "irb"
require "irb/frame"
# An example printer for irb.
#
# It's much like the standard library PrettyPrint, that shows the value of each
# expression as it runs.
#
# In order to use this library, you must first require it:
#
# require 'irb/xmp'
#
# Now, you can take advantage of the Object#xmp convenience method.
#
# xmp <<END
# foo = "bar"
# baz = 42
# END
# #=> foo = "bar"
# #==>"bar"
# #=> baz = 42
# #==>42
#
# You can also create an XMP object, with an optional binding to print
# expressions in the given binding:
#
# ctx = binding
# x = XMP.new ctx
# x.puts
# #=> today = "a good day"
# #==>"a good day"
# ctx.eval 'today # is what?'
# #=> "a good day"
class XMP
@RCS_ID='-$Id$-'
# Creates a new XMP object.
#
# The top-level binding or, optional +bind+ parameter will be used when
# creating the workspace. See WorkSpace.new for more information.
#
# This uses the +:XMP+ prompt mode, see IRB@Customizing+the+IRB+Prompt for
# full detail.
def initialize(bind = nil)
IRB.init_config(nil)
#IRB.parse_opts
@ -32,6 +69,17 @@ class XMP
IRB.conf[:MAIN_CONTEXT] = @irb.context
end
# Evaluates the given +exps+, for example:
#
# require 'irb/xmp'
# x = XMP.new
#
# x.puts '{:a => 1, :b => 2, :c => 3}'
# #=> {:a => 1, :b => 2, :c => 3}
# # ==>{:a=>1, :b=>2, :c=>3}
# x.puts 'foo = "bar"'
# # => foo = "bar"
# # ==>"bar"
def puts(exps)
@io.puts exps
@ -51,16 +99,22 @@ class XMP
end
end
# A custom InputMethod class used by XMP for evaluating string io.
class StringInputMethod < IRB::InputMethod
# Creates a new StringInputMethod object
def initialize
super
@exps = []
end
# Whether there are any expressions left in this printer.
def eof?
@exps.empty?
end
# Reads the next expression from this printer.
#
# See IO#gets for more information.
def gets
while l = @exps.shift
next if /^\s+$/ =~ l
@ -71,6 +125,10 @@ class XMP
l
end
# Concatenates all expressions in this printer, separated by newlines.
#
# An Encoding::CompatibilityError is raised of the given +exps+'s encoding
# doesn't match the previous expression evaluated.
def puts(exps)
if @encoding and exps.encoding != @encoding
enc = Encoding.compatible?(@exps.join("\n"), exps)
@ -85,10 +143,28 @@ class XMP
@exps.concat exps.split(/\n/)
end
# Returns the encoding of last expression printed by #puts.
attr_reader :encoding
end
end
# A convenience method that's only available when the you require the IRB::XMP standard library.
#
# Creates a new XMP object, using the given expressions as the +exps+
# parameter, and optional binding as +bind+ or uses the top-level binding. Then
# evaluates the given expressions using the +:XMP+ prompt mode.
#
# For example:
#
# require 'irb/xmp'
# ctx = binding
# xmp 'foo = "bar"', ctx
# #=> foo = "bar"
# #==>"bar"
# ctx.eval 'foo'
# #=> "bar"
#
# See XMP.new for more information.
def xmp(exps, bind = nil)
bind = IRB::Frame.top(1) unless bind
xmp = XMP.new(bind)