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.
@@ -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/color.rb CHANGED
@@ -123,13 +123,15 @@ module IRB # :nodoc:
123
123
  # If `complete` is false (code is incomplete), this does not warn compile_error.
124
124
  # This option is needed to avoid warning a user when the compile_error is happening
125
125
  # because the input is not wrong but just incomplete.
126
- def colorize_code(code, complete: true, ignore_error: false, colorable: colorable?)
126
+ def colorize_code(code, complete: true, ignore_error: false, colorable: colorable?, local_variables: [])
127
127
  return code unless colorable
128
128
 
129
129
  symbol_state = SymbolState.new
130
130
  colored = +''
131
+ lvars_code = RubyLex.generate_local_variables_assign_code(local_variables)
132
+ code_with_lvars = lvars_code ? "#{lvars_code}\n#{code}" : code
131
133
 
132
- scan(code, allow_last_error: !complete) do |token, str, expr|
134
+ scan(code_with_lvars, allow_last_error: !complete) do |token, str, expr|
133
135
  # handle uncolorable code
134
136
  if token.nil?
135
137
  colored << Reline::Unicode.escape_for_print(str)
@@ -152,6 +154,11 @@ module IRB # :nodoc:
152
154
  end
153
155
  end
154
156
  end
157
+
158
+ if lvars_code
159
+ raise "#{lvars_code.dump} should have no \\n" if lvars_code.include?("\n")
160
+ colored.sub!(/\A.+\n/, '') # delete_prefix lvars_code with colors
161
+ end
155
162
  colored
156
163
  end
157
164
 
@@ -64,25 +64,27 @@ module IRB
64
64
  if File.respond_to?(:absolute_path?)
65
65
  File.absolute_path?(p)
66
66
  else
67
- if File.absolute_path(p) == p
68
- true
69
- else
70
- false
71
- end
67
+ File.absolute_path(p) == p
72
68
  end
73
69
  end
74
70
 
71
+ GEM_PATHS =
72
+ if defined?(Gem::Specification)
73
+ Gem::Specification.latest_specs(true).map { |s|
74
+ s.require_paths.map { |p|
75
+ if absolute_path?(p)
76
+ p
77
+ else
78
+ File.join(s.full_gem_path, p)
79
+ end
80
+ }
81
+ }.flatten
82
+ else
83
+ []
84
+ end.freeze
85
+
75
86
  def self.retrieve_gem_and_system_load_path
76
- gem_paths = Gem::Specification.latest_specs(true).map { |s|
77
- s.require_paths.map { |p|
78
- if absolute_path?(p)
79
- p
80
- else
81
- File.join(s.full_gem_path, p)
82
- end
83
- }
84
- }.flatten if defined?(Gem::Specification)
85
- candidates = (gem_paths.to_a | $LOAD_PATH)
87
+ candidates = (GEM_PATHS | $LOAD_PATH)
86
88
  candidates.map do |p|
87
89
  if p.respond_to?(:to_path)
88
90
  p.to_path
@@ -171,10 +173,10 @@ module IRB
171
173
  receiver = $1
172
174
  message = $3
173
175
 
174
- candidates = String.instance_methods.collect{|m| m.to_s}
175
176
  if doc_namespace
176
177
  "String.#{message}"
177
178
  else
179
+ candidates = String.instance_methods.collect{|m| m.to_s}
178
180
  select_message(receiver, message, candidates)
179
181
  end
180
182
 
@@ -183,10 +185,10 @@ module IRB
183
185
  receiver = $1
184
186
  message = $2
185
187
 
186
- candidates = Regexp.instance_methods.collect{|m| m.to_s}
187
188
  if doc_namespace
188
189
  "Regexp.#{message}"
189
190
  else
191
+ candidates = Regexp.instance_methods.collect{|m| m.to_s}
190
192
  select_message(receiver, message, candidates)
191
193
  end
192
194
 
@@ -195,10 +197,10 @@ module IRB
195
197
  receiver = $1
196
198
  message = $2
197
199
 
198
- candidates = Array.instance_methods.collect{|m| m.to_s}
199
200
  if doc_namespace
200
201
  "Array.#{message}"
