use super::super::block::{
advance_line, can_start_regex_literal, skip_regex_literal, skip_script_string_literal,
};
use memchr::{memchr, memchr2, memchr3, memmem};
pub(super) fn skip_template_interpolation(
bytes: &[u8],
mut pos: usize,
len: usize,
line: &mut usize,
last_newline: &mut usize,
) -> Option<usize> {
debug_assert!(bytes.get(pos..).unwrap_or_default().starts_with(b"{{"));
let original_line = *line;
let original_last_newline = *last_newline;
let body_start = pos + 2;
let close_offset = memmem::find(bytes.get(body_start..).unwrap_or_default(), b"}}")?;
{
let close_start = body_start + close_offset;
let body = bytes.get(body_start..close_start).unwrap_or_default();
if memchr3(b'\'', b'"', b'`', body).is_none() && memchr2(b'/', b'{', body).is_none() {
let interpolation_end = close_start + 2;
advance_line(
bytes.get(pos..interpolation_end).unwrap_or_default(),
pos,
line,
last_newline,
);
return Some(interpolation_end);
}
}
let mut brace_depth = 0usize;
let mut prev_significant_char = b'{';
pos = body_start;
while pos < len
&& let Some(&b) = bytes.get(pos)
{
if b == b'\n' {
*line += 1;
*last_newline = pos;
prev_significant_char = b'\n';
pos += 1;
continue;
}
if matches!(b, b' ' | b'\t' | b'\r') {
pos += 1;
continue;
}
if b == b'/' && pos + 1 < len && bytes.get(pos + 1) == Some(&b'/') {
pos += 2;
if let Some(newline_offset) = memchr(b'\n', bytes.get(pos..).unwrap_or_default()) {
pos += newline_offset;
} else {
pos = len;
}
continue;
}
if b == b'/' && pos + 1 < len && bytes.get(pos + 1) == Some(&b'*') {
pos += 2;
if let Some(end_offset) = memmem::find(bytes.get(pos..).unwrap_or_default(), b"*/") {
advance_line(
bytes.get(pos..pos + end_offset).unwrap_or_default(),
pos,
line,
last_newline,
);
pos += end_offset + 2;
} else {
pos = len;
}
continue;
}
if b == b'/'
&& can_start_regex_literal(prev_significant_char)
&& let Some(next_pos) = skip_regex_literal(bytes, pos, len, line, last_newline)
{
prev_significant_char = b'/';
pos = next_pos;
continue;
}
if matches!(b, b'\'' | b'"' | b'`') {
let string_start = pos;
let line_before_string = *line;
let last_newline_before_string = *last_newline;
let string_end = skip_script_string_literal(bytes, pos, len, b, line, last_newline);
let string_closed = string_end > string_start && bytes.get(string_end - 1) == Some(&b);
if !string_closed
&& let Some(close_offset) = memmem::find(
bytes.get(string_start + 1..string_end).unwrap_or_default(),
b"}}",
)
{
let interpolation_end = string_start + 1 + close_offset + 2;
*line = line_before_string;
*last_newline = last_newline_before_string;
advance_line(
bytes
.get(string_start..interpolation_end)
.unwrap_or_default(),
string_start,
line,
last_newline,
);
return Some(interpolation_end);
}
pos = string_end;
prev_significant_char = b;
continue;
}
match b {
b'{' => {
brace_depth += 1;
prev_significant_char = b;
pos += 1;
}
b'}' if brace_depth == 0 && pos + 1 < len && bytes.get(pos + 1) == Some(&b'}') => {
return Some(pos + 2);
}
b'}' => {
brace_depth = brace_depth.saturating_sub(1);
prev_significant_char = b;
pos += 1;
}
b'\\' => {
if pos + 1 < len && bytes.get(pos + 1) == Some(&b'\n') {
*line += 1;
*last_newline = pos + 1;
}
pos = (pos + 2).min(len);
}
_ => {
prev_significant_char = b;
pos += 1;
}
}
}
*line = original_line;
*last_newline = original_last_newline;
None
}