use rowan::{NodeOrToken, SyntaxElement};
use super::core::FormatError;
use super::ir::Ir;
use crate::syntax::{RLanguage, SyntaxKind};
pub(super) fn split_lines(
elements: Vec<SyntaxElement<RLanguage>>,
context: &'static str,
) -> Result<Vec<Vec<SyntaxElement<RLanguage>>>, FormatError> {
let mut lines: Vec<Vec<SyntaxElement<RLanguage>>> = Vec::new();
let mut current: Vec<SyntaxElement<RLanguage>> = Vec::new();
let mut break_count = 0usize;
for element in elements {
if let NodeOrToken::Token(token) = &element {
if token.kind() == SyntaxKind::WHITESPACE {
continue;
}
if token.kind() == SyntaxKind::NEWLINE || token.kind() == SyntaxKind::SEMICOLON {
if !current.is_empty() {
lines.push(std::mem::take(&mut current));
break_count = 1;
} else if !lines.is_empty() {
break_count += 1;
}
continue;
}
}
if break_count >= 2 {
lines.push(Vec::new());
}
break_count = 0;
if !current.is_empty() {
if is_inline_trailing_comment(&element)
&& !current.iter().any(is_inline_trailing_comment)
{
current.push(element);
continue;
}
return Err(FormatError::AmbiguousConstruct {
context,
snippet: super::render::snippet_from_elements(&[current[0].clone(), element]),
});
}
current.push(element);
}
if !current.is_empty() {
lines.push(current);
}
Ok(lines)
}
pub(super) fn is_trivia(kind: SyntaxKind) -> bool {
matches!(kind, SyntaxKind::WHITESPACE | SyntaxKind::NEWLINE)
}
pub(super) fn is_inline_trailing_comment(element: &SyntaxElement<RLanguage>) -> bool {
inline_trailing_comment_text(element).is_some()
}
pub(super) fn inline_trailing_comment_text(element: &SyntaxElement<RLanguage>) -> Option<String> {
match element {
NodeOrToken::Token(tok) if tok.kind() == SyntaxKind::COMMENT => {
Some(tok.text().to_string())
}
NodeOrToken::Node(node) if node.kind() == SyntaxKind::ROXYGEN_BLOCK => {
let text = node.text().to_string();
(!text.contains('\n')).then_some(text)
}
_ => None,
}
}
pub(super) fn is_quarto_code_annotation(text: &str) -> bool {
let Some(annotation) = text.trim_end().strip_prefix("# <") else {
return false;
};
let Some(number) = annotation.strip_suffix('>') else {
return false;
};
!number.is_empty() && number.bytes().all(|byte| byte.is_ascii_digit())
}
pub(super) fn ir_inline_trailing_comment(text: &str) -> Ir {
let suffix = format!(" {text}");
if is_quarto_code_annotation(text) {
Ir::quarto_annotation_suffix(suffix)
} else {
Ir::line_suffix(suffix)
}
}