From f5801e2bf404cbb0f673a6ebb040b0ba6cd01b77 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Fri, 16 Feb 2024 16:47:32 +0000 Subject: [ruby/irb] Standardize command related names (https://github.com/ruby/irb/pull/873) * Replace ExtendCommand with Command and standardize command related names 1. Rename lib/irb/extend-command.rb to lib/irb/command.rb 2. Rename lib/irb/cmd/*.rb to lib/irb/command/*.rb 3. Rename test/irb/test_cmd.rb to test/irb/test_command.rb 4. Rename ExtendCommand to Command * Alias ExtendCommand to Command and deprecate it * Rename Command::Nop to Command::Base * Not deprecate old constants just yet * Add lib/irb/cmd/nop.rb back https://github.com/ruby/irb/commit/462c1284af --- lib/irb.rb | 2 +- lib/irb/cmd/backtrace.rb | 21 --- lib/irb/cmd/break.rb | 21 --- lib/irb/cmd/catch.rb | 21 --- lib/irb/cmd/chws.rb | 36 ---- lib/irb/cmd/continue.rb | 17 -- lib/irb/cmd/debug.rb | 80 --------- lib/irb/cmd/delete.rb | 17 -- lib/irb/cmd/edit.rb | 54 ------ lib/irb/cmd/exit.rb | 22 --- lib/irb/cmd/finish.rb | 17 -- lib/irb/cmd/force_exit.rb | 22 --- lib/irb/cmd/help.rb | 12 -- lib/irb/cmd/history.rb | 47 ------ lib/irb/cmd/info.rb | 21 --- lib/irb/cmd/irb_info.rb | 34 ---- lib/irb/cmd/load.rb | 76 --------- lib/irb/cmd/ls.rb | 139 ---------------- lib/irb/cmd/measure.rb | 45 ----- lib/irb/cmd/next.rb | 17 -- lib/irb/cmd/nop.rb | 55 +------ lib/irb/cmd/pushws.rb | 45 ----- lib/irb/cmd/show_cmds.rb | 59 ------- lib/irb/cmd/show_doc.rb | 48 ------ lib/irb/cmd/show_source.rb | 68 -------- lib/irb/cmd/step.rb | 17 -- lib/irb/cmd/subirb.rb | 109 ------------- lib/irb/cmd/whereami.rb | 25 --- lib/irb/command.rb | 363 +++++++++++++++++++++++++++++++++++++++++ lib/irb/command/backtrace.rb | 21 +++ lib/irb/command/base.rb | 55 +++++++ lib/irb/command/break.rb | 21 +++ lib/irb/command/catch.rb | 21 +++ lib/irb/command/chws.rb | 34 ++++ lib/irb/command/continue.rb | 17 ++ lib/irb/command/debug.rb | 79 +++++++++ lib/irb/command/delete.rb | 17 ++ lib/irb/command/edit.rb | 54 ++++++ lib/irb/command/exit.rb | 20 +++ lib/irb/command/finish.rb | 17 ++ lib/irb/command/force_exit.rb | 20 +++ lib/irb/command/help.rb | 12 ++ lib/irb/command/history.rb | 47 ++++++ lib/irb/command/info.rb | 21 +++ lib/irb/command/irb_info.rb | 32 ++++ lib/irb/command/load.rb | 74 +++++++++ lib/irb/command/ls.rb | 139 ++++++++++++++++ lib/irb/command/measure.rb | 43 +++++ lib/irb/command/next.rb | 17 ++ lib/irb/command/pushws.rb | 44 +++++ lib/irb/command/show_cmds.rb | 59 +++++++ lib/irb/command/show_doc.rb | 46 ++++++ lib/irb/command/show_source.rb | 67 ++++++++ lib/irb/command/step.rb | 17 ++ lib/irb/command/subirb.rb | 107 ++++++++++++ lib/irb/command/whereami.rb | 23 +++ lib/irb/ext/use-loader.rb | 4 +- lib/irb/extend-command.rb | 358 ---------------------------------------- lib/irb/statement.rb | 6 +- 59 files changed, 1496 insertions(+), 1506 deletions(-) delete mode 100644 lib/irb/cmd/backtrace.rb delete mode 100644 lib/irb/cmd/break.rb delete mode 100644 lib/irb/cmd/catch.rb delete mode 100644 lib/irb/cmd/chws.rb delete mode 100644 lib/irb/cmd/continue.rb delete mode 100644 lib/irb/cmd/debug.rb delete mode 100644 lib/irb/cmd/delete.rb delete mode 100644 lib/irb/cmd/edit.rb delete mode 100644 lib/irb/cmd/exit.rb delete mode 100644 lib/irb/cmd/finish.rb delete mode 100644 lib/irb/cmd/force_exit.rb delete mode 100644 lib/irb/cmd/help.rb delete mode 100644 lib/irb/cmd/history.rb delete mode 100644 lib/irb/cmd/info.rb delete mode 100644 lib/irb/cmd/irb_info.rb delete mode 100644 lib/irb/cmd/load.rb delete mode 100644 lib/irb/cmd/ls.rb delete mode 100644 lib/irb/cmd/measure.rb delete mode 100644 lib/irb/cmd/next.rb delete mode 100644 lib/irb/cmd/pushws.rb delete mode 100644 lib/irb/cmd/show_cmds.rb delete mode 100644 lib/irb/cmd/show_doc.rb delete mode 100644 lib/irb/cmd/show_source.rb delete mode 100644 lib/irb/cmd/step.rb delete mode 100644 lib/irb/cmd/subirb.rb delete mode 100644 lib/irb/cmd/whereami.rb create mode 100644 lib/irb/command.rb create mode 100644 lib/irb/command/backtrace.rb create mode 100644 lib/irb/command/base.rb create mode 100644 lib/irb/command/break.rb create mode 100644 lib/irb/command/catch.rb create mode 100644 lib/irb/command/chws.rb create mode 100644 lib/irb/command/continue.rb create mode 100644 lib/irb/command/debug.rb create mode 100644 lib/irb/command/delete.rb create mode 100644 lib/irb/command/edit.rb create mode 100644 lib/irb/command/exit.rb create mode 100644 lib/irb/command/finish.rb create mode 100644 lib/irb/command/force_exit.rb create mode 100644 lib/irb/command/help.rb create mode 100644 lib/irb/command/history.rb create mode 100644 lib/irb/command/info.rb create mode 100644 lib/irb/command/irb_info.rb create mode 100644 lib/irb/command/load.rb create mode 100644 lib/irb/command/ls.rb create mode 100644 lib/irb/command/measure.rb create mode 100644 lib/irb/command/next.rb create mode 100644 lib/irb/command/pushws.rb create mode 100644 lib/irb/command/show_cmds.rb create mode 100644 lib/irb/command/show_doc.rb create mode 100644 lib/irb/command/show_source.rb create mode 100644 lib/irb/command/step.rb create mode 100644 lib/irb/command/subirb.rb create mode 100644 lib/irb/command/whereami.rb delete mode 100644 lib/irb/extend-command.rb (limited to 'lib') diff --git a/lib/irb.rb b/lib/irb.rb index fd06626e9b..73d96947ea 100644 --- a/lib/irb.rb +++ b/lib/irb.rb @@ -9,7 +9,7 @@ require "reline" require_relative "irb/init" require_relative "irb/context" -require_relative "irb/extend-command" +require_relative "irb/command" require_relative "irb/ruby-lex" require_relative "irb/statement" diff --git a/lib/irb/cmd/backtrace.rb b/lib/irb/cmd/backtrace.rb deleted file mode 100644 index f632894618..0000000000 --- a/lib/irb/cmd/backtrace.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -require_relative "debug" - -module IRB - # :stopdoc: - - module ExtendCommand - class Backtrace < DebugCommand - def self.transform_args(args) - args&.dump - end - - def execute(*args) - super(pre_cmds: ["backtrace", *args].join(" ")) - end - end - end - - # :startdoc: -end diff --git a/lib/irb/cmd/break.rb b/lib/irb/cmd/break.rb deleted file mode 100644 index df259a90ca..0000000000 --- a/lib/irb/cmd/break.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -require_relative "debug" - -module IRB - # :stopdoc: - - module ExtendCommand - class Break < DebugCommand - def self.transform_args(args) - args&.dump - end - - def execute(args = nil) - super(pre_cmds: "break #{args}") - end - end - end - - # :startdoc: -end diff --git a/lib/irb/cmd/catch.rb b/lib/irb/cmd/catch.rb deleted file mode 100644 index 40b62c7533..0000000000 --- a/lib/irb/cmd/catch.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -require_relative "debug" - -module IRB - # :stopdoc: - - module ExtendCommand - class Catch < DebugCommand - def self.transform_args(args) - args&.dump - end - - def execute(*args) - super(pre_cmds: ["catch", *args].join(" ")) - end - end - end - - # :startdoc: -end diff --git a/lib/irb/cmd/chws.rb b/lib/irb/cmd/chws.rb deleted file mode 100644 index 31045f9bbb..0000000000 --- a/lib/irb/cmd/chws.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: false -# -# change-ws.rb - -# by Keiju ISHITSUKA(keiju@ruby-lang.org) -# - -require_relative "nop" -require_relative "../ext/change-ws" - -module IRB - # :stopdoc: - - module ExtendCommand - - class CurrentWorkingWorkspace < Nop - category "Workspace" - description "Show the current workspace." - - def execute(*obj) - irb_context.main - end - end - - class ChangeWorkspace < Nop - category "Workspace" - description "Change the current workspace to an object." - - def execute(*obj) - irb_context.change_workspace(*obj) - irb_context.main - end - end - end - - # :startdoc: -end diff --git a/lib/irb/cmd/continue.rb b/lib/irb/cmd/continue.rb deleted file mode 100644 index 9136177eef..0000000000 --- a/lib/irb/cmd/continue.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require_relative "debug" - -module IRB - # :stopdoc: - - module ExtendCommand - class Continue < DebugCommand - def execute(*args) - super(do_cmds: ["continue", *args].join(" ")) - end - end - end - - # :startdoc: -end diff --git a/lib/irb/cmd/debug.rb b/lib/irb/cmd/debug.rb deleted file mode 100644 index e236084ca4..0000000000 --- a/lib/irb/cmd/debug.rb +++ /dev/null @@ -1,80 +0,0 @@ -require_relative "nop" -require_relative "../debug" - -module IRB - # :stopdoc: - - module ExtendCommand - class Debug < Nop - category "Debugging" - description "Start the debugger of debug.gem." - - BINDING_IRB_FRAME_REGEXPS = [ - '', - binding.method(:irb).source_location.first, - ].map { |file| /\A#{Regexp.escape(file)}:\d+:in `irb'\z/ } - - def execute(pre_cmds: nil, do_cmds: nil) - 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 binding_irb? - 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 - - private - - def binding_irb? - caller.any? do |frame| - BINDING_IRB_FRAME_REGEXPS.any? do |regexp| - frame.match?(regexp) - end - end - end - end - - class DebugCommand < Debug - def self.category - "Debugging" - end - - def self.description - command_name = self.name.split("::").last.downcase - "Start the debugger of debug.gem and run its `#{command_name}` command." - end - end - end -end diff --git a/lib/irb/cmd/delete.rb b/lib/irb/cmd/delete.rb deleted file mode 100644 index aeb26d2572..0000000000 --- a/lib/irb/cmd/delete.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require_relative "debug" - -module IRB - # :stopdoc: - - module ExtendCommand - class Delete < DebugCommand - def execute(*args) - super(pre_cmds: ["delete", *args].join(" ")) - end - end - end - - # :startdoc: -end diff --git a/lib/irb/cmd/edit.rb b/lib/irb/cmd/edit.rb deleted file mode 100644 index dae65f3c39..0000000000 --- a/lib/irb/cmd/edit.rb +++ /dev/null @@ -1,54 +0,0 @@ -require 'shellwords' -require_relative "nop" -require_relative "../source_finder" - -module IRB - # :stopdoc: - - module ExtendCommand - class Edit < Nop - category "Misc" - description 'Open a file with the editor command defined with `ENV["VISUAL"]` or `ENV["EDITOR"]`.' - - class << self - def transform_args(args) - # Return a string literal as is for backward compatibility - if args.nil? || args.empty? || string_literal?(args) - args - else # Otherwise, consider the input as a String for convenience - args.strip.dump - end - end - end - - def execute(*args) - path = args.first - - 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/cmd/exit.rb b/lib/irb/cmd/exit.rb deleted file mode 100644 index 415e555335..0000000000 --- a/lib/irb/cmd/exit.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -require_relative "nop" - -module IRB - # :stopdoc: - - module ExtendCommand - class Exit < Nop - category "IRB" - description "Exit the current irb session." - - def execute(*) - IRB.irb_exit - rescue UncaughtThrowError - Kernel.exit - end - end - end - - # :startdoc: -end diff --git a/lib/irb/cmd/finish.rb b/lib/irb/cmd/finish.rb deleted file mode 100644 index 29f100feb5..0000000000 --- a/lib/irb/cmd/finish.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require_relative "debug" - -module IRB - # :stopdoc: - - module ExtendCommand - class Finish < DebugCommand - def execute(*args) - super(do_cmds: ["finish", *args].join(" ")) - end - end - end - - # :startdoc: -end diff --git a/lib/irb/cmd/force_exit.rb b/lib/irb/cmd/force_exit.rb deleted file mode 100644 index 7e9b308de0..0000000000 --- a/lib/irb/cmd/force_exit.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -require_relative "nop" - -module IRB - # :stopdoc: - - module ExtendCommand - class ForceExit < Nop - category "IRB" - description "Exit the current process." - - def execute(*) - throw :IRB_EXIT, true - rescue UncaughtThrowError - Kernel.exit! - end - end - end - - # :startdoc: -end diff --git a/lib/irb/cmd/help.rb b/lib/irb/cmd/help.rb deleted file mode 100644 index 7f894688cc..0000000000 --- a/lib/irb/cmd/help.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -require_relative "show_cmds" - -module IRB - module ExtendCommand - class Help < ShowCmds - category "IRB" - description "List all available commands and their description." - end - end -end diff --git a/lib/irb/cmd/history.rb b/lib/irb/cmd/history.rb deleted file mode 100644 index 5b712fa44d..0000000000 --- a/lib/irb/cmd/history.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: true - -require "stringio" -require_relative "nop" -require_relative "../pager" - -module IRB - # :stopdoc: - - module ExtendCommand - class History < Nop - category "IRB" - description "Shows the input history. `-g [query]` or `-G [query]` allows you to filter the output." - - def self.transform_args(args) - match = args&.match(/(-g|-G)\s+(?.+)\s*\n\z/) - return unless match - - "grep: #{Regexp.new(match[:grep]).inspect}" - end - - def execute(grep: nil) - 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/cmd/info.rb b/lib/irb/cmd/info.rb deleted file mode 100644 index 2c0a32b34f..0000000000 --- a/lib/irb/cmd/info.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -require_relative "debug" - -module IRB - # :stopdoc: - - module ExtendCommand - class Info < DebugCommand - def self.transform_args(args) - args&.dump - end - - def execute(*args) - super(pre_cmds: ["info", *args].join(" ")) - end - end - end - - # :startdoc: -end diff --git a/lib/irb/cmd/irb_info.rb b/lib/irb/cmd/irb_info.rb deleted file mode 100644 index 5b905a09bd..0000000000 --- a/lib/irb/cmd/irb_info.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: false - -require_relative "nop" - -module IRB - # :stopdoc: - - module ExtendCommand - class IrbInfo < Nop - category "IRB" - description "Show information about IRB." - - def execute - 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" - str += ".irbrc path: #{IRB.rc_file}\n" if File.exist?(IRB.rc_file) - 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/cmd/load.rb b/lib/irb/cmd/load.rb deleted file mode 100644 index a3e797a7e0..0000000000 --- a/lib/irb/cmd/load.rb +++ /dev/null @@ -1,76 +0,0 @@ -# frozen_string_literal: false -# -# load.rb - -# by Keiju ISHITSUKA(keiju@ruby-lang.org) -# - -require_relative "nop" -require_relative "../ext/loader" - -module IRB - # :stopdoc: - - module ExtendCommand - class LoaderCommand < Nop - 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(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(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(file_name = nil) - raise_cmd_argument_error unless file_name - - source_file(file_name) - end - end - end - # :startdoc: -end diff --git a/lib/irb/cmd/ls.rb b/lib/irb/cmd/ls.rb deleted file mode 100644 index 791b1c1b21..0000000000 --- a/lib/irb/cmd/ls.rb +++ /dev/null @@ -1,139 +0,0 @@ -# frozen_string_literal: true - -require "reline" -require "stringio" -require_relative "nop" -require_relative "../pager" -require_relative "../color" - -module IRB - # :stopdoc: - - module ExtendCommand - class Ls < Nop - category "Context" - description "Show methods, constants, and variables. `-g [query]` or `-G [query]` allows you to filter out the output." - - def self.transform_args(args) - if match = args&.match(/\A(?.+\s|)(-g|-G)\s+(?[^\s]+)\s*\n\z/) - args = match[:args] - "#{args}#{',' unless args.chomp.empty?} grep: /#{match[:grep]}/" - else - args - end - end - - def execute(*arg, grep: nil) - o = Output.new(grep: grep) - - obj = arg.empty? ? irb_context.workspace.main : arg.first - locals = arg.empty? ? irb_context.workspace.binding.local_variables : [] - 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) - o.print_result - 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 - - 80 - end - end - private_constant :Output - end - end - - # :startdoc: -end diff --git a/lib/irb/cmd/measure.rb b/lib/irb/cmd/measure.rb deleted file mode 100644 index 4e1125a0a6..0000000000 --- a/lib/irb/cmd/measure.rb +++ /dev/null @@ -1,45 +0,0 @@ -require_relative "nop" - -module IRB - # :stopdoc: - - module ExtendCommand - class Measure < Nop - category "Misc" - description "`measure` enables the mode to measure processing time. `measure :off` disables it." - - def initialize(*args) - super(*args) - end - - def execute(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". - - if block_given? - warn 'Configure IRB.conf[:MEASURE_PROC] to add custom measure methods.' - return - end - - 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/cmd/next.rb b/lib/irb/cmd/next.rb deleted file mode 100644 index d29c82e7fc..0000000000 --- a/lib/irb/cmd/next.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require_relative "debug" - -module IRB - # :stopdoc: - - module ExtendCommand - class Next < DebugCommand - def execute(*args) - super(do_cmds: ["next", *args].join(" ")) - end - end - end - - # :startdoc: -end diff --git a/lib/irb/cmd/nop.rb b/lib/irb/cmd/nop.rb index 7fb197c51f..49f89bac95 100644 --- a/lib/irb/cmd/nop.rb +++ b/lib/irb/cmd/nop.rb @@ -1,53 +1,4 @@ -# frozen_string_literal: false -# -# nop.rb - -# by Keiju ISHITSUKA(keiju@ruby-lang.org) -# +# frozen_string_literal: true -module IRB - # :stopdoc: - - module ExtendCommand - class CommandArgumentError < StandardError; end - - class Nop - class << self - def category(category = nil) - @category = category if category - @category - end - - def description(description = nil) - @description = description if description - @description - end - - private - - def string_literal?(args) - sexp = Ripper.sexp(args) - sexp && sexp.size == 2 && sexp.last&.first&.first == :string_literal - end - end - - def self.execute(irb_context, *opts, **kwargs, &block) - command = new(irb_context) - command.execute(*opts, **kwargs, &block) - rescue CommandArgumentError => e - puts e.message - end - - def initialize(irb_context) - @irb_context = irb_context - end - - attr_reader :irb_context - - def execute(*opts) - #nop - end - end - end - - # :startdoc: -end +# This file is just a placeholder for backward-compatibility. +# Please require 'irb' and inheirt your command from `IRB::Command::Base` instead. diff --git a/lib/irb/cmd/pushws.rb b/lib/irb/cmd/pushws.rb deleted file mode 100644 index 59996ceb0c..0000000000 --- a/lib/irb/cmd/pushws.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: false -# -# change-ws.rb - -# by Keiju ISHITSUKA(keiju@ruby-lang.org) -# - -require_relative "nop" -require_relative "../ext/workspaces" - -module IRB - # :stopdoc: - - module ExtendCommand - class Workspaces < Nop - category "Workspace" - description "Show workspaces." - - def execute(*obj) - irb_context.workspaces.collect{|ws| ws.main} - end - end - - class PushWorkspace < Workspaces - category "Workspace" - description "Push an object to the workspace stack." - - def execute(*obj) - irb_context.push_workspace(*obj) - super - end - end - - class PopWorkspace < Workspaces - category "Workspace" - description "Pop a workspace from the workspace stack." - - def execute(*obj) - irb_context.pop_workspace(*obj) - super - end - end - end - - # :startdoc: -end diff --git a/lib/irb/cmd/show_cmds.rb b/lib/irb/cmd/show_cmds.rb deleted file mode 100644 index a8d899e4ac..0000000000 --- a/lib/irb/cmd/show_cmds.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true - -require "stringio" -require_relative "nop" -require_relative "../pager" - -module IRB - # :stopdoc: - - module ExtendCommand - class ShowCmds < Nop - category "IRB" - description "List all available commands and their description." - - def execute(*args) - commands_info = IRB::ExtendCommandBundle.all_commands_info - commands_grouped_by_categories = commands_info.group_by { |cmd| cmd[:category] } - - user_aliases = irb_context.instance_variable_get(:@user_aliases) - - commands_grouped_by_categories["Aliases"] = user_aliases.map do |alias_name, target| - { display_name: alias_name, description: "Alias for `#{target}`" } - end - - if irb_context.with_debugger - # Remove the original "Debugging" category - commands_grouped_by_categories.delete("Debugging") - # Remove the `help` command as it's delegated to the debugger - commands_grouped_by_categories["Context"].delete_if { |cmd| cmd[:display_name] == :help } - # Add an empty "Debugging (from debug.gem)" category at the end - commands_grouped_by_categories["Debugging (from debug.gem)"] = [] - end - - longest_cmd_name_length = commands_info.map { |c| c[:display_name].length }.max - - output = StringIO.new - - commands_grouped_by_categories.each do |category, cmds| - 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 - - # Append the debugger help at the end - if irb_context.with_debugger - output.puts DEBUGGER__.help - end - - Pager.page_content(output.string) - end - end - end - - # :startdoc: -end diff --git a/lib/irb/cmd/show_doc.rb b/lib/irb/cmd/show_doc.rb deleted file mode 100644 index 99dd9ab95a..0000000000 --- a/lib/irb/cmd/show_doc.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true - -require_relative "nop" - -module IRB - module ExtendCommand - class ShowDoc < Nop - class << self - def transform_args(args) - # Return a string literal as is for backward compatibility - if args.empty? || string_literal?(args) - args - else # Otherwise, consider the input as a String for convenience - args.strip.dump - end - end - end - - category "Context" - description "Enter the mode to look up RI documents." - - def execute(*names) - 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 names.empty? - Ri.interactive - else - names.each do |name| - begin - Ri.display_name(name.to_s) - rescue RDoc::RI::Error - puts $!.message - end - 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/cmd/show_source.rb b/lib/irb/cmd/show_source.rb deleted file mode 100644 index cd07de3e90..0000000000 --- a/lib/irb/cmd/show_source.rb +++ /dev/null @@ -1,68 +0,0 @@ -# frozen_string_literal: true - -require_relative "nop" -require_relative "../source_finder" -require_relative "../pager" -require_relative "../color" - -module IRB - module ExtendCommand - class ShowSource < Nop - category "Context" - description "Show the source code of a given method or constant." - - class << self - def transform_args(args) - # Return a string literal as is for backward compatibility - if args.empty? || string_literal?(args) - args - else # Otherwise, consider the input as a String for convenience - args.strip.dump - end - end - end - - def execute(str = nil) - 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/cmd/step.rb b/lib/irb/cmd/step.rb deleted file mode 100644 index 2bc74a9d79..0000000000 --- a/lib/irb/cmd/step.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require_relative "debug" - -module IRB - # :stopdoc: - - module ExtendCommand - class Step < DebugCommand - def execute(*args) - super(do_cmds: ["step", *args].join(" ")) - end - end - end - - # :startdoc: -end diff --git a/lib/irb/cmd/subirb.rb b/lib/irb/cmd/subirb.rb deleted file mode 100644 index 5ffd646416..0000000000 --- a/lib/irb/cmd/subirb.rb +++ /dev/null @@ -1,109 +0,0 @@ -# frozen_string_literal: false -# -# multi.rb - -# by Keiju ISHITSUKA(keiju@ruby-lang.org) -# - -require_relative "nop" - -module IRB - # :stopdoc: - - module ExtendCommand - class MultiIRBCommand < Nop - def execute(*args) - extend_irb_context - end - - 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(*obj) - print_deprecated_warning - - if irb_context.with_debugger - print_debugger_warning - return - end - - super - IRB.irb(nil, *obj) - end - end - - class Jobs < MultiIRBCommand - category "Multi-irb (DEPRECATED)" - description "List of current sessions." - - def execute - print_deprecated_warning - - if irb_context.with_debugger - print_debugger_warning - return - end - - super - IRB.JobManager - end - end - - class Foreground < MultiIRBCommand - category "Multi-irb (DEPRECATED)" - description "Switches to the session of the given number." - - def execute(key = nil) - print_deprecated_warning - - if irb_context.with_debugger - print_debugger_warning - return - end - - super - - raise CommandArgumentError.new("Please specify the id of target IRB job (listed in the `jobs` command).") unless key - IRB.JobManager.switch(key) - end - end - - class Kill < MultiIRBCommand - category "Multi-irb (DEPRECATED)" - description "Kills the session with the given number." - - def execute(*keys) - print_deprecated_warning - - if irb_context.with_debugger - print_debugger_warning - return - end - - super - IRB.JobManager.kill(*keys) - end - end - end - - # :startdoc: -end diff --git a/lib/irb/cmd/whereami.rb b/lib/irb/cmd/whereami.rb deleted file mode 100644 index 8f56ba073d..0000000000 --- a/lib/irb/cmd/whereami.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -require_relative "nop" - -module IRB - # :stopdoc: - - module ExtendCommand - class Whereami < Nop - category "Context" - description "Show the source code around binding.irb again." - - def execute(*) - 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/command.rb b/lib/irb/command.rb new file mode 100644 index 0000000000..cab571cfe3 --- /dev/null +++ b/lib/irb/command.rb @@ -0,0 +1,363 @@ +# frozen_string_literal: false +# +# irb/command.rb - irb command +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# + +require_relative "command/base" + +module IRB # :nodoc: + module Command; end + ExtendCommand = Command + + # Installs the default irb extensions command bundle. + module ExtendCommandBundle + EXCB = ExtendCommandBundle # :nodoc: + + # See #install_alias_method. + NO_OVERRIDE = 0 + # See #install_alias_method. + OVERRIDE_PRIVATE_ONLY = 0x01 + # See #install_alias_method. + OVERRIDE_ALL = 0x02 + + # Displays current configuration. + # + # Modifying the configuration is achieved by sending a message to IRB.conf. + def irb_context + IRB.CurrentContext + end + + @ALIASES = [ + [:context, :irb_context, NO_OVERRIDE], + [:conf, :irb_context, NO_OVERRIDE], + ] + + + @EXTEND_COMMANDS = [ + [ + :irb_exit, :Exit, "command/exit", + [:exit, OVERRIDE_PRIVATE_ONLY], + [:quit, OVERRIDE_PRIVATE_ONLY], + [:irb_quit, OVERRIDE_PRIVATE_ONLY], + ], + [ + :irb_exit!, :ForceExit, "command/force_exit", + [:exit!, OVERRIDE_PRIVATE_ONLY], + ], + + [ + :irb_current_working_workspace, :CurrentWorkingWorkspace, "command/chws", + [: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], + ], + [ + :irb_change_workspace, :ChangeWorkspace, "command/chws", + [: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], + ], + + [ + :irb_workspaces, :Workspaces, "command/pushws", + [:workspaces, NO_OVERRIDE], + [:irb_bindings, OVERRIDE_ALL], + [:bindings, NO_OVERRIDE], + ], + [ + :irb_push_workspace, :PushWorkspace, "command/pushws", + [:pushws, NO_OVERRIDE], + [:irb_pushws, OVERRIDE_ALL], + [:irb_push_binding, OVERRIDE_ALL], + [:irb_pushb, OVERRIDE_ALL], + [:pushb, NO_OVERRIDE], + ], + [ + :irb_pop_workspace, :PopWorkspace, "command/pushws", + [:popws, NO_OVERRIDE], + [:irb_popws, OVERRIDE_ALL], + [:irb_pop_binding, OVERRIDE_ALL], + [:irb_popb, OVERRIDE_ALL], + [:popb, NO_OVERRIDE], + ], + + [ + :irb_load, :Load, "command/load"], + [ + :irb_require, :Require, "command/load"], + [ + :irb_source, :Source, "command/load", + [:source, NO_OVERRIDE], + ], + + [ + :irb, :IrbCommand, "command/subirb"], + [ + :irb_jobs, :Jobs, "command/subirb", + [:jobs, NO_OVERRIDE], + ], + [ + :irb_fg, :Foreground, "command/subirb", + [:fg, NO_OVERRIDE], + ], + [ + :irb_kill, :Kill, "command/subirb", + [:kill, OVERRIDE_PRIVATE_ONLY], + ], + + [ + :irb_debug, :Debug, "command/debug", + [:debug, NO_OVERRIDE], + ], + [ + :irb_edit, :Edit, "command/edit", + [:edit, NO_OVERRIDE], + ], + [ + :irb_break, :Break, "command/break", + ], + [ + :irb_catch, :Catch, "command/catch", + ], + [ + :irb_next, :Next, "command/next" + ], + [ + :irb_delete, :Delete, "command/delete", + [:delete, NO_OVERRIDE], + ], + [ + :irb_step, :Step, "command/step", + [:step, NO_OVERRIDE], + ], + [ + :irb_continue, :Continue, "command/continue", + [:continue, NO_OVERRIDE], + ], + [ + :irb_finish, :Finish, "command/finish", + [:finish, NO_OVERRIDE], + ], + [ + :irb_backtrace, :Backtrace, "command/backtrace", + [:backtrace, NO_OVERRIDE], + [:bt, NO_OVERRIDE], + ], + [ + :irb_debug_info, :Info, "command/info", + [:info, NO_OVERRIDE], + ], + + [ + :irb_help, :Help, "command/help", + [:help, NO_OVERRIDE], + ], + + [ + :irb_show_doc, :ShowDoc, "command/show_doc", + [:show_doc, NO_OVERRIDE], + ], + + [ + :irb_info, :IrbInfo, "command/irb_info" + ], + + [ + :irb_ls, :Ls, "command/ls", + [:ls, NO_OVERRIDE], + ], + + [ + :irb_measure, :Measure, "command/measure", + [:measure, NO_OVERRIDE], + ], + + [ + :irb_show_source, :ShowSource, "command/show_source", + [:show_source, NO_OVERRIDE], + ], + + [ + :irb_whereami, :Whereami, "command/whereami", + [:whereami, NO_OVERRIDE], + ], + [ + :irb_show_cmds, :ShowCmds, "command/show_cmds", + [:show_cmds, NO_OVERRIDE], + ], + + [ + :irb_history, :History, "command/history", + [:history, NO_OVERRIDE], + [:hist, NO_OVERRIDE], + ] + ] + + + @@commands = [] + + def self.all_commands_info + return @@commands unless @@commands.empty? + user_aliases = IRB.CurrentContext.command_aliases.each_with_object({}) do |(alias_name, target), result| + result[target] ||= [] + result[target] << alias_name + end + + @EXTEND_COMMANDS.each do |cmd_name, cmd_class, load_file, *aliases| + if !defined?(Command) || !Command.const_defined?(cmd_class, false) + require_relative load_file + end + + klass = Command.const_get(cmd_class, false) + aliases = aliases.map { |a| a.first } + + if additional_aliases = user_aliases[cmd_name] + aliases += additional_aliases + end + + display_name = aliases.shift || cmd_name + @@commands << { display_name: display_name, description: klass.description, category: klass.category } + end + + @@commands + end + + # Convert a command name to its implementation class if such command exists + def self.load_command(command) + command = command.to_sym + @EXTEND_COMMANDS.each do |cmd_name, cmd_class, load_file, *aliases| + next if cmd_name != command && aliases.all? { |alias_name, _| alias_name != command } + + if !defined?(Command) || !Command.const_defined?(cmd_class, false) + require_relative load_file + end + return Command.const_get(cmd_class, false) + end + nil + end + + # Installs the default irb commands. + def self.install_extend_commands + for args in @EXTEND_COMMANDS + def_extend_command(*args) + end + end + + # Evaluate the given +cmd_name+ on the given +cmd_class+ Class. + # + # Will also define any given +aliases+ for the method. + # + # The optional +load_file+ parameter will be required within the method + # definition. + def self.def_extend_command(cmd_name, cmd_class, load_file, *aliases) + case cmd_class + when Symbol + cmd_class = cmd_class.id2name + when String + when Class + cmd_class = cmd_class.name + end + + line = __LINE__; eval %[ + def #{cmd_name}(*opts, **kwargs, &b) + Kernel.require_relative "#{load_file}" + ::IRB::Command::#{cmd_class}.execute(irb_context, *opts, **kwargs, &b) + end + ], nil, __FILE__, line + + for ali, flag in aliases + @ALIASES.push [ali, cmd_name, flag] + end + end + + # Installs alias methods for the default irb commands, see + # ::install_extend_commands. + def install_alias_method(to, from, override = NO_OVERRIDE) + to = to.id2name unless to.kind_of?(String) + from = from.id2name unless from.kind_of?(String) + + if override == OVERRIDE_ALL or + (override == OVERRIDE_PRIVATE_ONLY) && !respond_to?(to) or + (override == NO_OVERRIDE) && !respond_to?(to, true) + target = self + (class << self; self; end).instance_eval{ + if target.respond_to?(to, true) && + !target.respond_to?(EXCB.irb_original_method_name(to), true) + alias_method(EXCB.irb_original_method_name(to), to) + end + alias_method to, from + } + else + Kernel.warn "irb: warn: can't alias #{to} from #{from}.\n" + end + end + + def self.irb_original_method_name(method_name) # :nodoc: + "irb_" + method_name + "_org" + end + + # Installs alias methods for the default irb commands on the given object + # using #install_alias_method. + def self.extend_object(obj) + unless (class << obj; ancestors; end).include?(EXCB) + super + for ali, com, flg in @ALIASES + obj.install_alias_method(ali, com, flg) + end + end + end + + install_extend_commands + end + + # Extends methods for the Context module + module ContextExtender + CE = ContextExtender # :nodoc: + + @EXTEND_COMMANDS = [ + [:eval_history=, "ext/eval_history.rb"], + [:use_loader=, "ext/use-loader.rb"], + ] + + # Installs the default context extensions as irb commands: + # + # Context#eval_history=:: +irb/ext/history.rb+ + # Context#use_tracer=:: +irb/ext/tracer.rb+ + # Context#use_loader=:: +irb/ext/use-loader.rb+ + def self.install_extend_commands + for args in @EXTEND_COMMANDS + def_extend_command(*args) + end + end + + # Evaluate the given +command+ from the given +load_file+ on the Context + # module. + # + # Will also define any given +aliases+ for the method. + def self.def_extend_command(cmd_name, load_file, *aliases) + line = __LINE__; Context.module_eval %[ + def #{cmd_name}(*opts, &b) + Context.module_eval {remove_method(:#{cmd_name})} + require_relative "#{load_file}" + __send__ :#{cmd_name}, *opts, &b + end + for ali in aliases + alias_method ali, cmd_name + end + ], __FILE__, line + end + + CE.install_extend_commands + end +end diff --git a/lib/irb/command/backtrace.rb b/lib/irb/command/backtrace.rb new file mode 100644 index 0000000000..47e5e60724 --- /dev/null +++ b/lib/irb/command/backtrace.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require_relative "debug" + +module IRB + # :stopdoc: + + module Command + class Backtrace < DebugCommand + def self.transform_args(args) + args&.dump + end + + def execute(*args) + super(pre_cmds: ["backtrace", *args].join(" ")) + end + end + end + + # :startdoc: +end diff --git a/lib/irb/command/base.rb b/lib/irb/command/base.rb new file mode 100644 index 0000000000..87d2fea356 --- /dev/null +++ b/lib/irb/command/base.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: false +# +# nop.rb - +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# + +module IRB + # :stopdoc: + + module Command + class CommandArgumentError < StandardError; end + + class Base + class << self + def category(category = nil) + @category = category if category + @category + end + + def description(description = nil) + @description = description if description + @description + end + + private + + def string_literal?(args) + sexp = Ripper.sexp(args) + sexp && sexp.size == 2 && sexp.last&.first&.first == :string_literal + end + end + + def self.execute(irb_context, *opts, **kwargs, &block) + command = new(irb_context) + command.execute(*opts, **kwargs, &block) + rescue CommandArgumentError => e + puts e.message + end + + def initialize(irb_context) + @irb_context = irb_context + end + + attr_reader :irb_context + + def execute(*opts) + #nop + end + end + + Nop = Base + end + + # :startdoc: +end diff --git a/lib/irb/command/break.rb b/lib/irb/command/break.rb new file mode 100644 index 0000000000..fa200f2d78 --- /dev/null +++ b/lib/irb/command/break.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require_relative "debug" + +module IRB + # :stopdoc: + + module Command + class Break < DebugCommand + def self.transform_args(args) + args&.dump + end + + def execute(args = nil) + super(pre_cmds: "break #{args}") + end + end + end + + # :startdoc: +end diff --git a/lib/irb/command/catch.rb b/lib/irb/command/catch.rb new file mode 100644 index 0000000000..6b2edff5e5 --- /dev/null +++ b/lib/irb/command/catch.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require_relative "debug" + +module IRB + # :stopdoc: + + module Command + class Catch < DebugCommand + def self.transform_args(args) + args&.dump + end + + def execute(*args) + super(pre_cmds: ["catch", *args].join(" ")) + end + end + end + + # :startdoc: +end diff --git a/lib/irb/command/chws.rb b/lib/irb/command/chws.rb new file mode 100644 index 0000000000..341d516155 --- /dev/null +++ b/lib/irb/command/chws.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: false +# +# change-ws.rb - +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +require_relative "../ext/change-ws" + +module IRB + # :stopdoc: + + module Command + + class CurrentWorkingWorkspace < Base + category "Workspace" + description "Show the current workspace." + + def execute(*obj) + irb_context.main + end + end + + class ChangeWorkspace < Base + category "Workspace" + description "Change the current workspace to an object." + + def execute(*obj) + irb_context.change_workspace(*obj) + irb_context.main + end + end + end + + # :startdoc: +end diff --git a/lib/irb/command/continue.rb b/lib/irb/command/continue.rb new file mode 100644 index 0000000000..8b6ffc860c --- /dev/null +++ b/lib/irb/command/continue.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require_relative "debug" + +module IRB + # :stopdoc: + + module Command + class Continue < DebugCommand + def execute(*args) + super(do_cmds: ["continue", *args].join(" ")) + end + end + end + + # :startdoc: +end diff --git a/lib/irb/command/debug.rb b/lib/irb/command/debug.rb new file mode 100644 index 0000000000..9a2c641251 --- /dev/null +++ b/lib/irb/command/debug.rb @@ -0,0 +1,79 @@ +require_relative "../debug" + +module IRB + # :stopdoc: + + module Command + class Debug < Base + category "Debugging" + description "Start the debugger of debug.gem." + + BINDING_IRB_FRAME_REGEXPS = [ + '', + binding.method(:irb).source_location.first, + ].map { |file| /\A#{Regexp.escape(file)}:\d+:in `irb'\z/ } + + def execute(pre_cmds: nil, do_cmds: nil) + 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 binding_irb? + 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 + + private + + def binding_irb? + caller.any? do |frame| + BINDING_IRB_FRAME_REGEXPS.any? do |regexp| + frame.match?(regexp) + end + end + end + end + + class DebugCommand < Debug + def self.category + "Debugging" + end + + def self.description + command_name = self.name.split("::").last.downcase + "Start the debugger of debug.gem and run its `#{command_name}` command." + end + end + end +end diff --git a/lib/irb/command/delete.rb b/lib/irb/command/delete.rb new file mode 100644 index 0000000000..a36b4577b0 --- /dev/null +++ b/lib/irb/command/delete.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require_relative "debug" + +module IRB + # :stopdoc: + + module Command + class Delete < DebugCommand + def execute(*args) + super(pre_cmds: ["delete", *args].join(" ")) + end + end + end + + # :startdoc: +end diff --git a/lib/irb/command/edit.rb b/lib/irb/command/edit.rb new file mode 100644 index 0000000000..1a8ded6bcf --- /dev/null +++ b/lib/irb/command/edit.rb @@ -0,0 +1,54 @@ +require 'shellwords' + +require_relative "../source_finder" + +module IRB + # :stopdoc: + + module Command + class Edit < Base + category "Misc" + description 'Open a file with the editor command defined with `ENV["VISUAL"]` or `ENV["EDITOR"]`.' + + class << self + def transform_args(args) + # Return a string literal as is for backward compatibility + if args.nil? || args.empty? || string_literal?(args) + args + else # Otherwise, consider the input as a String for convenience + args.strip.dump + end + end + end + + def execute(*args) + path = args.first + + 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 new file mode 100644 index 0000000000..3109ec16e3 --- /dev/null +++ b/lib/irb/command/exit.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module IRB + # :stopdoc: + + module Command + class Exit < Base + category "IRB" + description "Exit the current irb session." + + def execute(*) + IRB.irb_exit + rescue UncaughtThrowError + Kernel.exit + end + end + end + + # :startdoc: +end diff --git a/lib/irb/command/finish.rb b/lib/irb/command/finish.rb new file mode 100644 index 0000000000..05501819e2 --- /dev/null +++ b/lib/irb/command/finish.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require_relative "debug" + +module IRB + # :stopdoc: + + module Command + class Finish < DebugCommand + def execute(*args) + super(do_cmds: ["finish", *args].join(" ")) + end + end + end + + # :startdoc: +end diff --git a/lib/irb/command/force_exit.rb b/lib/irb/command/force_exit.rb new file mode 100644 index 0000000000..c2c5542e24 --- /dev/null +++ b/lib/irb/command/force_exit.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module IRB + # :stopdoc: + + module Command + class ForceExit < Base + category "IRB" + description "Exit the current process." + + def execute(*) + throw :IRB_EXIT, true + rescue UncaughtThrowError + Kernel.exit! + end + end + end + + # :startdoc: +end diff --git a/lib/irb/command/help.rb b/lib/irb/command/help.rb new file mode 100644 index 0000000000..67cc31a0bf --- /dev/null +++ b/lib/irb/command/help.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require_relative "show_cmds" + +module IRB + module Command + class Help < ShowCmds + category "IRB" + description "List all available commands and their description." + end + end +end diff --git a/lib/irb/command/history.rb b/lib/irb/command/history.rb new file mode 100644 index 0000000000..a47a8795dd --- /dev/null +++ b/lib/irb/command/history.rb @@ -0,0 +1,47 @@ +# 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 self.transform_args(args) + match = args&.match(/(-g|-G)\s+(?.+)\s*\n\z/) + return unless match + + "grep: #{Regexp.new(match[:grep]).inspect}" + end + + def execute(grep: nil) + 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 new file mode 100644 index 0000000000..a67be3eb85 --- /dev/null +++ b/lib/irb/command/info.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require_relative "debug" + +module IRB + # :stopdoc: + + module Command + class Info < DebugCommand + def self.transform_args(args) + args&.dump + end + + def execute(*args) + super(pre_cmds: ["info", *args].join(" ")) + end + end + end + + # :startdoc: +end diff --git a/lib/irb/command/irb_info.rb b/lib/irb/command/irb_info.rb new file mode 100644 index 0000000000..7fd3e2104a --- /dev/null +++ b/lib/irb/command/irb_info.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: false + +module IRB + # :stopdoc: + + module Command + class IrbInfo < Base + category "IRB" + description "Show information about IRB." + + def execute + 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" + str += ".irbrc path: #{IRB.rc_file}\n" if File.exist?(IRB.rc_file) + 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 new file mode 100644 index 0000000000..0558bc83b0 --- /dev/null +++ b/lib/irb/command/load.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: false +# +# load.rb - +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +require_relative "../ext/loader" + +module IRB + # :stopdoc: + + module Command + class LoaderCommand < Base + 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(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(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(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 new file mode 100644 index 0000000000..bbe4a1ee98 --- /dev/null +++ b/lib/irb/command/ls.rb @@ -0,0 +1,139 @@ +# frozen_string_literal: true + +require "reline" +require "stringio" + +require_relative "../pager" +require_relative "../color" + +module IRB + # :stopdoc: + + module Command + class Ls < Base + category "Context" + description "Show methods, constants, and variables. `-g [query]` or `-G [query]` allows you to filter out the output." + + def self.transform_args(args) + if match = args&.match(/\A(?.+\s|)(-g|-G)\s+(?[^\s]+)\s*\n\z/) + args = match[:args] + "#{args}#{',' unless args.chomp.empty?} grep: /#{match[:grep]}/" + else + args + end + end + + def execute(*arg, grep: nil) + o = Output.new(grep: grep) + + obj = arg.empty? ? irb_context.workspace.main : arg.first + locals = arg.empty? ? irb_context.workspace.binding.local_variables : [] + 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) + o.print_result + 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 - + 80 + end + end + private_constant :Output + end + end + + # :startdoc: +end diff --git a/lib/irb/command/measure.rb b/lib/irb/command/measure.rb new file mode 100644 index 0000000000..ee7927b010 --- /dev/null +++ b/lib/irb/command/measure.rb @@ -0,0 +1,43 @@ +module IRB + # :stopdoc: + + module Command + class Measure < Base + category "Misc" + description "`measure` enables the mode to measure processing time. `measure :off` disables it." + + def initialize(*args) + super(*args) + end + + def execute(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". + + if block_given? + warn 'Configure IRB.conf[:MEASURE_PROC] to add custom measure methods.' + return + end + + 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 new file mode 100644 index 0000000000..6487c9d24c --- /dev/null +++ b/lib/irb/command/next.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require_relative "debug" + +module IRB + # :stopdoc: + + module Command + class Next < DebugCommand + def execute(*args) + super(do_cmds: ["next", *args].join(" ")) + end + end + end + + # :startdoc: +end diff --git a/lib/irb/command/pushws.rb b/lib/irb/command/pushws.rb new file mode 100644 index 0000000000..a6fcd6a165 --- /dev/null +++ b/lib/irb/command/pushws.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: false +# +# change-ws.rb - +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# + +require_relative "../ext/workspaces" + +module IRB + # :stopdoc: + + module Command + class Workspaces < Base + category "Workspace" + description "Show workspaces." + + def execute(*obj) + irb_context.workspaces.collect{|ws| ws.main} + end + end + + class PushWorkspace < Workspaces + category "Workspace" + description "Push an object to the workspace stack." + + def execute(*obj) + irb_context.push_workspace(*obj) + super + end + end + + class PopWorkspace < Workspaces + category "Workspace" + description "Pop a workspace from the workspace stack." + + def execute(*obj) + irb_context.pop_workspace(*obj) + super + end + end + end + + # :startdoc: +end diff --git a/lib/irb/command/show_cmds.rb b/lib/irb/command/show_cmds.rb new file mode 100644 index 0000000000..940ed490d3 --- /dev/null +++ b/lib/irb/command/show_cmds.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require "stringio" + +require_relative "../pager" + +module IRB + # :stopdoc: + + module Command + class ShowCmds < Base + category "IRB" + description "List all available commands and their description." + + def execute(*args) + commands_info = IRB::ExtendCommandBundle.all_commands_info + commands_grouped_by_categories = commands_info.group_by { |cmd| cmd[:category] } + + user_aliases = irb_context.instance_variable_get(:@user_aliases) + + commands_grouped_by_categories["Aliases"] = user_aliases.map do |alias_name, target| + { display_name: alias_name, description: "Alias for `#{target}`" } + end + + if irb_context.with_debugger + # Remove the original "Debugging" category + commands_grouped_by_categories.delete("Debugging") + # Remove the `help` command as it's delegated to the debugger + commands_grouped_by_categories["Context"].delete_if { |cmd| cmd[:display_name] == :help } + # Add an empty "Debugging (from debug.gem)" category at the end + commands_grouped_by_categories["Debugging (from debug.gem)"] = [] + end + + longest_cmd_name_length = commands_info.map { |c| c[:display_name].length }.max + + output = StringIO.new + + commands_grouped_by_categories.each do |category, cmds| + 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 + + # Append the debugger help at the end + if irb_context.with_debugger + output.puts DEBUGGER__.help + end + + Pager.page_content(output.string) + end + end + end + + # :startdoc: +end diff --git a/lib/irb/command/show_doc.rb b/lib/irb/command/show_doc.rb new file mode 100644 index 0000000000..dca10ec4be --- /dev/null +++ b/lib/irb/command/show_doc.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module IRB + module Command + class ShowDoc < Base + class << self + def transform_args(args) + # Return a string literal as is for backward compatibility + if args.empty? || string_literal?(args) + args + else # Otherwise, consider the input as a String for convenience + args.strip.dump + end + end + end + + category "Context" + description "Enter the mode to look up RI documents." + + def execute(*names) + 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 names.empty? + Ri.interactive + else + names.each do |name| + begin + Ri.display_name(name.to_s) + rescue RDoc::RI::Error + puts $!.message + end + 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 new file mode 100644 index 0000000000..cc783e7532 --- /dev/null +++ b/lib/irb/command/show_source.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require_relative "../source_finder" +require_relative "../pager" +require_relative "../color" + +module IRB + module Command + class ShowSource < Base + category "Context" + description "Show the source code of a given method or constant." + + class << self + def transform_args(args) + # Return a string literal as is for backward compatibility + if args.empty? || string_literal?(args) + args + else # Otherwise, consider the input as a String for convenience + args.strip.dump + end + end + end + + def execute(str = nil) + 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 new file mode 100644 index 0000000000..cce7d2b0f8 --- /dev/null +++ b/lib/irb/command/step.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require_relative "debug" + +module IRB + # :stopdoc: + + module Command + class Step < DebugCommand + def execute(*args) + super(do_cmds: ["step", *args].join(" ")) + end + end + end + + # :startdoc: +end diff --git a/lib/irb/command/subirb.rb b/lib/irb/command/subirb.rb new file mode 100644 index 0000000000..0a75706f5f --- /dev/null +++ b/lib/irb/command/subirb.rb @@ -0,0 +1,107 @@ +# frozen_string_literal: false +# +# multi.rb - +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# + +module IRB + # :stopdoc: + + module Command + class MultiIRBCommand < Base + def execute(*args) + extend_irb_context + end + + 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(*obj) + print_deprecated_warning + + if irb_context.with_debugger + print_debugger_warning + return + end + + super + IRB.irb(nil, *obj) + end + end + + class Jobs < MultiIRBCommand + category "Multi-irb (DEPRECATED)" + description "List of current sessions." + + def execute + print_deprecated_warning + + if irb_context.with_debugger + print_debugger_warning + return + end + + super + IRB.JobManager + end + end + + class Foreground < MultiIRBCommand + category "Multi-irb (DEPRECATED)" + description "Switches to the session of the given number." + + def execute(key = nil) + print_deprecated_warning + + if irb_context.with_debugger + print_debugger_warning + return + end + + super + + raise CommandArgumentError.new("Please specify the id of target IRB job (listed in the `jobs` command).") unless key + IRB.JobManager.switch(key) + end + end + + class Kill < MultiIRBCommand + category "Multi-irb (DEPRECATED)" + description "Kills the session with the given number." + + def execute(*keys) + print_deprecated_warning + + if irb_context.with_debugger + print_debugger_warning + return + end + + super + IRB.JobManager.kill(*keys) + end + end + end + + # :startdoc: +end diff --git a/lib/irb/command/whereami.rb b/lib/irb/command/whereami.rb new file mode 100644 index 0000000000..d6658d7043 --- /dev/null +++ b/lib/irb/command/whereami.rb @@ -0,0 +1,23 @@ +# 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(*) + 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/ext/use-loader.rb b/lib/irb/ext/use-loader.rb index d0b8c2d4f4..c5ecbe2847 100644 --- a/lib/irb/ext/use-loader.rb +++ b/lib/irb/ext/use-loader.rb @@ -17,12 +17,12 @@ module IRB 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) - ExtendCommand::Load.execute(irb_context, *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) - ExtendCommand::Require.execute(irb_context, *opts, &b) + Command::Require.execute(irb_context, *opts, &b) end end diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb deleted file mode 100644 index d303bf76da..0000000000 --- a/lib/irb/extend-command.rb +++ /dev/null @@ -1,358 +0,0 @@ -# frozen_string_literal: false -# -# irb/extend-command.rb - irb extend command -# by Keiju ISHITSUKA(keiju@ruby-lang.org) -# - -module IRB # :nodoc: - # Installs the default irb extensions command bundle. - module ExtendCommandBundle - EXCB = ExtendCommandBundle # :nodoc: - - # See #install_alias_method. - NO_OVERRIDE = 0 - # See #install_alias_method. - OVERRIDE_PRIVATE_ONLY = 0x01 - # See #install_alias_method. - OVERRIDE_ALL = 0x02 - - # Displays current configuration. - # - # Modifying the configuration is achieved by sending a message to IRB.conf. - def irb_context - IRB.CurrentContext - end - - @ALIASES = [ - [:context, :irb_context, NO_OVERRIDE], - [:conf, :irb_context, NO_OVERRIDE], - ] - - - @EXTEND_COMMANDS = [ - [ - :irb_exit, :Exit, "cmd/exit", - [:exit, OVERRIDE_PRIVATE_ONLY], - [:quit, OVERRIDE_PRIVATE_ONLY], - [:irb_quit, OVERRIDE_PRIVATE_ONLY], - ], - [ - :irb_exit!, :ForceExit, "cmd/force_exit", - [:exit!, OVERRIDE_PRIVATE_ONLY], - ], - - [ - :irb_current_working_workspace, :CurrentWorkingWorkspace, "cmd/chws", - [: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], - ], - [ - :irb_change_workspace, :ChangeWorkspace, "cmd/chws", - [: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], - ], - - [ - :irb_workspaces, :Workspaces, "cmd/pushws", - [:workspaces, NO_OVERRIDE], - [:irb_bindings, OVERRIDE_ALL], - [:bindings, NO_OVERRIDE], - ], - [ - :irb_push_workspace, :PushWorkspace, "cmd/pushws", - [:pushws, NO_OVERRIDE], - [:irb_pushws, OVERRIDE_ALL], - [:irb_push_binding, OVERRIDE_ALL], - [:irb_pushb, OVERRIDE_ALL], - [:pushb, NO_OVERRIDE], - ], - [ - :irb_pop_workspace, :PopWorkspace, "cmd/pushws", - [:popws, NO_OVERRIDE], - [:irb_popws, OVERRIDE_ALL], - [:irb_pop_binding, OVERRIDE_ALL], - [:irb_popb, OVERRIDE_ALL], - [:popb, NO_OVERRIDE], - ], - - [ - :irb_load, :Load, "cmd/load"], - [ - :irb_require, :Require, "cmd/load"], - [ - :irb_source, :Source, "cmd/load", - [:source, NO_OVERRIDE], - ], - - [ - :irb, :IrbCommand, "cmd/subirb"], - [ - :irb_jobs, :Jobs, "cmd/subirb", - [:jobs, NO_OVERRIDE], - ], - [ - :irb_fg, :Foreground, "cmd/subirb", - [:fg, NO_OVERRIDE], - ], - [ - :irb_kill, :Kill, "cmd/subirb", - [:kill, OVERRIDE_PRIVATE_ONLY], - ], - - [ - :irb_debug, :Debug, "cmd/debug", - [:debug, NO_OVERRIDE], - ], - [ - :irb_edit, :Edit, "cmd/edit", - [:edit, NO_OVERRIDE], - ], - [ - :irb_break, :Break, "cmd/break", - ], - [ - :irb_catch, :Catch, "cmd/catch", - ], - [ - :irb_next, :Next, "cmd/next" - ], - [ - :irb_delete, :Delete, "cmd/delete", - [:delete, NO_OVERRIDE], - ], - [ - :irb_step, :Step, "cmd/step", - [:step, NO_OVERRIDE], - ], - [ - :irb_continue, :Continue, "cmd/continue", - [:continue, NO_OVERRIDE], - ], - [ - :irb_finish, :Finish, "cmd/finish", - [:finish, NO_OVERRIDE], - ], - [ - :irb_backtrace, :Backtrace, "cmd/backtrace", - [:backtrace, NO_OVERRIDE], - [:bt, NO_OVERRIDE], - ], - [ - :irb_debug_info, :Info, "cmd/info", - [:info, NO_OVERRIDE], - ], - - [ - :irb_help, :Help, "cmd/help", - [:help, NO_OVERRIDE], - ], - - [ - :irb_show_doc, :ShowDoc, "cmd/show_doc", - [:show_doc, NO_OVERRIDE], - ], - - [ - :irb_info, :IrbInfo, "cmd/irb_info" - ], - - [ - :irb_ls, :Ls, "cmd/ls", - [:ls, NO_OVERRIDE], - ], - - [ - :irb_measure, :Measure, "cmd/measure", - [:measure, NO_OVERRIDE], - ], - - [ - :irb_show_source, :ShowSource, "cmd/show_source", - [:show_source, NO_OVERRIDE], - ], - - [ - :irb_whereami, :Whereami, "cmd/whereami", - [:whereami, NO_OVERRIDE], - ], - [ - :irb_show_cmds, :ShowCmds, "cmd/show_cmds", - [:show_cmds, NO_OVERRIDE], - ], - - [ - :irb_history, :History, "cmd/history", - [:history, NO_OVERRIDE], - [:hist, NO_OVERRIDE], - ] - ] - - - @@commands = [] - - def self.all_commands_info - return @@commands unless @@commands.empty? - user_aliases = IRB.CurrentContext.command_aliases.each_with_object({}) do |(alias_name, target), result| - result[target] ||= [] - result[target] << alias_name - end - - @EXTEND_COMMANDS.each do |cmd_name, cmd_class, load_file, *aliases| - if !defined?(ExtendCommand) || !ExtendCommand.const_defined?(cmd_class, false) - require_relative load_file - end - - klass = ExtendCommand.const_get(cmd_class, false) - aliases = aliases.map { |a| a.first } - - if additional_aliases = user_aliases[cmd_name] - aliases += additional_aliases - end - - display_name = aliases.shift || cmd_name - @@commands << { display_name: display_name, description: klass.description, category: klass.category } - end - - @@commands - end - - # Convert a command name to its implementation class if such command exists - def self.load_command(command) - command = command.to_sym - @EXTEND_COMMANDS.each do |cmd_name, cmd_class, load_file, *aliases| - next if cmd_name != command && aliases.all? { |alias_name, _| alias_name != command } - - if !defined?(ExtendCommand) || !ExtendCommand.const_defined?(cmd_class, false) - require_relative load_file - end - return ExtendCommand.const_get(cmd_class, false) - end - nil - end - - # Installs the default irb commands. - def self.install_extend_commands - for args in @EXTEND_COMMANDS - def_extend_command(*args) - end - end - - # Evaluate the given +cmd_name+ on the given +cmd_class+ Class. - # - # Will also define any given +aliases+ for the method. - # - # The optional +load_file+ parameter will be required within the method - # definition. - def self.def_extend_command(cmd_name, cmd_class, load_file, *aliases) - case cmd_class - when Symbol - cmd_class = cmd_class.id2name - when String - when Class - cmd_class = cmd_class.name - end - - line = __LINE__; eval %[ - def #{cmd_name}(*opts, **kwargs, &b) - Kernel.require_relative "#{load_file}" - ::IRB::ExtendCommand::#{cmd_class}.execute(irb_context, *opts, **kwargs, &b) - end - ], nil, __FILE__, line - - for ali, flag in aliases - @ALIASES.push [ali, cmd_name, flag] - end - end - - # Installs alias methods for the default irb commands, see - # ::install_extend_commands. - def install_alias_method(to, from, override = NO_OVERRIDE) - to = to.id2name unless to.kind_of?(String) - from = from.id2name unless from.kind_of?(String) - - if override == OVERRIDE_ALL or - (override == OVERRIDE_PRIVATE_ONLY) && !respond_to?(to) or - (override == NO_OVERRIDE) && !respond_to?(to, true) - target = self - (class << self; self; end).instance_eval{ - if target.respond_to?(to, true) && - !target.respond_to?(EXCB.irb_original_method_name(to), true) - alias_method(EXCB.irb_original_method_name(to), to) - end - alias_method to, from - } - else - Kernel.warn "irb: warn: can't alias #{to} from #{from}.\n" - end - end - - def self.irb_original_method_name(method_name) # :nodoc: - "irb_" + method_name + "_org" - end - - # Installs alias methods for the default irb commands on the given object - # using #install_alias_method. - def self.extend_object(obj) - unless (class << obj; ancestors; end).include?(EXCB) - super - for ali, com, flg in @ALIASES - obj.install_alias_method(ali, com, flg) - end - end - end - - install_extend_commands - end - - # Extends methods for the Context module - module ContextExtender - CE = ContextExtender # :nodoc: - - @EXTEND_COMMANDS = [ - [:eval_history=, "ext/eval_history.rb"], - [:use_loader=, "ext/use-loader.rb"], - ] - - # Installs the default context extensions as irb commands: - # - # Context#eval_history=:: +irb/ext/history.rb+ - # Context#use_tracer=:: +irb/ext/tracer.rb+ - # Context#use_loader=:: +irb/ext/use-loader.rb+ - def self.install_extend_commands - for args in @EXTEND_COMMANDS - def_extend_command(*args) - end - end - - # Evaluate the given +command+ from the given +load_file+ on the Context - # module. - # - # Will also define any given +aliases+ for the method. - def self.def_extend_command(cmd_name, load_file, *aliases) - line = __LINE__; Context.module_eval %[ - def #{cmd_name}(*opts, &b) - Context.module_eval {remove_method(:#{cmd_name})} - require_relative "#{load_file}" - __send__ :#{cmd_name}, *opts, &b - end - for ali in aliases - alias_method ali, cmd_name - end - ], __FILE__, line - end - - CE.install_extend_commands - end -end diff --git a/lib/irb/statement.rb b/lib/irb/statement.rb index 4e17e51434..009eb2d20b 100644 --- a/lib/irb/statement.rb +++ b/lib/irb/statement.rb @@ -83,9 +83,9 @@ module IRB end def should_be_handled_by_debugger? - require_relative 'cmd/help' - require_relative 'cmd/debug' - IRB::ExtendCommand::DebugCommand > @command_class || IRB::ExtendCommand::Help == @command_class + require_relative 'command/help' + require_relative 'command/debug' + IRB::Command::DebugCommand > @command_class || IRB::Command::Help == @command_class end def evaluable_code -- cgit v1.2.3