From 7e9eb32669348b7e0a5775c8e0fc9566be11fc31 Mon Sep 17 00:00:00 2001 From: zzak Date: Fri, 21 Dec 2012 05:45:50 +0000 Subject: * 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 --- lib/irb.rb | 97 +++++++++++++++++++++++++++++++++++++++++---- lib/irb/completion.rb | 5 ++- lib/irb/context.rb | 65 +++++++++++++++++++++++++++--- lib/irb/ext/change-ws.rb | 1 + lib/irb/ext/history.rb | 3 +- lib/irb/ext/loader.rb | 14 ++++++- lib/irb/ext/multi-irb.rb | 43 +++++++++++++++++++- lib/irb/ext/save-history.rb | 11 +++-- lib/irb/ext/tracer.rb | 11 +++++ lib/irb/ext/use-loader.rb | 4 +- lib/irb/ext/workspaces.rb | 3 +- lib/irb/extend-command.rb | 71 ++++++++++++++++++++++++++++----- lib/irb/frame.rb | 16 +++++++- lib/irb/help.rb | 1 + lib/irb/input-method.rb | 65 ++++++++++++++++++++++++++---- lib/irb/inspector.rb | 48 +++++++++++++++++++--- lib/irb/notifier.rb | 89 ++++++++++++++++++++++++++++++++++++++++- lib/irb/output-method.rb | 40 ++++++++++++++----- lib/irb/workspace.rb | 4 ++ lib/irb/xmp.rb | 76 +++++++++++++++++++++++++++++++++++ 20 files changed, 607 insertions(+), 60 deletions(-) (limited to 'lib') diff --git a/lib/irb.rb b/lib/irb.rb index e56b69bae0..d212a314f8 100644 --- a/lib/irb.rb +++ b/lib/irb.rb @@ -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] diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb index 3f886f507c..3b4a07186f 100644 --- a/lib/irb/completion.rb +++ b/lib/irb/completion.rb @@ -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 = ["%", "&", "*", "**", "+", "-", "/", "<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>", "[]", "[]=", "^", "!", "!=", "!~"] diff --git a/lib/irb/context.rb b/lib/irb/context.rb index 0da8395640..cd191d253e 100644 --- a/lib/irb/context.rb +++ b/lib/irb/context.rb @@ -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 IRB.conf[:AP_NAME] attr_accessor :ap_name + # A copy of the default IRB.conf[:RC] attr_accessor :rc + # A copy of the default IRB.conf[:LOAD_MODULES] attr_accessor :load_modules + # Can be either name from IRB.conf[:IRB_NAME], or the number of + # the current job set by JobManager, such as irb#2 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 IRB.conf[:USE_READLINE] + # # See #use_readline= for more information. attr_reader :use_readline + # A copy of the default IRB.conf[:INSPECT_MODE] attr_reader :inspect_mode + # A copy of the default IRB.conf[:PROMPT_MODE] 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 IRB.conf[:AUTO_INDENT], 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 ^C (+control-c+) will be ignored or not. @@ -154,8 +181,20 @@ module IRB # # If set to +false+, ^D 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 IRB.conf[:VERBOSE] 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 diff --git a/lib/irb/ext/change-ws.rb b/lib/irb/ext/change-ws.rb index a28754e378..ce921eb5e3 100644 --- a/lib/irb/ext/change-ws.rb +++ b/lib/irb/ext/change-ws.rb @@ -12,6 +12,7 @@ module IRB # :nodoc: class Context + # Inherited from +TOPLEVEL_BINDING+. def home_workspace if defined? @home_workspace @home_workspace diff --git a/lib/irb/ext/history.rb b/lib/irb/ext/history.rb index 4d036e7cf0..3239c57a6c 100644 --- a/lib/irb/ext/history.rb +++ b/lib/irb/ext/history.rb @@ -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) diff --git a/lib/irb/ext/loader.rb b/lib/irb/ext/loader.rb index 2058a2fa29..6cdc8ec898 100644 --- a/lib/irb/ext/loader.rb +++ b/lib/irb/ext/loader.rb @@ -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 diff --git a/lib/irb/ext/multi-irb.rb b/lib/irb/ext/multi-irb.rb index 473a4361b7..e49a158fa3 100644 --- a/lib/irb/ext/multi-irb.rb +++ b/lib/irb/ext/multi-irb.rb @@ -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 diff --git a/lib/irb/ext/save-history.rb b/lib/irb/ext/save-history.rb index f9c983ac11..3e0740d6fa 100644 --- a/lib/irb/ext/save-history.rb +++ b/lib/irb/ext/save-history.rb @@ -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 IRB.conf[:SAVE_HISTORY] def save_history IRB.conf[:SAVE_HISTORY] end + # Sets IRB.conf[:SAVE_HISTORY] 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 IRB.conf[:HISTORY_FILE] def history_file IRB.conf[:HISTORY_FILE] end + # Set IRB.conf[:HISTORY_FILE] 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 diff --git a/lib/irb/ext/tracer.rb b/lib/irb/ext/tracer.rb index 46a9d53a2e..8c9083dbad 100644 --- a/lib/irb/ext/tracer.rb +++ b/lib/irb/ext/tracer.rb @@ -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 diff --git a/lib/irb/ext/use-loader.rb b/lib/irb/ext/use-loader.rb index 913a64116f..4e98f5b7a2 100644 --- a/lib/irb/ext/use-loader.rb +++ b/lib/irb/ext/use-loader.rb @@ -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 diff --git a/lib/irb/ext/workspaces.rb b/lib/irb/ext/workspaces.rb index 1232fee84f..641befbdf3 100644 --- a/lib/irb/ext/workspaces.rb +++ b/lib/irb/ext/workspaces.rb @@ -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 diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb index 3f37d39950..487c0bbeff 100644 --- a/lib/irb/extend-command.rb +++ b/lib/irb/extend-command.rb @@ -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 IRB.CurrentContext.exit. 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} + # Returns a unique method name to use as an alias for the given +name+. + # + # Usually returns #{prefix}#{name}#{postfix}, 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) diff --git a/lib/irb/frame.rb b/lib/irb/frame.rb index 8814b47a9d..bcfa3a3140 100644 --- a/lib/irb/frame.rb +++ b/lib/irb/frame.rb @@ -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 diff --git a/lib/irb/help.rb b/lib/irb/help.rb index 4a308b6e46..9fd734038f 100644 --- a/lib/irb/help.rb +++ b/lib/irb/help.rb @@ -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") diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb index 7227df4ca0..e1a680847c 100644 --- a/lib/irb/input-method.rb +++ b/lib/irb/input-method.rb @@ -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 diff --git a/lib/irb/inspector.rb b/lib/irb/inspector.rb index 6e93d3ccb9..94f42d5217 100644 --- a/lib/irb/inspector.rb +++ b/lib/irb/inspector.rb @@ -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(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(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 diff --git a/lib/irb/notifier.rb b/lib/irb/notifier.rb index d20679da4a..effb12c30d 100644 --- a/lib/irb/notifier.rb +++ b/lib/irb/notifier.rb @@ -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(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 [#0- +] - # (\*|\*[1-9][0-9]*\$|[1-9][0-9]*) - # .(\*|\*[1-9][0-9]*\$|[1-9][0-9]*|)? - # #(hh|h|l|ll|L|q|j|z|t) - # [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 + # + # % + # [#0- +] + # (\*|\*[1-9][0-9]*\$|[1-9][0-9]*) + # .(\*|\*[1-9][0-9]*\$|[1-9][0-9]*|)? + # #(hh|h|l|ll|L|q|j|z|t) + # [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 StdioOutputMethodIRB.conf[:__MAIN__] 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 diff --git a/lib/irb/xmp.rb b/lib/irb/xmp.rb index bcef964020..947d2cf5a2 100644 --- a/lib/irb/xmp.rb +++ b/lib/irb/xmp.rb @@ -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 < 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) -- cgit v1.2.3