1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Support Meta key in Reline

This commit is contained in:
aycabta 2019-05-24 23:38:40 +09:00
parent 2d34087a38
commit eb4e774711
11 changed files with 282 additions and 217 deletions

View file

@ -1,4 +1,5 @@
require 'io/console' require 'io/console'
require 'timeout'
require 'reline/version' require 'reline/version'
require 'reline/config' require 'reline/config'
require 'reline/key_actor' require 'reline/key_actor'
@ -6,6 +7,8 @@ require 'reline/key_stroke'
require 'reline/line_editor' require 'reline/line_editor'
module Reline module Reline
Key = Struct.new('Key', :char, :combined_char, :with_meta)
extend self extend self
FILENAME_COMPLETION_PROC = nil FILENAME_COMPLETION_PROC = nil
USERNAME_COMPLETION_PROC = nil USERNAME_COMPLETION_PROC = nil
@ -321,8 +324,7 @@ module Reline
key_stroke = Reline::KeyStroke.new(config) key_stroke = Reline::KeyStroke.new(config)
begin begin
loop do loop do
c = Reline::IOGate.getc key_stroke.read_io(@@config.keyseq_timeout) { |inputs|
key_stroke.input_to!(c)&.then { |inputs|
inputs.each { |c| inputs.each { |c|
@@line_editor.input_key(c) @@line_editor.input_key(c)
@@line_editor.rerender @@line_editor.rerender

View file

@ -9,7 +9,11 @@ class Reline::ANSI
@@output = val @@output = val
end end
@@buf = []
def self.getc def self.getc
unless @@buf.empty?
return @@buf.shift
end
c = nil c = nil
loop do loop do
result = select([@@input], [], [], 0.1) result = select([@@input], [], [], 0.1)
@ -20,6 +24,10 @@ class Reline::ANSI
c&.ord c&.ord
end end
def self.ungetc(c)
@@buf.unshift(c)
end
def self.get_screen_size def self.get_screen_size
@@input.winsize @@input.winsize
rescue Errno::ENOTTY rescue Errno::ENOTTY

View file

@ -16,6 +16,7 @@ class Reline::Config
history-size history-size
horizontal-scroll-mode horizontal-scroll-mode
input-meta input-meta
keyseq-timeout
mark-directories mark-directories
mark-modified-lines mark-modified-lines
mark-symlinked-directories mark-symlinked-directories
@ -44,6 +45,7 @@ class Reline::Config
@key_actors[:vi_insert] = Reline::KeyActor::ViInsert.new @key_actors[:vi_insert] = Reline::KeyActor::ViInsert.new
@key_actors[:vi_command] = Reline::KeyActor::ViCommand.new @key_actors[:vi_command] = Reline::KeyActor::ViCommand.new
@history_size = 500 @history_size = 500
@keyseq_timeout = 500
end end
def reset def reset
@ -178,6 +180,8 @@ class Reline::Config
when 'vi-insert' when 'vi-insert'
@keymap_label = :vi_insert @keymap_label = :vi_insert
end end
when 'keyseq-timeout'
@keyseq_timeout = value.to_i
end end
end end

View file

@ -8,6 +8,9 @@ class Reline::GeneralIO
end end
def self.getc def self.getc
unless @@buf.empty?
return @@buf.shift
end
c = nil c = nil
loop do loop do
result = select([@@input], [], [], 0.1) result = select([@@input], [], [], 0.1)
@ -18,6 +21,10 @@ class Reline::GeneralIO
c&.ord c&.ord
end end
def self.ungetc(c)
@@buf.unshift(c)
end
def self.get_screen_size def self.get_screen_size
[1, 1] [1, 1]
end end

View file

@ -277,13 +277,13 @@ class Reline::KeyActor::Emacs < Reline::KeyActor::Base
# 137 M-^I # 137 M-^I
:ed_unassigned, :ed_unassigned,
# 138 M-^J # 138 M-^J
:ed_unassigned, :key_newline,
# 139 M-^K # 139 M-^K
:ed_unassigned, :ed_unassigned,
# 140 M-^L # 140 M-^L
:ed_clear_screen, :ed_clear_screen,
# 141 M-^M # 141 M-^M
:ed_unassigned, :key_newline,
# 142 M-^N # 142 M-^N
:ed_unassigned, :ed_unassigned,
# 143 M-^O # 143 M-^O

View file

@ -257,261 +257,261 @@ class Reline::KeyActor::ViInsert < Reline::KeyActor::Base
# 127 ^? # 127 ^?
:vi_delete_prev_char, :vi_delete_prev_char,
# 128 M-^@ # 128 M-^@
:ed_insert, :ed_unassigned,
# 129 M-^A # 129 M-^A
:ed_insert, :ed_unassigned,
# 130 M-^B # 130 M-^B
:ed_insert, :ed_unassigned,
# 131 M-^C # 131 M-^C
:ed_insert, :ed_unassigned,
# 132 M-^D # 132 M-^D
:ed_insert, :ed_unassigned,
# 133 M-^E # 133 M-^E
:ed_insert, :ed_unassigned,
# 134 M-^F # 134 M-^F
:ed_insert, :ed_unassigned,
# 135 M-^G # 135 M-^G
:ed_insert, :ed_unassigned,
# 136 M-^H # 136 M-^H
:ed_insert, :ed_unassigned,
# 137 M-^I # 137 M-^I
:ed_insert, :ed_unassigned,
# 138 M-^J # 138 M-^J
:ed_insert, :key_newline,
# 139 M-^K # 139 M-^K
:ed_insert, :ed_unassigned,
# 140 M-^L # 140 M-^L
:ed_insert, :ed_unassigned,
# 141 M-^M # 141 M-^M
:ed_insert, :key_newline,
# 142 M-^N # 142 M-^N
:ed_insert, :ed_unassigned,
# 143 M-^O # 143 M-^O
:ed_insert, :ed_unassigned,
# 144 M-^P # 144 M-^P
:ed_insert, :ed_unassigned,
# 145 M-^Q # 145 M-^Q
:ed_insert, :ed_unassigned,
# 146 M-^R # 146 M-^R
:ed_insert, :ed_unassigned,
# 147 M-^S # 147 M-^S
:ed_insert, :ed_unassigned,
# 148 M-^T # 148 M-^T
:ed_insert, :ed_unassigned,
# 149 M-^U # 149 M-^U
:ed_insert, :ed_unassigned,
# 150 M-^V # 150 M-^V
:ed_insert, :ed_unassigned,
# 151 M-^W # 151 M-^W
:ed_insert, :ed_unassigned,
# 152 M-^X # 152 M-^X
:ed_insert, :ed_unassigned,
# 153 M-^Y # 153 M-^Y
:ed_insert, :ed_unassigned,
# 154 M-^Z # 154 M-^Z
:ed_insert, :ed_unassigned,
# 155 M-^[ # 155 M-^[
:ed_insert, :ed_unassigned,
# 156 M-^\ # 156 M-^\
:ed_insert, :ed_unassigned,
# 157 M-^] # 157 M-^]
:ed_insert, :ed_unassigned,
# 158 M-^^ # 158 M-^^
:ed_insert, :ed_unassigned,
# 159 M-^_ # 159 M-^_
:ed_insert, :ed_unassigned,
# 160 M-SPACE # 160 M-SPACE
:ed_insert, :ed_unassigned,
# 161 M-! # 161 M-!
:ed_insert, :ed_unassigned,
# 162 M-" # 162 M-"
:ed_insert, :ed_unassigned,
# 163 M-# # 163 M-#
:ed_insert, :ed_unassigned,
# 164 M-$ # 164 M-$
:ed_insert, :ed_unassigned,
# 165 M-% # 165 M-%
:ed_insert, :ed_unassigned,
# 166 M-& # 166 M-&
:ed_insert, :ed_unassigned,
# 167 M-' # 167 M-'
:ed_insert, :ed_unassigned,
# 168 M-( # 168 M-(
:ed_insert, :ed_unassigned,
# 169 M-) # 169 M-)
:ed_insert, :ed_unassigned,
# 170 M-* # 170 M-*
:ed_insert, :ed_unassigned,
# 171 M-+ # 171 M-+
:ed_insert, :ed_unassigned,
# 172 M-, # 172 M-,
:ed_insert, :ed_unassigned,
# 173 M-- # 173 M--
:ed_insert, :ed_unassigned,
# 174 M-. # 174 M-.
:ed_insert, :ed_unassigned,
# 175 M-/ # 175 M-/
:ed_insert, :ed_unassigned,
# 176 M-0 # 176 M-0
:ed_insert, :ed_unassigned,
# 177 M-1 # 177 M-1
:ed_insert, :ed_unassigned,
# 178 M-2 # 178 M-2
:ed_insert, :ed_unassigned,
# 179 M-3 # 179 M-3
:ed_insert, :ed_unassigned,
# 180 M-4 # 180 M-4
:ed_insert, :ed_unassigned,
# 181 M-5 # 181 M-5
:ed_insert, :ed_unassigned,
# 182 M-6 # 182 M-6
:ed_insert, :ed_unassigned,
# 183 M-7 # 183 M-7
:ed_insert, :ed_unassigned,
# 184 M-8 # 184 M-8
:ed_insert, :ed_unassigned,
# 185 M-9 # 185 M-9
:ed_insert, :ed_unassigned,
# 186 M-: # 186 M-:
:ed_insert, :ed_unassigned,
# 187 M-; # 187 M-;
:ed_insert, :ed_unassigned,
# 188 M-< # 188 M-<
:ed_insert, :ed_unassigned,
# 189 M-= # 189 M-=
:ed_insert, :ed_unassigned,
# 190 M-> # 190 M->
:ed_insert, :ed_unassigned,
# 191 M-? # 191 M-?
:ed_insert, :ed_unassigned,
# 192 M-@ # 192 M-@
:ed_insert, :ed_unassigned,
# 193 M-A # 193 M-A
:ed_insert, :ed_unassigned,
# 194 M-B # 194 M-B
:ed_insert, :ed_unassigned,
# 195 M-C # 195 M-C
:ed_insert, :ed_unassigned,
# 196 M-D # 196 M-D
:ed_insert, :ed_unassigned,
# 197 M-E # 197 M-E
:ed_insert, :ed_unassigned,
# 198 M-F # 198 M-F
:ed_insert, :ed_unassigned,
# 199 M-G # 199 M-G
:ed_insert, :ed_unassigned,
# 200 M-H # 200 M-H
:ed_insert, :ed_unassigned,
# 201 M-I # 201 M-I
:ed_insert, :ed_unassigned,
# 202 M-J # 202 M-J
:ed_insert, :ed_unassigned,
# 203 M-K # 203 M-K
:ed_insert, :ed_unassigned,
# 204 M-L # 204 M-L
:ed_insert, :ed_unassigned,
# 205 M-M # 205 M-M
:ed_insert, :ed_unassigned,
# 206 M-N # 206 M-N
:ed_insert, :ed_unassigned,
# 207 M-O # 207 M-O
:ed_insert, :ed_unassigned,
# 208 M-P # 208 M-P
:ed_insert, :ed_unassigned,
# 209 M-Q # 209 M-Q
:ed_insert, :ed_unassigned,
# 210 M-R # 210 M-R
:ed_insert, :ed_unassigned,
# 211 M-S # 211 M-S
:ed_insert, :ed_unassigned,
# 212 M-T # 212 M-T
:ed_insert, :ed_unassigned,
# 213 M-U # 213 M-U
:ed_insert, :ed_unassigned,
# 214 M-V # 214 M-V
:ed_insert, :ed_unassigned,
# 215 M-W # 215 M-W
:ed_insert, :ed_unassigned,
# 216 M-X # 216 M-X
:ed_insert, :ed_unassigned,
# 217 M-Y # 217 M-Y
:ed_insert, :ed_unassigned,
# 218 M-Z # 218 M-Z
:ed_insert, :ed_unassigned,
# 219 M-[ # 219 M-[
:ed_insert, :ed_unassigned,
# 220 M-\ # 220 M-\
:ed_insert, :ed_unassigned,
# 221 M-] # 221 M-]
:ed_insert, :ed_unassigned,
# 222 M-^ # 222 M-^
:ed_insert, :ed_unassigned,
# 223 M-_ # 223 M-_
:ed_insert, :ed_unassigned,
# 223 M-` # 223 M-`
:ed_insert, :ed_unassigned,
# 224 M-a # 224 M-a
:ed_insert, :ed_unassigned,
# 225 M-b # 225 M-b
:ed_insert, :ed_unassigned,
# 226 M-c # 226 M-c
:ed_insert, :ed_unassigned,
# 227 M-d # 227 M-d
:ed_insert, :ed_unassigned,
# 228 M-e # 228 M-e
:ed_insert, :ed_unassigned,
# 229 M-f # 229 M-f
:ed_insert, :ed_unassigned,
# 230 M-g # 230 M-g
:ed_insert, :ed_unassigned,
# 231 M-h # 231 M-h
:ed_insert, :ed_unassigned,
# 232 M-i # 232 M-i
:ed_insert, :ed_unassigned,
# 233 M-j # 233 M-j
:ed_insert, :ed_unassigned,
# 234 M-k # 234 M-k
:ed_insert, :ed_unassigned,
# 235 M-l # 235 M-l
:ed_insert, :ed_unassigned,
# 236 M-m # 236 M-m
:ed_insert, :ed_unassigned,
# 237 M-n # 237 M-n
:ed_insert, :ed_unassigned,
# 238 M-o # 238 M-o
:ed_insert, :ed_unassigned,
# 239 M-p # 239 M-p
:ed_insert, :ed_unassigned,
# 240 M-q # 240 M-q
:ed_insert, :ed_unassigned,
# 241 M-r # 241 M-r
:ed_insert, :ed_unassigned,
# 242 M-s # 242 M-s
:ed_insert, :ed_unassigned,
# 243 M-t # 243 M-t
:ed_insert, :ed_unassigned,
# 244 M-u # 244 M-u
:ed_insert, :ed_unassigned,
# 245 M-v # 245 M-v
:ed_insert, :ed_unassigned,
# 246 M-w # 246 M-w
:ed_insert, :ed_unassigned,
# 247 M-x # 247 M-x
:ed_insert, :ed_unassigned,
# 248 M-y # 248 M-y
:ed_insert, :ed_unassigned,
# 249 M-z # 249 M-z
:ed_insert, :ed_unassigned,
# 250 M-{ # 250 M-{
:ed_insert, :ed_unassigned,
# 251 M-| # 251 M-|
:ed_insert, :ed_unassigned,
# 252 M-} # 252 M-}
:ed_insert, :ed_unassigned,
# 253 M-~ # 253 M-~
:ed_insert, :ed_unassigned,
# 254 M-^? # 254 M-^?
:ed_insert :ed_unassigned
# 255 # 255
# EOF # EOF
] ]

View file

@ -13,30 +13,81 @@ class Reline::KeyStroke
def initialize(config) def initialize(config)
@config = config @config = config
@buffer = []
end end
def input_to(bytes) # Keystrokes of GNU Readline will timeout it with the specification of
case match_status(bytes) # "keyseq-timeout" when waiting for the 2nd character after the 1st one.
when :matching # If the 2nd character comes after 1st ESC without timeout it has a
nil # meta-property of meta-key to discriminate modified key with meta-key
when :matched # from multibyte characters that come with 8th bit on.
expand(bytes) #
when :unmatched # GNU Readline will wait for the 2nd character with "keyseq-timeout"
bytes # milli-seconds but wait forever after 3rd characters.
def read_io(keyseq_timeout, &block)
buffer = []
loop do
c = Reline::IOGate.getc
buffer << c
result = match_status(buffer)
case result
when :matched
block.(expand(bytes).map{ |c| Reline::Key.new(c, c, false) })
break
when :matching
if buffer.size == 1
begin
succ_c = nil
Timeout.timeout(keyseq_timeout / 1000.0) {
succ_c = Reline::IOGate.getc
}
rescue Timeout::Error # cancel matching only when first byte
block.([Reline::Key.new(c, c, false)])
break
else
if match_status(buffer.dup.push(succ_c)) == :unmatched
if c == "\e".ord
block.([Reline::Key.new(succ_c, succ_c | 0b10000000, true)])
else
block.([Reline::Key.new(c, c, false), Reline::Key.new(succ_c, succ_c, false)])
end
break
else
Reline::IOGate.ungetc(succ_c)
end
end
end
when :unmatched
if buffer.size == 1 and c == "\e".ord
read_escaped_key(buffer, block)
else
block.(buffer.map{ |c| Reline::Key.new(c, c, false) })
end
break
end
end end
end end
def input_to!(bytes) def read_escaped_key(buffer, block)
if bytes.nil? begin
return @buffer.push(nil)&.tap { clear } escaped_c = nil
Timeout.timeout(keyseq_timeout / 1000.0) {
escaped_c = Reline::IOGate.getc
}
rescue Timeout::Error # independent ESC
block.([Reline::Key.new(c, c, false)])
else
if escaped_c.nil?
block.([Reline::Key.new(c, c, false)])
elsif escaped_c >= 128 # maybe, first byte of multi byte
block.([Reline::Key.new(c, c, false), Reline::Key.new(escaped_c, escaped_c, false)])
elsif escaped_c == "\e".ord # escape twice
block.([Reline::Key.new(c, c, false), Reline::Key.new(c, c, false)])
else
block.([Reline::Key.new(escaped_c, escaped_c | 0b10000000, true)])
end
end end
@buffer.concat Array(bytes)
input_to(@buffer)&.tap { clear }
end end
private
def match_status(input) def match_status(input)
key_mapping.keys.select { |lhs| key_mapping.keys.select { |lhs|
lhs.start_with? input lhs.start_with? input
@ -53,6 +104,8 @@ class Reline::KeyStroke
} }
end end
private
def expand(input) def expand(input)
lhs = key_mapping.keys.select { |lhs| input.start_with? lhs }.sort_by(&:size).reverse.first lhs = key_mapping.keys.select { |lhs| input.start_with? lhs }.sort_by(&:size).reverse.first
return input unless lhs return input unless lhs
@ -70,8 +123,4 @@ class Reline::KeyStroke
def key_mapping def key_mapping
@config[:key_mapping].transform_keys(&:bytes) @config[:key_mapping].transform_keys(&:bytes)
end end
def clear
@buffer = []
end
end end

View file

@ -104,7 +104,6 @@ class Reline::LineEditor
@kill_ring = Reline::KillRing.new @kill_ring = Reline::KillRing.new
@vi_clipboard = '' @vi_clipboard = ''
@vi_arg = nil @vi_arg = nil
@meta_prefix = false
@waiting_proc = nil @waiting_proc = nil
@waiting_operator_proc = nil @waiting_operator_proc = nil
@completion_journey_data = nil @completion_journey_data = nil
@ -625,31 +624,38 @@ class Reline::LineEditor
private def normal_char(key) private def normal_char(key)
method_symbol = method_obj = nil method_symbol = method_obj = nil
@multibyte_buffer << key @multibyte_buffer << key.combined_char
if @multibyte_buffer.size > 1 if @multibyte_buffer.size > 1
if @multibyte_buffer.dup.force_encoding(@encoding).valid_encoding? if @multibyte_buffer.dup.force_encoding(@encoding).valid_encoding?
key = @multibyte_buffer.dup.force_encoding(@encoding) process_key(@multibyte_buffer.dup.force_encoding(@encoding), nil, nil)
@multibyte_buffer.clear @multibyte_buffer.clear
else else
# invalid # invalid
return return
end end
else # single byte else # single byte
return if key >= 128 # maybe, first byte of multi byte return if key.char >= 128 # maybe, first byte of multi byte
if @meta_prefix method_symbol = @config.editing_mode.get_method(key.combined_char)
key |= 0b10000000 if key.nobits?(0b10000000) if key.with_meta and method_symbol == :ed_unassigned
@meta_prefix = false # split ESC + key
end method_symbol = @config.editing_mode.get_method("\e".ord)
method_symbol = @config.editing_mode.get_method(key) if method_symbol and respond_to?(method_symbol, true)
if key.allbits?(0b10000000) and method_symbol == :ed_unassigned method_obj = method(method_symbol)
return # This is unknown input end
end process_key("\e".ord, method_symbol, method_obj)
if method_symbol and respond_to?(method_symbol, true) method_symbol = @config.editing_mode.get_method(key.char)
method_obj = method(method_symbol) if method_symbol and respond_to?(method_symbol, true)
method_obj = method(method_symbol)
end
process_key(key.char, method_symbol, method_obj)
else
if method_symbol and respond_to?(method_symbol, true)
method_obj = method(method_symbol)
end
process_key(key.combined_char, method_symbol, method_obj)
end end
@multibyte_buffer.clear @multibyte_buffer.clear
end end
process_key(key, method_symbol, method_obj)
if @config.editing_mode_is?(:vi_command) and @cursor > 0 and @cursor == @cursor_max if @config.editing_mode_is?(:vi_command) and @cursor > 0 and @cursor == @cursor_max
byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer) byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
@byte_pointer -= byte_size @byte_pointer -= byte_size
@ -660,7 +666,7 @@ class Reline::LineEditor
end end
def input_key(key) def input_key(key)
if key.nil? if key.nil? or key.char.nil?
if @first_char if @first_char
@line = nil @line = nil
end end
@ -669,30 +675,20 @@ class Reline::LineEditor
end end
@first_char = false @first_char = false
completion_occurs = false completion_occurs = false
if @config.editing_mode_is?(:emacs, :vi_insert) and key == "\C-i".ord if @config.editing_mode_is?(:emacs, :vi_insert) and key.char == "\C-i".ord
result = @completion_proc&.(@line) result = @completion_proc&.(@line)
if result.is_a?(Array) if result.is_a?(Array)
completion_occurs = true completion_occurs = true
complete(result) complete(result)
end end
elsif @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key) elsif @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
result = @completion_proc&.(@line) result = @completion_proc&.(@line)
if result.is_a?(Array) if result.is_a?(Array)
completion_occurs = true completion_occurs = true
move_completed_list(result, "\C-p".ord == key ? :up : :down) move_completed_list(result, "\C-p".ord == key.char ? :up : :down)
end end
elsif @config.editing_mode_is?(:emacs) and key == "\e".ord # meta key elsif Symbol === key.char and respond_to?(key.char, true)
if @meta_prefix process_key(key.char, key.char, method(key.char))
# escape twice
@meta_prefix = false
@kill_ring.process
else
@meta_prefix = true
end
elsif @config.editing_mode_is?(:vi_command) and key == "\e".ord
# suppress ^[ when command_mode
elsif Symbol === key and respond_to?(key, true)
process_key(key, key, method(key))
else else
normal_char(key) normal_char(key)
end end
@ -823,6 +819,15 @@ class Reline::LineEditor
end end
end end
private def key_newline(key)
if @is_multiline
next_line = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
cursor_line = @line.byteslice(0, @byte_pointer)
insert_new_line(cursor_line, next_line)
@cursor = 0
end
end
private def ed_insert(key) private def ed_insert(key)
if key.instance_of?(String) if key.instance_of?(String)
width = Reline::Unicode.get_mbchar_width(key) width = Reline::Unicode.get_mbchar_width(key)

