mirror of
https://github.com/storytold/wordcraft.git
synced 2026-11-03 00:40:33 -06:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f2afea452 |
@@ -46,8 +46,37 @@ pub fn paragraph_issues(
|
||||
}
|
||||
let text = proof_text(p);
|
||||
let spans: Vec<(std::ops::Range<usize>, wordcraft_proof::ProofLang)> = runs.iter().map(|r| (r.range.clone(), r.lang)).collect();
|
||||
let skip = |a: usize, b: usize| runs.iter().any(|r| r.skip && r.range.start < b && a < r.range.end);
|
||||
wordcraft_proof::check_text_with(&text, &spans, spelling, grammar, list_item).into_iter().filter(|i| !skip(i.start, i.end)).collect()
|
||||
let skipped = skipped_ranges(runs);
|
||||
let issues = wordcraft_proof::check_text_with(&text, &spans, spelling, grammar, list_item);
|
||||
if skipped.is_empty() {
|
||||
return issues;
|
||||
}
|
||||
issues.into_iter().filter(|i| !overlaps(&skipped, i.start, i.end)).collect()
|
||||
}
|
||||
|
||||
/// The byte ranges of the runs left unchecked, in order, those that overlap merged: their
|
||||
/// starts and ends both rise, so [`overlaps`] finds an issue's by binary search instead of
|
||||
/// looking at every run for every issue.
|
||||
fn skipped_ranges(runs: &[ProofRun]) -> Vec<(usize, usize)> {
|
||||
let mut ranges: Vec<(usize, usize)> = runs.iter().filter(|r| r.skip).map(|r| (r.range.start, r.range.end)).collect();
|
||||
ranges.sort_unstable();
|
||||
let mut merged: Vec<(usize, usize)> = Vec::with_capacity(ranges.len());
|
||||
for (s, e) in ranges {
|
||||
crate::work::steps(1);
|
||||
match merged.last_mut() {
|
||||
Some(last) if s < last.1 => last.1 = last.1.max(e),
|
||||
_ => merged.push((s, e)),
|
||||
}
|
||||
}
|
||||
merged
|
||||
}
|
||||
|
||||
/// Whether bytes `a..b` overlap one of `skipped` ([`skipped_ranges`]): the first range ending
|
||||
/// after `a` is the only one that can, as every later one starts no earlier.
|
||||
fn overlaps(skipped: &[(usize, usize)], a: usize, b: usize) -> bool {
|
||||
crate::work::steps(1);
|
||||
let k = skipped.partition_point(|&(_, e)| e <= a);
|
||||
skipped.get(k).is_some_and(|&(s, _)| s < b)
|
||||
}
|
||||
|
||||
/// Whether the paragraph is a list item: its own numbering, or its style's, names a list (not 0).
|
||||
@@ -101,3 +130,26 @@ pub fn proof_text(p: &wordcraft_doc::Paragraph) -> String {
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn run(range: std::ops::Range<usize>, skip: bool) -> ProofRun {
|
||||
ProofRun { range, lang: Default::default(), skip }
|
||||
}
|
||||
|
||||
/// The lookup finds what a look at every run finds, overlapping and empty runs included.
|
||||
#[test]
|
||||
fn skipped_lookup_matches_a_scan_of_every_run() {
|
||||
let runs = [run(0..4, false), run(4..9, true), run(5..6, true), run(9..9, true), run(9..12, false), run(12..20, true), run(14..30, true)];
|
||||
let skipped = skipped_ranges(&runs);
|
||||
for a in 0..32 {
|
||||
for b in a..32 {
|
||||
let scan = runs.iter().any(|r| r.skip && r.range.start < b && a < r.range.end);
|
||||
assert_eq!(overlaps(&skipped, a, b), scan, "{a}..{b}");
|
||||
}
|
||||
}
|
||||
assert!(skipped_ranges(&[run(0..5, false)]).is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user