[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
This commit is contained in:
Stan Lo 2022-12-08 19:10:19 +00:00 committed by git
parent 2cea8e014d
commit 3956bb859c
25 changed files with 182 additions and 17 deletions

View File

@ -6,7 +6,7 @@ module IRB
# :stopdoc:
module ExtendCommand
class Backtrace < Debug
class Backtrace < DebugCommand
def self.transform_args(args)
args&.dump
end

View File

@ -6,7 +6,7 @@ module IRB
# :stopdoc:
module ExtendCommand
class Break < Debug
class Break < DebugCommand
def self.transform_args(args)
args&.dump
end

View File

@ -6,7 +6,7 @@ module IRB
# :stopdoc:
module ExtendCommand
class Catch < Debug
class Catch < DebugCommand
def self.transform_args(args)
args&.dump
end

View File

@ -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

View File

@ -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

View File

@ -5,6 +5,9 @@ module IRB
module ExtendCommand
class Debug < Nop
category "Debugging"
description "Start the debugger of debug.gem."
BINDING_IRB_FRAME_REGEXPS = [
'<internal:prelude>',
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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([])

View File

@ -6,7 +6,7 @@ module IRB
# :stopdoc:
module ExtendCommand
class Info < Debug
class Info < DebugCommand
def self.transform_args(args)
args&.dump
end

View File

@ -7,6 +7,9 @@ module IRB
module ExtendCommand
class IrbInfo < Nop
category "IRB"
description "Show information about IRB."
def execute
Class.new {
def inspect

View File

@ -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

View File

@ -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(?<args>.+\s|)(-g|-G)\s+(?<grep>[^\s]+)\s*\n\z/)
args = match[:args]

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

39
lib/irb/cmd/show_cmds.rb Normal file
View File

@ -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

View File

@ -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

View File

@ -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(" "))

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"]