use tracing::debug;
use crate::{
ellipsis::replace_ellipsis,
table::reflow_table,
wrap::{LinkReferenceMatcher, classify_block, leading_indent},
};
fn is_indented_content_line(line: &str) -> bool {
let (indent_width, first_content_byte) = leading_indent(line);
indent_width >= 4
&& line[first_content_byte..]
.chars()
.any(|c| !c.is_whitespace())
}
pub(super) struct ProcessBuffer {
out: Vec<String>,
buf: Vec<String>,
in_table: bool,
ellipsis: bool,
}
impl ProcessBuffer {
pub(super) fn new(ellipsis: bool) -> Self {
Self {
out: Vec::new(),
buf: Vec::new(),
in_table: false,
ellipsis,
}
}
pub(super) fn push_out(&mut self, line: String) { self.out.push(line); }
pub(super) fn into_out(self) -> Vec<String> { self.out }
pub(super) fn flush(&mut self) {
debug!(
in_table = self.in_table,
lines = self.buf.len(),
"ProcessBuffer::flush"
);
if self.buf.is_empty() {
return;
}
let buffered = std::mem::take(&mut self.buf);
if self.in_table {
let table_lines = if self.ellipsis {
replace_ellipsis(&buffered)
} else {
buffered
};
self.out.extend(reflow_table(&table_lines));
} else {
self.out.extend(buffered);
}
self.in_table = false;
}
pub(super) fn push_verbatim(&mut self, line: &str) {
self.flush();
self.out.push(line.to_string());
}
pub(super) fn handle_fence_line(&mut self, line: &str, is_fence_marker: bool) -> bool {
if !is_fence_marker {
return false;
}
self.push_verbatim(line);
true
}
pub(super) fn handle_table_line(&mut self, line: String) -> Option<String> {
if leading_indent(&line).0 < 4 && line.trim_start().starts_with('|') {
debug!(
line_len = line.len(),
buffered_lines = self.buf.len(),
"ProcessBuffer: table-mode on"
);
self.in_table = true;
self.buf.push(line);
return None;
}
if line.trim().is_empty() {
if self.in_table {
self.flush();
}
return Some(line);
}
if self.in_table && classify_block(&line, LinkReferenceMatcher::production()).is_some() {
debug!(
line_len = line.len(),
in_table = self.in_table,
buffered_lines = self.buf.len(),
"ProcessBuffer: flushing on block boundary"
);
self.flush();
return Some(line);
}
if self.in_table && is_indented_content_line(&line) {
self.flush();
return Some(line);
}
if self.in_table && (line.contains('|') || crate::table::SEP_RE.is_match(line.trim())) {
self.buf.push(line);
return None;
}
if self.in_table {
self.flush();
}
Some(line)
}
}
#[cfg(test)]
mod tests;