Skip to content
Open
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
7 changes: 7 additions & 0 deletions lib/reline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class Core
filename_quote_characters
special_prefixes
completion_proc
completion_filter_proc
output_modifier_proc
prompt_proc
auto_indent_proc
Expand Down Expand Up @@ -134,6 +135,11 @@ def completion_proc=(p)
@completion_proc = p
end

def completion_filter_proc=(p)
raise ArgumentError unless p.respond_to?(:call) or p.nil?
@completion_filter_proc = p
end

def output_modifier_proc=(p)
raise ArgumentError unless p.respond_to?(:call) or p.nil?
@output_modifier_proc = p
Expand Down Expand Up @@ -318,6 +324,7 @@ def readline(prompt = '', add_hist = false)
line_editor.multiline_off
end
line_editor.completion_proc = completion_proc
line_editor.completion_filter_proc = completion_filter_proc
line_editor.completion_append_character = completion_append_character
line_editor.output_modifier_proc = output_modifier_proc
line_editor.prompt_proc = prompt_proc
Expand Down
17 changes: 11 additions & 6 deletions lib/reline/line_editor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class Reline::LineEditor
attr_reader :byte_pointer
attr_accessor :confirm_multiline_termination_proc
attr_accessor :completion_proc
attr_writer :completion_filter_proc
attr_accessor :completion_append_character
attr_accessor :output_modifier_proc
attr_accessor :prompt_proc
Expand Down Expand Up @@ -799,6 +800,14 @@ def editing_mode
@menu_info = MenuInfo.new(list)
end

private def completion_filter_proc
@completion_filter_proc || if @config.completion_ignore_case
->(target, candidate) { candidate.downcase.start_with?(target) }
else
->(target, candidate) { candidate.start_with?(target) }
end
end

private def filter_normalize_candidates(target, list)
target = target.downcase if @config.completion_ignore_case
list.select do |item|
Expand All @@ -810,11 +819,7 @@ def editing_mode
end
end

if @config.completion_ignore_case
item.downcase.start_with?(target)
else
item.start_with?(target)
end
completion_filter_proc.(target, item)
end.map do |item|
item.unicode_normalize
rescue Encoding::CompatibilityError
Expand Down Expand Up @@ -894,7 +899,7 @@ def dialog_proc_scope_completion_journey_data
list = call_completion_proc(preposing, target, postposing, quote)
return unless list.is_a?(Array)

candidates = list.select{ |item| item.start_with?(target) }
candidates = list.select(&completion_filter_proc.curry.(target))
return if candidates.empty?

pre = preposing.split("\n", -1).last || ''
Expand Down
24 changes: 24 additions & 0 deletions test/reline/test_key_actor_emacs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,30 @@ def test_completion_with_completion_ignore_case
assert_line_around_cursor('Foo_baz', '')
end

def test_completion_with_completion_filter_proc
@line_editor.completion_proc = proc { |word|
%w{
foo_bar
foo_baz
qux
}.map { |i|
i.encode(@encoding)
}
}
# Fuzzy matching: characters appear in order but not necessarily contiguous
@line_editor.completion_filter_proc = ->(target, candidate) {
idx = 0
target.each_char.all? { |c| (idx = candidate.index(c, idx)) && (idx += 1) }
}
input_keys('fb')
assert_line_around_cursor('fb', '')
input_keys("\C-i")
# 'fb' fuzzy matches 'foo_bar' and 'foo_baz' (f...b)
assert_line_around_cursor('foo_ba', '')
input_keys("\C-i")
assert_equal(%w{foo_bar foo_baz}, @line_editor.instance_variable_get(:@menu_info).list)
end

def test_completion_in_middle_of_line
@line_editor.completion_proc = proc { |word|
%w{
Expand Down
23 changes: 23 additions & 0 deletions test/reline/test_reline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def setup
Reline.send(:test_mode)
Reline.output_modifier_proc = nil
Reline.completion_proc = nil
Reline.completion_filter_proc = nil
Reline.prompt_proc = nil
Reline.auto_indent_proc = nil
Reline.pre_input_hook = nil
Expand Down Expand Up @@ -163,6 +164,28 @@ def test_completion_proc
assert_equal(dummy, Reline.completion_proc)
end

def test_completion_filter_proc
assert_equal(nil, Reline.completion_filter_proc)

dummy_proc = proc {}
Reline.completion_filter_proc = dummy_proc
assert_equal(dummy_proc, Reline.completion_filter_proc)

l = lambda {}
Reline.completion_filter_proc = l
assert_equal(l, Reline.completion_filter_proc)

assert_raise(ArgumentError) { Reline.completion_filter_proc = 42 }
assert_raise(ArgumentError) { Reline.completion_filter_proc = "hoge" }

dummy = DummyCallbackObject.new
Reline.completion_filter_proc = dummy
assert_equal(dummy, Reline.completion_filter_proc)

Reline.completion_filter_proc = nil
assert_nil(Reline.completion_filter_proc)
end

def test_output_modifier_proc
assert_equal(nil, Reline.output_modifier_proc)

Expand Down
Loading