From 3773296bfc45a60baf9e5cc025bfed2d6b493eaa Mon Sep 17 00:00:00 2001 From: aycabta Date: Mon, 6 Sep 2021 03:58:48 +0900 Subject: [PATCH] [ruby/reline] Allow Reline::KeyStroke to compare raw and meta-key processed key sequences https://github.com/ruby/reline/commit/731103f9c9 --- lib/reline.rb | 12 +++++++-- lib/reline/key_stroke.rb | 58 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 65 insertions(+), 5 deletions(-) diff --git a/lib/reline.rb b/lib/reline.rb index 3569787a5d..33aa6cdbf1 100644 --- a/lib/reline.rb +++ b/lib/reline.rb @@ -390,15 +390,23 @@ module Reline block.([Reline::Key.new(c, c, false)]) break else - if key_stroke.match_status(buffer.dup.push(succ_c)) == :unmatched + case key_stroke.match_status(buffer.dup.push(succ_c)) + when :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 + when :matching Reline::IOGate.ungetc(succ_c) + when :matched + buffer << succ_c + expanded = key_stroke.expand(buffer).map{ |expanded_c| + Reline::Key.new(expanded_c, expanded_c, false) + } + block.(expanded) + break end end end diff --git a/lib/reline/key_stroke.rb b/lib/reline/key_stroke.rb index 017e3db00a..675b658f57 100644 --- a/lib/reline/key_stroke.rb +++ b/lib/reline/key_stroke.rb @@ -1,8 +1,59 @@ 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) - other.size <= size && other == self.take(other.size) + 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 @@ -19,8 +70,8 @@ class Reline::KeyStroke key_mapping.keys.select { |lhs| lhs.start_with? input }.tap { |it| - return :matched if it.size == 1 && (it.max_by(&:size)&.size&.== input.size) - return :matching if it.size == 1 && (it.max_by(&:size)&.size&.!= input.size) + 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 } @@ -32,6 +83,7 @@ class Reline::KeyStroke end def expand(input) + input = input.compress_meta_key lhs = key_mapping.keys.select { |item| input.start_with? item }.sort_by(&:size).reverse.first return input unless lhs rhs = key_mapping[lhs]