Skip to content

Commit

Permalink
Format Function definitions
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaReiser committed Jun 8, 2023
1 parent 62f994d commit 01a9a40
Show file tree
Hide file tree
Showing 44 changed files with 1,405 additions and 666 deletions.
19 changes: 19 additions & 0 deletions crates/ruff_python_ast/src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,22 @@ impl<'a> From<&'a StmtAsyncFunctionDef> for AnyFunctionDefinition<'a> {
Self::AsyncFunctionDefinition(value)
}
}

impl<'a> From<AnyFunctionDefinition<'a>> for AnyNodeRef<'a> {
fn from(value: AnyFunctionDefinition<'a>) -> Self {
match value {
AnyFunctionDefinition::FunctionDefinition(function_def) => {
AnyNodeRef::StmtFunctionDef(function_def)
}
AnyFunctionDefinition::AsyncFunctionDefinition(async_def) => {
AnyNodeRef::StmtAsyncFunctionDef(async_def)
}
}
}
}

impl<'a> From<&'a AnyFunctionDefinition<'a>> for AnyNodeRef<'a> {
fn from(value: &'a AnyFunctionDefinition<'a>) -> Self {
(*value).into()
}
}
8 changes: 7 additions & 1 deletion crates/ruff_python_formatter/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ def to_camel_case(node: str) -> str:
# )
# src.joinpath(groups[group]).joinpath("mod.rs").write_text(rustfmt(mod_section))
for node in group_nodes:
node_path = src.joinpath(groups[group]).joinpath(f"{to_camel_case(node)}.rs")
# Don't override existing manual implementations
if node_path.exists():
continue

