Format call expressions (without call chaining) (#5341)

## Summary

This formats call expressions with magic trailing comma and parentheses
behaviour but without call chaining

## Test Plan

Lots of new test fixtures, including some that don't work yet
This commit is contained in:
konstin 2023-06-27 11:29:40 +02:00 committed by GitHub
parent 50a7769d69
commit 7f6cb9dfb5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
53 changed files with 1662 additions and 1853 deletions

View file

@ -1,8 +1,10 @@
use crate::comments::Comments;
use crate::builders::PyFormatterExtensions;
use crate::comments::{dangling_comments, Comments};
use crate::expression::parentheses::{
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
};
use crate::{not_yet_implemented_custom_text, FormatNodeRule, PyFormatter};
use crate::{AsFormat, FormatNodeRule, PyFormatter};
use ruff_formatter::prelude::{format_with, group, soft_block_indent, text};
use ruff_formatter::{write, Buffer, FormatResult};
use rustpython_parser::ast::ExprCall;
@ -11,19 +13,75 @@ pub struct FormatExprCall;
impl FormatNodeRule<ExprCall> for FormatExprCall {
fn fmt_fields(&self, item: &ExprCall, f: &mut PyFormatter) -> FormatResult<()> {
if item.args.is_empty() && item.keywords.is_empty() {
write!(
let ExprCall {
range: _,
func,
args,
keywords,
} = item;
// We have a case with `f()` without any argument, which is a special case because we can
// have a comment with no node attachment inside:
// ```python
// f(
// # This function has a dangling comment
// )
// ```
if args.is_empty() && keywords.is_empty() {
let comments = f.context().comments().clone();
let comments = comments.dangling_comments(item);
return write!(
f,
[not_yet_implemented_custom_text("NOT_IMPLEMENTED_call()")]
)
} else {
write!(
f,
[not_yet_implemented_custom_text(
"NOT_IMPLEMENTED_call(NOT_IMPLEMENTED_arg)"
)]
)
[
func.format(),
text("("),
dangling_comments(comments),
text(")")
]
);
}
let all_args = format_with(|f| {
f.join_comma_separated()
.entries(
// We have the parentheses from the call so the arguments never need any
args.iter()
.map(|arg| (arg, arg.format().with_options(Parenthesize::Never))),
)
.nodes(keywords.iter())
.finish()
});
write!(
f,
[
func.format(),
text("("),
// The outer group is for things like
// ```python
// get_collection(
// hey_this_is_a_very_long_call,
// it_has_funny_attributes_asdf_asdf,
// too_long_for_the_line,
// really=True,
// )
// ```
// The inner group is for things like:
// ```python
// get_collection(
// hey_this_is_a_very_long_call, it_has_funny_attributes_asdf_asdf, really=True
// )
// ```
// TODO(konstin): Doesn't work see wrongly formatted test
&group(&soft_block_indent(&group(&all_args))),
text(")")
]
)
}
fn fmt_dangling_comments(&self, _node: &ExprCall, _f: &mut PyFormatter) -> FormatResult<()> {
// Handled in `fmt_fields`
Ok(())
}
}