Class: IRB::RelineInputMethod

Inherits:
StdioInputMethod show all
Includes:
HistorySavingAbility
Defined in:
lib/irb/input-method.rb

Direct Known Subclasses

ReidlineInputMethod

Constant Summary collapse

HISTORY =
Reline::HISTORY
ALT_KEY_NAME =
RUBY_PLATFORM.match?(/darwin/) ? "Option" : "Alt"
PRESS_ALT_D_TO_READ_FULL_DOC =
"Press #{ALT_KEY_NAME}+d to read the full document".freeze
PRESS_ALT_D_TO_SEE_MORE =
"Press #{ALT_KEY_NAME}+d to see more".freeze
ALT_D_SEQUENCES =
[
  [27, 100], # Normal Alt+d when convert-meta isn't used.
  # When option/alt is not configured as a meta key in terminal emulator,
  # option/alt + d will send a unicode character depend on OS keyboard setting.
  [195, 164], # "ä" in somewhere (FIXME: environment information is unknown).
  [226, 136, 130] # "∂" Alt+d on Mac keyboard.
].freeze

Constants inherited from InputMethod

InputMethod::BASIC_WORD_BREAK_CHARACTERS

Instance Attribute Summary

Attributes inherited from InputMethod

#prompt

Instance Method Summary collapse

Methods included from HistorySavingAbility

#load_history, #reset_history_counter, #save_history, #support_history_saving?

Methods inherited from StdioInputMethod

#encoding, #line, #readable_after_eof?

Methods inherited from InputMethod

#readable_after_eof?, #support_history_saving?, #winsize

Constructor Details

#initialize(completor) ⇒ RelineInputMethod

Creates a new input method object using Reline



270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/irb/input-method.rb', line 270

def initialize(completor)
  IRB.__send__(:set_encoding, Reline.encoding_system_needs.name, override: false)

  super()

  @eof = false
  @completor = completor

  Reline.basic_word_break_characters = BASIC_WORD_BREAK_CHARACTERS
  Reline.completion_append_character = nil
  Reline.completer_quote_characters = ''
  Reline.completion_proc = ->(target, preposing, postposing) {
    bind = IRB.conf[:MAIN_CONTEXT].workspace.binding
    @completion_params = [preposing, target, postposing, bind]
    @completor.completion_candidates(preposing, target, postposing, bind: bind)
  }
  Reline.output_modifier_proc = proc do |input, complete:|
    IRB.CurrentContext.colorize_input(input, complete: complete)
  end
  Reline.dig_perfect_match_proc = ->(matched) { display_document(matched) }
  Reline.autocompletion = IRB.conf[:USE_AUTOCOMPLETE]

  if IRB.conf[:USE_AUTOCOMPLETE]
    begin
      require 'rdoc'
      Reline.add_dialog_proc(:show_doc, show_doc_dialog_proc, Reline::DEFAULT_DIALOG_CONTEXT)
    rescue LoadError
    end
  end
end

Instance Method Details

#auto_indent(&block) ⇒ Object



314
315
316
# File 'lib/irb/input-method.rb', line 314

def auto_indent(&block)
  @auto_indent_proc = block
end

#check_termination(&block) ⇒ Object



306
307
308
# File 'lib/irb/input-method.rb', line 306

def check_termination(&block)
  @check_termination_proc = block
end

#command_doc_dialog_contents(command_name, width) ⇒ Object



392
393
394
395
396
397
# File 'lib/irb/input-method.rb', line 392

def command_doc_dialog_contents(command_name, width)
  command_class = IRB::Command.load_command(command_name)
  return unless command_class

  [PRESS_ALT_D_TO_READ_FULL_DOC, ""] + command_class.doc_dialog_content(command_name, width)
end

#completion_infoObject



301
302
303
304
# File 'lib/irb/input-method.rb', line 301

def completion_info
  autocomplete_message = Reline.autocompletion ? 'Autocomplete' : 'Tab Complete'
  "#{autocomplete_message}, #{@completor.inspect}"
end

#dialog_doc_position(cursor_pos_to_render, autocomplete_dialog, screen_width) ⇒ Object



433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
# File 'lib/irb/input-method.rb', line 433

def dialog_doc_position(cursor_pos_to_render, autocomplete_dialog, screen_width)
  width = 40
  right_x = cursor_pos_to_render.x + autocomplete_dialog.width
  if right_x + width > screen_width
    right_width = screen_width - (right_x + 1)
    left_x = autocomplete_dialog.column - width
    left_x = 0 if left_x < 0
    left_width = width > autocomplete_dialog.column ? autocomplete_dialog.column : width
    if right_width.positive? && left_width.positive?
      if right_width >= left_width
        width = right_width
        x = right_x
      else
        width = left_width
        x = left_x
      end
    elsif right_width.positive? && left_width <= 0
      width = right_width
      x = right_x
    elsif right_width <= 0 && left_width.positive?
      width = left_width
      x = left_x
    else
      return nil
    end
  else
    x = right_x
  end
  [x, width]
end

#display_document(matched) ⇒ Object



464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
# File 'lib/irb/input-method.rb', line 464

def display_document(matched)
  target = retrieve_document_target(matched)
  return unless target

  case target
  when CommandDocument
    command_class = IRB::Command.load_command(target.name)
    if command_class
      content = command_class.help_message || command_class.description
      Pager.page(retain_content: true) do |io|
        io.puts content
      end
    end
  when MethodDocument
    driver = rdoc_ri_driver
    return unless driver

    if matched =~ /\A(?:::)?RubyVM/ && !ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
      IRB.__send__(:easter_egg)
      return
    end

    if target.names.length > 1
      out = RDoc::Markup::Document.new
      target.names.each do |m|
        begin
          driver.add_method(out, m)
        rescue RDoc::RI::Driver::NotFoundError
        end
      end
      driver.display(out)
    else
      begin
        driver.display_names([target.name])
      rescue RDoc::RI::Driver::NotFoundError
      end
    end
  end
