irb 1.3.1 → 1.3.6

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.
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "nop"
4
+
5
+ # :stopdoc:
6
+ module IRB
7
+ module ExtendCommand
8
+ class Whereami < Nop
9
+ def execute(*)
10
+ code = irb_context.workspace.code_around_binding
11
+ if code
12
+ puts code
13
+ else
14
+ puts "The current context doesn't have code."
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ # :startdoc:
data/lib/irb/color.rb CHANGED
@@ -17,7 +17,7 @@ module IRB # :nodoc:
17
17
  CYAN = 36
18
18
 
19
19
  TOKEN_KEYWORDS = {
20
- on_kw: ['nil', 'self', 'true', 'false', '__FILE__', '__LINE__'],
20
+ on_kw: ['nil', 'self', 'true', 'false', '__FILE__', '__LINE__', '__ENCODING__'],
21
21
  on_const: ['ENV'],
22
22
  }
23
23
  private_constant :TOKEN_KEYWORDS
@@ -64,6 +64,7 @@ module IRB # :nodoc:
64
64
  on_alias_error: [[RED, REVERSE], ALL],
65
65
  on_class_name_error:[[RED, REVERSE], ALL],
66
66
  on_param_error: [[RED, REVERSE], ALL],
67
+ on___end__: [[GREEN], ALL],
67
68
  }
68
69
  rescue NameError
69
70
  # Give up highlighting Ripper-incompatible older Ruby
@@ -76,7 +77,7 @@ module IRB # :nodoc:
76
77
 
77
78
  class << self
78
79
  def colorable?
79
- $stdout.tty? && supported? && (/mswin|mingw/ =~ RUBY_PLATFORM || (ENV.key?('TERM') && ENV['TERM'] != 'dumb'))
80
+ $stdout.tty? && (/mswin|mingw/ =~ RUBY_PLATFORM || (ENV.key?('TERM') && ENV['TERM'] != 'dumb'))
80
81
  end
81
82
 
82
83
  def inspect_colorable?(obj, seen: {}.compare_by_identity)
@@ -100,26 +101,27 @@ module IRB # :nodoc:
100
101
  end
101
102
  end
102
103
 
103
- def clear
104
- return '' unless colorable?
104
+ def clear(colorable: colorable?)
105
+ return '' unless colorable
105
106
  "\e[#{CLEAR}m"
106
107
  end
107
108
 
108
- def colorize(text, seq)
109
- return text unless colorable?
109
+ def colorize(text, seq, colorable: colorable?)
110
+ return text unless colorable
110
111
  seq = seq.map { |s| "\e[#{const_get(s)}m" }.join('')
111
- "#{seq}#{text}#{clear}"
112
+ "#{seq}#{text}#{clear(colorable: colorable)}"
112
113
  end
113
114
 
114
115
  # If `complete` is false (code is incomplete), this does not warn compile_error.
115
116
  # This option is needed to avoid warning a user when the compile_error is happening
116
117
  # because the input is not wrong but just incomplete.
117
- def colorize_code(code, complete: true, ignore_error: false)
118
- return code unless colorable?
118
+ def colorize_code(code, complete: true, ignore_error: false, colorable: colorable?)
119
+ return code unless colorable
119
120
 
120
121
  symbol_state = SymbolState.new
121
122
  colored = +''
122
123
  length = 0
124
+ end_seen = false
123
125
 
124
126
  scan(code, allow_last_error: !complete) do |token, str, expr|
125
127
  # IRB::ColorPrinter skips colorizing fragments with any invalid token
@@ -132,16 +134,17 @@ module IRB # :nodoc:
132
134
  line = Reline::Unicode.escape_for_print(line)
133
135
  if seq = dispatch_seq(token, expr, line, in_symbol: in_symbol)
134
136
  colored << seq.map { |s| "\e[#{s}m" }.join('')
135
- colored << line.sub(/\Z/, clear)
137
+ colored << line.sub(/\Z/, clear(colorable: colorable))
136
138
  else
137
139
  colored << line
138
140
  end
139
141
  end
140
142
  length += str.bytesize
143
+ end_seen = true if token == :on___end__
141
144
  end
