reline 0.3.9 → 0.6.1

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.
@@ -3,142 +3,113 @@ class Reline::KeyStroke
3
3
  CSI_PARAMETER_BYTES_RANGE = 0x30..0x3f
4
4
  CSI_INTERMEDIATE_BYTES_RANGE = (0x20..0x2f)
5
5
 
6
- def initialize(config)
7
- @config = config
8
- end
6
+ attr_accessor :encoding
9
7
 
10
- def compress_meta_key(ary)
11
- return ary unless @config.convert_meta
12
- ary.inject([]) { |result, key|
13
- if result.size > 0 and result.last == "\e".ord
14
- result[result.size - 1] = Reline::Key.new(key, key | 0b10000000, true)
15
- else
16
- result << key
17
- end
18
- result
19
- }
8
+ def initialize(config, encoding)
9
+ @config = config
10
+ @encoding = encoding
20
11
  end
21
12
 
22
- def start_with?(me, other)
23
- compressed_me = compress_meta_key(me)
24
- compressed_other = compress_meta_key(other)
25
- i = 0
26
- loop do
27
- my_c = compressed_me[i]
28
- other_c = compressed_other[i]
29
- other_is_last = (i + 1) == compressed_other.size
30
- me_is_last = (i + 1) == compressed_me.size
31
- if my_c != other_c
32
- if other_c == "\e".ord and other_is_last and my_c.is_a?(Reline::Key) and my_c.with_meta
33
- return true
34
- else
35
- return false
36
- end
37
- elsif other_is_last
38
- return true
39
- elsif me_is_last
40
- return false
41
- end
42
- i += 1
43
- end
44
- end
13
+ # Input exactly matches to a key sequence
14
+ MATCHING = :matching
15
+ # Input partially matches to a key sequence
16
+ MATCHED = :matched
17
+ # Input matches to a key sequence and the key sequence is a prefix of another key sequence
18
+ MATCHING_MATCHED = :matching_matched
19
+ # Input does not match to any key sequence
20
+ UNMATCHED = :unmatched
45
21
 
46
- def equal?(me, other)
47
- case me
48
- when Array
49
- compressed_me = compress_meta_key(me)
50
- compressed_other = compress_meta_key(other)
51
- compressed_me.size == compressed_other.size and [compressed_me, compressed_other].transpose.all?{ |i| equal?(i[0], i[1]) }
52
- when Integer
53
- if other.is_a?(Reline::Key)
54
- if other.combined_char == "\e".ord
55
- false
56
- else
57
- other.combined_char == me
58
- end
59
- else
60
- me == other
61
- end
62
- when Reline::Key
63
- if other.is_a?(Integer)
64
- me.combined_char == other
22
+ def match_status(input)
23
+ matching = key_mapping.matching?(input)
24
+ matched = key_mapping.get(input)
25
+ if matching && matched
26
+ MATCHING_MATCHED
27
+ elsif matching
28
+ MATCHING
29
+ elsif matched
30
+ MATCHED
31
+ elsif input[0] == ESC_BYTE
32
+ match_unknown_escape_sequence(input, vi_mode: @config.editing_mode_is?(:vi_insert, :vi_command))
33
+ else
34
+ s = input.pack('c*').force_encoding(@encoding)
35
+ if s.valid_encoding?
36
+ s.size == 1 ? MATCHED : UNMATCHED
65
37
  else
66
- me == other
38
+ # Invalid string is MATCHING (part of valid string) or MATCHED (invalid bytes to be ignored)
39
+ MATCHING_MATCHED
67
40
  end
68
41
  end
69
42
  end
70
43
 
71
- def match_status(input)
72
- key_mapping.keys.select { |lhs|
73
- start_with?(lhs, input)
74
- }.tap { |it|
75
- return :matched if it.size == 1 && equal?(it[0], input)
76
- return :matching if it.size == 1 && !equal?(it[0], input)
77
- return :matched if it.max_by(&:size)&.size&.< input.size
78
- return :matching if it.size > 1
79
- }
80
- if key_mapping.keys.any? { |lhs| start_with?(input, lhs) }
81
- :matched
82
- else
83
- match_unknown_escape_sequence(input).first
44
+ def expand(input)
45
+ matched_bytes = nil
46
+ (1..input.size).each do |i|
47
+ bytes = input.take(i)
48
+ status = match_status(bytes)
49
+ matched_bytes = bytes if status == MATCHED || status == MATCHING_MATCHED
50
+ break if status == MATCHED || status == UNMATCHED
84
51
  end
85
- end
52
+ return [[], []] unless matched_bytes
86
53
 
