use std::collections::{BTreeSet, HashMap, HashSet};
use std::fmt::Write as _;
const SECTION_BODY_FRACTION: f64 = 0.35;
const MIN_SECTION_BODY_LINES: usize = 2;
const MIN_CONTENT_LINES: usize = 24;
const STOP_WORDS: &[&str] = &[
"the", "and", "for", "with", "that", "this", "from", "into", "your", "you", "are", "can",
"will", "not", "all", "use", "using", "used", "lean", "ctx", "context", "agent", "agents",
];
struct Unit {
start: usize,
end: usize,
kind: UnitKind,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum UnitKind {
Heading,
Prose,
Fence,
}
impl Unit {
fn content_lines(&self, lines: &[&str]) -> usize {
lines[self.start..self.end]
.iter()
.filter(|l| !l.trim().is_empty())
.count()
}
}
pub(crate) fn compact_markdown(content: &str) -> Option<String> {
if content.trim().is_empty() || !looks_like_markdown(content) {
return None;
}
let lines: Vec<&str> = content.lines().collect();
let units = parse_units(&lines);
let content_total: usize = units.iter().map(|u| u.content_lines(&lines)).sum();
if content_total < MIN_CONTENT_LINES {
return None;
}
let keep = select_units(&lines, &units, content_total);
let mut out = String::new();
let mut omitted = 0usize;
for (uidx, unit) in units.iter().enumerate() {
if keep.contains(&uidx) {
flush_omission(&mut out, &mut omitted);
for line in &lines[unit.start..unit.end] {
out.push_str(line.trim_end());
out.push('\n');
}
} else {
omitted += unit.content_lines(&lines);
}
}
flush_omission(&mut out, &mut omitted);
let kept_content = out.lines().filter(|l| !l.trim().is_empty()).count();
if kept_content >= content_total || out.len() >= content.len() {
None
} else {
Some(out)
}
}
pub(crate) fn has_markdown_headings(content: &str) -> bool {
content.lines().any(is_heading)
}
fn parse_units(lines: &[&str]) -> Vec<Unit> {
let mut units = Vec::new();
let mut i = 0;
while i < lines.len() {
let trimmed = lines[i].trim_start();
if trimmed.is_empty() {
i += 1;
continue;
}
if let Some(marker) = fence_marker(trimmed) {
let mut end = i + 1;
while end < lines.len() && !is_closing_fence(lines[end], marker) {
end += 1;
}
let end = (end + 1).min(lines.len());
units.push(Unit {
start: i,
end,
kind: UnitKind::Fence,
});
i = end;
continue;
}
let kind = if is_heading(lines[i]) {
UnitKind::Heading
} else {
UnitKind::Prose
};
units.push(Unit {
start: i,
end: i + 1,
kind,
});
i += 1;
}
units
}
fn fence_marker(trimmed: &str) -> Option<char> {
['`', '~']
.into_iter()
.find(|&marker| trimmed.chars().take_while(|c| *c == marker).count() >= 3)
}
fn is_closing_fence(line: &str, marker: char) -> bool {
let trimmed = line.trim_start();
trimmed.chars().take_while(|c| *c == marker).count() >= 3
&& trimmed.chars().all(|c| c == marker || c.is_whitespace())
}
fn select_units(lines: &[&str], units: &[Unit], content_total: usize) -> HashSet<usize> {
let docs = token_sets(lines);
let df = document_frequency(&docs);
let mut keep = HashSet::new();
let mut section_body: Vec<usize> = Vec::new();
for (uidx, unit) in units.iter().enumerate() {
if unit.kind == UnitKind::Heading {
select_section_body(
&mut keep,
§ion_body,
lines,
units,
&docs,
&df,
content_total,
);
section_body.clear();
keep.insert(uidx);
} else {
section_body.push(uidx);
}
}
select_section_body(
&mut keep,
§ion_body,
lines,
units,
&docs,
&df,
content_total,
);
keep
}
fn select_section_body(
keep: &mut HashSet<usize>,
body: &[usize],
lines: &[&str],
units: &[Unit],
docs: &[BTreeSet<String>],
df: &HashMap<String, usize>,
content_total: usize,
) {
if body.is_empty() {
return;
}
let body_lines: usize = body.iter().map(|u| units[*u].content_lines(lines)).sum();
let target = section_body_budget(body_lines);
let mut scored: Vec<(usize, f64)> = body
.iter()
.map(|uidx| {
(
*uidx,
score_unit(&units[*uidx], lines, docs, content_total, df),
)
})
.collect();
scored.sort_by(|a, b| {
b.1.partial_cmp(&a.1)
.unwrap_or(std::cmp::Ordering::Equal)
.then_with(|| a.0.cmp(&b.0))
});
if let Some(first) = body.first() {
keep.insert(*first);
}
let mut taken = 0usize;
for (uidx, _) in scored {
if taken >= target {
break;
}
keep.insert(uidx);
taken += units[uidx].content_lines(lines);
}
}
fn section_body_budget(line_count: usize) -> usize {
let fractional = ((line_count as f64) * SECTION_BODY_FRACTION).ceil() as usize;
fractional.max(MIN_SECTION_BODY_LINES).min(line_count)
}
fn flush_omission(out: &mut String, omitted: &mut usize) {
if *omitted == 0 {
return;
}
let _ = writeln!(out, "... [lean-ctx: omitted {omitted} lines]");
*omitted = 0;
}
fn looks_like_markdown(content: &str) -> bool {
content.lines().any(is_heading)
|| content.lines().any(|l| l.trim_start().starts_with("- "))
|| content.lines().any(|l| l.trim_start().starts_with("* "))
|| content.lines().any(|l| l.trim_start().starts_with("| "))
}
fn is_heading(line: &str) -> bool {
let trimmed = line.trim_start();
let hashes = trimmed.chars().take_while(|c| *c == '#').count();
(1..=6).contains(&hashes) && (trimmed.len() == hashes || trimmed[hashes..].starts_with(' '))
}
fn token_sets(lines: &[&str]) -> Vec<BTreeSet<String>> {
lines
.iter()
.map(|line| tokens(line).into_iter().collect())
.collect()
}
fn document_frequency(docs: &[BTreeSet<String>]) -> HashMap<String, usize> {
let mut df = HashMap::new();
for doc in docs {
for token in doc {
*df.entry(token.clone()).or_insert(0) += 1;
}
}
df
}
fn score_unit(
unit: &Unit,
lines: &[&str],
docs: &[BTreeSet<String>],
content_total: usize,
df: &HashMap<String, usize>,
) -> f64 {
match unit.kind {
UnitKind::Fence => {
let mut tokens = BTreeSet::new();
for doc in &docs[unit.start..unit.end] {
tokens.extend(doc.iter().cloned());
}
idf_sum(&tokens, content_total, df) + 10.0 + position_bonus(unit.start)
}
_ => score_line(
unit.start,
lines[unit.start],
&docs[unit.start],
content_total,
df,
),
}
}
fn score_line(
idx: usize,
line: &str,
tokens: &BTreeSet<String>,
line_count: usize,
df: &HashMap<String, usize>,
) -> f64 {
let mut score = idf_sum(tokens, line_count, df);
let trimmed = line.trim_start();
if trimmed.starts_with("- ") || trimmed.starts_with("* ") || trimmed.starts_with("| ") {
score += 2.0;
}
if line.contains('`')
|| line.contains("MUST")
|| line.contains("SHOULD")
|| line.contains("BLOCKING")
|| line.contains("WARNING")
|| line.contains("ctx_")
|| line.contains("lean-ctx")
{
score += 10.0;
}
score + position_bonus(idx)
}
fn idf_sum(tokens: &BTreeSet<String>, line_count: usize, df: &HashMap<String, usize>) -> f64 {
let mut score = 0.0;
for token in tokens {
let freq = *df.get(token).unwrap_or(&1) as f64;
score += ((line_count as f64 + 1.0) / (freq + 1.0)).ln();
}
score
}
fn position_bonus(idx: usize) -> f64 {
1.0 / (idx + 1) as f64
}
fn tokens(line: &str) -> Vec<String> {
let mut out = Vec::new();
let mut cur = String::new();
for ch in line.chars() {
if ch.is_ascii_alphanumeric() || matches!(ch, '_' | '-' | '/' | '.' | ':') {
cur.push(ch);
} else if !cur.is_empty() {
push_token(&mut out, &cur);
cur.clear();
}
}
if !cur.is_empty() {
push_token(&mut out, &cur);
}
out
}
fn push_token(out: &mut Vec<String>, token: &str) {
let t = token
.trim_matches(|c: char| matches!(c, '.' | ',' | ':' | ';' | '(' | ')' | '[' | ']'))
.to_ascii_lowercase();
if t.len() < 3 || STOP_WORDS.contains(&t.as_str()) {
return;
}
if t.len() >= 8 || t.chars().any(|c| matches!(c, '_' | '-' | '/' | '.' | ':')) {
out.push(t);
}
}
#[cfg(test)]
mod tests {
use super::{compact_markdown, has_markdown_headings};
#[test]
fn keeps_all_headings_and_shrinks() {
let input = "# Title\n\nIntro paragraph with ordinary words.\n\n## Setup\n\n";
let body = "- `ctx_read` keeps important details for agents.\n";
let repeated = "This sentence is useful once but repeated many times for filler.\n";
let mut doc = input.to_string();
for _ in 0..20 {
doc.push_str(repeated);
}
doc.push_str(body);
doc.push_str("## Safety\n\nMUST preserve warnings and exact commands.\n");
for _ in 0..20 {
doc.push_str(repeated);
}
let compacted = compact_markdown(&doc).expect("markdown should compact");
assert!(compacted.len() < doc.len());
assert!(compacted.contains("# Title"));
assert!(compacted.contains("## Setup"));
assert!(compacted.contains("## Safety"));
assert!(compacted.contains("ctx_read"));
assert!(compacted.contains("MUST preserve"));
assert!(compacted.contains("[lean-ctx: omitted"));
}
#[test]
fn keeps_body_lines_from_each_section() {
let mut doc = String::from("# Root\n\nRoot intro.\n");
for section in 0..8 {
doc.push_str(&format!("\n## Section {section}\n\n"));
doc.push_str(&format!("Section {section} overview line.\n"));
for item in 0..12 {
doc.push_str(&format!(
"- repeated filler item {item} for section {section}.\n"
));
}
doc.push_str(&format!(
"BLOCKING section {section} exact requirement with `ctx_read`.\n"
));
}
let compacted = compact_markdown(&doc).expect("markdown should compact");
assert!(compacted.len() < doc.len());
for section in 0..8 {
assert!(compacted.contains(&format!("## Section {section}")));
assert!(compacted.contains(&format!("Section {section} overview line.")));
assert!(compacted.contains(&format!("BLOCKING section {section}")));
}
assert!(compacted.contains("[lean-ctx: omitted"));
}
#[test]
fn short_docs_pass_through() {
assert!(compact_markdown("# Title\n\nSmall.\n").is_none());
}
#[test]
fn output_is_deterministic_across_calls() {
let mut doc = String::from("# Determinism\n\nIntro line for the document.\n");
for section in 0..4 {
doc.push_str(&format!("\n## Section {section}\n\n"));
for i in 0..30 {
doc.push_str(&format!(
"candidate_{i} shared_token_{} another_token_{} overlapping detail item.\n",
i % 3,
i % 7,
));
}
}
let first = compact_markdown(&doc).expect("doc should compact");
for _ in 0..16 {
let next = compact_markdown(&doc).expect("doc should compact");
assert_eq!(first, next, "compaction must be byte-stable across calls");
}
}
#[test]
fn fenced_blocks_stay_atomic() {
let filler = "Ordinary explanatory filler sentence repeated for volume.\n";
let mut doc = String::from("# Guide\n\nIntro line.\n\n## Usage\n\n");
for _ in 0..30 {
doc.push_str(filler);
}
doc.push_str("Run the exact `ctx_read` command below:\n\n");
doc.push_str(
"```bash\nlean-ctx read src/lib.rs\n\nlean-ctx search \"ctx_read\" src/\n```\n",
);
for _ in 0..30 {
doc.push_str(filler);
}
let compacted = compact_markdown(&doc).expect("doc should compact");
let mut in_fence = false;
for line in compacted.lines() {
if line.trim_start().starts_with("```") {
in_fence = !in_fence;
} else if in_fence {
assert!(
!line.starts_with("... [lean-ctx:"),
"omission marker inside a fenced block:\n{compacted}"
);
}
}
assert!(!in_fence, "unbalanced code fences:\n{compacted}");
assert!(compacted.contains(
"```bash\nlean-ctx read src/lib.rs\n\nlean-ctx search \"ctx_read\" src/\n```"
));
assert!(compacted.contains("[lean-ctx: omitted"));
}
#[test]
fn heading_inside_fence_is_not_structure() {
let mut doc = String::from("# Real Heading\n\nIntro.\n\n");
doc.push_str("```sh\n# just a shell comment\necho done\n```\n");
for i in 0..30 {
doc.push_str(&format!("Body filler sentence number {i} for volume.\n"));
}
let compacted = compact_markdown(&doc).expect("doc should compact");
let fences = compacted.matches("```").count();
assert_eq!(fences % 2, 0, "fences must stay balanced:\n{compacted}");
}
#[test]
fn has_markdown_headings_requires_atx_space() {
assert!(has_markdown_headings("# Title\nbody\n"));
assert!(has_markdown_headings("###\nempty heading is valid\n"));
assert!(!has_markdown_headings("#!/usr/bin/env bash\necho hi\n"));
assert!(!has_markdown_headings("#pragma once\nplain text\n"));
assert!(!has_markdown_headings("- a list\n- alone\n"));
}
}