142
145
 
143
146
  # give up colorizing incomplete Ripper tokens
144
- if length != code.bytesize
147
+ unless end_seen or length == code.bytesize
145
148
  return Reline::Unicode.escape_for_print(code)
146
149
  end
147
150
 
@@ -158,11 +161,6 @@ module IRB # :nodoc:
158
161
  seen.delete(obj)
159
162
  end
160
163
 
161
- def supported?
162
- return @supported if defined?(@supported)
163
- @supported = Ripper::Lexer::Elem.method_defined?(:state)
164
- end
165
-
166
164
  def scan(code, allow_last_error:)
167
165
  pos = [1, 0]
168
166
 
@@ -4,11 +4,30 @@ require 'irb/color'
4
4
 
5
5
  module IRB
6
6
  class ColorPrinter < ::PP
7
- def self.pp(obj, out = $>, width = 79)
8
- q = ColorPrinter.new(out, width)
9
- q.guard_inspect_key {q.pp obj}
10
- q.flush
11
- out << "\n"
7
+ class << self
8
+ def pp(obj, out = $>, width = screen_width)
9
+ q = ColorPrinter.new(out, width)
10
+ q.guard_inspect_key {q.pp obj}
11
+ q.flush
12
+ out << "\n"
13
+ end
14
+
15
+ private
16
+
17
+ def screen_width
18
+ Reline.get_screen_size.last
19
+ rescue Errno::EINVAL # in `winsize': Invalid argument - <STDIN>
20
+ 79
21
+ end
22
+ end
23
+
24
+ def pp(obj)
25
+ if obj.is_a?(String)
26
+ # Avoid calling Ruby 2.4+ String#pretty_print that splits a string by "\n"
27
+ text(obj.inspect)
28
+ else
29
+ super
30
+ end
12
31
  end
13
32
 
14
33
  def text(str, width = nil)
@@ -7,7 +7,7 @@
7
7
  # From Original Idea of shugo@ruby-lang.org
8
8
  #
9
9
 
10
- autoload :RDoc, "rdoc"
10
+ require_relative 'ruby-lex'
11
11
 
12
12
  module IRB
13
13
  module InputCompletor # :nodoc:
@@ -38,8 +38,69 @@ module IRB
38
38
 
39
39
  BASIC_WORD_BREAK_CHARACTERS = " \t\n`><=;|&{("
40
40
 
41
- CompletionProc = proc { |input|
42
- retrieve_completion_data(input).compact.map{ |i| i.encode(Encoding.default_external) }
41
+ def self.retrieve_files_to_require_from_load_path
42
+ @@files_from_load_path ||= $LOAD_PATH.flat_map { |path|
43
+ begin
44
+ Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: path)
45
+ rescue Errno::ENOENT
46
+ []
47
+ end
48
+ }.uniq.map { |path|
49
+ path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '')
50
+ }
51
+ end
52
+
53
+ def self.retrieve_files_to_require_relative_from_current_dir
54
+ @@files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path|
55
+ path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '')
56
+ }
57
+ end
58
+
59
+ CompletionRequireProc = lambda { |target, preposing = nil, postposing = nil|
60
+ if target =~ /\A(['"])([^'"]+)\Z/
61
+ quote = $1
62
+ actual_target = $2
63
+ else
64
+ return nil # It's not String literal
65
+ end
66
+ tokens = RubyLex.ripper_lex_without_warning(preposing.gsub(/\s*\z/, ''))
67
+ tok = nil
68
+ tokens.reverse_each do |t|
69
+ unless [:on_lparen, :on_sp, :on_ignored_sp, :on_nl, :on_ignored_nl, :on_comment].include?(t.event)
70
+ tok = t
71
+ break
72
+ end
73
+ end
74
+ result = []
75
+ if tok && tok.event == :on_ident && tok.state == Ripper::EXPR_CMDARG
76
+ case tok.tok
77
+ when 'require'
78
+ result = retrieve_files_to_require_from_load_path.select { |path|
79
+ path.start_with?(actual_target)
80
+ }.map { |path|
81
+ quote + path
82
+ }
83
+ when 'require_relative'
84
+ result = retrieve_files_to_require_relative_from_current_dir.select { |path|
85
+ path.start_with?(actual_target)
86
+ }.map { |path|
87
+ quote + path
88
+ }
89
+ end
90
+ end
91
+ result
92
+ }
93
+
94
+ CompletionProc = lambda { |target, preposing = nil, postposing = nil|
95
+ if preposing && postposing
96
+ result = CompletionRequireProc.(target, preposing, postposing)
97
+ unless result
98
+ result = retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) }
99
+ end
100
+ result
101
+ else
102
+ retrieve_completion_data(target).compact.map{ |i| i.encode(Encoding.default_external) }
103
+ end
43
104
  }
