123456789_123456789_123456789_123456789_123456789_

Class: Reline::KeyStroke

Relationships & Source Files
Inherits: Object
Defined in: lib/reline/key_stroke.rb

Constant Summary

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(config, encoding) ⇒ KeyStroke

[ GitHub ]

  
# File 'lib/reline/key_stroke.rb', line 8

def initialize(config, encoding)
  @config = config
  @encoding = encoding
end

Instance Attribute Details

#encoding (rw)

[ GitHub ]

  
# File 'lib/reline/key_stroke.rb', line 6

attr_accessor :encoding

Instance Method Details

#expand(input)

[ GitHub ]

  
# File 'lib/reline/key_stroke.rb', line 49

def expand(input)
  matched_bytes = nil
  (1..input.size).each do |i|
    bytes = input.take(i)
    status = match_status(bytes)
    matched_bytes = bytes if status == MATCHED || status == MATCHING_MATCHED
    break if status == MATCHED || status == UNMATCHED
  end
  return [[], []] unless matched_bytes

  func = key_mapping.get(matched_bytes)
  if func.is_a?(Array)
    keys = func.map { |c| Reline::Key.new(c, c, false) }
  elsif func
    keys = [Reline::Key.new(func, func, false)]
  elsif matched_bytes.size == 2 && matched_bytes[0] == ESC_BYTE
    keys = [Reline::Key.new(matched_bytes[1], matched_bytes[1] | 0b10000000, true)]
  else
    s = matched_bytes.pack('c*').force_encoding(@encoding)
    if s.valid_encoding? && s.size == 1
      keys = [Reline::Key.new(s.ord, s.ord, false)]
    else
      keys = []
    end
  end

  [keys, input.drop(matched_bytes.size)]
end

#key_mapping (private)

[ GitHub ]

  
# File 'lib/reline/key_stroke.rb', line 117

def key_mapping
  @config.key_bindings
end

#match_status(input)

[ GitHub ]

  
# File 'lib/reline/key_stroke.rb', line 22

def match_status(input)
  matching = key_mapping.matching?(input)
  matched = key_mapping.get(input)

  # FIXME: Workaround for single byte. remove this after MAPPING is merged into KeyActor.
  matched ||= input.size == 1 && input[0] < 0x80
  matching ||= input == [ESC_BYTE]

  if matching && matched
    MATCHING_MATCHED
  elsif matching
    MATCHING
  elsif matched
    MATCHED
  elsif input[0] == ESC_BYTE
    match_unknown_escape_sequence(input, vi_mode: @config.editing_mode_is?(:vi_insert, :vi_command))
  else
    s = input.pack('c*').force_encoding(@encoding)
    if s.valid_encoding?
      s.size == 1 ? MATCHED : UNMATCHED
    else
      # Invalid string is MATCHING (part of valid string) or MATCHED (invalid bytes to be ignored)
      MATCHING_MATCHED
    end
  end
end

#match_unknown_escape_sequence(input, vi_mode: false) (private)

returns match status of CSI/SS3 sequence and matched length

[ GitHub ]

  
# File 'lib/reline/key_stroke.rb', line 81

def match_unknown_escape_sequence(input, vi_mode: false)
  idx = 0
  return UNMATCHED unless input[idx] == ESC_BYTE
  idx += 1
  idx += 1 if input[idx] == ESC_BYTE

  case input[idx]
  when nil
    if idx == 1 # `ESC`
      return MATCHING_MATCHED
    else # `ESC ESC`
      return MATCHING
    end
  when 91 # == '['.ord
    # CSI sequence `ESC [ ... char`
    idx += 1
    idx += 1 while idx < input.size && CSI_PARAMETER_BYTES_RANGE.cover?(input[idx])
    idx += 1 while idx < input.size && CSI_INTERMEDIATE_BYTES_RANGE.cover?(input[idx])
  when 79 # == 'O'.ord
    # SS3 sequence `ESC O char`
    idx += 1
  else
    # `ESC char` or `ESC ESC char`
    return UNMATCHED if vi_mode
  end

  case input.size
  when idx
    MATCHING
  when idx + 1
    MATCHED
  else
    UNMATCHED
  end
end