pub fn extract_braced(text: &str, command: &str) -> Option<String> {
let pattern = format!("\\{}{{", command);
if let Some(start) = text.find(&pattern) {
let after = &text[start + pattern.len()..];
let mut depth = 1;
let mut content = String::new();
for ch in after.chars() {
match ch {
'{' => {
depth += 1;
content.push(ch);
}
'}' => {
depth -= 1;
if depth == 0 {
return Some(clean_text(&content));
}
content.push(ch);
}
_ => content.push(ch),
}
}
}
None
}
pub fn read_braced_from_chars(chars: &mut std::iter::Peekable<std::str::Chars>) -> Option<String> {
while let Some(&c) = chars.peek() {
if c.is_whitespace() {
chars.next();
} else {
break;
}
}
if chars.peek() != Some(&'{') {
return None;
}
chars.next();
let mut content = String::new();
let mut depth = 1;
for c in chars.by_ref() {
match c {
'{' => {
depth += 1;
content.push(c);
}
'}' => {
depth -= 1;
if depth == 0 {
return Some(content);
}
content.push(c);
}
_ => content.push(c),
}
}
Some(content)
}
pub fn extract_env_name(line: &str) -> Option<String> {
if let Some(start) = line.find("\\begin{") {
let after = &line[start + 7..];
if let Some(end) = after.find('}') {
return Some(after[..end].to_string());
}
}
None
}
pub fn clean_text(text: &str) -> String {
text.to_string()
.replace("\\\\", "\n")
.replace("\\&", "&")
.replace("\\#", "#")
.replace("\\_", "_")
.replace("\\{", "{")
.replace("\\}", "}")
.replace("\\%", "%")
.trim()
.to_string()
}
pub fn collect_environment(lines: &[&str], start_idx: usize, env_name: &str) -> (String, usize) {
let mut content = String::new();
let mut i = start_idx + 1;
let end_marker = format!("\\end{{{}}}", env_name);
while i < lines.len() {
let line = lines[i];
if line.trim().contains(&end_marker) {
return (content, i + 1);
}
content.push_str(line);
content.push('\n');
i += 1;
}
(content, i)
}