irb 1.15.1 → 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.
@@ -8,30 +8,33 @@
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
14
37
  # constants or variables
15
- ReservedWords = %w[
16
- __ENCODING__ __LINE__ __FILE__
17
- BEGIN END
18
- alias and
19
- begin break
20
- case class
21
- def defined? do
22
- else elsif end ensure
23
- false for
24
- if in
25
- module
26
- next nil not
27
- or
28
- redo rescue retry return
29
- self super
30
- then true
31
- undef unless until
32
- when while
33
- yield
34
- ]
35
38
 
36
39
  HELP_COMMAND_PREPOSING = /\Ahelp\s+/
37
40
 
@@ -60,13 +63,13 @@ module IRB
60
63
 
61
64
  def retrieve_gem_and_system_load_path
62
65
  candidates = (GEM_PATHS | $LOAD_PATH)
63
- candidates.map do |p|
66
+ candidates.filter_map do |p|
64
67
  if p.respond_to?(:to_path)
65
68
  p.to_path
66
69
  else
67
70
  String(p) rescue nil
68
71
  end
69
- end.compact.sort
72
+ end.sort
70
73
  end
71
74
 
72
75
  def retrieve_files_to_require_from_load_path
@@ -96,6 +99,12 @@ module IRB
96
99
  end
97
100
  end
98
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
+
99
108
  def retrieve_files_to_require_relative_from_current_dir
100
109
  @files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path|
101
110
  path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '')
@@ -127,12 +136,21 @@ module IRB
127
136
 
128
137
  return commands unless result
129
138
 
130
- commands | result.completion_candidates.map { target + _1 }
139
+ encoded_candidates = result.completion_candidates.filter_map do |i|
140
+ encoded = i.encode(Encoding.default_external)
141
+ target + encoded
142
+ rescue Encoding::UndefinedConversionError
143
+ # If the string cannot be converted, we just ignore it
144
+ nil
145
+ end
146
+ commands | encoded_candidates
131
147
  end
132
148
 
133
149
  def doc_namespace(preposing, matched, _postposing, bind:)
134
- result = ReplTypeCompletor.analyze(preposing + matched, binding: bind, filename: @context.irb_path)
135
- result&.doc_namespace('')
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
136
154
  end
137
155
  end
138
156
 
@@ -179,28 +197,15 @@ module IRB
179
197
  else
180
198
  return nil # It's not String literal
181
199
  end