44
105
 
45
106
  def self.retrieve_completion_data(input, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding, doc_namespace: false)
@@ -266,13 +327,22 @@ module IRB
266
327
  end
267
328
 
268
329
  PerfectMatchedProc = ->(matched, bind: IRB.conf[:MAIN_CONTEXT].workspace.binding) {
330
+ begin
331
+ require 'rdoc'
332
+ rescue LoadError
333
+ return
334
+ end
335
+
269
336
  RDocRIDriver ||= RDoc::RI::Driver.new
337
+
270
338
  if matched =~ /\A(?:::)?RubyVM/ and not ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
271
339
  IRB.__send__(:easter_egg)
272
340
  return
273
341
  end
342
+
274
343
  namespace = retrieve_completion_data(matched, bind: bind, doc_namespace: true)
275
344
  return unless namespace
345
+
276
346
  if namespace.is_a?(Array)
277
347
  out = RDoc::Markup::Document.new
278
348
  namespace.each do |m|
@@ -31,8 +31,31 @@ module IRB # :nodoc:
31
31
  load_file(path, priv)
32
32
  end
33
33
 
34
+ if File.respond_to?(:absolute_path?)
35
+ def absolute_path?(path)
36
+ File.absolute_path?(path)
37
+ end
38
+ else
39
+ separator =
40
+ if File::ALT_SEPARATOR
41
+ "[#{Regexp.quote(File::SEPARATOR + File::ALT_SEPARATOR)}]"
42
+ else
43
+ File::SEPARATOR
44
+ end
45
+ ABSOLUTE_PATH_PATTERN = # :nodoc:
46
+ case Dir.pwd
47
+ when /\A\w:/, /\A#{separator}{2}/
48
+ /\A(?:\w:|#{separator})#{separator}/
49
+ else
50
+ /\A#{separator}/
51
+ end
52
+ def absolute_path?(path)
53
+ ABSOLUTE_PATH_PATTERN =~ path
54
+ end
55
+ end
56
+
34
57
  def search_file_from_ruby_path(fn) # :nodoc:
35
- if /^#{Regexp.quote(File::Separator)}/ =~ fn
58
+ if absolute_path?(fn)
36
59
  return fn if File.exist?(fn)
37
60
  return nil
38
61
  end
@@ -50,16 +73,18 @@ module IRB # :nodoc:
50
73
  # See Irb#suspend_input_method for more information.
51
74
  def source_file(path)
52
75
  irb.suspend_name(path, File.basename(path)) do
53
- irb.suspend_input_method(FileInputMethod.new(path)) do
54
- |back_io|
55
- irb.signal_status(:IN_LOAD) do
56
- if back_io.kind_of?(FileInputMethod)
57
- irb.eval_input
58
- else
59
- begin
76
+ FileInputMethod.open(path) do |io|
77
+ irb.suspend_input_method(io) do
78
+ |back_io|
79
+ irb.signal_status(:IN_LOAD) do
80
+ if back_io.kind_of?(FileInputMethod)
60
81
  irb.eval_input
61
- rescue LoadAbort
62
- print "load abort!!\n"
82
+ else
83
+ begin
84
+ irb.eval_input
85
+ rescue LoadAbort
86
+ print "load abort!!\n"
87
+ end
63
88
  end
64
89
  end
65
90
  end
@@ -79,16 +104,18 @@ module IRB # :nodoc:
79
104
  ws = WorkSpace.new
80
105
  end
81
106
  irb.suspend_workspace(ws) do
82
- irb.suspend_input_method(FileInputMethod.new(path)) do
83
- |back_io|
84
- irb.signal_status(:IN_LOAD) do
85
- if back_io.kind_of?(FileInputMethod)
86
- irb.eval_input
87
- else
88
- begin
107
+ FileInputMethod.open(path) do |io|
108
+ irb.suspend_input_method(io) do
109
+ |back_io|
110
+ irb.signal_status(:IN_LOAD) do
111
+ if back_io.kind_of?(FileInputMethod)
89
112
  irb.eval_input
90
- rescue LoadAbort
91
- print "load abort!!\n"
113
+ else
114
+ begin
115
+ irb.eval_input
116
+ rescue LoadAbort
117
+ print "load abort!!\n"
118
+ end
92
119
  end
93
120
  end
94
121
  end
@@ -81,6 +81,8 @@ module IRB
81
81
  end
82
82
  }
83
83
  end
84
+ @loaded_history_lines = history.size
85
+ @loaded_history_mtime = File.mtime(history_file)
84
86
  end
85
87
  end
86
88
 
@@ -105,12 +107,20 @@ module IRB
105
107
  raise
106
108
  end
107
109
 
108
- open(history_file, "w:#{IRB.conf[:LC_MESSAGES].encoding}", 0600) do |f|
110
+ if File.exist?(history_file) && @loaded_history_mtime &&
111
+ File.mtime(history_file) != @loaded_history_mtime
112
+ history = history[@loaded_history_lines..-1]
113
+ append_history = true
114
+ end
115
+
116
+ open(history_file, "#{append_history ? 'a' : 'w'}:#{IRB.conf[:LC_MESSAGES].encoding}", 0600) do |f|
109
117
  hist = history.map{ |l| l.split("\n").join("\\\n") }
110
- begin
111
- hist = hist.last(num) if hist.size > num and num > 0
112
- rescue RangeError # bignum too big to convert into `long'
113
- # Do nothing because the bignum should be treated as inifinity
118
+ unless append_history
119
+ begin
120
+ hist = hist.last(num) if hist.size > num and num > 0
121
+ rescue RangeError # bignum too big to convert into `long'
122
+ # Do nothing because the bignum should be treated as inifinity
123
+ end
114
124
  end
115
125
  f.puts(hist)
116
126
  end
@@ -126,7 +126,23 @@ module IRB # :nodoc:
126
126
  ],
