irb 1.4.3 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 025d4dccc426fe09dc77b79d7efb8157abc559b89a7455249e5d7e9b2ff1562c
4
- data.tar.gz: fd2cffe5c013c56a5d454ce81331ac1c24b6826c81db6ae6f2240e6e2d5f8c06
3
+ metadata.gz: e7dc14606c6de90f8f56048a7ec6f8ca2bc83a8d4e0aa50d60dbe84141389601
4
+ data.tar.gz: bca1a92ee2088e483073d48dd9e0b3cdbf9b8d67768ed3fd922bb7e91da68039
5
5
  SHA512:
6
- metadata.gz: 582d9176beb3f3b9a016f6d44b90b9d781066230f4c64dec93ba5d645ae54df8f7f6f1a5dcaab20899390cd048050eaed6db9b45fdcf44c55a57098013855aa9
7
- data.tar.gz: ab9b30b770c677778cc4871b249a0967177805e543bd8576077099be7d3a5d93e2ef33c35362326cd7f889cd1f855d956c2cb9bd992560029e152c6293720a94
6
+ metadata.gz: b44c813a4fc0010423d0e7ac189dc264ea92587db1b05e16e6c8ec49ff20bc2b4c18b5cfa454543b7d894be0b6610c96a3bf17b85b31d9083d962665063c8a85
7
+ data.tar.gz: af7f077aa6b2c67ccbe67209a35e622bff087a28615b3086a0511188da1b09d2284b662d659d3b308bf251488547aeaecf9da2b29454c2031d52c0e902ba7889
data/Gemfile CHANGED
@@ -11,4 +11,5 @@ group :development do
11
11
  gem "stackprof" if is_unix && !is_truffleruby
12
12
  gem "test-unit"
13
13
  gem "reline", github: "ruby/reline" if ENV["WITH_LATEST_RELINE"] == "true"
14
+ gem "debug"
14
15
  end
data/README.md CHANGED
@@ -40,6 +40,59 @@ irb(main):006:1> end
40
40
 
41
41
  The Readline extension module can be used with irb. Use of Readline is default if it's installed.
42
42
 
43
+ ## Commands
44
+
45
+ The following commands are available on IRB.
46
+
47
+ * `cwws`
48
+ * Show the current workspace.
49
+ * `cb`, `cws`, `chws`
50
+ * Change the current workspace to an object.
51
+ * `bindings`, `workspaces`
52
+ * Show workspaces.
53
+ * `edit`
54
+ * Open a file with the editor command defined with `ENV["EDITOR"]`
55
+ * `edit` - opens the file the current context belongs to (if applicable)
56
+ * `edit foo.rb` - opens `foo.rb`
57
+ * `edit Foo` - opens the location of `Foo`
58
+ * `edit Foo.bar` - opens the location of `Foo.bar`
59
+ * `edit Foo#bar` - opens the location of `Foo#bar`
60
+ * `pushb`, `pushws`
61
+ * Push an object to the workspace stack.
62
+ * `popb`, `popws`
63
+ * Pop a workspace from the workspace stack.
64
+ * `load`
65
+ * Load a Ruby file.
66
+ * `require`
67
+ * Require a Ruby file.
68
+ * `source`
69
+ * Loads a given file in the current session.
70
+ * `irb`
71
+ * Start a child IRB.
72
+ * `jobs`
73
+ * List of current sessions.
74
+ * `fg`
75
+ * Switches to the session of the given number.
76
+ * `kill`
77
+ * Kills the session with the given number.
78
+ * `help`
79
+ * Enter the mode to look up RI documents.
80
+ * `irb_info`
81
+ * Show information about IRB.
82
+ * `ls`
83
+ * Show methods, constants, and variables.
84
+ `-g [query]` or `-G [query]` allows you to filter out the output.
85
+ * `measure`
86
+ * `measure` enables the mode to measure processing time. `measure :off` disables it.
87
+ * `$`, `show_source`
88
+ * Show the source code of a given method or constant.
89
+ * `@`, `whereami`
90
+ * Show the source code around binding.irb again.
91
+ * `debug`
92
+ * Start the debugger of debug.gem.
93
+ * `break`, `delete`, `next`, `step`, `continue`, `finish`, `backtrace`, `info`, `catch`
94
+ * Start the debugger of debug.gem and run the command on it.
95
+
43
96
  ## Documentation
44
97
 
45
98
  https://docs.ruby-lang.org/en/master/IRB.html