87
- def expand(input)
88
- lhs = key_mapping.keys.select { |item| start_with?(input, item) }.sort_by(&:size).last
89
- unless lhs
90
- status, size = match_unknown_escape_sequence(input)
91
- case status
92
- when :matched
93
- return [:ed_unassigned] + expand(input.drop(size))
94
- when :matching
95
- return [:ed_unassigned]
54
+ func = key_mapping.get(matched_bytes)
55
+ s = matched_bytes.pack('c*').force_encoding(@encoding)
56
+ if func.is_a?(Array)
57
+ # Perform simple macro expansion for single byte key bindings.
58
+ # Multibyte key bindings and recursive macro expansion are not supported yet.
59
+ macro = func.pack('c*').force_encoding(@encoding)
60
+ keys = macro.chars.map do |c|
61
+ f = key_mapping.get(c.bytes)
62
+ Reline::Key.new(c, f.is_a?(Symbol) ? f : :ed_insert, false)
63
+ end
64
+ elsif func
65
+ keys = [Reline::Key.new(s, func, false)]
66
+ else
67
+ if s.valid_encoding? && s.size == 1
68
+ keys = [Reline::Key.new(s, :ed_insert, false)]
96
69
  else
97
- return input
70
+ keys = []
98
71
  end
99
72
  end
100
- rhs = key_mapping[lhs]
101
73
 
102
- case rhs
103
- when String
104
- rhs_bytes = rhs.bytes
105
- expand(expand(rhs_bytes) + expand(input.drop(lhs.size)))
106
- when Symbol
107
- [rhs] + expand(input.drop(lhs.size))
108
- when Array
109
- rhs
110
- end
74
+ [keys, input.drop(matched_bytes.size)]
111
75
  end
112
76
 
113
77
  private
114
78
 
115
79
  # returns match status of CSI/SS3 sequence and matched length
116
- def match_unknown_escape_sequence(input)
80
+ def match_unknown_escape_sequence(input, vi_mode: false)
117
81
  idx = 0
118
- return [:unmatched, nil] unless input[idx] == ESC_BYTE
82
+ return UNMATCHED unless input[idx] == ESC_BYTE
119
83
  idx += 1
120
84
  idx += 1 if input[idx] == ESC_BYTE
121
85
 
122
86
  case input[idx]
123
87
  when nil
124
- return [:matching, nil]
88
+ if idx == 1 # `ESC`
89
+ return MATCHING_MATCHED
90
+ else # `ESC ESC`
91
+ return MATCHING
92
+ end
125
93
  when 91 # == '['.ord
126
- # CSI sequence
94
+ # CSI sequence `ESC [ ... char`
127
95
  idx += 1
128
96
  idx += 1 while idx < input.size && CSI_PARAMETER_BYTES_RANGE.cover?(input[idx])
129
97
  idx += 1 while idx < input.size && CSI_INTERMEDIATE_BYTES_RANGE.cover?(input[idx])
130
- input[idx] ? [:matched, idx + 1] : [:matching, nil]
131
98
  when 79 # == 'O'.ord
132
- # SS3 sequence
133
- input[idx + 1] ? [:matched, idx + 2] : [:matching, nil]
99
+ # SS3 sequence `ESC O char`
100
+ idx += 1
134
101
  else
135
- if idx == 1
136
- # `ESC char`, make it :unmatched so that it will be handled correctly in `read_2nd_character_of_key_sequence`
137
- [:unmatched, nil]
138
- else
139
- # `ESC ESC char`
140
- [:matched, idx + 1]
141
- end
102
+ # `ESC char` or `ESC ESC char`
103
+ return UNMATCHED if vi_mode
104
+ end
105
+
106
+ case input.size
107
+ when idx
108
+ MATCHING
109
+ when idx + 1
110
+ MATCHED
111
+ else
112
+ UNMATCHED
142
113
  end
143
114
  end
144
115
 
@@ -14,7 +14,7 @@ class Reline::KillRing
14
14
  end
15
15
 
16
16
  def ==(other)
17
- object_id == other.object_id
17
+ equal?(other)
18
18
  end
19
19
  end
20
20
 
@@ -68,7 +68,7 @@ class Reline::KillRing
68
68
  def append(string, before_p = false)
69
69
  case @state
70
70
  when State::FRESH, State::YANK
71
- @ring << RingPoint.new(string)
71
+ @ring << RingPoint.new(+string)
72
72
  @state = State::CONTINUED
73
73
  when State::CONTINUED, State::PROCESSED
74
74
  if before_p