use nom::{
Parser,
branch::alt,
bytes::complete::{tag, take_until1},
};
use crate::parser::{HsmlProcessContext, HsmlResult, Span, advance, take_prefix};
fn is_text_block_line(line: &str, indent: &str) -> bool {
if let Some(after_indent) = line.strip_prefix(indent) {
after_indent.starts_with(' ') || after_indent.starts_with('\t')
} else {
false
}
}
fn newline_len_at(s: &str) -> usize {
if s.starts_with("\r\n") {
2
} else if s.starts_with('\n') {
1
} else {
0
}
}
pub(super) fn process_text_block<'a>(
input: Span<'a>,
context: &mut HsmlProcessContext,
) -> HsmlResult<'a, Span<'a>> {
let (rest, _) = tag(".")(input)?;
let (rest, _) = alt((tag("\r\n"), tag("\n"))).parse(rest)?;
let rest_str = *rest.fragment();
if let Some(first_line) = rest_str.lines().next()
&& !first_line.is_empty()
&& !is_text_block_line(first_line, &context.indent_string)
{
return Ok((rest, take_prefix(rest, 0)));
}
let mut text_block_end = 0;
let mut pos = 0;
while pos < rest_str.len() {
if pos > 0 {
let line_end = rest_str[pos..]
.find('\n')
.map_or(rest_str.len(), |i| pos + i);
let line = &rest_str[pos..line_end].trim_end_matches('\r');
if !line.is_empty() && !is_text_block_line(line, &context.indent_string) {
break;
}
}
let line_end = rest_str[pos..]
.find('\n')
.map_or(rest_str.len(), |i| pos + i);
text_block_end = line_end;
if line_end >= rest_str.len() {
break;
}
let next_pos = line_end + 1;
let nl = newline_len_at(&rest_str[next_pos..]);
if nl > 0 {
text_block_end = next_pos + nl;
pos = next_pos + nl;
continue;
}
let next_line_end = rest_str[next_pos..]
.find('\n')
.map_or(rest_str.len(), |i| next_pos + i);
let next_line = rest_str[next_pos..next_line_end].trim_end_matches('\r');
if !is_text_block_line(next_line, &context.indent_string) {
break;
}
pos = next_pos;
}
let text_block = take_prefix(rest, text_block_end);
let rest = advance(rest, text_block_end);
Ok((rest, text_block))
}
pub(super) fn process_text(input: Span<'_>) -> HsmlResult<'_, Span<'_>> {
let (input, _) = tag(" ")(input)?;
take_until1("\n")(input)
}