irb 1.7.1 → 1.13.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/.document +1 -1
  3. data/Gemfile +10 -1
  4. data/README.md +265 -20
  5. data/Rakefile +13 -10
  6. data/doc/irb/irb.rd.ja +1 -3
  7. data/irb.gemspec +2 -1
  8. data/lib/irb/cmd/nop.rb +3 -52
  9. data/lib/irb/color.rb +4 -2
  10. data/lib/irb/command/backtrace.rb +17 -0
  11. data/lib/irb/command/base.rb +62 -0
  12. data/lib/irb/command/break.rb +17 -0
  13. data/lib/irb/command/catch.rb +17 -0
  14. data/lib/irb/command/chws.rb +40 -0
  15. data/lib/irb/command/context.rb +16 -0
  16. data/lib/irb/{cmd → command}/continue.rb +3 -3
  17. data/lib/irb/command/debug.rb +71 -0
  18. data/lib/irb/{cmd → command}/delete.rb +3 -3
  19. data/lib/irb/command/disable_irb.rb +19 -0
  20. data/lib/irb/command/edit.rb +63 -0
  21. data/lib/irb/command/exit.rb +18 -0
  22. data/lib/irb/{cmd → command}/finish.rb +3 -3
  23. data/lib/irb/command/force_exit.rb +18 -0
  24. data/lib/irb/command/help.rb +83 -0
  25. data/lib/irb/command/history.rb +45 -0
  26. data/lib/irb/command/info.rb +17 -0
  27. data/lib/irb/command/internal_helpers.rb +27 -0
  28. data/lib/irb/{cmd → command}/irb_info.rb +7 -7
  29. data/lib/irb/{cmd → command}/load.rb +23 -8
  30. data/lib/irb/{cmd → command}/ls.rb +42 -19
  31. data/lib/irb/{cmd → command}/measure.rb +18 -17
  32. data/lib/irb/{cmd → command}/next.rb +3 -3
  33. data/lib/irb/command/pushws.rb +65 -0
  34. data/lib/irb/command/show_doc.rb +51 -0
  35. data/lib/irb/command/show_source.rb +74 -0
  36. data/lib/irb/{cmd → command}/step.rb +3 -3
  37. data/lib/irb/command/subirb.rb +123 -0
  38. data/lib/irb/{cmd → command}/whereami.rb +3 -5
  39. data/lib/irb/command.rb +23 -0
  40. data/lib/irb/completion.rb +133 -102
  41. data/lib/irb/context.rb +182 -66
  42. data/lib/irb/debug/ui.rb +103 -0
  43. data/lib/irb/{cmd/debug.rb → debug.rb} +53 -59
  44. data/lib/irb/default_commands.rb +265 -0
  45. data/lib/irb/easter-egg.rb +16 -6
  46. data/lib/irb/ext/change-ws.rb +6 -8
  47. data/lib/irb/ext/{history.rb → eval_history.rb} +7 -7
  48. data/lib/irb/ext/loader.rb +4 -4
  49. data/lib/irb/ext/multi-irb.rb +5 -5
  50. data/lib/irb/ext/tracer.rb +12 -51
  51. data/lib/irb/ext/use-loader.rb +6 -8
  52. data/lib/irb/ext/workspaces.rb +10 -34
  53. data/lib/irb/frame.rb +1 -1
  54. data/lib/irb/help.rb +3 -3
  55. data/lib/irb/helper_method/base.rb +16 -0
  56. data/lib/irb/helper_method/conf.rb +11 -0
  57. data/lib/irb/helper_method.rb +29 -0
  58. data/lib/irb/{ext/save-history.rb → history.rb} +20 -58
  59. data/lib/irb/init.rb +154 -58
  60. data/lib/irb/input-method.rb +238 -203
  61. data/lib/irb/inspector.rb +3 -3
  62. data/lib/irb/lc/error.rb +1 -11
  63. data/lib/irb/lc/help-message +4 -0
  64. data/lib/irb/lc/ja/error.rb +1 -11
  65. data/lib/irb/lc/ja/help-message +13 -0
  66. data/lib/irb/locale.rb +2 -2
  67. data/lib/irb/nesting_parser.rb +13 -3
  68. data/lib/irb/notifier.rb +1 -1
  69. data/lib/irb/output-method.rb +2 -8
  70. data/lib/irb/pager.rb +91 -0
  71. data/lib/irb/ruby-lex.rb +391 -471
  72. data/lib/irb/ruby_logo.aa +43 -0
  73. data/lib/irb/source_finder.rb +139 -0
  74. data/lib/irb/statement.rb +80 -0
  75. data/lib/irb/version.rb +3 -3
  76. data/lib/irb/workspace.rb +24 -4
  77. data/lib/irb/ws-for-case-2.rb +1 -1
  78. data/lib/irb/xmp.rb +3 -3
  79. data/lib/irb.rb +1232 -604
  80. data/man/irb.1 +7 -0
  81. metadata +60 -32
  82. data/lib/irb/cmd/backtrace.rb +0 -21
  83. data/lib/irb/cmd/break.rb +0 -21
  84. data/lib/irb/cmd/catch.rb +0 -21
  85. data/lib/irb/cmd/chws.rb +0 -36
  86. data/lib/irb/cmd/edit.rb +0 -61
  87. data/lib/irb/cmd/help.rb +0 -23
  88. data/lib/irb/cmd/info.rb +0 -21
  89. data/lib/irb/cmd/pushws.rb +0 -45
  90. data/lib/irb/cmd/show_cmds.rb +0 -39
  91. data/lib/irb/cmd/show_doc.rb +0 -48
  92. data/lib/irb/cmd/show_source.rb +0 -113
  93. data/lib/irb/cmd/subirb.rb +0 -66
  94. data/lib/irb/extend-command.rb +0 -356
  95. data/lib/irb/src_encoding.rb +0 -7
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # change-ws.rb -
4
+ # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5
+ #
6
+ require_relative "../ext/change-ws"
7
+
8
+ module IRB
9
+ # :stopdoc:
10
+
11
+ module Command
12
+
13
+ class CurrentWorkingWorkspace < Base
14
+ category "Workspace"
15
+ description "Show the current workspace."
16
+
17
+ def execute(_arg)
18
+ puts "Current workspace: #{irb_context.main}"
19
+ end
20
+ end
21
+
22
+ class ChangeWorkspace < Base
23
+ category "Workspace"
24
+ description "Change the current workspace to an object."
25
+
26
+ def execute(arg)
27
+ if arg.empty?
28
+ irb_context.change_workspace
29
+ else
30
+ obj = eval(arg, irb_context.workspace.binding)
31
+ irb_context.change_workspace(obj)
32
+ end
33
+
34
+ puts "Current workspace: #{irb_context.main}"
35
+ end
36
+ end
37
+ end
38
+
39
+ # :startdoc:
40
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IRB
4
+ module Command
5
+ class Context < Base
6
+ category "IRB"
7
+ description "Displays current configuration."
8
+
9
+ def execute(_arg)
10
+ # This command just displays the configuration.
11
+ # Modifying the configuration is achieved by sending a message to IRB.conf.
12
+ Pager.page_content(IRB.CurrentContext.inspect)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -5,10 +5,10 @@ require_relative "debug"
5
5
  module IRB
