use ra_ap_syntax::ast::{self, HasName};
use ra_ap_syntax::{AstNode, NodeOrToken, SyntaxKind};
use crate::formatter::printer::Printer;
use super::comments;
fn collect_inner_comments_excluding_trailing(node: &ra_ap_syntax::SyntaxNode) -> Vec<String> {
let newline_before_node = comments::has_newline_before_node(node);
let mut first_comment_skipped = false;
node.children_with_tokens()
.take_while(|child| !matches!(child, NodeOrToken::Node(n) if n.kind() == SyntaxKind::NAME))
.filter_map(|child| match child {
NodeOrToken::Token(t) if t.kind() == SyntaxKind::COMMENT => {
let text = t.text().to_string();
if text.starts_with("///") || text.starts_with("//!") {
return None;
}
if !newline_before_node && !first_comment_skipped {
first_comment_skipped = true;
return None;
}
Some(text)
}
_ => None,
})
.collect()
}
fn get_trailing_comment_for_prev(node: &ra_ap_syntax::SyntaxNode) -> Option<(String, String)> {
if comments::has_newline_before_node(node) {
return None;
}
let whitespace_before = get_whitespace_before_node(node);
for child in node.children_with_tokens() {
match child {
NodeOrToken::Token(t) => {
if t.kind() == SyntaxKind::COMMENT {
let text = t.text().to_string();
if !text.starts_with("///") && !text.starts_with("//!") {
return Some((whitespace_before, text));
}
} else if t.kind() == SyntaxKind::WHITESPACE && t.text().contains('\n') {
return None;
}
}
NodeOrToken::Node(n) if n.kind() == SyntaxKind::NAME => {
return None;
}
_ => {}
}
}
None
}
fn get_whitespace_before_node(node: &ra_ap_syntax::SyntaxNode) -> String {
let mut current = node.prev_sibling_or_token();
while let Some(item) = current {
match &item {
NodeOrToken::Token(t) => {
if t.kind() == SyntaxKind::WHITESPACE {
return t.text().to_string();
} else if t.kind() == SyntaxKind::COMMA {
current = t.prev_sibling_or_token();
continue;
} else {
return " ".to_string();
}
}
NodeOrToken::Node(_) => return " ".to_string(),
}
}
" ".to_string()
}
pub fn format_record_fields(fields: &ast::RecordFieldList, buf: &mut String, indent: usize) {
let field_list: Vec<_> = fields.fields().collect();
for (idx, field) in field_list.iter().enumerate() {
let trailing_comment = if idx + 1 < field_list.len() {
get_trailing_comment_for_prev(field_list[idx + 1].syntax())
} else {
comments::get_trailing_comment_sibling(field.syntax())
};
for comment in collect_inner_comments_excluding_trailing(field.syntax()) {
buf.line(indent, &comment);
}
buf.doc_comments(field, indent);
buf.attrs(field, indent);
buf.indent(indent);
buf.visibility(field);
if let Some(name) = field.name() {
buf.push_str(&name.text());
}
buf.push_str(": ");
if let Some(ty) = field.ty() {
buf.push_str(&ty.syntax().text().to_string());
}
if let Some(expr) = field.expr() {
buf.push_str(" = ");
buf.push_str(&expr.syntax().text().to_string());
}
if let Some((ref whitespace, ref comment)) = trailing_comment {
buf.push(',');
buf.push_str(whitespace);
buf.newline(comment);
} else {
buf.push_str(",\n");
}
}
}