irb 1.16.0 → 1.18.0
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 +8 -2
- data/lib/irb/color.rb +251 -163
- data/lib/irb/command/base.rb +35 -0
- data/lib/irb/command/copy.rb +11 -1
- data/lib/irb/command/internal_helpers.rb +6 -3
- data/lib/irb/command/ls.rb +6 -4
- data/lib/irb/completion.rb +38 -16
- data/lib/irb/context.rb +1 -1
- data/lib/irb/debug.rb +2 -2
- data/lib/irb/init.rb +3 -0
- data/lib/irb/input-method.rb +141 -111
- data/lib/irb/nesting_parser.rb +362 -213
- data/lib/irb/pager.rb +8 -0
- data/lib/irb/ruby-lex.rb +204 -303
- data/lib/irb/ruby_logo.aa +4 -0
- data/lib/irb/source_finder.rb +5 -14
- data/lib/irb/startup_message.rb +83 -0
- data/lib/irb/version.rb +2 -2
- data/lib/irb.rb +39 -17
- metadata +16 -1
data/lib/irb/completion.rb
CHANGED
|
@@ -8,6 +8,29 @@
|
|
|
8
8
|
require_relative 'ruby-lex'
|
|
9
9
|
|
|
10
10
|
module IRB
|
|
11
|
+
class DocumentTarget # :nodoc:
|
|
12
|
+
attr_reader :name
|
|
13
|
+
|
|
14
|
+
def initialize(name)
|
|
15
|
+
@name = name
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class CommandDocument < DocumentTarget # :nodoc:
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Represents a method/class documentation target. May hold multiple names
|
|
23
|
+
# when the receiver is ambiguous (e.g. `{}.any?` could be Hash#any? or Proc#any?).
|
|
24
|
+
# The dialog popup uses only the first name; the full-screen display renders all.
|
|
25
|
+
class MethodDocument < DocumentTarget # :nodoc:
|
|
26
|
+
attr_reader :names
|
|
27
|
+
|
|
28
|
+
def initialize(*names)
|
|
29
|
+
super(names.first)
|
|
30
|
+
@names = names
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
11
34
|
class BaseCompletor # :nodoc:
|
|
12
35
|
|
|
13
36
|
# Set of reserved words used by Ruby, you should not use these for
|
|
@@ -76,6 +99,12 @@ module IRB
|
|
|
76
99
|
end
|
|
77
100
|
end
|
|
78
101
|
|
|
102
|
+
def command_document_target(preposing, matched)
|
|
103
|
+
if preposing.empty? && IRB::Command.command_names.include?(matched)
|
|
104
|
+
CommandDocument.new(matched)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
79
108
|
def retrieve_files_to_require_relative_from_current_dir
|
|
80
109
|
@files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path|
|
|
81
110
|
path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '')
|
|
@@ -118,8 +147,10 @@ module IRB
|
|
|
118
147
|
end
|
|
119
148
|
|
|
120
149
|
def doc_namespace(preposing, matched, _postposing, bind:)
|
|
121
|
-
|
|
122
|
-
|
|
150
|
+
command_document_target(preposing, matched) || begin
|
|
151
|
+
result = ReplTypeCompletor.analyze(preposing + matched, binding: bind, filename: @context.irb_path)
|
|
152
|
+
result&.doc_namespace('')
|
|
153
|
+
end
|
|
123
154
|
end
|
|
124
155
|
end
|
|
125
156
|
|
|
@@ -166,22 +197,13 @@ module IRB
|
|
|
166
197
|
else
|
|
167
198
|
return nil # It's not String literal
|
|
168
199
|
end
|
|
169
|
-
tokens = RubyLex.ripper_lex_without_warning(preposing.rstrip)
|
|
170
|
-
tok = nil
|
|
171
|
-
tokens.reverse_each do |t|
|
|
172
|
-
unless [:on_lparen, :on_sp, :on_ignored_sp, :on_nl, :on_ignored_nl, :on_comment].include?(t.event)
|
|
173
|
-
tok = t
|
|
174
|
-
break
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
return unless tok&.event == :on_ident && tok.state == Ripper::EXPR_CMDARG
|
|
178
200
|
|
|
179
|
-
case
|
|
180
|
-
when
|
|
201
|
+
case preposing
|
|
202
|
+
when /(^|[^\w])require\(? *\z/
|
|
181
203
|
retrieve_files_to_require_from_load_path.filter_map { |path|
|
|
182
204
|
quote + path if path.start_with?(actual_target)
|
|
183
205
|
}
|
|
184
|
-
when
|
|
206
|
+
when /(^|[^\w])require_relative\(? *\z/
|
|
185
207
|
retrieve_files_to_require_relative_from_current_dir.filter_map { |path|
|
|
186
208
|
quote + path if path.start_with?(actual_target)
|
|
187
209
|
}
|
|
@@ -210,8 +232,8 @@ module IRB
|
|
|
210
232
|
commands | completion_data
|
|
211
233
|
end
|
|
212
234
|
|
|
213
|
-
def doc_namespace(
|
|
214
|
-
retrieve_completion_data(matched, bind: bind, doc_namespace: true)
|
|
235
|
+
def doc_namespace(preposing, matched, _postposing, bind:)
|
|
236
|
+
command_document_target(preposing, matched) || retrieve_completion_data(matched, bind: bind, doc_namespace: true)
|
|
215
237
|
end
|
|
216
238
|
|
|
217
239
|
def retrieve_completion_data(input, bind:, doc_namespace:)
|
data/lib/irb/context.rb
CHANGED
|
@@ -649,7 +649,7 @@ module IRB
|
|
|
649
649
|
parsed_input = parse_input(input, false)
|
|
650
650
|
if parsed_input.is_a?(Statement::Command)
|
|
651
651
|
name, sep, arg = input.split(/(\s+)/, 2)
|
|
652
|
-
arg = IRB::Color.colorize_code(arg, complete: complete, local_variables: lvars)
|
|
652
|
+
arg = IRB::Color.colorize_code(arg, complete: complete, local_variables: lvars) if arg
|
|
653
653
|
"#{IRB::Color.colorize(name, [:BOLD])}\e[m#{sep}#{arg}"
|
|
654
654
|
else
|
|
655
655
|
IRB::Color.colorize_code(input, complete: complete, local_variables: lvars)
|
data/lib/irb/debug.rb
CHANGED
|
@@ -49,7 +49,7 @@ module IRB
|
|
|
49
49
|
def DEBUGGER__.capture_frames(*args)
|
|
50
50
|
frames = capture_frames_without_irb(*args)
|
|
51
51
|
frames.reject! do |frame|
|
|
52
|
-
frame.realpath&.start_with?(IRB_DIR) || frame.path
|
|
52
|
+
frame.realpath&.start_with?(IRB_DIR) || frame.path&.start_with?("<internal:")
|
|
53
53
|
end
|
|
54
54
|
frames
|
|
55
55
|
end
|
|
@@ -87,7 +87,7 @@ module IRB
|
|
|
87
87
|
module SkipPathHelperForIRB
|
|
88
88
|
def skip_internal_path?(path)
|
|
89
89
|
# The latter can be removed once https://github.com/ruby/debug/issues/866 is resolved
|
|
90
|
-
super || path
|
|
90
|
+
super || path&.match?(IRB_DIR) || path&.match?('<internal:prelude>')
|
|
91
91
|
end
|
|
92
92
|
end
|
|
93
93
|
|
data/lib/irb/init.rb
CHANGED
|
@@ -87,6 +87,7 @@ module IRB # :nodoc:
|
|
|
87
87
|
@CONF[:IGNORE_SIGINT] = true
|
|
88
88
|
@CONF[:IGNORE_EOF] = false
|
|
89
89
|
@CONF[:USE_PAGER] = true
|
|
90
|
+
@CONF[:SHOW_BANNER] = true
|
|
90
91
|
@CONF[:EXTRA_DOC_DIRS] = []
|
|
91
92
|
@CONF[:ECHO] = nil
|
|
92
93
|
@CONF[:ECHO_ON_ASSIGNMENT] = nil
|
|
@@ -345,6 +346,8 @@ module IRB # :nodoc:
|
|
|
345
346
|
opt = $1 || argv.shift
|
|
346
347
|
prompt_mode = opt.upcase.tr("-", "_").intern
|
|
347
348
|
@CONF[:PROMPT_MODE] = prompt_mode
|
|
349
|
+
when "--nobanner"
|
|
350
|
+
@CONF[:SHOW_BANNER] = false
|
|
348
351
|
when "--noprompt"
|
|
349
352
|
@CONF[:PROMPT_MODE] = :NULL
|
|
350
353
|
when "--script"
|
data/lib/irb/input-method.rb
CHANGED
|
@@ -255,6 +255,16 @@ module IRB
|
|
|
255
255
|
|
|
256
256
|
class RelineInputMethod < StdioInputMethod
|
|
257
257
|
HISTORY = Reline::HISTORY
|
|
258
|
+
ALT_KEY_NAME = RUBY_PLATFORM.match?(/darwin/) ? "Option" : "Alt"
|
|
259
|
+
PRESS_ALT_D_TO_READ_FULL_DOC = "Press #{ALT_KEY_NAME}+d to read the full document".freeze
|
|
260
|
+
PRESS_ALT_D_TO_SEE_MORE = "Press #{ALT_KEY_NAME}+d to see more".freeze
|
|
261
|
+
ALT_D_SEQUENCES = [
|
|
262
|
+
[27, 100], # Normal Alt+d when convert-meta isn't used.
|
|
263
|
+
# When option/alt is not configured as a meta key in terminal emulator,
|
|
264
|
+
# option/alt + d will send a unicode character depend on OS keyboard setting.
|
|
265
|
+
[195, 164], # "ä" in somewhere (FIXME: environment information is unknown).
|
|
266
|
+
[226, 136, 130] # "∂" Alt+d on Mac keyboard.
|
|
267
|
+
].freeze
|
|
258
268
|
include HistorySavingAbility
|
|
259
269
|
# Creates a new input method object using Reline
|
|
260
270
|
def initialize(completor)
|
|
@@ -305,9 +315,17 @@ module IRB
|
|
|
305
315
|
@auto_indent_proc = block
|
|
306
316
|
end
|
|
307
317
|
|
|
308
|
-
def
|
|
318
|
+
def retrieve_document_target(matched)
|
|
309
319
|
preposing, _target, postposing, bind = @completion_params
|
|
310
|
-
@completor.doc_namespace(preposing, matched, postposing, bind: bind)
|
|
320
|
+
result = @completor.doc_namespace(preposing, matched, postposing, bind: bind)
|
|
321
|
+
case result
|
|
322
|
+
when DocumentTarget, nil
|
|
323
|
+
result
|
|
324
|
+
when Array
|
|
325
|
+
MethodDocument.new(*result)
|
|
326
|
+
when String
|
|
327
|
+
MethodDocument.new(result)
|
|
328
|
+
end
|
|
311
329
|
end
|
|
312
330
|
|
|
313
331
|
def rdoc_ri_driver
|
|
@@ -328,146 +346,158 @@ module IRB
|
|
|
328
346
|
input_method = self # self is changed in the lambda below.
|
|
329
347
|
->() {
|
|
330
348
|
dialog.trap_key = nil
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
# When option/alt is not configured as a meta key in terminal emulator,
|
|
334
|
-
# option/alt + d will send a unicode character depend on OS keyboard setting.
|
|
335
|
-
[195, 164], # "ä" in somewhere (FIXME: environment information is unknown).
|
|
336
|
-
[226, 136, 130] # "∂" Alt+d on Mac keyboard.
|
|
337
|
-
]
|
|
338
|
-
|
|
339
|
-
if just_cursor_moving and completion_journey_data.nil?
|
|
349
|
+
|
|
350
|
+
if just_cursor_moving && completion_journey_data.nil?
|
|
340
351
|
return nil
|
|
341
352
|
end
|
|
342
353
|
cursor_pos_to_render, result, pointer, autocomplete_dialog = context.pop(4)
|
|
343
|
-
return nil if result.nil?
|
|
354
|
+
return nil if result.nil? || pointer.nil? || pointer < 0
|
|
344
355
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
356
|
+
matched_text = result[pointer]
|
|
357
|
+
show_easter_egg = matched_text&.match?(/\ARubyVM/) && !ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
|
|
358
|
+
target = show_easter_egg ? nil : input_method.retrieve_document_target(matched_text)
|
|
348
359
|
|
|
349
|
-
|
|
360
|
+
x, width = input_method.dialog_doc_position(cursor_pos_to_render, autocomplete_dialog, screen_width)
|
|
361
|
+
return nil unless x
|
|
350
362
|
|
|
351
|
-
|
|
363
|
+
dialog.trap_key = ALT_D_SEQUENCES
|
|
352
364
|
|
|
353
365
|
if key.match?(dialog.name)
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
# so we need to turn on and off alternate screen manually.
|
|
360
|
-
begin
|
|
361
|
-
print "\e[?1049h"
|
|
362
|
-
driver.display_names([name])
|
|
363
|
-
rescue RDoc::RI::Driver::NotFoundError
|
|
364
|
-
ensure
|
|
365
|
-
print "\e[?1049l"
|
|
366
|
-
end
|
|
366
|
+
begin
|
|
367
|
+
print "\e[?1049h"
|
|
368
|
+
input_method.display_document(matched_text)
|
|
369
|
+
ensure
|
|
370
|
+
print "\e[?1049l"
|
|
367
371
|
end
|
|
368
372
|
end
|
|
369
373
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
used_for_class = false
|
|
379
|
-
if not name =~ /#|\./
|
|
380
|
-
found, klasses, includes, extends = driver.classes_and_includes_and_extends_for(name)
|
|
381
|
-
if not found.empty?
|
|
382
|
-
doc = driver.class_document(name, found, klasses, includes, extends)
|
|
383
|
-
used_for_class = true
|
|
374
|
+
contents = case target
|
|
375
|
+
when CommandDocument
|
|
376
|
+
input_method.command_doc_dialog_contents(target.name, width)
|
|
377
|
+
when MethodDocument
|
|
378
|
+
input_method.rdoc_dialog_contents(target.name, width)
|
|
379
|
+
else
|
|
380
|
+
if show_easter_egg
|
|
381
|
+
input_method.easter_egg_dialog_contents
|
|
384
382
|
end
|
|
385
383
|
end
|
|
386
|
-
unless
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
384
|
+
return nil unless contents
|
|
385
|
+
|
|
386
|
+
contents = contents.take(preferred_dialog_height)
|
|
387
|
+
y = cursor_pos_to_render.y
|
|
388
|
+
Reline::DialogRenderInfo.new(pos: Reline::CursorPos.new(x, y), contents: contents, width: width, bg_color: '49')
|
|
389
|
+
}
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def command_doc_dialog_contents(command_name, width)
|
|
393
|
+
command_class = IRB::Command.load_command(command_name)
|
|
394
|
+
return unless command_class
|
|
395
|
+
|
|
396
|
+
[PRESS_ALT_D_TO_READ_FULL_DOC, ""] + command_class.doc_dialog_content(command_name, width)
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
def easter_egg_dialog_contents
|
|
400
|
+
type = STDOUT.external_encoding == Encoding::UTF_8 ? :unicode : :ascii
|
|
401
|
+
lines = IRB.send(:easter_egg_logo, type).split("\n")
|
|
402
|
+
lines[0][0, PRESS_ALT_D_TO_SEE_MORE.size] = PRESS_ALT_D_TO_SEE_MORE
|
|
403
|
+
lines
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def rdoc_dialog_contents(name, width)
|
|
407
|
+
driver = rdoc_ri_driver
|
|
408
|
+
return unless driver
|
|
409
|
+
|
|
410
|
+
name = driver.expand_name(name)
|
|
411
|
+
|
|
412
|
+
doc = if name =~ /#|\./
|
|
413
|
+
d = RDoc::Markup::Document.new
|
|
414
|
+
driver.add_method(d, name)
|
|
415
|
+
d
|
|
416
|
+
else
|
|
417
|
+
found, klasses, includes, extends = driver.classes_and_includes_and_extends_for(name)
|
|
418
|
+
if found.empty?
|
|
419
|
+
d = RDoc::Markup::Document.new
|
|
420
|
+
driver.add_method(d, name)
|
|
421
|
+
d
|
|
422
|
+
else
|
|
423
|
+
driver.class_document(name, found, klasses, includes, extends)
|
|
395
424
|
end
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
formatter = RDoc::Markup::ToAnsi.new
|
|
428
|
+
formatter.width = width
|
|
429
|
+
[PRESS_ALT_D_TO_READ_FULL_DOC] + doc.accept(formatter).split("\n")
|
|
430
|
+
rescue RDoc::RI::Driver::NotFoundError
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
def dialog_doc_position(cursor_pos_to_render, autocomplete_dialog, screen_width)
|
|
434
|
+
width = 40
|
|
435
|
+
right_x = cursor_pos_to_render.x + autocomplete_dialog.width
|
|
436
|
+
if right_x + width > screen_width
|
|
437
|
+
right_width = screen_width - (right_x + 1)
|
|
438
|
+
left_x = autocomplete_dialog.column - width
|
|
439
|
+
left_x = 0 if left_x < 0
|
|
440
|
+
left_width = width > autocomplete_dialog.column ? autocomplete_dialog.column : width
|
|
441
|
+
if right_width.positive? && left_width.positive?
|
|
442
|
+
if right_width >= left_width
|
|
414
443
|
width = right_width
|
|
415
444
|
x = right_x
|
|
416
|
-
|
|
445
|
+
else
|
|
417
446
|
width = left_width
|
|
418
447
|
x = left_x
|
|
419
|
-
else # Both are negative width.
|
|
420
|
-
return nil
|
|
421
448
|
end
|
|
422
|
-
|
|
449
|
+
elsif right_width.positive? && left_width <= 0
|
|
450
|
+
width = right_width
|
|
423
451
|
x = right_x
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
dialog.trap_key = alt_d
|
|
428
|
-
mod_key = RUBY_PLATFORM.match?(/darwin/) ? "Option" : "Alt"
|
|
429
|
-
if show_easter_egg
|
|
430
|
-
type = STDOUT.external_encoding == Encoding::UTF_8 ? :unicode : :ascii
|
|
431
|
-
contents = IRB.send(:easter_egg_logo, type).split("\n")
|
|
432
|
-
message = "Press #{mod_key}+d to see more"
|
|
433
|
-
contents[0][0, message.size] = message
|
|
452
|
+
elsif right_width <= 0 && left_width.positive?
|
|
453
|
+
width = left_width
|
|
454
|
+
x = left_x
|
|
434
455
|
else
|
|
435
|
-
|
|
436
|
-
contents = [message] + doc.accept(formatter).split("\n")
|
|
456
|
+
return nil
|
|
437
457
|
end
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}
|
|
458
|
+
else
|
|
459
|
+
x = right_x
|
|
460
|
+
end
|
|
461
|
+
[x, width]
|
|
443
462
|
end
|
|
444
463
|
|
|
445
464
|
def display_document(matched)
|
|
446
|
-
|
|
447
|
-
return unless
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
465
|
+
target = retrieve_document_target(matched)
|
|
466
|
+
return unless target
|
|
467
|
+
|
|
468
|
+
case target
|
|
469
|
+
when CommandDocument
|
|
470
|
+
command_class = IRB::Command.load_command(target.name)
|
|
471
|
+
if command_class
|
|
472
|
+
content = command_class.help_message || command_class.description
|
|
473
|
+
Pager.page(retain_content: true) do |io|
|
|
474
|
+
io.puts content
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
when MethodDocument
|
|
478
|
+
driver = rdoc_ri_driver
|
|
479
|
+
return unless driver
|
|
453
480
|
|
|
454
|
-
|
|
455
|
-
|
|
481
|
+
if matched =~ /\A(?:::)?RubyVM/ && !ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
|
|
482
|
+
IRB.__send__(:easter_egg)
|
|
483
|
+
return
|
|
484
|
+
end
|
|
456
485
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
486
|
+
if target.names.length > 1
|
|
487
|
+
out = RDoc::Markup::Document.new
|
|
488
|
+
target.names.each do |m|
|
|
489
|
+
begin
|
|
490
|
+
driver.add_method(out, m)
|
|
491
|
+
rescue RDoc::RI::Driver::NotFoundError
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
driver.display(out)
|
|
495
|
+
else
|
|
460
496
|
begin
|
|
461
|
-
driver.
|
|
497
|
+
driver.display_names([target.name])
|
|
462
498
|
rescue RDoc::RI::Driver::NotFoundError
|
|
463
499
|
end
|
|
464
500
|
end
|
|
465
|
-
driver.display(out)
|
|
466
|
-
else
|
|
467
|
-
begin
|
|
468
|
-
driver.display_names([namespace])
|
|
469
|
-
rescue RDoc::RI::Driver::NotFoundError
|
|
470
|
-
end
|
|
471
501
|
end
|
|
472
502
|
end
|
|
473
503
|
|