6
6
  # :stopdoc:
7
7
 
8
- module ExtendCommand
8
+ module Command
9
9
  class Continue < DebugCommand
10
- def execute(*args)
11
- super(do_cmds: ["continue", *args].join(" "))
10
+ def execute(arg)
11
+ execute_debug_command(do_cmds: "continue #{arg}")
12
12
  end
13
13
  end
14
14
  end
@@ -0,0 +1,71 @@
1
+ require_relative "../debug"
2
+
3
+ module IRB
4
+ # :stopdoc:
5
+
6
+ module Command
7
+ class Debug < Base
8
+ category "Debugging"
9
+ description "Start the debugger of debug.gem."
10
+
11
+ def execute(_arg)
12
+ execute_debug_command
13
+ end
14
+
15
+ def execute_debug_command(pre_cmds: nil, do_cmds: nil)
16
+ pre_cmds = pre_cmds&.rstrip
17
+ do_cmds = do_cmds&.rstrip
18
+
19
+ if irb_context.with_debugger
20
+ # If IRB is already running with a debug session, throw the command and IRB.debug_readline will pass it to the debugger.
21
+ if cmd = pre_cmds || do_cmds
22
+ throw :IRB_EXIT, cmd
23
+ else
24
+ puts "IRB is already running with a debug session."
25
+ return
26
+ end
27
+ else
28
+ # If IRB is not running with a debug session yet, then:
29
+ # 1. Check if the debugging command is run from a `binding.irb` call.
30
+ # 2. If so, try setting up the debug gem.
31
+ # 3. Insert a debug breakpoint at `Irb#debug_break` with the intended command.
32
+ # 4. Exit the current Irb#run call via `throw :IRB_EXIT`.
33
+ # 5. `Irb#debug_break` will be called and trigger the breakpoint, which will run the intended command.
34
+ unless irb_context.from_binding?
35
+ puts "Debugging commands are only available when IRB is started with binding.irb"
36
+ return
37
+ end
38
+
39
+ if IRB.respond_to?(:JobManager)
40
+ warn "Can't start the debugger when IRB is running in a multi-IRB session."
41
+ return
42
+ end
43
+
44
+ unless IRB::Debug.setup(irb_context.irb)
45
+ puts <<~MSG
46
+ You need to install the debug gem before using this command.
47
+ If you use `bundle exec`, please add `gem "debug"` into your Gemfile.
48
+ MSG
49
+ return
50
+ end
51
+
52
+ IRB::Debug.insert_debug_break(pre_cmds: pre_cmds, do_cmds: do_cmds)
53
+
54
+ # exit current Irb#run call
55
+ throw :IRB_EXIT
56
+ end
57
+ end
58
+ end
59
+
60
+ class DebugCommand < Debug
61
+ def self.category
62
+ "Debugging"
63
+ end
64
+
65
+ def self.description
66
+ command_name = self.name.split("::").last.downcase
67
+ "Start the debugger of debug.gem and run its `#{command_name}` command."
68
+ end
69
+ end
70
+ end
71
+ end
@@ -5,10 +5,10 @@ require_relative "debug"
5
5
  module IRB
