use crate::syntax::SyntaxKind;
use rowan::GreenNodeBuilder;
use crate::parser::utils::helpers::strip_newline;
pub(crate) fn is_indented_code_line(content: &str) -> bool {
if content.is_empty() {
return false;
}
if content.starts_with('\t') {
return true;
}
let spaces = content.chars().take_while(|&c| c == ' ').count();
spaces >= 4
}
pub(crate) fn parse_indented_code_block(
builder: &mut GreenNodeBuilder<'static>,
lines: &[&str],
start_pos: usize,
bq_depth: usize,
base_indent: usize,
) -> usize {
use super::blockquotes::{
count_blockquote_markers, emit_one_blockquote_marker, strip_n_blockquote_markers,
};
use crate::parser::utils::marker_utils::parse_blockquote_marker_info;
builder.start_node(SyntaxKind::CODE_BLOCK.into());
builder.start_node(SyntaxKind::CODE_CONTENT.into());
let mut current_pos = start_pos;
let code_indent = base_indent + 4;
while current_pos < lines.len() {
let line = lines[current_pos];
let (line_bq_depth, _) = count_blockquote_markers(line);
let inner = if bq_depth > 0 {
strip_n_blockquote_markers(line, bq_depth)
} else {
line
};
if line_bq_depth < bq_depth {
break;
}
if inner.trim().is_empty() {
let mut look_pos = current_pos + 1;
let mut continues = false;
while look_pos < lines.len() {
let (look_bq_depth, look_inner) = count_blockquote_markers(lines[look_pos]);
if look_bq_depth < bq_depth {
break;
}
if look_inner.trim_end_matches('\n').trim().is_empty() {
look_pos += 1;
continue;
}
let (look_indent, _) = leading_indent(look_inner);
if look_indent >= code_indent {
continues = true;
}
break;
}
if !continues {
break;
}
if bq_depth > 0 && current_pos > start_pos {
let marker_info = parse_blockquote_marker_info(line);
for i in 0..bq_depth {
if let Some(info) = marker_info.get(i) {
emit_one_blockquote_marker(
builder,
info.leading_spaces,
info.has_trailing_space,
);
}
}
}
let (blank_content, newline_str) = strip_newline(inner);
if !blank_content.is_empty() {
builder.token(SyntaxKind::WHITESPACE.into(), blank_content);
}
builder.token(SyntaxKind::TEXT.into(), "");
builder.token(
SyntaxKind::NEWLINE.into(),
if newline_str.is_empty() {
"\n"
} else {
newline_str
},
);
current_pos += 1;
continue;
}
let (indent_cols, indent_bytes) = leading_indent(inner);
if indent_cols < code_indent {
break;
}
if bq_depth > 0 && current_pos > start_pos {
let marker_info = parse_blockquote_marker_info(line);
for i in 0..bq_depth {
if let Some(info) = marker_info.get(i) {
emit_one_blockquote_marker(
builder,
info.leading_spaces,
info.has_trailing_space,
);
}
}
}
if indent_bytes > 0 {
let indent_str = &inner[..indent_bytes];
builder.token(SyntaxKind::WHITESPACE.into(), indent_str);
}
let content = &inner[indent_bytes..];
let (content_without_newline, newline_str) = strip_newline(content);
if !content_without_newline.is_empty() {
builder.token(SyntaxKind::TEXT.into(), content_without_newline);
}
if !newline_str.is_empty() {
builder.token(SyntaxKind::NEWLINE.into(), newline_str);
}
current_pos += 1;
}
builder.finish_node(); builder.finish_node();
current_pos
}
use crate::parser::utils::container_stack::leading_indent;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_indented_code_line() {
assert!(is_indented_code_line(" code"));
assert!(is_indented_code_line(" code"));
assert!(is_indented_code_line("\tcode"));
assert!(!is_indented_code_line(" not enough"));
assert!(!is_indented_code_line(""));
assert!(!is_indented_code_line("no indent"));
}
#[test]
fn test_parse_simple_code_block() {
let input = vec![" code line 1", " code line 2"];
let mut builder = GreenNodeBuilder::new();
let new_pos = parse_indented_code_block(&mut builder, &input, 0, 0, 0);
assert_eq!(new_pos, 2);
}
#[test]
fn test_parse_code_block_with_blank_line() {
let input = vec![" code line 1", "", " code line 2"];
let mut builder = GreenNodeBuilder::new();
let new_pos = parse_indented_code_block(&mut builder, &input, 0, 0, 0);
assert_eq!(new_pos, 3);
}
#[test]
fn test_parse_code_block_stops_at_unindented() {
let input = vec![" code line 1", " code line 2", "not code"];
let mut builder = GreenNodeBuilder::new();
let new_pos = parse_indented_code_block(&mut builder, &input, 0, 0, 0);
assert_eq!(new_pos, 2);
}
#[test]
fn test_parse_code_block_with_tab() {
let input = vec!["\tcode with tab", "\tanother line"];
let mut builder = GreenNodeBuilder::new();
let new_pos = parse_indented_code_block(&mut builder, &input, 0, 0, 0);
assert_eq!(new_pos, 2);
}
}