diff options
Diffstat (limited to 'lib/irb/command')
27 files changed, 1124 insertions, 0 deletions
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([email protected]) +# + +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([email protected]) +# +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 = [ + '<internal:prelude>', + 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+(?<grep>.+)\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([email protected]) +# +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(?<args>.+\s|)(-g|-G)\s+(?<grep>[^\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 - <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 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([email protected]) +# + +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([email protected]) +# + +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 |