howl.util.Matcher(candidates)
matches if all characters are present
c = { 'One', 'Green Fields', 'two', 'overflow' }
m = Matcher c
assert.same { 'One', 'Green Fields' }, m('ne')
candidates are automatically converted to strings
candidate = setmetatable {}, __tostring: -> 'auto'
m = Matcher { candidate }
assert.same { candidate }, m('auto')
candidates can be multi-valued tables
c = { { 'One', 'Uno' } }
m = Matcher c
assert.same { c[1] }, m('One')
multi-valued candidates are automatically converted to strings
candidate = setmetatable {}, __tostring: -> 'auto'
m = Matcher { { candidate, 'desc' } }
assert.same { { candidate, 'desc' } }, m('auto')
prefers boundary matches over straight ones over fuzzy ones
c = { 'kiss her', 'some/stuff/here', 'openssh', 'sss hhh' }
m = Matcher c
assert.same {
'sss hhh',
'some/stuff/here'
'openssh',
'kiss her'
}, m('ssh')
prefers early occurring matches over ones at the end
c = { 'Two items to bind them tight', 'One item to match them' }
m = Matcher c
assert.same {
'One item to match them',
'Two items to bind them tight'
}, m('ni')
prefers shorter matching candidates over longer ones
c = { 'src/tools.sh', 'TODO' }
m = Matcher c
assert.same {
'TODO',
'src/tools.sh'
}, m('to')
prefers tighter matches to longer ones
c = { 'aa bb cc dd', 'zzzzzzzzzzzzzzz ad' }
m = Matcher c
assert.same {
'zzzzzzzzzzzzzzz ad',
'aa bb cc dd',
}, m('ad')
"special" characters are matched as is
c = { 'Item 2. 1%w', 'Item 22 2a' }
m = Matcher c
assert.same { 'Item 2. 1%w' }, m('%w')
accepts ustring both for candidates and searches
c = { 'one', 'two' }
m = Matcher c
assert.same { 'one', 'two' }, m('o')
boundary matches can not skip separators
assert.equal 'boundary', Matcher.explain('sk', 'nih/says/knights').how
assert.not_equal 'boundary', Matcher.explain('nk', 'nih/says/knights').how
explain(search, text)
sets .how to the type of match
assert.equal 'exact', Matcher.explain('fu', 'snafu').how
returns a list of character offsets indicating where <search> matched
assert.same { how: 'exact', 4, 5, 6 }, Matcher.explain 'ƒlu', 'sñaƒlux'
assert.same { how: 'fuzzy', 2, 4, 6 }, Matcher.explain 'hiʈ', 'Čhriʂʈmas'
assert.same { how: 'boundary', 1, 4, 9, 10 }, Matcher.explain 'itʂo', 'iʂ that ʂo'
lower-cases the search and text just as for matching
assert.not_nil Matcher.explain 'FU', 'ʂnafu'
assert.not_nil Matcher.explain 'fu', 'SNAFU'
accepts ustring both for <search> and <text>
assert.not_nil Matcher.explain 'FU', 'snafu'
with reverse matching (reverse = true specified as an option)
prefers late occurring matches over ones at the end
c = { 'match me', 'me match' }
m = Matcher c, reverse: true
assert.same {
'me match'
'match me',
}, m('mat')
still prefers tighter matches to longer ones
c = { 'aabbac', 'abbaca' }
m = Matcher c, reverse: true
assert.same {
'aabbac',
'abbaca',
}, m('aa')
still prefers boundary matches over straight ones over fuzzy ones
c = { 'just kiss her', 'some/stuff/here', 'sshopen', 'open/ssh', 'ss xh' }
m = Matcher c, reverse: true
assert.same {
'open/ssh',
'sshopen',
'some/stuff/here'
'ss xh',
'just kiss her'
}, m('ssh')
explain(search, text) works correctly
assert.same { how: 'exact', 7, 8, 9 }, Matcher.explain 'aƒl', 'ƒluxsñaƒlux', reverse: true
assert.same { how: 'boundary', 1, 5 }, Matcher.explain 'as', 'app_spec.fu', reverse: true
assert.same { how: 'fuzzy', 5, 8 }, Matcher.explain 'sc', 'app_spec.fu', reverse: true