forked from Mirrors/helix
Merge branch 'master'
commit
1ad06ee586
@ -0,0 +1,74 @@
|
||||
use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
|
||||
use fuzzy_matcher::FuzzyMatcher;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
pub struct FuzzyQuery {
|
||||
queries: Vec<String>,
|
||||
}
|
||||
|
||||
impl FuzzyQuery {
|
||||
pub fn new(query: &str) -> FuzzyQuery {
|
||||
let mut saw_backslash = false;
|
||||
let queries = query
|
||||
.split(|c| {
|
||||
saw_backslash = match c {
|
||||
' ' if !saw_backslash => return true,
|
||||
'\\' => true,
|
||||
_ => false,
|
||||
};
|
||||
false
|
||||
})
|
||||
.filter_map(|query| {
|
||||
if query.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(query.replace("\\ ", " "))
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
FuzzyQuery { queries }
|
||||
}
|
||||
|
||||
pub fn fuzzy_match(&self, item: &str, matcher: &Matcher) -> Option<i64> {
|
||||
// use the rank of the first query for the rank, because merging ranks is not really possible
|
||||
// this behaviour matches fzf and skim
|
||||
let score = matcher.fuzzy_match(item, self.queries.get(0)?)?;
|
||||
if self
|
||||
.queries
|
||||
.iter()
|
||||
.any(|query| matcher.fuzzy_match(item, query).is_none())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
Some(score)
|
||||
}
|
||||
|
||||
pub fn fuzzy_indicies(&self, item: &str, matcher: &Matcher) -> Option<(i64, Vec<usize>)> {
|
||||
if self.queries.len() == 1 {
|
||||
return matcher.fuzzy_indices(item, &self.queries[0]);
|
||||
}
|
||||
|
||||
// use the rank of the first query for the rank, because merging ranks is not really possible
|
||||
// this behaviour matches fzf and skim
|
||||
let (score, mut indicies) = matcher.fuzzy_indices(item, self.queries.get(0)?)?;
|
||||
|
||||
// fast path for the common case of not using a space
|
||||
// during matching this branch should be free thanks to branch prediction
|
||||
if self.queries.len() == 1 {
|
||||
return Some((score, indicies));
|
||||
}
|
||||
|
||||
for query in &self.queries[1..] {
|
||||
let (_, matched_indicies) = matcher.fuzzy_indices(item, query)?;
|
||||
indicies.extend_from_slice(&matched_indicies);
|
||||
}
|
||||
|
||||
// deadup and remove duplicate matches
|
||||
indicies.sort_unstable();
|
||||
indicies.dedup();
|
||||
|
||||
Some((score, indicies))
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
use crate::ui::fuzzy_match::FuzzyQuery;
|
||||
use crate::ui::fuzzy_match::Matcher;
|
||||
|
||||
fn run_test<'a>(query: &str, items: &'a [&'a str]) -> Vec<String> {
|
||||
let query = FuzzyQuery::new(query);
|
||||
let matcher = Matcher::default();
|
||||
items
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
let (_, indicies) = query.fuzzy_indicies(item, &matcher)?;
|
||||
let matched_string = indicies
|
||||
.iter()
|
||||
.map(|&pos| item.chars().nth(pos).unwrap())
|
||||
.collect();
|
||||
Some(matched_string)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_single_value() {
|
||||
let matches = run_test("foo", &["foobar", "foo", "bar"]);
|
||||
assert_eq!(matches, &["foo", "foo"])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_multiple_values() {
|
||||
let matches = run_test(
|
||||
"foo bar",
|
||||
&["foo bar", "foo bar", "bar foo", "bar", "foo"],
|
||||
);
|
||||
assert_eq!(matches, &["foobar", "foobar", "barfoo"])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn space_escape() {
|
||||
let matches = run_test(r"foo\ bar", &["bar foo", "foo bar", "foobar"]);
|
||||
assert_eq!(matches, &["foo bar"])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trim() {
|
||||
let matches = run_test(r" foo bar ", &["bar foo", "foo bar", "foobar"]);
|
||||
assert_eq!(matches, &["barfoo", "foobar", "foobar"]);
|
||||
let matches = run_test(r" foo bar\ ", &["bar foo", "foo bar", "foobar"]);
|
||||
assert_eq!(matches, &["bar foo"])
|
||||
}
|
@ -1,16 +1,60 @@
|
||||
(comment_directive) @comment
|
||||
(comment) @comment
|
||||
|
||||
(filter_identifier) @function.method
|
||||
(function_identifier) @function.method
|
||||
(test) @function.builtin
|
||||
(variable) @variable
|
||||
(string) @string
|
||||
(interpolated_string) @string
|
||||
(operator) @operator
|
||||
(number) @constant.numeric.integer
|
||||
(boolean) @constant.builtin.boolean
|
||||
(null) @constant.builtin
|
||||
(keyword) @keyword
|
||||
(attribute) @attribute
|
||||
(tag) @tag
|
||||
(conditional) @keyword.control.conditional
|
||||
(repeat) @keyword.control.repeat
|
||||
(method) @function.method
|
||||
(parameter) @variable.parameter
|
||||
|
||||
[
|
||||
"{%"
|
||||
"{%-"
|
||||
"{%~"
|
||||
"%}"
|
||||
"-%}"
|
||||
"~%}"
|
||||
"{{"
|
||||
"{{-"
|
||||
"{{~"
|
||||
"}}"
|
||||
"-}}"
|
||||
"~}}"
|
||||
"{{"
|
||||
"}}"
|
||||
"{{-"
|
||||
"-}}"
|
||||
"{{~"
|
||||
"~}}"
|
||||
"{%"
|
||||
"%}"
|
||||
"{%-"
|
||||
"-%}"
|
||||
"{%~"
|
||||
"~%}"
|
||||
] @keyword
|
||||
|
||||
[
|
||||
","
|
||||
"."
|
||||
"?"
|
||||
":"
|
||||
"="
|
||||
] @punctuation.delimiter
|
||||
|
||||
(interpolated_string [
|
||||
"#{"
|
||||
"}"
|
||||
] @punctuation.delimiter)
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
] @punctuation.bracket
|
||||
|
||||
(hash [
|
||||
"}"
|
||||
] @punctuation.bracket)
|
||||
|
||||
|
Loading…
Reference in New Issue