View file

@ -101,6 +101,10 @@ class Reline::Windows
end end
end end
def self.ungetc(c)
@@buf.unshift(c)
end
def self.get_screen_size def self.get_screen_size
csbi = 0.chr * 24 csbi = 0.chr * 24
@@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi) @@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi)

View file

@ -40,13 +40,13 @@ class Reline::TestCase < Test::Unit::TestCase
eighth_bit = 0b10000000 eighth_bit = 0b10000000
byte = c.bytes.first byte = c.bytes.first
if byte.allbits?(eighth_bit) if byte.allbits?(eighth_bit)
@line_editor.input_key("\e".ord) @line_editor.input_key(Reline::Key.new(byte ^ eighth_bit, byte, true))
byte ^= eighth_bit else
@line_editor.input_key(Reline::Key.new(byte, byte, false))
end end
@line_editor.input_key(byte)
else else
c.bytes.each do |b| c.bytes.each do |b|
@line_editor.input_key(b) @line_editor.input_key(Reline::Key.new(b, b, false))
end end
end end
end end

View file

@ -4,12 +4,16 @@ class Reline::KeyStroke::Test < Reline::TestCase
using Module.new { using Module.new {
refine Array do refine Array do
def as_s def as_s
map(&:chr).join join
end
def to_keys
map{ |b| Reline::Key.new(b, b, false) }
end end
end end
} }
def test_input_to! def test_match_status
config = { config = {
key_mapping: { key_mapping: {
"a" => "xx", "a" => "xx",
@ -19,33 +23,15 @@ class Reline::KeyStroke::Test < Reline::TestCase
} }
} }
stroke = Reline::KeyStroke.new(config) stroke = Reline::KeyStroke.new(config)
result = ("abzwabk".bytes).map { |char| assert_equal(:matching, stroke.match_status("a".bytes))
stroke.input_to!(char)&.then { |result| assert_equal(:matching, stroke.match_status("ab".bytes))
"#{result.as_s}" assert_equal(:matched, stroke.match_status("abc".bytes))
} assert_equal(:matched, stroke.match_status("abz".bytes))
} assert_equal(:matched, stroke.match_status("abx".bytes))
assert_equal(result, [nil, nil, "yz", "w", nil, nil, "yk"]) assert_equal(:matched, stroke.match_status("ac".bytes))
end assert_equal(:matched, stroke.match_status("aa".bytes))
assert_equal(:matched, stroke.match_status("x".bytes))
def test_input_to assert_equal(:unmatched, stroke.match_status("m".bytes))
config = { assert_equal(:matched, stroke.match_status("abzwabk".bytes))
key_mapping: {
"a" => "xx",
"ab" => "y",
"abc" => "z",
"x" => "rr"
}
}
stroke = Reline::KeyStroke.new(config)
assert_equal(stroke.input_to("a".bytes)&.as_s, nil)
assert_equal(stroke.input_to("ab".bytes)&.as_s, nil)
assert_equal(stroke.input_to("abc".bytes)&.as_s, "z")
assert_equal(stroke.input_to("abz".bytes)&.as_s, "yz")
assert_equal(stroke.input_to("abx".bytes)&.as_s, "yrr")
assert_equal(stroke.input_to("ac".bytes)&.as_s, "rrrrc")
assert_equal(stroke.input_to("aa".bytes)&.as_s, "rrrrrrrr")
assert_equal(stroke.input_to("x".bytes)&.as_s, "rr")
assert_equal(stroke.input_to("m".bytes)&.as_s, "m")
assert_equal(stroke.input_to("abzwabk".bytes)&.as_s, "yzwabk")
end end
end end