aboutsummaryrefslogtreecommitdiffstats
path: root/lib/reline/key_stroke.rb
blob: e097924aec2b8d578d7a3ae7fcb47308a3958d02 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
class Reline::KeyStroke
  using Module.new {
    refine Integer do
      def ==(other)
        if other.is_a?(Reline::Key)
          if other.combined_char == "\e".ord
            false
          else
            other.combined_char == self
          end
        else
          super
        end
      end
    end

    refine Array do
      def start_with?(other)
        compressed_me = compress_meta_key
        compressed_other = other.compress_meta_key
        i = 0
        loop do
          my_c = compressed_me[i]
          other_c = compressed_other[i]
          other_is_last = (i + 1) == compressed_other.size
          me_is_last = (i + 1) == compressed_me.size
          if my_c != other_c
            if other_c == "\e".ord and other_is_last and my_c.is_a?(Reline::Key) and my_c.with_meta
              return true
            else
              return false
            end
          elsif other_is_last
            return true
          elsif me_is_last
            return false
          end
          i += 1
        end
      end

      def ==(other)
        compressed_me = compress_meta_key
        compressed_other = other.compress_meta_key
        compressed_me.size == compressed_other.size and [compressed_me, compressed_other].transpose.all?{ |i| i[0] == i[1] }
      end

      def compress_meta_key
        inject([]) { |result, key|
          if result.size > 0 and result.last == "\e".ord
            result[result.size - 1] = Reline::Key.new(key, key | 0b10000000, true)
          else
            result << key
          end
          result
        }
      end

      def bytes
        self
      end
    end
  }

  def initialize(config)
    @config = config
  end

  def match_status(input)
    key_mapping.keys.select { |lhs|
      lhs.start_with? input
    }.tap { |it|
      return :matched  if it.size == 1 && (it.max_by(&:size)&.== input)
      return :matching if it.size == 1 && (it.max_by(&:size)&.!= input)
      return :matched  if it.max_by(&:size)&.size&.< input.size
      return :matching if it.size > 1
    }
    key_mapping.keys.select { |lhs|
      input.start_with? lhs
    }.tap { |it|
      return it.size > 0 ? :matched : :unmatched
    }
  end

  def expand(input)
    input = input.compress_meta_key
    lhs = key_mapping.keys.select { |item| input.start_with? item }.sort_by(&:size).last
    return input unless lhs
    rhs = key_mapping[lhs]

    case rhs
    when String
      rhs_bytes = rhs.bytes
      expand(expand(rhs_bytes) + expand(input.drop(lhs.size)))
    when Symbol
      [rhs] + expand(input.drop(lhs.size))
    when Array
      rhs
    end
  end

  private

  def key_mapping
    @config.key_bindings
  end
end