6
6
  # :stopdoc:
7
7
 
8
- module ExtendCommand
8
+ module Command
9
9
  class Delete < DebugCommand
10
- def execute(*args)
11
- super(pre_cmds: ["delete", *args].join(" "))
10
+ def execute(arg)
11
+ execute_debug_command(pre_cmds: "delete #{arg}")
12
12
  end
13
13
  end
14
14
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IRB
4
+ # :stopdoc:
5
+
6
+ module Command
7
+ class DisableIrb < Base
8
+ category "IRB"
9
+ description "Disable binding.irb."
10
+
11
+ def execute(*)
12
+ ::Binding.define_method(:irb) {}
13
+ IRB.irb_exit
14
+ end
15
+ end
16
+ end
17
+
18
+ # :startdoc:
19
+ end
@@ -0,0 +1,63 @@
1
+ require 'shellwords'
2
+
3
+ require_relative "../color"
4
+ require_relative "../source_finder"
5
+
6
+ module IRB
7
+ # :stopdoc:
8
+
9
+ module Command
10
+ class Edit < Base
11
+ include RubyArgsExtractor
12
+
13
+ category "Misc"
14
+ description 'Open a file or source location.'
15
+ help_message <<~HELP_MESSAGE
16
+ Usage: edit [FILE or constant or method signature]
17
+
18
+ Open a file in the editor specified in #{highlight('ENV["VISUAL"]')} or #{highlight('ENV["EDITOR"]')}
19
+
20
+ - If no arguments are provided, IRB will attempt to open the file the current context was defined in.
21
+ - If FILE is provided, IRB will open the file.
22
+ - If a constant or method signature is provided, IRB will attempt to locate the source file and open it.
23
+
24
+ Examples:
25
+
26
+ edit
27
+ edit foo.rb
28
+ edit Foo
29
+ edit Foo#bar
30
+ HELP_MESSAGE
31
+
32
+ def execute(arg)
33
+ # Accept string literal for backward compatibility
34
+ path = unwrap_string_literal(arg)
35
+
36
+ if path.nil?
37
+ path = @irb_context.irb_path
38
+ elsif !File.exist?(path)
39
+ source = SourceFinder.new(@irb_context).find_source(path)
40
+
41
+ if source&.file_exist? && !source.binary_file?
42
+ path = source.file
43
+ end
44
+ end
45
+
46
+ unless File.exist?(path)
47
+ puts "Can not find file: #{path}"
48
+ return
49
+ end
50
+
51
+ if editor = (ENV['VISUAL'] || ENV['EDITOR'])
52
+ puts "command: '#{editor}'"
53
+ puts " path: #{path}"
54
+ system(*Shellwords.split(editor), path)
55
+ else
56
+ puts "Can not find editor setting: ENV['VISUAL'] or ENV['EDITOR']"
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ # :startdoc:
63
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IRB
4
+ # :stopdoc:
5
+
6
+ module Command
7
+ class Exit < Base
8
+ category "IRB"
9
+ description "Exit the current irb session."
10
+
11
+ def execute(_arg)
12
+ IRB.irb_exit
13
+ end
14
+ end
15
+ end
16
+
17
+ # :startdoc:
18
+ end
@@ -5,10 +5,10 @@ require_relative "debug"
5
5
  module IRB
