diff options
author | Hiroshi SHIBATA <[email protected]> | 2025-01-23 16:13:13 +0900 |
---|---|---|
committer | Hiroshi SHIBATA <[email protected]> | 2025-01-24 15:46:46 +0900 |
commit | 0fdc9b9fd168f9d25d78c79a9e970be7d0967363 (patch) | |
tree | 6a7b0bc8b9f94fdcb609689c1b644dcfa0c43d36 /lib/irb/context.rb | |
parent | 881924f2593e89e5ef78a73a4e14948a66ca0e08 (diff) |
Migrate irb and reline to the bundled gems
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/12624
Diffstat (limited to 'lib/irb/context.rb')
-rw-r--r-- | lib/irb/context.rb | 751 |
1 files changed, 0 insertions, 751 deletions
diff --git a/lib/irb/context.rb b/lib/irb/context.rb deleted file mode 100644 index 395d9081f8..0000000000 --- a/lib/irb/context.rb +++ /dev/null @@ -1,751 +0,0 @@ -# frozen_string_literal: true -# -# irb/context.rb - irb context -# by Keiju ISHITSUKA([email protected]) -# - -require_relative "workspace" -require_relative "inspector" -require_relative "input-method" -require_relative "output-method" - -module IRB - # A class that wraps the current state of the irb session, including the - # configuration of IRB.conf. - class Context - KERNEL_PUBLIC_METHOD = ::Kernel.instance_method(:public_method) - KERNEL_METHOD = ::Kernel.instance_method(:method) - - ASSIGN_OPERATORS_REGEXP = Regexp.union(%w[= += -= *= /= %= **= &= |= &&= ||= ^= <<= >>=]) - # Creates a new IRB context. - # - # The optional +input_method+ argument: - # - # +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_stack << workspace - else - @workspace_stack << WorkSpace.new - end - @thread = Thread.current - - # copy of default configuration - @ap_name = IRB.conf[:AP_NAME] - @rc = IRB.conf[:RC] - @load_modules = IRB.conf[:LOAD_MODULES] - - if IRB.conf.has_key?(:USE_SINGLELINE) - @use_singleline = IRB.conf[:USE_SINGLELINE] - elsif IRB.conf.has_key?(:USE_READLINE) # backward compatibility - @use_singleline = IRB.conf[:USE_READLINE] - else - @use_singleline = nil - end - if IRB.conf.has_key?(:USE_MULTILINE) - @use_multiline = IRB.conf[:USE_MULTILINE] - 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_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] - self.use_loader = IRB.conf[:USE_LOADER] if IRB.conf[:USE_LOADER] - self.eval_history = IRB.conf[:EVAL_HISTORY] if IRB.conf[:EVAL_HISTORY] - - @ignore_sigint = IRB.conf[:IGNORE_SIGINT] - @ignore_eof = IRB.conf[:IGNORE_EOF] - - @back_trace_limit = IRB.conf[:BACK_TRACE_LIMIT] - - self.prompt_mode = IRB.conf[:PROMPT_MODE] - - @irb_name = IRB.conf[:IRB_NAME] - - unless IRB.conf[:SINGLE_IRB] or !defined?(IRB::JobManager) - @irb_name = @irb_name + "#" + IRB.JobManager.n_jobs.to_s - end - - self.irb_path = "(" + @irb_name + ")" - - case input_method - when nil - @io = nil - case use_multiline? - when nil - if term_interactive? && IRB.conf[:PROMPT_MODE] != :INF_RUBY && !use_singleline? - # Both of multiline mode and singleline mode aren't specified. - @io = RelineInputMethod.new(build_completor) - else - @io = nil - end - when false - @io = nil - when true - @io = RelineInputMethod.new(build_completor) - end - unless @io - case use_singleline? - when nil - if (defined?(ReadlineInputMethod) && term_interactive? && - IRB.conf[:PROMPT_MODE] != :INF_RUBY) - @io = ReadlineInputMethod.new - else - @io = nil - end - when false - @io = nil - when true - if defined?(ReadlineInputMethod) - @io = ReadlineInputMethod.new - else - @io = nil - end - else - @io = nil - end - 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) - self.irb_path = input_method - else - @io = input_method - end - @extra_doc_dirs = IRB.conf[:EXTRA_DOC_DIRS] - - @echo = IRB.conf[:ECHO] - if @echo.nil? - @echo = true - end - - @echo_on_assignment = IRB.conf[:ECHO_ON_ASSIGNMENT] - if @echo_on_assignment.nil? - @echo_on_assignment = :truncate - end - - @newline_before_multiline_output = IRB.conf[:NEWLINE_BEFORE_MULTILINE_OUTPUT] - if @newline_before_multiline_output.nil? - @newline_before_multiline_output = true - end - - @command_aliases = IRB.conf[:COMMAND_ALIASES].dup - end - - 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 - - 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 - end - - # The toplevel workspace, see #home_workspace - attr_reader :workspace_home - # The current thread in this context. - attr_reader :thread - # The current input method. - # - # Can be either StdioInputMethod, ReadlineInputMethod, - # RelineInputMethod, 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 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. - # - # A copy of the default <code>IRB.conf[:USE_MULTILINE]</code> - attr_reader :use_multiline - # Whether singleline editor mode is enabled or not. - # - # A copy of the default <code>IRB.conf[:USE_SINGLELINE]</code> - attr_reader :use_singleline - # Whether colorization is enabled or not. - # - # 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> - attr_reader :inspect_mode - # Inspector for the current context - attr_reader :inspect_method - - # A copy of the default <code>IRB.conf[:PROMPT_MODE]</code> - attr_reader :prompt_mode - # Standard IRB prompt. - # - # See {Custom Prompts}[rdoc-ref:IRB@Custom+Prompts] for more information. - attr_accessor :prompt_i - # IRB prompt for continuated strings. - # - # 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 {Custom Prompts}[rdoc-ref:IRB@Custom+Prompts] for more information. - attr_accessor :prompt_c - - # 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= - # - # To disable auto-indentation in irb: - # - # IRB.conf[:AUTO_INDENT] = false - # - # or - # - # irb_context.auto_indent_mode = false - # - # or - # - # IRB.CurrentContext.auto_indent_mode = false - # - # See IRB@Configuration for more information. - 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. - # - # If set to +false+, <code>^C</code> will quit irb. - # - # If set to +true+, - # - # * during input: cancel input then return to top level. - # * during execute: abandon current execution. - attr_accessor :ignore_sigint - # Whether <code>^D</code> (+control-d+) will be ignored or not. - # - # If set to +false+, <code>^D</code> will quit irb. - attr_accessor :ignore_eof - # Specify the installation locations of the ri file to be displayed in the - # document dialog. - attr_accessor :extra_doc_dirs - # Whether to echo the return value to output or not. - # - # Uses <code>IRB.conf[:ECHO]</code> if available, or defaults to +true+. - # - # puts "hello" - # # hello - # #=> nil - # IRB.CurrentContext.echo = false - # puts "omg" - # # omg - attr_accessor :echo - # Whether to echo for assignment expressions. - # - # If set to +false+, the value of assignment will not be shown. - # - # If set to +true+, the value of assignment will be shown. - # - # If set to +:truncate+, the value of assignment will be shown and truncated. - # - # It defaults to +:truncate+. - # - # a = "omg" - # #=> omg - # - # a = "omg" * 10 - # #=> omgomgomgomgomgomgomg... - # - # IRB.CurrentContext.echo_on_assignment = false - # a = "omg" - # - # IRB.CurrentContext.echo_on_assignment = true - # a = "omg" * 10 - # #=> omgomgomgomgomgomgomgomgomgomg - # - # To set the behaviour of showing on assignment in irb: - # - # IRB.conf[:ECHO_ON_ASSIGNMENT] = :truncate or true or false - # - # or - # - # irb_context.echo_on_assignment = :truncate or true or false - # - # or - # - # IRB.CurrentContext.echo_on_assignment = :truncate or true or false - attr_accessor :echo_on_assignment - # Whether a newline is put before multiline output. - # - # Uses <code>IRB.conf[:NEWLINE_BEFORE_MULTILINE_OUTPUT]</code> if available, - # or defaults to +true+. - # - # "abc\ndef" - # #=> - # abc - # def - # IRB.CurrentContext.newline_before_multiline_output = false - # "abc\ndef" - # #=> abc - # def - attr_accessor :newline_before_multiline_output - # Whether verbose messages are displayed or not. - # - # A copy of the default <code>IRB.conf[:VERBOSE]</code> - attr_accessor :verbose - - # The limit of backtrace lines displayed as top +n+ and tail +n+. - # - # The default value is 16. - # - # Can also be set using the +--back-trace-limit+ command line option. - 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_reline use_multiline - # backward compatibility - alias use_reline? use_multiline - # backward compatibility - alias use_readline use_singleline - # backward compatibility - alias use_readline? use_singleline - # Alias for #use_autocomplete - alias use_autocomplete? use_autocomplete - # Alias for #rc - alias rc? rc - alias ignore_sigint? ignore_sigint - alias ignore_eof? ignore_eof - alias echo? echo - alias echo_on_assignment? echo_on_assignment - alias newline_before_multiline_output? newline_before_multiline_output - - # Returns whether messages are displayed or not. - def verbose? - if @verbose.nil? - if @io.kind_of?(RelineInputMethod) - false - elsif defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod) - false - elsif !STDIN.tty? or @io.kind_of?(FileInputMethod) - true - else - false - end - else - @verbose - end - end - - # Whether #verbose? is +true+, and +input_method+ is either - # StdioInputMethod or RelineInputMethod or ReadlineInputMethod, see #io - # for more information. - def prompting? - verbose? || @io.prompting? - 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.local_variable_set :_, value - end - - # Sets the +mode+ of the prompt in this context. - # - # 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] - @return_format = pconf[:RETURN] - @return_format = "%s\n" if @return_format == nil - if ai = pconf.include?(:AUTO_INDENT) - @auto_indent_mode = ai - else - @auto_indent_mode = IRB.conf[:AUTO_INDENT] - 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 - - # Specifies the inspect mode with +opt+: - # - # +true+:: display +inspect+ - # +false+:: display +to_s+ - # +nil+:: inspect mode in non-math mode, - # non-inspect mode in math mode - # - # See IRB::Inspector for more information. - # - # Can also be set using the +--inspect+ and +--noinspect+ command line - # options. - def inspect_mode=(opt) - - if i = Inspector::INSPECTORS[opt] - @inspect_mode = opt - @inspect_method = i - i.init - else - case opt - when nil - if Inspector.keys_with_inspector(Inspector::INSPECTORS[true]).include?(@inspect_mode) - self.inspect_mode = false - elsif Inspector.keys_with_inspector(Inspector::INSPECTORS[false]).include?(@inspect_mode) - self.inspect_mode = true - else - puts "Can't switch inspect mode." - return - end - when /^\s*\{.*\}\s*$/ - begin - inspector = eval "proc#{opt}" - rescue Exception - puts "Can't switch inspect mode(#{opt})." - return - end - self.inspect_mode = inspector - when Proc - self.inspect_mode = IRB::Inspector(opt) - when Inspector - prefix = "usr%d" - i = 1 - while Inspector::INSPECTORS[format(prefix, i)]; i += 1; end - @inspect_mode = format(prefix, i) - @inspect_method = opt - Inspector.def_inspector(format(prefix, i), @inspect_method) - else - puts "Can't switch inspect mode(#{opt})." - return - end - end - print "Switch to#{unless @inspect_mode; ' non';end} inspect mode.\n" if verbose? - @inspect_mode - end - - def evaluate(statement, line_no) # :nodoc: - @line_no = line_no - - 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) - when Statement::IncorrectAlias - warn statement.message - end - - nil - end - - def from_binding? - @irb.from_binding - end - - 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 parse_input(code, is_assignment_expression) - command_name, arg = code.strip.split(/\s+/, 2) - arg ||= '' - - # command can only be 1 line - if code.lines.size != 1 || - # command name is required - command_name.nil? || - # local variable have precedence over command - local_variables.include?(command_name.to_sym) || - # assignment expression is not a command - (is_assignment_expression || - (arg.start_with?(ASSIGN_OPERATORS_REGEXP) && !arg.start_with?(/==|=~/))) - return Statement::Expression.new(code, is_assignment_expression) - end - - command = command_name.to_sym - - # Check command aliases - if aliased_name = command_aliases[command] - if command_class = Command.load_command(aliased_name) - command = aliased_name - elsif HelperMethod.helper_methods[aliased_name] - message = <<~MESSAGE - Using command alias `#{command}` for helper method `#{aliased_name}` is not supported. - Please check the value of `IRB.conf[:COMMAND_ALIASES]`. - MESSAGE - return Statement::IncorrectAlias.new(message) - else - message = <<~MESSAGE - You're trying to use command alias `#{command}` for command `#{aliased_name}`, but `#{aliased_name}` does not exist. - Please check the value of `IRB.conf[:COMMAND_ALIASES]`. - MESSAGE - return Statement::IncorrectAlias.new(message) - end - else - command_class = Command.load_command(command) - end - - # Check visibility - public_method = !!KERNEL_PUBLIC_METHOD.bind_call(main, command) rescue false - private_method = !public_method && !!KERNEL_METHOD.bind_call(main, command) rescue false - if command_class && Command.execute_as_command?(command, public_method: public_method, private_method: private_method) - Statement::Command.new(code, command_class, arg) - else - Statement::Expression.new(code, is_assignment_expression) - end - end - - def colorize_input(input, complete:) - if IRB.conf[:USE_COLORIZE] && IRB::Color.colorable? - lvars = local_variables || [] - parsed_input = parse_input(input, false) - if parsed_input.is_a?(Statement::Command) - name, sep, arg = input.split(/(\s+)/, 2) - arg = IRB::Color.colorize_code(arg, complete: complete, local_variables: lvars) - "#{IRB::Color.colorize(name, [:BOLD])}\e[m#{sep}#{arg}" - else - IRB::Color.colorize_code(input, complete: complete, local_variables: lvars) - end - else - Reline::Unicode.escape_for_print(input) - end - end - - def inspect_last_value(output = +'') # :nodoc: - @inspect_method.inspect_value(@last_value, output) - end - - def inspector_support_stream_output? - @inspect_method.support_stream_output? - end - - NOPRINTING_IVARS = ["@last_value"] # :nodoc: - NO_INSPECTING_IVARS = ["@irb", "@io"] # :nodoc: - IDNAME_IVARS = ["@prompt_mode"] # :nodoc: - - alias __inspect__ inspect - def inspect # :nodoc: - array = [] - for ivar in instance_variables.sort{|e1, e2| e1 <=> e2} - ivar = ivar.to_s - name = ivar.sub(/^@(.*)$/, '\1') - val = instance_eval(ivar) - case ivar - when *NOPRINTING_IVARS - array.push format("conf.%s=%s", name, "...") - when *NO_INSPECTING_IVARS - array.push format("conf.%s=%s", name, val.to_s) - when *IDNAME_IVARS - array.push format("conf.%s=:%s", name, val.id2name) - else - array.push format("conf.%s=%s", name, val.inspect) - end - end - array.join("\n") - end - alias __to_s__ to_s - alias to_s inspect - - def local_variables # :nodoc: - workspace.binding.local_variables - end - - def safe_method_call_on_main(method_name) - main_object = main - Object === main_object ? main_object.__send__(method_name) : Object.instance_method(method_name).bind_call(main_object) - end - - private - - def term_interactive? - return true if ENV['TEST_IRB_FORCE_INTERACTIVE'] - STDIN.tty? && ENV['TERM'] != 'dumb' - end - - def build_completor - completor_type = IRB.conf[:COMPLETOR] - - # Gem repl_type_completor is added to bundled gems in Ruby 3.4. - # Use :type as default completor only in Ruby 3.4 or later. - verbose = !!completor_type - completor_type ||= RUBY_VERSION >= '3.4' ? :type : :regexp - - case completor_type - when :regexp - return RegexpCompletor.new - when :type - completor = build_type_completor(verbose: verbose) - return completor if completor - else - warn "Invalid value for IRB.conf[:COMPLETOR]: #{completor_type}" - end - # Fallback to RegexpCompletor - RegexpCompletor.new - end - - def build_type_completor(verbose:) - if RUBY_ENGINE == 'truffleruby' - # Avoid SyntaxError. truffleruby does not support endless method definition yet. - warn 'TypeCompletor is not supported on TruffleRuby yet' if verbose - return - end - - begin - require 'repl_type_completor' - rescue LoadError => e - warn "TypeCompletor requires `gem repl_type_completor`: #{e.message}" if verbose - return - end - - ReplTypeCompletor.preload_rbs - TypeCompletor.new(self) - end - end -end |