diff options
Diffstat (limited to 'lib/irb')
75 files changed, 0 insertions, 8226 deletions
diff --git a/lib/irb/cmd/nop.rb b/lib/irb/cmd/nop.rb deleted file mode 100644 index 9d2e3c4d47..0000000000 --- a/lib/irb/cmd/nop.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -# This file is just a placeholder for backward-compatibility. -# Please require 'irb' and inherit your command from `IRB::Command::Base` instead. diff --git a/lib/irb/color.rb b/lib/irb/color.rb deleted file mode 100644 index a7e311087a..0000000000 --- a/lib/irb/color.rb +++ /dev/null @@ -1,263 +0,0 @@ -# frozen_string_literal: true -require 'reline' -require 'ripper' -require_relative 'ruby-lex' - -module IRB # :nodoc: - module Color - CLEAR = 0 - BOLD = 1 - UNDERLINE = 4 - REVERSE = 7 - BLACK = 30 - RED = 31 - GREEN = 32 - YELLOW = 33 - BLUE = 34 - MAGENTA = 35 - CYAN = 36 - WHITE = 37 - - TOKEN_KEYWORDS = { - on_kw: ['nil', 'self', 'true', 'false', '__FILE__', '__LINE__', '__ENCODING__'], - on_const: ['ENV'], - } - private_constant :TOKEN_KEYWORDS - - # A constant of all-bit 1 to match any Ripper's state in #dispatch_seq - ALL = -1 - private_constant :ALL - - begin - # Following pry's colors where possible, but sometimes having a compromise like making - # backtick and regexp as red (string's color, because they're sharing tokens). - TOKEN_SEQ_EXPRS = { - on_CHAR: [[BLUE, BOLD], ALL], - on_backtick: [[RED, BOLD], ALL], - on_comment: [[BLUE, BOLD], ALL], - on_const: [[BLUE, BOLD, UNDERLINE], ALL], - on_embexpr_beg: [[RED], ALL], - on_embexpr_end: [[RED], ALL], - on_embvar: [[RED], ALL], - on_float: [[MAGENTA, BOLD], ALL], - on_gvar: [[GREEN, BOLD], ALL], - on_backref: [[GREEN, BOLD], ALL], - on_heredoc_beg: [[RED], ALL], - on_heredoc_end: [[RED], ALL], - on_ident: [[BLUE, BOLD], Ripper::EXPR_ENDFN], - on_imaginary: [[BLUE, BOLD], ALL], - on_int: [[BLUE, BOLD], ALL], - on_kw: [[GREEN], ALL], - on_label: [[MAGENTA], ALL], - on_label_end: [[RED, BOLD], ALL], - on_qsymbols_beg: [[RED, BOLD], ALL], - on_qwords_beg: [[RED, BOLD], ALL], - on_rational: [[BLUE, BOLD], ALL], - on_regexp_beg: [[RED, BOLD], ALL], - on_regexp_end: [[RED, BOLD], ALL], - on_symbeg: [[YELLOW], ALL], - on_symbols_beg: [[RED, BOLD], ALL], - on_tstring_beg: [[RED, BOLD], ALL], - on_tstring_content: [[RED], ALL], - on_tstring_end: [[RED, BOLD], ALL], - on_words_beg: [[RED, BOLD], ALL], - on_parse_error: [[RED, REVERSE], ALL], - compile_error: [[RED, REVERSE], ALL], - on_assign_error: [[RED, REVERSE], ALL], - on_alias_error: [[RED, REVERSE], ALL], - on_class_name_error:[[RED, REVERSE], ALL], - on_param_error: [[RED, REVERSE], ALL], - on___end__: [[GREEN], ALL], - } - rescue NameError - # Give up highlighting Ripper-incompatible older Ruby - TOKEN_SEQ_EXPRS = {} - end - private_constant :TOKEN_SEQ_EXPRS - - ERROR_TOKENS = TOKEN_SEQ_EXPRS.keys.select { |k| k.to_s.end_with?('error') } - private_constant :ERROR_TOKENS - - class << self - def colorable? - supported = $stdout.tty? && (/mswin|mingw/.match?(RUBY_PLATFORM) || (ENV.key?('TERM') && ENV['TERM'] != 'dumb')) - - # because ruby/debug also uses irb's color module selectively, - # irb won't be activated in that case. - if IRB.respond_to?(:conf) - supported && !!IRB.conf.fetch(:USE_COLORIZE, true) - else - supported - end - end - - def inspect_colorable?(obj, seen: {}.compare_by_identity) - case obj - when String, Symbol, Regexp, Integer, Float, FalseClass, TrueClass, NilClass - true - when Hash - without_circular_ref(obj, seen: seen) do - obj.all? { |k, v| inspect_colorable?(k, seen: seen) && inspect_colorable?(v, seen: seen) } - end - when Array - without_circular_ref(obj, seen: seen) do - obj.all? { |o| inspect_colorable?(o, seen: seen) } - end - when Range - inspect_colorable?(obj.begin, seen: seen) && inspect_colorable?(obj.end, seen: seen) - when Module - !obj.name.nil? - else - false - end - end - - def clear(colorable: colorable?) - return '' unless colorable - "\e[#{CLEAR}m" - end - - def colorize(text, seq, colorable: colorable?) - return text unless colorable - seq = seq.map { |s| "\e[#{const_get(s)}m" }.join('') - "#{seq}#{text}#{clear(colorable: colorable)}" - end - - # If `complete` is false (code is incomplete), this does not warn compile_error. - # This option is needed to avoid warning a user when the compile_error is happening - # because the input is not wrong but just incomplete. - def colorize_code(code, complete: true, ignore_error: false, colorable: colorable?, local_variables: []) - return code unless colorable - - symbol_state = SymbolState.new - colored = +'' - lvars_code = RubyLex.generate_local_variables_assign_code(local_variables) - code_with_lvars = lvars_code ? "#{lvars_code}\n#{code}" : code - - scan(code_with_lvars, allow_last_error: !complete) do |token, str, expr| - # handle uncolorable code - if token.nil? - colored << Reline::Unicode.escape_for_print(str) - next - end - - # IRB::ColorPrinter skips colorizing fragments with any invalid token - if ignore_error && ERROR_TOKENS.include?(token) - return Reline::Unicode.escape_for_print(code) - end - - in_symbol = symbol_state.scan_token(token) - str.each_line do |line| - line = Reline::Unicode.escape_for_print(line) - if seq = dispatch_seq(token, expr, line, in_symbol: in_symbol) - colored << seq.map { |s| "\e[#{s}m" }.join('') - colored << line.sub(/\Z/, clear(colorable: colorable)) - else - colored << line - end - end - end - - if lvars_code - raise "#{lvars_code.dump} should have no \\n" if lvars_code.include?("\n") - colored.sub!(/\A.+\n/, '') # delete_prefix lvars_code with colors - end - colored - end - - private - - def without_circular_ref(obj, seen:, &block) - return false if seen.key?(obj) - seen[obj] = true - block.call - ensure - seen.delete(obj) - end - - def scan(code, allow_last_error:) - verbose, $VERBOSE = $VERBOSE, nil - RubyLex.compile_with_errors_suppressed(code) do |inner_code, line_no| - lexer = Ripper::Lexer.new(inner_code, '(ripper)', line_no) - byte_pos = 0 - line_positions = [0] - inner_code.lines.each do |line| - line_positions << line_positions.last + line.bytesize - end - - on_scan = proc do |elem| - start_pos = line_positions[elem.pos[0] - 1] + elem.pos[1] - - # yield uncolorable code - if byte_pos < start_pos - yield(nil, inner_code.byteslice(byte_pos...start_pos), nil) - end - - if byte_pos <= start_pos - str = elem.tok - yield(elem.event, str, elem.state) - byte_pos = start_pos + str.bytesize - end - end - - lexer.scan.each do |elem| - next if allow_last_error and /meets end of file|unexpected end-of-input/ =~ elem.message - on_scan.call(elem) - end - # yield uncolorable DATA section - yield(nil, inner_code.byteslice(byte_pos...inner_code.bytesize), nil) if byte_pos < inner_code.bytesize - end - ensure - $VERBOSE = verbose - end - - def dispatch_seq(token, expr, str, in_symbol:) - if ERROR_TOKENS.include?(token) - TOKEN_SEQ_EXPRS[token][0] - elsif in_symbol - [YELLOW] - elsif TOKEN_KEYWORDS.fetch(token, []).include?(str) - [CYAN, BOLD] - elsif (seq, exprs = TOKEN_SEQ_EXPRS[token]; (expr & (exprs || 0)) != 0) - seq - else - nil - end - end - end - - # A class to manage a state to know whether the current token is for Symbol or not. - class SymbolState - def initialize - # Push `true` to detect Symbol. `false` to increase the nest level for non-Symbol. - @stack = [] - end - - # Return true if the token is a part of Symbol. - def scan_token(token) - prev_state = @stack.last - case token - when :on_symbeg, :on_symbols_beg, :on_qsymbols_beg - @stack << true - when :on_ident, :on_op, :on_const, :on_ivar, :on_cvar, :on_gvar, :on_kw, :on_backtick - if @stack.last # Pop only when it's Symbol - @stack.pop - return prev_state - end - when :on_tstring_beg - @stack << false - when :on_embexpr_beg - @stack << false - return prev_state - when :on_tstring_end # :on_tstring_end may close Symbol - @stack.pop - return prev_state - when :on_embexpr_end - @stack.pop - end - @stack.last - end - end - private_constant :SymbolState - end -end diff --git a/lib/irb/color_printer.rb b/lib/irb/color_printer.rb deleted file mode 100644 index 7a7e817858..0000000000 --- a/lib/irb/color_printer.rb +++ /dev/null @@ -1,56 +0,0 @@ -# frozen_string_literal: true -require 'pp' -require_relative 'color' - -module IRB - class ColorPrinter < ::PP - class << self - def pp(obj, out = $>, width = screen_width, colorize: true) - q = ColorPrinter.new(out, width, colorize: colorize) - q.guard_inspect_key {q.pp obj} - q.flush - out << "\n" - end - - private - - def screen_width - Reline.get_screen_size.last - rescue Errno::EINVAL # in `winsize': Invalid argument - <STDIN> - 79 - end - end - - def initialize(out, width, colorize: true) - @colorize = colorize - - super(out, width) - end - - def pp(obj) - if String === obj - # Avoid calling Ruby 2.4+ String#pretty_print that splits a string by "\n" - text(obj.inspect) - else - super - end - end - - def text(str, width = nil) - unless str.is_a?(String) - str = str.inspect - end - width ||= str.length - - case str - when '' - when ',', '=>', '[', ']', '{', '}', '..', '...', /\A@\w+\z/ - super(str, width) - when /\A#</, '=', '>' - super(@colorize ? Color.colorize(str, [:GREEN]) : str, width) - else - super(@colorize ? Color.colorize_code(str, ignore_error: true) : str, width) - end - end - end -end diff --git a/lib/irb/command.rb b/lib/irb/command.rb deleted file mode 100644 index 68a4b52727..0000000000 --- a/lib/irb/command.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true -# -# irb/command.rb - irb command -# by Keiju ISHITSUKA([email protected]) -# - -require_relative "command/base" - -module IRB # :nodoc: - module Command - @commands = {} - - class << self - attr_reader :commands - - # Registers a command with the given name. - # Aliasing is intentionally not supported at the moment. - def register(name, command_class) - @commands[name.to_sym] = [command_class, []] - end - end - end -end diff --git a/lib/irb/command/backtrace.rb b/lib/irb/command/backtrace.rb deleted file mode 100644 index 687bb075ac..0000000000 --- a/lib/irb/command/backtrace.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require_relative "debug" - -module IRB - # :stopdoc: - - module Command - class Backtrace < DebugCommand - def execute(arg) - execute_debug_command(pre_cmds: "backtrace #{arg}") - end - end - end - - # :startdoc: -end diff --git a/lib/irb/command/base.rb b/lib/irb/command/base.rb deleted file mode 100644 index 2f39b75cca..0000000000 --- a/lib/irb/command/base.rb +++ /dev/null @@ -1,60 +0,0 @@ -# frozen_string_literal: true -# -# nop.rb - -# by Keiju ISHITSUKA([email protected]) -# - -module IRB - module Command - class CommandArgumentError < StandardError; end # :nodoc: - - class << self - def extract_ruby_args(*args, **kwargs) # :nodoc: - throw :EXTRACT_RUBY_ARGS, [args, kwargs] - end - end - - class Base - class << self - def category(category = nil) - @category = category if category - @category || "No category" - end - - def description(description = nil) - @description = description if description - @description || "No description provided." - end - - def help_message(help_message = nil) - @help_message = help_message if help_message - @help_message - end - - def execute(irb_context, arg) - new(irb_context).execute(arg) - rescue CommandArgumentError => e - puts e.message - end - - private - - def highlight(text) - Color.colorize(text, [:BOLD, :BLUE]) - end - end - - def initialize(irb_context) - @irb_context = irb_context - end - - attr_reader :irb_context - - def execute(arg) - #nop - end - end - - Nop = Base # :nodoc: - end -end diff --git a/lib/irb/command/break.rb b/lib/irb/command/break.rb deleted file mode 100644 index a8f81fe665..0000000000 --- a/lib/irb/command/break.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require_relative "debug" - -module IRB - # :stopdoc: - - module Command - class Break < DebugCommand - def execute(arg) - execute_debug_command(pre_cmds: "break #{arg}") - end - end - end - - # :startdoc: -end diff --git a/lib/irb/command/catch.rb b/lib/irb/command/catch.rb deleted file mode 100644 index 529dcbca5a..0000000000 --- a/lib/irb/command/catch.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require_relative "debug" - -module IRB - # :stopdoc: - - module Command - class Catch < DebugCommand - def execute(arg) - execute_debug_command(pre_cmds: "catch #{arg}") - end - end - end - - # :startdoc: -end diff --git a/lib/irb/command/cd.rb b/lib/irb/command/cd.rb deleted file mode 100644 index b83c8689ae..0000000000 --- a/lib/irb/command/cd.rb +++ /dev/null @@ -1,51 +0,0 @@ -# frozen_string_literal: true - -module IRB - module Command - class CD < Base - category "Workspace" - description "Move into the given object or leave the current context." - - help_message(<<~HELP) - Usage: cd ([target]|..) - - IRB uses a stack of workspaces to keep track of context(s), with `pushws` and `popws` commands to manipulate the stack. - The `cd` command is an attempt to simplify the operation and will be subject to change. - - When given: - - an object, cd will use that object as the new context by pushing it onto the workspace stack. - - "..", cd will leave the current context by popping the top workspace off the stack. - - no arguments, cd will move to the top workspace on the stack by popping off all workspaces. - - Examples: - - cd Foo - cd Foo.new - cd @ivar - cd .. - cd - HELP - - def execute(arg) - case arg - when ".." - irb_context.pop_workspace - when "" - # TODO: decide what workspace commands should be kept, and underlying APIs should look like, - # and perhaps add a new API to clear the workspace stack. - prev_workspace = irb_context.pop_workspace - while prev_workspace - prev_workspace = irb_context.pop_workspace - end - else - begin - obj = eval(arg, irb_context.workspace.binding) - irb_context.push_workspace(obj) - rescue StandardError => e - warn "Error: #{e}" - end - end - end - end - end -end diff --git a/lib/irb/command/chws.rb b/lib/irb/command/chws.rb deleted file mode 100644 index ef456d0961..0000000000 --- a/lib/irb/command/chws.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true -# -# change-ws.rb - -# by Keiju ISHITSUKA([email protected]) -# -require_relative "../ext/change-ws" - -module IRB - # :stopdoc: - - module Command - - class CurrentWorkingWorkspace < Base - category "Workspace" - description "Show the current workspace." - - def execute(_arg) - puts "Current workspace: #{irb_context.main}" - end - end - - class ChangeWorkspace < Base - category "Workspace" - description "Change the current workspace to an object." - - def execute(arg) - if arg.empty? - irb_context.change_workspace - else - obj = eval(arg, irb_context.workspace.binding) - irb_context.change_workspace(obj) - end - - puts "Current workspace: #{irb_context.main}" - end - end - end - - # :startdoc: -end diff --git a/lib/irb/command/context.rb b/lib/irb/command/context.rb deleted file mode 100644 index b4fc807343..0000000000 --- a/lib/irb/command/context.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -module IRB - module Command - class Context < Base - category "IRB" - description "Displays current configuration." - - def execute(_arg) - # This command just displays the configuration. - # Modifying the configuration is achieved by sending a message to IRB.conf. - Pager.page_content(IRB.CurrentContext.inspect) - end - end - end -end diff --git a/lib/irb/command/continue.rb b/lib/irb/command/continue.rb deleted file mode 100644 index 0daa029b15..0000000000 --- a/lib/irb/command/continue.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require_relative "debug" - -module IRB - # :stopdoc: - - module Command - class Continue < DebugCommand - def execute(arg) - execute_debug_command(do_cmds: "continue #{arg}") - end - end - end - - # :startdoc: -end diff --git a/lib/irb/command/copy.rb b/lib/irb/command/copy.rb deleted file mode 100644 index 93410b878a..0000000000 --- a/lib/irb/command/copy.rb +++ /dev/null @@ -1,73 +0,0 @@ -# frozen_string_literal: true - -module IRB - module Command - class Copy < Base - category "Misc" - description "Copy expression output to clipboard" - - help_message(<<~HELP) - Usage: copy ([expression]) - - When given: - - an expression, copy the inspect result of the expression to the clipboard. - - no arguments, copy the last evaluated result (`_`) to the clipboard. - - Examples: - - copy Foo.new - copy User.all.to_a - copy - HELP - - def execute(arg) - # Copy last value if no expression was supplied - arg = '_' if arg.to_s.strip.empty? - - value = irb_context.workspace.binding.eval(arg) - output = irb_context.inspect_method.inspect_value(value, +'', colorize: false).chomp - - if clipboard_available? - copy_to_clipboard(output) - else - warn "System clipboard not found" - end - rescue StandardError => e - warn "Error: #{e}" - end - - private - - def copy_to_clipboard(text) - IO.popen(clipboard_program, 'w') do |io| - io.write(text) - end - - raise IOError.new("Copying to clipboard failed") unless $? == 0 - - puts "Copied to system clipboard" - rescue Errno::ENOENT => e - warn e.message - warn "Is IRB.conf[:COPY_COMMAND] set to a bad value?" - end - - def clipboard_program - @clipboard_program ||= if IRB.conf[:COPY_COMMAND] - IRB.conf[:COPY_COMMAND] - elsif executable?("pbcopy") - "pbcopy" - elsif executable?("xclip") - "xclip -selection clipboard" - end - end - - def executable?(command) - system("which #{command} > /dev/null 2>&1") - end - - def clipboard_available? - !!clipboard_program - end - end - end -end diff --git a/lib/irb/command/debug.rb b/lib/irb/command/debug.rb deleted file mode 100644 index 3ebb57fe54..0000000000 --- a/lib/irb/command/debug.rb +++ /dev/null @@ -1,73 +0,0 @@ -require_relative "../debug" - -module IRB - # :stopdoc: - - module Command - class Debug < Base - category "Debugging" - description "Start the debugger of debug.gem." - - def execute(_arg) - execute_debug_command - end - - def execute_debug_command(pre_cmds: nil, do_cmds: nil) - pre_cmds = pre_cmds&.rstrip - do_cmds = do_cmds&.rstrip - - if irb_context.with_debugger - # If IRB is already running with a debug session, throw the command and IRB.debug_readline will pass it to the debugger. - if cmd = pre_cmds || do_cmds - throw :IRB_EXIT, cmd - else - puts "IRB is already running with a debug session." - return - end - else - # If IRB is not running with a debug session yet, then: - # 1. Check if the debugging command is run from a `binding.irb` call. - # 2. If so, try setting up the debug gem. - # 3. Insert a debug breakpoint at `Irb#debug_break` with the intended command. - # 4. Exit the current Irb#run call via `throw :IRB_EXIT`. - # 5. `Irb#debug_break` will be called and trigger the breakpoint, which will run the intended command. - unless irb_context.from_binding? - puts "Debugging commands are only available when IRB is started with binding.irb" - return - end - - if IRB.respond_to?(:JobManager) - warn "Can't start the debugger when IRB is running in a multi-IRB session." - return - end - - unless IRB::Debug.setup(irb_context.irb) - puts <<~MSG - You need to install the debug gem before using this command. - If you use `bundle exec`, please add `gem "debug"` into your Gemfile. - MSG - return - end - - IRB::Debug.insert_debug_break(pre_cmds: pre_cmds, do_cmds: do_cmds) - - # exit current Irb#run call - throw :IRB_EXIT - end - end - end - - class DebugCommand < Debug - class << self - def category - "Debugging" - end - - def description - command_name = self.name.split("::").last.downcase - "Start the debugger of debug.gem and run its `#{command_name}` command." - end - end - end - end -end diff --git a/lib/irb/command/delete.rb b/lib/irb/command/delete.rb deleted file mode 100644 index 2a57a4a3de..0000000000 --- a/lib/irb/command/delete.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require_relative "debug" - -module IRB - # :stopdoc: - - module Command - class Delete < DebugCommand - def execute(arg) - execute_debug_command(pre_cmds: "delete #{arg}") - end - end - end - - # :startdoc: -end diff --git a/lib/irb/command/disable_irb.rb b/lib/irb/command/disable_irb.rb deleted file mode 100644 index 0b00d0302b..0000000000 --- a/lib/irb/command/disable_irb.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -module IRB - # :stopdoc: - - module Command - class DisableIrb < Base - category "IRB" - description "Disable binding.irb." - - def execute(*) - ::Binding.define_method(:irb) {} - IRB.irb_exit - end - end - end - - # :startdoc: -end diff --git a/lib/irb/command/edit.rb b/lib/irb/command/edit.rb deleted file mode 100644 index cb7e0c4873..0000000000 --- a/lib/irb/command/edit.rb +++ /dev/null @@ -1,63 +0,0 @@ -require 'shellwords' - -require_relative "../color" -require_relative "../source_finder" - -module IRB - # :stopdoc: - - module Command - class Edit < Base - include RubyArgsExtractor - - category "Misc" - description 'Open a file or source location.' - help_message <<~HELP_MESSAGE - Usage: edit [FILE or constant or method signature] - - Open a file in the editor specified in #{highlight('ENV["VISUAL"]')} or #{highlight('ENV["EDITOR"]')} - - - If no arguments are provided, IRB will attempt to open the file the current context was defined in. - - If FILE is provided, IRB will open the file. - - If a constant or method signature is provided, IRB will attempt to locate the source file and open it. - - Examples: - - edit - edit foo.rb - edit Foo - edit Foo#bar - HELP_MESSAGE - - def execute(arg) - # Accept string literal for backward compatibility - path = unwrap_string_literal(arg) - - if path.nil? - path = @irb_context.irb_path - elsif !File.exist?(path) - source = SourceFinder.new(@irb_context).find_source(path) - - if source&.file_exist? && !source.binary_file? - path = source.file - end - end - - unless File.exist?(path) - puts "Can not find file: #{path}" - return - end - - if editor = (ENV['VISUAL'] || ENV['EDITOR']) - puts "command: '#{editor}'" - puts " path: #{path}" - system(*Shellwords.split(editor), path) - else - puts "Can not find editor setting: ENV['VISUAL'] or ENV['EDITOR']" - end - end - end - end - - # :startdoc: -end diff --git a/lib/irb/command/exit.rb b/lib/irb/command/exit.rb deleted file mode 100644 index b4436f0343..0000000000 --- a/lib/irb/command/exit.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -module IRB - # :stopdoc: - - module Command - class Exit < Base - category "IRB" - description "Exit the current irb session." - - def execute(_arg) - IRB.irb_exit - end - end - end - - # :startdoc: -end diff --git a/lib/irb/command/finish.rb b/lib/irb/command/finish.rb deleted file mode 100644 index 3311a0e6e9..0000000000 --- a/lib/irb/command/finish.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require_relative "debug" - -module IRB - # :stopdoc: - - module Command - class Finish < DebugCommand - def execute(arg) - execute_debug_command(do_cmds: "finish #{arg}") - end - end - end - - # :startdoc: -end diff --git a/lib/irb/command/force_exit.rb b/lib/irb/command/force_exit.rb deleted file mode 100644 index 14086aa849..0000000000 --- a/lib/irb/command/force_exit.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -module IRB - # :stopdoc: - - module Command - class ForceExit < Base - category "IRB" - description "Exit the current process." - - def execute(_arg) - throw :IRB_EXIT, true - end - end - end - - # :startdoc: -end diff --git a/lib/irb/command/help.rb b/lib/irb/command/help.rb deleted file mode 100644 index 12b468fefc..0000000000 --- a/lib/irb/command/help.rb +++ /dev/null @@ -1,83 +0,0 @@ -# frozen_string_literal: true - -module IRB - module Command - class Help < Base - category "Help" - description "List all available commands. Use `help <command>` to get information about a specific command." - - def execute(command_name) - content = - if command_name.empty? - help_message - else - if command_class = Command.load_command(command_name) - command_class.help_message || command_class.description - else - "Can't find command `#{command_name}`. Please check the command name and try again.\n\n" - end - end - Pager.page_content(content) - end - - private - - def help_message - commands_info = IRB::Command.all_commands_info - helper_methods_info = IRB::HelperMethod.all_helper_methods_info - commands_grouped_by_categories = commands_info.group_by { |cmd| cmd[:category] } - commands_grouped_by_categories["Helper methods"] = helper_methods_info - - if irb_context.with_debugger - # Remove the original "Debugging" category - commands_grouped_by_categories.delete("Debugging") - end - - longest_cmd_name_length = commands_info.map { |c| c[:display_name].length }.max - - output = StringIO.new - - help_cmds = commands_grouped_by_categories.delete("Help") - no_category_cmds = commands_grouped_by_categories.delete("No category") - aliases = irb_context.instance_variable_get(:@command_aliases).map do |alias_name, target| - { display_name: alias_name, description: "Alias for `#{target}`" } - end - - # Display help commands first - add_category_to_output("Help", help_cmds, output, longest_cmd_name_length) - - # Display the rest of the commands grouped by categories - commands_grouped_by_categories.each do |category, cmds| - add_category_to_output(category, cmds, output, longest_cmd_name_length) - end - - # Display commands without a category - if no_category_cmds - add_category_to_output("No category", no_category_cmds, output, longest_cmd_name_length) - end - - # Display aliases - add_category_to_output("Aliases", aliases, output, longest_cmd_name_length) - - # Append the debugger help at the end - if irb_context.with_debugger - # Add "Debugging (from debug.gem)" category as title - add_category_to_output("Debugging (from debug.gem)", [], output, longest_cmd_name_length) - output.puts DEBUGGER__.help - end - - output.string - end - - def add_category_to_output(category, cmds, output, longest_cmd_name_length) - output.puts Color.colorize(category, [:BOLD]) - - cmds.each do |cmd| - output.puts " #{cmd[:display_name].to_s.ljust(longest_cmd_name_length)} #{cmd[:description]}" - end - - output.puts - end - end - end -end diff --git a/lib/irb/command/history.rb b/lib/irb/command/history.rb deleted file mode 100644 index e385c66102..0000000000 --- a/lib/irb/command/history.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -require "stringio" - -require_relative "../pager" - -module IRB - # :stopdoc: - - module Command - class History < Base - category "IRB" - description "Shows the input history. `-g [query]` or `-G [query]` allows you to filter the output." - - def execute(arg) - - if (match = arg&.match(/(-g|-G)\s+(?<grep>.+)\s*\z/)) - grep = Regexp.new(match[:grep]) - end - - formatted_inputs = irb_context.io.class::HISTORY.each_with_index.reverse_each.filter_map do |input, index| - next if grep && !input.match?(grep) - - header = "#{index}: " - - first_line, *other_lines = input.split("\n") - first_line = "#{header}#{first_line}" - - truncated_lines = other_lines.slice!(1..) # Show 1 additional line (2 total) - other_lines << "..." if truncated_lines&.any? - - other_lines.map! do |line| - " " * header.length + line - end - - [first_line, *other_lines].join("\n") + "\n" - end - - Pager.page_content(formatted_inputs.join) - end - end - end - - # :startdoc: -end diff --git a/lib/irb/command/info.rb b/lib/irb/command/info.rb deleted file mode 100644 index d08ce00a32..0000000000 --- a/lib/irb/command/info.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require_relative "debug" - -module IRB - # :stopdoc: - - module Command - class Info < DebugCommand - def execute(arg) - execute_debug_command(pre_cmds: "info #{arg}") - end - end - end - - # :startdoc: -end diff --git a/lib/irb/command/internal_helpers.rb b/lib/irb/command/internal_helpers.rb deleted file mode 100644 index a01ddb1d45..0000000000 --- a/lib/irb/command/internal_helpers.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -module IRB - module Command - # Internal use only, for default command's backward compatibility. - module RubyArgsExtractor # :nodoc: - def unwrap_string_literal(str) - return if str.empty? - - sexp = Ripper.sexp(str) - if sexp && sexp.size == 2 && sexp.last&.first&.first == :string_literal - @irb_context.workspace.binding.eval(str).to_s - else - str - end - end - - def ruby_args(arg) - # Use throw and catch to handle arg that includes `;` - # For example: "1, kw: (2; 3); 4" will be parsed to [[1], { kw: 3 }] - catch(:EXTRACT_RUBY_ARGS) do - @irb_context.workspace.binding.eval "::IRB::Command.extract_ruby_args #{arg}" - end || [[], {}] - end - end - end -end diff --git a/lib/irb/command/irb_info.rb b/lib/irb/command/irb_info.rb deleted file mode 100644 index 6d868de94c..0000000000 --- a/lib/irb/command/irb_info.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -module IRB - # :stopdoc: - - module Command - class IrbInfo < Base - category "IRB" - description "Show information about IRB." - - def execute(_arg) - str = "Ruby version: #{RUBY_VERSION}\n" - str += "IRB version: #{IRB.version}\n" - str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n" - str += "Completion: #{IRB.CurrentContext.io.respond_to?(:completion_info) ? IRB.CurrentContext.io.completion_info : 'off'}\n" - rc_files = IRB.irbrc_files - str += ".irbrc paths: #{rc_files.join(", ")}\n" if rc_files.any? - str += "RUBY_PLATFORM: #{RUBY_PLATFORM}\n" - str += "LANG env: #{ENV["LANG"]}\n" if ENV["LANG"] && !ENV["LANG"].empty? - str += "LC_ALL env: #{ENV["LC_ALL"]}\n" if ENV["LC_ALL"] && !ENV["LC_ALL"].empty? - str += "East Asian Ambiguous Width: #{Reline.ambiguous_width.inspect}\n" - if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/ - codepage = `chcp`.b.sub(/.*: (\d+)\n/, '\1') - str += "Code page: #{codepage}\n" - end - puts str - nil - end - end - end - - # :startdoc: -end diff --git a/lib/irb/command/load.rb b/lib/irb/command/load.rb deleted file mode 100644 index 1cd3f279d1..0000000000 --- a/lib/irb/command/load.rb +++ /dev/null @@ -1,91 +0,0 @@ -# frozen_string_literal: true -# -# load.rb - -# by Keiju ISHITSUKA([email protected]) -# -require_relative "../ext/loader" - -module IRB - # :stopdoc: - - module Command - class LoaderCommand < Base - include RubyArgsExtractor - include IrbLoader - - def raise_cmd_argument_error - raise CommandArgumentError.new("Please specify the file name.") - end - end - - class Load < LoaderCommand - category "IRB" - description "Load a Ruby file." - - def execute(arg) - args, kwargs = ruby_args(arg) - execute_internal(*args, **kwargs) - end - - def execute_internal(file_name = nil, priv = nil) - raise_cmd_argument_error unless file_name - irb_load(file_name, priv) - end - end - - class Require < LoaderCommand - category "IRB" - description "Require a Ruby file." - - def execute(arg) - args, kwargs = ruby_args(arg) - execute_internal(*args, **kwargs) - end - - def execute_internal(file_name = nil) - raise_cmd_argument_error unless file_name - - rex = Regexp.new("#{Regexp.quote(file_name)}(\.o|\.rb)?") - return false if $".find{|f| f =~ rex} - - case file_name - when /\.rb$/ - begin - if irb_load(file_name) - $".push file_name - return true - end - rescue LoadError - end - when /\.(so|o|sl)$/ - return ruby_require(file_name) - end - - begin - irb_load(f = file_name + ".rb") - $".push f - return true - rescue LoadError - return ruby_require(file_name) - end - end - end - - class Source < LoaderCommand - category "IRB" - description "Loads a given file in the current session." - - def execute(arg) - args, kwargs = ruby_args(arg) - execute_internal(*args, **kwargs) - end - - def execute_internal(file_name = nil) - raise_cmd_argument_error unless file_name - - source_file(file_name) - end - end - end - # :startdoc: -end diff --git a/lib/irb/command/ls.rb b/lib/irb/command/ls.rb deleted file mode 100644 index 944efd7570..0000000000 --- a/lib/irb/command/ls.rb +++ /dev/null @@ -1,167 +0,0 @@ -# frozen_string_literal: true - -require "reline" -require "stringio" - -require_relative "../pager" -require_relative "../color" - -module IRB - # :stopdoc: - - module Command - class Ls < Base - class EvaluationError < StandardError; end - - category "Context" - description "Show methods, constants, and variables." - - help_message <<~HELP_MESSAGE - Usage: ls [obj] [-g [query]] - - -g [query] Filter the output with a query. - HELP_MESSAGE - - def evaluate(code) - @irb_context.workspace.binding.eval(code) - rescue Exception => e - puts "#{e.class}: #{e.message}" - raise EvaluationError - end - - def execute(arg) - if match = arg.match(/\A(?<target>.+\s|)(-g|-G)\s+(?<grep>.+)$/) - target = match[:target] - grep = Regexp.new(match[:grep]) - elsif match = arg.match(/\A((?<target>.+),|)\s*grep:(?<grep>.+)/) - # Legacy style `ls obj, grep: /regexp/` - # Evaluation order should be eval(target) then eval(grep) - target = match[:target] || '' - grep_regexp_code = match[:grep] - else - target = arg.strip - end - - if target.empty? - obj = irb_context.workspace.main - locals = irb_context.workspace.binding.local_variables - else - obj = evaluate(target) - end - - if grep_regexp_code - grep = evaluate(grep_regexp_code) - end - - o = Output.new(grep: grep) - - klass = (obj.class == Class || obj.class == Module ? obj : obj.class) - - o.dump("constants", obj.constants) if obj.respond_to?(:constants) - dump_methods(o, klass, obj) - o.dump("instance variables", obj.instance_variables) - o.dump("class variables", klass.class_variables) - o.dump("locals", locals) if locals - o.print_result - rescue EvaluationError - end - - def dump_methods(o, klass, obj) - singleton_class = begin obj.singleton_class; rescue TypeError; nil end - dumped_mods = Array.new - ancestors = klass.ancestors - ancestors = ancestors.reject { |c| c >= Object } if klass < Object - singleton_ancestors = (singleton_class&.ancestors || []).reject { |c| c >= Class } - - # singleton_class' ancestors should be at the front - maps = class_method_map(singleton_ancestors, dumped_mods) + class_method_map(ancestors, dumped_mods) - maps.each do |mod, methods| - name = mod == singleton_class ? "#{klass}.methods" : "#{mod}#methods" - o.dump(name, methods) - end - end - - def class_method_map(classes, dumped_mods) - dumped_methods = Array.new - classes.map do |mod| - next if dumped_mods.include? mod - - dumped_mods << mod - - methods = mod.public_instance_methods(false).select do |method| - if dumped_methods.include? method - false - else - dumped_methods << method - true - end - end - - [mod, methods] - end.compact - end - - class Output - MARGIN = " " - - def initialize(grep: nil) - @grep = grep - @line_width = screen_width - MARGIN.length # right padding - @io = StringIO.new - end - - def print_result - Pager.page_content(@io.string) - end - - def dump(name, strs) - strs = strs.grep(@grep) if @grep - strs = strs.sort - return if strs.empty? - - # Attempt a single line - @io.print "#{Color.colorize(name, [:BOLD, :BLUE])}: " - if fits_on_line?(strs, cols: strs.size, offset: "#{name}: ".length) - @io.puts strs.join(MARGIN) - return - end - @io.puts - - # Dump with the largest # of columns that fits on a line - cols = strs.size - until fits_on_line?(strs, cols: cols, offset: MARGIN.length) || cols == 1 - cols -= 1 - end - widths = col_widths(strs, cols: cols) - strs.each_slice(cols) do |ss| - @io.puts ss.map.with_index { |s, i| "#{MARGIN}%-#{widths[i]}s" % s }.join - end - end - - private - - def fits_on_line?(strs, cols:, offset: 0) - width = col_widths(strs, cols: cols).sum + MARGIN.length * (cols - 1) - width <= @line_width - offset - end - - def col_widths(strs, cols:) - cols.times.map do |col| - (col...strs.size).step(cols).map do |i| - strs[i].length - end.max - end - end - - def screen_width - Reline.get_screen_size.last - rescue Errno::EINVAL # in `winsize': Invalid argument - <STDIN> - 80 - end - end - private_constant :Output - end - end - - # :startdoc: -end diff --git a/lib/irb/command/measure.rb b/lib/irb/command/measure.rb deleted file mode 100644 index f96be20de8..0000000000 --- a/lib/irb/command/measure.rb +++ /dev/null @@ -1,49 +0,0 @@ -module IRB - # :stopdoc: - - module Command - class Measure < Base - include RubyArgsExtractor - - category "Misc" - description "`measure` enables the mode to measure processing time. `measure :off` disables it." - - def initialize(*args) - super(*args) - end - - def execute(arg) - if arg&.match?(/^do$|^do[^\w]|^\{/) - warn 'Configure IRB.conf[:MEASURE_PROC] to add custom measure methods.' - return - end - args, kwargs = ruby_args(arg) - execute_internal(*args, **kwargs) - end - - def execute_internal(type = nil, arg = nil) - # Please check IRB.init_config in lib/irb/init.rb that sets - # IRB.conf[:MEASURE_PROC] to register default "measure" methods, - # "measure :time" (abbreviated as "measure") and "measure :stackprof". - - case type - when :off - IRB.unset_measure_callback(arg) - when :list - IRB.conf[:MEASURE_CALLBACKS].each do |type_name, _, arg_val| - puts "- #{type_name}" + (arg_val ? "(#{arg_val.inspect})" : '') - end - when :on - added = IRB.set_measure_callback(arg) - puts "#{added[0]} is added." if added - else - added = IRB.set_measure_callback(type, arg) - puts "#{added[0]} is added." if added - end - nil - end - end - end - - # :startdoc: -end diff --git a/lib/irb/command/next.rb b/lib/irb/command/next.rb deleted file mode 100644 index 3fc6b68d21..0000000000 --- a/lib/irb/command/next.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require_relative "debug" - -module IRB - # :stopdoc: - - module Command - class Next < DebugCommand - def execute(arg) - execute_debug_command(do_cmds: "next #{arg}") - end - end - end - - # :startdoc: -end diff --git a/lib/irb/command/pushws.rb b/lib/irb/command/pushws.rb deleted file mode 100644 index b51928c650..0000000000 --- a/lib/irb/command/pushws.rb +++ /dev/null @@ -1,65 +0,0 @@ -# frozen_string_literal: true -# -# change-ws.rb - -# by Keiju ISHITSUKA([email protected]) -# - -require_relative "../ext/workspaces" - -module IRB - # :stopdoc: - - module Command - class Workspaces < Base - category "Workspace" - description "Show workspaces." - - def execute(_arg) - inspection_resuls = irb_context.instance_variable_get(:@workspace_stack).map do |ws| - truncated_inspect(ws.main) - end - - puts "[" + inspection_resuls.join(", ") + "]" - end - - private - - def truncated_inspect(obj) - obj_inspection = obj.inspect - - if obj_inspection.size > 20 - obj_inspection = obj_inspection[0, 19] + "...>" - end - - obj_inspection - end - end - - class PushWorkspace < Workspaces - category "Workspace" - description "Push an object to the workspace stack." - - def execute(arg) - if arg.empty? - irb_context.push_workspace - else - obj = eval(arg, irb_context.workspace.binding) - irb_context.push_workspace(obj) - end - super - end - end - - class PopWorkspace < Workspaces - category "Workspace" - description "Pop a workspace from the workspace stack." - - def execute(_arg) - irb_context.pop_workspace - super - end - end - end - - # :startdoc: -end diff --git a/lib/irb/command/show_doc.rb b/lib/irb/command/show_doc.rb deleted file mode 100644 index 8a2188e4eb..0000000000 --- a/lib/irb/command/show_doc.rb +++ /dev/null @@ -1,51 +0,0 @@ -# frozen_string_literal: true - -module IRB - module Command - class ShowDoc < Base - include RubyArgsExtractor - - category "Context" - description "Look up documentation with RI." - - help_message <<~HELP_MESSAGE - Usage: show_doc [name] - - When name is provided, IRB will look up the documentation for the given name. - When no name is provided, a RI session will be started. - - Examples: - - show_doc - show_doc Array - show_doc Array#each - - HELP_MESSAGE - - def execute(arg) - # Accept string literal for backward compatibility - name = unwrap_string_literal(arg) - require 'rdoc/ri/driver' - - unless ShowDoc.const_defined?(:Ri) - opts = RDoc::RI::Driver.process_args([]) - ShowDoc.const_set(:Ri, RDoc::RI::Driver.new(opts)) - end - - if name.nil? - Ri.interactive - else - begin - Ri.display_name(name) - rescue RDoc::RI::Error - puts $!.message - end - end - - nil - rescue LoadError, SystemExit - warn "Can't display document because `rdoc` is not installed." - end - end - end -end diff --git a/lib/irb/command/show_source.rb b/lib/irb/command/show_source.rb deleted file mode 100644 index f4c6f104a2..0000000000 --- a/lib/irb/command/show_source.rb +++ /dev/null @@ -1,74 +0,0 @@ -# frozen_string_literal: true - -require_relative "../source_finder" -require_relative "../pager" -require_relative "../color" - -module IRB - module Command - class ShowSource < Base - include RubyArgsExtractor - - category "Context" - description "Show the source code of a given method, class/module, or constant." - - help_message <<~HELP_MESSAGE - Usage: show_source [target] [-s] - - -s Show the super method. You can stack it like `-ss` to show the super of the super, etc. - - Examples: - - show_source Foo - show_source Foo#bar - show_source Foo#bar -s - show_source Foo.baz - show_source Foo::BAR - HELP_MESSAGE - - def execute(arg) - # Accept string literal for backward compatibility - str = unwrap_string_literal(arg) - unless str.is_a?(String) - puts "Error: Expected a string but got #{str.inspect}" - return - end - - str, esses = str.split(" -") - super_level = esses ? esses.count("s") : 0 - source = SourceFinder.new(@irb_context).find_source(str, super_level) - - if source - show_source(source) - elsif super_level > 0 - puts "Error: Couldn't locate a super definition for #{str}" - else - puts "Error: Couldn't locate a definition for #{str}" - end - nil - end - - private - - def show_source(source) - if source.binary_file? - content = "\n#{bold('Defined in binary file')}: #{source.file}\n\n" - else - code = source.colorized_content || 'Source not available' - content = <<~CONTENT - - #{bold("From")}: #{source.file}:#{source.line} - - #{code.chomp} - - CONTENT - end - Pager.page_content(content) - end - - def bold(str) - Color.colorize(str, [:BOLD]) - end - end - end -end diff --git a/lib/irb/command/step.rb b/lib/irb/command/step.rb deleted file mode 100644 index 29e5e35ac0..0000000000 --- a/lib/irb/command/step.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require_relative "debug" - -module IRB - # :stopdoc: - - module Command - class Step < DebugCommand - def execute(arg) - execute_debug_command(do_cmds: "step #{arg}") - end - end - end - - # :startdoc: -end diff --git a/lib/irb/command/subirb.rb b/lib/irb/command/subirb.rb deleted file mode 100644 index 85af28c1a5..0000000000 --- a/lib/irb/command/subirb.rb +++ /dev/null @@ -1,123 +0,0 @@ -# frozen_string_literal: true -# -# multi.rb - -# by Keiju ISHITSUKA([email protected]) -# - -module IRB - # :stopdoc: - - module Command - class MultiIRBCommand < Base - include RubyArgsExtractor - - private - - def print_deprecated_warning - warn <<~MSG - Multi-irb commands are deprecated and will be removed in IRB 2.0.0. Please use workspace commands instead. - If you have any use case for multi-irb, please leave a comment at https://github.com/ruby/irb/issues/653 - MSG - end - - def extend_irb_context - # this extension patches IRB context like IRB.CurrentContext - require_relative "../ext/multi-irb" - end - - def print_debugger_warning - warn "Multi-IRB commands are not available when the debugger is enabled." - end - end - - class IrbCommand < MultiIRBCommand - category "Multi-irb (DEPRECATED)" - description "Start a child IRB." - - def execute(arg) - args, kwargs = ruby_args(arg) - execute_internal(*args, **kwargs) - end - - def execute_internal(*obj) - print_deprecated_warning - - if irb_context.with_debugger - print_debugger_warning - return - end - - extend_irb_context - IRB.irb(nil, *obj) - puts IRB.JobManager.inspect - end - end - - class Jobs < MultiIRBCommand - category "Multi-irb (DEPRECATED)" - description "List of current sessions." - - def execute(_arg) - print_deprecated_warning - - if irb_context.with_debugger - print_debugger_warning - return - end - - extend_irb_context - puts IRB.JobManager.inspect - end - end - - class Foreground < MultiIRBCommand - category "Multi-irb (DEPRECATED)" - description "Switches to the session of the given number." - - def execute(arg) - args, kwargs = ruby_args(arg) - execute_internal(*args, **kwargs) - end - - def execute_internal(key = nil) - print_deprecated_warning - - if irb_context.with_debugger - print_debugger_warning - return - end - - extend_irb_context - - raise CommandArgumentError.new("Please specify the id of target IRB job (listed in the `jobs` command).") unless key - IRB.JobManager.switch(key) - puts IRB.JobManager.inspect - end - end - - class Kill < MultiIRBCommand - category "Multi-irb (DEPRECATED)" - description "Kills the session with the given number." - - def execute(arg) - args, kwargs = ruby_args(arg) - execute_internal(*args, **kwargs) - end - - def execute_internal(*keys) - print_deprecated_warning - - if irb_context.with_debugger - print_debugger_warning - return - end - - extend_irb_context - IRB.JobManager.kill(*keys) - puts IRB.JobManager.inspect - end - end - end - - # :startdoc: -end diff --git a/lib/irb/command/whereami.rb b/lib/irb/command/whereami.rb deleted file mode 100644 index c8439f1212..0000000000 --- a/lib/irb/command/whereami.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -module IRB - # :stopdoc: - - module Command - class Whereami < Base - category "Context" - description "Show the source code around binding.irb again." - - def execute(_arg) - code = irb_context.workspace.code_around_binding - if code - puts code - else - puts "The current context doesn't have code." - end - end - end - end - - # :startdoc: -end diff --git a/lib/irb/completion.rb b/lib/irb/completion.rb deleted file mode 100644 index 3e97047067..0000000000 --- a/lib/irb/completion.rb +++ /dev/null @@ -1,504 +0,0 @@ -# frozen_string_literal: true -# -# irb/completion.rb - -# by Keiju ISHITSUKA([email protected]) -# From Original Idea of [email protected] -# - -require_relative 'ruby-lex' - -module IRB - class BaseCompletor # :nodoc: - - # Set of reserved words used by Ruby, you should not use these for - # constants or variables - ReservedWords = %w[ - __ENCODING__ __LINE__ __FILE__ - BEGIN END - alias and - begin break - case class - def defined? do - else elsif end ensure - false for - if in - module - next nil not - or - redo rescue retry return - self super - then true - undef unless until - when while - yield - ] - - HELP_COMMAND_PREPOSING = /\Ahelp\s+/ - - def completion_candidates(preposing, target, postposing, bind:) - raise NotImplementedError - end - - def doc_namespace(preposing, matched, postposing, bind:) - raise NotImplementedError - end - - GEM_PATHS = - if defined?(Gem::Specification) - Gem::Specification.latest_specs(true).map { |s| - s.require_paths.map { |p| - if File.absolute_path?(p) - p - else - File.join(s.full_gem_path, p) - end - } - }.flatten - else - [] - end.freeze - - def retrieve_gem_and_system_load_path - candidates = (GEM_PATHS | $LOAD_PATH) - candidates.map do |p| - if p.respond_to?(:to_path) - p.to_path - else - String(p) rescue nil - end - end.compact.sort - end - - def retrieve_files_to_require_from_load_path - @files_from_load_path ||= - ( - shortest = [] - rest = retrieve_gem_and_system_load_path.each_with_object([]) { |path, result| - begin - names = Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: path) - rescue Errno::ENOENT - nil - end - next if names.empty? - names.map! { |n| n.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '') }.sort! - shortest << names.shift - result.concat(names) - } - shortest.sort! | rest - ) - end - - def command_candidates(target) - if !target.empty? - IRB::Command.command_names.select { _1.start_with?(target) } - else - [] - end - end - - def retrieve_files_to_require_relative_from_current_dir - @files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path| - path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '') - } - end - end - - class TypeCompletor < BaseCompletor # :nodoc: - def initialize(context) - @context = context - end - - def inspect - ReplTypeCompletor.info - end - - def completion_candidates(preposing, target, _postposing, bind:) - # When completing the argument of `help` command, only commands should be candidates - return command_candidates(target) if preposing.match?(HELP_COMMAND_PREPOSING) - - commands = if preposing.empty? - command_candidates(target) - # It doesn't make sense to propose commands with other preposing - else - [] - end - - result = ReplTypeCompletor.analyze(preposing + target, binding: bind, filename: @context.irb_path) - - return commands unless result - - commands | result.completion_candidates.map { target + _1 } - end - - def doc_namespace(preposing, matched, _postposing, bind:) - result = ReplTypeCompletor.analyze(preposing + matched, binding: bind, filename: @context.irb_path) - result&.doc_namespace('') - end - end - - class RegexpCompletor < BaseCompletor # :nodoc: - KERNEL_METHODS = ::Kernel.instance_method(:methods) - KERNEL_PRIVATE_METHODS = ::Kernel.instance_method(:private_methods) - KERNEL_INSTANCE_VARIABLES = ::Kernel.instance_method(:instance_variables) - OBJECT_CLASS_INSTANCE_METHOD = ::Object.instance_method(:class) - MODULE_CONSTANTS_INSTANCE_METHOD = ::Module.instance_method(:constants) - - using Module.new { - refine ::Binding do - def eval_methods - KERNEL_METHODS.bind_call(receiver) - end - - def eval_private_methods - KERNEL_PRIVATE_METHODS.bind_call(receiver) - end - - def eval_instance_variables - KERNEL_INSTANCE_VARIABLES.bind_call(receiver) - end - - def eval_global_variables - ::Kernel.global_variables - end - - def eval_class_constants - klass = OBJECT_CLASS_INSTANCE_METHOD.bind_call(receiver) - MODULE_CONSTANTS_INSTANCE_METHOD.bind_call(klass) - end - end - } - - def inspect - 'RegexpCompletor' - end - - def complete_require_path(target, preposing, postposing) - if target =~ /\A(['"])([^'"]+)\Z/ - quote = $1 - actual_target = $2 - else - return nil # It's not String literal - end - tokens = RubyLex.ripper_lex_without_warning(preposing.gsub(/\s*\z/, '')) - tok = nil - tokens.reverse_each do |t| - unless [:on_lparen, :on_sp, :on_ignored_sp, :on_nl, :on_ignored_nl, :on_comment].include?(t.event) - tok = t - break - end - end - return unless tok&.event == :on_ident && tok.state == Ripper::EXPR_CMDARG - - case tok.tok - when 'require' - retrieve_files_to_require_from_load_path.select { |path| - path.start_with?(actual_target) - }.map { |path| - quote + path - } - when 'require_relative' - retrieve_files_to_require_relative_from_current_dir.select { |path| - path.start_with?(actual_target) - }.map { |path| - quote + path - } - end - end - - def completion_candidates(preposing, target, postposing, bind:) - if result = complete_require_path(target, preposing, postposing) - return result - end - - commands = command_candidates(target) - - # When completing the argument of `help` command, only commands should be candidates - return commands if preposing.match?(HELP_COMMAND_PREPOSING) - - # It doesn't make sense to propose commands with other preposing - commands = [] unless preposing.empty? - - completion_data = retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.map{ |i| i.encode(Encoding.default_external) } - commands | completion_data - end - - def doc_namespace(_preposing, matched, _postposing, bind:) - retrieve_completion_data(matched, bind: bind, doc_namespace: true) - end - - def retrieve_completion_data(input, bind:, doc_namespace:) - case input - # this regexp only matches the closing character because of irb's Reline.completer_quote_characters setting - # details are described in: https://github.com/ruby/irb/pull/523 - when /^(.*["'`])\.([^.]*)$/ - # String - receiver = $1 - message = $2 - - if doc_namespace - "String.#{message}" - else - candidates = String.instance_methods.collect{|m| m.to_s} - select_message(receiver, message, candidates) - end - - # this regexp only matches the closing character because of irb's Reline.completer_quote_characters setting - # details are described in: https://github.com/ruby/irb/pull/523 - when /^(.*\/)\.([^.]*)$/ - # Regexp - receiver = $1 - message = $2 - - if doc_namespace - "Regexp.#{message}" - else - candidates = Regexp.instance_methods.collect{|m| m.to_s} - select_message(receiver, message, candidates) - end - - when /^([^\]]*\])\.([^.]*)$/ - # Array - receiver = $1 - message = $2 - - if doc_namespace - "Array.#{message}" - else - candidates = Array.instance_methods.collect{|m| m.to_s} - select_message(receiver, message, candidates) - end - - when /^([^\}]*\})\.([^.]*)$/ - # Hash or Proc - receiver = $1 - message = $2 - - if doc_namespace - ["Hash.#{message}", "Proc.#{message}"] - else - hash_candidates = Hash.instance_methods.collect{|m| m.to_s} - proc_candidates = Proc.instance_methods.collect{|m| m.to_s} - select_message(receiver, message, hash_candidates | proc_candidates) - end - - when /^(:[^:.]+)$/ - # Symbol - if doc_namespace - nil - else - sym = $1 - candidates = Symbol.all_symbols.collect do |s| - s.inspect - rescue EncodingError - # ignore - end - candidates.grep(/^#{Regexp.quote(sym)}/) - end - when /^::([A-Z][^:\.\(\)]*)$/ - # Absolute Constant or class methods - receiver = $1 - - candidates = Object.constants.collect{|m| m.to_s} - - if doc_namespace - candidates.find { |i| i == receiver } - else - candidates.grep(/^#{Regexp.quote(receiver)}/).collect{|e| "::" + e} - end - - when /^([A-Z].*)::([^:.]*)$/ - # Constant or class methods - receiver = $1 - message = $2 - - if doc_namespace - "#{receiver}::#{message}" - else - begin - candidates = eval("#{receiver}.constants.collect{|m| m.to_s}", bind) - candidates |= eval("#{receiver}.methods.collect{|m| m.to_s}", bind) - rescue Exception - candidates = [] - end - - select_message(receiver, message, candidates.sort, "::") - end - - when /^(:[^:.]+)(\.|::)([^.]*)$/ - # Symbol - receiver = $1 - sep = $2 - message = $3 - - if doc_namespace - "Symbol.#{message}" - else - candidates = Symbol.instance_methods.collect{|m| m.to_s} - select_message(receiver, message, candidates, sep) - end - - when /^(?<num>-?(?:0[dbo])?[0-9_]+(?:\.[0-9_]+)?(?:(?:[eE][+-]?[0-9]+)?i?|r)?)(?<sep>\.|::)(?<mes>[^.]*)$/ - # Numeric - receiver = $~[:num] - sep = $~[:sep] - message = $~[:mes] - - begin - instance = eval(receiver, bind) - - if doc_namespace - "#{instance.class.name}.#{message}" - else - candidates = instance.methods.collect{|m| m.to_s} - select_message(receiver, message, candidates, sep) - end - rescue Exception - if doc_namespace - nil - else - [] - end - end - - when /^(-?0x[0-9a-fA-F_]+)(\.|::)([^.]*)$/ - # Numeric(0xFFFF) - receiver = $1 - sep = $2 - message = $3 - - begin - instance = eval(receiver, bind) - if doc_namespace - "#{instance.class.name}.#{message}" - else - candidates = instance.methods.collect{|m| m.to_s} - select_message(receiver, message, candidates, sep) - end - rescue Exception - if doc_namespace - nil - else - [] - end - end - - when /^(\$[^.]*)$/ - # global var - gvar = $1 - all_gvars = global_variables.collect{|m| m.to_s} - - if doc_namespace - all_gvars.find{ |i| i == gvar } - else - all_gvars.grep(Regexp.new(Regexp.quote(gvar))) - end - - when /^([^.:"].*)(\.|::)([^.]*)$/ - # variable.func or func.func - receiver = $1 - sep = $2 - message = $3 - - gv = bind.eval_global_variables.collect{|m| m.to_s}.push("true", "false", "nil") - lv = bind.local_variables.collect{|m| m.to_s} - iv = bind.eval_instance_variables.collect{|m| m.to_s} - cv = bind.eval_class_constants.collect{|m| m.to_s} - - if (gv | lv | iv | cv).include?(receiver) or /^[A-Z]/ =~ receiver && /\./ !~ receiver - # foo.func and foo is var. OR - # foo::func and foo is var. OR - # foo::Const and foo is var. OR - # Foo::Bar.func - begin - candidates = [] - rec = eval(receiver, bind) - if sep == "::" and rec.kind_of?(Module) - candidates = rec.constants.collect{|m| m.to_s} - end - candidates |= rec.methods.collect{|m| m.to_s} - rescue Exception - candidates = [] - end - else - # func1.func2 - candidates = [] - end - - if doc_namespace - rec_class = rec.is_a?(Module) ? rec : rec.class - "#{rec_class.name}#{sep}#{candidates.find{ |i| i == message }}" rescue nil - else - select_message(receiver, message, candidates, sep) - end - - when /^\.([^.]*)$/ - # unknown(maybe String) - - receiver = "" - message = $1 - - candidates = String.instance_methods(true).collect{|m| m.to_s} - - if doc_namespace - "String.#{candidates.find{ |i| i == message }}" - else - select_message(receiver, message, candidates.sort) - end - when /^\s*$/ - # empty input - if doc_namespace - nil - else - [] - end - else - if doc_namespace - vars = (bind.local_variables | bind.eval_instance_variables).collect{|m| m.to_s} - perfect_match_var = vars.find{|m| m.to_s == input} - if perfect_match_var - eval("#{perfect_match_var}.class.name", bind) rescue nil - else - candidates = (bind.eval_methods | bind.eval_private_methods | bind.local_variables | bind.eval_instance_variables | bind.eval_class_constants).collect{|m| m.to_s} - candidates |= ReservedWords - candidates.find{ |i| i == input } - end - else - candidates = (bind.eval_methods | bind.eval_private_methods | bind.local_variables | bind.eval_instance_variables | bind.eval_class_constants).collect{|m| m.to_s} - candidates |= ReservedWords - candidates.grep(/^#{Regexp.quote(input)}/).sort - end - end - end - - # Set of available operators in Ruby - Operators = %w[% & * ** + - / < << <= <=> == === =~ > >= >> [] []= ^ ! != !~] - - def select_message(receiver, message, candidates, sep = ".") - candidates.grep(/^#{Regexp.quote(message)}/).collect do |e| - case e - when /^[a-zA-Z_]/ - receiver + sep + e - when /^[0-9]/ - when *Operators - #receiver + " " + e - end - end - end - end - - module InputCompletor - class << self - private def regexp_completor - @regexp_completor ||= RegexpCompletor.new - end - - def retrieve_completion_data(input, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding, doc_namespace: false) - regexp_completor.retrieve_completion_data(input, bind: bind, doc_namespace: doc_namespace) - end - end - CompletionProc = ->(target, preposing = nil, postposing = nil) { - regexp_completor.completion_candidates(preposing || '', target, postposing || '', bind: IRB.conf[:MAIN_CONTEXT].workspace.binding) - } - end - deprecate_constant :InputCompletor -end 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 diff --git a/lib/irb/debug.rb b/lib/irb/debug.rb deleted file mode 100644 index 59be1365bd..0000000000 --- a/lib/irb/debug.rb +++ /dev/null @@ -1,127 +0,0 @@ -# frozen_string_literal: true - -module IRB - module Debug - IRB_DIR = File.expand_path('..', __dir__) - - class << self - def insert_debug_break(pre_cmds: nil, do_cmds: nil) - options = { oneshot: true, hook_call: false } - - if pre_cmds || do_cmds - options[:command] = ['irb', pre_cmds, do_cmds] - end - if DEBUGGER__::LineBreakpoint.instance_method(:initialize).parameters.include?([:key, :skip_src]) - options[:skip_src] = true - end - - # To make debugger commands like `next` or `continue` work without asking - # the user to quit IRB after that, we need to exit IRB first and then hit - # a TracePoint on #debug_break. - file, lineno = IRB::Irb.instance_method(:debug_break).source_location - DEBUGGER__::SESSION.add_line_breakpoint(file, lineno + 1, **options) - end - - def setup(irb) - # When debug session is not started at all - unless defined?(DEBUGGER__::SESSION) - begin - require "debug/session" - rescue LoadError # debug.gem is not written in Gemfile - return false unless load_bundled_debug_gem - end - DEBUGGER__::CONFIG.set_config - configure_irb_for_debugger(irb) - - DEBUGGER__.initialize_session{ IRB::Debug::UI.new(irb) } - end - - # When debug session was previously started but not by IRB - if defined?(DEBUGGER__::SESSION) && !irb.context.with_debugger - configure_irb_for_debugger(irb) - DEBUGGER__::SESSION.reset_ui(IRB::Debug::UI.new(irb)) - end - - # Apply patches to debug gem so it skips IRB frames - unless DEBUGGER__.respond_to?(:capture_frames_without_irb) - DEBUGGER__.singleton_class.send(:alias_method, :capture_frames_without_irb, :capture_frames) - - def DEBUGGER__.capture_frames(*args) - frames = capture_frames_without_irb(*args) - frames.reject! do |frame| - frame.realpath&.start_with?(IRB_DIR) || frame.path == "<internal:prelude>" - end - frames - end - - DEBUGGER__::ThreadClient.prepend(SkipPathHelperForIRB) - end - - if !DEBUGGER__::CONFIG[:no_hint] && irb.context.io.is_a?(RelineInputMethod) - Reline.output_modifier_proc = proc do |input, complete:| - unless input.strip.empty? - cmd = input.split(/\s/, 2).first - - if !complete && DEBUGGER__.commands.key?(cmd) - input = input.sub(/\n$/, " # debug command\n") - end - end - - irb.context.colorize_input(input, complete: complete) - end - end - - true - end - - private - - def configure_irb_for_debugger(irb) - require 'irb/debug/ui' - IRB.instance_variable_set(:@debugger_irb, irb) - irb.context.with_debugger = true - irb.context.irb_name += ":rdbg" - irb.context.io.load_history if irb.context.io.class < HistorySavingAbility - end - - module SkipPathHelperForIRB - def skip_internal_path?(path) - # The latter can be removed once https://github.com/ruby/debug/issues/866 is resolved - super || path.match?(IRB_DIR) || path.match?('<internal:prelude>') - end - end - - # This is used when debug.gem is not written in Gemfile. Even if it's not - # installed by `bundle install`, debug.gem is installed by default because - # it's a bundled gem. This method tries to activate and load that. - def load_bundled_debug_gem - # Discover latest debug.gem under GEM_PATH - debug_gem = Gem.paths.path.flat_map { |path| Dir.glob("#{path}/gems/debug-*") }.select do |path| - File.basename(path).match?(/\Adebug-\d+\.\d+\.\d+(\w+)?\z/) - end.sort_by do |path| - Gem::Version.new(File.basename(path).delete_prefix('debug-')) - end.last - return false unless debug_gem - - # Discover debug/debug.so under extensions for Ruby 3.2+ - ext_name = "/debug/debug.#{RbConfig::CONFIG['DLEXT']}" - ext_path = Gem.paths.path.flat_map do |path| - Dir.glob("#{path}/extensions/**/#{File.basename(debug_gem)}#{ext_name}") - end.first - - # Attempt to forcibly load the bundled gem - if ext_path - $LOAD_PATH << ext_path.delete_suffix(ext_name) - end - $LOAD_PATH << "#{debug_gem}/lib" - begin - require "debug/session" - puts "Loaded #{File.basename(debug_gem)}" - true - rescue LoadError - false - end - end - end - end -end diff --git a/lib/irb/debug/ui.rb b/lib/irb/debug/ui.rb deleted file mode 100644 index a21ec6b11d..0000000000 --- a/lib/irb/debug/ui.rb +++ /dev/null @@ -1,101 +0,0 @@ -require 'io/console/size' -require 'debug/console' - -module IRB - module Debug - class UI < DEBUGGER__::UI_Base - def initialize(irb) - @irb = irb - end - - def remote? - false - end - - def activate session, on_fork: false - end - - def deactivate - end - - def width - if (w = IO.console_size[1]) == 0 # for tests PTY - 80 - else - w - end - end - - def quit n - yield - exit n - end - - def ask prompt - setup_interrupt do - print prompt - ($stdin.gets || '').strip - end - end - - def puts str = nil - case str - when Array - str.each{|line| - $stdout.puts line.chomp - } - when String - Pager.page_content(str, retain_content: true) - when nil - $stdout.puts - end - end - - def readline _ - setup_interrupt do - tc = DEBUGGER__::SESSION.instance_variable_get(:@tc) - cmd = @irb.debug_readline(tc.current_frame.eval_binding || TOPLEVEL_BINDING) - - case cmd - when nil # when user types C-d - "continue" - else - cmd - end - end - end - - def setup_interrupt - DEBUGGER__::SESSION.intercept_trap_sigint false do - current_thread = Thread.current # should be session_server thread - - prev_handler = trap(:INT){ - current_thread.raise Interrupt - } - - yield - ensure - trap(:INT, prev_handler) - end - end - - def after_fork_parent - parent_pid = Process.pid - - at_exit{ - DEBUGGER__::SESSION.intercept_trap_sigint_end - trap(:SIGINT, :IGNORE) - - if Process.pid == parent_pid - # only check child process from its parent - begin - # wait for all child processes to keep terminal - Process.waitpid - rescue Errno::ESRCH, Errno::ECHILD - end - end - } - end - end - end -end diff --git a/lib/irb/default_commands.rb b/lib/irb/default_commands.rb deleted file mode 100644 index 9820a1f304..0000000000 --- a/lib/irb/default_commands.rb +++ /dev/null @@ -1,279 +0,0 @@ -# frozen_string_literal: true - -require_relative "command" -require_relative "command/internal_helpers" -require_relative "command/backtrace" -require_relative "command/break" -require_relative "command/catch" -require_relative "command/cd" -require_relative "command/chws" -require_relative "command/context" -require_relative "command/continue" -require_relative "command/copy" -require_relative "command/debug" -require_relative "command/delete" -require_relative "command/disable_irb" -require_relative "command/edit" -require_relative "command/exit" -require_relative "command/finish" -require_relative "command/force_exit" -require_relative "command/help" -require_relative "command/history" -require_relative "command/info" -require_relative "command/irb_info" -require_relative "command/load" -require_relative "command/ls" -require_relative "command/measure" -require_relative "command/next" -require_relative "command/pushws" -require_relative "command/show_doc" -require_relative "command/show_source" -require_relative "command/step" -require_relative "command/subirb" -require_relative "command/whereami" - -module IRB - module Command - NO_OVERRIDE = 0 - OVERRIDE_PRIVATE_ONLY = 0x01 - OVERRIDE_ALL = 0x02 - - class << self - # This API is for IRB's internal use only and may change at any time. - # Please do NOT use it. - def _register_with_aliases(name, command_class, *aliases) - @commands[name.to_sym] = [command_class, aliases] - end - - def all_commands_info - user_aliases = IRB.CurrentContext.command_aliases.each_with_object({}) do |(alias_name, target), result| - result[target] ||= [] - result[target] << alias_name - end - - commands.map do |command_name, (command_class, aliases)| - aliases = aliases.map { |a| a.first } - - if additional_aliases = user_aliases[command_name] - aliases += additional_aliases - end - - display_name = aliases.shift || command_name - { - display_name: display_name, - description: command_class.description, - category: command_class.category - } - end - end - - def command_override_policies - @@command_override_policies ||= commands.flat_map do |cmd_name, (cmd_class, aliases)| - [[cmd_name, OVERRIDE_ALL]] + aliases - end.to_h - end - - def execute_as_command?(name, public_method:, private_method:) - case command_override_policies[name] - when OVERRIDE_ALL - true - when OVERRIDE_PRIVATE_ONLY - !public_method - when NO_OVERRIDE - !public_method && !private_method - end - end - - def command_names - command_override_policies.keys.map(&:to_s) - end - - # Convert a command name to its implementation class if such command exists - def load_command(command) - command = command.to_sym - commands.each do |command_name, (command_class, aliases)| - if command_name == command || aliases.any? { |alias_name, _| alias_name == command } - return command_class - end - end - nil - end - end - - _register_with_aliases(:irb_context, Command::Context, - [:context, NO_OVERRIDE] - ) - - _register_with_aliases(:irb_exit, Command::Exit, - [:exit, OVERRIDE_PRIVATE_ONLY], - [:quit, OVERRIDE_PRIVATE_ONLY], - [:irb_quit, OVERRIDE_PRIVATE_ONLY] - ) - - _register_with_aliases(:irb_exit!, Command::ForceExit, - [:exit!, OVERRIDE_PRIVATE_ONLY] - ) - - _register_with_aliases(:irb_current_working_workspace, Command::CurrentWorkingWorkspace, - [:cwws, NO_OVERRIDE], - [:pwws, NO_OVERRIDE], - [:irb_print_working_workspace, OVERRIDE_ALL], - [:irb_cwws, OVERRIDE_ALL], - [:irb_pwws, OVERRIDE_ALL], - [:irb_current_working_binding, OVERRIDE_ALL], - [:irb_print_working_binding, OVERRIDE_ALL], - [:irb_cwb, OVERRIDE_ALL], - [:irb_pwb, OVERRIDE_ALL], - ) - - _register_with_aliases(:irb_change_workspace, Command::ChangeWorkspace, - [:chws, NO_OVERRIDE], - [:cws, NO_OVERRIDE], - [:irb_chws, OVERRIDE_ALL], - [:irb_cws, OVERRIDE_ALL], - [:irb_change_binding, OVERRIDE_ALL], - [:irb_cb, OVERRIDE_ALL], - [:cb, NO_OVERRIDE], - ) - - _register_with_aliases(:irb_workspaces, Command::Workspaces, - [:workspaces, NO_OVERRIDE], - [:irb_bindings, OVERRIDE_ALL], - [:bindings, NO_OVERRIDE], - ) - - _register_with_aliases(:irb_push_workspace, Command::PushWorkspace, - [:pushws, NO_OVERRIDE], - [:irb_pushws, OVERRIDE_ALL], - [:irb_push_binding, OVERRIDE_ALL], - [:irb_pushb, OVERRIDE_ALL], - [:pushb, NO_OVERRIDE], - ) - - _register_with_aliases(:irb_pop_workspace, Command::PopWorkspace, - [:popws, NO_OVERRIDE], - [:irb_popws, OVERRIDE_ALL], - [:irb_pop_binding, OVERRIDE_ALL], - [:irb_popb, OVERRIDE_ALL], - [:popb, NO_OVERRIDE], - ) - - _register_with_aliases(:irb_load, Command::Load) - _register_with_aliases(:irb_require, Command::Require) - _register_with_aliases(:irb_source, Command::Source, - [:source, NO_OVERRIDE] - ) - - _register_with_aliases(:irb, Command::IrbCommand) - _register_with_aliases(:irb_jobs, Command::Jobs, - [:jobs, NO_OVERRIDE] - ) - _register_with_aliases(:irb_fg, Command::Foreground, - [:fg, NO_OVERRIDE] - ) - _register_with_aliases(:irb_kill, Command::Kill, - [:kill, OVERRIDE_PRIVATE_ONLY] - ) - - _register_with_aliases(:irb_debug, Command::Debug, - [:debug, NO_OVERRIDE] - ) - _register_with_aliases(:irb_edit, Command::Edit, - [:edit, NO_OVERRIDE] - ) - - _register_with_aliases(:irb_break, Command::Break, - [:break, OVERRIDE_ALL] - ) - _register_with_aliases(:irb_catch, Command::Catch, - [:catch, OVERRIDE_PRIVATE_ONLY] - ) - _register_with_aliases(:irb_next, Command::Next, - [:next, OVERRIDE_ALL] - ) - _register_with_aliases(:irb_delete, Command::Delete, - [:delete, NO_OVERRIDE] - ) - - _register_with_aliases(:irb_step, Command::Step, - [:step, NO_OVERRIDE] - ) - _register_with_aliases(:irb_continue, Command::Continue, - [:continue, NO_OVERRIDE] - ) - _register_with_aliases(:irb_finish, Command::Finish, - [:finish, NO_OVERRIDE] - ) - _register_with_aliases(:irb_backtrace, Command::Backtrace, - [:backtrace, NO_OVERRIDE], - [:bt, NO_OVERRIDE] - ) - - _register_with_aliases(:irb_debug_info, Command::Info, - [:info, NO_OVERRIDE] - ) - - _register_with_aliases(:irb_help, Command::Help, - [:help, NO_OVERRIDE], - [:show_cmds, NO_OVERRIDE] - ) - - _register_with_aliases(:irb_show_doc, Command::ShowDoc, - [:show_doc, NO_OVERRIDE], - [:ri, NO_OVERRIDE] - ) - - _register_with_aliases(:irb_info, Command::IrbInfo) - - _register_with_aliases(:irb_ls, Command::Ls, - [:ls, NO_OVERRIDE] - ) - - _register_with_aliases(:irb_measure, Command::Measure, - [:measure, NO_OVERRIDE] - ) - - _register_with_aliases(:irb_show_source, Command::ShowSource, - [:show_source, NO_OVERRIDE] - ) - - _register_with_aliases(:irb_whereami, Command::Whereami, - [:whereami, NO_OVERRIDE] - ) - - _register_with_aliases(:irb_history, Command::History, - [:history, NO_OVERRIDE], - [:hist, NO_OVERRIDE] - ) - - _register_with_aliases(:irb_disable_irb, Command::DisableIrb, - [:disable_irb, NO_OVERRIDE] - ) - - register(:cd, Command::CD) - register(:copy, Command::Copy) - end - - ExtendCommand = Command - - # For backward compatibility, we need to keep this module: - # - As a container of helper methods - # - As a place to register commands with the deprecated def_extend_command method - module ExtendCommandBundle - # For backward compatibility - NO_OVERRIDE = Command::NO_OVERRIDE - OVERRIDE_PRIVATE_ONLY = Command::OVERRIDE_PRIVATE_ONLY - OVERRIDE_ALL = Command::OVERRIDE_ALL - - # Deprecated. Doesn't have any effect. - @EXTEND_COMMANDS = [] - - class << self - # Drepcated. Use Command.regiser instead. - def def_extend_command(cmd_name, cmd_class, _, *aliases) - Command._register_with_aliases(cmd_name, cmd_class, *aliases) - Command.class_variable_set(:@@command_override_policies, nil) - end - end - end -end diff --git a/lib/irb/easter-egg.rb b/lib/irb/easter-egg.rb deleted file mode 100644 index 07b6137be3..0000000000 --- a/lib/irb/easter-egg.rb +++ /dev/null @@ -1,152 +0,0 @@ -require "reline" - -module IRB - class << self - class Vec - def initialize(x, y, z) - @x, @y, @z = x, y, z - end - - attr_reader :x, :y, :z - - def sub(other) - Vec.new(@x - other.x, @y - other.y, @z - other.z) - end - - def dot(other) - @x*other.x + @y*other.y + @z*other.z - end - - def cross(other) - ox, oy, oz = other.x, other.y, other.z - Vec.new(@y*oz-@z*oy, @z*ox-@x*oz, @x*oy-@y*ox) - end - - def normalize - r = Math.sqrt(self.dot(self)) - Vec.new(@x / r, @y / r, @z / r) - end - end - - class Canvas - def initialize((h, w)) - @data = (0..h-2).map { [0] * w } - @scale = [w / 2.0, h-2].min - @center = Complex(w / 2, h-2) - end - - def line((x1, y1), (x2, y2)) - p1 = Complex(x1, y1) / 2 * @scale + @center - p2 = Complex(x2, y2) / 2 * @scale + @center - line0(p1, p2) - end - - private def line0(p1, p2) - mid = (p1 + p2) / 2 - if (p1 - p2).abs < 1 - x, y = mid.rect - @data[y / 2][x] |= (y % 2 > 1 ? 2 : 1) - else - line0(p1, mid) - line0(p2, mid) - end - end - - def draw - @data.each {|row| row.fill(0) } - yield - @data.map {|row| row.map {|n| " ',;"[n] }.join }.join("\n") - end - end - - class RubyModel - def initialize - @faces = init_ruby_model - end - - def init_ruby_model - cap_vertices = (0..5).map {|i| Vec.new(*Complex.polar(1, i * Math::PI / 3).rect, 1) } - middle_vertices = (0..5).map {|i| Vec.new(*Complex.polar(2, (i + 0.5) * Math::PI / 3).rect, 0) } - bottom_vertex = Vec.new(0, 0, -2) - - faces = [cap_vertices] - 6.times do |j| - i = j-1 - faces << [cap_vertices[i], middle_vertices[i], cap_vertices[j]] - faces << [cap_vertices[j], middle_vertices[i], middle_vertices[j]] - faces << [middle_vertices[i], bottom_vertex, middle_vertices[j]] - end - - faces - end - - def render_frame(i) - angle = i / 10.0 - dir = Vec.new(*Complex.polar(1, angle).rect, Math.sin(angle)).normalize - dir2 = Vec.new(*Complex.polar(1, angle - Math::PI/2).rect, 0) - up = dir.cross(dir2) - nm = dir.cross(up) - @faces.each do |vertices| - v0, v1, v2, = vertices - if v1.sub(v0).cross(v2.sub(v0)).dot(dir) > 0 - points = vertices.map {|p| [nm.dot(p), up.dot(p)] } - (points + [points[0]]).each_cons(2) do |p1, p2| - yield p1, p2 - end - end - end - end - end - - private def easter_egg_logo(type) - @easter_egg_logos ||= File.read(File.join(__dir__, 'ruby_logo.aa'), encoding: 'UTF-8:UTF-8') - .split(/TYPE: ([A-Z_]+)\n/)[1..] - .each_slice(2) - .to_h - @easter_egg_logos[type.to_s.upcase] - end - - private def easter_egg(type = nil) - print "\e[?1049h" - type ||= [:logo, :dancing].sample - case type - when :logo - Pager.page do |io| - logo_type = STDOUT.external_encoding == Encoding::UTF_8 ? :unicode_large : :ascii_large - io.write easter_egg_logo(logo_type) - STDIN.raw { STDIN.getc } if io == STDOUT - end - when :dancing - STDOUT.cooked do - interrupted = false - prev_trap = trap("SIGINT") { interrupted = true } - canvas = Canvas.new(Reline.get_screen_size) - Reline::IOGate.set_winch_handler do - canvas = Canvas.new(Reline.get_screen_size) - end - ruby_model = RubyModel.new - print "\e[?25l" # hide cursor - 0.step do |i| # TODO (0..).each needs Ruby 2.6 or later - buff = canvas.draw do - ruby_model.render_frame(i) do |p1, p2| - canvas.line(p1, p2) - end - end - buff[0, 20] = "\e[0mPress Ctrl+C to stop\e[31m\e[1m" - print "\e[H" + buff - sleep 0.05 - break if interrupted - end - rescue Interrupt - ensure - print "\e[?25h" # show cursor - trap("SIGINT", prev_trap) - end - end - ensure - print "\e[0m\e[?1049l" - end - end -end - -IRB.__send__(:easter_egg, ARGV[0]&.to_sym) if $0 == __FILE__ diff --git a/lib/irb/ext/change-ws.rb b/lib/irb/ext/change-ws.rb deleted file mode 100644 index 60e8afe31f..0000000000 --- a/lib/irb/ext/change-ws.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true -# -# irb/ext/cb.rb - -# by Keiju ISHITSUKA([email protected]) -# - -module IRB # :nodoc: - class Context - - # Inherited from +TOPLEVEL_BINDING+. - def home_workspace - if defined? @home_workspace - @home_workspace - else - @home_workspace = workspace - end - end - - # Changes the current workspace to given object or binding. - # - # If the optional argument is omitted, the workspace will be - # #home_workspace which is inherited from +TOPLEVEL_BINDING+ or the main - # object, <code>IRB.conf[:MAIN_CONTEXT]</code> when irb was initialized. - # - # See IRB::WorkSpace.new for more information. - def change_workspace(*_main) - if _main.empty? - replace_workspace(home_workspace) - return main - end - - workspace = WorkSpace.new(_main[0]) - replace_workspace(workspace) - workspace.load_helper_methods_to_main - end - end -end diff --git a/lib/irb/ext/eval_history.rb b/lib/irb/ext/eval_history.rb deleted file mode 100644 index 6c21ff00ee..0000000000 --- a/lib/irb/ext/eval_history.rb +++ /dev/null @@ -1,149 +0,0 @@ -# frozen_string_literal: true -# -# history.rb - -# by Keiju ISHITSUKA([email protected]) -# - -module IRB # :nodoc: - - class Context - - NOPRINTING_IVARS.push "@eval_history_values" - - # See #set_last_value - alias _set_last_value set_last_value - - def set_last_value(value) - _set_last_value(value) - - if defined?(@eval_history) && @eval_history - @eval_history_values.push @line_no, @last_value - workspace.evaluate "__ = IRB.CurrentContext.instance_eval{@eval_history_values}" - end - - @last_value - end - - remove_method :eval_history= if method_defined?(:eval_history=) - # The command result history limit. This method is not available until - # #eval_history= was called with non-nil value (directly or via - # setting <code>IRB.conf[:EVAL_HISTORY]</code> in <code>.irbrc</code>). - attr_reader :eval_history - # Sets command result history limit. Default value is set from - # <code>IRB.conf[:EVAL_HISTORY]</code>. - # - # +no+ is an Integer or +nil+. - # - # Returns +no+ of history items if greater than 0. - # - # If +no+ is 0, the number of history items is unlimited. - # - # If +no+ is +nil+, execution result history isn't used (default). - # - # EvalHistory values are available via <code>__</code> variable, see - # IRB::EvalHistory. - def eval_history=(no) - if no - if defined?(@eval_history) && @eval_history - @eval_history_values.size(no) - else - @eval_history_values = EvalHistory.new(no) - IRB.conf[:__TMP__EHV__] = @eval_history_values - workspace.evaluate("__ = IRB.conf[:__TMP__EHV__]") - IRB.conf.delete(:__TMP_EHV__) - end - else - @eval_history_values = nil - end - @eval_history = no - end - end - - # Represents history of results of previously evaluated commands. - # - # Available via <code>__</code> variable, only if <code>IRB.conf[:EVAL_HISTORY]</code> - # or <code>IRB::CurrentContext().eval_history</code> is non-nil integer value - # (by default it is +nil+). - # - # Example (in `irb`): - # - # # Initialize history - # IRB::CurrentContext().eval_history = 10 - # # => 10 - # - # # Perform some commands... - # 1 + 2 - # # => 3 - # puts 'x' - # # x - # # => nil - # raise RuntimeError - # # ...error raised - # - # # Inspect history (format is "<item number> <evaluated value>": - # __ - # # => 1 10 - # # 2 3 - # # 3 nil - # - # __[1] - # # => 10 - # - class EvalHistory - - def initialize(size = 16) # :nodoc: - @size = size - @contents = [] - end - - def size(size) # :nodoc: - if size != 0 && size < @size - @contents = @contents[@size - size .. @size] - end - @size = size - end - - # Get one item of the content (both positive and negative indexes work). - def [](idx) - begin - if idx >= 0 - @contents.find{|no, val| no == idx}[1] - else - @contents[idx][1] - end - rescue NameError - nil - end - end - - def push(no, val) # :nodoc: - @contents.push [no, val] - @contents.shift if @size != 0 && @contents.size > @size - end - - alias real_inspect inspect - - def inspect # :nodoc: - if @contents.empty? - return real_inspect - end - - unless (last = @contents.pop)[1].equal?(self) - @contents.push last - last = nil - end - str = @contents.collect{|no, val| - if val.equal?(self) - "#{no} ...self-history..." - else - "#{no} #{val.inspect}" - end - }.join("\n") - if str == "" - str = "Empty." - end - @contents.push last if last - str - end - end -end diff --git a/lib/irb/ext/loader.rb b/lib/irb/ext/loader.rb deleted file mode 100644 index df5aaa8e5a..0000000000 --- a/lib/irb/ext/loader.rb +++ /dev/null @@ -1,127 +0,0 @@ -# frozen_string_literal: true -# -# loader.rb - -# by Keiju ISHITSUKA([email protected]) -# - -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 - - 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 - - load_file(path, priv) - end - - def search_file_from_ruby_path(fn) # :nodoc: - if File.absolute_path?(fn) - return fn if File.exist?(fn) - return nil - end - - for path in $: - if File.exist?(f = File.join(path, fn)) - return f - end - end - 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 = irb_context.irb - irb.suspend_name(path, File.basename(path)) do - FileInputMethod.open(path) do |io| - irb.suspend_input_method(io) do - |back_io| - irb.signal_status(:IN_LOAD) do - if back_io.kind_of?(FileInputMethod) - irb.eval_input - else - begin - irb.eval_input - rescue LoadAbort - print "load abort!!\n" - end - end - end - end - end - 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 = irb_context.irb - irb.suspend_name(path, File.basename(path)) do - - if priv - ws = WorkSpace.new(Module.new) - else - ws = WorkSpace.new - end - irb.suspend_workspace(ws) do - FileInputMethod.open(path) do |io| - irb.suspend_input_method(io) do - |back_io| - irb.signal_status(:IN_LOAD) do - if back_io.kind_of?(FileInputMethod) - irb.eval_input - else - begin - irb.eval_input - rescue LoadAbort - print "load abort!!\n" - end - end - end - end - end - end - end - end - - def old # :nodoc: - back_io = @io - back_path = irb_path - back_name = @irb_name - back_scanner = @irb.scanner - begin - @io = FileInputMethod.new(path) - @irb_name = File.basename(path) - self.irb_path = path - @irb.signal_status(:IN_LOAD) do - if back_io.kind_of?(FileInputMethod) - @irb.eval_input - else - begin - @irb.eval_input - rescue LoadAbort - print "load abort!!\n" - end - end - end - ensure - @io = back_io - @irb_name = back_name - self.irb_path = back_path - @irb.scanner = back_scanner - end - end - end -end diff --git a/lib/irb/ext/multi-irb.rb b/lib/irb/ext/multi-irb.rb deleted file mode 100644 index 9f234f0cdc..0000000000 --- a/lib/irb/ext/multi-irb.rb +++ /dev/null @@ -1,258 +0,0 @@ -# frozen_string_literal: true -# -# irb/multi-irb.rb - multiple irb module -# by Keiju ISHITSUKA([email protected]) -# - -module IRB - class JobManager # :nodoc: - - # Creates a new JobManager object - def initialize - @jobs = [] - @current_job = nil - end - - # The active irb session - attr_accessor :current_job - - # The total number of irb sessions, used to set +irb_name+ of the current - # Context. - def n_jobs - @jobs.size - end - - # Returns the thread for the given +key+ object, see #search for more - # information. - def thread(key) - th, = search(key) - th - end - - # Returns the irb session for the given +key+ object, see #search for more - # information. - def irb(key) - _, irb = search(key) - irb - end - - # Returns the top level thread. - def main_thread - @jobs[0][0] - end - - # Returns the top level irb session. - def main_irb - @jobs[0][1] - end - - # Add the given +irb+ session to the jobs Array. - def insert(irb) - @jobs.push [Thread.current, irb] - end - - # Changes the current active irb session to the given +key+ in the jobs - # Array. - # - # Raises an IrbAlreadyDead exception if the given +key+ is no longer alive. - # - # If the given irb session is already active, an IrbSwitchedToCurrentThread - # exception is raised. - def switch(key) - th, irb = search(key) - fail IrbAlreadyDead unless th.alive? - fail IrbSwitchedToCurrentThread if th == Thread.current - @current_job = irb - th.run - Thread.stop - @current_job = irb(Thread.current) - end - - # Terminates the irb sessions specified by the given +keys+. - # - # Raises an IrbAlreadyDead exception if one of the given +keys+ is already - # terminated. - # - # See Thread#exit for more information. - def kill(*keys) - for key in keys - th, _ = search(key) - fail IrbAlreadyDead unless th.alive? - th.exit - end - end - - # Returns the associated job for the given +key+. - # - # If given an Integer, it will return the +key+ index for the jobs Array. - # - # When an instance of Irb is given, it will return the irb session - # associated with +key+. - # - # If given an instance of Thread, it will return the associated thread - # +key+ using Object#=== on the jobs Array. - # - # Otherwise returns the irb session with the same top-level binding as the - # given +key+. - # - # Raises a NoSuchJob exception if no job can be found with the given +key+. - def search(key) - job = case key - when Integer - @jobs[key] - when Irb - @jobs.find{|k, v| v.equal?(key)} - when Thread - @jobs.assoc(key) - else - @jobs.find{|k, v| v.context.main.equal?(key)} - end - fail NoSuchJob, key if job.nil? - job - end - - # Deletes the job at the given +key+. - def delete(key) - case key - when Integer - fail NoSuchJob, key unless @jobs[key] - @jobs[key] = nil - else - catch(:EXISTS) do - @jobs.each_index do - |i| - if @jobs[i] and (@jobs[i][0] == key || - @jobs[i][1] == key || - @jobs[i][1].context.main.equal?(key)) - @jobs[i] = nil - throw :EXISTS - end - end - fail NoSuchJob, key - end - end - until assoc = @jobs.pop; end unless @jobs.empty? - @jobs.push assoc - end - - # Outputs a list of jobs, see the irb command +irb_jobs+, or +jobs+. - def inspect - ary = [] - @jobs.each_index do - |i| - th, irb = @jobs[i] - next if th.nil? - - if th.alive? - if th.stop? - t_status = "stop" - else - t_status = "running" - end - else - t_status = "exited" - end - ary.push format("#%d->%s on %s (%s: %s)", - i, - irb.context.irb_name, - irb.context.main, - th, - t_status) - end - ary.join("\n") - end - end - - @JobManager = JobManager.new - - # The current JobManager in the session - def IRB.JobManager # :nodoc: - @JobManager - end - - # The current Context in this session - def IRB.CurrentContext # :nodoc: - IRB.JobManager.irb(Thread.current).context - end - - # Creates a new IRB session, see Irb.new. - # - # The optional +file+ argument is given to Context.new, along with the - # workspace created with the remaining arguments, see WorkSpace.new - def IRB.irb(file = nil, *main) # :nodoc: - workspace = WorkSpace.new(*main) - parent_thread = Thread.current - Thread.start do - begin - irb = Irb.new(workspace, file) - rescue - print "Subirb can't start with context(self): ", workspace.main.inspect, "\n" - print "return to main irb\n" - Thread.pass - Thread.main.wakeup - Thread.exit - end - @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC] - @JobManager.insert(irb) - @JobManager.current_job = irb - begin - system_exit = false - catch(:IRB_EXIT) do - irb.eval_input - end - rescue SystemExit - system_exit = true - raise - #fail - ensure - unless system_exit - @JobManager.delete(irb) - if @JobManager.current_job == irb - if parent_thread.alive? - @JobManager.current_job = @JobManager.irb(parent_thread) - parent_thread.run - else - @JobManager.current_job = @JobManager.main_irb - @JobManager.main_thread.run - end - end - end - end - end - Thread.stop - @JobManager.current_job = @JobManager.irb(Thread.current) - end - - @CONF[:SINGLE_IRB_MODE] = false - @JobManager.insert(@CONF[:MAIN_CONTEXT].irb) - @JobManager.current_job = @CONF[:MAIN_CONTEXT].irb - - class Irb - def signal_handle - unless @context.ignore_sigint? - print "\nabort!!\n" if @context.verbose? - exit - end - - case @signal_status - when :IN_INPUT - print "^C\n" - IRB.JobManager.thread(self).raise RubyLex::TerminateLineInput - when :IN_EVAL - IRB.irb_abort(self) - when :IN_LOAD - IRB.irb_abort(self, LoadAbort) - when :IN_IRB - # ignore - else - # ignore other cases as well - end - end - end - - trap("SIGINT") do - @JobManager.current_job.signal_handle - Thread.stop - end - -end diff --git a/lib/irb/ext/tracer.rb b/lib/irb/ext/tracer.rb deleted file mode 100644 index fd6daa88ae..0000000000 --- a/lib/irb/ext/tracer.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true -# -# irb/lib/tracer.rb - -# by Keiju ISHITSUKA([email protected]) -# -# Loading the gem "tracer" will cause it to extend IRB commands with: -# https://github.com/ruby/tracer/blob/v0.2.2/lib/tracer/irb.rb -begin - require "tracer" -rescue LoadError - $stderr.puts "Tracer extension of IRB is enabled but tracer gem wasn't found." - return # This is about to disable loading below -end - -module IRB - class CallTracer < ::CallTracer - IRB_DIR = File.expand_path('../..', __dir__) - - def skip?(tp) - super || tp.path.match?(IRB_DIR) || tp.path.match?('<internal:prelude>') - end - end - 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 https://github.com/ruby/tracer for more information. - def evaluate(statements, file = __FILE__, line = __LINE__) - if IRB.conf[:USE_TRACER] == true - CallTracer.new(colorize: Color.colorable?).start do - __evaluate__(statements, file, line) - end - else - __evaluate__(statements, file, line) - end - end - end -end diff --git a/lib/irb/ext/use-loader.rb b/lib/irb/ext/use-loader.rb deleted file mode 100644 index c8a3ea1fe8..0000000000 --- a/lib/irb/ext/use-loader.rb +++ /dev/null @@ -1,67 +0,0 @@ -# frozen_string_literal: true -# -# use-loader.rb - -# by Keiju ISHITSUKA([email protected]) -# - -require_relative "../command/load" -require_relative "loader" - -class Object - alias __original__load__IRB_use_loader__ load - alias __original__require__IRB_use_loader__ require -end - -module IRB - module ExtendCommandBundle - remove_method :irb_load if method_defined?(:irb_load) - # Loads the given file similarly to Kernel#load, see IrbLoader#irb_load - def irb_load(*opts, &b) - Command::Load.execute(irb_context, *opts, &b) - end - remove_method :irb_require if method_defined?(:irb_require) - # Loads the given file similarly to Kernel#require - def irb_require(*opts, &b) - Command::Require.execute(irb_context, *opts, &b) - end - end - - class Context - - IRB.conf[:USE_LOADER] = false - - # Returns whether +irb+'s own file reader method is used by - # +load+/+require+ or not. - # - # This mode is globally affected (irb-wide). - def use_loader - IRB.conf[:USE_LOADER] - end - - alias use_loader? use_loader - - remove_method :use_loader= if method_defined?(:use_loader=) - # Sets <code>IRB.conf[:USE_LOADER]</code> - # - # See #use_loader for more information. - def use_loader=(opt) - - if IRB.conf[:USE_LOADER] != opt - IRB.conf[:USE_LOADER] = opt - if opt - (class<<workspace.main;self;end).instance_eval { - alias_method :load, :irb_load - alias_method :require, :irb_require - } - else - (class<<workspace.main;self;end).instance_eval { - alias_method :load, :__original__load__IRB_use_loader__ - alias_method :require, :__original__require__IRB_use_loader__ - } - end - end - print "Switch to load/require#{unless use_loader; ' non';end} trace mode.\n" if verbose? - opt - end - end -end diff --git a/lib/irb/ext/workspaces.rb b/lib/irb/ext/workspaces.rb deleted file mode 100644 index da09faa83e..0000000000 --- a/lib/irb/ext/workspaces.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true -# -# push-ws.rb - -# by Keiju ISHITSUKA([email protected]) -# - -module IRB # :nodoc: - class Context - # Creates a new workspace with the given object or binding, and appends it - # onto the current #workspaces stack. - # - # See IRB::Context#change_workspace and IRB::WorkSpace.new for more - # information. - def push_workspace(*_main) - if _main.empty? - if @workspace_stack.size > 1 - # swap the top two workspaces - previous_workspace, current_workspace = @workspace_stack.pop(2) - @workspace_stack.push current_workspace, previous_workspace - end - else - new_workspace = WorkSpace.new(workspace.binding, _main[0]) - @workspace_stack.push new_workspace - new_workspace.load_helper_methods_to_main - end - end - - # Removes the last element from the current #workspaces stack and returns - # it, or +nil+ if the current workspace stack is empty. - # - # Also, see #push_workspace. - def pop_workspace - @workspace_stack.pop if @workspace_stack.size > 1 - end - end -end diff --git a/lib/irb/frame.rb b/lib/irb/frame.rb deleted file mode 100644 index 4b697c8719..0000000000 --- a/lib/irb/frame.rb +++ /dev/null @@ -1,80 +0,0 @@ -# frozen_string_literal: true -# -# frame.rb - -# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd) -# - -module IRB - class Frame - class FrameOverflow < StandardError - def initialize - super("frame overflow") - end - end - class FrameUnderflow < StandardError - def initialize - super("frame underflow") - end - end - - # 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' - @frames.push binding - when 'return', 'end' - @frames.pop - 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 - - # 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 - - @backtrace = Frame.new - set_trace_func proc{|event, file, line, id, binding, klass| - @backtrace.trace_func(event, file, line, id, binding) - } - end -end diff --git a/lib/irb/help.rb b/lib/irb/help.rb deleted file mode 100644 index a24bc10a15..0000000000 --- a/lib/irb/help.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true -# -# irb/help.rb - print usage module -# by Keiju ISHITSUKA([email protected]) -# - -module IRB - # Outputs the irb help message, see IRB@Command-Line+Options. - def IRB.print_usage # :nodoc: - lc = IRB.conf[:LC_MESSAGES] - path = lc.find("irb/help-message") - space_line = false - File.open(path){|f| - f.each_line do |l| - if /^\s*$/ =~ l - lc.puts l unless space_line - space_line = true - next - end - space_line = false - - l.sub!(/#.*$/, "") - next if /^\s*$/ =~ l - lc.puts l - end - } - end -end diff --git a/lib/irb/helper_method.rb b/lib/irb/helper_method.rb deleted file mode 100644 index f1f6fff915..0000000000 --- a/lib/irb/helper_method.rb +++ /dev/null @@ -1,29 +0,0 @@ -require_relative "helper_method/base" - -module IRB - module HelperMethod - @helper_methods = {} - - class << self - attr_reader :helper_methods - - def register(name, helper_class) - @helper_methods[name] = helper_class - - if defined?(HelpersContainer) - HelpersContainer.install_helper_methods - end - end - - def all_helper_methods_info - @helper_methods.map do |name, helper_class| - { display_name: name, description: helper_class.description } - end - end - end - - # Default helper_methods - require_relative "helper_method/conf" - register(:conf, HelperMethod::Conf) - end -end diff --git a/lib/irb/helper_method/base.rb b/lib/irb/helper_method/base.rb deleted file mode 100644 index a68001ed28..0000000000 --- a/lib/irb/helper_method/base.rb +++ /dev/null @@ -1,16 +0,0 @@ -require "singleton" - -module IRB - module HelperMethod - class Base - include Singleton - - class << self - def description(description = nil) - @description = description if description - @description - end - end - end - end -end diff --git a/lib/irb/helper_method/conf.rb b/lib/irb/helper_method/conf.rb deleted file mode 100644 index 718ed279c0..0000000000 --- a/lib/irb/helper_method/conf.rb +++ /dev/null @@ -1,11 +0,0 @@ -module IRB - module HelperMethod - class Conf < Base - description "Returns the current IRB context." - - def execute - IRB.CurrentContext - end - end - end -end diff --git a/lib/irb/history.rb b/lib/irb/history.rb deleted file mode 100644 index 0beff15539..0000000000 --- a/lib/irb/history.rb +++ /dev/null @@ -1,116 +0,0 @@ -require "pathname" - -module IRB - module History - DEFAULT_ENTRY_LIMIT = 1000 - - class << self - # Integer representation of <code>IRB.conf[:HISTORY_FILE]</code>. - def save_history - return 0 if IRB.conf[:SAVE_HISTORY] == false - return DEFAULT_ENTRY_LIMIT if IRB.conf[:SAVE_HISTORY] == true - IRB.conf[:SAVE_HISTORY].to_i - end - - def save_history? - !save_history.zero? - end - - def infinite? - save_history.negative? - end - - # Might be nil when HOME and XDG_CONFIG_HOME are not available. - def history_file - if (history_file = IRB.conf[:HISTORY_FILE]) - File.expand_path(history_file) - else - IRB.rc_file("_history") - end - end - end - end - - module HistorySavingAbility # :nodoc: - def support_history_saving? - true - end - - def reset_history_counter - @loaded_history_lines = self.class::HISTORY.size - end - - def load_history - history_file = History.history_file - return unless File.exist?(history_file.to_s) - - history = self.class::HISTORY - - File.open(history_file, "r:#{IRB.conf[:LC_MESSAGES].encoding}") do |f| - f.each { |l| - l = l.chomp - if self.class == RelineInputMethod and history.last&.end_with?("\\") - history.last.delete_suffix!("\\") - history.last << "\n" << l - else - history << l - end - } - end - @loaded_history_lines = history.size - @loaded_history_mtime = File.mtime(history_file) - end - - def save_history - return unless History.save_history? - return unless (history_file = History.history_file) - unless ensure_history_file_writable(history_file) - warn <<~WARN - Can't write history to #{History.history_file.inspect} due to insufficient permissions. - Please verify the value of `IRB.conf[:HISTORY_FILE]`. Ensure the folder exists and that both the folder and file (if it exists) are writable. - WARN - return - end - - history = self.class::HISTORY.to_a - - if File.exist?(history_file) && - File.mtime(history_file) != @loaded_history_mtime - history = history[@loaded_history_lines..-1] if @loaded_history_lines - append_history = true - end - - File.open(history_file, (append_history ? "a" : "w"), 0o600, encoding: IRB.conf[:LC_MESSAGES]&.encoding) do |f| - hist = history.map { |l| l.scrub.split("\n").join("\\\n") } - - unless append_history || History.infinite? - # Check size before slicing because array.last(huge_number) raises RangeError. - hist = hist.last(History.save_history) if hist.size > History.save_history - end - - f.puts(hist) - end - end - - private - - # Returns boolean whether writing to +history_file+ will be possible. - # Permissions of already existing +history_file+ are changed to - # owner-only-readable if necessary [BUG #7694]. - def ensure_history_file_writable(history_file) - history_file = Pathname.new(history_file) - - return false unless history_file.dirname.writable? - return true unless history_file.exist? - - begin - if history_file.stat.mode & 0o66 != 0 - history_file.chmod 0o600 - end - true - rescue Errno::EPERM # no permissions - false - end - end - end -end diff --git a/lib/irb/init.rb b/lib/irb/init.rb deleted file mode 100644 index 720c4fec46..0000000000 --- a/lib/irb/init.rb +++ /dev/null @@ -1,540 +0,0 @@ -# frozen_string_literal: true -# -# irb/init.rb - irb initialize module -# by Keiju ISHITSUKA([email protected]) -# - -module IRB # :nodoc: - @CONF = {} - @INITIALIZED = false - # Displays current configuration. - # - # Modifying the configuration is achieved by sending a message to IRB.conf. - # - # See IRB@Configuration for more information. - def IRB.conf - @CONF - end - - def @CONF.inspect - array = [] - for k, v in sort{|a1, a2| a1[0].id2name <=> a2[0].id2name} - case k - when :MAIN_CONTEXT, :__TMP__EHV__ - array.push format("CONF[:%s]=...myself...", k.id2name) - when :PROMPT - s = v.collect{ - |kk, vv| - ss = vv.collect{|kkk, vvv| ":#{kkk.id2name}=>#{vvv.inspect}"} - format(":%s=>{%s}", kk.id2name, ss.join(", ")) - } - array.push format("CONF[:%s]={%s}", k.id2name, s.join(", ")) - else - array.push format("CONF[:%s]=%s", k.id2name, v.inspect) - end - end - array.join("\n") - end - - # Returns the current version of IRB, including release version and last - # updated date. - def IRB.version - format("irb %s (%s)", @RELEASE_VERSION, @LAST_UPDATE_DATE) - end - - def IRB.initialized? - !!@INITIALIZED - end - - # initialize config - def IRB.setup(ap_path, argv: ::ARGV) - IRB.init_config(ap_path) - IRB.init_error - IRB.parse_opts(argv: argv) - IRB.run_config - IRB.validate_config - IRB.load_modules - - unless @CONF[:PROMPT][@CONF[:PROMPT_MODE]] - fail UndefinedPromptMode, @CONF[:PROMPT_MODE] - end - @INITIALIZED = true - end - - # @CONF default setting - def IRB.init_config(ap_path) - # default configurations - unless ap_path and @CONF[:AP_NAME] - ap_path = File.join(File.dirname(File.dirname(__FILE__)), "irb.rb") - end - @CONF[:VERSION] = version - @CONF[:AP_NAME] = File::basename(ap_path, ".rb") - - @CONF[:IRB_NAME] = "irb" - @CONF[:IRB_LIB_PATH] = File.dirname(__FILE__) - - @CONF[:RC] = true - @CONF[:LOAD_MODULES] = [] - @CONF[:IRB_RC] = nil - - @CONF[:USE_SINGLELINE] = false unless defined?(ReadlineInputMethod) - @CONF[:USE_COLORIZE] = (nc = ENV['NO_COLOR']).nil? || nc.empty? - @CONF[:USE_AUTOCOMPLETE] = ENV.fetch("IRB_USE_AUTOCOMPLETE", "true") != "false" - @CONF[:COMPLETOR] = ENV["IRB_COMPLETOR"]&.to_sym - @CONF[:INSPECT_MODE] = true - @CONF[:USE_TRACER] = false - @CONF[:USE_LOADER] = false - @CONF[:IGNORE_SIGINT] = true - @CONF[:IGNORE_EOF] = false - @CONF[:USE_PAGER] = true - @CONF[:EXTRA_DOC_DIRS] = [] - @CONF[:ECHO] = nil - @CONF[:ECHO_ON_ASSIGNMENT] = nil - @CONF[:VERBOSE] = nil - - @CONF[:EVAL_HISTORY] = nil - @CONF[:SAVE_HISTORY] = History::DEFAULT_ENTRY_LIMIT - - @CONF[:BACK_TRACE_LIMIT] = 16 - - @CONF[:PROMPT] = { - :NULL => { - :PROMPT_I => nil, - :PROMPT_S => nil, - :PROMPT_C => nil, - :RETURN => "%s\n" - }, - :DEFAULT => { - :PROMPT_I => "%N(%m):%03n> ", - :PROMPT_S => "%N(%m):%03n%l ", - :PROMPT_C => "%N(%m):%03n* ", - :RETURN => "=> %s\n" - }, - :CLASSIC => { - :PROMPT_I => "%N(%m):%03n:%i> ", - :PROMPT_S => "%N(%m):%03n:%i%l ", - :PROMPT_C => "%N(%m):%03n:%i* ", - :RETURN => "%s\n" - }, - :SIMPLE => { - :PROMPT_I => ">> ", - :PROMPT_S => "%l> ", - :PROMPT_C => "?> ", - :RETURN => "=> %s\n" - }, - :INF_RUBY => { - :PROMPT_I => "%N(%m):%03n> ", - :PROMPT_S => nil, - :PROMPT_C => nil, - :RETURN => "%s\n", - :AUTO_INDENT => true - }, - :XMP => { - :PROMPT_I => nil, - :PROMPT_S => nil, - :PROMPT_C => nil, - :RETURN => " ==>%s\n" - } - } - - @CONF[:PROMPT_MODE] = (STDIN.tty? ? :DEFAULT : :NULL) - @CONF[:AUTO_INDENT] = true - - @CONF[:CONTEXT_MODE] = 4 # use a copy of TOPLEVEL_BINDING - @CONF[:SINGLE_IRB] = false - - @CONF[:MEASURE] = false - @CONF[:MEASURE_PROC] = {} - @CONF[:MEASURE_PROC][:TIME] = proc { |context, code, line_no, &block| - time = Time.now - result = block.() - now = Time.now - puts 'processing time: %fs' % (now - time) if IRB.conf[:MEASURE] - result - } - # arg can be either a symbol for the mode (:cpu, :wall, ..) or a hash for - # a more complete configuration. - # See https://github.com/tmm1/stackprof#all-options. - @CONF[:MEASURE_PROC][:STACKPROF] = proc { |context, code, line_no, arg, &block| - return block.() unless IRB.conf[:MEASURE] - success = false - begin - require 'stackprof' - success = true - rescue LoadError - puts 'Please run "gem install stackprof" before measuring by StackProf.' - end - if success - result = nil - arg = { mode: arg || :cpu } unless arg.is_a?(Hash) - stackprof_result = StackProf.run(**arg) do - result = block.() - end - case stackprof_result - when File - puts "StackProf report saved to #{stackprof_result.path}" - when Hash - StackProf::Report.new(stackprof_result).print_text - else - puts "Stackprof ran with #{arg.inspect}" - end - result - else - block.() - end - } - @CONF[:MEASURE_CALLBACKS] = [] - - @CONF[:LC_MESSAGES] = Locale.new - - @CONF[:AT_EXIT] = [] - - @CONF[:COMMAND_ALIASES] = { - # Symbol aliases - :'$' => :show_source, - :'@' => :whereami, - } - - @CONF[:COPY_COMMAND] = ENV.fetch("IRB_COPY_COMMAND", nil) - end - - def IRB.set_measure_callback(type = nil, arg = nil, &block) - added = nil - if type - type_sym = type.upcase.to_sym - if IRB.conf[:MEASURE_PROC][type_sym] - added = [type_sym, IRB.conf[:MEASURE_PROC][type_sym], arg] - end - elsif IRB.conf[:MEASURE_PROC][:CUSTOM] - added = [:CUSTOM, IRB.conf[:MEASURE_PROC][:CUSTOM], arg] - elsif block_given? - added = [:BLOCK, block, arg] - found = IRB.conf[:MEASURE_CALLBACKS].find{ |m| m[0] == added[0] && m[2] == added[2] } - if found - found[1] = block - return added - else - IRB.conf[:MEASURE_CALLBACKS] << added - return added - end - else - added = [:TIME, IRB.conf[:MEASURE_PROC][:TIME], arg] - end - if added - IRB.conf[:MEASURE] = true - found = IRB.conf[:MEASURE_CALLBACKS].find{ |m| m[0] == added[0] && m[2] == added[2] } - if found - # already added - nil - else - IRB.conf[:MEASURE_CALLBACKS] << added if added - added - end - else - nil - end - end - - def IRB.unset_measure_callback(type = nil) - if type.nil? - IRB.conf[:MEASURE_CALLBACKS].clear - else - type_sym = type.upcase.to_sym - IRB.conf[:MEASURE_CALLBACKS].reject!{ |t, | t == type_sym } - end - IRB.conf[:MEASURE] = nil if IRB.conf[:MEASURE_CALLBACKS].empty? - end - - def IRB.init_error - @CONF[:LC_MESSAGES].load("irb/error.rb") - end - - # option analyzing - def IRB.parse_opts(argv: ::ARGV) - load_path = [] - while opt = argv.shift - case opt - when "-f" - @CONF[:RC] = false - when "-d" - $DEBUG = true - $VERBOSE = true - when "-w" - Warning[:deprecated] = $VERBOSE = true - when /^-W(.+)?/ - opt = $1 || argv.shift - case opt - when "0" - $VERBOSE = nil - when "1" - $VERBOSE = false - else - Warning[:deprecated] = $VERBOSE = true - end - when /^-r(.+)?/ - opt = $1 || argv.shift - @CONF[:LOAD_MODULES].push opt if opt - when /^-I(.+)?/ - opt = $1 || argv.shift - load_path.concat(opt.split(File::PATH_SEPARATOR)) if opt - when '-U' - set_encoding("UTF-8", "UTF-8") - when /^-E(.+)?/, /^--encoding(?:=(.+))?/ - opt = $1 || argv.shift - set_encoding(*opt.split(':', 2)) - when "--inspect" - if /^-/ !~ argv.first - @CONF[:INSPECT_MODE] = argv.shift - else - @CONF[:INSPECT_MODE] = true - end - when "--noinspect" - @CONF[:INSPECT_MODE] = false - when "--no-pager" - @CONF[:USE_PAGER] = false - when "--singleline", "--readline", "--legacy" - @CONF[:USE_SINGLELINE] = true - when "--nosingleline", "--noreadline" - @CONF[:USE_SINGLELINE] = false - when "--multiline", "--reidline" - if opt == "--reidline" - warn <<~MSG.strip - --reidline is deprecated, please use --multiline instead. - MSG - end - - @CONF[:USE_MULTILINE] = true - when "--nomultiline", "--noreidline" - if opt == "--noreidline" - warn <<~MSG.strip - --noreidline is deprecated, please use --nomultiline instead. - MSG - end - - @CONF[:USE_MULTILINE] = false - when /^--extra-doc-dir(?:=(.+))?/ - opt = $1 || argv.shift - @CONF[:EXTRA_DOC_DIRS] << opt - when "--echo" - @CONF[:ECHO] = true - when "--noecho" - @CONF[:ECHO] = false - when "--echo-on-assignment" - @CONF[:ECHO_ON_ASSIGNMENT] = true - when "--noecho-on-assignment" - @CONF[:ECHO_ON_ASSIGNMENT] = false - when "--truncate-echo-on-assignment" - @CONF[:ECHO_ON_ASSIGNMENT] = :truncate - when "--verbose" - @CONF[:VERBOSE] = true - when "--noverbose" - @CONF[:VERBOSE] = false - when "--colorize" - @CONF[:USE_COLORIZE] = true - when "--nocolorize" - @CONF[:USE_COLORIZE] = false - when "--autocomplete" - @CONF[:USE_AUTOCOMPLETE] = true - when "--noautocomplete" - @CONF[:USE_AUTOCOMPLETE] = false - when "--regexp-completor" - @CONF[:COMPLETOR] = :regexp - when "--type-completor" - @CONF[:COMPLETOR] = :type - when /^--prompt-mode(?:=(.+))?/, /^--prompt(?:=(.+))?/ - opt = $1 || argv.shift - prompt_mode = opt.upcase.tr("-", "_").intern - @CONF[:PROMPT_MODE] = prompt_mode - when "--noprompt" - @CONF[:PROMPT_MODE] = :NULL - when "--script" - noscript = false - when "--noscript" - noscript = true - when "--inf-ruby-mode" - @CONF[:PROMPT_MODE] = :INF_RUBY - when "--sample-book-mode", "--simple-prompt" - @CONF[:PROMPT_MODE] = :SIMPLE - when "--tracer" - @CONF[:USE_TRACER] = true - when /^--back-trace-limit(?:=(.+))?/ - @CONF[:BACK_TRACE_LIMIT] = ($1 || argv.shift).to_i - when /^--context-mode(?:=(.+))?/ - @CONF[:CONTEXT_MODE] = ($1 || argv.shift).to_i - when "--single-irb" - @CONF[:SINGLE_IRB] = true - when "-v", "--version" - print IRB.version, "\n" - exit 0 - when "-h", "--help" - require_relative "help" - IRB.print_usage - exit 0 - when "--" - if !noscript && (opt = argv.shift) - @CONF[:SCRIPT] = opt - $0 = opt - end - break - when /^-./ - fail UnrecognizedSwitch, opt - else - if noscript - argv.unshift(opt) - else - @CONF[:SCRIPT] = opt - $0 = opt - end - break - end - end - - load_path.collect! do |path| - /\A\.\// =~ path ? path : File.expand_path(path) - end - $LOAD_PATH.unshift(*load_path) - end - - # Run the config file - def IRB.run_config - if @CONF[:RC] - irbrc_files.each do |rc| - load rc - rescue StandardError, ScriptError => e - warn "Error loading RC file '#{rc}':\n#{e.full_message(highlight: false)}" - end - end - end - - IRBRC_EXT = "rc" - - def IRB.rc_file(ext) - prepare_irbrc_name_generators - - # When irbrc exist in default location - if (rcgen = @existing_rc_name_generators.first) - return rcgen.call(ext) - end - - # When irbrc does not exist in default location - rc_file_generators do |rcgen| - return rcgen.call(ext) - end - - # When HOME and XDG_CONFIG_HOME are not available - nil - end - - def IRB.irbrc_files - prepare_irbrc_name_generators - @irbrc_files - end - - def IRB.validate_config - conf[:IRB_NAME] = conf[:IRB_NAME].to_s - - irb_rc = conf[:IRB_RC] - unless irb_rc.nil? || irb_rc.respond_to?(:call) - raise_validation_error "IRB.conf[:IRB_RC] should be a callable object. Got #{irb_rc.inspect}." - end - - back_trace_limit = conf[:BACK_TRACE_LIMIT] - unless back_trace_limit.is_a?(Integer) - raise_validation_error "IRB.conf[:BACK_TRACE_LIMIT] should be an integer. Got #{back_trace_limit.inspect}." - end - - prompt = conf[:PROMPT] - unless prompt.is_a?(Hash) - msg = "IRB.conf[:PROMPT] should be a Hash. Got #{prompt.inspect}." - - if prompt.is_a?(Symbol) - msg += " Did you mean to set `IRB.conf[:PROMPT_MODE]`?" - end - - raise_validation_error msg - end - - eval_history = conf[:EVAL_HISTORY] - unless eval_history.nil? || eval_history.is_a?(Integer) - raise_validation_error "IRB.conf[:EVAL_HISTORY] should be an integer. Got #{eval_history.inspect}." - end - end - - def IRB.raise_validation_error(msg) - raise TypeError, msg, @irbrc_files - end - - # loading modules - def IRB.load_modules - for m in @CONF[:LOAD_MODULES] - begin - require m - rescue LoadError => err - warn "#{err.class}: #{err}", uplevel: 0 - end - end - end - - class << IRB - private - - def prepare_irbrc_name_generators - return if @existing_rc_name_generators - - @existing_rc_name_generators = [] - @irbrc_files = [] - rc_file_generators do |rcgen| - irbrc = rcgen.call(IRBRC_EXT) - if File.exist?(irbrc) - @irbrc_files << irbrc - @existing_rc_name_generators << rcgen - end - end - generate_current_dir_irbrc_files.each do |irbrc| - @irbrc_files << irbrc if File.exist?(irbrc) - end - @irbrc_files.uniq! - end - - # enumerate possible rc-file base name generators - def rc_file_generators - if irbrc = ENV["IRBRC"] - yield proc{|rc| rc == "rc" ? irbrc : irbrc+rc} - end - if xdg_config_home = ENV["XDG_CONFIG_HOME"] - irb_home = File.join(xdg_config_home, "irb") - if File.directory?(irb_home) - yield proc{|rc| irb_home + "/irb#{rc}"} - end - end - if home = ENV["HOME"] - yield proc{|rc| home+"/.irb#{rc}"} - if xdg_config_home.nil? || xdg_config_home.empty? - yield proc{|rc| home+"/.config/irb/irb#{rc}"} - end - end - end - - # possible irbrc files in current directory - def generate_current_dir_irbrc_files - current_dir = Dir.pwd - %w[.irbrc irbrc _irbrc $irbrc].map { |file| "#{current_dir}/#{file}" } - end - - def set_encoding(extern, intern = nil, override: true) - verbose, $VERBOSE = $VERBOSE, nil - Encoding.default_external = extern unless extern.nil? || extern.empty? - Encoding.default_internal = intern unless intern.nil? || intern.empty? - [$stdin, $stdout, $stderr].each do |io| - io.set_encoding(extern, intern) - end - if override - @CONF[:LC_MESSAGES].instance_variable_set(:@override_encoding, extern) - else - @CONF[:LC_MESSAGES].instance_variable_set(:@encoding, extern) - end - ensure - $VERBOSE = verbose - end - end -end diff --git a/lib/irb/input-method.rb b/lib/irb/input-method.rb deleted file mode 100644 index 260d9a1cbf..0000000000 --- a/lib/irb/input-method.rb +++ /dev/null @@ -1,515 +0,0 @@ -# frozen_string_literal: true -# -# irb/input-method.rb - input methods used irb -# by Keiju ISHITSUKA([email protected]) -# - -require_relative 'completion' -require_relative "history" -require 'io/console' -require 'reline' - -module IRB - class InputMethod - BASIC_WORD_BREAK_CHARACTERS = " \t\n`><=;|&{(" - - # 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 - fail NotImplementedError - end - public :gets - - def winsize - if instance_variable_defined?(:@stdout) && @stdout.tty? - @stdout.winsize - else - [24, 80] - end - end - - # Whether this input method is still readable when there is no more data to - # read. - # - # See IO#eof for more information. - def readable_after_eof? - false - end - - def support_history_saving? - false - end - - def prompting? - false - end - - # For debug message - def inspect - 'Abstract InputMethod' - end - end - - class StdioInputMethod < InputMethod - # Creates a new input method object - def initialize - @line_no = 0 - @line = [] - @stdin = IO.open(STDIN.to_i, :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-") - @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 - # Workaround for debug compatibility test https://github.com/ruby/debug/pull/1100 - puts if ENV['RUBY_DEBUG_TEST_UI'] - - 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? - if @stdin.wait_readable(0.00001) - c = @stdin.getc - result = c.nil? ? true : false - @stdin.ungetc(c) unless c.nil? - result - else # buffer is empty - false - end - end - - # Whether this input method is still readable when there is no more data to - # read. - # - # See IO#eof for more information. - def readable_after_eof? - true - end - - def prompting? - STDIN.tty? - 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 - - # For debug message - def inspect - 'StdioInputMethod' - end - end - - # Use a File for IO with irb, see InputMethod - class FileInputMethod < InputMethod - class << self - def open(file, &block) - begin - io = new(file) - block.call(io) - ensure - io&.close - end - end - end - - # Creates a new input method object - def initialize(file) - @io = file.is_a?(IO) ? file : File.open(file) - @external_encoding = @io.external_encoding - 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? - @io.closed? || @io.eof? - end - - # Reads the next line from this input method. - # - # See IO#gets for more information. - def gets - print @prompt - @io.gets - end - - # The external encoding for standard input. - def encoding - @external_encoding - end - - # For debug message - def inspect - 'FileInputMethod' - end - - def close - @io.close - end - end - - class ReadlineInputMethod < StdioInputMethod - class << self - def initialize_readline - require "readline" - rescue LoadError - else - include ::Readline - end - end - - include HistorySavingAbility - - # Creates a new input method object using Readline - def initialize - self.class.initialize_readline - if Readline.respond_to?(:encoding_system_needs) - IRB.__send__(:set_encoding, Readline.encoding_system_needs.name, override: false) - end - - super - - @eof = false - @completor = RegexpCompletor.new - - if Readline.respond_to?("basic_word_break_characters=") - Readline.basic_word_break_characters = BASIC_WORD_BREAK_CHARACTERS - end - Readline.completion_append_character = nil - Readline.completion_proc = ->(target) { - bind = IRB.conf[:MAIN_CONTEXT].workspace.binding - @completor.completion_candidates('', target, '', bind: bind) - } - end - - def completion_info - 'RegexpCompletor' - end - - # Reads the next line from this input method. - # - # See IO#gets for more information. - def gets - Readline.input = @stdin - Readline.output = @stdout - if l = readline(@prompt, false) - HISTORY.push(l) if !l.empty? - @line[@line_no += 1] = l + "\n" - else - @eof = true - l - 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 - - def prompting? - true - end - - # For debug message - def inspect - readline_impl = (defined?(Reline) && Readline == Reline) ? 'Reline' : 'ext/readline' - str = "ReadlineInputMethod with #{readline_impl} #{Readline::VERSION}" - inputrc_path = File.expand_path(ENV['INPUTRC'] || '~/.inputrc') - str += " and #{inputrc_path}" if File.exist?(inputrc_path) - str - end - end - - class RelineInputMethod < StdioInputMethod - HISTORY = Reline::HISTORY - include HistorySavingAbility - # Creates a new input method object using Reline - def initialize(completor) - IRB.__send__(:set_encoding, Reline.encoding_system_needs.name, override: false) - - super() - - @eof = false - @completor = completor - - Reline.basic_word_break_characters = BASIC_WORD_BREAK_CHARACTERS - Reline.completion_append_character = nil - Reline.completer_quote_characters = '' - Reline.completion_proc = ->(target, preposing, postposing) { - bind = IRB.conf[:MAIN_CONTEXT].workspace.binding - @completion_params = [preposing, target, postposing, bind] - @completor.completion_candidates(preposing, target, postposing, bind: bind) - } - Reline.output_modifier_proc = proc do |input, complete:| - IRB.CurrentContext.colorize_input(input, complete: complete) - end - Reline.dig_perfect_match_proc = ->(matched) { display_document(matched) } - Reline.autocompletion = IRB.conf[:USE_AUTOCOMPLETE] - - if IRB.conf[:USE_AUTOCOMPLETE] - begin - require 'rdoc' - Reline.add_dialog_proc(:show_doc, show_doc_dialog_proc, Reline::DEFAULT_DIALOG_CONTEXT) - rescue LoadError - end - end - end - - def completion_info - autocomplete_message = Reline.autocompletion ? 'Autocomplete' : 'Tab Complete' - "#{autocomplete_message}, #{@completor.inspect}" - end - - def check_termination(&block) - @check_termination_proc = block - end - - def dynamic_prompt(&block) - @prompt_proc = block - end - - def auto_indent(&block) - @auto_indent_proc = block - end - - def retrieve_doc_namespace(matched) - preposing, _target, postposing, bind = @completion_params - @completor.doc_namespace(preposing, matched, postposing, bind: bind) - end - - def rdoc_ri_driver - return @rdoc_ri_driver if defined?(@rdoc_ri_driver) - - begin - require 'rdoc' - rescue LoadError - @rdoc_ri_driver = nil - else - options = {} - options[:extra_doc_dirs] = IRB.conf[:EXTRA_DOC_DIRS] unless IRB.conf[:EXTRA_DOC_DIRS].empty? - @rdoc_ri_driver = RDoc::RI::Driver.new(options) - end - end - - def show_doc_dialog_proc - input_method = self # self is changed in the lambda below. - ->() { - dialog.trap_key = nil - alt_d = [ - [27, 100], # Normal Alt+d when convert-meta isn't used. - # When option/alt is not configured as a meta key in terminal emulator, - # option/alt + d will send a unicode character depend on OS keyboard setting. - [195, 164], # "ä" in somewhere (FIXME: environment information is unknown). - [226, 136, 130] # "∂" Alt+d on Mac keyboard. - ] - - if just_cursor_moving and completion_journey_data.nil? - return nil - end - cursor_pos_to_render, result, pointer, autocomplete_dialog = context.pop(4) - return nil if result.nil? or pointer.nil? or pointer < 0 - - name = input_method.retrieve_doc_namespace(result[pointer]) - # Use first one because document dialog does not support multiple namespaces. - name = name.first if name.is_a?(Array) - - show_easter_egg = name&.match?(/\ARubyVM/) && !ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER'] - - driver = input_method.rdoc_ri_driver - - if key.match?(dialog.name) - if show_easter_egg - IRB.__send__(:easter_egg) - else - # RDoc::RI::Driver#display_names uses pager command internally. - # Some pager command like `more` doesn't use alternate screen - # so we need to turn on and off alternate screen manually. - begin - print "\e[?1049h" - driver.display_names([name]) - rescue RDoc::RI::Driver::NotFoundError - ensure - print "\e[?1049l" - end - end - end - - begin - name = driver.expand_name(name) - rescue RDoc::RI::Driver::NotFoundError - return nil - rescue - return nil # unknown error - end - doc = nil - used_for_class = false - if not name =~ /#|\./ - found, klasses, includes, extends = driver.classes_and_includes_and_extends_for(name) - if not found.empty? - doc = driver.class_document(name, found, klasses, includes, extends) - used_for_class = true - end - end - unless used_for_class - doc = RDoc::Markup::Document.new - begin - driver.add_method(doc, name) - rescue RDoc::RI::Driver::NotFoundError - doc = nil - rescue - return nil # unknown error - end - end - return nil if doc.nil? - width = 40 - - right_x = cursor_pos_to_render.x + autocomplete_dialog.width - if right_x + width > screen_width - right_width = screen_width - (right_x + 1) - left_x = autocomplete_dialog.column - width - left_x = 0 if left_x < 0 - left_width = width > autocomplete_dialog.column ? autocomplete_dialog.column : width - if right_width.positive? and left_width.positive? - if right_width >= left_width - width = right_width - x = right_x - else - width = left_width - x = left_x - end - elsif right_width.positive? and left_width <= 0 - width = right_width - x = right_x - elsif right_width <= 0 and left_width.positive? - width = left_width - x = left_x - else # Both are negative width. - return nil - end - else - x = right_x - end - formatter = RDoc::Markup::ToAnsi.new - formatter.width = width - dialog.trap_key = alt_d - mod_key = RUBY_PLATFORM.match?(/darwin/) ? "Option" : "Alt" - if show_easter_egg - type = STDOUT.external_encoding == Encoding::UTF_8 ? :unicode : :ascii - contents = IRB.send(:easter_egg_logo, type).split("\n") - message = "Press #{mod_key}+d to see more" - contents[0][0, message.size] = message - else - message = "Press #{mod_key}+d to read the full document" - contents = [message] + doc.accept(formatter).split("\n") - end - contents = contents.take(preferred_dialog_height) - - y = cursor_pos_to_render.y - Reline::DialogRenderInfo.new(pos: Reline::CursorPos.new(x, y), contents: contents, width: width, bg_color: '49') - } - end - - def display_document(matched) - driver = rdoc_ri_driver - return unless driver - - if matched =~ /\A(?:::)?RubyVM/ and not ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER'] - IRB.__send__(:easter_egg) - return - end - - namespace = retrieve_doc_namespace(matched) - return unless namespace - - if namespace.is_a?(Array) - out = RDoc::Markup::Document.new - namespace.each do |m| - begin - driver.add_method(out, m) - rescue RDoc::RI::Driver::NotFoundError - end - end - driver.display(out) - else - begin - driver.display_names([namespace]) - rescue RDoc::RI::Driver::NotFoundError - end - end - end - - # Reads the next line from this input method. - # - # See IO#gets for more information. - def gets - Reline.input = @stdin - Reline.output = @stdout - Reline.prompt_proc = @prompt_proc - Reline.auto_indent_proc = @auto_indent_proc if @auto_indent_proc - if l = Reline.readmultiline(@prompt, false, &@check_termination_proc) - Reline::HISTORY.push(l) if !l.empty? - @line[@line_no += 1] = l + "\n" - else - @eof = true - l - 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 - - def prompting? - true - end - - # For debug message - def inspect - config = Reline::Config.new - str = "RelineInputMethod with Reline #{Reline::VERSION}" - inputrc_path = File.expand_path(config.inputrc_path) - str += " and #{inputrc_path}" if File.exist?(inputrc_path) - str - end - end - - class ReidlineInputMethod < RelineInputMethod - def initialize - warn <<~MSG.strip - IRB::ReidlineInputMethod is deprecated, please use IRB::RelineInputMethod instead. - MSG - super - end - end -end diff --git a/lib/irb/inspector.rb b/lib/irb/inspector.rb deleted file mode 100644 index 75a257b4ba..0000000000 --- a/lib/irb/inspector.rb +++ /dev/null @@ -1,136 +0,0 @@ -# frozen_string_literal: true -# -# irb/inspector.rb - inspect methods -# by Keiju ISHITSUKA([email protected]) -# - -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 - KERNEL_INSPECT = Object.instance_method(:inspect) - # Default inspectors available to irb, this includes: - # - # +:pp+:: Using Kernel#pretty_inspect - # +:yaml+:: Using YAML.dump - # +:marshal+:: Using Marshal.dump - INSPECTORS = {} - - class << self - # Determines the inspector to use where +inspector+ is one of the keys passed - # during inspector definition. - def keys_with_inspector(inspector) - INSPECTORS.select{|k, v| v == inspector}.collect{|k, v| k} - end - - # Example - # - # Inspector.def_inspector(key, init_p=nil){|v| v.inspect} - # Inspector.def_inspector([key1,..], init_p=nil){|v| v.inspect} - # Inspector.def_inspector(key, inspector) - # Inspector.def_inspector([key1,...], inspector) - def def_inspector(key, arg=nil, &block) - if block_given? - inspector = IRB::Inspector(block, arg) - else - inspector = arg - end - - case key - when Array - for k in key - def_inspector(k, inspector) - end - when Symbol - INSPECTORS[key] = inspector - INSPECTORS[key.to_s] = inspector - when String - INSPECTORS[key] = inspector - INSPECTORS[key.intern] = inspector - else - INSPECTORS[key] = inspector - end - end - end - - # 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 - # dependent libraries. - def init - @init.call if @init - end - - def support_stream_output? - second_parameter_type = @inspect.parameters[1]&.first - second_parameter_type == :req || second_parameter_type == :opt - end - - # Proc to call when the input is evaluated and output in irb. - def inspect_value(v, output, colorize: true) - support_stream_output? ? @inspect.call(v, output, colorize: colorize) : output << @inspect.call(v, colorize: colorize) - rescue => e - puts "An error occurred when inspecting the object: #{e.inspect}" - - begin - puts "Result of Kernel#inspect: #{KERNEL_INSPECT.bind_call(v)}" - '' - rescue => e - puts "An error occurred when running Kernel#inspect: #{e.inspect}" - puts e.backtrace.join("\n") - '' - end - end - end - - Inspector.def_inspector([false, :to_s, :raw]){|v| v.to_s} - Inspector.def_inspector([:p, :inspect]){|v, colorize: true| - Color.colorize_code(v.inspect, colorable: colorize && Color.colorable? && Color.inspect_colorable?(v)) - } - Inspector.def_inspector([true, :pp, :pretty_inspect], proc{require_relative "color_printer"}){|v, output, colorize: true| - IRB::ColorPrinter.pp(v, output, colorize: colorize) - } - Inspector.def_inspector([:yaml, :YAML], proc{require "yaml"}){|v| - begin - YAML.dump(v) - rescue - puts "(can't dump yaml. use inspect)" - v.inspect - end - } - - Inspector.def_inspector([:marshal, :Marshal, :MARSHAL, Marshal]){|v| - Marshal.dump(v) - } -end diff --git a/lib/irb/irb.gemspec b/lib/irb/irb.gemspec deleted file mode 100644 index 9a93382b7a..0000000000 --- a/lib/irb/irb.gemspec +++ /dev/null @@ -1,46 +0,0 @@ -begin - require_relative "lib/irb/version" -rescue LoadError - # for Ruby core repository - require_relative "version" -end - -Gem::Specification.new do |spec| - spec.name = "irb" - spec.version = IRB::VERSION - spec.authors = ["aycabta", "Keiju ISHITSUKA"] - spec.email = ["[email protected]", "[email protected]"] - - spec.summary = %q{Interactive Ruby command-line tool for REPL (Read Eval Print Loop).} - spec.description = %q{Interactive Ruby command-line tool for REPL (Read Eval Print Loop).} - spec.homepage = "https://github.com/ruby/irb" - spec.licenses = ["Ruby", "BSD-2-Clause"] - - spec.metadata["homepage_uri"] = spec.homepage - spec.metadata["source_code_uri"] = spec.homepage - spec.metadata["documentation_uri"] = "https://ruby.github.io/irb/" - spec.metadata["changelog_uri"] = "#{spec.homepage}/releases" - - spec.files = [ - "Gemfile", - "LICENSE.txt", - "README.md", - "Rakefile", - "bin/console", - "bin/setup", - "doc/irb/irb-tools.rd.ja", - "doc/irb/irb.rd.ja", - "exe/irb", - "irb.gemspec", - "man/irb.1", - ] + Dir.glob("lib/**/*") - spec.bindir = "exe" - spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } - spec.require_paths = ["lib"] - - spec.required_ruby_version = Gem::Requirement.new(">= 2.7") - - spec.add_dependency "reline", ">= 0.4.2" - spec.add_dependency "rdoc", ">= 4.0.0" - spec.add_dependency "pp", ">= 0.6.0" -end diff --git a/lib/irb/lc/error.rb b/lib/irb/lc/error.rb deleted file mode 100644 index ee0f047822..0000000000 --- a/lib/irb/lc/error.rb +++ /dev/null @@ -1,52 +0,0 @@ -# frozen_string_literal: true -# -# irb/lc/error.rb - -# by Keiju ISHITSUKA([email protected]) -# - -module IRB - # :stopdoc: - - class UnrecognizedSwitch < StandardError - def initialize(val) - super("Unrecognized switch: #{val}") - end - end - class CantReturnToNormalMode < StandardError - def initialize - super("Can't return to normal mode.") - end - end - class IllegalParameter < StandardError - def initialize(val) - super("Invalid parameter(#{val}).") - end - end - class IrbAlreadyDead < StandardError - def initialize - super("Irb is already dead.") - end - end - class IrbSwitchedToCurrentThread < StandardError - def initialize - super("Switched to current thread.") - end - end - class NoSuchJob < StandardError - def initialize(val) - super("No such job(#{val}).") - end - end - class CantChangeBinding < StandardError - def initialize(val) - super("Can't change binding to (#{val}).") - end - end - class UndefinedPromptMode < StandardError - def initialize(val) - super("Undefined prompt mode(#{val}).") - end - end - - # :startdoc: -end diff --git a/lib/irb/lc/help-message b/lib/irb/lc/help-message deleted file mode 100644 index 37347306e8..0000000000 --- a/lib/irb/lc/help-message +++ /dev/null @@ -1,55 +0,0 @@ -Usage: irb.rb [options] [programfile] [arguments] - -f Don't initialize from configuration file. - -d Set $DEBUG and $VERBOSE to true (same as 'ruby -d'). - -r load-module Require load-module (same as 'ruby -r'). - -I path Specify $LOAD_PATH directory (same as 'ruby -I'). - -U Set external and internal encodings to UTF-8. - -E ex[:in] Set default external (ex) and internal (in) encodings - (same as 'ruby -E'). - -w Suppress warnings (same as 'ruby -w'). - -W[level=2] Set warning level: 0=silence, 1=medium, 2=verbose - (same as 'ruby -W'). - --context-mode n Set n[0-4] to method to create Binding Object, - when new workspace was created. - --extra-doc-dir Add an extra doc dir for the doc dialog. - --echo Show result (default). - --noecho Don't show result. - --echo-on-assignment - Show result on assignment. - --noecho-on-assignment - Don't show result on assignment. - --truncate-echo-on-assignment - Show truncated result on assignment (default). - --inspect Use 'inspect' for output. - --noinspect Don't use 'inspect' for output. - --no-pager Don't use pager. - --multiline Use multiline editor module (default). - --nomultiline Don't use multiline editor module. - --singleline Use single line editor module. - --nosingleline Don't use single line editor module (default). - --colorize Use color-highlighting (default). - --nocolorize Don't use color-highlighting. - --autocomplete Use auto-completion (default). - --noautocomplete Don't use auto-completion. - --regexp-completor - Use regexp based completion (default). - --type-completor Use type based completion. - --prompt prompt-mode, --prompt-mode prompt-mode - Set prompt mode. Pre-defined prompt modes are: - 'default', 'classic', 'simple', 'inf-ruby', 'xmp', 'null'. - --inf-ruby-mode Use prompt appropriate for inf-ruby-mode on emacs. - Suppresses --multiline and --singleline. - --sample-book-mode, --simple-prompt - Set prompt mode to 'simple'. - --noprompt Don't output prompt. - --script Script mode (default, treat first argument as script) - --noscript No script mode (leave arguments in argv) - --single-irb Share self with sub-irb. - --tracer Show stack trace for each command. - --back-trace-limit n[=16] - Display backtrace top n and bottom n. - --verbose Show details. - --noverbose Don't show details. - -v, --version Print the version of irb. - -h, --help Print help. - -- Separate options of irb from the list of command-line args. diff --git a/lib/irb/lc/ja/error.rb b/lib/irb/lc/ja/error.rb deleted file mode 100644 index 9e2e5b8870..0000000000 --- a/lib/irb/lc/ja/error.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true -# -# irb/lc/ja/error.rb - -# by Keiju ISHITSUKA([email protected]) -# - -module IRB - # :stopdoc: - - class UnrecognizedSwitch < StandardError - def initialize(val) - super("スイッチ(#{val})が分りません") - end - end - class CantReturnToNormalMode < StandardError - def initialize - super("Normalモードに戻れません.") - end - end - class IllegalParameter < StandardError - def initialize(val) - super("パラメータ(#{val})が間違っています.") - end - end - class IrbAlreadyDead < StandardError - def initialize - super("Irbは既に死んでいます.") - end - end - class IrbSwitchedToCurrentThread < StandardError - def initialize - super("カレントスレッドに切り替わりました.") - end - end - class NoSuchJob < StandardError - def initialize(val) - super("そのようなジョブ(#{val})はありません.") - end - end - class CantChangeBinding < StandardError - def initialize(val) - super("バインディング(#{val})に変更できません.") - end - end - class UndefinedPromptMode < StandardError - def initialize(val) - super("プロンプトモード(#{val})は定義されていません.") - end - end - - # :startdoc: -end -# vim:fileencoding=utf-8 diff --git a/lib/irb/lc/ja/help-message b/lib/irb/lc/ja/help-message deleted file mode 100644 index 844c67bbba..0000000000 --- a/lib/irb/lc/ja/help-message +++ /dev/null @@ -1,58 +0,0 @@ -Usage: irb.rb [options] [programfile] [arguments] - -f ~/.irbrc を読み込まない. - -d $DEBUG をtrueにする(ruby -d と同じ) - -r load-module ruby -r と同じ. - -I path $LOAD_PATH に path を追加する. - -U ruby -U と同じ. - -E enc ruby -E と同じ. - -w ruby -w と同じ. - -W[level=2] ruby -W と同じ. - --context-mode n 新しいワークスペースを作成した時に関連する Binding - オブジェクトの作成方法を 0 から 4 のいずれかに設定する. - --extra-doc-dir 指定したディレクトリのドキュメントを追加で読み込む. - --echo 実行結果を表示する(デフォルト). - --noecho 実行結果を表示しない. - --echo-on-assignment - 代入結果を表示する. - --noecho-on-assignment - 代入結果を表示しない. - --truncate-echo-on-assignment - truncateされた代入結果を表示する(デフォルト). - --inspect 結果出力にinspectを用いる. - --noinspect 結果出力にinspectを用いない. - --no-pager ページャを使用しない. - --multiline マルチラインエディタを利用する. - --nomultiline マルチラインエディタを利用しない. - --singleline シングルラインエディタを利用する. - --nosingleline シングルラインエディタを利用しない. - --colorize 色付けを利用する. - --nocolorize 色付けを利用しない. - --autocomplete オートコンプリートを利用する. - --noautocomplete オートコンプリートを利用しない. - --regexp-completor - 補完に正規表現を利用する. - --type-completor 補完に型情報を利用する. - --prompt prompt-mode/--prompt-mode prompt-mode - プロンプトモードを切り替える. - 現在定義されているプロンプトモードは, - default, classic, simple, inf-ruby, xmp, null. - --inf-ruby-mode emacsのinf-ruby-mode用のプロンプト表示を行なう. 特 - に指定がない限り, シングルラインエディタとマルチラ - インエディタは使わなくなる. - --sample-book-mode/--simple-prompt - 非常にシンプルなプロンプトを用いるモードです. - --noprompt プロンプト表示を行なわない. - --script スクリプトモード(最初の引数をスクリプトファイルとして扱う、デフォルト) - --noscript 引数をargvとして扱う. - --single-irb irb 中で self を実行して得られるオブジェクトをサ - ブ irb と共有する. - --tracer コマンド実行時にトレースを行なう. - --back-trace-limit n - バックトレース表示をバックトレースの頭から n, 後ろ - からnだけ行なう. デフォルトは16 - - --verbose 詳細なメッセージを出力する. - --noverbose 詳細なメッセージを出力しない(デフォルト). - -v, --version irbのバージョンを表示する. - -h, --help irb のヘルプを表示する. - -- 以降のコマンドライン引数をオプションとして扱わない. diff --git a/lib/irb/locale.rb b/lib/irb/locale.rb deleted file mode 100644 index 2abcc7354b..0000000000 --- a/lib/irb/locale.rb +++ /dev/null @@ -1,153 +0,0 @@ -# frozen_string_literal: true -# -# irb/locale.rb - internationalization module -# by Keiju ISHITSUKA([email protected]) -# - -module IRB # :nodoc: - class Locale - - LOCALE_NAME_RE = %r[ - (?<language>[[:alpha:]]{2,3}) - (?:_ (?<territory>[[:alpha:]]{2,3}) )? - (?:\. (?<codeset>[^@]+) )? - (?:@ (?<modifier>.*) )? - ]x - LOCALE_DIR = "/lc/" - - LEGACY_ENCODING_ALIAS_MAP = { - 'ujis' => Encoding::EUC_JP, - 'euc' => Encoding::EUC_JP - } - - @@loaded = [] - - def initialize(locale = nil) - @override_encoding = nil - @lang = @territory = @encoding_name = @modifier = nil - @locale = locale || ENV["IRB_LANG"] || ENV["LC_MESSAGES"] || ENV["LC_ALL"] || ENV["LANG"] || "C" - if m = LOCALE_NAME_RE.match(@locale) - @lang, @territory, @encoding_name, @modifier = m[:language], m[:territory], m[:codeset], m[:modifier] - - if @encoding_name - if @encoding = LEGACY_ENCODING_ALIAS_MAP[@encoding_name] - warn(("%s is obsolete. use %s" % ["#{@lang}_#{@territory}.#{@encoding_name}", "#{@lang}_#{@territory}.#{@encoding.name}"]), uplevel: 1) - else - @encoding = Encoding.find(@encoding_name) rescue nil - end - end - end - @encoding ||= (Encoding.find('locale') rescue Encoding::ASCII_8BIT) - end - - attr_reader :lang, :territory, :modifier - - def encoding - @override_encoding || @encoding - end - - def String(mes) - mes = super(mes) - if encoding - mes.encode(encoding, undef: :replace) - else - mes - end - end - - def format(*opts) - String(super(*opts)) - end - - def gets(*rs) - String(super(*rs)) - end - - def readline(*rs) - String(super(*rs)) - end - - def print(*opts) - ary = opts.collect{|opt| String(opt)} - super(*ary) - end - - def printf(*opts) - s = format(*opts) - print s - end - - def puts(*opts) - ary = opts.collect{|opt| String(opt)} - super(*ary) - end - - def load(file) - found = find(file) - if found - unless @@loaded.include?(found) - @@loaded << found # cache - Kernel.load(found) - end - else - raise LoadError, "No such file to load -- #{file}" - end - end - - def find(file, paths = $:) - dir = File.dirname(file) - dir = "" if dir == "." - base = File.basename(file) - - if dir.start_with?('/') - return each_localized_path(dir, base).find{|full_path| File.readable? full_path} - else - return search_file(paths, dir, base) - end - end - - # @param paths load paths in which IRB find a localized file. - # @param dir directory - # @param file basename to be localized - # - # typically, for the parameters and a <path> in paths, it searches - # <path>/<dir>/<locale>/<file> - def search_file(lib_paths, dir, file) - each_localized_path(dir, file) do |lc_path| - lib_paths.each do |libpath| - full_path = File.join(libpath, lc_path) - return full_path if File.readable?(full_path) - end - redo if defined?(Gem) and Gem.try_activate(lc_path) - end - nil - end - - def each_localized_path(dir, file) - return enum_for(:each_localized_path) unless block_given? - each_sublocale do |lc| - yield lc.nil? ? File.join(dir, LOCALE_DIR, file) : File.join(dir, LOCALE_DIR, lc, file) - end - end - - def each_sublocale - if @lang - if @territory - if @encoding_name - yield "#{@lang}_#{@territory}.#{@encoding_name}@#{@modifier}" if @modifier - yield "#{@lang}_#{@territory}.#{@encoding_name}" - end - yield "#{@lang}_#{@territory}@#{@modifier}" if @modifier - yield "#{@lang}_#{@territory}" - end - if @encoding_name - yield "#{@lang}.#{@encoding_name}@#{@modifier}" if @modifier - yield "#{@lang}.#{@encoding_name}" - end - yield "#{@lang}@#{@modifier}" if @modifier - yield "#{@lang}" - end - yield nil - end - end -end diff --git a/lib/irb/nesting_parser.rb b/lib/irb/nesting_parser.rb deleted file mode 100644 index c1c9a5cc76..0000000000 --- a/lib/irb/nesting_parser.rb +++ /dev/null @@ -1,239 +0,0 @@ -# frozen_string_literal: true -module IRB - module NestingParser - IGNORE_TOKENS = %i[on_sp on_ignored_nl on_comment on_embdoc_beg on_embdoc on_embdoc_end] - - class << self - # Scan each token and call the given block with array of token and other information for parsing - def scan_opens(tokens) - opens = [] - pending_heredocs = [] - first_token_on_line = true - tokens.each do |t| - skip = false - last_tok, state, args = opens.last - case state - when :in_alias_undef - skip = t.event == :on_kw - when :in_unquoted_symbol - unless IGNORE_TOKENS.include?(t.event) - opens.pop - skip = true - end - when :in_lambda_head - opens.pop if t.event == :on_tlambeg || (t.event == :on_kw && t.tok == 'do') - when :in_method_head - unless IGNORE_TOKENS.include?(t.event) - next_args = [] - body = nil - if args.include?(:receiver) - case t.event - when :on_lparen, :on_ivar, :on_gvar, :on_cvar - # def (receiver). | def @ivar. | def $gvar. | def @@cvar. - next_args << :dot - when :on_kw - case t.tok - when 'self', 'true', 'false', 'nil' - # def self(arg) | def self. - next_args.push(:arg, :dot) - else - # def if(arg) - skip = true - next_args << :arg - end - when :on_op, :on_backtick - # def +(arg) - skip = true - next_args << :arg - when :on_ident, :on_const - # def a(arg) | def a. - next_args.push(:arg, :dot) - end - end - if args.include?(:dot) - # def receiver.name - next_args << :name if t.event == :on_period || (t.event == :on_op && t.tok == '::') - end - if args.include?(:name) - if %i[on_ident on_const on_op on_kw on_backtick].include?(t.event) - # def name(arg) | def receiver.name(arg) - next_args << :arg - skip = true - end - end - if args.include?(:arg) - case t.event - when :on_nl, :on_semicolon - # def receiver.f; - body = :normal - when :on_lparen - # def receiver.f() - next_args << :eq - else - if t.event == :on_op && t.tok == '=' - # def receiver.f = - body = :oneliner - else - # def receiver.f arg - next_args << :arg_without_paren - end - end - end - if args.include?(:eq) - if t.event == :on_op && t.tok == '=' - body = :oneliner - else - body = :normal - end - end - if args.include?(:arg_without_paren) - if %i[on_semicolon on_nl].include?(t.event) - # def f a; - body = :normal - else - # def f a, b - next_args << :arg_without_paren - end - end - if body == :oneliner - opens.pop - elsif body - opens[-1] = [last_tok, nil] - else - opens[-1] = [last_tok, :in_method_head, next_args] - end - end - when :in_for_while_until_condition - if t.event == :on_semicolon || t.event == :on_nl || (t.event == :on_kw && t.tok == 'do') - skip = true if t.event == :on_kw && t.tok == 'do' - opens[-1] = [last_tok, nil] - end - end - - unless skip - case t.event - when :on_kw - case t.tok - when 'begin', 'class', 'module', 'do', 'case' - opens << [t, nil] - when 'end' - opens.pop - when 'def' - opens << [t, :in_method_head, [:receiver, :name]] - when 'if', 'unless' - unless t.state.allbits?(Ripper::EXPR_LABEL) - opens << [t, nil] - end - when 'while', 'until' - unless t.state.allbits?(Ripper::EXPR_LABEL) - opens << [t, :in_for_while_until_condition] - end - when 'ensure', 'rescue' - unless t.state.allbits?(Ripper::EXPR_LABEL) - opens.pop - opens << [t, nil] - end - when 'alias' - opens << [t, :in_alias_undef, 2] - when 'undef' - opens << [t, :in_alias_undef, 1] - when 'elsif', 'else', 'when' - opens.pop - opens << [t, nil] - when 'for' - opens << [t, :in_for_while_until_condition] - when 'in' - if last_tok&.event == :on_kw && %w[case in].include?(last_tok.tok) && first_token_on_line - opens.pop - opens << [t, nil] - end - end - when :on_tlambda - opens << [t, :in_lambda_head] - when :on_lparen, :on_lbracket, :on_lbrace, :on_tlambeg, :on_embexpr_beg, :on_embdoc_beg - opens << [t, nil] - when :on_rparen, :on_rbracket, :on_rbrace, :on_embexpr_end, :on_embdoc_end - opens.pop - when :on_heredoc_beg - pending_heredocs << t - when :on_heredoc_end - opens.pop - when :on_backtick - opens << [t, nil] unless t.state == Ripper::EXPR_ARG - when :on_tstring_beg, :on_words_beg, :on_qwords_beg, :on_symbols_beg, :on_qsymbols_beg, :on_regexp_beg - opens << [t, nil] - when :on_tstring_end, :on_regexp_end, :on_label_end - opens.pop - when :on_symbeg - if t.tok == ':' - opens << [t, :in_unquoted_symbol] - else - opens << [t, nil] - end - end - end - if t.event == :on_nl || t.event == :on_semicolon - first_token_on_line = true - elsif t.event != :on_sp - first_token_on_line = false - end - if pending_heredocs.any? && t.tok.include?("\n") - pending_heredocs.reverse_each { |t| opens << [t, nil] } - pending_heredocs = [] - end - if opens.last && opens.last[1] == :in_alias_undef && !IGNORE_TOKENS.include?(t.event) && t.event != :on_heredoc_end - tok, state, arg = opens.pop - opens << [tok, state, arg - 1] if arg >= 1 - end - yield t, opens if block_given? - end - opens.map(&:first) + pending_heredocs.reverse - end - - def open_tokens(tokens) - # scan_opens without block will return a list of open tokens at last token position - scan_opens(tokens) - end - - # Calculates token information [line_tokens, prev_opens, next_opens, min_depth] for each line. - # Example code - # ["hello - # world"+( - # First line - # line_tokens: [[lbracket, '['], [tstring_beg, '"'], [tstring_content("hello\nworld"), "hello\n"]] - # prev_opens: [] - # next_tokens: [lbracket, tstring_beg] - # min_depth: 0 (minimum at beginning of line) - # Second line - # line_tokens: [[tstring_content("hello\nworld"), "world"], [tstring_end, '"'], [op, '+'], [lparen, '(']] - # prev_opens: [lbracket, tstring_beg] - # next_tokens: [lbracket, lparen] - # min_depth: 1 (minimum just after tstring_end) - def parse_by_line(tokens) - line_tokens = [] - prev_opens = [] - min_depth = 0 - output = [] - last_opens = scan_opens(tokens) do |t, opens| - depth = t == opens.last&.first ? opens.size - 1 : opens.size - min_depth = depth if depth < min_depth - if t.tok.include?("\n") - t.tok.each_line do |line| - line_tokens << [t, line] - next if line[-1] != "\n" - next_opens = opens.map(&:first) - output << [line_tokens, prev_opens, next_opens, min_depth] - prev_opens = next_opens - min_depth = prev_opens.size - line_tokens = [] - end - else - line_tokens << [t, t.tok] - end - end - output << [line_tokens, prev_opens, last_opens, min_depth] if line_tokens.any? - output - end - end - end -end diff --git a/lib/irb/notifier.rb b/lib/irb/notifier.rb deleted file mode 100644 index dc1b9ef14b..0000000000 --- a/lib/irb/notifier.rb +++ /dev/null @@ -1,230 +0,0 @@ -# frozen_string_literal: true -# -# notifier.rb - output methods used by irb -# by Keiju ISHITSUKA([email protected]) -# - -require_relative "output-method" - -module IRB - # An output formatter used internally by the lexer. - module Notifier - class ErrUndefinedNotifier < StandardError - def initialize(val) - super("undefined notifier level: #{val} is specified") - end - end - class ErrUnrecognizedLevel < StandardError - def initialize(val) - super("unrecognized notifier level: #{val} is specified") - end - end - - # 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, StdioOutputMethod 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 - - @notifiers = [D_NOMSG] - @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 - @level_notifier = value - when Integer - l = @notifiers[value] - raise ErrUndefinedNotifier, value unless l - @level_notifier = l - else - raise ErrUnrecognizedLevel, value unless l - end - end - - 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 # :nodoc: - end -end diff --git a/lib/irb/output-method.rb b/lib/irb/output-method.rb deleted file mode 100644 index 69942f47a2..0000000000 --- a/lib/irb/output-method.rb +++ /dev/null @@ -1,80 +0,0 @@ -# frozen_string_literal: true -# -# output-method.rb - output methods used by irb -# by Keiju ISHITSUKA([email protected]) -# - -module IRB - # 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 - # 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) - raise NotImplementedError - end - - # Prints the given +opts+, with a newline delimiter. - def printn(*opts) - print opts.join(" "), "\n" - end - - # 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) - end - print sprintf(format, *opts) - end - - # 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) - print "\n" - 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 - end -end diff --git a/lib/irb/pager.rb b/lib/irb/pager.rb deleted file mode 100644 index 16ff30cf89..0000000000 --- a/lib/irb/pager.rb +++ /dev/null @@ -1,213 +0,0 @@ -# frozen_string_literal: true - -require 'reline' - -module IRB - # The implementation of this class is borrowed from RDoc's lib/rdoc/ri/driver.rb. - # Please do NOT use this class directly outside of IRB. - class Pager - PAGE_COMMANDS = [ENV['RI_PAGER'], ENV['PAGER'], 'less', 'more'].compact.uniq - - class << self - def page_content(content, **options) - if content_exceeds_screen_height?(content) - page(**options) do |io| - io.puts content - end - else - $stdout.puts content - end - end - - def page(retain_content: false) - if should_page? && pager = setup_pager(retain_content: retain_content) - begin - pid = pager.pid - yield pager - ensure - pager.close - end - else - yield $stdout - end - # When user presses Ctrl-C, IRB would raise `IRB::Abort` - # But since Pager is implemented by running paging commands like `less` in another process with `IO.popen`, - # the `IRB::Abort` exception only interrupts IRB's execution but doesn't affect the pager - # So to properly terminate the pager with Ctrl-C, we need to catch `IRB::Abort` and kill the pager process - rescue IRB::Abort - begin - begin - Process.kill("TERM", pid) if pid - rescue Errno::EINVAL - # SIGTERM not supported (windows) - Process.kill("KILL", pid) - end - rescue Errno::ESRCH - # Pager process already terminated - end - nil - rescue Errno::EPIPE - end - - def should_page? - IRB.conf[:USE_PAGER] && STDIN.tty? && (ENV.key?("TERM") && ENV["TERM"] != "dumb") - end - - def page_with_preview(width, height, formatter_proc) - overflow_callback = ->(lines) do - modified_output = formatter_proc.call(lines.join, true) - content, = take_first_page(width, [height - 2, 0].max) {|o| o.write modified_output } - content = content.chomp - content = "#{content}\e[0m" if Color.colorable? - $stdout.puts content - $stdout.puts 'Preparing full inspection value...' - end - out = PageOverflowIO.new(width, height, overflow_callback, delay: 0.1) - yield out - content = formatter_proc.call(out.string, out.multipage?) - if out.multipage? - page(retain_content: true) do |io| - io.puts content - end - else - $stdout.puts content - end - end - - def take_first_page(width, height) - overflow_callback = proc do |lines| - return lines.join, true - end - out = Pager::PageOverflowIO.new(width, height, overflow_callback) - yield out - [out.string, false] - end - - private - - def content_exceeds_screen_height?(content) - screen_height, screen_width = begin - Reline.get_screen_size - rescue Errno::EINVAL - [24, 80] - end - - pageable_height = screen_height - 3 # leave some space for previous and the current prompt - - return true if content.lines.size > pageable_height - - _, overflow = take_first_page(screen_width, pageable_height) {|out| out.write content } - overflow - end - - def setup_pager(retain_content:) - require 'shellwords' - - PAGE_COMMANDS.each do |pager_cmd| - cmd = Shellwords.split(pager_cmd) - next if cmd.empty? - - if cmd.first == 'less' - cmd << '-R' unless cmd.include?('-R') - cmd << '-X' if retain_content && !cmd.include?('-X') - end - - begin - io = IO.popen(cmd, 'w') - rescue - next - end - - if $? && $?.pid == io.pid && $?.exited? # pager didn't work - next - end - - return io - end - - nil - end - end - - # Writable IO that has page overflow callback - class PageOverflowIO - attr_reader :string, :first_page_lines - - # Maximum size of a single cell in terminal - # Assumed worst case: "\e[1;3;4;9;38;2;255;128;128;48;2;128;128;255mA\e[0m" - # bold, italic, underline, crossed_out, RGB forgound, RGB background - MAX_CHAR_PER_CELL = 50 - - def initialize(width, height, overflow_callback, delay: nil) - @lines = [] - @first_page_lines = nil - @width = width - @height = height - @buffer = +'' - @overflow_callback = overflow_callback - @col = 0 - @string = +'' - @multipage = false - @delay_until = (Time.now + delay if delay) - end - - def puts(text = '') - text = text.to_s unless text.is_a?(String) - write(text) - write("\n") unless text.end_with?("\n") - end - - def write(text) - text = text.to_s unless text.is_a?(String) - @string << text - if @multipage - if @delay_until && Time.now > @delay_until - @overflow_callback.call(@first_page_lines) - @delay_until = nil - end - return - end - - overflow_size = (@width * (@height - @lines.size) + @width - @col) * MAX_CHAR_PER_CELL - if text.size >= overflow_size - text = text[0, overflow_size] - overflow = true - end - @buffer << text - @col += Reline::Unicode.calculate_width(text, true) - if text.include?("\n") || @col >= @width - @buffer.lines.each do |line| - wrapped_lines = Reline::Unicode.split_by_width(line.chomp, @width).first.compact - wrapped_lines.pop if wrapped_lines.last == '' - @lines.concat(wrapped_lines) - if line.end_with?("\n") - if @lines.empty? || @lines.last.end_with?("\n") - @lines << "\n" - else - @lines[-1] += "\n" - end - end - end - @buffer.clear - @buffer << @lines.pop unless @lines.last.end_with?("\n") - @col = Reline::Unicode.calculate_width(@buffer, true) - end - if overflow || @lines.size > @height || (@lines.size == @height && @col > 0) - @first_page_lines = @lines.take(@height) - if !@delay_until || Time.now > @delay_until - @overflow_callback.call(@first_page_lines) - @delay_until = nil - end - @multipage = true - end - end - - def multipage? - @multipage - end - - alias print write - alias << write - end - end -end diff --git a/lib/irb/ruby-lex.rb b/lib/irb/ruby-lex.rb deleted file mode 100644 index 3abb53b4ea..0000000000 --- a/lib/irb/ruby-lex.rb +++ /dev/null @@ -1,476 +0,0 @@ -# frozen_string_literal: true -# -# irb/ruby-lex.rb - ruby lexcal analyzer -# by Keiju ISHITSUKA([email protected]) -# - -require "ripper" -require "jruby" if RUBY_ENGINE == "jruby" -require_relative "nesting_parser" - -module IRB - # :stopdoc: - class RubyLex - ASSIGNMENT_NODE_TYPES = [ - # Local, instance, global, class, constant, instance, and index assignment: - # "foo = bar", - # "@foo = bar", - # "$foo = bar", - # "@@foo = bar", - # "::Foo = bar", - # "a::Foo = bar", - # "Foo = bar" - # "foo.bar = 1" - # "foo[1] = bar" - :assign, - - # Operation assignment: - # "foo += bar" - # "foo -= bar" - # "foo ||= bar" - # "foo &&= bar" - :opassign, - - # Multiple assignment: - # "foo, bar = 1, 2 - :massign, - ] - - ERROR_TOKENS = [ - :on_parse_error, - :compile_error, - :on_assign_error, - :on_alias_error, - :on_class_name_error, - :on_param_error - ] - - LTYPE_TOKENS = %i[ - on_heredoc_beg on_tstring_beg - on_regexp_beg on_symbeg on_backtick - on_symbols_beg on_qsymbols_beg - on_words_beg on_qwords_beg - ] - - class TerminateLineInput < StandardError - def initialize - super("Terminate Line Input") - end - end - - class << self - def compile_with_errors_suppressed(code, line_no: 1) - begin - result = yield code, line_no - rescue ArgumentError - # Ruby can issue an error for the code if there is an - # incomplete magic comment for encoding in it. Force an - # expression with a new line before the code in this - # case to prevent magic comment handling. To make sure - # line numbers in the lexed code remain the same, - # decrease the line number by one. - code = ";\n#{code}" - line_no -= 1 - result = yield code, line_no - end - result - end - - def generate_local_variables_assign_code(local_variables) - "#{local_variables.join('=')}=nil;" unless local_variables.empty? - end - - # Some part of the code is not included in Ripper's token. - # Example: DATA part, token after heredoc_beg when heredoc has unclosed embexpr. - # With interpolated tokens, tokens.map(&:tok).join will be equal to code. - def interpolate_ripper_ignored_tokens(code, tokens) - line_positions = [0] - code.lines.each do |line| - line_positions << line_positions.last + line.bytesize - end - prev_byte_pos = 0 - interpolated = [] - prev_line = 1 - tokens.each do |t| - line, col = t.pos - byte_pos = line_positions[line - 1] + col - if prev_byte_pos < byte_pos - tok = code.byteslice(prev_byte_pos...byte_pos) - pos = [prev_line, prev_byte_pos - line_positions[prev_line - 1]] - interpolated << Ripper::Lexer::Elem.new(pos, :on_ignored_by_ripper, tok, 0) - prev_line += tok.count("\n") - end - interpolated << t - prev_byte_pos = byte_pos + t.tok.bytesize - prev_line += t.tok.count("\n") - end - if prev_byte_pos < code.bytesize - tok = code.byteslice(prev_byte_pos..) - pos = [prev_line, prev_byte_pos - line_positions[prev_line - 1]] - interpolated << Ripper::Lexer::Elem.new(pos, :on_ignored_by_ripper, tok, 0) - end - interpolated - end - - def ripper_lex_without_warning(code, local_variables: []) - verbose, $VERBOSE = $VERBOSE, nil - lvars_code = generate_local_variables_assign_code(local_variables) - original_code = code - if lvars_code - code = "#{lvars_code}\n#{code}" - line_no = 0 - else - line_no = 1 - end - - compile_with_errors_suppressed(code, line_no: line_no) do |inner_code, line_no| - lexer = Ripper::Lexer.new(inner_code, '-', line_no) - tokens = [] - lexer.scan.each do |t| - next if t.pos.first == 0 - prev_tk = tokens.last - position_overlapped = prev_tk && t.pos[0] == prev_tk.pos[0] && t.pos[1] < prev_tk.pos[1] + prev_tk.tok.bytesize - if position_overlapped - tokens[-1] = t if ERROR_TOKENS.include?(prev_tk.event) && !ERROR_TOKENS.include?(t.event) - else - tokens << t - end - end - interpolate_ripper_ignored_tokens(original_code, tokens) - end - ensure - $VERBOSE = verbose - end - end - - def check_code_state(code, local_variables:) - tokens = self.class.ripper_lex_without_warning(code, local_variables: local_variables) - opens = NestingParser.open_tokens(tokens) - [tokens, opens, code_terminated?(code, tokens, opens, local_variables: local_variables)] - end - - def code_terminated?(code, tokens, opens, local_variables:) - case check_code_syntax(code, local_variables: local_variables) - when :unrecoverable_error - true - when :recoverable_error - false - when :other_error - opens.empty? && !should_continue?(tokens) - when :valid - !should_continue?(tokens) - end - end - - def assignment_expression?(code, local_variables:) - # Try to parse the code and check if the last of possibly multiple - # expressions is an assignment type. - - # If the expression is invalid, Ripper.sexp should return nil which will - # result in false being returned. Any valid expression should return an - # s-expression where the second element of the top level array is an - # array of parsed expressions. The first element of each expression is the - # expression's type. - verbose, $VERBOSE = $VERBOSE, nil - code = "#{RubyLex.generate_local_variables_assign_code(local_variables) || 'nil;'}\n#{code}" - # Get the last node_type of the line. drop(1) is to ignore the local_variables_assign_code part. - node_type = Ripper.sexp(code)&.dig(1)&.drop(1)&.dig(-1, 0) - ASSIGNMENT_NODE_TYPES.include?(node_type) - ensure - $VERBOSE = verbose - end - - def should_continue?(tokens) - # Look at the last token and check if IRB need to continue reading next line. - # Example code that should continue: `a\` `a +` `a.` - # Trailing spaces, newline, comments are skipped - return true if tokens.last&.event == :on_sp && tokens.last.tok == "\\\n" - - tokens.reverse_each do |token| - case token.event - when :on_sp, :on_nl, :on_ignored_nl, :on_comment, :on_embdoc_beg, :on_embdoc, :on_embdoc_end - # Skip - when :on_regexp_end, :on_heredoc_end, :on_semicolon - # State is EXPR_BEG but should not continue - return false - else - # Endless range should not continue - return false if token.event == :on_op && token.tok.match?(/\A\.\.\.?\z/) - - # EXPR_DOT and most of the EXPR_BEG should continue - return token.state.anybits?(Ripper::EXPR_BEG | Ripper::EXPR_DOT) - end - end - false - end - - def check_code_syntax(code, local_variables:) - lvars_code = RubyLex.generate_local_variables_assign_code(local_variables) - code = "#{lvars_code}\n#{code}" - - begin # check if parser error are available - verbose, $VERBOSE = $VERBOSE, nil - case RUBY_ENGINE - when 'ruby' - self.class.compile_with_errors_suppressed(code) do |inner_code, line_no| - RubyVM::InstructionSequence.compile(inner_code, nil, nil, line_no) - end - when 'jruby' - JRuby.compile_ir(code) - else - catch(:valid) do - eval("BEGIN { throw :valid, true }\n#{code}") - false - end - end - rescue EncodingError - # This is for a hash with invalid encoding symbol, {"\xAE": 1} - :unrecoverable_error - rescue SyntaxError => e - case e.message - when /unexpected keyword_end/ - # "syntax error, unexpected keyword_end" - # - # example: - # if ( - # end - # - # example: - # end - return :unrecoverable_error - when /unexpected '\.'/ - # "syntax error, unexpected '.'" - # - # example: - # . - return :unrecoverable_error - when /unexpected tREGEXP_BEG/ - # "syntax error, unexpected tREGEXP_BEG, expecting keyword_do or '{' or '('" - # - # example: - # method / f / - return :unrecoverable_error - when /unterminated (?:string|regexp) meets end of file/ - # "unterminated regexp meets end of file" - # - # example: - # / - # - # "unterminated string meets end of file" - # - # example: - # ' - return :recoverable_error - when /unexpected end-of-input/ - # "syntax error, unexpected end-of-input, expecting keyword_end" - # - # example: - # if true - # hoge - # if false - # fuga - # end - return :recoverable_error - else - return :other_error - end - ensure - $VERBOSE = verbose - end - :valid - end - - def calc_indent_level(opens) - indent_level = 0 - opens.each_with_index do |t, index| - case t.event - when :on_heredoc_beg - if opens[index + 1]&.event != :on_heredoc_beg - if t.tok.match?(/^<<[~-]/) - indent_level += 1 - else - indent_level = 0 - end - end - when :on_tstring_beg, :on_regexp_beg, :on_symbeg, :on_backtick - # No indent: "", //, :"", `` - # Indent: %(), %r(), %i(), %x() - indent_level += 1 if t.tok.start_with? '%' - when :on_embdoc_beg - indent_level = 0 - else - indent_level += 1 unless t.tok == 'alias' || t.tok == 'undef' - end - end - indent_level - end - - FREE_INDENT_TOKENS = %i[on_tstring_beg on_backtick on_regexp_beg on_symbeg] - - def free_indent_token?(token) - FREE_INDENT_TOKENS.include?(token&.event) - end - - # Calculates the difference of pasted code's indent and indent calculated from tokens - def indent_difference(lines, line_results, line_index) - loop do - _tokens, prev_opens, _next_opens, min_depth = line_results[line_index] - open_token = prev_opens.last - if !open_token || (open_token.event != :on_heredoc_beg && !free_indent_token?(open_token)) - # If the leading whitespace is an indent, return the difference - indent_level = calc_indent_level(prev_opens.take(min_depth)) - calculated_indent = 2 * indent_level - actual_indent = lines[line_index][/^ */].size - return actual_indent - calculated_indent - elsif open_token.event == :on_heredoc_beg && open_token.tok.match?(/^<<[^-~]/) - return 0 - end - # If the leading whitespace is not an indent but part of a multiline token - # Calculate base_indent of the multiline token's beginning line - line_index = open_token.pos[0] - 1 - end - end - - def process_indent_level(tokens, lines, line_index, is_newline) - line_results = NestingParser.parse_by_line(tokens) - result = line_results[line_index] - if result - _tokens, prev_opens, next_opens, min_depth = result - else - # When last line is empty - prev_opens = next_opens = line_results.last[2] - min_depth = next_opens.size - end - - # To correctly indent line like `end.map do`, we use shortest open tokens on each line for indent calculation. - # Shortest open tokens can be calculated by `opens.take(min_depth)` - indent = 2 * calc_indent_level(prev_opens.take(min_depth)) - - preserve_indent = lines[line_index - (is_newline ? 1 : 0)][/^ */].size - - prev_open_token = prev_opens.last - next_open_token = next_opens.last - - # Calculates base indent for pasted code on the line where prev_open_token is located - # irb(main):001:1* if a # base_indent is 2, indent calculated from tokens is 0 - # irb(main):002:1* if b # base_indent is 6, indent calculated from tokens is 2 - # irb(main):003:0> c # base_indent is 6, indent calculated from tokens is 4 - if prev_open_token - base_indent = [0, indent_difference(lines, line_results, prev_open_token.pos[0] - 1)].max - else - base_indent = 0 - end - - if free_indent_token?(prev_open_token) - if is_newline && prev_open_token.pos[0] == line_index - # First newline inside free-indent token - base_indent + indent - else - # Accept any number of indent inside free-indent token - preserve_indent - end - elsif prev_open_token&.event == :on_embdoc_beg || next_open_token&.event == :on_embdoc_beg - if prev_open_token&.event == next_open_token&.event - # Accept any number of indent inside embdoc content - preserve_indent - else - # =begin or =end - 0 - end - elsif prev_open_token&.event == :on_heredoc_beg - tok = prev_open_token.tok - if prev_opens.size <= next_opens.size - if is_newline && lines[line_index].empty? && line_results[line_index - 1][1].last != next_open_token - # First line in heredoc - tok.match?(/^<<[-~]/) ? base_indent + indent : indent - elsif tok.match?(/^<<~/) - # Accept extra indent spaces inside `<<~` heredoc - [base_indent + indent, preserve_indent].max - else - # Accept any number of indent inside other heredoc - preserve_indent - end - else - # Heredoc close - prev_line_indent_level = calc_indent_level(prev_opens) - tok.match?(/^<<[~-]/) ? base_indent + 2 * (prev_line_indent_level - 1) : 0 - end - else - base_indent + indent - end - end - - def ltype_from_open_tokens(opens) - start_token = opens.reverse_each.find do |tok| - LTYPE_TOKENS.include?(tok.event) - end - return nil unless start_token - - case start_token&.event - when :on_tstring_beg - case start_token&.tok - when ?" then ?" - when /^%.$/ then ?" - when /^%Q.$/ then ?" - when ?' then ?' - when /^%q.$/ then ?' - end - when :on_regexp_beg then ?/ - when :on_symbeg then ?: - when :on_backtick then ?` - when :on_qwords_beg then ?] - when :on_words_beg then ?] - when :on_qsymbols_beg then ?] - when :on_symbols_beg then ?] - when :on_heredoc_beg - start_token&.tok =~ /<<[-~]?(['"`])\w+\1/ - $1 || ?" - else - nil - end - end - - def check_termination_in_prev_line(code, local_variables:) - tokens = self.class.ripper_lex_without_warning(code, local_variables: local_variables) - past_first_newline = false - index = tokens.rindex do |t| - # traverse first token before last line - if past_first_newline - if t.tok.include?("\n") - true - end - elsif t.tok.include?("\n") - past_first_newline = true - false - else - false - end - end - - if index - first_token = nil - last_line_tokens = tokens[(index + 1)..(tokens.size - 1)] - last_line_tokens.each do |t| - unless [:on_sp, :on_ignored_sp, :on_comment].include?(t.event) - first_token = t - break - end - end - - if first_token && first_token.state != Ripper::EXPR_DOT - tokens_without_last_line = tokens[0..index] - code_without_last_line = tokens_without_last_line.map(&:tok).join - opens_without_last_line = NestingParser.open_tokens(tokens_without_last_line) - if code_terminated?(code_without_last_line, tokens_without_last_line, opens_without_last_line, local_variables: local_variables) - return last_line_tokens.map(&:tok).join - end - end - end - false - end - end - # :startdoc: -end - -RubyLex = IRB::RubyLex -Object.deprecate_constant(:RubyLex) diff --git a/lib/irb/ruby_logo.aa b/lib/irb/ruby_logo.aa deleted file mode 100644 index d0143a448b..0000000000 --- a/lib/irb/ruby_logo.aa +++ /dev/null @@ -1,118 +0,0 @@ -TYPE: ASCII_LARGE - - ,,,;;;;;;;;;;;;;;;;;;;;;;,, - ,,,;;;;;;;;;,, ,;;;' ''';;, - ,,;;;''' ';;;, ,,;;'' '';, - ,;;'' ;;;;;;;;,,,,,, ';; - ,;;'' ;;;;';;;'''';;;;;;;;;,,,;; - ,,;'' ;;;; ';;, ''''';;, - ,;;' ;;;' ';;, ;; - ,;;' ,;;; '';,, ;; - ,;;' ;;; ';;, ,;; - ;;' ;;;' '';,, ;;; - ,;' ;;;; ';;, ;;' - ,;;' ,;;;;' ,,,,,,,,,,,,;;;;; - ,;' ,;;;;;;;;;;;;;;;;;;;;'''''''';;; - ;;' ,;;;;;;;;;,, ;;;; - ;;' ,;;;'' ;;, ';;,, ,;;;; - ;;' ,;;;' ;; '';;, ,;';;; - ;;' ,;;;' ;;, '';;,, ,;',;;; - ,;;; ,;;;' ;; '';;,, ,;' ;;;' - ;;;; ,,;;;' ;;, ';;;' ;;; -,;;; ,;;;;' ;; ,;;; ;;; -;;;;; ,,;;;;;' ;;, ,;';; ;;; -;;;;;, ,,;;;;;;;' ;; ,;;' ;;; ;;; -;;;;;;;,,,,,,,;;;;;;;;;;;;;;,,, ;;, ,;' ;; ;;; -;;' ;;;;;;;;;;'''' ,;';; ''';;;;,,, ;; ,;; ;; ;;; -;; ;;;'' ;; ';; ''';;;;,,,, ;;, ,;;' ;;, ;; -;; ;;;;, ;;' ';; ''';;;;,,;;;;' ';; ;; -;;;;;;';, ,;; ;; '';;;;, ;;,;; -;;; ;; ;;, ;; ;; ,;;' ';;, ;;;;; -;; ;;; ;;, ;;' ;; ,,;'' ';;, ;;;;; -;; ;; ;; ;; ;; ,;;' '';, ;;;; -;;,;; ;; ;;' ;; ,;;'' ';,, ;;;' - ;;;; ';; ,;; ;;,,;;'' ';;, ;;; - ';;; ';; ;; ,;;;;;;;;;;;;;,,,,,,,,,,,, ';;;;; - ';, ';,;;' ,,,;;'' '''''''';;;;;;;;;;;;;;;;;;; - ';;,,, ;;;; ,,,,;;;;;;,,,,,;;;;;;;;;;;;;;;;;;;'''''''''''''' - ''';;;;;;;;;;;;;;''''''''''''''' -TYPE: ASCII - ,,,;;;;''''';;;'';, - ,,;'' ';;,;;; ', - ,,'' ;;'';'''';;;;;; - ,;' ;; ',, ; - ,;' ,;' ';, ; - ;' ,;; ',,,; - ,' ,;;,,,,,,,,,,,;;;; - ;' ;;';;;; ,;; - ;' ,;' ;; '',, ,;;; - ;; ,;' ; '';, ,; ;' -;; ,;;' ;; ;; ;; -;;, ,,;;' ; ;'; ;; -;';;,,,,;;;;;;;,,, ;; ,' ; ;; -; ;;''' ,;'; ''';,,, ; ,;' ;;;; -;;;;, ; '; ''';;;' ';;; -;'; ;, ;' '; ,;' ', ;;; -;;; ; ,; '; ,,' ',, ;; -;;; '; ;' ';,,'' ';,;; - '; ';,; ,,;''''''''';;;;;;,,;;; - ';,,;;,,;;;;;;;;;;'''''''''''''' -TYPE: UNICODE_LARGE - - ⣀⣤⣴⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣶⣤⣄⡀ - ⢀⣀⣤⣴⣾⣿⣿⣿⠿⣿⣿⣿⣿⣦⣀ ⢀⣤⣶⣿⠿⠛⠁⠈⠉⠙⠻⢿⣷⣦⡀ - ⢀⣠⣴⣾⡿⠿⠛⠉⠉ ⠈⠙⢿⣿⣷⣤⡀ ⣠⣴⣾⡿⠟⠉ ⠉⠻⣿⣦ - ⢀⣤⣶⣿⠟⠋⠁ ⢿⣿⣿⣿⣿⣿⣿⣧⣤⣤⣤⣀⣀⣀⡀ ⠘⢿⣷⡀ - ⢀⣠⣾⡿⠟⠉ ⢸⣿⣿⣿⠟⢿⣿⣯⡙⠛⠛⠛⠿⠿⠿⢿⣿⣿⣶⣶⣶⣦⣤⣬⣿⣧ - ⣠⣴⣿⠟⠋ ⢸⣿⣿⡿ ⠈⠻⣿⣶⣄ ⠉⠉⠉⠙⠛⢻⣿⡆ - ⣠⣾⡿⠛⠁ ⣼⣿⣿⠃ ⠈⠙⢿⣷⣤⡀ ⠈⣿⡇ - ⣠⣾⡿⠋ ⢠⣿⣿⡏ ⠙⠻⣿⣦⣀ ⣿⡇ - ⣠⣾⡿⠋ ⢀⣿⣿⣿ ⠈⠛⢿⣷⣄⡀ ⢠⣿⡇ - ⢀⣾⡿⠋ ⢀⣾⣿⣿⠇ ⠙⠻⣿⣦⣀ ⢸⣿⡇ - ⢀⣴⣿⠟⠁ ⢀⣾⣿⣿⡟ ⠈⠻⢿⣷⣄ ⣾⣿⠇ - ⢠⣾⡿⠃ ⣠⣿⣿⣿⣿⠃ ⣀⣀⣀⣀⣀⣀⣀⣀⣤⣤⣤⣤⣽⣿⣿⣿⣿ - ⣰⣿⠟ ⣴⣿⣿⣿⣿⣿⣶⣶⣿⣿⣿⣿⣿⠿⠿⠿⠿⠿⠿⠿⠿⠛⠛⠛⠛⠛⠛⠛⠛⣿⣿⣿ - ⣼⣿⠏ ⢠⣾⣿⣿⣿⡿⣿⣿⢿⣷⣦⣄ ⣼⣿⣿⣿ - ⣼⣿⠃ ⢀⣴⣿⣿⣿⠟⠋ ⢸⣿⡆⠈⠛⠿⣿⣦⣄⡀ ⣰⣿⣿⣿⡇ - ⢀⣾⣿⠃ ⢀⣴⣿⣿⣿⠟⠁ ⣿⣷ ⠈⠙⠻⣿⣶⣄⡀ ⣰⣿⠟⣿⣿⡇ - ⢀⣾⣿⠇ ⢀⣴⣿⣿⣿⠟⠁ ⢸⣿⡆ ⠙⠻⢿⣷⣤⣀ ⣰⣿⠏⢠⣿⣿⡇ - ⢠⣿⣿⡟ ⢀⣴⣿⣿⡿⠛⠁ ⣿⣷ ⠉⠻⢿⣷⣦⣀ ⣴⣿⠏ ⢸⣿⣿⠃ - ⣿⣿⣿⡇ ⣠⣴⣿⣿⡿⠋ ⢸⣿⡆ ⠈⠛⢿⣿⣿⠃ ⢸⣿⣿ -⢠⣿⣿⣿ ⢀⣴⣾⣿⣿⡿⠋ ⠈⣿⣧ ⢠⣾⣿⣿ ⢸⣿⣿ -⢸⣿⣿⣿⡇ ⣀⣴⣾⣿⣿⣿⡿⠋ ⢹⣿⡆ ⣴⣿⠟⢹⣿⡀ ⢸⣿⡿ -⢸⣿⡟⣿⣿⣄ ⣀⣤⣶⣿⣿⣿⣿⣿⡟⠉ ⠈⣿⣷ ⢠⣾⡿⠋ ⢸⣿⡇ ⣼⣿⡇ -⢸⣿⡇⢹⣿⣿⣷⣦⣤⣤⣤⣤⣤⣴⣶⣾⣿⣿⣿⣿⡿⠿⣿⣿⣿⣿⣷⣶⣤⣤⣀⡀ ⢹⣿⡆ ⢀⣴⣿⠟ ⣿⣧ ⣿⣿⡇ -⢸⣿⠃ ⢿⣿⣿⣿⣿⣿⣿⡿⠿⠿⠛⠛⠉⠉⠁ ⢰⣿⠟⣿⣷⡀⠉⠙⠛⠿⢿⣿⣶⣦⣤⣀⡀ ⠈⣿⣷ ⣠⣿⡿⠁ ⢿⣿ ⣿⣿⡇ -⢸⣿ ⢀⣾⣿⣿⠋⠉⠁ ⢀⣿⡿ ⠘⣿⣷⡀ ⠉⠙⠛⠿⠿⣿⣶⣦⣤⣄⣀ ⢹⣿⡄ ⣠⣾⡿⠋ ⢸⣿⡆ ⣿⣿ -⣸⣿⢀⣾⣿⣿⣿⣆ ⣸⣿⠃ ⠘⢿⣷⡀ ⠈⠉⠛⠻⠿⣿⣷⣶⣤⣌⣿⣷⣾⡿⠋ ⠘⣿⡇ ⣿⣿ -⣿⣿⣾⡿⣿⡿⠹⣿⡆ ⢠⣿⡏ ⠈⢿⣷⡀ ⠈⠉⠙⣻⣿⣿⣿⣀ ⣿⣷⢰⣿⣿ -⣿⣿⡿⢁⣿⡇ ⢻⣿⡄ ⣾⣿ ⠈⢿⣷⡀ ⢀⣤⣾⡿⠋⠈⠻⢿⣷⣄ ⢻⣿⢸⣿⡟ -⣿⣿⠁⢸⣿⡇ ⢻⣿⡄ ⢸⣿⠇ ⠈⢿⣷⡀ ⣀⣴⣿⠟⠋ ⠙⢿⣷⣤⡀ ⢸⣿⣿⣿⡇ -⣿⣿ ⢸⣿⠁ ⠈⢿⣷⡀ ⢀⣿⡟ ⠈⢿⣷⡀ ⢀⣤⣾⡿⠛⠁ ⠙⠻⣿⣦⡀ ⠈⣿⣿⣿⡇ -⢸⣿⡄⣿⣿ ⠈⣿⣷⡀ ⣼⣿⠃ ⠈⢿⣷⡀ ⢀⣠⣶⣿⠟⠋ ⠈⠻⣿⣦⣄ ⣿⣿⣿⠇ -⠈⣿⣷⣿⡿ ⠘⣿⣧ ⢠⣿⡏ ⠈⢿⣷⣄⣤⣶⣿⠟⠋ ⠈⠛⢿⣷⣄ ⢸⣿⣿ - ⠘⣿⣿⡇ ⠘⣿⣧ ⣾⣿ ⢀⣠⣼⣿⣿⣿⣿⣿⣷⣶⣶⣶⣶⣶⣶⣤⣤⣤⣤⣤⣤⣀⣀⣀⣀⣀⣀⡀ ⠙⢿⣷⣼⣿⣿ - ⠈⠻⣿⣦⡀ ⠹⣿⣆⢸⣿⠇ ⣀⣠⣴⣾⡿⠟⠋⠁ ⠉⠉⠉⠉⠉⠉⠛⠛⣛⣛⣛⣻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣿⣿⣿⡿ - ⠈⠻⢿⣷⣦⣄⣀⡀ ⢹⣿⣿⡟ ⢀⣀⣀⣤⣤⣶⣾⣿⣿⣿⣯⣥⣤⣤⣤⣤⣶⣶⣶⣶⣶⣶⣶⣾⣿⣿⣿⣿⠿⠿⠿⠿⠿⠿⠿⠟⠛⠛⠛⠛⠛⠛⠛⠉⠉⠉⠉⠉⠉ - ⠉⠙⠛⠿⠿⠿⣿⣿⣿⣿⠿⠿⠿⠿⠿⠿⠿⠛⠛⠛⠛⠛⠛⠛⠋⠉⠉⠉⠉⠉⠉⠉ -TYPE: UNICODE - ⣀⣤⣴⣾⣿⣿⣿⡛⠛⠛⠛⠛⣻⣿⠿⠛⠛⠶⣤⡀ - ⣀⣴⠾⠛⠉⠁ ⠙⣿⣶⣤⣶⣟⣉ ⠈⠻⣦ - ⣀⣴⠟⠋ ⢸⣿⠟⠻⣯⡙⠛⠛⠛⠶⠶⠶⢶⣽⣇ - ⣠⡾⠋⠁ ⣾⡿ ⠈⠛⢦⣄ ⣿ - ⣠⡾⠋ ⣰⣿⠃ ⠙⠷⣤⡀ ⣿ - ⢀⡾⠋ ⣰⣿⡏ ⠈⠻⣦⣄⢠⣿ - ⣰⠟⠁ ⣴⣿⣿⣁⣀⣠⣤⣤⣤⣤⣤⣤⣤⣴⠶⠿⣿⡏ - ⣼⠏ ⢀⣾⣿⠟⣿⠿⣯⣍⠁ ⣰⣿⡇ - ⢀⣼⠋ ⢀⣴⣿⠟⠁ ⢸⡇ ⠙⠻⢦⣄⡀ ⢠⡿⣿⡇ -⢀⣾⡏ ⢀⣴⣿⠟⠁ ⣿ ⠉⠻⢶⣄⡀⣰⡟ ⣿⠃ -⣾⣿⠁ ⣠⣶⡿⠋⠁ ⢹⡇ ⠈⣿⡏ ⢸⣿ -⣿⣿⡆ ⢀⣠⣴⣿⡿⠋ ⠈⣿ ⢀⡾⠋⣿ ⢸⣿ -⣿⠸⣿⣶⣤⣤⣤⣤⣶⣾⠿⠿⣿⣿⠶⣤⣤⣀⡀ ⢹⡇ ⣴⠟⠁ ⣿⡀⢸⣿ -⣿⢀⣿⣟⠛⠋⠉⠁ ⢰⡟⠹⣧ ⠈⠉⠛⠻⠶⢦⣤⣀⡀ ⠈⣿ ⣠⡾⠃ ⢸⡇⢸⡇ -⣿⣾⣿⢿⡄ ⣿⠁ ⠘⣧ ⠉⠙⠛⠷⣿⣿⡋ ⠸⣇⣸⡇ -⣿⠃⣿⠈⢿⡄ ⣸⠇ ⠘⣧ ⢀⣤⠾⠋⠈⠻⣦⡀ ⣿⣿⡇ -⣿⢸⡏ ⠈⣷⡀ ⢠⡿ ⠘⣧⡀ ⣠⡴⠟⠁ ⠈⠻⣦⣀ ⢿⣿⠁ -⢻⣾⡇ ⠘⣷ ⣼⠃ ⠘⣷⣠⣴⠟⠋ ⠙⢷⣄⢸⣿ - ⠻⣧⡀ ⠘⣧⣰⡏ ⢀⣠⣤⠶⠛⠉⠛⠛⠛⠛⠛⠛⠻⢶⣶⣶⣶⣶⣶⣤⣤⣽⣿⣿ - ⠈⠛⠷⢦⣤⣽⣿⣥⣤⣶⣶⡿⠿⠿⠶⠶⠶⠶⠾⠛⠛⠛⠛⠛⠛⠛⠋⠉⠉⠉⠉⠉⠉⠁ diff --git a/lib/irb/source_finder.rb b/lib/irb/source_finder.rb deleted file mode 100644 index 6e1e58069f..0000000000 --- a/lib/irb/source_finder.rb +++ /dev/null @@ -1,138 +0,0 @@ -# frozen_string_literal: true - -require_relative "ruby-lex" - -module IRB - class SourceFinder - class EvaluationError < StandardError; end - - class Source - attr_reader :file, :line - def initialize(file, line, ast_source = nil) - @file = file - @line = line - @ast_source = ast_source - end - - def file_exist? - File.exist?(@file) - end - - def binary_file? - # If the line is zero, it means that the target's source is probably in a binary file. - @line.zero? - end - - def file_content - @file_content ||= File.read(@file) - end - - def colorized_content - if !binary_file? && file_exist? - end_line = find_end - # To correctly colorize, we need to colorize full content and extract the relevant lines. - colored = IRB::Color.colorize_code(file_content) - colored.lines[@line - 1...end_line].join - elsif @ast_source - IRB::Color.colorize_code(@ast_source) - end - end - - private - - def find_end - lex = RubyLex.new - code = file_content - lines = code.lines[(@line - 1)..-1] - tokens = RubyLex.ripper_lex_without_warning(lines.join) - prev_tokens = [] - - # chunk with line number - tokens.chunk { |tok| tok.pos[0] }.each do |lnum, chunk| - code = lines[0..lnum].join - prev_tokens.concat chunk - continue = lex.should_continue?(prev_tokens) - syntax = lex.check_code_syntax(code, local_variables: []) - if !continue && syntax == :valid - return @line + lnum - end - end - @line - end - end - - private_constant :Source - - def initialize(irb_context) - @irb_context = irb_context - end - - def find_source(signature, super_level = 0) - case signature - when /\A(::)?[A-Z]\w*(::[A-Z]\w*)*\z/ # ConstName, ::ConstName, ConstPath::ConstName - eval_receiver_or_owner(signature) # trigger autoload - *parts, name = signature.split('::', -1) - base = - if parts.empty? # ConstName - find_const_owner(name) - elsif parts == [''] # ::ConstName - Object - else # ConstPath::ConstName - eval_receiver_or_owner(parts.join('::')) - end - file, line = base.const_source_location(name) - when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method - owner = eval_receiver_or_owner(Regexp.last_match[:owner]) - method = Regexp.last_match[:method] - return unless owner.respond_to?(:instance_method) - method = method_target(owner, super_level, method, "owner") - file, line = method&.source_location - when /\A((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\z/ # method, receiver.method, receiver::method - receiver = eval_receiver_or_owner(Regexp.last_match[:receiver] || 'self') - method = Regexp.last_match[:method] - return unless receiver.respond_to?(method, true) - method = method_target(receiver, super_level, method, "receiver") - file, line = method&.source_location - end - return unless file && line - - if File.exist?(file) - Source.new(file, line) - elsif method - # Method defined with eval, probably in IRB session - source = RubyVM::InstructionSequence.of(method)&.script_lines&.join rescue nil - Source.new(file, line, source) - end - rescue EvaluationError - nil - end - - private - - def method_target(owner_receiver, super_level, method, type) - case type - when "owner" - target_method = owner_receiver.instance_method(method) - when "receiver" - target_method = owner_receiver.method(method) - end - super_level.times do |s| - target_method = target_method.super_method if target_method - end - target_method - rescue NameError - nil - end - - def eval_receiver_or_owner(code) - @irb_context.workspace.binding.eval(code) - rescue Exception - raise EvaluationError - end - - def find_const_owner(name) - module_nesting = @irb_context.workspace.binding.eval('::Module.nesting') - module_nesting.find { |mod| mod.const_defined?(name, false) } || module_nesting.find { |mod| mod.const_defined?(name) } || Object - end - end -end diff --git a/lib/irb/statement.rb b/lib/irb/statement.rb deleted file mode 100644 index 6a959995d1..0000000000 --- a/lib/irb/statement.rb +++ /dev/null @@ -1,101 +0,0 @@ -# frozen_string_literal: true - -module IRB - class Statement - attr_reader :code - - def is_assignment? - raise NotImplementedError - end - - def suppresses_echo? - raise NotImplementedError - end - - def should_be_handled_by_debugger? - raise NotImplementedError - end - - class EmptyInput < Statement - def is_assignment? - false - end - - def suppresses_echo? - true - end - - # Debugger takes empty input to repeat the last command - def should_be_handled_by_debugger? - true - end - - def code - "" - end - end - - class Expression < Statement - def initialize(code, is_assignment) - @code = code - @is_assignment = is_assignment - end - - def suppresses_echo? - @code.match?(/;\s*\z/) - end - - def should_be_handled_by_debugger? - true - end - - def is_assignment? - @is_assignment - end - end - - class IncorrectAlias < Statement - attr_reader :message - - def initialize(message) - @code = "" - @message = message - end - - def should_be_handled_by_debugger? - false - end - - def is_assignment? - false - end - - def suppresses_echo? - true - end - end - - class Command < Statement - attr_reader :command_class, :arg - - def initialize(original_code, command_class, arg) - @code = original_code - @command_class = command_class - @arg = arg - end - - def is_assignment? - false - end - - def suppresses_echo? - true - end - - def should_be_handled_by_debugger? - require_relative 'command/debug' - IRB::Command::DebugCommand > @command_class - end - end - end -end diff --git a/lib/irb/version.rb b/lib/irb/version.rb deleted file mode 100644 index b195956fe0..0000000000 --- a/lib/irb/version.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true -# -# irb/version.rb - irb version definition file -# by Keiju ISHITSUKA([email protected]) -# - -module IRB # :nodoc: - VERSION = "1.15.1" - @RELEASE_VERSION = VERSION - @LAST_UPDATE_DATE = "2025-01-22" -end diff --git a/lib/irb/workspace.rb b/lib/irb/workspace.rb deleted file mode 100644 index ced9d78663..0000000000 --- a/lib/irb/workspace.rb +++ /dev/null @@ -1,171 +0,0 @@ -# frozen_string_literal: true -# -# irb/workspace-binding.rb - -# by Keiju ISHITSUKA([email protected]) -# - -require_relative "helper_method" - -IRB::TOPLEVEL_BINDING = binding -module IRB # :nodoc: - class WorkSpace - # Creates a new workspace. - # - # set self to main if specified, otherwise - # inherit main from TOPLEVEL_BINDING. - def initialize(*main) - if Binding === main[0] - @binding = main.shift - elsif IRB.conf[:SINGLE_IRB] - @binding = TOPLEVEL_BINDING - else - case IRB.conf[:CONTEXT_MODE] - when 0 # binding in proc on TOPLEVEL_BINDING - @binding = eval("proc{binding}.call", - TOPLEVEL_BINDING, - __FILE__, - __LINE__) - when 1 # binding in loaded file - require "tempfile" - f = Tempfile.open("irb-binding") - f.print <<EOF - $binding = binding -EOF - f.close - load f.path - @binding = $binding - - when 2 # binding in loaded file(thread use) - unless defined? BINDING_QUEUE - IRB.const_set(:BINDING_QUEUE, Thread::SizedQueue.new(1)) - Thread.abort_on_exception = true - Thread.start do - eval "require \"irb/ws-for-case-2\"", TOPLEVEL_BINDING, __FILE__, __LINE__ - end - Thread.pass - end - @binding = BINDING_QUEUE.pop - - when 3 # binding in function on TOPLEVEL_BINDING - @binding = eval("self.class.remove_method(:irb_binding) if defined?(irb_binding); private; def irb_binding; binding; end; irb_binding", - TOPLEVEL_BINDING, - __FILE__, - __LINE__ - 3) - when 4 # binding is a copy of TOPLEVEL_BINDING (default) - # Note that this will typically be IRB::TOPLEVEL_BINDING - # This is to avoid RubyGems' local variables (see issue #17623) - @binding = TOPLEVEL_BINDING.dup - end - end - - if main.empty? - @main = eval("self", @binding) - else - @main = main[0] - end - IRB.conf[:__MAIN__] = @main - - unless main.empty? - case @main - when Module - @binding = eval("::IRB.conf[:__MAIN__].module_eval('::Kernel.binding', __FILE__, __LINE__)", @binding, __FILE__, __LINE__) - else - begin - @binding = eval("::IRB.conf[:__MAIN__].instance_eval('::Kernel.binding', __FILE__, __LINE__)", @binding, __FILE__, __LINE__) - rescue TypeError - fail CantChangeBinding, @main.inspect - end - end - end - - @binding.local_variable_set(:_, nil) - 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 - - def load_helper_methods_to_main - # Do not load helper methods to frozen objects and BasicObject - return unless Object === @main && [email protected]? - - ancestors = class<<main;ancestors;end - main.extend ExtendCommandBundle if !ancestors.include?(ExtendCommandBundle) - main.extend HelpersContainer if !ancestors.include?(HelpersContainer) - end - - # Evaluate the given +statements+ within the context of this workspace. - def evaluate(statements, file = __FILE__, line = __LINE__) - eval(statements, @binding, file, line) - end - - def local_variable_set(name, value) - @binding.local_variable_set(name, value) - end - - def local_variable_get(name) - @binding.local_variable_get(name) - end - - # error message manipulator - # WARN: Rails patches this method to filter its own backtrace. Be cautious when changing it. - # See: https://github.com/rails/rails/blob/main/railties/lib/rails/commands/console/console_command.rb#L8:~:text=def,filter_backtrace - def filter_backtrace(bt) - return nil if bt =~ /\/irb\/.*\.rb/ - return nil if bt =~ /\/irb\.rb/ - return nil if bt =~ /tool\/lib\/.*\.rb|runner\.rb/ # for tests in Ruby repository - case IRB.conf[:CONTEXT_MODE] - when 1 - return nil if bt =~ %r!/tmp/irb-binding! - when 3 - bt = bt.sub(/:\s*in `irb_binding'/, '') - end - bt - end - - def code_around_binding - file, pos = @binding.source_location - - if defined?(::SCRIPT_LINES__[file]) && lines = ::SCRIPT_LINES__[file] - code = ::SCRIPT_LINES__[file].join('') - else - begin - code = File.read(file) - rescue SystemCallError - return - end - end - - lines = Color.colorize_code(code).lines - pos -= 1 - - start_pos = [pos - 5, 0].max - end_pos = [pos + 5, lines.size - 1].min - - line_number_fmt = Color.colorize("%#{end_pos.to_s.length}d", [:BLUE, :BOLD]) - fmt = " %2s #{line_number_fmt}: %s" - - body = (start_pos..end_pos).map do |current_pos| - sprintf(fmt, pos == current_pos ? '=>' : '', current_pos + 1, lines[current_pos]) - end.join("") - - "\nFrom: #{file} @ line #{pos + 1} :\n\n#{body}#{Color.clear}\n" - end - end - - module HelpersContainer - class << self - def install_helper_methods - HelperMethod.helper_methods.each do |name, helper_method_class| - define_method name do |*args, **opts, &block| - helper_method_class.instance.execute(*args, **opts, &block) - end unless method_defined?(name) - end - end - end - - install_helper_methods - end -end diff --git a/lib/irb/ws-for-case-2.rb b/lib/irb/ws-for-case-2.rb deleted file mode 100644 index 03f42d73d9..0000000000 --- a/lib/irb/ws-for-case-2.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true -# -# irb/ws-for-case-2.rb - -# by Keiju ISHITSUKA([email protected]) -# - -while true - IRB::BINDING_QUEUE.push _ = binding -end diff --git a/lib/irb/xmp.rb b/lib/irb/xmp.rb deleted file mode 100644 index b1bc53283e..0000000000 --- a/lib/irb/xmp.rb +++ /dev/null @@ -1,164 +0,0 @@ -# frozen_string_literal: true -# -# xmp.rb - irb version of gotoken xmp -# by Keiju ISHITSUKA(Nippon Rational Inc.) -# - -require_relative "../irb" -require_relative "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 - - # 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 {Custom Prompts}[rdoc-ref:IRB@Custom+Prompts] for more information. - def initialize(bind = nil) - IRB.init_config(nil) - - IRB.conf[:PROMPT_MODE] = :XMP - - bind = IRB::Frame.top(1) unless bind - ws = IRB::WorkSpace.new(bind) - @io = StringInputMethod.new - @irb = IRB::Irb.new(ws, @io) - @irb.context.ignore_sigint = false - - 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 - - if @irb.context.ignore_sigint - begin - trap_proc_b = trap("SIGINT"){@irb.signal_handle} - catch(:IRB_EXIT) do - @irb.eval_input - end - ensure - trap("SIGINT", trap_proc_b) - end - else - catch(:IRB_EXIT) do - @irb.eval_input - end - 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 - l.concat "\n" - print @prompt, l - break - end - 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) - if enc.nil? - raise Encoding::CompatibilityError, "Encoding in which the passed expression is encoded is not compatible to the preceding's one" - else - @encoding = enc - end - else - @encoding = exps.encoding - end - @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) - xmp.puts exps - xmp -end |