use super::build::{
emit_block_macro, emit_md_code_block, emit_md_list, is_block_macro_line,
is_md_code_block_start, is_md_list_start,
};
use crate::parser::events::Event;
use crate::parser::lexer::{RoxygenRole, TokKind, Token};
use crate::syntax::SyntaxKind;
pub(crate) fn emit_roxygen_block(tokens: &[Token], start: usize, events: &mut Vec<Event>) -> usize {
debug_assert_eq!(tokens[start].kind, TokKind::RoxygenMarker);
events.push(Event::Start(SyntaxKind::ROXYGEN_BLOCK));
let mut i = start;
let mut section_open = false;
let mut para_open = false;
loop {
match classify_line(tokens, i) {
LineKind::Tag => {
if para_open {
events.push(Event::Finish); para_open = false;
}
if section_open {
events.push(Event::Finish); }
events.push(Event::Start(SyntaxKind::ROXYGEN_SECTION));
section_open = true;
i = emit_tag_line(tokens, i, events);
}
LineKind::Blank => {
if para_open {
events.push(Event::Finish); para_open = false;
}
if !section_open {
events.push(Event::Start(SyntaxKind::ROXYGEN_SECTION));
section_open = true;
}
i = emit_line_tokens(tokens, i, events); }
LineKind::Prose => {
if !section_open {
events.push(Event::Start(SyntaxKind::ROXYGEN_SECTION));
section_open = true;
}
if is_md_code_block_start(tokens, i) {
if para_open {
events.push(Event::Finish); para_open = false;
}
i = emit_md_code_block(tokens, i, events);
} else if is_md_list_start(tokens, i, para_open) {
if para_open {
events.push(Event::Finish); para_open = false;
}
i = emit_md_list(tokens, i, events);
} else if is_block_macro_line(tokens, i) {
if para_open {
events.push(Event::Finish); para_open = false;
}
i = emit_block_macro(tokens, i, events);
} else {
if !para_open {
events.push(Event::Start(SyntaxKind::ROXYGEN_PARAGRAPH));
para_open = true;
}
i = emit_line_tokens(tokens, i, events);
}
}
}
if tokens.get(i).map(|t| &t.kind) == Some(&TokKind::Newline) {
let mut m = i + 1;
while tokens.get(m).map(|t| &t.kind) == Some(&TokKind::Whitespace) {
m += 1;
}
if tokens.get(m).map(|t| &t.kind) == Some(&TokKind::RoxygenMarker) {
for idx in i..m {
events.push(Event::Tok(idx));
}
i = m;
continue;
}
}
break;
}
if para_open {
events.push(Event::Finish); }
if section_open {
events.push(Event::Finish); }
events.push(Event::Finish); i
}
pub(super) enum LineKind {
Tag,
Blank,
Prose,
}
pub(super) fn classify_line(tokens: &[Token], start: usize) -> LineKind {
let content = line_content_start(tokens, start);
let mut i = content;
while let Some(tok) = tokens.get(i) {
match tok.kind.roxygen_role() {
Some(RoxygenRole::At) => return LineKind::Tag,
Some(RoxygenRole::Content) => return LineKind::Prose,
_ if tok.kind == TokKind::Whitespace => i += 1,
_ => break,
}
}
LineKind::Blank
}
pub(super) fn line_content_start(tokens: &[Token], marker: usize) -> usize {
let mut i = marker + 1;
while tokens.get(i).map(|t| &t.kind) == Some(&TokKind::Whitespace) {
i += 1;
}
i
}
pub(super) fn is_line_body_kind(kind: &TokKind) -> bool {
matches!(kind, TokKind::Whitespace)
|| matches!(kind.roxygen_role(), Some(role) if role != RoxygenRole::Marker)
}
fn emit_line_tokens(tokens: &[Token], start: usize, events: &mut Vec<Event>) -> usize {
events.push(Event::Tok(start)); let mut i = start + 1;
while tokens.get(i).is_some_and(|t| is_line_body_kind(&t.kind)) {
events.push(Event::Tok(i));
i += 1;
}
i
}
fn emit_tag_line(tokens: &[Token], start: usize, events: &mut Vec<Event>) -> usize {
events.push(Event::Tok(start)); let mut i = start + 1;
while tokens.get(i).map(|t| &t.kind) == Some(&TokKind::Whitespace) {
events.push(Event::Tok(i)); i += 1;
}
events.push(Event::Start(SyntaxKind::ROXYGEN_TAG));
while tokens.get(i).is_some_and(|t| is_line_body_kind(&t.kind)) {
events.push(Event::Tok(i));
i += 1;
}
events.push(Event::Finish); i
}