@@ -54,6 +107,13 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
54
107
 
55
108
  Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/irb.
56
109
 
110
+ ## Releasing
111
+
112
+ ```
113
+ rake release
114
+ gh release create vX.Y.Z --generate-notes
115
+ ```
116
+
57
117
  ## License
58
118
 
59
119
  The gem is available as open source under the terms of the [2-Clause BSD License](https://opensource.org/licenses/BSD-2-Clause).
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "debug"
4
+
5
+ module IRB
6
+ # :stopdoc:
7
+
8
+ module ExtendCommand
9
+ class Backtrace < Debug
10
+ def self.transform_args(args)
11
+ args&.dump
12
+ end
13
+
14
+ def execute(*args)
15
+ super(pre_cmds: ["backtrace", *args].join(" "))
16
+ end
17
+ end
18
+ end
19
+
20
+ # :startdoc:
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "debug"
4
+
5
+ module IRB
6
+ # :stopdoc:
7
+
8
+ module ExtendCommand
9
+ class Break < Debug
10
+ def self.transform_args(args)
11
+ args&.dump
12
+ end
13
+
14
+ def execute(args = nil)
15
+ super(pre_cmds: "break #{args}")
16
+ end
17
+ end
18
+ end
19
+
20
+ # :startdoc:
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "debug"
4
+
5
+ module IRB
6
+ # :stopdoc:
7
+
8
+ module ExtendCommand
9
+ class Catch < Debug
10
+ def self.transform_args(args)
11
+ args&.dump
12
+ end
13
+
14
+ def execute(*args)
15
+ super(pre_cmds: ["catch", *args].join(" "))
16
+ end
17
+ end
18
+ end
19
+
20
+ # :startdoc:
21
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "debug"
4
+
5
+ module IRB
6
+ # :stopdoc:
7
+
8
+ module ExtendCommand
9
+ class Continue < Debug
10
+ def execute(*args)
11
+ super(do_cmds: ["continue", *args].join(" "))
12
+ end
13
+ end
14
+ end
15
+
16
+ # :startdoc:
17
+ end
@@ -0,0 +1,112 @@
1
+ require_relative "nop"
2
+
3
+ module IRB
4
+ # :stopdoc:
5
+
6
+ module ExtendCommand
7
+ class Debug < Nop
8
+ BINDING_IRB_FRAME_REGEXPS = [
9
+ '<internal:prelude>',
10
+ binding.method(:irb).source_location.first,
11
+ ].map { |file| /\A#{Regexp.escape(file)}:\d+:in `irb'\z/ }
12
+ IRB_DIR = File.expand_path('..', __dir__)
13
+
14
+ def execute(pre_cmds: nil, do_cmds: nil)
15
+ unless binding_irb?
16
+ puts "`debug` command is only available when IRB is started with binding.irb"
17
+ return
18
+ end
19
+
20
+ unless setup_debugger
21
+ puts <<~MSG
22
+ You need to install the debug gem before using this command.
23
+ If you use `bundle exec`, please add `gem "debug"` into your Gemfile.
24
+ MSG
25
+ return
26
+ end
27
+
28
+ options = { oneshot: true, hook_call: false }
29
+ if pre_cmds || do_cmds
30
+ options[:command] = ['irb', pre_cmds, do_cmds]
31
+ end
32
+ if DEBUGGER__::LineBreakpoint.instance_method(:initialize).parameters.include?([:key, :skip_src])
33
+ options[:skip_src] = true
34
+ end
35
+
36
+ # To make debugger commands like `next` or `continue` work without asking
37
+ # the user to quit IRB after that, we need to exit IRB first and then hit
38
+ # a TracePoint on #debug_break.
39
+ file, lineno = IRB::Irb.instance_method(:debug_break).source_location
40
+ DEBUGGER__::SESSION.add_line_breakpoint(file, lineno + 1, **options)
41
+ # exit current Irb#run call
42
+ throw :IRB_EXIT
43
+ end
44
+
45
+ private
46
+
47
+ def binding_irb?
48
+ caller.any? do |frame|
49
+ BINDING_IRB_FRAME_REGEXPS.any? do |regexp|
50
+ frame.match?(regexp)
51
+ end
52
+ end
53
+ end
54
+
55
+ def setup_debugger
56
+ unless defined?(DEBUGGER__::SESSION)
57
+ begin
58
+ require "debug/session"
59
+ rescue LoadError # debug.gem is not written in Gemfile
60
+ return false unless load_bundled_debug_gem
61
+ end
62
+ DEBUGGER__.start(nonstop: true)
63
+ end
64
+
65
+ unless DEBUGGER__.respond_to?(:capture_frames_without_irb)
66
+ DEBUGGER__.singleton_class.send(:alias_method, :capture_frames_without_irb, :capture_frames)
67
+
68
+ def DEBUGGER__.capture_frames(*args)
69
+ frames = capture_frames_without_irb(*args)
70
+ frames.reject! do |frame|
71
+ frame.realpath&.start_with?(IRB_DIR) || frame.path == "<internal:prelude>"
72
+ end
73
+ frames
74
+ end
75
+ end
76
+
77
+ true
78
+ end
79
+
80
+ # This is used when debug.gem is not written in Gemfile. Even if it's not
81
+ # installed by `bundle install`, debug.gem is installed by default because
82
+ # it's a bundled gem. This method tries to activate and load that.
83
+ def load_bundled_debug_gem
84
+ # Discover latest debug.gem under GEM_PATH
85
+ debug_gem = Gem.paths.path.flat_map { |path| Dir.glob("#{path}/gems/debug-*") }.select do |path|
86
+ File.basename(path).match?(/\Adebug-\d+\.\d+\.\d+(\w+)?\z/)
87
+ end.sort_by do |path|
88
+ Gem::Version.new(File.basename(path).delete_prefix('debug-'))
89
+ end.last
90
+ return false unless debug_gem
91
+
92
+ # Discover debug/debug.so under extensions for Ruby 3.2+
93
+ debug_so = Gem.paths.path.flat_map do |path|
94
+ Dir.glob("#{path}/extensions/**/#{File.basename(debug_gem)}/debug/debug.so")
95
+ end.first
96
+
97
+ # Attempt to forcibly load the bundled gem
98
+ if debug_so
99
+ $LOAD_PATH << debug_so.delete_suffix('/debug/debug.so')
100
+ end
101
+ $LOAD_PATH << "#{debug_gem}/lib"
102
+ begin
103
+ require "debug/session"
104
+ puts "Loaded #{File.basename(debug_gem)}"
105
+ true
106
+ rescue LoadError
107
+ false
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "debug"
4
+
5
+ module IRB
6
+ # :stopdoc:
7
+
8
+ module ExtendCommand
9
+ class Delete < Debug
10
+ def execute(*args)
11
+ super(pre_cmds: ["delete", *args].join(" "))
12
+ end
13
+ end
14
+ end
15
+
16
+ # :startdoc:
17
+ end
@@ -0,0 +1,65 @@
1
+ require 'shellwords'
2
+ require_relative "nop"
3
+
4
+ module IRB
5
+ # :stopdoc:
6
+
7
+ module ExtendCommand
8
+ class Edit < Nop
9
+ class << self
10
+ def transform_args(args)
11
+ # Return a string literal as is for backward compatibility
12
+ if args.nil? || args.empty? || string_literal?(args)
13
+ args
14
+ else # Otherwise, consider the input as a String for convenience
15
+ args.strip.dump
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def string_literal?(args)
22
+ sexp = Ripper.sexp(args)
23
+ sexp && sexp.size == 2 && sexp.last&.first&.first == :string_literal
24
+ end
25
+ end
26
+
27
+ def execute(*args)
28
+ path = args.first
29
+
30
+ if path.nil? && (irb_path = @irb_context.irb_path)
31
+ path = irb_path
32
+ end
33
+
34
+ if !File.exist?(path)
35
+ require_relative "show_source"
36
+
37
+ source =
38
+ begin
39
+ ShowSource.find_source(path, @irb_context)
40
+ rescue NameError
41
+ # if user enters a path that doesn't exist, it'll cause NameError when passed here because find_source would try to evaluate it as well
42
+ # in this case, we should just ignore the error
43
+ end
44
+
45
+ if source && File.exist?(source.file)
46
+ path = source.file
47
+ else
48
+ puts "Can not find file: #{path}"
49
+ return
50
+ end
51
+ end
52
+
53
+ if editor = ENV['EDITOR']
54
+ puts "command: '#{editor}'"
55
+ puts " path: #{path}"
56
+ system(*Shellwords.split(editor), path)
57
+ else
58
+ puts "Can not find editor setting: ENV['EDITOR']"
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ # :startdoc:
65
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "debug"
4
+
5
+ module IRB
6
+ # :stopdoc:
7
+
8
+ module ExtendCommand
9
+ class Finish < Debug
10
+ def execute(*args)
11
+ super(do_cmds: ["finish", *args].join(" "))
12
+ end
13
+ end
14
+ end
15
+
16
+ # :startdoc:
17
+ end
data/lib/irb/cmd/info.rb CHANGED
@@ -1,31 +1,18 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
2
 
3
- require_relative "nop"
3
+ require_relative "debug"
4
4
 
5
5
  module IRB
6
6
  # :stopdoc:
7
7
 
8
8
  module ExtendCommand
9
- class Info < Nop
10
- def execute
11
- Class.new {
12
- def inspect
13
- str = "Ruby version: #{RUBY_VERSION}\n"
14
- str += "IRB version: #{IRB.version}\n"
15
- str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n"
16
- str += ".irbrc path: #{IRB.rc_file}\n" if File.exist?(IRB.rc_file)
17
- str += "RUBY_PLATFORM: #{RUBY_PLATFORM}\n"
18
- str += "LANG env: #{ENV["LANG"]}\n" if ENV["LANG"] && !ENV["LANG"].empty?
19
- str += "LC_ALL env: #{ENV["LC_ALL"]}\n" if ENV["LC_ALL"] && !ENV["LC_ALL"].empty?
20
- str += "East Asian Ambiguous Width: #{Reline.ambiguous_width.inspect}\n"
21
- if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
22
- codepage = `chcp`.b.sub(/.*: (\d+)\n/, '\1')
23
- str += "Code page: #{codepage}\n"
24
- end
25
- str
26
- end
27
- alias_method :to_s, :inspect
28
- }.new
9
+ class Info < Debug
10
+ def self.transform_args(args)
11
+ args&.dump
12
+ end
13
+
14
+ def execute(*args)
15
+ super(pre_cmds: ["info", *args].join(" "))
29
16
  end
30
17
  end
31
18
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: false
2
+
3
+ require_relative "nop"
4
+
5
+ module IRB
6
+ # :stopdoc:
7
+
8
+ module ExtendCommand
9
+ class IrbInfo < Nop
10
+ def execute
11
+ Class.new {
12
+ def inspect
13
+ str = "Ruby version: #{RUBY_VERSION}\n"
14
+ str += "IRB version: #{IRB.version}\n"
15
+ str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n"
16
+ str += ".irbrc path: #{IRB.rc_file}\n" if File.exist?(IRB.rc_file)
17
+ str += "RUBY_PLATFORM: #{RUBY_PLATFORM}\n"
18
+ str += "LANG env: #{ENV["LANG"]}\n" if ENV["LANG"] && !ENV["LANG"].empty?
19
+ str += "LC_ALL env: #{ENV["LC_ALL"]}\n" if ENV["LC_ALL"] && !ENV["LC_ALL"].empty?
20
+ str += "East Asian Ambiguous Width: #{Reline.ambiguous_width.inspect}\n"
21
+ if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
22
+ codepage = `chcp`.b.sub(/.*: (\d+)\n/, '\1')
23
+ str += "Code page: #{codepage}\n"
24
+ end
25
+ str
26
+ end
27
+ alias_method :to_s, :inspect
28
+ }.new
29
+ end
30
+ end
31
+ end
32
+
33
+ # :startdoc:
34
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "debug"
4
+
5
+ module IRB
6
+ # :stopdoc:
7
+
8
+ module ExtendCommand
9
+ class Next < Debug
10
+ def execute(*args)
11
+ super(do_cmds: ["next", *args].join(" "))
12
+ end
13
+ end
14
+ end
15
+
16
+ # :startdoc:
17
+ end
@@ -19,8 +19,50 @@ module IRB
19
19
  end
20
20
  end
21
21
 
22
+ def find_source(str, irb_context)
23
+ case str
24
+ when /\A[A-Z]\w*(::[A-Z]\w*)*\z/ # Const::Name
25
+ eval(str, irb_context.workspace.binding) # trigger autoload
26
+ base = irb_context.workspace.binding.receiver.yield_self { |r| r.is_a?(Module) ? r : Object }
27
+ file, line = base.const_source_location(str) if base.respond_to?(:const_source_location) # Ruby 2.7+
28
+ when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method
29
+ owner = eval(Regexp.last_match[:owner], irb_context.workspace.binding)
30
+ method = Regexp.last_match[:method]
31
+ if owner.respond_to?(:instance_method) && owner.instance_methods.include?(method.to_sym)
32
+ file, line = owner.instance_method(method).source_location
33
+ end
34
+ when /\A((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\z/ # method, receiver.method, receiver::method
35
+ receiver = eval(Regexp.last_match[:receiver] || 'self', irb_context.workspace.binding)
36
+ method = Regexp.last_match[:method]
37
+ file, line = receiver.method(method).source_location if receiver.respond_to?(method)
38
+ end
39
+ if file && line
40
+ Source.new(file: file, first_line: line, last_line: find_end(file, line))
41
+ end
42
+ end
43
+
22
44
  private
23
45
 
46
+ def find_end(file, first_line)
47
+ return first_line unless File.exist?(file)
48
+ lex = RubyLex.new
49
+ lines = File.read(file).lines[(first_line - 1)..-1]
50
+ tokens = RubyLex.ripper_lex_without_warning(lines.join)
51
+ prev_tokens = []
52
+
53
+ # chunk with line number
54
+ tokens.chunk { |tok| tok.pos[0] }.each do |lnum, chunk|
55
+ code = lines[0..lnum].join
56
+ prev_tokens.concat chunk
57
+ continue = lex.process_continue(prev_tokens)
58
+ code_block_open = lex.check_code_block(code, prev_tokens)
59
+ if !continue && !code_block_open
60
+ return first_line + lnum
61
+ end
62
+ end
63
+ first_line
64
+ end
65
+
24
66
  def string_literal?(args)
25
67
  sexp = Ripper.sexp(args)
26
68
  sexp && sexp.size == 2 && sexp.last&.first&.first == :string_literal
@@ -32,7 +74,8 @@ module IRB
32
74
  puts "Error: Expected a string but got #{str.inspect}"
33
75
  return
34
76
  end
35
- source = find_source(str)
77
+
78
+ source = self.class.find_source(str, @irb_context)
36
79
  if source && File.exist?(source.file)
37
80
  show_source(source)
38
81
  else
@@ -53,48 +96,6 @@ module IRB
53
96
  puts
54
97
  end
55
98
 
56
- def find_source(str)
57
- case str
58
- when /\A[A-Z]\w*(::[A-Z]\w*)*\z/ # Const::Name
59
- eval(str, irb_context.workspace.binding) # trigger autoload
60
- base = irb_context.workspace.binding.receiver.yield_self { |r| r.is_a?(Module) ? r : Object }
61
- file, line = base.const_source_location(str) if base.respond_to?(:const_source_location) # Ruby 2.7+
62
- when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method
63
- owner = eval(Regexp.last_match[:owner], irb_context.workspace.binding)
64
- method = Regexp.last_match[:method]
65
- if owner.respond_to?(:instance_method) && owner.instance_methods.include?(method.to_sym)
66
- file, line = owner.instance_method(method).source_location
67
- end
68
- when /\A((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\z/ # method, receiver.method, receiver::method
69
- receiver = eval(Regexp.last_match[:receiver] || 'self', irb_context.workspace.binding)
70
- method = Regexp.last_match[:method]
71
- file, line = receiver.method(method).source_location if receiver.respond_to?(method)
72
- end
73
- if file && line
74
- Source.new(file: file, first_line: line, last_line: find_end(file, line))
75
- end
76
- end
77
-
78
- def find_end(file, first_line)
79
- return first_line unless File.exist?(file)
80
- lex = RubyLex.new
81
- lines = File.read(file).lines[(first_line - 1)..-1]
82
- tokens = RubyLex.ripper_lex_without_warning(lines.join)
83
- prev_tokens = []
84
-
85
- # chunk with line number
86
- tokens.chunk { |tok| tok.pos[0] }.each do |lnum, chunk|
87
- code = lines[0..lnum].join
88
- prev_tokens.concat chunk
89
- continue = lex.process_continue(prev_tokens)
90
- code_block_open = lex.check_code_block(code, prev_tokens)
91
- if !continue && !code_block_open
92
- return first_line + lnum
93
- end
94
- end
95
- first_line
96
- end
97
-
98
99
  def bold(str)
99
100
  Color.colorize(str, [:BOLD])
100
101
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "debug"
4
+
5
+ module IRB
6
+ # :stopdoc:
7
+
8
+ module ExtendCommand
9
+ class Step < Debug
10
+ def execute(*args)
11
+ # Run `next` first to move out of binding.irb
12
+ super(pre_cmds: "next", do_cmds: ["step", *args].join(" "))
13
+ end
14
+ end
15
+ end
16
+
17
+ # :startdoc:
18
+ end
data/lib/irb/context.rb CHANGED
@@ -486,9 +486,9 @@ module IRB
486
486
  @workspace.local_variable_set(:_, exception)
487
487
  end
488
488
 
489
- # Transform a non-identifier alias (ex: @, $)
489
+ # Transform a non-identifier alias (@, $) or keywords (next, break)
490
490
  command, args = line.split(/\s/, 2)
491
- if original = symbol_alias(command)
491
+ if original = command_aliases[command.to_sym]
492
492
  line = line.gsub(/\A#{Regexp.escape(command)}/, original.to_s)
493
493
  command = original
494
494
  end
@@ -545,10 +545,16 @@ module IRB
545
545
  workspace.binding.local_variables
546
546
  end
547
547
 
548
- # Return a command name if it's aliased from the argument and it's not an identifier.
549
- def symbol_alias(command)
548
+ # Return true if it's aliased from the argument and it's not an identifier.
549
+ def symbol_alias?(command)
550
550
  return nil if command.match?(/\A\w+\z/)
551
- command_aliases[command.to_sym]
551
+ command_aliases.key?(command.to_sym)
552
+ end
553
+
554
+ # Return true if the command supports transforming args
555
+ def transform_args?(command)
556
+ command = command_aliases.fetch(command.to_sym, command)
557
+ ExtendCommandBundle.load_command(command)&.respond_to?(:transform_args)
552
558
  end
553
559
  end
554
560
  end
@@ -116,13 +116,56 @@ module IRB # :nodoc:
116
116
  [:kill, OVERRIDE_PRIVATE_ONLY],
117
117
  ],
118
118
 
119
+ [
120
+ :irb_debug, :Debug, "cmd/debug",
121
+ [:debug, NO_OVERRIDE],
122
+ ],
123
+ [
124
+ :irb_edit, :Edit, "cmd/edit",
125
+ [:edit, NO_OVERRIDE],
126
+ ],
127
+ [
128
+ :irb_break, :Break, "cmd/break",
129
+ ],
130
+ [
131
+ :irb_catch, :Catch, "cmd/catch",
132
+ ],
133
+ [
134
+ :irb_next, :Next, "cmd/next",
135
+ ],
136
+ [
137
+ :irb_delete, :Delete, "cmd/delete",
138
+ [:delete, NO_OVERRIDE],
139
+ ],
140
+ [
141
+ :irb_step, :Step, "cmd/step",
142
+ [:step, NO_OVERRIDE],
143
+ ],
144
+ [
145
+ :irb_continue, :Continue, "cmd/continue",
146
+ [:continue, NO_OVERRIDE],
147
+ ],
148
+ [
149
+ :irb_finish, :Finish, "cmd/finish",
150
+ [:finish, NO_OVERRIDE],
151
+ ],
152
+ [
153
+ :irb_backtrace, :Backtrace, "cmd/backtrace",
154
+ [:backtrace, NO_OVERRIDE],
155
+ [:bt, NO_OVERRIDE],
156
+ ],
157
+ [
158
+ :irb_debug_info, :Info, "cmd/info",
159
+ [:info, NO_OVERRIDE],
160
+ ],
161
+
119
162
  [
120
163
  :irb_help, :Help, "cmd/help",
121
164
  [:help, NO_OVERRIDE],
122
165
  ],
123
166
 
124
167
  [
125
- :irb_info, :Info, "cmd/info"
168
+ :irb_info, :IrbInfo, "cmd/irb_info"
126
169
  ],
127
170
 
128
171
  [
@@ -161,26 +204,7 @@ module IRB # :nodoc:
161
204
  nil
162
205
  end
163
206
 
164
- # Installs the default irb commands:
165
- #
166
- # +irb_current_working_workspace+:: Context#main
167
- # +irb_change_workspace+:: Context#change_workspace
168
- # +irb_workspaces+:: Context#workspaces
169
- # +irb_push_workspace+:: Context#push_workspace
170
- # +irb_pop_workspace+:: Context#pop_workspace
171
- # +irb_load+:: #irb_load
172
- # +irb_require+:: #irb_require
173
- # +irb_source+:: IrbLoader#source_file
174
- # +irb+:: IRB.irb
175
- # +irb_jobs+:: JobManager
176
- # +irb_fg+:: JobManager#switch
177
- # +irb_kill+:: JobManager#kill
178
- # +irb_help+:: IRB@Command+line+options
179
- # +irb_info+:: #inspect
180
- # +irb_ls+:: Output#dump
181
- # +irb_measure+:: IRB::unset_measure_callback
182
- # +irb_show_source+:: #find_source, #show_source
183
- # +irb_whereami+:: Workspace#code_around_binding
207
+ # Installs the default irb commands.
184
208
  def self.install_extend_commands
185
209
  for args in @EXTEND_COMMANDS
186
210
  def_extend_command(*args)
data/lib/irb/init.rb CHANGED
@@ -160,8 +160,13 @@ module IRB # :nodoc:
160
160
  @CONF[:AT_EXIT] = []
161
161
 
162
162
  @CONF[:COMMAND_ALIASES] = {
163
+ # Symbol aliases
163
164
  :'$' => :show_source,
164
165
  :'@' => :whereami,
166
+ # Keyword aliases
167
+ :break => :irb_break,
168
+ :catch => :irb_catch,
169
+ :next => :irb_next,
165
170
  }
166
171
  end
167
172
 
data/lib/irb/ruby-lex.rb CHANGED
@@ -65,9 +65,9 @@ class RubyLex
65
65
  false
66
66
  end
67
67
  else
68
- # Accept any single-line input starting with a non-identifier alias (ex: @, $)
68
+ # Accept any single-line input for symbol aliases or commands that transform args
69
69
  command = code.split(/\s/, 2).first
70
- if context.symbol_alias(command)
70
+ if context.symbol_alias?(command) || context.transform_args?(command)
71
71
  next true
72
72
  end
73
73
 
data/lib/irb/version.rb CHANGED
@@ -11,7 +11,7 @@
11
11
  #
12
12
 
13
13
  module IRB # :nodoc:
14
- VERSION = "1.4.3"
14
+ VERSION = "1.5.1"
15
15
  @RELEASE_VERSION = VERSION
16
- @LAST_UPDATE_DATE = "2022-11-17"
16
+ @LAST_UPDATE_DATE = "2022-11-28"
17
17
  end
data/lib/irb.rb CHANGED
@@ -53,6 +53,52 @@ require_relative "irb/easter-egg"
53
53
  #
54
54
  # :include: ./irb/lc/help-message
55
55
  #
56
+ # == Commands
57
+ #
58
+ # The following commands are available on IRB.
59
+ #
60
+ # * cwws
61
+ # * Show the current workspace.
62
+ # * cb, cws, chws
63
+ # * Change the current workspace to an object.
64
+ # * bindings, workspaces
65
+ # * Show workspaces.
66
+ # * pushb, pushws
67
+ # * Push an object to the workspace stack.
68
+ # * popb, popws
69
+ # * Pop a workspace from the workspace stack.
70
+ # * load
71
+ # * Load a Ruby file.
72
+ # * require
73
+ # * Require a Ruby file.
74
+ # * source
75
+ # * Loads a given file in the current session.
76
+ # * irb
77
+ # * Start a child IRB.
78
+ # * jobs
79
+ # * List of current sessions.
80
+ # * fg
81
+ # * Switches to the session of the given number.
82
+ # * kill
83
+ # * Kills the session with the given number.
84
+ # * help
85
+ # * Enter the mode to look up RI documents.
86
+ # * irb_info
87
+ # * Show information about IRB.
88
+ # * ls
89
+ # * Show methods, constants, and variables.
90
+ # -g [query] or -G [query] allows you to filter out the output.
91
+ # * measure
92
+ # * measure enables the mode to measure processing time. measure :off disables it.
93
+ # * $, show_source
94
+ # * Show the source code of a given method or constant.
95
+ # * @, whereami
96
+ # * Show the source code around binding.irb again.
97
+ # * debug
98
+ # * Start the debugger of debug.gem.
99
+ # * break, delete, next, step, continue, finish, backtrace, info, catch
100
+ # * Start the debugger of debug.gem and run the command on it.
101
+ #
56
102
  # == Configuration
57
103
  #
58
104
  # IRB reads a personal initialization file when it's invoked.
@@ -93,9 +139,9 @@ require_relative "irb/easter-egg"
93
139
  #
94
140
  # === Autocompletion
95
141
  #
96
- # To enable autocompletion for irb, add the following to your +.irbrc+:
142
+ # To disable autocompletion for irb, add the following to your +.irbrc+:
97
143
  #
98
- # require 'irb/completion'
144
+ # IRB.conf[:USE_AUTOCOMPLETE] = false
99
145
  #
100
146
  # === History
101
147
  #
@@ -426,14 +472,21 @@ module IRB
426
472
  def initialize(workspace = nil, input_method = nil)
427
473
  @context = Context.new(self, workspace, input_method)
428
474
  @context.main.extend ExtendCommandBundle
429
- @context.command_aliases.each do |alias_name, cmd_name|
430
- next if @context.symbol_alias(alias_name)
431
- @context.main.install_alias_method(alias_name, cmd_name)
432
- end
433
475
  @signal_status = :IN_IRB
434
476
  @scanner = RubyLex.new
435
477
  end
436
478
 
479
+ # A hook point for `debug` command's TracePoint after :IRB_EXIT as well as its clean-up
480
+ def debug_break
481
+ # it means the debug command is executed
482
+ if defined?(DEBUGGER__) && DEBUGGER__.respond_to?(:capture_frames_without_irb)
483
+ # after leaving this initial breakpoint, revert the capture_frames patch
484
+ DEBUGGER__.singleton_class.send(:alias_method, :capture_frames, :capture_frames_without_irb)
485
+ # and remove the redundant method
486
+ DEBUGGER__.singleton_class.send(:undef_method, :capture_frames_without_irb)
487
+ end
488
+ end
489
+
437
490
  def run(conf = IRB.conf)
438
491
  conf[:IRB_RC].call(context) if conf[:IRB_RC]
439
492
  conf[:MAIN_CONTEXT] = context
@@ -924,12 +977,13 @@ class Binding
924
977
  #
925
978
  #
926
979
  # See IRB@IRB+Usage for more information.
927
- def irb
980
+ def irb(show_code: true)
928
981
  IRB.setup(source_location[0], argv: [])
929
982
  workspace = IRB::WorkSpace.new(self)
930
- STDOUT.print(workspace.code_around_binding)
983
+ STDOUT.print(workspace.code_around_binding) if show_code
931
984
  binding_irb = IRB::Irb.new(workspace)
932
985
  binding_irb.context.irb_path = File.expand_path(source_location[0])
933
986
  binding_irb.run(IRB.conf)
987
+ binding_irb.debug_break
934
988
  end
935
989
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: irb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.3
4
+ version: 1.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - aycabta
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2022-11-17 00:00:00.000000000 Z
12
+ date: 2022-11-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: reline
@@ -46,16 +46,27 @@ files:
46
46
  - exe/irb
47
47
  - irb.gemspec
48
48
  - lib/irb.rb
49
+ - lib/irb/cmd/backtrace.rb
50
+ - lib/irb/cmd/break.rb
51
+ - lib/irb/cmd/catch.rb
49
52
  - lib/irb/cmd/chws.rb
53
+ - lib/irb/cmd/continue.rb
54
+ - lib/irb/cmd/debug.rb
55
+ - lib/irb/cmd/delete.rb
56
+ - lib/irb/cmd/edit.rb
57
+ - lib/irb/cmd/finish.rb
50
58
  - lib/irb/cmd/fork.rb
51
59
  - lib/irb/cmd/help.rb
52
60
  - lib/irb/cmd/info.rb
61
+ - lib/irb/cmd/irb_info.rb
53
62
  - lib/irb/cmd/load.rb
54
63
  - lib/irb/cmd/ls.rb
55
64
  - lib/irb/cmd/measure.rb
65
+ - lib/irb/cmd/next.rb
56
66
  - lib/irb/cmd/nop.rb
57
67
  - lib/irb/cmd/pushws.rb
58
68
  - lib/irb/cmd/show_source.rb
69
+ - lib/irb/cmd/step.rb
59
70
  - lib/irb/cmd/subirb.rb
60
71
  - lib/irb/cmd/whereami.rb
61
72
  - lib/irb/color.rb
@@ -114,7 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
114
125
  - !ruby/object:Gem::Version
115
126
  version: '0'
116
127
  requirements: []
117
- rubygems_version: 3.4.0.dev
128
+ rubygems_version: 3.3.26
118
129
  signing_key:
119
130
  specification_version: 4
120
131
  summary: Interactive Ruby command-line tool for REPL (Read Eval Print Loop).