howl.util.Matcher
matches if the search matches exactly
c = { 'One', 'Green Fields', 'two' }
m = Matcher c
assert.same { 'One' }, 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 exact ones
c = { 'kiss her', 'some/stuff/here', 'openssh', 'sss hhh' }
m = Matcher c
assert.same {
'sss hhh',
'some/stuff/here'
'openssh',
}, m('ssh')
prefers early occurring matches over ones at the end
c = { 'Discard all apples', 'all aardvarks' }
m = Matcher c
assert.same {
'all aardvarks',
'Discard all apples'
}, m('aa')
prefers shorter matching candidates over longer ones
c = { 'x/tools.sh', 'x/torx' }
m = Matcher c
assert.same {
'x/torx',
'x/tools.sh'
}, m('to')
prefers tighter matches to longer ones
c = { 'awesome_apples', 'an_aardvark' }
m = Matcher c
assert.same {
'an_aardvark',
'awesome_apples',
}, m('aa')
"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')
assert.same { }, m('.*')
boundary matches can not skip separators
m = Matcher { 'nih/says/knights' }
assert.same { 'nih/says/knights' }, m('sk')
assert.same {}, m('nk')
accepts ustring both for <search> and <text>
assert.not_nil Matcher.explain 'FU', 'snafu'
boundary matches are as tight as possible
assert.same { how: 'boundary', {1, 1}, {6, 2} }, Matcher.explain 'hth', 'hail the howl'
(boundary matches)
matches if the search matches at boundaries
m = Matcher { 'green fields', 'green sfinx' }
assert.same { 'green fields' }, m('gf')
assert.same { 'apaass_so' }, Matcher({'apaass_so'})('as')
matches if the search matches at upper case boundaries
m = Matcher { 'camelCase', 'a CreditCard', 'chacha' }
assert.same { 'camelCase', 'a CreditCard' }, m('cc')
allows for multiple-character boundaries
m = Matcher { 'green/_fields', 'green sfinx' }
assert.same { 'green/_fields' }, m('gf')
with reverse matching (reverse = true specified as an option)
prefers late occurring exact matches over ones at the start
c = { 'xmatch me', 'me xmatch' }
m = Matcher c, reverse: true
assert.same {
'me xmatch'
'xmatch me',
}, m('mat')
prefers late occurring boundary matches over ones at the start
c = { 'match natchos', 'me match now' }
m = Matcher c, reverse: true
assert.same {
'me match now'
'match natchos',
}, m('mn')
still prefers tighter matches to longer ones
c = { 'an_aardvark', 'a_apple' }
m = Matcher c, reverse: true
assert.same {
'a_apple',
'an_aardvark',
}, m('aa')
still prefers boundary matches over straight ones
c = { 'some/stuff/here', 'sshopen', 'open/ssh', 'ss xh' }
m = Matcher c, reverse: true
assert.same {
'open/ssh',
'sshopen',
'some/stuff/here'
}, m('ssh')
explain(search, text) works correctly
assert.same { how: 'exact', {7, 3} }, Matcher.explain 'aƒl', 'ƒluxsñaƒlux', reverse: true
assert.same { how: 'boundary', {1, 1}, {5, 1} }, Matcher.explain 'as', 'app_spec.fu', reverse: true
handles boundary matches
handles boundary matches
m = Matcher { 'spec/aplication_spec.moon' }, reverse: true
assert.same { 'spec/aplication_spec.moon' }, m('as')
allows for multiple-character boundaries
m = Matcher { 'spec/aplication/_spec.moon' }, reverse: true
assert.same { 'spec/aplication/_spec.moon' }, m('as')
with preserve_order = true specified as an option
preserves order of matches, irrespective of match score
c = {'xabx0', 'ax_bx1', 'xabx2', 'ax_bx3'}
m = Matcher c, preserve_order: true
assert.same c, m('ab')
for large data sets
returns a partial match when more than 1000 items match
items = for i = 1, 2000
"item-#{i}"
m = Matcher items
matches, opts = m('item')
assert.equals 1000, #matches
assert.is_true opts.partial
matches, opts = m('item-123')
assert.is_true #matches < 1000
assert.is_false opts.partial
allows slightly more than 1000 when the alternative would be irritating
items = for i = 1, 1100
"item-#{i}"
m = Matcher items
matches, opts = m('item')
assert.equals 1100, #matches
assert.is_false opts.partial