mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-12 13:24:19 +00:00
161 lines
4.3 KiB
Rust
161 lines
4.3 KiB
Rust
use std::{
|
|
borrow::Cow,
|
|
cmp::{self, Ordering},
|
|
sync::{atomic::AtomicBool, Arc},
|
|
};
|
|
|
|
use gpui::executor;
|
|
|
|
use crate::{
|
|
matcher::{Match, MatchCandidate, Matcher},
|
|
CharBag,
|
|
};
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct StringMatchCandidate {
|
|
pub id: usize,
|
|
pub string: String,
|
|
pub char_bag: CharBag,
|
|
}
|
|
|
|
impl Match for StringMatch {
|
|
fn score(&self) -> f64 {
|
|
self.score
|
|
}
|
|
|
|
fn set_positions(&mut self, positions: Vec<usize>) {
|
|
self.positions = positions;
|
|
}
|
|
}
|
|
|
|
impl StringMatchCandidate {
|
|
pub fn new(id: usize, string: String) -> Self {
|
|
Self {
|
|
id,
|
|
char_bag: CharBag::from(string.as_str()),
|
|
string,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> MatchCandidate for &'a StringMatchCandidate {
|
|
fn has_chars(&self, bag: CharBag) -> bool {
|
|
self.char_bag.is_superset(bag)
|
|
}
|
|
|
|
fn to_string(&self) -> Cow<'a, str> {
|
|
self.string.as_str().into()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct StringMatch {
|
|
pub candidate_id: usize,
|
|
pub score: f64,
|
|
pub positions: Vec<usize>,
|
|
pub string: String,
|
|
}
|
|
|
|
impl PartialEq for StringMatch {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.cmp(other).is_eq()
|
|
}
|
|
}
|
|
|
|
impl Eq for StringMatch {}
|
|
|
|
impl PartialOrd for StringMatch {
|
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
Some(self.cmp(other))
|
|
}
|
|
}
|
|
|
|
impl Ord for StringMatch {
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
self.score
|
|
.partial_cmp(&other.score)
|
|
.unwrap_or(Ordering::Equal)
|
|
.then_with(|| self.candidate_id.cmp(&other.candidate_id))
|
|
}
|
|
}
|
|
|
|
pub async fn match_strings(
|
|
candidates: &[StringMatchCandidate],
|
|
query: &str,
|
|
smart_case: bool,
|
|
max_results: usize,
|
|
cancel_flag: &AtomicBool,
|
|
background: Arc<executor::Background>,
|
|
) -> Vec<StringMatch> {
|
|
if candidates.is_empty() || max_results == 0 {
|
|
return Default::default();
|
|
}
|
|
|
|
if query.is_empty() {
|
|
return candidates
|
|
.iter()
|
|
.map(|candidate| StringMatch {
|
|
candidate_id: candidate.id,
|
|
score: 0.,
|
|
positions: Default::default(),
|
|
string: candidate.string.clone(),
|
|
})
|
|
.collect();
|
|
}
|
|
|
|
let lowercase_query = query.to_lowercase().chars().collect::<Vec<_>>();
|
|
let query = query.chars().collect::<Vec<_>>();
|
|
|
|
let lowercase_query = &lowercase_query;
|
|
let query = &query;
|
|
let query_char_bag = CharBag::from(&lowercase_query[..]);
|
|
|
|
let num_cpus = background.num_cpus().min(candidates.len());
|
|
let segment_size = (candidates.len() + num_cpus - 1) / num_cpus;
|
|
let mut segment_results = (0..num_cpus)
|
|
.map(|_| Vec::with_capacity(max_results.min(candidates.len())))
|
|
.collect::<Vec<_>>();
|
|
|
|
background
|
|
.scoped(|scope| {
|
|
for (segment_idx, results) in segment_results.iter_mut().enumerate() {
|
|
let cancel_flag = &cancel_flag;
|
|
scope.spawn(async move {
|
|
let segment_start = cmp::min(segment_idx * segment_size, candidates.len());
|
|
let segment_end = cmp::min(segment_start + segment_size, candidates.len());
|
|
let mut matcher = Matcher::new(
|
|
query,
|
|
lowercase_query,
|
|
query_char_bag,
|
|
smart_case,
|
|
max_results,
|
|
);
|
|
|
|
matcher.match_candidates(
|
|
&[],
|
|
&[],
|
|
candidates[segment_start..segment_end].iter(),
|
|
results,
|
|
cancel_flag,
|
|
|candidate, score| StringMatch {
|
|
candidate_id: candidate.id,
|
|
score,
|
|
positions: Vec::new(),
|
|
string: candidate.string.to_string(),
|
|
},
|
|
);
|
|
});
|
|
}
|
|
})
|
|
.await;
|
|
|
|
let mut results = Vec::new();
|
|
for segment_result in segment_results {
|
|
if results.is_empty() {
|
|
results = segment_result;
|
|
} else {
|
|
util::extend_sorted(&mut results, segment_result, max_results, |a, b| b.cmp(a));
|
|
}
|
|
}
|
|
results
|
|
}
|