diff options
Diffstat (limited to 'lib/irb/context.rb')
-rw-r--r-- | lib/irb/context.rb | 287 |
1 files changed, 221 insertions, 66 deletions
diff --git a/lib/irb/context.rb b/lib/irb/context.rb index 0a46c1b1d4..5d2ff97328 100644 --- a/lib/irb/context.rb +++ b/lib/irb/context.rb @@ -1,14 +1,9 @@ -# frozen_string_literal: false +# frozen_string_literal: true # # irb/context.rb - irb context -# $Release Version: 0.9.6$ -# $Revision$ # by Keiju ISHITSUKA(keiju@ruby-lang.org) # -# -- -# -# -# + require_relative "workspace" require_relative "inspector" require_relative "input-method" @@ -22,17 +17,18 @@ module IRB # # The optional +input_method+ argument: # - # +nil+:: uses stdin or Reidline or Readline + # +nil+:: uses stdin or Reline or Readline # +String+:: uses a File # +other+:: uses this as InputMethod def initialize(irb, workspace = nil, input_method = nil) @irb = irb + @workspace_stack = [] if workspace - @workspace = workspace + @workspace_stack << workspace else - @workspace = WorkSpace.new + @workspace_stack << WorkSpace.new end - @thread = Thread.current if defined? Thread + @thread = Thread.current # copy of default configuration @ap_name = IRB.conf[:AP_NAME] @@ -48,18 +44,25 @@ module IRB end if IRB.conf.has_key?(:USE_MULTILINE) @use_multiline = IRB.conf[:USE_MULTILINE] - elsif IRB.conf.has_key?(:USE_REIDLINE) # backward compatibility + elsif IRB.conf.has_key?(:USE_RELINE) # backward compatibility + warn <<~MSG.strip + USE_RELINE is deprecated, please use USE_MULTILINE instead. + MSG + @use_multiline = IRB.conf[:USE_RELINE] + elsif IRB.conf.has_key?(:USE_REIDLINE) + warn <<~MSG.strip + USE_REIDLINE is deprecated, please use USE_MULTILINE instead. + MSG @use_multiline = IRB.conf[:USE_REIDLINE] else @use_multiline = nil end - @use_colorize = IRB.conf[:USE_COLORIZE] @use_autocomplete = IRB.conf[:USE_AUTOCOMPLETE] @verbose = IRB.conf[:VERBOSE] @io = nil self.inspect_mode = IRB.conf[:INSPECT_MODE] - self.use_tracer = IRB.conf[:USE_TRACER] if IRB.conf[:USE_TRACER] + self.use_tracer = IRB.conf[:USE_TRACER] self.use_loader = IRB.conf[:USE_LOADER] if IRB.conf[:USE_LOADER] self.eval_history = IRB.conf[:EVAL_HISTORY] if IRB.conf[:EVAL_HISTORY] @@ -75,28 +78,28 @@ module IRB else @irb_name = IRB.conf[:IRB_NAME]+"#"+IRB.JobManager.n_jobs.to_s end - @irb_path = "(" + @irb_name + ")" + self.irb_path = "(" + @irb_name + ")" case input_method when nil @io = nil case use_multiline? when nil - if STDIN.tty? && IRB.conf[:PROMPT_MODE] != :INF_RUBY && !use_singleline? + if term_interactive? && IRB.conf[:PROMPT_MODE] != :INF_RUBY && !use_singleline? # Both of multiline mode and singleline mode aren't specified. - @io = ReidlineInputMethod.new + @io = RelineInputMethod.new(build_completor) else @io = nil end when false @io = nil when true - @io = ReidlineInputMethod.new + @io = RelineInputMethod.new(build_completor) end unless @io case use_singleline? when nil - if (defined?(ReadlineInputMethod) && STDIN.tty? && + if (defined?(ReadlineInputMethod) && term_interactive? && IRB.conf[:PROMPT_MODE] != :INF_RUBY) @io = ReadlineInputMethod.new else @@ -116,15 +119,17 @@ module IRB end @io = StdioInputMethod.new unless @io + when '-' + @io = FileInputMethod.new($stdin) + @irb_name = '-' + self.irb_path = '-' when String @io = FileInputMethod.new(input_method) @irb_name = File.basename(input_method) - @irb_path = input_method + self.irb_path = input_method else @io = input_method end - self.save_history = IRB.conf[:SAVE_HISTORY] if IRB.conf[:SAVE_HISTORY] - @extra_doc_dirs = IRB.conf[:EXTRA_DOC_DIRS] @echo = IRB.conf[:ECHO] @@ -141,23 +146,119 @@ module IRB if @newline_before_multiline_output.nil? @newline_before_multiline_output = true end + + @user_aliases = IRB.conf[:COMMAND_ALIASES].dup + @command_aliases = @user_aliases.merge(KEYWORD_ALIASES) + end + + private def term_interactive? + return true if ENV['TEST_IRB_FORCE_INTERACTIVE'] + STDIN.tty? && ENV['TERM'] != 'dumb' + end + + # because all input will eventually be evaluated as Ruby code, + # command names that conflict with Ruby keywords need special workaround + # we can remove them once we implemented a better command system for IRB + KEYWORD_ALIASES = { + :break => :irb_break, + :catch => :irb_catch, + :next => :irb_next, + }.freeze + + private_constant :KEYWORD_ALIASES + + def use_tracer=(val) + require_relative "ext/tracer" if val + IRB.conf[:USE_TRACER] = val + end + + def eval_history=(val) + self.class.remove_method(__method__) + require_relative "ext/eval_history" + __send__(__method__, val) + end + + def use_loader=(val) + self.class.remove_method(__method__) + require_relative "ext/use-loader" + __send__(__method__, val) + end + + private def build_completor + completor_type = IRB.conf[:COMPLETOR] + case completor_type + when :regexp + return RegexpCompletor.new + when :type + completor = build_type_completor + return completor if completor + else + warn "Invalid value for IRB.conf[:COMPLETOR]: #{completor_type}" + end + # Fallback to RegexpCompletor + RegexpCompletor.new + end + + private def build_type_completor + if RUBY_ENGINE == 'truffleruby' + # Avoid SyntaxError. truffleruby does not support endless method definition yet. + warn 'TypeCompletor is not supported on TruffleRuby yet' + return + end + + begin + require 'repl_type_completor' + rescue LoadError => e + warn "TypeCompletor requires `gem repl_type_completor`: #{e.message}" + return + end + + ReplTypeCompletor.preload_rbs + TypeCompletor.new(self) + end + + def save_history=(val) + IRB.conf[:SAVE_HISTORY] = val + end + + def save_history + IRB.conf[:SAVE_HISTORY] + 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 + + # Workspace in the current context. + def workspace + @workspace_stack.last + end + + # Replace the current workspace with the given +workspace+. + def replace_workspace(workspace) + @workspace_stack.pop + @workspace_stack.push(workspace) end # The top-level workspace, see WorkSpace#main def main - @workspace.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, - # ReidlineInputMethod, FileInputMethod or other specified when the + # RelineInputMethod, FileInputMethod or other specified when the # context is created. See ::new for more # information on +input_method+. attr_accessor :io @@ -172,9 +273,27 @@ module IRB # 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 + + # Can be one of the following: + # - the #irb_name surrounded by parenthesis + # - the +input_method+ passed to Context.new + # - the file path of the current IRB context in a binding.irb session + attr_reader :irb_path + + # Sets @irb_path to the given +path+ as well as @eval_path + # @eval_path is used for evaluating code in the context of IRB session + # It's the same as irb_path, but with the IRB name postfix + # This makes sure users can distinguish the methods defined in the IRB session + # from the methods defined in the current file's context, especially with binding.irb + def irb_path=(path) + @irb_path = path + + if File.exist?(path) + @eval_path = "#{path}(#{IRB.conf[:IRB_NAME]})" + else + @eval_path = path + end + end # Whether multiline editor mode is enabled or not. # @@ -186,8 +305,6 @@ module IRB attr_reader :use_singleline # Whether colorization is enabled or not. # - # A copy of the default <code>IRB.conf[:USE_COLORIZE]</code> - attr_reader :use_colorize # A copy of the default <code>IRB.conf[:USE_AUTOCOMPLETE]</code> attr_reader :use_autocomplete # A copy of the default <code>IRB.conf[:INSPECT_MODE]</code> @@ -197,18 +314,29 @@ module IRB attr_reader :prompt_mode # Standard IRB prompt. # - # See IRB@Customizing+the+IRB+Prompt for more information. + # See {Custom Prompts}[rdoc-ref:IRB@Custom+Prompts] for more information. attr_accessor :prompt_i # IRB prompt for continuated strings. # - # See IRB@Customizing+the+IRB+Prompt for more information. + # See {Custom Prompts}[rdoc-ref:IRB@Custom+Prompts] for more information. attr_accessor :prompt_s # IRB prompt for continuated statement. (e.g. immediately after an +if+) # - # See IRB@Customizing+the+IRB+Prompt for more information. + # See {Custom Prompts}[rdoc-ref:IRB@Custom+Prompts] for more information. attr_accessor :prompt_c - # See IRB@Customizing+the+IRB+Prompt for more information. - attr_accessor :prompt_n + + # TODO: Remove this when developing v2.0 + def prompt_n + warn "IRB::Context#prompt_n is deprecated and will be removed in the next major release." + "" + end + + # TODO: Remove this when developing v2.0 + def prompt_n=(_) + warn "IRB::Context#prompt_n= is deprecated and will be removed in the next major release." + "" + end + # Can be either the default <code>IRB.conf[:AUTO_INDENT]</code>, or the # mode set by #prompt_mode= # @@ -316,24 +444,25 @@ module IRB # The default value is 16. # # Can also be set using the +--back-trace-limit+ command line option. - # - # See IRB@Command+line+options for more command line options. attr_accessor :back_trace_limit + # User-defined IRB command aliases + attr_accessor :command_aliases + + attr_accessor :with_debugger + # Alias for #use_multiline alias use_multiline? use_multiline # Alias for #use_singleline alias use_singleline? use_singleline # backward compatibility - alias use_reidline use_multiline + alias use_reline use_multiline # backward compatibility - alias use_reidline? use_multiline + alias use_reline? use_multiline # backward compatibility alias use_readline use_singleline # backward compatibility alias use_readline? use_singleline - # Alias for #use_colorize - alias use_colorize? use_colorize # Alias for #use_autocomplete alias use_autocomplete? use_autocomplete # Alias for #rc @@ -347,7 +476,7 @@ module IRB # Returns whether messages are displayed or not. def verbose? if @verbose.nil? - if @io.kind_of?(ReidlineInputMethod) + if @io.kind_of?(RelineInputMethod) false elsif defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod) false @@ -362,12 +491,10 @@ module IRB end # Whether #verbose? is +true+, and +input_method+ is either - # StdioInputMethod or ReidlineInputMethod or ReadlineInputMethod, see #io + # StdioInputMethod or RelineInputMethod or ReadlineInputMethod, see #io # for more information. def prompting? - verbose? || (STDIN.tty? && @io.kind_of?(StdioInputMethod) || - @io.kind_of?(ReidlineInputMethod) || - (defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod))) + verbose? || @io.prompting? end # The return value of the last statement evaluated. @@ -377,19 +504,18 @@ module IRB # to #last_value. def set_last_value(value) @last_value = value - @workspace.local_variable_set :_, value + workspace.local_variable_set :_, value end # Sets the +mode+ of the prompt in this context. # - # See IRB@Customizing+the+IRB+Prompt for more information. + # See {Custom Prompts}[rdoc-ref:IRB@Custom+Prompts] for more information. def prompt_mode=(mode) @prompt_mode = mode pconf = IRB.conf[:PROMPT][mode] @prompt_i = pconf[:PROMPT_I] @prompt_s = pconf[:PROMPT_S] @prompt_c = pconf[:PROMPT_C] - @prompt_n = pconf[:PROMPT_N] @return_format = pconf[:RETURN] @return_format = "%s\n" if @return_format == nil if ai = pconf.include?(:AUTO_INDENT) @@ -421,8 +547,6 @@ module IRB # # Can also be set using the +--inspect+ and +--noinspect+ command line # options. - # - # See IRB@Command+line+options for more command line options. def inspect_mode=(opt) if i = Inspector::INSPECTORS[opt] @@ -466,26 +590,53 @@ module IRB @inspect_mode end - def evaluate(line, line_no, exception: nil) # :nodoc: + def evaluate(statement, line_no) # :nodoc: @line_no = line_no - if exception - line_no -= 1 - line = "begin ::Kernel.raise _; rescue _.class\n#{line}\n""end" - @workspace.local_variable_set(:_, exception) + + case statement + when Statement::EmptyInput + return + when Statement::Expression + result = evaluate_expression(statement.code, line_no) + set_last_value(result) + when Statement::Command + statement.command_class.execute(self, statement.arg) + set_last_value(nil) end - set_last_value(@workspace.evaluate(self, line, irb_path, line_no)) + + nil end - def inspect_last_value # :nodoc: - @inspect_method.inspect_value(@last_value) + def from_binding? + @irb.from_binding end - alias __exit__ exit - # Exits the current session, see IRB.irb_exit - def exit(ret = 0) - IRB.irb_exit(@irb, ret) - rescue UncaughtThrowError - super + def evaluate_expression(code, line_no) # :nodoc: + result = nil + if IRB.conf[:MEASURE] && IRB.conf[:MEASURE_CALLBACKS].empty? + IRB.set_measure_callback + end + + if IRB.conf[:MEASURE] && !IRB.conf[:MEASURE_CALLBACKS].empty? + last_proc = proc do + result = workspace.evaluate(code, @eval_path, line_no) + end + IRB.conf[:MEASURE_CALLBACKS].inject(last_proc) do |chain, item| + _name, callback, arg = item + proc do + callback.(self, code, line_no, arg) do + chain.call + end + end + end.call + else + result = workspace.evaluate(code, @eval_path, line_no) + end + result + end + + def inspect_last_value # :nodoc: + @inspect_method.inspect_value(@last_value) end NOPRINTING_IVARS = ["@last_value"] # :nodoc: @@ -514,5 +665,9 @@ module IRB end alias __to_s__ to_s alias to_s inspect + + def local_variables # :nodoc: + workspace.binding.local_variables + end end end |