127
127
 
128
128
  [
129
- :measure, :Measure, "irb/cmd/measure"
129
+ :irb_ls, :Ls, "irb/cmd/ls",
130
+ [:ls, NO_OVERRIDE],
131
+ ],
132
+
133
+ [
134
+ :irb_measure, :Measure, "irb/cmd/measure",
135
+ [:measure, NO_OVERRIDE],
136
+ ],
137
+
138
+ [
139
+ :irb_show_source, :ShowSource, "irb/cmd/show_source",
140
+ [:show_source, NO_OVERRIDE],
141
+ ],
142
+
143
+ [
144
+ :irb_whereami, :Whereami, "irb/cmd/whereami",
145
+ [:whereami, NO_OVERRIDE],
130
146
  ],
131
147
 
132
148
  ]
@@ -168,12 +184,13 @@ module IRB # :nodoc:
168
184
  end
169
185
 
170
186
  if load_file
187
+ kwargs = ", **kwargs" if RUBY_ENGINE == "ruby" && RUBY_VERSION >= "2.7.0"
171
188
  line = __LINE__; eval %[
172
- def #{cmd_name}(*opts, &b)
189
+ def #{cmd_name}(*opts#{kwargs}, &b)
173
190
  require "#{load_file}"
174
191
  arity = ExtendCommand::#{cmd_class}.instance_method(:execute).arity
175
192
  args = (1..(arity < 0 ? ~arity : arity)).map {|i| "arg" + i.to_s }
176
- args << "*opts" if arity < 0
193
+ args << "*opts#{kwargs}" if arity < 0
177
194
  args << "&block"
178
195
  args = args.join(", ")
179
196
  line = __LINE__; eval %[
@@ -184,7 +201,7 @@ module IRB # :nodoc:
184
201
  end
185
202
  end
186
203
  ], nil, __FILE__, line
187
- __send__ :#{cmd_name}_, *opts, &b
204
+ __send__ :#{cmd_name}_, *opts#{kwargs}, &b
188
205
  end
189
206
  ], nil, __FILE__, line