6
6
  # :stopdoc:
7
7
 
8
- module ExtendCommand
8
+ module Command
9
9
  class Finish < DebugCommand
10
- def execute(*args)
11
- super(do_cmds: ["finish", *args].join(" "))
10
+ def execute(arg)
11
+ execute_debug_command(do_cmds: "finish #{arg}")
12
12
  end
13
13
  end
14
14
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IRB
4
+ # :stopdoc:
5
+
6
+ module Command
7
+ class ForceExit < Base
8
+ category "IRB"
9
+ description "Exit the current process."
10
+
11
+ def execute(_arg)
12
+ throw :IRB_EXIT, true
13
+ end
14
+ end
15
+ end
16
+
17
+ # :startdoc:
18
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IRB
4
+ module Command
5
+ class Help < Base
6
+ category "Help"
7
+ description "List all available commands. Use `help <command>` to get information about a specific command."
8
+
9
+ def execute(command_name)
10
+ content =
11
+ if command_name.empty?
12
+ help_message
13
+ else
14
+ if command_class = Command.load_command(command_name)
15
+ command_class.help_message || command_class.description
16
+ else
17
+ "Can't find command `#{command_name}`. Please check the command name and try again.\n\n"
18
+ end
19
+ end
20
+ Pager.page_content(content)
21
+ end
22
+
23
+ private
24
+
25
+ def help_message
26
+ commands_info = IRB::Command.all_commands_info
27
+ helper_methods_info = IRB::HelperMethod.all_helper_methods_info
28
+ commands_grouped_by_categories = commands_info.group_by { |cmd| cmd[:category] }
29
+ commands_grouped_by_categories["Helper methods"] = helper_methods_info
30
+
31
+ if irb_context.with_debugger
32
+ # Remove the original "Debugging" category
33
+ commands_grouped_by_categories.delete("Debugging")
34
+ end
35
+
36
+ longest_cmd_name_length = commands_info.map { |c| c[:display_name].length }.max
37
+
38
+ output = StringIO.new
39
+
40
+ help_cmds = commands_grouped_by_categories.delete("Help")
41
+ no_category_cmds = commands_grouped_by_categories.delete("No category")
42
+ aliases = irb_context.instance_variable_get(:@user_aliases).map do |alias_name, target|
43
+ { display_name: alias_name, description: "Alias for `#{target}`" }
44
+ end
45
+
46
+ # Display help commands first
47
+ add_category_to_output("Help", help_cmds, output, longest_cmd_name_length)
48
+
49
+ # Display the rest of the commands grouped by categories
50
+ commands_grouped_by_categories.each do |category, cmds|
51
+ add_category_to_output(category, cmds, output, longest_cmd_name_length)
52
+ end
53
+
54
+ # Display commands without a category
55
+ if no_category_cmds
56
+ add_category_to_output("No category", no_category_cmds, output, longest_cmd_name_length)
57
+ end
58
+
59
+ # Display aliases
60
+ add_category_to_output("Aliases", aliases, output, longest_cmd_name_length)
61
+
62
+ # Append the debugger help at the end
63
+ if irb_context.with_debugger
64
+ # Add "Debugging (from debug.gem)" category as title
65
+ add_category_to_output("Debugging (from debug.gem)", [], output, longest_cmd_name_length)
66
+ output.puts DEBUGGER__.help
67
+ end
68
+
69
+ output.string
70
+ end
71
+
72
+ def add_category_to_output(category, cmds, output, longest_cmd_name_length)
73
+ output.puts Color.colorize(category, [:BOLD])
74
+
75
+ cmds.each do |cmd|
76
+ output.puts " #{cmd[:display_name].to_s.ljust(longest_cmd_name_length)} #{cmd[:description]}"
77
+ end
78
+
79
+ output.puts
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+
5
+ require_relative "../pager"
6
+
7
+ module IRB
8
+ # :stopdoc:
9
+
10
+ module Command
11
+ class History < Base
12
+ category "IRB"
13
+ description "Shows the input history. `-g [query]` or `-G [query]` allows you to filter the output."
14
+
15
+ def execute(arg)
16
+
17
+ if (match = arg&.match(/(-g|-G)\s+(?<grep>.+)\s*\n\z/))
18
+ grep = Regexp.new(match[:grep])
19
+ end
20
+
21
+ formatted_inputs = irb_context.io.class::HISTORY.each_with_index.reverse_each.filter_map do |input, index|
22
+ next if grep && !input.match?(grep)
23
+
24
+ header = "#{index}: "
25
+
26
+ first_line, *other_lines = input.split("\n")
27
+ first_line = "#{header}#{first_line}"
28
+
29
+ truncated_lines = other_lines.slice!(1..) # Show 1 additional line (2 total)
30
+ other_lines << "..." if truncated_lines&.any?
31
+
32
+ other_lines.map! do |line|
33
+ " " * header.length + line
34
+ end
35
+
36
+ [first_line, *other_lines].join("\n") + "\n"
37
+ end
38
+
39
+ Pager.page_content(formatted_inputs.join)
40
+ end
41
+ end
42
+ end
43
+
44
+ # :startdoc:
45
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "debug"
4
+
5
+ module IRB
6
+ # :stopdoc:
7
+
8
+ module Command
9
+ class Info < DebugCommand
10
+ def execute(arg)
11
+ execute_debug_command(pre_cmds: "info #{arg}")
12
+ end
13
+ end
14
+ end
15
+
16
+ # :startdoc:
17
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IRB
4
+ module Command
5
+ # Internal use only, for default command's backward compatibility.
6
+ module RubyArgsExtractor # :nodoc:
7
+ def unwrap_string_literal(str)
8
+ return if str.empty?
9
+
10
+ sexp = Ripper.sexp(str)
11
+ if sexp && sexp.size == 2 && sexp.last&.first&.first == :string_literal
12
+ @irb_context.workspace.binding.eval(str).to_s
13
+ else
14
+ str
15
+ end
16
+ end
17
+
18
+ def ruby_args(arg)
19
+ # Use throw and catch to handle arg that includes `;`
20
+ # For example: "1, kw: (2; 3); 4" will be parsed to [[1], { kw: 3 }]
21
+ catch(:EXTRACT_RUBY_ARGS) do
22
+ @irb_context.workspace.binding.eval "IRB::Command.extract_ruby_args #{arg}"
23
+ end || [[], {}]
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,20 +1,20 @@
1
- # frozen_string_literal: false
2
-
3
- require_relative "nop"
1
+ # frozen_string_literal: true
4
2
 