code = f"""
use crate::{{verbatim_text, FormatNodeRule, PyFormatter}};
use ruff_formatter::{{write, Buffer, FormatResult}};
Expand All @@ -91,7 +96,8 @@ def to_camel_case(node: str) -> str:
}}
}}
""".strip() # noqa: E501
src.joinpath(groups[group]).joinpath(f"{to_camel_case(node)}.rs").write_text(

node_path.write_text(
rustfmt(code)
)

Expand Down
5 changes: 3 additions & 2 deletions crates/ruff_python_formatter/src/comments/format.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::comments::SourceComment;
use crate::context::NodeLevel;
use crate::prelude::*;
use crate::trivia::{lines_after, lines_before};
use crate::trivia::{lines_after, lines_before, skip_trailing_trivia};
use ruff_formatter::{format_args, write, FormatError, SourceCode};
use ruff_python_ast::node::AnyNodeRef;
use ruff_python_ast::prelude::AstNode;
Expand Down Expand Up @@ -86,9 +86,10 @@ impl Format<PyFormatContext<'_>> for FormatLeadingAlternateBranchComments<'_> {

write!(f, [leading_comments(self.comments)])?;
} else if let Some(last_preceding) = self.last_node {
let full_end = skip_trailing_trivia(last_preceding.end(), f.context().contents());
// The leading comments formatting ensures that it preserves the right amount of lines after
// We need to take care of this ourselves, if there's no leading `else` comment.
if lines_after(last_preceding.end(), f.context().contents()) > 1 {
if lines_after(full_end, f.context().contents()) > 1 {
write!(f, [empty_line()])?;
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/ruff_python_formatter/src/comments/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ use crate::comments::map::MultiMap;
use crate::comments::node_key::NodeRefEqualityKey;
use crate::comments::visitor::CommentsVisitor;
pub(crate) use format::{
dangling_comments, dangling_node_comments, leading_alternate_branch_comments,
dangling_comments, dangling_node_comments, leading_alternate_branch_comments, leading_comments,
leading_node_comments, trailing_comments, trailing_node_comments,
};
use ruff_formatter::{SourceCode, SourceCodeSlice};
Expand Down
32 changes: 32 additions & 0 deletions crates/ruff_python_formatter/src/comments/placement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub(super) fn place_comment<'a>(
.or_else(|comment| {
handle_trailing_binary_expression_left_or_operator_comment(comment, locator)
})
.or_else(handle_leading_function_with_decorators_comment)
}

/// Handles leading comments in front of a match case or a trailing comment of the `match` statement.
Expand Down Expand Up @@ -511,6 +512,11 @@ fn handle_trailing_end_of_line_condition_comment<'a>(
| AnyNodeRef::StmtAsyncWith(StmtAsyncWith { items, .. }) => {
items.last().map(AnyNodeRef::from)
}
AnyNodeRef::StmtFunctionDef(StmtFunctionDef { returns, args, .. })
| AnyNodeRef::StmtAsyncFunctionDef(StmtAsyncFunctionDef { returns, args, .. }) => returns
.as_deref()
.map(AnyNodeRef::from)
.or_else(|| Some(AnyNodeRef::from(args.as_ref()))),
_ => None,
};

Expand Down Expand Up @@ -820,6 +826,32 @@ fn find_pos_only_slash_offset(
None
}

/// Handles own line comments between the last function decorator and the *header* of the function.
/// It attaches these comments as dangling comments to the function instead of making them
/// leading argument comments.
///
/// ```python
/// @decorator
/// # leading function comment
/// def test():
/// ...
/// ```
fn handle_leading_function_with_decorators_comment(comment: DecoratedComment) -> CommentPlacement {
let is_preceding_decorator = comment
.preceding_node()
.map_or(false, |node| node.is_decorator());

let is_following_arguments = comment
.following_node()
.map_or(false, |node| node.is_arguments());

if comment.text_position().is_own_line() && is_preceding_decorator && is_following_arguments {
CommentPlacement::dangling(comment.enclosing_node(), comment)
} else {
CommentPlacement::Default(comment)
}
}

/// Returns `true` if `right` is `Some` and `left` and `right` are referentially equal.
fn are_same_optional<'a, T>(left: AnyNodeRef, right: Option<T>) -> bool
where
Expand Down
7 changes: 7 additions & 0 deletions crates/ruff_python_formatter/src/comments/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,13 @@ impl<'ast> PreorderVisitor<'ast> for CommentsVisitor<'ast> {
self.finish_node(expr);
}

fn visit_decorator(&mut self, decorator: &'ast Decorator) {
if self.start_node(decorator).is_traverse() {
walk_decorator(self, decorator);
}
self.finish_node(decorator);
}

fn visit_expr(&mut self, expr: &'ast Expr) {
if self.start_node(expr).is_traverse() {
walk_expr(self, expr);
Expand Down
5 changes: 4 additions & 1 deletion crates/ruff_python_formatter/src/expression/expr_constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ impl FormatNodeRule<ExprConstant> for FormatExprConstant {

impl NeedsParentheses for ExprConstant {
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
default_expression_needs_parentheses(self.into(), parenthesize, source)
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
Parentheses::Optional => Parentheses::Never,
parentheses => parentheses,
}
}
}
32 changes: 32 additions & 0 deletions crates/ruff_python_formatter/src/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2858,3 +2858,35 @@ impl<'ast> IntoFormat<PyFormatContext<'ast>> for ast::MatchCase {
FormatOwnedWithRule::new(self, crate::other::match_case::FormatMatchCase::default())
}
}

impl FormatRule<ast::Decorator, PyFormatContext<'_>> for crate::other::decorator::FormatDecorator {
#[inline]
fn fmt(
&self,
node: &ast::Decorator,
f: &mut Formatter<PyFormatContext<'_>>,
) -> FormatResult<()> {
FormatNodeRule::<ast::Decorator>::fmt(self, node, f)
}
}
impl<'ast> AsFormat<PyFormatContext<'ast>> for ast::Decorator {
type Format<'a> = FormatRefWithRule<
'a,
ast::Decorator,
crate::other::decorator::FormatDecorator,
PyFormatContext<'ast>,
>;
fn format(&self) -> Self::Format<'_> {
FormatRefWithRule::new(self, crate::other::decorator::FormatDecorator::default())
}
}
impl<'ast> IntoFormat<PyFormatContext<'ast>> for ast::Decorator {
type Format = FormatOwnedWithRule<
ast::Decorator,
crate::other::decorator::FormatDecorator,
PyFormatContext<'ast>,
>;
fn into_format(self) -> Self::Format {
FormatOwnedWithRule::new(self, crate::other::decorator::FormatDecorator::default())
}
}
8 changes: 3 additions & 5 deletions crates/ruff_python_formatter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,17 @@ where
N: AstNode,
{
fn fmt(&self, node: &N, f: &mut PyFormatter) -> FormatResult<()> {
write!(f, [source_position(node.start())])?;
self.fmt_leading_comments(node, f)?;
self.fmt_node(node, f)?;
self.fmt_dangling_comments(node, f)?;
self.fmt_trailing_comments(node, f)?;
write!(f, [source_position(node.end())])
self.fmt_trailing_comments(node, f)
}

/// Formats the node without comments. Ignores any suppression comments.
fn fmt_node(&self, node: &N, f: &mut PyFormatter) -> FormatResult<()> {
write!(f, [source_position(node.start())])?;
self.fmt_fields(node, f)?;

Ok(())
write!(f, [source_position(node.end())])
}

/// Formats the node's fields.
Expand Down
4 changes: 2 additions & 2 deletions crates/ruff_python_formatter/src/other/arguments.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{not_yet_implemented, FormatNodeRule, PyFormatter};
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
use ruff_formatter::{write, Buffer, FormatResult};
use rustpython_parser::ast::Arguments;

Expand All @@ -7,6 +7,6 @@ pub struct FormatArguments;

impl FormatNodeRule<Arguments> for FormatArguments {
fn fmt_fields(&self, item: &Arguments, f: &mut PyFormatter) -> FormatResult<()> {
write!(f, [not_yet_implemented(item)])
write!(f, [verbatim_text(item)])
}
}
27 changes: 27 additions & 0 deletions crates/ruff_python_formatter/src/other/decorator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use crate::expression::parentheses::Parenthesize;
use crate::prelude::*;
use crate::FormatNodeRule;
use ruff_formatter::write;
use rustpython_parser::ast::Decorator;

#[derive(Default)]
pub struct FormatDecorator;

impl FormatNodeRule<Decorator> for FormatDecorator {
fn fmt_fields(&self, item: &Decorator, f: &mut PyFormatter) -> FormatResult<()> {
let Decorator {
expression,
range: _,
} = item;

// Manually handle the case where it has leading comments? Could I already have done that before?

write!(
f,
[
text("@"),
expression.format().with_options(Parenthesize::Optional)
]
)
}
}
1 change: 1 addition & 0 deletions crates/ruff_python_formatter/src/other/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub(crate) mod alias;
pub(crate) mod arg;
pub(crate) mod arguments;
pub(crate) mod comprehension;
pub(crate) mod decorator;
pub(crate) mod excepthandler_except_handler;
pub(crate) mod keyword;
pub(crate) mod match_case;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,16 @@ class NormalClass (
```diff
--- Black
+++ Ruff
@@ -1,30 +1,16 @@
@@ -1,30 +1,18 @@
-class SimpleClassWithBlankParentheses:
- pass
-
+NOT_YET_IMPLEMENTED_StmtClassDef
-class ClassWithSpaceParentheses:
- first_test_data = 90
- second_test_data = 100
-
- def test_func(self):
- return None
+NOT_YET_IMPLEMENTED_StmtClassDef
Expand All @@ -57,15 +57,15 @@ class NormalClass (
+NOT_YET_IMPLEMENTED_StmtClassDef
-def public_func_with_blank_parentheses():
def public_func_with_blank_parentheses():
- return None
+NOT_YET_IMPLEMENTED_StmtFunctionDef
+ NOT_YET_IMPLEMENTED_StmtReturn
-def class_under_the_func_with_blank_parentheses():
def class_under_the_func_with_blank_parentheses():
- class InsideFunc:
- pass
+NOT_YET_IMPLEMENTED_StmtFunctionDef
+ NOT_YET_IMPLEMENTED_StmtClassDef
-class NormalClass:
Expand All @@ -87,10 +87,12 @@ NOT_YET_IMPLEMENTED_StmtClassDef
NOT_YET_IMPLEMENTED_StmtClassDef
NOT_YET_IMPLEMENTED_StmtFunctionDef
def public_func_with_blank_parentheses():
NOT_YET_IMPLEMENTED_StmtReturn
NOT_YET_IMPLEMENTED_StmtFunctionDef
def class_under_the_func_with_blank_parentheses():
NOT_YET_IMPLEMENTED_StmtClassDef
NOT_YET_IMPLEMENTED_StmtClassDef
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,32 @@ def bobtwo(): \
```diff
--- Black
+++ Ruff
@@ -1,6 +1,4 @@
@@ -1,6 +1,8 @@
-def bob(): # pylint: disable=W9016
- pass
+NOT_YET_IMPLEMENTED_StmtFunctionDef
+def bob():
+ # pylint: disable=W9016
+ NOT_YET_IMPLEMENTED_StmtPass
-def bobtwo(): # some comment here
- pass
+NOT_YET_IMPLEMENTED_StmtFunctionDef
+def bobtwo():
+ # some comment here
+ NOT_YET_IMPLEMENTED_StmtPass
```

## Ruff Output

```py
NOT_YET_IMPLEMENTED_StmtFunctionDef
def bob():
# pylint: disable=W9016
NOT_YET_IMPLEMENTED_StmtPass
NOT_YET_IMPLEMENTED_StmtFunctionDef
def bobtwo():
# some comment here
NOT_YET_IMPLEMENTED_StmtPass
```

## Black Output
Expand Down
Loading

0 comments on commit 01a9a40

Please sign in to comment.