use crate::formatter::printer::Printer;
use ra_ap_syntax::{
AstNode, NodeOrToken, SyntaxKind, SyntaxNode,
ast::{self, HasGenericParams, HasName, HasVisibility},
};
use super::{format_block_expr_contents, format_stmt_list};
pub fn format_function(node: &SyntaxNode, buf: &mut String, indent: usize) {
let func = match ast::Fn::cast(node.clone()) {
Some(f) => f,
None => return,
};
for child in node.children_with_tokens() {
match child {
NodeOrToken::Token(t) => {
if t.kind() == SyntaxKind::COMMENT {
let text = t.text();
if !text.starts_with("///") && !text.starts_with("//!") {
buf.line(indent, text);
}
} else if t.kind() != SyntaxKind::WHITESPACE {
break;
}
}
NodeOrToken::Node(_) => {
break;
}
}
}
buf.doc_comments(&func, indent);
buf.attrs(&func, indent);
buf.indent(indent);
buf.visibility(&func);
if func.const_token().is_some() {
buf.push_str("const ");
}
if func.async_token().is_some() {
buf.push_str("async ");
}
if func.unsafe_token().is_some() {
buf.push_str("unsafe ");
}
buf.push_str("fn ");
if let Some(name) = func.name() {
buf.push_str(&name.text());
}
if let Some(generics) = func.generic_param_list() {
buf.push_str(&generics.syntax().text().to_string());
}
let mut used_multiline_params = false;
if let Some(params) = func.param_list() {
let params_vec: Vec<_> = params
.params()
.map(|p| p.syntax().text().to_string())
.collect();
let has_self = params.self_param().is_some();
let self_text = if let Some(self_param) = params.self_param() {
self_param.syntax().text().to_string()
} else {
String::new()
};
let mut single_line_content = String::new();
if has_self {
single_line_content.push_str(&self_text);
if !params_vec.is_empty() {
single_line_content.push_str(", ");
}
}
for (i, p) in params_vec.iter().enumerate() {
if i > 0 {
single_line_content.push_str(", ");
}
single_line_content.push_str(p);
}
let mut hypothetical_line_len = indent;
if let Some(vis) = func.visibility() {
hypothetical_line_len += u32::from(vis.syntax().text().len()) as usize + 1;
}
if func.const_token().is_some() {
hypothetical_line_len += 6;
}
if func.async_token().is_some() {
hypothetical_line_len += 6;
}
if func.unsafe_token().is_some() {
hypothetical_line_len += 7;
}
hypothetical_line_len += 3;
if let Some(name) = func.name() {
hypothetical_line_len += name.text().len();
}
if let Some(generics) = func.generic_param_list() {
hypothetical_line_len += u32::from(generics.syntax().text().len()) as usize;
}
hypothetical_line_len += 2 + single_line_content.len();
if let Some(ret) = func.ret_type() {
hypothetical_line_len += 4;
if let Some(ty) = ret.ty() {
hypothetical_line_len += u32::from(ty.syntax().text().len()) as usize;
}
}
hypothetical_line_len += 2;
let is_single_line = hypothetical_line_len < crate::formatter::config::MAX_WIDTH;
buf.push('(');
if is_single_line {
if let Some(self_param) = params.self_param() {
buf.push_str(&self_param.syntax().text().to_string());
if !params_vec.is_empty() {
buf.push_str(", ");
}
}
for (i, p) in params_vec.iter().enumerate() {
if i > 0 {
buf.push_str(", ");
}
buf.push_str(p);
}
buf.push(')');
} else {
used_multiline_params = true;
buf.push('\n');
let inner_indent = indent + 4;
if let Some(self_param) = params.self_param() {
buf.line(inner_indent, &format!("{},", self_param.syntax().text()));
}
for p in params_vec {
buf.line(inner_indent, &format!("{},", p));
}
buf.indent(indent);
buf.push(')');
}
} else {
buf.push_str("()");
}
if let Some(ret) = func.ret_type() {
buf.push_str(" -> ");
if let Some(ty) = ret.ty() {
buf.push_str(&ty.syntax().text().to_string());
}
}
if let Some(where_clause) = func.where_clause() {
buf.push('\n');
buf.indent(indent);
buf.push_str(&where_clause.syntax().text().to_string());
}
if let Some(body) = func.body() {
let is_empty = if let Some(stmt_list) = body.stmt_list() {
stmt_list.statements().next().is_none()
&& stmt_list.tail_expr().is_none()
&& !stmt_list
.syntax()
.children_with_tokens()
.any(|c| matches!(&c, NodeOrToken::Token(t) if t.kind() == SyntaxKind::COMMENT))
} else {
true
};
let keep_expanded = used_multiline_params || func.where_clause().is_some();
if is_empty && !keep_expanded {
buf.newline(" {}");
} else if is_empty && keep_expanded {
if func.where_clause().is_some() {
buf.open_brace_newline(indent);
} else {
buf.open_brace();
}
buf.close_brace_ln(indent);
} else {
if func.where_clause().is_some() {
buf.open_brace_newline(indent);
} else {
buf.open_brace();
}
let stmt_list = body.stmt_list();
if let Some(stmt_list) = stmt_list {
format_stmt_list(stmt_list.syntax(), buf, indent + 4);
} else {
for child in body.syntax().children_with_tokens() {
match child {
NodeOrToken::Node(n) => {
format_block_expr_contents(&n, buf, indent + 4);
}
NodeOrToken::Token(t) => {
if t.kind() == SyntaxKind::COMMENT {
buf.line(indent + 4, t.text());
}
}
}
}
}
buf.close_brace_ln(indent);
}
} else {
buf.newline(";");
}
}