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 +4 -4
- data/Gemfile +1 -0
- data/README.md +60 -0
- data/lib/irb/cmd/backtrace.rb +21 -0
- data/lib/irb/cmd/break.rb +21 -0
- data/lib/irb/cmd/catch.rb +21 -0
- data/lib/irb/cmd/continue.rb +17 -0
- data/lib/irb/cmd/debug.rb +112 -0
- data/lib/irb/cmd/delete.rb +17 -0
- data/lib/irb/cmd/edit.rb +65 -0
- data/lib/irb/cmd/finish.rb +17 -0
- data/lib/irb/cmd/info.rb +9 -22
- data/lib/irb/cmd/irb_info.rb +34 -0
- data/lib/irb/cmd/next.rb +17 -0
- data/lib/irb/cmd/show_source.rb +44 -43
- data/lib/irb/cmd/step.rb +18 -0
- data/lib/irb/context.rb +11 -5
- data/lib/irb/extend-command.rb +45 -21
- data/lib/irb/init.rb +5 -0
- data/lib/irb/ruby-lex.rb +2 -2
- data/lib/irb/version.rb +2 -2
- data/lib/irb.rb +62 -8
- metadata +14 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7dc14606c6de90f8f56048a7ec6f8ca2bc83a8d4e0aa50d60dbe84141389601
|
4
|
+
data.tar.gz: bca1a92ee2088e483073d48dd9e0b3cdbf9b8d67768ed3fd922bb7e91da68039
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b44c813a4fc0010423d0e7ac189dc264ea92587db1b05e16e6c8ec49ff20bc2b4c18b5cfa454543b7d894be0b6610c96a3bf17b85b31d9083d962665063c8a85
|
7
|
+
data.tar.gz: af7f077aa6b2c67ccbe67209a35e622bff087a28615b3086a0511188da1b09d2284b662d659d3b308bf251488547aeaecf9da2b29454c2031d52c0e902ba7889
|
data/Gemfile
CHANGED
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
|
data/lib/irb/cmd/edit.rb
ADDED
@@ -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:
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "
|
3
|
+
require_relative "debug"
|
4
4
|
|
5
5
|
module IRB
|
6
6
|
# :stopdoc:
|
7
7
|
|
8
8
|
module ExtendCommand
|
9
|
-
class Info <
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
data/lib/irb/cmd/next.rb
ADDED
@@ -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
|
data/lib/irb/cmd/show_source.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/irb/cmd/step.rb
ADDED
@@ -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 (
|
489
|
+
# Transform a non-identifier alias (@, $) or keywords (next, break)
|
490
490
|
command, args = line.split(/\s/, 2)
|
491
|
-
if original =
|
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
|
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
|
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
|
data/lib/irb/extend-command.rb
CHANGED
@@ -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, :
|
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
|
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
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
|
142
|
+
# To disable autocompletion for irb, add the following to your +.irbrc+:
|
97
143
|
#
|
98
|
-
#
|
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
|
+
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-
|
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.
|
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).
|