190
207
  else
data/lib/irb/init.rb CHANGED
@@ -44,7 +44,7 @@ module IRB # :nodoc:
44
44
  @CONF[:IRB_RC] = nil
45
45
 
46
46
  @CONF[:USE_SINGLELINE] = false unless defined?(ReadlineInputMethod)
47
- @CONF[:USE_COLORIZE] = true
47
+ @CONF[:USE_COLORIZE] = !ENV['NO_COLOR']
48
48
  @CONF[:INSPECT_MODE] = true
49
49
  @CONF[:USE_TRACER] = false
50
50
  @CONF[:USE_LOADER] = false
@@ -120,7 +120,11 @@ module IRB # :nodoc:
120
120
  puts 'processing time: %fs' % (now - time) if IRB.conf[:MEASURE]
121
121
  result
122
122
  }
123
+ # arg can be either a symbol for the mode (:cpu, :wall, ..) or a hash for
124
+ # a more complete configuration.
125
+ # See https://github.com/tmm1/stackprof#all-options.
123
126
  @CONF[:MEASURE_PROC][:STACKPROF] = proc { |context, code, line_no, arg, &block|
127
+ return block.() unless IRB.conf[:MEASURE]
124
128
  success = false
125
129
  begin
126
130
  require 'stackprof'
@@ -130,10 +134,18 @@ module IRB # :nodoc:
130
134
  end
131
135
  if success
132
136
  result = nil
133
- stackprof_result = StackProf.run(mode: arg ? arg : :cpu) do
137
+ arg = { mode: arg || :cpu } unless arg.is_a?(Hash)
138
+ stackprof_result = StackProf.run(**arg) do
134
139
  result = block.()
135
140
  end
136
- StackProf::Report.new(stackprof_result).print_text if IRB.conf[:MEASURE]
141
+ case stackprof_result
142
+ when File
143
+ puts "StackProf report saved to #{stackprof_result.path}"
144
+ when Hash
145
+ StackProf::Report.new(stackprof_result).print_text
146
+ else
147
+ puts "Stackprof ran with #{arg.inspect}"
148
+ end
137
149
  result
138
150
  else
139
151
  block.()
@@ -146,7 +158,7 @@ module IRB # :nodoc:
146
158
  @CONF[:AT_EXIT] = []
147
159
  end
148
160
 
149
- def IRB.set_measure_callback(type = nil, arg = nil)
161
+ def IRB.set_measure_callback(type = nil, arg = nil, &block)
150
162
  added = nil
151
163
  if type
152
164
  type_sym = type.upcase.to_sym
@@ -155,6 +167,16 @@ module IRB # :nodoc:
155
167
  end
156
168
  elsif IRB.conf[:MEASURE_PROC][:CUSTOM]
157
169
  added = [:CUSTOM, IRB.conf[:MEASURE_PROC][:CUSTOM], arg]
170
+ elsif block_given?
171
+ added = [:BLOCK, block, arg]
172
+ found = IRB.conf[:MEASURE_CALLBACKS].find{ |m| m[0] == added[0] && m[2] == added[2] }
173
+ if found
174
+ found[1] = block
175
+ return added
176
+ else
177
+ IRB.conf[:MEASURE_CALLBACKS] << added
178
+ return added
179
+ end
158
180
  else
159
181
  added = [:TIME, IRB.conf[:MEASURE_PROC][:TIME], arg]
160
182
  end
@@ -291,11 +313,11 @@ module IRB # :nodoc:
291
313
  break
292
314
  end
293
315
  end
316
+
294
317
  load_path.collect! do |path|
295
318
  /\A\.\// =~ path ? path : File.expand_path(path)
296
319
  end
297
320
  $LOAD_PATH.unshift(*load_path)
298
-
299
321
  end
300
322
 
301
323
  # running config