5
3
  module IRB
6
4
  # :stopdoc:
7
5
 
8
- module ExtendCommand
9
- class IrbInfo < Nop
6
+ module Command
7
+ class IrbInfo < Base
10
8
  category "IRB"
11
9
  description "Show information about IRB."
12
10
 
13
- def execute
11
+ def execute(_arg)
14
12
  str = "Ruby version: #{RUBY_VERSION}\n"
15
13
  str += "IRB version: #{IRB.version}\n"
16
14
  str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n"
17
- str += ".irbrc path: #{IRB.rc_file}\n" if File.exist?(IRB.rc_file)
15
+ str += "Completion: #{IRB.CurrentContext.io.respond_to?(:completion_info) ? IRB.CurrentContext.io.completion_info : 'off'}\n"
16
+ rc_files = IRB.irbrc_files
17
+ str += ".irbrc paths: #{rc_files.join(", ")}\n" if rc_files.any?
18
18
  str += "RUBY_PLATFORM: #{RUBY_PLATFORM}\n"
19
19
  str += "LANG env: #{ENV["LANG"]}\n" if ENV["LANG"] && !ENV["LANG"].empty?
20
20
  str += "LC_ALL env: #{ENV["LC_ALL"]}\n" if ENV["LC_ALL"] && !ENV["LC_ALL"].empty?