end

#dynamic_prompt(&block) ⇒ Object



310
311
312
# File 'lib/irb/input-method.rb', line 310

def dynamic_prompt(&block)
  @prompt_proc = block
end

#easter_egg_dialog_contentsObject



399
400
401
402
403
404
# File 'lib/irb/input-method.rb', line 399

def easter_egg_dialog_contents
  type = STDOUT.external_encoding == Encoding::UTF_8 ? :unicode : :ascii
  lines = IRB.send(:easter_egg_logo, type).split("\n")
  lines[0][0, PRESS_ALT_D_TO_SEE_MORE.size] = PRESS_ALT_D_TO_SEE_MORE
  lines
end

#eof?Boolean

Whether the end of this input method has been reached, returns true if there is no more data to read.

See IO#eof? for more information.

Returns:

  • (Boolean)


525
526
527
# File 'lib/irb/input-method.rb', line 525

def eof?
  @eof
end

#getsObject

Reads the next line from this input method.

See IO#gets for more information.



507
508
509
510
511
512
513
514
515
516
517
518
519
# File 'lib/irb/input-method.rb', line 507

def gets
  Reline.input = @stdin
  Reline.output = @stdout
  Reline.prompt_proc = @prompt_proc
  Reline.auto_indent_proc = @auto_indent_proc if @auto_indent_proc
  if l = Reline.readmultiline(@prompt, false, &@check_termination_proc)
    Reline::HISTORY.push(l) if !l.empty? && l != Reline::HISTORY.to_a.last
    @line[@line_no += 1] = l + "\n"
  else
    @eof = true
    l
  end
end

#inspectObject

For debug message



534
535
536
537
538
539
540
# File 'lib/irb/input-method.rb', line 534

def inspect
  config = Reline::Config.new
  str = "RelineInputMethod with Reline #{Reline::VERSION}"
  inputrc_path = File.expand_path(config.inputrc_path)
  str += " and #{inputrc_path}" if File.exist?(inputrc_path)
  str
end

#prompting?Boolean

Returns:

  • (Boolean)


529
530
531
# File 'lib/irb/input-method.rb', line 529

def prompting?
  true
end

#rdoc_dialog_contents(name, width) ⇒ Object



406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
# File 'lib/irb/input-method.rb', line 406

def rdoc_dialog_contents(name, width)
  driver = rdoc_ri_driver
  return unless driver

  name = driver.expand_name(name)

  doc = if name =~ /#|\./
    d = RDoc::Markup::Document.new
    driver.add_method(d, name)
    d
  else
    found, klasses, includes, extends = driver.classes_and_includes_and_extends_for(name)
    if found.empty?
      d = RDoc::Markup::Document.new
      driver.add_method(d, name)
      d
    else
      driver.class_document(name, found, klasses, includes, extends)
    end
  end

  formatter = RDoc::Markup::ToAnsi.new
  formatter.width = width
  [PRESS_ALT_D_TO_READ_FULL_DOC] + doc.accept(formatter).split("\n")
rescue RDoc::RI::Driver::NotFoundError
end

#rdoc_ri_driverObject



331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/irb/input-method.rb', line 331

def rdoc_ri_driver
  return @rdoc_ri_driver if defined?(@rdoc_ri_driver)

  begin
    require 'rdoc'
  rescue LoadError
    @rdoc_ri_driver = nil
  else
    options = {}
    options[:extra_doc_dirs] = IRB.conf[:EXTRA_DOC_DIRS] unless IRB.conf[:EXTRA_DOC_DIRS].empty?
    @rdoc_ri_driver = RDoc::RI::Driver.new(options)
  end
end

#retrieve_document_target(matched) ⇒ Object



318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/irb/input-method.rb', line 318

def retrieve_document_target(matched)
  preposing, _target, postposing, bind = @completion_params
  result = @completor.doc_namespace(preposing, matched, postposing, bind: bind)
  case result
  when DocumentTarget, nil
    result
  when Array
    MethodDocument.new(*result)
  when String
    MethodDocument.new(result)
  end
end

#show_doc_dialog_procObject



345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/irb/input-method.rb', line 345

def show_doc_dialog_proc
  input_method = self # self is changed in the lambda below.
  ->() {
    dialog.trap_key = nil

    if just_cursor_moving && completion_journey_data.nil?
      return nil
    end
    cursor_pos_to_render, result, pointer, autocomplete_dialog = context.pop(4)
    return nil if result.nil? || pointer.nil? || pointer < 0

    matched_text = result[pointer]
    show_easter_egg = matched_text&.match?(/\ARubyVM/) && !ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']
    target = show_easter_egg ? nil : input_method.retrieve_document_target(matched_text)

    x, width = input_method.dialog_doc_position(cursor_pos_to_render, autocomplete_dialog, screen_width)
    return nil unless x

    dialog.trap_key = ALT_D_SEQUENCES

    if key.match?(dialog.name)
      begin
        print "\e[?1049h"
        input_method.display_document(matched_text)
      ensure
        print "\e[?1049l"
      end
    end

    contents = case target
    when CommandDocument
      input_method.command_doc_dialog_contents(target.name, width)
    when MethodDocument
      input_method.rdoc_dialog_contents(target.name, width)
    else
      if show_easter_egg
        input_method.easter_egg_dialog_contents
      end
    end
    return nil unless contents

    contents = contents.take(preferred_dialog_height)
    y = cursor_pos_to_render.y
    Reline::DialogRenderInfo.new(pos: Reline::CursorPos.new(x, y), contents: contents, width: width, bg_color: '49')
  }
end