class IRB::RelineInputMethod

Constants

HISTORY

Public Class Methods

new(completor) click to toggle source

Creates a new input method object using Reline

Calls superclass method IRB::StdioInputMethod::new
# File irb/input-method.rb, line 254
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

Public Instance Methods

auto_indent(&block) click to toggle source
# File irb/input-method.rb, line 298
def auto_indent(&block)
  @auto_indent_proc = block
end
check_termination(&block) click to toggle source
# File irb/input-method.rb, line 290
def check_termination(&block)
  @check_termination_proc = block
end
completion_info() click to toggle source
# File irb/input-method.rb, line 285
def completion_info
  autocomplete_message = Reline.autocompletion ? 'Autocomplete' : 'Tab Complete'
  "#{autocomplete_message}, #{@completor.inspect}"
end
display_document(matched) click to toggle source
# File irb/input-method.rb, line 439
def display_document(matched)
  driver = rdoc_ri_driver
  return unless driver

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

  namespace = retrieve_doc_namespace(matched)
  return unless namespace

  if namespace.is_a?(Array)
    out = RDoc::Markup::Document.new
    namespace.each do |m|
      begin
        driver.add_method(out, m)
      rescue RDoc::RI::Driver::NotFoundError
      end
    end
    driver.display(out)
  else
    begin
      driver.display_names([namespace])
    rescue RDoc::RI::Driver::NotFoundError
    end
  end
end
dynamic_prompt(&block) click to toggle source
# File irb/input-method.rb, line 294
def dynamic_prompt(&block)
  @prompt_proc = block
end
eof?() click to toggle source

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.

# File irb/input-method.rb, line 489
def eof?
  @eof
end
gets() click to toggle source

Reads the next line from this input method.

See IO#gets for more information.

# File irb/input-method.rb, line 471
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?
    @line[@line_no += 1] = l + "\n"
  else
    @eof = true
    l
  end
end
inspect() click to toggle source

For debug message

# File irb/input-method.rb, line 498
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?() click to toggle source
# File irb/input-method.rb, line 493
def prompting?
  true
end
rdoc_ri_driver() click to toggle source
# File irb/input-method.rb, line 307
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_doc_namespace(matched) click to toggle source
# File irb/input-method.rb, line 302
def retrieve_doc_namespace(matched)
  preposing, _target, postposing, bind = @completion_params
  @completor.doc_namespace(preposing, matched, postposing, bind: bind)
end
show_doc_dialog_proc() click to toggle source
# File irb/input-method.rb, line 321
def show_doc_dialog_proc
  input_method = self # self is changed in the lambda below.
  ->() {
    dialog.trap_key = nil
    alt_d = [
      [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.
    ]

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

    name = input_method.retrieve_doc_namespace(result[pointer])
    # Use first one because document dialog does not support multiple namespaces.
    name = name.first if name.is_a?(Array)

    show_easter_egg = name&.match?(/\ARubyVM/) && !ENV['RUBY_YES_I_AM_NOT_A_NORMAL_USER']

    driver = input_method.rdoc_ri_driver

    if key.match?(dialog.name)
      if show_easter_egg
        IRB.__send__(:easter_egg)
      else
        # RDoc::RI::Driver#display_names uses pager command internally.
        # Some pager command like `more` doesn't use alternate screen
        # so we need to turn on and off alternate screen manually.
        begin
          print "\e[?1049h"
          driver.display_names([name])
        rescue RDoc::RI::Driver::NotFoundError
        ensure
          print "\e[?1049l"
        end
      end
    end

    begin
      name = driver.expand_name(name)
    rescue RDoc::RI::Driver::NotFoundError
      return nil
    rescue
      return nil # unknown error
    end
    doc = nil
    used_for_class = false
    if not name =~ /#|\./
      found, klasses, includes, extends = driver.classes_and_includes_and_extends_for(name)
      if not found.empty?
        doc = driver.class_document(name, found, klasses, includes, extends)
        used_for_class = true
      end
    end
    unless used_for_class
      doc = RDoc::Markup::Document.new
      begin
        driver.add_method(doc, name)
      rescue RDoc::RI::Driver::NotFoundError
        doc = nil
      rescue
        return nil # unknown error
      end
    end
    return nil if doc.nil?
    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? and 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? and left_width <= 0
        width = right_width
        x = right_x
      elsif right_width <= 0 and left_width.positive?
        width = left_width
        x = left_x
      else # Both are negative width.
        return nil
      end
    else
      x = right_x
    end
    formatter = RDoc::Markup::ToAnsi.new
    formatter.width = width
    dialog.trap_key = alt_d
    mod_key = RUBY_PLATFORM.match?(/darwin/) ? "Option" : "Alt"
    if show_easter_egg
      type = STDOUT.external_encoding == Encoding::UTF_8 ? :unicode : :ascii
      contents = IRB.send(:easter_egg_logo, type).split("\n")
      message = "Press #{mod_key}+d to see more"
      contents[0][0, message.size] = message
    else
      message = "Press #{mod_key}+d to read the full document"
      contents = [message] + doc.accept(formatter).split("\n")
    end
    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