201
202
  else
203
+ candidates = Array.instance_methods.collect{|m| m.to_s}
202
204
  select_message(receiver, message, candidates)
203
205
  end
204
206
 
@@ -207,29 +209,33 @@ module IRB
207
209
  receiver = $1
208
210
  message = $2
209
211
 
210
- proc_candidates = Proc.instance_methods.collect{|m| m.to_s}
211
- hash_candidates = Hash.instance_methods.collect{|m| m.to_s}
212
212
  if doc_namespace
213
213
  ["Proc.#{message}", "Hash.#{message}"]
214
214
  else
215
+ proc_candidates = Proc.instance_methods.collect{|m| m.to_s}
216
+ hash_candidates = Hash.instance_methods.collect{|m| m.to_s}
215
217
  select_message(receiver, message, proc_candidates | hash_candidates)
216
218
  end
217
219
 
218
220
  when /^(:[^:.]*)$/
219
221
  # Symbol
220
- return nil if doc_namespace
221
- sym = $1
222
- candidates = Symbol.all_symbols.collect do |s|
223
- ":" + s.id2name.encode(Encoding.default_external)
224
- rescue EncodingError
225
- # ignore
222
+ if doc_namespace
223
+ nil
224
+ else
225
+ sym = $1
226
+ candidates = Symbol.all_symbols.collect do |s|
227
+ ":" + s.id2name.encode(Encoding.default_external)
228
+ rescue EncodingError
229
+ # ignore
230
+ end
231
+ candidates.grep(/^#{Regexp.quote(sym)}/)
226
232
  end
227
- candidates.grep(/^#{Regexp.quote(sym)}/)
228
-
229
233
  when /^::([A-Z][^:\.\(\)]*)$/
230
234
  # Absolute Constant or class methods
231
235
  receiver = $1
236
+
232
237
  candidates = Object.constants.collect{|m| m.to_s}
238
+
233
239
  if doc_namespace
234
240
  candidates.find { |i| i == receiver }
235
241
  else
@@ -240,16 +246,18 @@ module IRB
240
246
  # Constant or class methods
241
247
  receiver = $1
242
248
  message = $2
243
- begin
244
- candidates = eval("#{receiver}.constants.collect{|m| m.to_s}", bind)
245
- candidates |= eval("#{receiver}.methods.collect{|m| m.to_s}", bind)
246
- rescue Exception
247
- candidates = []
248
- end
249
+
249
250
  if doc_namespace
250
251
  "#{receiver}::#{message}"
251
252
  else
252
- select_message(receiver, message, candidates, "::")
253
+ begin
254
+ candidates = eval("#{receiver}.constants.collect{|m| m.to_s}", bind)
255
+ candidates |= eval("#{receiver}.methods.collect{|m| m.to_s}", bind)
256
+ rescue Exception
257
+ candidates = []
258
+ end
259
+
260
+ select_message(receiver, message, candidates.sort, "::")
253
261
  end
254
262
 
255
263
  when /^(:[^:.]+)(\.|::)([^.]*)$/
@@ -258,10 +266,10 @@ module IRB
258
266
  sep = $2
259
267
  message = $3
260
268
 
261
- candidates = Symbol.instance_methods.collect{|m| m.to_s}
262
269
  if doc_namespace
263
270
  "Symbol.#{message}"
264
271
  else
272
+ candidates = Symbol.instance_methods.collect{|m| m.to_s}
265
273
  select_message(receiver, message, candidates, sep)
266
274
  end
267
275
 
@@ -273,6 +281,7 @@ module IRB
273
281
 
274
282
  begin
275
283
  instance = eval(receiver, bind)
284
+
276
285
  if doc_namespace
277
286
  "#{instance.class.name}.#{message}"
278
287
  else
@@ -283,7 +292,7 @@ module IRB
283
292
  if doc_namespace
284
293
  nil
285
294
  else
286
- candidates = []
295
+ []
287
296
  end
288
297
  end
289
298
 
@@ -305,7 +314,7 @@ module IRB
305
314
  if doc_namespace
306
315
  nil
307
316
  else
308
- candidates = []
317
+ []
309
318
  end
310
319
  end
311
320
 
@@ -313,6 +322,7 @@ module IRB
313
322
  # global var
314
323
  gvar = $1
315
324
  all_gvars = global_variables.collect{|m| m.to_s}
325
+
316
326
  if doc_namespace
317
327
  all_gvars.find{ |i| i == gvar }
318
328
  else
@@ -351,11 +361,13 @@ module IRB
351
361
  to_ignore = ignored_modules
352
362
  ObjectSpace.each_object(Module){|m|
353
363
  next if (to_ignore.include?(m) rescue true)
364
+ next unless m.respond_to?(:instance_methods) # JRuby has modules that represent java packages. They don't include many common ruby methods
354
365
  candidates.concat m.instance_methods(false).collect{|x| x.to_s}
355
366
  }
356
367
  candidates.sort!
357
368
  candidates.uniq!
358
369
  end
370
+
359
371
  if doc_namespace
360
372
  rec_class = rec.is_a?(Module) ? rec : rec.class
361
373
  "#{rec_class.name}#{sep}#{candidates.find{ |i| i == message }}"
@@ -370,10 +382,11 @@ module IRB
370
382
  message = $1
371
383
 
372
384
  candidates = String.instance_methods(true).collect{|m| m.to_s}
385
+
373
386
  if doc_namespace
374
387
  "String.#{candidates.find{ |i| i == message }}"
375
388
  else
376
- select_message(receiver, message, candidates)
389
+ select_message(receiver, message, candidates.sort)
377
390
  end
378
391
 
379
392
  else
@@ -390,7 +403,7 @@ module IRB
390
403
  else
391
404
  candidates = (bind.eval_methods | bind.eval_private_methods | bind.local_variables | bind.eval_instance_variables | bind.eval_class_constants).collect{|m| m.to_s}
392
405
  candidates |= ReservedWords
393
- candidates.grep(/^#{Regexp.quote(input)}/)
406
+ candidates.grep(/^#{Regexp.quote(input)}/).sort
394
407
  end
395
408
  end
396
409
  end
data/lib/irb/context.rb CHANGED
@@ -49,12 +49,15 @@ module IRB
49
49
  if IRB.conf.has_key?(:USE_MULTILINE)
50
50
  @use_multiline = IRB.conf[:USE_MULTILINE]
51
51
  elsif IRB.conf.has_key?(:USE_RELINE) # backward compatibility
52
+ warn <<~MSG.strip
53
+ USE_RELINE is deprecated, please use USE_MULTILINE instead.
54
+ MSG
52
55
  @use_multiline = IRB.conf[:USE_RELINE]
53
56
  elsif IRB.conf.has_key?(:USE_REIDLINE)
54
57
  warn <<~MSG.strip
55
- USE_REIDLINE is deprecated, please use USE_RELINE instead.
58
+ USE_REIDLINE is deprecated, please use USE_MULTILINE instead.
56
59
  MSG
57
- @use_multiline = IRB.conf[:USE_RELINE]
60
+ @use_multiline = IRB.conf[:USE_REIDLINE]
58
61
  else
59
62
  @use_multiline = nil
60
63
  end
@@ -149,6 +152,8 @@ module IRB
149
152
  if @newline_before_multiline_output.nil?
150
153
  @newline_before_multiline_output = true
151
154
  end
155
+
156
+ @command_aliases = IRB.conf[:COMMAND_ALIASES]
152
157
  end
153
158
 
154
159
  # The top-level workspace, see WorkSpace#main
@@ -326,6 +331,9 @@ module IRB
326
331
  # See IRB@Command+line+options for more command line options.
327
332
  attr_accessor :back_trace_limit
328
333
 
334
+ # User-defined IRB command aliases
335
+ attr_accessor :command_aliases
336
+
329
337
  # Alias for #use_multiline
330
338
  alias use_multiline? use_multiline
331
339
  # Alias for #use_singleline
@@ -477,6 +485,20 @@ module IRB
477
485
  line = "begin ::Kernel.raise _; rescue _.class\n#{line}\n""end"
478
486
  @workspace.local_variable_set(:_, exception)
479
487
  end
488
+
489
+ # Transform a non-identifier alias (@, $) or keywords (next, break)
490
+ command, args = line.split(/\s/, 2)
491
+ if original = command_aliases[command.to_sym]
492
+ line = line.gsub(/\A#{Regexp.escape(command)}/, original.to_s)
493
+ command = original
494
+ end
495
+
496
+ # Hook command-specific transformation
497
+ command_class = ExtendCommandBundle.load_command(command)
498
+ if command_class&.respond_to?(:transform_args)
499
+ line = "#{command} #{command_class.transform_args(args)}"
500
+ end
501
+
480
502
  set_last_value(@workspace.evaluate(self, line, irb_path, line_no))
481
503
  end
482
504
 
@@ -518,5 +540,21 @@ module IRB
518
540
  end
519
541
  alias __to_s__ to_s
520
542
  alias to_s inspect
543
+
544
+ def local_variables # :nodoc:
545
+ workspace.binding.local_variables
546
+ end
547
+
548
+ # Return true if it's aliased from the argument and it's not an identifier.
549
+ def symbol_alias?(command)
550
+ return nil if command.match?(/\A\w+\z/)
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)
558
+ end
521
559
  end
522
560
  end
@@ -73,7 +73,7 @@ module IRB
73
73
  open(history_file, "r:#{IRB.conf[:LC_MESSAGES].encoding}") do |f|
74
74
  f.each { |l|
75
75
  l = l.chomp
76
- if self.class == ReidlineInputMethod and history.last&.end_with?("\\")
76
+ if self.class == RelineInputMethod and history.last&.end_with?("\\")
77
77
  history.last.delete_suffix!("\\")
78
78
  history.last << "\n" << l
79
79
  else
@@ -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
  [
@@ -147,21 +190,21 @@ module IRB # :nodoc:
147
190
 
148
191
  ]
149
192
 
150
- # Installs the default irb commands:
151
- #
152
- # +irb_current_working_workspace+:: Context#main
153
- # +irb_change_workspace+:: Context#change_workspace
154
- # +irb_workspaces+:: Context#workspaces
155
- # +irb_push_workspace+:: Context#push_workspace
156
- # +irb_pop_workspace+:: Context#pop_workspace
157
- # +irb_load+:: #irb_load
158
- # +irb_require+:: #irb_require
159
- # +irb_source+:: IrbLoader#source_file
160
- # +irb+:: IRB.irb
161
- # +irb_jobs+:: JobManager
162
- # +irb_fg+:: JobManager#switch
163
- # +irb_kill+:: JobManager#kill
164
- # +irb_help+:: IRB@Command+line+options
193
+ # Convert a command name to its implementation class if such command exists
194
+ def self.load_command(command)
195
+ command = command.to_sym
196
+ @EXTEND_COMMANDS.each do |cmd_name, cmd_class, load_file, *aliases|
197
+ next if cmd_name != command && aliases.all? { |alias_name, _| alias_name != command }
198
+
199
+ if !defined?(ExtendCommand) || !ExtendCommand.const_defined?(cmd_class, false)
200
+ require_relative load_file
201
+ end
202
+ return ExtendCommand.const_get(cmd_class, false)
203
+ end
204
+ nil
205
+ end
206
+
207
+ # Installs the default irb commands.
165
208
  def self.install_extend_commands
166
209
  for args in @EXTEND_COMMANDS
167
210
  def_extend_command(*args)
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] = !ENV['NO_COLOR']
47
+ @CONF[:USE_COLORIZE] = (nc = ENV['NO_COLOR']).nil? || nc.empty?
48
48
  @CONF[:USE_AUTOCOMPLETE] = true
49
49
  @CONF[:INSPECT_MODE] = true
50
50
  @CONF[:USE_TRACER] = false
@@ -158,6 +158,16 @@ module IRB # :nodoc:
158
158
  @CONF[:LC_MESSAGES] = Locale.new
159
159
 
160
160
  @CONF[:AT_EXIT] = []
161
+
162
+ @CONF[:COMMAND_ALIASES] = {
163
+ # Symbol aliases
164
+ :'$' => :show_source,
165
+ :'@' => :whereami,
166
+ # Keyword aliases
167
+ :break => :irb_break,
168
+ :catch => :irb_catch,
169
+ :next => :irb_next,
170
+ }
161
171
  end
162
172
 
163
173
  def IRB.set_measure_callback(type = nil, arg = nil, &block)
@@ -255,8 +265,20 @@ module IRB # :nodoc:
255
265
  when "--nosingleline", "--noreadline"
256
266
  @CONF[:USE_SINGLELINE] = false
257
267
  when "--multiline", "--reidline"
268
+ if opt == "--reidline"
269
+ warn <<~MSG.strip
270
+ --reidline is deprecated, please use --multiline instead.
271
+ MSG
272
+ end
273
+
258
274
  @CONF[:USE_MULTILINE] = true
259
275
  when "--nomultiline", "--noreidline"
276
+ if opt == "--noreidline"
277
+ warn <<~MSG.strip
278
+ --noreidline is deprecated, please use --nomultiline instead.
279
+ MSG
280
+ end
281
+
260
282
  @CONF[:USE_MULTILINE] = false
261
283
  when /^--extra-doc-dir(?:=(.+))?/
262
284
  opt = $1 || argv.shift
@@ -379,11 +401,9 @@ module IRB # :nodoc:
379
401
  end
380
402
  if xdg_config_home = ENV["XDG_CONFIG_HOME"]
381
403
  irb_home = File.join(xdg_config_home, "irb")
382
- unless File.exist? irb_home
383
- require 'fileutils'
384
- FileUtils.mkdir_p irb_home
404
+ if File.directory?(irb_home)
405
+ yield proc{|rc| irb_home + "/irb#{rc}"}
385
406
  end
386
- yield proc{|rc| irb_home + "/irb#{rc}"}
387
407
  end
388
408
  if home = ENV["HOME"]
389
409
  yield proc{|rc| home+"/.irb#{rc}"}
@@ -286,7 +286,8 @@ module IRB
286
286
  if IRB.conf[:USE_COLORIZE]
287
287
  proc do |output, complete: |
288
288
  next unless IRB::Color.colorable?
289
- IRB::Color.colorize_code(output, complete: complete)
289
+ lvars = IRB.CurrentContext&.local_variables || []
290
+ IRB::Color.colorize_code(output, complete: complete, local_variables: lvars)
290
291
  end
291
292
  else
292
293
  proc do |output|
@@ -295,8 +296,13 @@ module IRB
295
296
  end
296
297
  Reline.dig_perfect_match_proc = IRB::InputCompletor::PerfectMatchedProc
297
298
  Reline.autocompletion = IRB.conf[:USE_AUTOCOMPLETE]
299
+
298
300
  if IRB.conf[:USE_AUTOCOMPLETE]
299
- Reline.add_dialog_proc(:show_doc, SHOW_DOC_DIALOG, Reline::DEFAULT_DIALOG_CONTEXT)
301
+ begin
302
+ require 'rdoc'
303
+ Reline.add_dialog_proc(:show_doc, SHOW_DOC_DIALOG, Reline::DEFAULT_DIALOG_CONTEXT)
304
+ rescue LoadError
305
+ end
300
306
  end
301
307
  end
302
308
 
@@ -320,11 +326,6 @@ module IRB
320
326
  [195, 164], # The "ä" that appears when Alt+d is pressed on xterm.
321
327
  [226, 136, 130] # The "∂" that appears when Alt+d in pressed on iTerm2.
322
328
  ]
323
- begin
324
- require 'rdoc'
325
- rescue LoadError
326
- return nil
327
- end
328
329
 
329
330
  if just_cursor_moving and completion_journey_data.nil?
330
331
  return nil
@@ -460,7 +461,7 @@ module IRB
460
461
  # For debug message
461
462
  def inspect
462
463
  config = Reline::Config.new
463
- str = "ReidlineInputMethod with Reline #{Reline::VERSION}"
464
+ str = "RelineInputMethod with Reline #{Reline::VERSION}"
464
465
  if config.respond_to?(:inputrc_path)
465
466
  inputrc_path = File.expand_path(config.inputrc_path)
466
467
  else