From 3956bb859c2442d34ea171db8f92f3e5895c43d9 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Thu, 8 Dec 2022 19:10:19 +0000 Subject: [ruby/irb] Add "show_cmds" command to list all commands' descriptions (https://github.com/ruby/irb/pull/463) https://github.com/ruby/irb/commit/7e857655ac --- lib/irb/cmd/backtrace.rb | 2 +- lib/irb/cmd/break.rb | 2 +- lib/irb/cmd/catch.rb | 2 +- lib/irb/cmd/chws.rb | 6 ++++++ lib/irb/cmd/continue.rb | 2 +- lib/irb/cmd/debug.rb | 14 +++++++++++++ lib/irb/cmd/delete.rb | 2 +- lib/irb/cmd/edit.rb | 3 +++ lib/irb/cmd/finish.rb | 2 +- lib/irb/cmd/help.rb | 3 +++ lib/irb/cmd/info.rb | 2 +- lib/irb/cmd/irb_info.rb | 3 +++ lib/irb/cmd/load.rb | 10 ++++++++++ lib/irb/cmd/ls.rb | 3 +++ lib/irb/cmd/measure.rb | 3 +++ lib/irb/cmd/next.rb | 2 +- lib/irb/cmd/nop.rb | 11 +++++++++++ lib/irb/cmd/pushws.rb | 9 +++++++++ lib/irb/cmd/show_cmds.rb | 39 ++++++++++++++++++++++++++++++++++++ lib/irb/cmd/show_source.rb | 3 +++ lib/irb/cmd/step.rb | 2 +- lib/irb/cmd/subirb.rb | 12 ++++++++++++ lib/irb/cmd/whereami.rb | 3 +++ lib/irb/extend-command.rb | 49 ++++++++++++++++++++++++++++++++++++++-------- test/irb/test_cmd.rb | 10 ++++++++++ 25 files changed, 182 insertions(+), 17 deletions(-) create mode 100644 lib/irb/cmd/show_cmds.rb diff --git a/lib/irb/cmd/backtrace.rb b/lib/irb/cmd/backtrace.rb index ac4f0e0e7e..f632894618 100644 --- a/lib/irb/cmd/backtrace.rb +++ b/lib/irb/cmd/backtrace.rb @@ -6,7 +6,7 @@ module IRB # :stopdoc: module ExtendCommand - class Backtrace < Debug + class Backtrace < DebugCommand def self.transform_args(args) args&.dump end diff --git a/lib/irb/cmd/break.rb b/lib/irb/cmd/break.rb index 2c82413f6a..df259a90ca 100644 --- a/lib/irb/cmd/break.rb +++ b/lib/irb/cmd/break.rb @@ -6,7 +6,7 @@ module IRB # :stopdoc: module ExtendCommand - class Break < Debug + class Break < DebugCommand def self.transform_args(args) args&.dump end diff --git a/lib/irb/cmd/catch.rb b/lib/irb/cmd/catch.rb index 8c9e086a9c..40b62c7533 100644 --- a/lib/irb/cmd/catch.rb +++ b/lib/irb/cmd/catch.rb @@ -6,7 +6,7 @@ module IRB # :stopdoc: module ExtendCommand - class Catch < Debug + class Catch < DebugCommand def self.transform_args(args) args&.dump end diff --git a/lib/irb/cmd/chws.rb b/lib/irb/cmd/chws.rb index b28c090686..7c84ba0a4b 100644 --- a/lib/irb/cmd/chws.rb +++ b/lib/irb/cmd/chws.rb @@ -19,12 +19,18 @@ module IRB module ExtendCommand class CurrentWorkingWorkspace < Nop + category "IRB" + description "Show the current workspace." + def execute(*obj) irb_context.main end end class ChangeWorkspace < Nop + category "IRB" + description "Change the current workspace to an object." + def execute(*obj) irb_context.change_workspace(*obj) irb_context.main diff --git a/lib/irb/cmd/continue.rb b/lib/irb/cmd/continue.rb index 94696e4b6c..9136177eef 100644 --- a/lib/irb/cmd/continue.rb +++ b/lib/irb/cmd/continue.rb @@ -6,7 +6,7 @@ module IRB # :stopdoc: module ExtendCommand - class Continue < Debug + class Continue < DebugCommand def execute(*args) super(do_cmds: ["continue", *args].join(" ")) end diff --git a/lib/irb/cmd/debug.rb b/lib/irb/cmd/debug.rb index 2c09c99cf0..76f17e3c2f 100644 --- a/lib/irb/cmd/debug.rb +++ b/lib/irb/cmd/debug.rb @@ -5,6 +5,9 @@ module IRB module ExtendCommand class Debug < Nop + category "Debugging" + description "Start the debugger of debug.gem." + BINDING_IRB_FRAME_REGEXPS = [ '', binding.method(:irb).source_location.first, @@ -108,5 +111,16 @@ module IRB 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 index 3810ae414e..aeb26d2572 100644 --- a/lib/irb/cmd/delete.rb +++ b/lib/irb/cmd/delete.rb @@ -6,7 +6,7 @@ module IRB # :stopdoc: module ExtendCommand - class Delete < Debug + class Delete < DebugCommand def execute(*args) super(pre_cmds: ["delete", *args].join(" ")) end diff --git a/lib/irb/cmd/edit.rb b/lib/irb/cmd/edit.rb index 8d3fab3273..98fa07e49d 100644 --- a/lib/irb/cmd/edit.rb +++ b/lib/irb/cmd/edit.rb @@ -6,6 +6,9 @@ module IRB module ExtendCommand class Edit < Nop + category "Misc" + description 'Open a file with the editor command defined with `ENV["EDITOR"]`.' + class << self def transform_args(args) # Return a string literal as is for backward compatibility diff --git a/lib/irb/cmd/finish.rb b/lib/irb/cmd/finish.rb index de4b4f12cf..29f100feb5 100644 --- a/lib/irb/cmd/finish.rb +++ b/lib/irb/cmd/finish.rb @@ -6,7 +6,7 @@ module IRB # :stopdoc: module ExtendCommand - class Finish < Debug + class Finish < DebugCommand def execute(*args) super(do_cmds: ["finish", *args].join(" ")) end diff --git a/lib/irb/cmd/help.rb b/lib/irb/cmd/help.rb index 0497c57457..3ef59f5eea 100644 --- a/lib/irb/cmd/help.rb +++ b/lib/irb/cmd/help.rb @@ -16,6 +16,9 @@ module IRB module ExtendCommand class Help < Nop + category "Context" + description "Enter the mode to look up RI documents." + def execute(*names) require 'rdoc/ri/driver' opts = RDoc::RI::Driver.process_args([]) diff --git a/lib/irb/cmd/info.rb b/lib/irb/cmd/info.rb index 413c286429..2c0a32b34f 100644 --- a/lib/irb/cmd/info.rb +++ b/lib/irb/cmd/info.rb @@ -6,7 +6,7 @@ module IRB # :stopdoc: module ExtendCommand - class Info < Debug + class Info < DebugCommand def self.transform_args(args) args&.dump end diff --git a/lib/irb/cmd/irb_info.rb b/lib/irb/cmd/irb_info.rb index 8a4e1bd603..da11e8d40b 100644 --- a/lib/irb/cmd/irb_info.rb +++ b/lib/irb/cmd/irb_info.rb @@ -7,6 +7,9 @@ module IRB module ExtendCommand class IrbInfo < Nop + category "IRB" + description "Show information about IRB." + def execute Class.new { def inspect diff --git a/lib/irb/cmd/load.rb b/lib/irb/cmd/load.rb index 2c5c01e89c..76368f856c 100644 --- a/lib/irb/cmd/load.rb +++ b/lib/irb/cmd/load.rb @@ -20,6 +20,9 @@ module IRB class Load < Nop include IrbLoader + category "IRB" + description "Load a Ruby file." + def execute(file_name, priv = nil) return irb_load(file_name, priv) end @@ -28,6 +31,9 @@ module IRB class Require < Nop include IrbLoader + category "IRB" + description "Require a Ruby file." + def execute(file_name) rex = Regexp.new("#{Regexp.quote(file_name)}(\.o|\.rb)?") @@ -58,6 +64,10 @@ module IRB class Source < Nop include IrbLoader + + category "IRB" + description "Loads a given file in the current session." + def execute(file_name) source_file(file_name) end diff --git a/lib/irb/cmd/ls.rb b/lib/irb/cmd/ls.rb index 735bf17241..b65fae2bf1 100644 --- a/lib/irb/cmd/ls.rb +++ b/lib/irb/cmd/ls.rb @@ -9,6 +9,9 @@ module IRB 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] diff --git a/lib/irb/cmd/measure.rb b/lib/irb/cmd/measure.rb index a97baee9f1..9122e2dac9 100644 --- a/lib/irb/cmd/measure.rb +++ b/lib/irb/cmd/measure.rb @@ -5,6 +5,9 @@ module IRB 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 diff --git a/lib/irb/cmd/next.rb b/lib/irb/cmd/next.rb index 2943a753fb..d29c82e7fc 100644 --- a/lib/irb/cmd/next.rb +++ b/lib/irb/cmd/next.rb @@ -6,7 +6,7 @@ module IRB # :stopdoc: module ExtendCommand - class Next < Debug + class Next < DebugCommand def execute(*args) super(do_cmds: ["next", *args].join(" ")) end diff --git a/lib/irb/cmd/nop.rb b/lib/irb/cmd/nop.rb index 881a736722..280bfe4677 100644 --- a/lib/irb/cmd/nop.rb +++ b/lib/irb/cmd/nop.rb @@ -14,6 +14,17 @@ module IRB module ExtendCommand class Nop + class << self + def category(category = nil) + @category = category if category + @category + end + + def description(description = nil) + @description = description if description + @description + end + end if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.7.0" def self.execute(conf, *opts, **kwargs, &block) diff --git a/lib/irb/cmd/pushws.rb b/lib/irb/cmd/pushws.rb index 791d8f8dbb..41d2e705f1 100644 --- a/lib/irb/cmd/pushws.rb +++ b/lib/irb/cmd/pushws.rb @@ -18,12 +18,18 @@ module IRB module ExtendCommand class Workspaces < Nop + category "IRB" + description "Show workspaces." + def execute(*obj) irb_context.workspaces.collect{|ws| ws.main} end end class PushWorkspace < Workspaces + category "IRB" + description "Push an object to the workspace stack." + def execute(*obj) irb_context.push_workspace(*obj) super @@ -31,6 +37,9 @@ module IRB end class PopWorkspace < Workspaces + category "IRB" + description "Pop a workspace from the workspace stack." + def execute(*obj) irb_context.pop_workspace(*obj) super diff --git a/lib/irb/cmd/show_cmds.rb b/lib/irb/cmd/show_cmds.rb new file mode 100644 index 0000000000..acced27d48 --- /dev/null +++ b/lib/irb/cmd/show_cmds.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require "stringio" +require_relative "nop" + +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] } + longest_cmd_name_length = commands_info.map { |c| c[:display_name] }.max { |a, b| a.length <=> b.length }.length + + 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 + + puts output.string + + nil + end + end + end + + # :startdoc: +end diff --git a/lib/irb/cmd/show_source.rb b/lib/irb/cmd/show_source.rb index 03c21b78c7..b68aa257c2 100644 --- a/lib/irb/cmd/show_source.rb +++ b/lib/irb/cmd/show_source.rb @@ -9,6 +9,9 @@ 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 diff --git a/lib/irb/cmd/step.rb b/lib/irb/cmd/step.rb index dbd59806f8..d3d0f16291 100644 --- a/lib/irb/cmd/step.rb +++ b/lib/irb/cmd/step.rb @@ -6,7 +6,7 @@ module IRB # :stopdoc: module ExtendCommand - class Step < Debug + class Step < DebugCommand def execute(*args) # Run `next` first to move out of binding.irb super(pre_cmds: "next", do_cmds: ["step", *args].join(" ")) diff --git a/lib/irb/cmd/subirb.rb b/lib/irb/cmd/subirb.rb index 4d113c5bd7..43364f1393 100644 --- a/lib/irb/cmd/subirb.rb +++ b/lib/irb/cmd/subirb.rb @@ -30,24 +30,36 @@ module IRB end class IrbCommand < MultiIRBCommand + category "IRB" + description "Start a child IRB." + def execute(*obj) IRB.irb(nil, *obj) end end class Jobs < MultiIRBCommand + category "IRB" + description "List of current sessions." + def execute IRB.JobManager end end class Foreground < MultiIRBCommand + category "IRB" + description "Switches to the session of the given number." + def execute(key) IRB.JobManager.switch(key) end end class Kill < MultiIRBCommand + category "IRB" + description "Kills the session with the given number." + def execute(*keys) IRB.JobManager.kill(*keys) end diff --git a/lib/irb/cmd/whereami.rb b/lib/irb/cmd/whereami.rb index b8c7e047fa..8f56ba073d 100644 --- a/lib/irb/cmd/whereami.rb +++ b/lib/irb/cmd/whereami.rb @@ -7,6 +7,9 @@ module IRB 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 diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb index 802c9aa6dc..9fa2ce5d86 100644 --- a/lib/irb/extend-command.rb +++ b/lib/irb/extend-command.rb @@ -45,14 +45,15 @@ module IRB # :nodoc: [:quit, :irb_exit, OVERRIDE_PRIVATE_ONLY], ] + @EXTEND_COMMANDS = [ [ :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], - [:cwws, NO_OVERRIDE], - [:pwws, NO_OVERRIDE], [:irb_current_working_binding, OVERRIDE_ALL], [:irb_print_working_binding, OVERRIDE_ALL], [:irb_cwb, OVERRIDE_ALL], @@ -60,10 +61,10 @@ module IRB # :nodoc: ], [ :irb_change_workspace, :ChangeWorkspace, "cmd/chws", - [:irb_chws, OVERRIDE_ALL], - [:irb_cws, OVERRIDE_ALL], [: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], @@ -77,16 +78,16 @@ module IRB # :nodoc: ], [ :irb_push_workspace, :PushWorkspace, "cmd/pushws", - [:irb_pushws, OVERRIDE_ALL], [: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", - [:irb_popws, OVERRIDE_ALL], [:popws, NO_OVERRIDE], + [:irb_popws, OVERRIDE_ALL], [:irb_pop_binding, OVERRIDE_ALL], [:irb_popb, OVERRIDE_ALL], [:popb, NO_OVERRIDE], @@ -131,7 +132,7 @@ module IRB # :nodoc: :irb_catch, :Catch, "cmd/catch", ], [ - :irb_next, :Next, "cmd/next", + :irb_next, :Next, "cmd/next" ], [ :irb_delete, :Delete, "cmd/delete", @@ -187,9 +188,41 @@ module IRB # :nodoc: :irb_whereami, :Whereami, "cmd/whereami", [:whereami, NO_OVERRIDE], ], - + [ + :irb_show_cmds, :ShowCmds, "cmd/show_cmds", + [:show_cmds, 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 diff --git a/test/irb/test_cmd.rb b/test/irb/test_cmd.rb index db48e1f1ae..ac0c115339 100644 --- a/test/irb/test_cmd.rb +++ b/test/irb/test_cmd.rb @@ -583,6 +583,16 @@ module TestIRB $bar = nil end + def test_show_cmds + out, err = execute_lines( + "show_cmds\n" + ) + + assert_empty err + assert_match(/List all available commands and their description/, out) + assert_match(/Start the debugger of debug\.gem/, out) + end + class EditTest < CommandTestCase def setup @original_editor = ENV["EDITOR"] -- cgit v1.2.3