@@ -1,17 +1,16 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
2
  #
3
3
  # load.rb -
4
4
  # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5
5
  #
6
-
7
- require_relative "nop"
8
6
  require_relative "../ext/loader"
9
7
 
10
8
  module IRB
11
9
  # :stopdoc:
12
10
 
13
- module ExtendCommand
14
- class LoaderCommand < Nop
11
+ module Command
12
+ class LoaderCommand < Base
13
+ include RubyArgsExtractor
15
14
  include IrbLoader
16
15
 
17
16
  def raise_cmd_argument_error
@@ -23,7 +22,12 @@ module IRB
23
22
  category "IRB"
24
23
  description "Load a Ruby file."
25
24
 
26
- def execute(file_name = nil, priv = nil)
25
+ def execute(arg)
26
+ args, kwargs = ruby_args(arg)
27
+ execute_internal(*args, **kwargs)
28
+ end
29
+
30
+ def execute_internal(file_name = nil, priv = nil)
27
31
  raise_cmd_argument_error unless file_name
28
32
  irb_load(file_name, priv)
29
33
  end
@@ -32,7 +36,13 @@ module IRB
32
36
  class Require < LoaderCommand
33
37
  category "IRB"
34
38
  description "Require a Ruby file."
35
- def execute(file_name = nil)
39
+
40
+ def execute(arg)
41
+ args, kwargs = ruby_args(arg)
42
+ execute_internal(*args, **kwargs)
43
+ end
44
+
45
+ def execute_internal(file_name = nil)
36
46
  raise_cmd_argument_error unless file_name
37
47
 
38
48
  rex = Regexp.new("#{Regexp.quote(file_name)}(\.o|\.rb)?")
@@ -65,7 +75,12 @@ module IRB
65
75
  category "IRB"
66
76
  description "Loads a given file in the current session."
67
77
 
68
- def execute(file_name = nil)
78
+ def execute(arg)
79
+ args, kwargs = ruby_args(arg)
80
+ execute_internal(*args, **kwargs)
81
+ end
82
+
83
+ def execute_internal(file_name = nil)
69
84
  raise_cmd_argument_error unless file_name
70
85
 
71
86
  source_file(file_name)
@@ -1,39 +1,57 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "reline"
4
- require_relative "nop"
4
+ require "stringio"
5
+
6
+ require_relative "../pager"
5
7
  require_relative "../color"
6
8
 
7
9
  module IRB
8
10
  # :stopdoc:
9
11
 
