summaryrefslogtreecommitdiff
path: root/lib/reline/key_stroke.rb
diff options
context:
space:
mode:
authoraycabta <aycabta@gmail.com>2019-04-27 14:53:09 +0900
committeraycabta <aycabta@gmail.com>2019-04-30 11:44:20 +0900
commit17350c7e5534c8678097d70698fe08614a6c3997 (patch)
tree0f41959093014a97d50aeb06a052f5450b4cb6b1 /lib/reline/key_stroke.rb
parenteb45ba61160dbae412407f232fe9b3252eb99362 (diff)
Add Reline as a fallback library for Readline
* lib/reine.rb, lib/reline/*: Reline is a readline stdlib compatible library. * lib/readline.rb: Readline uses a fallback to Reline when ext/readline doesn't exist. * tool/sync_default_gems.rb: add ruby/reline as a default gem. * appveyor.yml: add "set RELINE_TEST_ENCODING=Windows-31J" for test suit of Reline, and add "--exclude readline" to "nmake test-all" on Visual Studio builds because of strange behavior. * spec/ruby/library/readline/spec_helper.rb: skip Reline as with RbReadline.
Diffstat (limited to 'lib/reline/key_stroke.rb')
-rw-r--r--lib/reline/key_stroke.rb74
1 files changed, 74 insertions, 0 deletions
diff --git a/lib/reline/key_stroke.rb b/lib/reline/key_stroke.rb
new file mode 100644
index 0000000000..ac0a820759
--- /dev/null
+++ b/lib/reline/key_stroke.rb
@@ -0,0 +1,74 @@
+class Reline::KeyStroke
+ using Module.new {
+ refine Array do
+ def start_with?(other)
+ other.size <= size && other == self.take(other.size)
+ end
+
+ def bytes
+ self
+ end
+ end
+ }
+
+ def initialize(config)
+ @config = config
+ @buffer = []
+ end
+
+ def input_to(bytes)
+ case match_status(bytes)
+ when :matching
+ nil
+ when :matched
+ expand(bytes)
+ when :unmatched
+ bytes
+ end
+ end
+
+ def input_to!(bytes)
+ @buffer.concat Array(bytes)
+ input_to(@buffer)&.tap { clear }
+ end
+
+ private
+
+ 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)&.size&.== input.size)
+ return :matching if it.size == 1 && (it.max_by(&:size)&.size&.!= input.size)
+ 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)
+ lhs = key_mapping.keys.select { |lhs| input.start_with? lhs }.sort_by(&:size).reverse.first
+ 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))
+ end
+ end
+
+ def key_mapping
+ @config[:key_mapping].transform_keys(&:bytes)
+ end
+
+ def clear
+ @buffer = []
+ end
+end