182
- tokens = RubyLex.ripper_lex_without_warning(preposing.gsub(/\s*\z/, ''))
183
- tok = nil
184
- tokens.reverse_each do |t|
185
- unless [:on_lparen, :on_sp, :on_ignored_sp, :on_nl, :on_ignored_nl, :on_comment].include?(t.event)
186
- tok = t
187
- break
188
- end
189
- end
190
- return unless tok&.event == :on_ident && tok.state == Ripper::EXPR_CMDARG
191
-
192
- case tok.tok
193
- when 'require'
194
- retrieve_files_to_require_from_load_path.select { |path|
195
- path.start_with?(actual_target)
196
- }.map { |path|
197
- quote + path
200
+
201
+ case preposing
202
+ when /(^|[^\w])require\(? *\z/
203
+ retrieve_files_to_require_from_load_path.filter_map { |path|
204
+ quote + path if path.start_with?(actual_target)
198
205
  }
199
- when 'require_relative'
200
- retrieve_files_to_require_relative_from_current_dir.select { |path|
201
- path.start_with?(actual_target)
202
- }.map { |path|
203
- quote + path
206
+ when /(^|[^\w])require_relative\(? *\z/
207
+ retrieve_files_to_require_relative_from_current_dir.filter_map { |path|
208
+ quote + path if path.start_with?(actual_target)
204
209
  }
205
210
  end
206
211
  end
@@ -218,12 +223,17 @@ module IRB
218
223
  # It doesn't make sense to propose commands with other preposing
219
224
  commands = [] unless preposing.empty?
220
225
 
221
- completion_data = retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.map{ |i| i.encode(Encoding.default_external) }
226
+ completion_data = retrieve_completion_data(target, bind: bind, doc_namespace: false).compact.filter_map do |i|
227
+ i.encode(Encoding.default_external)
228
+ rescue Encoding::UndefinedConversionError
229
+ # If the string cannot be converted, we just ignore it
230
+ nil
231
+ end
222
232
  commands | completion_data
223
233
  end
224
234
 
225
- def doc_namespace(_preposing, matched, _postposing, bind:)
226
- 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)
227
237
  end
228
238
 
229
239
  def retrieve_completion_data(input, bind:, doc_namespace:)
@@ -287,7 +297,7 @@ module IRB
287
297
  nil
288
298
  else
289
299
  sym = $1
290
- candidates = Symbol.all_symbols.collect do |s|
300
+ candidates = Symbol.all_symbols.filter_map do |s|
291
301
  s.inspect
292
302
  rescue EncodingError
293
303
  # ignore
@@ -459,12 +469,19 @@ module IRB
459
469
  eval("#{perfect_match_var}.class.name", bind) rescue nil
460
470
  else
461
471
  candidates = (bind.eval_methods | bind.eval_private_methods | bind.local_variables | bind.eval_instance_variables | bind.eval_class_constants).collect{|m| m.to_s}
462
- candidates |= ReservedWords
472
+ candidates |= RubyLex::RESERVED_WORDS.map(&:to_s)
463
473
  candidates.find{ |i| i == input }
464
474
  end
465
475
  else
466
476
  candidates = (bind.eval_methods | bind.eval_private_methods | bind.local_variables | bind.eval_instance_variables | bind.eval_class_constants).collect{|m| m.to_s}
467
- candidates |= ReservedWords
477
+ candidates |= RubyLex::RESERVED_WORDS.map(&:to_s)
478
+
479
+ target_encoding = Encoding.default_external
480
+ candidates = candidates.compact.filter_map do |candidate|
481
+ candidate.encoding == target_encoding ? candidate : candidate.encode(target_encoding)
482
+ rescue EncodingError
483
+ nil
484
+ end
468
485
  candidates.grep(/^#{Regexp.quote(input)}/).sort
469
486
  end
470
487
  end
data/lib/irb/context.rb CHANGED
@@ -632,14 +632,15 @@ module IRB
632
632
  command_class = Command.load_command(command)
633
633
  end
634
634
 
635
- # Check visibility
636
- public_method = !!KERNEL_PUBLIC_METHOD.bind_call(main, command) rescue false
637
- private_method = !public_method && !!KERNEL_METHOD.bind_call(main, command) rescue false
638
- if command_class && Command.execute_as_command?(command, public_method: public_method, private_method: private_method)
639
- Statement::Command.new(code, command_class, arg)
640
- else
641
- Statement::Expression.new(code, is_assignment_expression)
635
+ if command_class
636
+ # Check whether the command conflicts with existing methods
637
+ public_method = !!KERNEL_PUBLIC_METHOD.bind_call(main, command) rescue false
638
+ private_method = !public_method && !!KERNEL_METHOD.bind_call(main, command) rescue false
639
+ if Command.execute_as_command?(command, public_method: public_method, private_method: private_method)
640
+ return Statement::Command.new(code, command_class, arg)
641
+ end
642
642
  end
643
+ Statement::Expression.new(code, is_assignment_expression)
643
644
  end
644
645
 
645
646
  def colorize_input(input, complete:)
@@ -648,7 +649,7 @@ module IRB
648
649
  parsed_input = parse_input(input, false)
649
650
  if parsed_input.is_a?(Statement::Command)
650
651
  name, sep, arg = input.split(/(\s+)/, 2)
651
- 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
652
653
  "#{IRB::Color.colorize(name, [:BOLD])}\e[m#{sep}#{arg}"
653
654
  else
654
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 == "<internal:prelude>"
52
+ frame.realpath&.start_with?(IRB_DIR) || frame.path&.start_with?("<internal:")
53
53
  end
54
54
  frames
55
55
  end
@@ -60,7 +60,7 @@ module IRB
60
60
  if !DEBUGGER__::CONFIG[:no_hint] && irb.context.io.is_a?(RelineInputMethod)
61
61
  Reline.output_modifier_proc = proc do |input, complete:|
62
62
  unless input.strip.empty?
63
- cmd = input.split(/\s/, 2).first
63
+ cmd = input[/\S+/]
64
64
 
65
65
  if !complete && DEBUGGER__.commands.key?(cmd)
66
66
  input = input.sub(/\n$/, " # debug command\n")
@@ -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.match?(IRB_DIR) || path.match?('<internal:prelude>')
90
+ super || path&.match?(IRB_DIR) || path&.match?('<internal:prelude>')
91
91
  end
92
92
  end
93
93
 
@@ -121,12 +121,13 @@ module IRB
121
121
  interrupted = false
122
122
  prev_trap = trap("SIGINT") { interrupted = true }
123
123
  canvas = Canvas.new(Reline.get_screen_size)
124
+ otio = Reline::IOGate.prep
124
125
  Reline::IOGate.set_winch_handler do
125
126
  canvas = Canvas.new(Reline.get_screen_size)
126
127
  end
127
128
  ruby_model = RubyModel.new
128
129
  print "\e[?25l" # hide cursor
129
- 0.step do |i| # TODO (0..).each needs Ruby 2.6 or later
130
+ (0..).each do |i|
130
131
  buff = canvas.draw do
131
132
  ruby_model.render_frame(i) do |p1, p2|
132
133
  canvas.line(p1, p2)
@@ -139,6 +140,7 @@ module IRB
139
140
  end
140
141
  rescue Interrupt
141
142
  ensure
143
+ Reline::IOGate.deprep(otio)
142
144
  print "\e[?25h" # show cursor
143
145
  trap("SIGINT", prev_trap)
144
146
  end
@@ -65,6 +65,7 @@ module IRB
65
65
  @current_job = irb
66
66
  th.run
67
67
  Thread.stop
68
+ Thread.pass
68
69
  @current_job = irb(Thread.current)
69
70
  end
70
71
 
@@ -220,6 +221,7 @@ module IRB
220
221
  end
221
222
  end
222
223
  Thread.stop
224
+ Thread.pass
223
225
  @JobManager.current_job = @JobManager.irb(Thread.current)
224
226
  end
225
227
 
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"
@@ -26,10 +26,11 @@ module IRB
26
26
 
27
27
  def winsize
28
28
  if instance_variable_defined?(:@stdout) && @stdout.tty?
29
- @stdout.winsize
30
- else
31
- [24, 80]
29
+ winsize = @stdout.winsize
30
+ # If width or height is 0, something is wrong.
31
+ return winsize unless winsize.include? 0
32
32
  end
33
+ [24, 80]
33
34
  end
34
35
 
35
36
  # Whether this input method is still readable when there is no more data to
@@ -175,10 +176,15 @@ module IRB
175
176
  class ReadlineInputMethod < StdioInputMethod
176
177
  class << self
177
178
  def initialize_readline
178
- require "readline"
179
- rescue LoadError
180
- else
181
- include ::Readline
179
+ return if defined?(self::Readline)
180
+
181
+ begin
182
+ require 'readline'
183
+ const_set(:Readline, ::Readline)
184
+ rescue LoadError
185
+ const_set(:Readline, ::Reline)
186
+ end
187
+ const_set(:HISTORY, self::Readline::HISTORY)
182
188
  end
183
189
  end
184
190
 
@@ -216,8 +222,8 @@ module IRB
216
222
  def gets
217
223
  Readline.input = @stdin
218
224
  Readline.output = @stdout
219
- if l = readline(@prompt, false)
220
- HISTORY.push(l) if !l.empty?
225
+ if l = Readline.readline(@prompt, false)
226
+ Readline::HISTORY.push(l) if !l.empty? && l != Readline::HISTORY.to_a.last
221
227
  @line[@line_no += 1] = l + "\n"
222
228
  else
223
229
  @eof = true
@@ -239,7 +245,7 @@ module IRB
239
245
 
240
246
  # For debug message
241
247
  def inspect
242
- readline_impl = (defined?(Reline) && Readline == Reline) ? 'Reline' : 'ext/readline'
248
+ readline_impl = Readline == ::Reline ? 'Reline' : 'ext/readline'
243
249
  str = "ReadlineInputMethod with #{readline_impl} #{Readline::VERSION}"
244
250
  inputrc_path = File.expand_path(ENV['INPUTRC'] || '~/.inputrc')
245
251
  str += " and #{inputrc_path}" if File.exist?(inputrc_path)
@@ -249,6 +255,16 @@ module IRB
249
255
 
250
256
  class RelineInputMethod < StdioInputMethod
251
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
252
268
  include HistorySavingAbility
253
269
  # Creates a new input method object using Reline
254
270
  def initialize(completor)
@@ -299,9 +315,17 @@ module IRB
299
315
  @auto_indent_proc = block
300
316
  end
301
317
 
302
- def retrieve_doc_namespace(matched)
318
+ def retrieve_document_target(matched)
303
319
  preposing, _target, postposing, bind = @completion_params
304
- @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
305
329
  end
306
330
 
307
331
  def rdoc_ri_driver
@@ -322,146 +346,158 @@ module IRB
322
346
  input_method = self # self is changed in the lambda below.
323
347
  ->() {
324
348
  dialog.trap_key = nil
325
- alt_d = [
326
- [27, 100], # Normal Alt+d when convert-meta isn't used.
327
- # When option/alt is not configured as a meta key in terminal emulator,
328
- # option/alt + d will send a unicode character depend on OS keyboard setting.
329
- [195, 164], # "ä" in somewhere (FIXME: environment information is unknown).
330
- [226, 136, 130] # "∂" Alt+d on Mac keyboard.
331
- ]
332
-
333
- if just_cursor_moving and completion_journey_data.nil?
349
+
350
+ if just_cursor_moving && completion_journey_data.nil?
334
351
  return nil
335
352
  end
336
353
  cursor_pos_to_render, result, pointer, autocomplete_dialog = context.pop(4)
337
- return nil if result.nil? or pointer.nil? or pointer < 0
354
+ return nil if result.nil? || pointer.nil? || pointer < 0
338
355
 
339
- name = input_method.retrieve_doc_namespace(result[pointer])
340
- # Use first one because document dialog does not support multiple namespaces.
341
- name = name.first if name.is_a?(Array)
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)
342
359
 
343
- show_easter_egg = name&.match?(/\ARubyVM/) && !ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
360
+ x, width = input_method.dialog_doc_position(cursor_pos_to_render, autocomplete_dialog, screen_width)
361
+ return nil unless x
344
362
 
345
- driver = input_method.rdoc_ri_driver
363
+ dialog.trap_key = ALT_D_SEQUENCES
346
364
 
347
365
  if key.match?(dialog.name)
348
- if show_easter_egg
349
- IRB.__send__(:easter_egg)
350
- else
351
- # RDoc::RI::Driver#display_names uses pager command internally.
352
- # Some pager command like `more` doesn't use alternate screen
353
- # so we need to turn on and off alternate screen manually.
354
- begin
355
- print "\e[?1049h"
356
- driver.display_names([name])
357
- rescue RDoc::RI::Driver::NotFoundError
358
- ensure
359
- print "\e[?1049l"
360
- end
366
+ begin
367
+ print "\e[?1049h"
368
+ input_method.display_document(matched_text)
369
+ ensure
370
+ print "\e[?1049l"
361
371
  end
362
372
  end
363
373
 
364
- begin
365
- name = driver.expand_name(name)
366
- rescue RDoc::RI::Driver::NotFoundError
367
- return nil
368
- rescue
369
- return nil # unknown error
370
- end
371
- doc = nil
372
- used_for_class = false
373
- if not name =~ /#|\./
374
- found, klasses, includes, extends = driver.classes_and_includes_and_extends_for(name)
375
- if not found.empty?
376
- doc = driver.class_document(name, found, klasses, includes, extends)
377
- 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
378
382
  end
379
383
  end
380
- unless used_for_class
381
- doc = RDoc::Markup::Document.new
382
- begin
383
- driver.add_method(doc, name)
384
- rescue RDoc::RI::Driver::NotFoundError
385
- doc = nil
386
- rescue
387
- return nil # unknown error
388
- end
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)
389
424
  end
390
- return nil if doc.nil?
391
- width = 40
392
-
393
- right_x = cursor_pos_to_render.x + autocomplete_dialog.width
394
- if right_x + width > screen_width
395
- right_width = screen_width - (right_x + 1)
396
- left_x = autocomplete_dialog.column - width
397
- left_x = 0 if left_x < 0
398
- left_width = width > autocomplete_dialog.column ? autocomplete_dialog.column : width
399
- if right_width.positive? and left_width.positive?
400
- if right_width >= left_width
401
- width = right_width
402
- x = right_x
403
- else
404
- width = left_width
405
- x = left_x
406
- end
407
- elsif right_width.positive? and left_width <= 0
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
408
443
  width = right_width
409
444
  x = right_x
410
- elsif right_width <= 0 and left_width.positive?
445
+ else
411
446
  width = left_width
412
447
  x = left_x
413
- else # Both are negative width.
414
- return nil
415
448
  end
416
- else
449
+ elsif right_width.positive? && left_width <= 0
450
+ width = right_width
417
451
  x = right_x
418
- end
419
- formatter = RDoc::Markup::ToAnsi.new
420
- formatter.width = width
421
- dialog.trap_key = alt_d
422
- mod_key = RUBY_PLATFORM.match?(/darwin/) ? "Option" : "Alt"
423
- if show_easter_egg
424
- type = STDOUT.external_encoding == Encoding::UTF_8 ? :unicode : :ascii
425
- contents = IRB.send(:easter_egg_logo, type).split("\n")
426
- message = "Press #{mod_key}+d to see more"
427
- contents[0][0, message.size] = message
452
+ elsif right_width <= 0 && left_width.positive?
453
+ width = left_width
454
+ x = left_x
428
455
  else
429
- message = "Press #{mod_key}+d to read the full document"
430
- contents = [message] + doc.accept(formatter).split("\n")
456
+ return nil
431
457
  end
432
- contents = contents.take(preferred_dialog_height)
433
-
434
- y = cursor_pos_to_render.y
435
- Reline::DialogRenderInfo.new(pos: Reline::CursorPos.new(x, y), contents: contents, width: width, bg_color: '49')
436
- }
458
+ else
459
+ x = right_x
460
+ end
461
+ [x, width]
437
462
  end
438
463
 
439
464
  def display_document(matched)
440
- driver = rdoc_ri_driver
441
- return unless driver
442
-
443
- if matched =~ /\A(?:::)?RubyVM/ and not ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
444
- IRB.__send__(:easter_egg)
445
- return
446
- end
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
447
480
 
448
- namespace = retrieve_doc_namespace(matched)
449
- return unless namespace
481
+ if matched =~ /\A(?:::)?RubyVM/ && !ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
482
+ IRB.__send__(:easter_egg)
483
+ return
484
+ end
450
485
 
451
- if namespace.is_a?(Array)
452
- out = RDoc::Markup::Document.new
453
- namespace.each do |m|
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
454
496
  begin
455
- driver.add_method(out, m)
497
+ driver.display_names([target.name])
456
498
  rescue RDoc::RI::Driver::NotFoundError
457
499
  end
458
500
  end
459
- driver.display(out)
460
- else
461
- begin
462
- driver.display_names([namespace])
463
- rescue RDoc::RI::Driver::NotFoundError
464
- end
465
501
  end
466
502
  end
467
503
 
@@ -474,7 +510,7 @@ module IRB
474
510
  Reline.prompt_proc = @prompt_proc
475
511
  Reline.auto_indent_proc = @auto_indent_proc if @auto_indent_proc
476
512
  if l = Reline.readmultiline(@prompt, false, &@check_termination_proc)
477
- Reline::HISTORY.push(l) if !l.empty?
513
+ Reline::HISTORY.push(l) if !l.empty? && l != Reline::HISTORY.to_a.last
478
514
  @line[@line_no += 1] = l + "\n"
479
515
  else
480
516
  @eof = true