Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add key stroke. #1

Merged
merged 1 commit into from
Apr 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions lib/reline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
require 'reline/version'
require 'reline/config'
require 'reline/key_actor'
require 'reline/key_stroke'
require 'reline/line_editor'

module Reline
Expand Down Expand Up @@ -85,11 +86,24 @@ def self.readline(prompt = '', add_hist = false)
@line_editor.completion_proc = @completion_proc
@line_editor.retrieve_completion_block = method(:retrieve_completion_block)
@line_editor.rerender
config = {
key_mapping: {
# TODO
# "a" => "bb",
# "z" => "aa",
# "y" => "ak",
}
}
key_stroke = Reline::KeyStroke.new(config)
begin
while c = getc
@line_editor.input_key(c)
@line_editor.rerender
break if @line_editor.finished?
key_stroke.input_to!(c)&.then { |inputs|
inputs.each { |c|
@line_editor.input_key(c)
@line_editor.rerender
break if @line_editor.finished?
}
}
end
move_cursor_column(0)
if add_hist and @line_editor.line and @line_editor.line.chomp.size > 0
Expand Down
70 changes: 70 additions & 0 deletions lib/reline/key_stroke.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
class Reline::KeyStroke
using Module.new {
refine Array do
def start_with?(other)
other.size <= size && other == self.take(other.size)
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
51 changes: 51 additions & 0 deletions test/key_stroke_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
require 'helper'

class Reline::KeyStroke::Test < Reline::TestCase
using Module.new {
refine Array do
def as_s
map(&:chr).join
end
end
}

def test_input_to!
config = {
key_mapping: {
"a" => "xx",
"ab" => "y",
"abc" => "z",
"x" => "rr"
}
}
stroke = Reline::KeyStroke.new(config)
result = ("abzwabk".bytes).map { |char|
stroke.input_to!(char)&.then { |result|
"#{result.as_s}"
}
}
assert_equal(result, [nil, nil, "yz", "w", nil, nil, "yk"])
end

def test_input_to
config = {
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