10
- module ExtendCommand
11
- class Ls < Nop
12
+ module Command
13
+ class Ls < Base
14
+ include RubyArgsExtractor
15
+
12
16
  category "Context"
13
- description "Show methods, constants, and variables. `-g [query]` or `-G [query]` allows you to filter out the output."
17
+ description "Show methods, constants, and variables."
18
+
19
+ help_message <<~HELP_MESSAGE
20
+ Usage: ls [obj] [-g [query]]
14
21
 
15
- def self.transform_args(args)
16
- if match = args&.match(/\A(?<args>.+\s|)(-g|-G)\s+(?<grep>[^\s]+)\s*\n\z/)
17
- args = match[:args]
18
- "#{args}#{',' unless args.chomp.empty?} grep: /#{match[:grep]}/"
22
+ -g [query] Filter the output with a query.
23
+ HELP_MESSAGE
24
+
25
+ def execute(arg)
26
+ if match = arg.match(/\A(?<target>.+\s|)(-g|-G)\s+(?<grep>.+)$/)
27
+ if match[:target].empty?
28
+ use_main = true
29
+ else
30
+ obj = @irb_context.workspace.binding.eval(match[:target])
31
+ end
32
+ grep = Regexp.new(match[:grep])
19
33
  else
20
- args
34
+ args, kwargs = ruby_args(arg)
35
+ use_main = args.empty?
36
+ obj = args.first
37
+ grep = kwargs[:grep]
38
+ end
39
+
40
+ if use_main
41
+ obj = irb_context.workspace.main
42
+ locals = irb_context.workspace.binding.local_variables
21
43
  end
22
- end
23
44
 
24
- def execute(*arg, grep: nil)
25
45
  o = Output.new(grep: grep)
26
46
 
27
- obj = arg.empty? ? irb_context.workspace.main : arg.first
28
- locals = arg.empty? ? irb_context.workspace.binding.local_variables : []
29
47
  klass = (obj.class == Class || obj.class == Module ? obj : obj.class)
30
48
 
31
49
  o.dump("constants", obj.constants) if obj.respond_to?(:constants)
32
50
  dump_methods(o, klass, obj)
33
51
  o.dump("instance variables", obj.instance_variables)
34
52
  o.dump("class variables", klass.class_variables)
35
- o.dump("locals", locals)
36
- nil
53
+ o.dump("locals", locals) if locals
54
+ o.print_result
37
55
  end
38
56
 
39
57
  def dump_methods(o, klass, obj)
@@ -77,6 +95,11 @@ module IRB
77
95
  def initialize(grep: nil)
78
96
  @grep = grep
79
97
  @line_width = screen_width - MARGIN.length # right padding
98
+ @io = StringIO.new
99
+ end
100
+
101
+ def print_result
102
+ Pager.page_content(@io.string)
80
103
  end
81
104
 
82
105
  def dump(name, strs)
@@ -85,12 +108,12 @@ module IRB
85
108
  return if strs.empty?
86
109
 
87
110
  # Attempt a single line
88
- print "#{Color.colorize(name, [:BOLD, :BLUE])}: "
111
+ @io.print "#{Color.colorize(name, [:BOLD, :BLUE])}: "
89
112
  if fits_on_line?(strs, cols: strs.size, offset: "#{name}: ".length)
90
- puts strs.join(MARGIN)
113
+ @io.puts strs.join(MARGIN)
91
114
  return
92
115
  end
93
- puts
116
+ @io.puts
94
117
 
95
118
  # Dump with the largest # of columns that fits on a line
96
119
  cols = strs.size
@@ -99,7 +122,7 @@ module IRB
99
122
  end
100
123
  widths = col_widths(strs, cols: cols)
101
124
  strs.each_slice(cols) do |ss|
102
- puts ss.map.with_index { |s, i| "#{MARGIN}%-#{widths[i]}s" % s }.join
125
+ @io.puts ss.map.with_index { |s, i| "#{MARGIN}%-#{widths[i]}s" % s }.join
103
126
  end
104
127
  end
105
128