This commit is contained in:
Senstella
2025-10-07 22:46:07 +09:00
commit faf1f49d50
7 changed files with 1745 additions and 0 deletions

105
src/matcher.rs Normal file
View File

@@ -0,0 +1,105 @@
use nucleo_matcher::{
Config, Matcher,
pattern::{AtomKind, CaseMatching, Normalization, Pattern},
};
use std::sync::{Mutex, OnceLock};
static MATCHER: OnceLock<Mutex<Matcher>> = OnceLock::new();
pub fn match_lines(code: &str, start: &str, end: &str) -> Option<(usize, usize)> {
let mut matcher = MATCHER
.get_or_init(|| Mutex::new(Matcher::new(Config::DEFAULT)))
.lock()
.ok()?;
let lines: Vec<&str> = code.lines().collect();
let n = lines.len();
let start_parts: Vec<&str> = start.split('\n').collect();
let end_parts: Vec<&str> = end.split('\n').collect();
let start_len = start_parts.len();
let end_len = end_parts.len();
if start_len == 0 || end_len == 0 || n == 0 {
return None;
}
let non_empty: Vec<bool> = lines.iter().map(|line| !line.trim().is_empty()).collect();
let nei: Vec<usize> = (0..n).filter(|&i| non_empty[i]).collect();
let m = nei.len();
if m < start_len || m < end_len {
return None;
}
let start_patterns: Vec<Pattern> = start_parts
.iter()
.map(|&part| {
Pattern::new(
part,
CaseMatching::Ignore,
Normalization::Smart,
AtomKind::Fuzzy,
)
})
.collect();
let end_patterns: Vec<Pattern> = end_parts
.iter()
.map(|&part| {
Pattern::new(
part,
CaseMatching::Ignore,
Normalization::Smart,
AtomKind::Fuzzy,
)
})
.collect();
let mut start_candidates: Vec<(usize, usize)> = vec![];
for p in 0..=m - start_len {
let matches_all = (0..start_len).all(|k| {
let line = lines[nei[p + k]];
!start_patterns[k]
.match_list(std::iter::once(line), &mut matcher)
.is_empty()
});
if matches_all {
start_candidates.push((nei[p], nei[p + start_len - 1]));
}
}
let mut end_candidates: Vec<(usize, usize)> = vec![];
for p in 0..=m - end_len {
let matches_all = (0..end_len).all(|k| {
let line = lines[nei[p + k]];
!end_patterns[k]
.match_list(std::iter::once(line), &mut matcher)
.is_empty()
});
if matches_all {
end_candidates.push((nei[p], nei[p + end_len - 1]));
}
}
let mut result = None;
for &(s_start, s_end) in &start_candidates {
let pos = end_candidates.partition_point(|&(e_start, _)| e_start <= s_end);
if end_candidates[pos..].len() == 1 {
let (_, e_end) = end_candidates[pos];
if result.is_some() {
return None;
}
result = Some((s_start, e_end));
} else {
return None;
}
}
result
}