irb 1.4.2 → 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: 25f63261bbf06e8b1605b8a680e538dd90641ad2492c0bfb0f8e127f673e2793
4
- data.tar.gz: 9f113f6c97887e83f9d476dba5e4c0a5656d76cc554e1e818c94e8828b4051fa
3
+ metadata.gz: e7dc14606c6de90f8f56048a7ec6f8ca2bc83a8d4e0aa50d60dbe84141389601
4
+ data.tar.gz: bca1a92ee2088e483073d48dd9e0b3cdbf9b8d67768ed3fd922bb7e91da68039
5
5
  SHA512:
6
- metadata.gz: 024d8e4dc13978bbae5ed3404e3ecff763da3fcc01e26c43d3e7e54822121bbc708de49c5a9c951a15c0ceee05a60cf282cbbd6ae6ec42960e9a8943a9217417
7
- data.tar.gz: 146edac46f0d81a01e120493e56eefa8d8b51143851718876617673bcbddf19b54e246d55541833101926e17186ce55b6484e2fb20e6f57735ee49ddfe0b6890
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).
data/Rakefile CHANGED
@@ -8,6 +8,31 @@ Rake::TestTask.new(:test) do |t|
8
8
  t.test_files = FileList["test/irb/test_*.rb"]
9
9
  end
10
10
 
11
+ # To make sure they have been correctly setup for Ruby CI.
12
+ desc "Run each irb test file in isolation."
13
+ task :test_in_isolation do
14
+ failed = false
15
+
16
+ FileList["test/irb/test_*.rb"].each do |test_file|
17
+ ENV["TEST"] = test_file
18
+ begin
19
+ Rake::Task["test"].execute
20
+ rescue => e
21
+ failed = true
22
+ msg = "Test '#{test_file}' failed when being executed in isolation. Please make sure 'rake test TEST=#{test_file}' passes."
23
+ separation_line = '=' * msg.length
24
+
25
+ puts <<~MSG
26
+ #{separation_line}
27
+ #{msg}
28
+ #{separation_line}
29
+ MSG
30
+ end
31
+ end
32
+
33
+ fail "Some tests failed when being executed in isolation" if failed
34
+ end
35
+
11
36
  Rake::TestTask.new(:test_yamatanooroti) do |t|
12
37
  t.libs << 'test' << "test/lib"
13
38
  t.libs << 'lib'
data/irb.gemspec CHANGED
@@ -34,7 +34,7 @@ Gem::Specification.new do |spec|
34
34
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
35
35
  spec.require_paths = ["lib"]
36
36
 
37
- spec.required_ruby_version = Gem::Requirement.new(">= 2.5")
37
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.6")
38
38
 
39
39
  spec.add_dependency "reline", ">= 0.3.0"
40
40
  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 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
data/lib/irb/cmd/ls.rb CHANGED
@@ -9,6 +9,15 @@ module IRB
9
9
 
10
10
  module ExtendCommand
11
11
  class Ls < Nop
12
+ def self.transform_args(args)
13
+ if match = args&.match(/\A(?<args>.+\s|)(-g|-G)\s+(?<grep>[^\s]+)\s*\n\z/)
14
+ args = match[:args]
15
+ "#{args}#{',' unless args.chomp.empty?} grep: /#{match[:grep]}/"
16
+ else
17
+ args
18
+ end
19
+ end
20
+
12
21
  def execute(*arg, grep: nil)
13
22
  o = Output.new(grep: grep)
14
23
 
@@ -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
@@ -9,12 +9,73 @@ module IRB
9
9
 
10
10
  module ExtendCommand
11
11
  class ShowSource < Nop
12
+ class << self
13
+ def transform_args(args)
14
+ # Return a string literal as is for backward compatibility
15
+ if args.empty? || string_literal?(args)
16
+ args
17
+ else # Otherwise, consider the input as a String for convenience
18
+ args.strip.dump
19
+ end
20
+ end
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
+
44
+ private
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
+
66
+ def string_literal?(args)
67
+ sexp = Ripper.sexp(args)
68
+ sexp && sexp.size == 2 && sexp.last&.first&.first == :string_literal
69
+ end
70
+ end
71
+
12
72
  def execute(str = nil)
13
73
  unless str.is_a?(String)
14
74
  puts "Error: Expected a string but got #{str.inspect}"
15
75
  return
16
76
  end
17
- source = find_source(str)
77
+
78
+ source = self.class.find_source(str, @irb_context)
18
79
  if source && File.exist?(source.file)
19
80
  show_source(source)
20
81
  else
@@ -35,48 +96,6 @@ module IRB
35
96
  puts
36
97
  end
37
98
 
38
- def find_source(str)
39
- case str
40
- when /\A[A-Z]\w*(::[A-Z]\w*)*\z/ # Const::Name
41
- eval(str, irb_context.workspace.binding) # trigger autoload
42
- base = irb_context.workspace.binding.receiver.yield_self { |r| r.is_a?(Module) ? r : Object }
43
- file, line = base.const_source_location(str) if base.respond_to?(:const_source_location) # Ruby 2.7+
44
- when /\A(?<owner>[A-Z]\w*(::[A-Z]\w*)*)#(?<method>[^ :.]+)\z/ # Class#method
45
- owner = eval(Regexp.last_match[:owner], irb_context.workspace.binding)
46
- method = Regexp.last_match[:method]
47
- if owner.respond_to?(:instance_method) && owner.instance_methods.include?(method.to_sym)
48
- file, line = owner.instance_method(method).source_location
49
- end
50
- when /\A((?<receiver>.+)(\.|::))?(?<method>[^ :.]+)\z/ # method, receiver.method, receiver::method
51
- receiver = eval(Regexp.last_match[:receiver] || 'self', irb_context.workspace.binding)
52
- method = Regexp.last_match[:method]
53
- file, line = receiver.method(method).source_location if receiver.respond_to?(method)
54
- end
55
- if file && line
56
- Source.new(file: file, first_line: line, last_line: find_end(file, line))
57
- end
58
- end
59
-
60
- def find_end(file, first_line)
61
- return first_line unless File.exist?(file)
62
- lex = RubyLex.new
63
- lines = File.read(file).lines[(first_line - 1)..-1]
64
- tokens = RubyLex.ripper_lex_without_warning(lines.join)
65
- prev_tokens = []
66
-
67
- # chunk with line number
68
- tokens.chunk { |tok| tok.pos[0] }.each do |lnum, chunk|
69
- code = lines[0..lnum].join
70
- prev_tokens.concat chunk
71
- continue = lex.process_continue(prev_tokens)
72
- code_block_open = lex.check_code_block(code, prev_tokens)
73
- if !continue && !code_block_open
74
- return first_line + lnum
75
- end
76
- end
77
- first_line
78
- end
79
-
80
99
  def bold(str)
81
100
  Color.colorize(str, [:BOLD])
82
101
  end