use std::borrow::Cow;
use super::{
Checker,
blocks::check_plain,
claim::{Docs, is_run},
};
use crate::{Finding, grammar::Block as Rule, markdown::Block};
pub(super) fn extents(
block: &Block,
rule: &Rule,
divides: bool,
reserve: usize,
) -> Option<(usize, usize)> {
if !divides || !is_run(rule.kind) || block.units.len() < 2 || rule.max == Some(0) {
return None;
}
let len = block.units.len();
let mut upper = rule.max.map_or(len, |m| m.min(len));
let lower = rule.min.max(1);
if reserve > 0 && len >= lower + reserve {
upper = upper.min(len - reserve);
}
Some((lower.min(upper), upper))
}
fn narrow(block: &Block, n: usize, findings: &[Finding]) -> usize {
findings
.iter()
.filter_map(|f| f.line)
.min()
.map_or(n, |l| {
block.units.iter().take(n).filter(|u| u.line < l).count()
})
.min(n - 1)
}
pub(super) fn extent(
c: &mut Checker<'_>,
docs: &mut Docs<'_>,
di: usize,
rule: &Rule,
section: Option<usize>,
firm: bool,
(lower, upper): (usize, usize),
) -> Result<usize, Vec<Finding>> {
let mut first: Option<Vec<Finding>> = None;
let mut n = upper;
loop {
let at = c.save();
check_plain(c, &prefix(&docs[di], n), rule, section);
if !c.failed(&at) {
commit(docs, di, n);
return Ok(1);
}
let dropped = c.rollback(&at);
let next = narrow(&docs[di], n, &dropped);
if first.is_none() {
first = Some(dropped);
}
if next < lower {
break;
}
n = next;
}
if firm {
check_plain(c, &prefix(&docs[di], upper), rule, section);
commit(docs, di, upper);
return Ok(1);
}
Err(first.unwrap_or_default())
}
fn commit(docs: &mut Docs<'_>, di: usize, n: usize) {
if n >= docs[di].units.len() {
return;
}
let tail = cut(docs[di].to_mut(), n);
docs.insert(di + 1, Cow::Owned(tail));
}
pub(super) fn cut(block: &mut Block, n: usize) -> Block {
let units = block.units.split_off(n);
let line = units.first().map(|u| u.line).unwrap_or(block.end_line);
let end_line = units.last().map(|u| u.end_line).unwrap_or(block.end_line);
let html_lines = block
.html_lines
.iter()
.copied()
.filter(|l| *l >= line)
.collect();
block.html_lines.retain(|l| *l < line);
block.end_line = block.units.last().map(|u| u.end_line).unwrap_or(block.line);
Block {
html_lines,
..Block::new(block.kind, line, end_line, units)
}
}
fn prefix(block: &Block, n: usize) -> Block {
let mut head = block.clone();
let _ = cut(&mut head, n);
head
}