mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-02 18:02:58 +00:00
Support fmt: skip
on compound statements (#6593)
This commit is contained in:
parent
4dc32a00d0
commit
fa7442da2f
23 changed files with 1385 additions and 625 deletions
73
crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/match.py
vendored
Normal file
73
crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/match.py
vendored
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
def http_error(status):
|
||||||
|
match status : # fmt: skip
|
||||||
|
case 400 : # fmt: skip
|
||||||
|
return "Bad request"
|
||||||
|
case 404:
|
||||||
|
return "Not found"
|
||||||
|
case 418:
|
||||||
|
return "I'm a teapot"
|
||||||
|
case _:
|
||||||
|
return "Something's wrong with the internet"
|
||||||
|
|
||||||
|
# point is an (x, y) tuple
|
||||||
|
match point:
|
||||||
|
case (0, 0): # fmt: skip
|
||||||
|
print("Origin")
|
||||||
|
case (0, y):
|
||||||
|
print(f"Y={y}")
|
||||||
|
case (x, 0):
|
||||||
|
print(f"X={x}")
|
||||||
|
case (x, y):
|
||||||
|
print(f"X={x}, Y={y}")
|
||||||
|
case _:
|
||||||
|
raise ValueError("Not a point")
|
||||||
|
|
||||||
|
class Point:
|
||||||
|
x: int
|
||||||
|
y: int
|
||||||
|
|
||||||
|
def location(point):
|
||||||
|
match point:
|
||||||
|
case Point(x=0, y =0 ) : # fmt: skip
|
||||||
|
print("Origin is the point's location.")
|
||||||
|
case Point(x=0, y=y):
|
||||||
|
print(f"Y={y} and the point is on the y-axis.")
|
||||||
|
case Point(x=x, y=0):
|
||||||
|
print(f"X={x} and the point is on the x-axis.")
|
||||||
|
case Point():
|
||||||
|
print("The point is located somewhere else on the plane.")
|
||||||
|
case _:
|
||||||
|
print("Not a point")
|
||||||
|
|
||||||
|
|
||||||
|
match points:
|
||||||
|
case []:
|
||||||
|
print("No points in the list.")
|
||||||
|
case [
|
||||||
|
Point(0, 0)
|
||||||
|
]: # fmt: skip
|
||||||
|
print("The origin is the only point in the list.")
|
||||||
|
case [Point(x, y)]:
|
||||||
|
print(f"A single point {x}, {y} is in the list.")
|
||||||
|
case [Point(0, y1), Point(0, y2)]:
|
||||||
|
print(f"Two points on the Y axis at {y1}, {y2} are in the list.")
|
||||||
|
case _:
|
||||||
|
print("Something else is found in the list.")
|
||||||
|
|
||||||
|
|
||||||
|
match test_variable:
|
||||||
|
case (
|
||||||
|
'warning',
|
||||||
|
code,
|
||||||
|
40
|
||||||
|
): # fmt: skip
|
||||||
|
print("A warning has been received.")
|
||||||
|
case ('error', code, _):
|
||||||
|
print(f"An error {code} occurred.")
|
||||||
|
|
||||||
|
|
||||||
|
match point:
|
||||||
|
case Point(x, y) if x == y: # fmt: skip
|
||||||
|
print(f"The point is located on the diagonal Y=X at {x}.")
|
||||||
|
case Point(x, y):
|
||||||
|
print(f"Point is not on the diagonal.")
|
31
crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/or_else.py
vendored
Normal file
31
crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/or_else.py
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
for item in container:
|
||||||
|
if search_something(item):
|
||||||
|
# Found it!
|
||||||
|
process(item)
|
||||||
|
break
|
||||||
|
# leading comment
|
||||||
|
else : #fmt: skip
|
||||||
|
# Didn't find anything..
|
||||||
|
not_found_in_container()
|
||||||
|
|
||||||
|
|
||||||
|
while i < 10:
|
||||||
|
print(i)
|
||||||
|
|
||||||
|
# leading comment
|
||||||
|
else : #fmt: skip
|
||||||
|
# Didn't find anything..
|
||||||
|
print("I was already larger than 9")
|
||||||
|
|
||||||
|
|
||||||
|
try : # fmt: skip
|
||||||
|
some_call()
|
||||||
|
except Exception : # fmt: skip
|
||||||
|
pass
|
||||||
|
except : # fmt: skip
|
||||||
|
handle_exception()
|
||||||
|
|
||||||
|
else : # fmt: skip
|
||||||
|
pass
|
||||||
|
finally : # fmt: skip
|
||||||
|
finally_call()
|
20
crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/parentheses.py
vendored
Normal file
20
crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/parentheses.py
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
if (
|
||||||
|
# a leading condition comment
|
||||||
|
len([1, 23, 3, 4, 5]) > 2 # trailing condition comment
|
||||||
|
# trailing own line comment
|
||||||
|
): # fmt: skip
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if ( # trailing open parentheses comment
|
||||||
|
# a leading condition comment
|
||||||
|
len([1, 23, 3, 4, 5]) > 2
|
||||||
|
) and ((((y)))): # fmt: skip
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if ( # trailing open parentheses comment
|
||||||
|
# a leading condition comment
|
||||||
|
len([1, 23, 3, 4, 5]) > 2
|
||||||
|
) and y: # fmt: skip
|
||||||
|
pass
|
28
crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/type_params.py
vendored
Normal file
28
crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/type_params.py
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
class TestTypeParam[ T ]: # fmt: skip
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TestTypeParam [ # trailing open paren comment
|
||||||
|
# leading comment
|
||||||
|
T # trailing type param comment
|
||||||
|
# trailing type param own line comment
|
||||||
|
]: # fmt: skip
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TestTrailingComment4[
|
||||||
|
T
|
||||||
|
] ( # trailing arguments open parenthesis comment
|
||||||
|
# leading argument comment
|
||||||
|
A # trailing argument comment
|
||||||
|
# trailing argument own line comment
|
||||||
|
): # fmt: skip
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test [
|
||||||
|
# comment
|
||||||
|
A,
|
||||||
|
|
||||||
|
# another
|
||||||
|
|
||||||
|
B,
|
||||||
|
] (): # fmt: skip
|
||||||
|
...
|
|
@ -400,7 +400,6 @@ impl<'a> Comments<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over the [leading](self#leading-comments) and [trailing comments](self#trailing-comments) of `node`.
|
/// Returns an iterator over the [leading](self#leading-comments) and [trailing comments](self#trailing-comments) of `node`.
|
||||||
#[allow(unused)]
|
|
||||||
pub(crate) fn leading_trailing_comments<T>(
|
pub(crate) fn leading_trailing_comments<T>(
|
||||||
&self,
|
&self,
|
||||||
node: T,
|
node: T,
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
use crate::comments::{trailing_comments, SourceComment};
|
|
||||||
use crate::expression::maybe_parenthesize_expression;
|
|
||||||
use crate::expression::parentheses::Parenthesize;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::{FormatNodeRule, PyFormatter};
|
|
||||||
use ruff_formatter::FormatRuleWithOptions;
|
use ruff_formatter::FormatRuleWithOptions;
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use ruff_python_ast::ExceptHandlerExceptHandler;
|
use ruff_python_ast::ExceptHandlerExceptHandler;
|
||||||
|
|
||||||
|
use crate::comments::SourceComment;
|
||||||
|
use crate::expression::maybe_parenthesize_expression;
|
||||||
|
use crate::expression::parentheses::Parenthesize;
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::statement::clause::{clause_header, ClauseHeader};
|
||||||
|
use crate::{FormatNodeRule, PyFormatter};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default)]
|
||||||
pub enum ExceptHandlerKind {
|
pub enum ExceptHandlerKind {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -49,32 +51,42 @@ impl FormatNodeRule<ExceptHandlerExceptHandler> for FormatExceptHandlerExceptHan
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
text("except"),
|
clause_header(
|
||||||
match self.except_handler_kind {
|
ClauseHeader::ExceptHandler(item),
|
||||||
ExceptHandlerKind::Regular => None,
|
dangling_comments,
|
||||||
ExceptHandlerKind::Starred => Some(text("*")),
|
&format_with(|f| {
|
||||||
}
|
write!(
|
||||||
]
|
f,
|
||||||
)?;
|
[
|
||||||
|
text("except"),
|
||||||
|
match self.except_handler_kind {
|
||||||
|
ExceptHandlerKind::Regular => None,
|
||||||
|
ExceptHandlerKind::Starred => Some(text("*")),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)?;
|
||||||
|
|
||||||
if let Some(type_) = type_ {
|
if let Some(type_) = type_ {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
space(),
|
space(),
|
||||||
maybe_parenthesize_expression(type_, item, Parenthesize::IfBreaks)
|
maybe_parenthesize_expression(
|
||||||
]
|
type_,
|
||||||
)?;
|
item,
|
||||||
if let Some(name) = name {
|
Parenthesize::IfBreaks
|
||||||
write!(f, [space(), text("as"), space(), name.format()])?;
|
)
|
||||||
}
|
]
|
||||||
}
|
)?;
|
||||||
write!(
|
if let Some(name) = name {
|
||||||
f,
|
write!(f, [space(), text("as"), space(), name.format()])?;
|
||||||
[
|
}
|
||||||
text(":"),
|
}
|
||||||
trailing_comments(dangling_comments),
|
|
||||||
block_indent(&body.format()),
|
Ok(())
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
block_indent(&body.format())
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,10 @@ use ruff_python_ast::{MatchCase, Pattern, Ranged};
|
||||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||||
use ruff_text_size::TextRange;
|
use ruff_text_size::TextRange;
|
||||||
|
|
||||||
use crate::comments::{leading_comments, trailing_comments, SourceComment};
|
use crate::comments::{leading_comments, SourceComment};
|
||||||
use crate::expression::parentheses::parenthesized;
|
use crate::expression::parentheses::parenthesized;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::statement::clause::{clause_header, ClauseHeader};
|
||||||
use crate::{FormatError, FormatNodeRule, PyFormatter};
|
use crate::{FormatError, FormatNodeRule, PyFormatter};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -23,30 +24,39 @@ impl FormatNodeRule<MatchCase> for FormatMatchCase {
|
||||||
let comments = f.context().comments().clone();
|
let comments = f.context().comments().clone();
|
||||||
let dangling_item_comments = comments.dangling_comments(item);
|
let dangling_item_comments = comments.dangling_comments(item);
|
||||||
|
|
||||||
write!(f, [text("case"), space()])?;
|
|
||||||
let leading_pattern_comments = comments.leading_comments(pattern);
|
|
||||||
if !leading_pattern_comments.is_empty() {
|
|
||||||
parenthesized(
|
|
||||||
"(",
|
|
||||||
&format_args![leading_comments(leading_pattern_comments), pattern.format()],
|
|
||||||
")",
|
|
||||||
)
|
|
||||||
.fmt(f)?;
|
|
||||||
} else if is_match_case_pattern_parenthesized(item, pattern, f.context())? {
|
|
||||||
parenthesized("(", &pattern.format(), ")").fmt(f)?;
|
|
||||||
} else {
|
|
||||||
pattern.format().fmt(f)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(guard) = guard {
|
|
||||||
write!(f, [space(), text("if"), space(), guard.format()])?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
text(":"),
|
clause_header(
|
||||||
trailing_comments(dangling_item_comments),
|
ClauseHeader::MatchCase(item),
|
||||||
|
dangling_item_comments,
|
||||||
|
&format_with(|f| {
|
||||||
|
write!(f, [text("case"), space()])?;
|
||||||
|
|
||||||
|
let leading_pattern_comments = comments.leading_comments(pattern);
|
||||||
|
if !leading_pattern_comments.is_empty() {
|
||||||
|
parenthesized(
|
||||||
|
"(",
|
||||||
|
&format_args![
|
||||||
|
leading_comments(leading_pattern_comments),
|
||||||
|
pattern.format()
|
||||||
|
],
|
||||||
|
")",
|
||||||
|
)
|
||||||
|
.fmt(f)?;
|
||||||
|
} else if is_match_case_pattern_parenthesized(item, pattern, f.context())? {
|
||||||
|
parenthesized("(", &pattern.format(), ")").fmt(f)?;
|
||||||
|
} else {
|
||||||
|
pattern.format().fmt(f)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(guard) = guard {
|
||||||
|
write!(f, [space(), text("if"), space(), guard.format()])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}),
|
||||||
|
),
|
||||||
block_indent(&body.format())
|
block_indent(&body.format())
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
407
crates/ruff_python_formatter/src/statement/clause.rs
Normal file
407
crates/ruff_python_formatter/src/statement/clause.rs
Normal file
|
@ -0,0 +1,407 @@
|
||||||
|
use crate::comments::{
|
||||||
|
leading_alternate_branch_comments, trailing_comments, SourceComment, SuppressionKind,
|
||||||
|
};
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::verbatim::write_suppressed_clause_header;
|
||||||
|
use ruff_formatter::{Argument, Arguments, FormatError};
|
||||||
|
use ruff_python_ast::node::AnyNodeRef;
|
||||||
|
use ruff_python_ast::{
|
||||||
|
ElifElseClause, ExceptHandlerExceptHandler, MatchCase, Ranged, StmtClassDef, StmtFor,
|
||||||
|
StmtFunctionDef, StmtIf, StmtMatch, StmtTry, StmtWhile, StmtWith,
|
||||||
|
};
|
||||||
|
use ruff_python_trivia::{SimpleToken, SimpleTokenKind, SimpleTokenizer};
|
||||||
|
use ruff_text_size::{TextRange, TextSize};
|
||||||
|
|
||||||
|
/// The header of a compound statement clause.
|
||||||
|
///
|
||||||
|
/// > A compound statement consists of one or more ‘clauses.’ A clause consists of a header and a ‘suite.’
|
||||||
|
/// > The clause headers of a particular compound statement are all at the same indentation level.
|
||||||
|
/// > Each clause header begins with a uniquely identifying keyword and ends with a colon.
|
||||||
|
/// [source](https://docs.python.org/3/reference/compound_stmts.html#compound-statements)
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub(crate) enum ClauseHeader<'a> {
|
||||||
|
Class(&'a StmtClassDef),
|
||||||
|
Function(&'a StmtFunctionDef),
|
||||||
|
If(&'a StmtIf),
|
||||||
|
ElifElse(&'a ElifElseClause),
|
||||||
|
Try(&'a StmtTry),
|
||||||
|
ExceptHandler(&'a ExceptHandlerExceptHandler),
|
||||||
|
TryFinally(&'a StmtTry),
|
||||||
|
Match(&'a StmtMatch),
|
||||||
|
MatchCase(&'a MatchCase),
|
||||||
|
For(&'a StmtFor),
|
||||||
|
While(&'a StmtWhile),
|
||||||
|
With(&'a StmtWith),
|
||||||
|
OrElse(ElseClause<'a>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ClauseHeader<'a> {
|
||||||
|
/// The range from the clause keyword up to and including the final colon.
|
||||||
|
pub(crate) fn range(self, source: &str) -> FormatResult<TextRange> {
|
||||||
|
let keyword_range = self.first_keyword_range(source)?;
|
||||||
|
|
||||||
|
let mut last_child_end = None;
|
||||||
|
|
||||||
|
self.visit(&mut |child| last_child_end = Some(child.end()));
|
||||||
|
|
||||||
|
let end = match self {
|
||||||
|
ClauseHeader::Class(class) => Some(last_child_end.unwrap_or(class.name.end())),
|
||||||
|
ClauseHeader::Function(function) => Some(last_child_end.unwrap_or(function.name.end())),
|
||||||
|
ClauseHeader::ElifElse(_)
|
||||||
|
| ClauseHeader::Try(_)
|
||||||
|
| ClauseHeader::If(_)
|
||||||
|
| ClauseHeader::TryFinally(_)
|
||||||
|
| ClauseHeader::Match(_)
|
||||||
|
| ClauseHeader::MatchCase(_)
|
||||||
|
| ClauseHeader::For(_)
|
||||||
|
| ClauseHeader::While(_)
|
||||||
|
| ClauseHeader::With(_)
|
||||||
|
| ClauseHeader::OrElse(_) => last_child_end,
|
||||||
|
|
||||||
|
ClauseHeader::ExceptHandler(handler) => handler
|
||||||
|
.name
|
||||||
|
.as_ref()
|
||||||
|
.map(ruff_python_ast::Ranged::end)
|
||||||
|
.or(last_child_end),
|
||||||
|
};
|
||||||
|
|
||||||
|
let colon = colon_range(end.unwrap_or(keyword_range.end()), source)?;
|
||||||
|
|
||||||
|
Ok(TextRange::new(keyword_range.start(), colon.end()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Visits the nodes in the case header.
|
||||||
|
pub(crate) fn visit<F>(self, visitor: &mut F)
|
||||||
|
where
|
||||||
|
F: FnMut(AnyNodeRef),
|
||||||
|
{
|
||||||
|
fn visit<'a, N, F>(node: N, visitor: &mut F)
|
||||||
|
where
|
||||||
|
N: Into<AnyNodeRef<'a>>,
|
||||||
|
F: FnMut(AnyNodeRef<'a>),
|
||||||
|
{
|
||||||
|
visitor(node.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
match self {
|
||||||
|
ClauseHeader::Class(StmtClassDef {
|
||||||
|
type_params,
|
||||||
|
arguments,
|
||||||
|
range: _,
|
||||||
|
decorator_list: _,
|
||||||
|
name: _,
|
||||||
|
body: _,
|
||||||
|
}) => {
|
||||||
|
if let Some(type_params) = type_params.as_deref() {
|
||||||
|
visit(type_params, visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(arguments) = arguments {
|
||||||
|
visit(arguments.as_ref(), visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClauseHeader::Function(StmtFunctionDef {
|
||||||
|
type_params,
|
||||||
|
parameters,
|
||||||
|
range: _,
|
||||||
|
is_async: _,
|
||||||
|
decorator_list: _,
|
||||||
|
name: _,
|
||||||
|
returns: _,
|
||||||
|
body: _,
|
||||||
|
}) => {
|
||||||
|
if let Some(type_params) = type_params.as_ref() {
|
||||||
|
visit(type_params, visitor);
|
||||||
|
}
|
||||||
|
visit(parameters.as_ref(), visitor);
|
||||||
|
}
|
||||||
|
ClauseHeader::If(StmtIf {
|
||||||
|
test,
|
||||||
|
range: _,
|
||||||
|
body: _,
|
||||||
|
elif_else_clauses: _,
|
||||||
|
}) => {
|
||||||
|
visit(test.as_ref(), visitor);
|
||||||
|
}
|
||||||
|
ClauseHeader::ElifElse(ElifElseClause {
|
||||||
|
test,
|
||||||
|
range: _,
|
||||||
|
body: _,
|
||||||
|
}) => {
|
||||||
|
if let Some(test) = test.as_ref() {
|
||||||
|
visit(test, visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ClauseHeader::ExceptHandler(ExceptHandlerExceptHandler {
|
||||||
|
type_: type_expr,
|
||||||
|
range: _,
|
||||||
|
name: _,
|
||||||
|
body: _,
|
||||||
|
}) => {
|
||||||
|
if let Some(type_expr) = type_expr.as_deref() {
|
||||||
|
visit(type_expr, visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClauseHeader::Match(StmtMatch {
|
||||||
|
subject,
|
||||||
|
range: _,
|
||||||
|
cases: _,
|
||||||
|
}) => {
|
||||||
|
visit(subject.as_ref(), visitor);
|
||||||
|
}
|
||||||
|
ClauseHeader::MatchCase(MatchCase {
|
||||||
|
guard,
|
||||||
|
pattern,
|
||||||
|
range: _,
|
||||||
|
body: _,
|
||||||
|
}) => {
|
||||||
|
visit(pattern, visitor);
|
||||||
|
|
||||||
|
if let Some(guard) = guard.as_deref() {
|
||||||
|
visit(guard, visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClauseHeader::For(StmtFor {
|
||||||
|
target,
|
||||||
|
iter,
|
||||||
|
range: _,
|
||||||
|
is_async: _,
|
||||||
|
body: _,
|
||||||
|
orelse: _,
|
||||||
|
}) => {
|
||||||
|
visit(target.as_ref(), visitor);
|
||||||
|
visit(iter.as_ref(), visitor);
|
||||||
|
}
|
||||||
|
ClauseHeader::While(StmtWhile {
|
||||||
|
test,
|
||||||
|
range: _,
|
||||||
|
body: _,
|
||||||
|
orelse: _,
|
||||||
|
}) => {
|
||||||
|
visit(test.as_ref(), visitor);
|
||||||
|
}
|
||||||
|
ClauseHeader::With(StmtWith {
|
||||||
|
items,
|
||||||
|
range: _,
|
||||||
|
is_async: _,
|
||||||
|
body: _,
|
||||||
|
}) => {
|
||||||
|
for item in items {
|
||||||
|
visit(item, visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClauseHeader::Try(_) | ClauseHeader::TryFinally(_) | ClauseHeader::OrElse(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the range of the first keyword that marks the start of the clause header.
|
||||||
|
fn first_keyword_range(self, source: &str) -> FormatResult<TextRange> {
|
||||||
|
match self {
|
||||||
|
ClauseHeader::Class(header) => {
|
||||||
|
find_keyword(header.start(), SimpleTokenKind::Class, source)
|
||||||
|
}
|
||||||
|
ClauseHeader::Function(header) => {
|
||||||
|
let keyword = if header.is_async {
|
||||||
|
SimpleTokenKind::Async
|
||||||
|
} else {
|
||||||
|
SimpleTokenKind::Def
|
||||||
|
};
|
||||||
|
find_keyword(header.start(), keyword, source)
|
||||||
|
}
|
||||||
|
ClauseHeader::If(header) => find_keyword(header.start(), SimpleTokenKind::If, source),
|
||||||
|
ClauseHeader::ElifElse(ElifElseClause {
|
||||||
|
test: None, range, ..
|
||||||
|
}) => find_keyword(range.start(), SimpleTokenKind::Else, source),
|
||||||
|
ClauseHeader::ElifElse(ElifElseClause {
|
||||||
|
test: Some(_),
|
||||||
|
range,
|
||||||
|
..
|
||||||
|
}) => find_keyword(range.start(), SimpleTokenKind::Elif, source),
|
||||||
|
ClauseHeader::Try(header) => find_keyword(header.start(), SimpleTokenKind::Try, source),
|
||||||
|
ClauseHeader::ExceptHandler(header) => {
|
||||||
|
find_keyword(header.start(), SimpleTokenKind::Except, source)
|
||||||
|
}
|
||||||
|
ClauseHeader::TryFinally(header) => {
|
||||||
|
let last_statement = header
|
||||||
|
.orelse
|
||||||
|
.last()
|
||||||
|
.map(AnyNodeRef::from)
|
||||||
|
.or_else(|| header.handlers.last().map(AnyNodeRef::from))
|
||||||
|
.or_else(|| header.body.last().map(AnyNodeRef::from))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
find_keyword(last_statement.end(), SimpleTokenKind::Finally, source)
|
||||||
|
}
|
||||||
|
ClauseHeader::Match(header) => {
|
||||||
|
find_keyword(header.start(), SimpleTokenKind::Match, source)
|
||||||
|
}
|
||||||
|
ClauseHeader::MatchCase(header) => {
|
||||||
|
find_keyword(header.start(), SimpleTokenKind::Case, source)
|
||||||
|
}
|
||||||
|
ClauseHeader::For(header) => {
|
||||||
|
let keyword = if header.is_async {
|
||||||
|
SimpleTokenKind::Async
|
||||||
|
} else {
|
||||||
|
SimpleTokenKind::For
|
||||||
|
};
|
||||||
|
find_keyword(header.start(), keyword, source)
|
||||||
|
}
|
||||||
|
ClauseHeader::While(header) => {
|
||||||
|
find_keyword(header.start(), SimpleTokenKind::While, source)
|
||||||
|
}
|
||||||
|
ClauseHeader::With(header) => {
|
||||||
|
let keyword = if header.is_async {
|
||||||
|
SimpleTokenKind::Async
|
||||||
|
} else {
|
||||||
|
SimpleTokenKind::With
|
||||||
|
};
|
||||||
|
|
||||||
|
find_keyword(header.start(), keyword, source)
|
||||||
|
}
|
||||||
|
ClauseHeader::OrElse(header) => match header {
|
||||||
|
ElseClause::Try(try_stmt) => {
|
||||||
|
let last_statement = try_stmt
|
||||||
|
.handlers
|
||||||
|
.last()
|
||||||
|
.map(AnyNodeRef::from)
|
||||||
|
.or_else(|| try_stmt.body.last().map(AnyNodeRef::from))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
find_keyword(last_statement.end(), SimpleTokenKind::Else, source)
|
||||||
|
}
|
||||||
|
ElseClause::For(StmtFor { body, .. })
|
||||||
|
| ElseClause::While(StmtWhile { body, .. }) => {
|
||||||
|
find_keyword(body.last().unwrap().end(), SimpleTokenKind::Else, source)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub(crate) enum ElseClause<'a> {
|
||||||
|
Try(&'a StmtTry),
|
||||||
|
For(&'a StmtFor),
|
||||||
|
While(&'a StmtWhile),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct FormatClauseHeader<'a, 'ast> {
|
||||||
|
header: ClauseHeader<'a>,
|
||||||
|
/// How to format the clause header
|
||||||
|
formatter: Argument<'a, PyFormatContext<'ast>>,
|
||||||
|
|
||||||
|
/// Leading comments coming before the branch, together with the previous node, if any. Only relevant
|
||||||
|
/// for alternate branches.
|
||||||
|
leading_comments: Option<(&'a [SourceComment], Option<AnyNodeRef<'a>>)>,
|
||||||
|
|
||||||
|
/// The trailing comments coming after the colon.
|
||||||
|
trailing_colon_comment: &'a [SourceComment],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Formats a clause header, handling the case where the clause header is suppressed and should not be formatted.
|
||||||
|
///
|
||||||
|
/// Calls the `formatter` to format the content of the `header`, except if the `trailing_colon_comment` is a `fmt: skip` suppression comment.
|
||||||
|
/// Takes care of formatting the `trailing_colon_comment` and adds the `:` at the end of the header.
|
||||||
|
pub(crate) fn clause_header<'a, 'ast, Content>(
|
||||||
|
header: ClauseHeader<'a>,
|
||||||
|
trailing_colon_comment: &'a [SourceComment],
|
||||||
|
formatter: &'a Content,
|
||||||
|
) -> FormatClauseHeader<'a, 'ast>
|
||||||
|
where
|
||||||
|
Content: Format<PyFormatContext<'ast>>,
|
||||||
|
{
|
||||||
|
FormatClauseHeader {
|
||||||
|
header,
|
||||||
|
formatter: Argument::new(formatter),
|
||||||
|
leading_comments: None,
|
||||||
|
trailing_colon_comment,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FormatClauseHeader<'a, '_> {
|
||||||
|
/// Sets the leading comments that precede an alternate branch.
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn with_leading_comments<N>(
|
||||||
|
mut self,
|
||||||
|
comments: &'a [SourceComment],
|
||||||
|
last_node: Option<N>,
|
||||||
|
) -> Self
|
||||||
|
where
|
||||||
|
N: Into<AnyNodeRef<'a>>,
|
||||||
|
{
|
||||||
|
self.leading_comments = Some((comments, last_node.map(Into::into)));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ast> Format<PyFormatContext<'ast>> for FormatClauseHeader<'_, 'ast> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<PyFormatContext<'ast>>) -> FormatResult<()> {
|
||||||
|
if let Some((leading_comments, last_node)) = self.leading_comments {
|
||||||
|
leading_alternate_branch_comments(leading_comments, last_node).fmt(f)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if SuppressionKind::has_skip_comment(self.trailing_colon_comment, f.context().source()) {
|
||||||
|
write_suppressed_clause_header(self.header, f)?;
|
||||||
|
} else {
|
||||||
|
f.write_fmt(Arguments::from(&self.formatter))?;
|
||||||
|
text(":").fmt(f)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
trailing_comments(self.trailing_colon_comment).fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the range of `keyword` starting the search at `start_position`. Expects only comments and `(` between
|
||||||
|
/// the `start_position` and the `keyword` token.
|
||||||
|
fn find_keyword(
|
||||||
|
start_position: TextSize,
|
||||||
|
keyword: SimpleTokenKind,
|
||||||
|
source: &str,
|
||||||
|
) -> FormatResult<TextRange> {
|
||||||
|
let mut tokenizer = SimpleTokenizer::starts_at(start_position, source).skip_trivia();
|
||||||
|
|
||||||
|
match tokenizer.next() {
|
||||||
|
Some(token) if token.kind() == keyword => Ok(token.range()),
|
||||||
|
Some(other) => {
|
||||||
|
debug_assert!(
|
||||||
|
false,
|
||||||
|
"Expected the keyword token {keyword:?} but found the token {other:?} instead."
|
||||||
|
);
|
||||||
|
Err(FormatError::syntax_error(
|
||||||
|
"Expected the keyword token but found another token instead.",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
debug_assert!(
|
||||||
|
false,
|
||||||
|
"Expected the keyword token {keyword:?} but reached the end of the source instead."
|
||||||
|
);
|
||||||
|
Err(FormatError::syntax_error(
|
||||||
|
"Expected the case header keyword token but reached the end of the source instead.",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the range of the `:` ending the clause header or `Err` if the colon can't be found.
|
||||||
|
fn colon_range(after_keyword_or_condition: TextSize, source: &str) -> FormatResult<TextRange> {
|
||||||
|
let mut tokenizer = SimpleTokenizer::starts_at(after_keyword_or_condition, source)
|
||||||
|
.skip_trivia()
|
||||||
|
.skip_while(|token| token.kind() == SimpleTokenKind::RParen);
|
||||||
|
|
||||||
|
match tokenizer.next() {
|
||||||
|
Some(SimpleToken {
|
||||||
|
kind: SimpleTokenKind::Colon,
|
||||||
|
range,
|
||||||
|
}) => Ok(range),
|
||||||
|
Some(token) => {
|
||||||
|
debug_assert!(false, "Expected the colon marking the end of the case header but found {token:?} instead.");
|
||||||
|
Err(FormatError::syntax_error("Expected colon marking the end of the case header but found another token instead."))
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
debug_assert!(false, "Expected the colon marking the end of the case header but found the end of the range.");
|
||||||
|
Err(FormatError::syntax_error("Expected the colon marking the end of the case header but found the end of the range."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ use ruff_python_ast::Stmt;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
pub(super) mod clause;
|
||||||
pub(crate) mod stmt_ann_assign;
|
pub(crate) mod stmt_ann_assign;
|
||||||
pub(crate) mod stmt_assert;
|
pub(crate) mod stmt_assert;
|
||||||
pub(crate) mod stmt_assign;
|
pub(crate) mod stmt_assign;
|
||||||
|
|
|
@ -5,6 +5,8 @@ use ruff_python_trivia::lines_after_ignoring_trivia;
|
||||||
use crate::comments::{leading_comments, trailing_comments, SourceComment};
|
use crate::comments::{leading_comments, trailing_comments, SourceComment};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::statement::suite::SuiteKind;
|
use crate::statement::suite::SuiteKind;
|
||||||
|
|
||||||
|
use crate::statement::clause::{clause_header, ClauseHeader};
|
||||||
use crate::FormatNodeRule;
|
use crate::FormatNodeRule;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -30,78 +32,81 @@ impl FormatNodeRule<StmtClassDef> for FormatStmtClassDef {
|
||||||
let (leading_definition_comments, trailing_definition_comments) =
|
let (leading_definition_comments, trailing_definition_comments) =
|
||||||
dangling_comments.split_at(trailing_definition_comments_start);
|
dangling_comments.split_at(trailing_definition_comments_start);
|
||||||
|
|
||||||
FormatDecorators {
|
|
||||||
decorators: decorator_list,
|
|
||||||
leading_definition_comments,
|
|
||||||
}
|
|
||||||
.fmt(f)?;
|
|
||||||
|
|
||||||
write!(f, [text("class"), space(), name.format()])?;
|
|
||||||
|
|
||||||
if let Some(type_params) = type_params.as_deref() {
|
|
||||||
write!(f, [type_params.format()])?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(arguments) = arguments.as_deref() {
|
|
||||||
// Drop empty the arguments node entirely (i.e., remove the parentheses) if it is empty,
|
|
||||||
// e.g., given:
|
|
||||||
// ```python
|
|
||||||
// class A():
|
|
||||||
// ...
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// Format as:
|
|
||||||
// ```python
|
|
||||||
// class A:
|
|
||||||
// ...
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// However, preserve any dangling end-of-line comments, e.g., given:
|
|
||||||
// ```python
|
|
||||||
// class A( # comment
|
|
||||||
// ):
|
|
||||||
// ...
|
|
||||||
//
|
|
||||||
// Format as:
|
|
||||||
// ```python
|
|
||||||
// class A: # comment
|
|
||||||
// ...
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// However, the arguments contain any dangling own-line comments, we retain the
|
|
||||||
// parentheses, e.g., given:
|
|
||||||
// ```python
|
|
||||||
// class A( # comment
|
|
||||||
// # comment
|
|
||||||
// ):
|
|
||||||
// ...
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// Format as:
|
|
||||||
// ```python
|
|
||||||
// class A( # comment
|
|
||||||
// # comment
|
|
||||||
// ):
|
|
||||||
// ...
|
|
||||||
// ```
|
|
||||||
if arguments.is_empty()
|
|
||||||
&& comments
|
|
||||||
.dangling_comments(arguments)
|
|
||||||
.iter()
|
|
||||||
.all(|comment| comment.line_position().is_end_of_line())
|
|
||||||
{
|
|
||||||
let dangling = comments.dangling_comments(arguments);
|
|
||||||
write!(f, [trailing_comments(dangling)])?;
|
|
||||||
} else {
|
|
||||||
write!(f, [arguments.format()])?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
text(":"),
|
FormatDecorators {
|
||||||
trailing_comments(trailing_definition_comments),
|
decorators: decorator_list,
|
||||||
|
leading_definition_comments,
|
||||||
|
},
|
||||||
|
clause_header(
|
||||||
|
ClauseHeader::Class(item),
|
||||||
|
trailing_definition_comments,
|
||||||
|
&format_with(|f| {
|
||||||
|
write!(f, [text("class"), space(), name.format()])?;
|
||||||
|
|
||||||
|
if let Some(type_params) = type_params.as_deref() {
|
||||||
|
write!(f, [type_params.format()])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(arguments) = arguments.as_deref() {
|
||||||
|
// Drop empty the arguments node entirely (i.e., remove the parentheses) if it is empty,
|
||||||
|
// e.g., given:
|
||||||
|
// ```python
|
||||||
|
// class A():
|
||||||
|
// ...
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// Format as:
|
||||||
|
// ```python
|
||||||
|
// class A:
|
||||||
|
// ...
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// However, preserve any dangling end-of-line comments, e.g., given:
|
||||||
|
// ```python
|
||||||
|
// class A( # comment
|
||||||
|
// ):
|
||||||
|
// ...
|
||||||
|
//
|
||||||
|
// Format as:
|
||||||
|
// ```python
|
||||||
|
// class A: # comment
|
||||||
|
// ...
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// However, the arguments contain any dangling own-line comments, we retain the
|
||||||
|
// parentheses, e.g., given:
|
||||||
|
// ```python
|
||||||
|
// class A( # comment
|
||||||
|
// # comment
|
||||||
|
// ):
|
||||||
|
// ...
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// Format as:
|
||||||
|
// ```python
|
||||||
|
// class A( # comment
|
||||||
|
// # comment
|
||||||
|
// ):
|
||||||
|
// ...
|
||||||
|
// ```
|
||||||
|
if arguments.is_empty()
|
||||||
|
&& comments
|
||||||
|
.dangling_comments(arguments)
|
||||||
|
.iter()
|
||||||
|
.all(|comment| comment.line_position().is_end_of_line())
|
||||||
|
{
|
||||||
|
let dangling = comments.dangling_comments(arguments);
|
||||||
|
write!(f, [trailing_comments(dangling)])?;
|
||||||
|
} else {
|
||||||
|
write!(f, [arguments.format()])?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}),
|
||||||
|
),
|
||||||
block_indent(&body.format().with_options(SuiteKind::Class))
|
block_indent(&body.format().with_options(SuiteKind::Class))
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use ruff_formatter::{format_args, write};
|
use ruff_formatter::{format_args, write};
|
||||||
use ruff_python_ast::{Expr, Ranged, Stmt, StmtFor};
|
use ruff_python_ast::{Expr, Ranged, Stmt, StmtFor};
|
||||||
|
|
||||||
use crate::comments::{leading_alternate_branch_comments, trailing_comments, SourceComment};
|
use crate::comments::SourceComment;
|
||||||
use crate::expression::expr_tuple::TupleParentheses;
|
use crate::expression::expr_tuple::TupleParentheses;
|
||||||
use crate::expression::maybe_parenthesize_expression;
|
use crate::expression::maybe_parenthesize_expression;
|
||||||
use crate::expression::parentheses::Parenthesize;
|
use crate::expression::parentheses::Parenthesize;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::statement::clause::{clause_header, ClauseHeader, ElseClause};
|
||||||
use crate::FormatNodeRule;
|
use crate::FormatNodeRule;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -49,16 +50,20 @@ impl FormatNodeRule<StmtFor> for FormatStmtFor {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
is_async.then_some(format_args![text("async"), space()]),
|
clause_header(
|
||||||
text("for"),
|
ClauseHeader::For(item),
|
||||||
space(),
|
trailing_condition_comments,
|
||||||
ExprTupleWithoutParentheses(target),
|
&format_args![
|
||||||
space(),
|
is_async.then_some(format_args![text("async"), space()]),
|
||||||
text("in"),
|
text("for"),
|
||||||
space(),
|
space(),
|
||||||
maybe_parenthesize_expression(iter, item, Parenthesize::IfBreaks),
|
ExprTupleWithoutParentheses(target),
|
||||||
text(":"),
|
space(),
|
||||||
trailing_comments(trailing_condition_comments),
|
text("in"),
|
||||||
|
space(),
|
||||||
|
maybe_parenthesize_expression(iter, item, Parenthesize::IfBreaks),
|
||||||
|
],
|
||||||
|
),
|
||||||
block_indent(&body.format())
|
block_indent(&body.format())
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
|
@ -75,9 +80,12 @@ impl FormatNodeRule<StmtFor> for FormatStmtFor {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
leading_alternate_branch_comments(leading, body.last()),
|
clause_header(
|
||||||
text("else:"),
|
ClauseHeader::OrElse(ElseClause::For(item)),
|
||||||
trailing_comments(trailing),
|
trailing,
|
||||||
|
&text("else"),
|
||||||
|
)
|
||||||
|
.with_leading_comments(leading, body.last()),
|
||||||
block_indent(&orelse.format())
|
block_indent(&orelse.format())
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -2,10 +2,11 @@ use ruff_formatter::write;
|
||||||
use ruff_python_ast::{Parameters, Ranged, StmtFunctionDef};
|
use ruff_python_ast::{Parameters, Ranged, StmtFunctionDef};
|
||||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||||
|
|
||||||
use crate::comments::{trailing_comments, SourceComment};
|
use crate::comments::SourceComment;
|
||||||
use crate::expression::maybe_parenthesize_expression;
|
use crate::expression::maybe_parenthesize_expression;
|
||||||
use crate::expression::parentheses::{Parentheses, Parenthesize};
|
use crate::expression::parentheses::{Parentheses, Parenthesize};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::statement::clause::{clause_header, ClauseHeader};
|
||||||
use crate::statement::stmt_class_def::FormatDecorators;
|
use crate::statement::stmt_class_def::FormatDecorators;
|
||||||
use crate::statement::suite::SuiteKind;
|
use crate::statement::suite::SuiteKind;
|
||||||
use crate::FormatNodeRule;
|
use crate::FormatNodeRule;
|
||||||
|
@ -35,105 +36,114 @@ impl FormatNodeRule<StmtFunctionDef> for FormatStmtFunctionDef {
|
||||||
let (leading_definition_comments, trailing_definition_comments) =
|
let (leading_definition_comments, trailing_definition_comments) =
|
||||||
dangling_comments.split_at(trailing_definition_comments_start);
|
dangling_comments.split_at(trailing_definition_comments_start);
|
||||||
|
|
||||||
FormatDecorators {
|
|
||||||
decorators: decorator_list,
|
|
||||||
leading_definition_comments,
|
|
||||||
}
|
|
||||||
.fmt(f)?;
|
|
||||||
|
|
||||||
if *is_async {
|
|
||||||
write!(f, [text("async"), space()])?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(f, [text("def"), space(), name.format()])?;
|
|
||||||
|
|
||||||
if let Some(type_params) = type_params.as_ref() {
|
|
||||||
write!(f, [type_params.format()])?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let format_inner = format_with(|f: &mut PyFormatter| {
|
|
||||||
write!(f, [parameters.format()])?;
|
|
||||||
|
|
||||||
if let Some(return_annotation) = returns.as_ref() {
|
|
||||||
write!(f, [space(), text("->"), space()])?;
|
|
||||||
|
|
||||||
if return_annotation.is_tuple_expr() {
|
|
||||||
let parentheses = if comments.has_leading_comments(return_annotation.as_ref()) {
|
|
||||||
Parentheses::Always
|
|
||||||
} else {
|
|
||||||
Parentheses::Never
|
|
||||||
};
|
|
||||||
write!(f, [return_annotation.format().with_options(parentheses)])?;
|
|
||||||
} else if comments.has_trailing_comments(return_annotation.as_ref()) {
|
|
||||||
// Intentionally parenthesize any return annotations with trailing comments.
|
|
||||||
// This avoids an instability in cases like:
|
|
||||||
// ```python
|
|
||||||
// def double(
|
|
||||||
// a: int
|
|
||||||
// ) -> (
|
|
||||||
// int # Hello
|
|
||||||
// ):
|
|
||||||
// pass
|
|
||||||
// ```
|
|
||||||
// If we allow this to break, it will be formatted as follows:
|
|
||||||
// ```python
|
|
||||||
// def double(
|
|
||||||
// a: int
|
|
||||||
// ) -> int: # Hello
|
|
||||||
// pass
|
|
||||||
// ```
|
|
||||||
// On subsequent formats, the `# Hello` will be interpreted as a dangling
|
|
||||||
// comment on a function, yielding:
|
|
||||||
// ```python
|
|
||||||
// def double(a: int) -> int: # Hello
|
|
||||||
// pass
|
|
||||||
// ```
|
|
||||||
// Ideally, we'd reach that final formatting in a single pass, but doing so
|
|
||||||
// requires that the parent be aware of how the child is formatted, which
|
|
||||||
// is challenging. As a compromise, we break those expressions to avoid an
|
|
||||||
// instability.
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
[return_annotation.format().with_options(Parentheses::Always)]
|
|
||||||
)?;
|
|
||||||
} else {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
[maybe_parenthesize_expression(
|
|
||||||
return_annotation,
|
|
||||||
item,
|
|
||||||
if empty_parameters(parameters, f.context().source()) {
|
|
||||||
// If the parameters are empty, add parentheses if the return annotation
|
|
||||||
// breaks at all.
|
|
||||||
Parenthesize::IfBreaksOrIfRequired
|
|
||||||
} else {
|
|
||||||
// Otherwise, use our normal rules for parentheses, which allows us to break
|
|
||||||
// like:
|
|
||||||
// ```python
|
|
||||||
// def f(
|
|
||||||
// x,
|
|
||||||
// ) -> Tuple[
|
|
||||||
// int,
|
|
||||||
// int,
|
|
||||||
// ]:
|
|
||||||
// ...
|
|
||||||
// ```
|
|
||||||
Parenthesize::IfBreaks
|
|
||||||
},
|
|
||||||
)]
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
write!(f, [group(&format_inner)])?;
|
|
||||||
|
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
text(":"),
|
FormatDecorators {
|
||||||
trailing_comments(trailing_definition_comments),
|
decorators: decorator_list,
|
||||||
|
leading_definition_comments,
|
||||||
|
},
|
||||||
|
clause_header(
|
||||||
|
ClauseHeader::Function(item),
|
||||||
|
trailing_definition_comments,
|
||||||
|
&format_with(|f| {
|
||||||
|
if *is_async {
|
||||||
|
write!(f, [text("async"), space()])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, [text("def"), space(), name.format()])?;
|
||||||
|
|
||||||
|
if let Some(type_params) = type_params.as_ref() {
|
||||||
|
write!(f, [type_params.format()])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let format_inner = format_with(|f: &mut PyFormatter| {
|
||||||
|
write!(f, [parameters.format()])?;
|
||||||
|
|
||||||
|
if let Some(return_annotation) = returns.as_ref() {
|
||||||
|
write!(f, [space(), text("->"), space()])?;
|
||||||
|
|
||||||
|
if return_annotation.is_tuple_expr() {
|
||||||
|
let parentheses = if comments
|
||||||
|
.has_leading_comments(return_annotation.as_ref())
|
||||||
|
{
|
||||||
|
Parentheses::Always
|
||||||
|
} else {
|
||||||
|
Parentheses::Never
|
||||||
|
};
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[return_annotation.format().with_options(parentheses)]
|
||||||
|
)?;
|
||||||
|
} else if comments.has_trailing_comments(return_annotation.as_ref())
|
||||||
|
{
|
||||||
|
// Intentionally parenthesize any return annotations with trailing comments.
|
||||||
|
// This avoids an instability in cases like:
|
||||||
|
// ```python
|
||||||
|
// def double(
|
||||||
|
// a: int
|
||||||
|
// ) -> (
|
||||||
|
// int # Hello
|
||||||
|
// ):
|
||||||
|
// pass
|
||||||
|
// ```
|
||||||
|
// If we allow this to break, it will be formatted as follows:
|
||||||
|
// ```python
|
||||||
|
// def double(
|
||||||
|
// a: int
|
||||||
|
// ) -> int: # Hello
|
||||||
|
// pass
|
||||||
|
// ```
|
||||||
|
// On subsequent formats, the `# Hello` will be interpreted as a dangling
|
||||||
|
// comment on a function, yielding:
|
||||||
|
// ```python
|
||||||
|
// def double(a: int) -> int: # Hello
|
||||||
|
// pass
|
||||||
|
// ```
|
||||||
|
// Ideally, we'd reach that final formatting in a single pass, but doing so
|
||||||
|
// requires that the parent be aware of how the child is formatted, which
|
||||||
|
// is challenging. As a compromise, we break those expressions to avoid an
|
||||||
|
// instability.
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[return_annotation
|
||||||
|
.format()
|
||||||
|
.with_options(Parentheses::Always)]
|
||||||
|
)?;
|
||||||
|
} else {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[maybe_parenthesize_expression(
|
||||||
|
return_annotation,
|
||||||
|
item,
|
||||||
|
if empty_parameters(parameters, f.context().source()) {
|
||||||
|
// If the parameters are empty, add parentheses if the return annotation
|
||||||
|
// breaks at all.
|
||||||
|
Parenthesize::IfBreaksOrIfRequired
|
||||||
|
} else {
|
||||||
|
// Otherwise, use our normal rules for parentheses, which allows us to break
|
||||||
|
// like:
|
||||||
|
// ```python
|
||||||
|
// def f(
|
||||||
|
// x,
|
||||||
|
// ) -> Tuple[
|
||||||
|
// int,
|
||||||
|
// int,
|
||||||
|
// ]:
|
||||||
|
// ...
|
||||||
|
// ```
|
||||||
|
Parenthesize::IfBreaks
|
||||||
|
},
|
||||||
|
)]
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
group(&format_inner).fmt(f)
|
||||||
|
}),
|
||||||
|
),
|
||||||
block_indent(&body.format().with_options(SuiteKind::Function))
|
block_indent(&body.format().with_options(SuiteKind::Function))
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
use crate::comments::{leading_alternate_branch_comments, trailing_comments, SourceComment};
|
use ruff_formatter::{format_args, write};
|
||||||
|
use ruff_python_ast::node::AnyNodeRef;
|
||||||
|
use ruff_python_ast::{ElifElseClause, StmtIf};
|
||||||
|
|
||||||
|
use crate::comments::SourceComment;
|
||||||
use crate::expression::maybe_parenthesize_expression;
|
use crate::expression::maybe_parenthesize_expression;
|
||||||
use crate::expression::parentheses::Parenthesize;
|
use crate::expression::parentheses::Parenthesize;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::statement::clause::{clause_header, ClauseHeader};
|
||||||
use crate::FormatNodeRule;
|
use crate::FormatNodeRule;
|
||||||
use ruff_formatter::write;
|
|
||||||
use ruff_python_ast::node::AnyNodeRef;
|
|
||||||
use ruff_python_ast::{ElifElseClause, StmtIf};
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FormatStmtIf;
|
pub struct FormatStmtIf;
|
||||||
|
@ -25,11 +27,15 @@ impl FormatNodeRule<StmtIf> for FormatStmtIf {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
text("if"),
|
clause_header(
|
||||||
space(),
|
ClauseHeader::If(item),
|
||||||
maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks),
|
trailing_colon_comment,
|
||||||
text(":"),
|
&format_args![
|
||||||
trailing_comments(trailing_colon_comment),
|
text("if"),
|
||||||
|
space(),
|
||||||
|
maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks),
|
||||||
|
],
|
||||||
|
),
|
||||||
block_indent(&body.format())
|
block_indent(&body.format())
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
|
@ -70,26 +76,28 @@ pub(crate) fn format_elif_else_clause(
|
||||||
let trailing_colon_comment = comments.dangling_comments(item);
|
let trailing_colon_comment = comments.dangling_comments(item);
|
||||||
let leading_comments = comments.leading_comments(item);
|
let leading_comments = comments.leading_comments(item);
|
||||||
|
|
||||||
leading_alternate_branch_comments(leading_comments, last_node).fmt(f)?;
|
|
||||||
|
|
||||||
if let Some(test) = test {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
[
|
|
||||||
text("elif"),
|
|
||||||
space(),
|
|
||||||
maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks),
|
|
||||||
]
|
|
||||||
)?;
|
|
||||||
} else {
|
|
||||||
text("else").fmt(f)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
text(":"),
|
clause_header(
|
||||||
trailing_comments(trailing_colon_comment),
|
ClauseHeader::ElifElse(item),
|
||||||
|
trailing_colon_comment,
|
||||||
|
&format_with(|f| {
|
||||||
|
if let Some(test) = test {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[
|
||||||
|
text("elif"),
|
||||||
|
space(),
|
||||||
|
maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
text("else").fmt(f)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.with_leading_comments(leading_comments, last_node),
|
||||||
block_indent(&body.format())
|
block_indent(&body.format())
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use ruff_formatter::{format_args, write};
|
use ruff_formatter::{format_args, write};
|
||||||
use ruff_python_ast::StmtMatch;
|
use ruff_python_ast::StmtMatch;
|
||||||
|
|
||||||
use crate::comments::{leading_alternate_branch_comments, trailing_comments, SourceComment};
|
use crate::comments::{leading_alternate_branch_comments, SourceComment};
|
||||||
use crate::context::{NodeLevel, WithNodeLevel};
|
use crate::context::{NodeLevel, WithNodeLevel};
|
||||||
use crate::expression::maybe_parenthesize_expression;
|
use crate::expression::maybe_parenthesize_expression;
|
||||||
use crate::expression::parentheses::Parenthesize;
|
use crate::expression::parentheses::Parenthesize;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::statement::clause::{clause_header, ClauseHeader};
|
||||||
use crate::FormatNodeRule;
|
use crate::FormatNodeRule;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -25,16 +26,16 @@ impl FormatNodeRule<StmtMatch> for FormatStmtMatch {
|
||||||
// There can be at most one dangling comment after the colon in a match statement.
|
// There can be at most one dangling comment after the colon in a match statement.
|
||||||
debug_assert!(dangling_item_comments.len() <= 1);
|
debug_assert!(dangling_item_comments.len() <= 1);
|
||||||
|
|
||||||
write!(
|
clause_header(
|
||||||
f,
|
ClauseHeader::Match(item),
|
||||||
[
|
dangling_item_comments,
|
||||||
|
&format_args![
|
||||||
text("match"),
|
text("match"),
|
||||||
space(),
|
space(),
|
||||||
maybe_parenthesize_expression(subject, item, Parenthesize::IfBreaks),
|
maybe_parenthesize_expression(subject, item, Parenthesize::IfBreaks),
|
||||||
text(":"),
|
],
|
||||||
trailing_comments(dangling_item_comments)
|
)
|
||||||
]
|
.fmt(f)?;
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut cases_iter = cases.iter();
|
let mut cases_iter = cases.iter();
|
||||||
let Some(first) = cases_iter.next() else {
|
let Some(first) = cases_iter.next() else {
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use ruff_formatter::{write, FormatRuleWithOptions};
|
use ruff_formatter::{write, FormatRuleWithOptions};
|
||||||
use ruff_python_ast::{ExceptHandler, Ranged, StmtTry, Suite};
|
use ruff_python_ast::{ExceptHandler, Ranged, StmtTry};
|
||||||
|
|
||||||
use crate::comments;
|
use crate::comments;
|
||||||
|
use crate::comments::leading_alternate_branch_comments;
|
||||||
use crate::comments::SourceComment;
|
use crate::comments::SourceComment;
|
||||||
use crate::comments::{leading_alternate_branch_comments, trailing_comments};
|
|
||||||
use crate::other::except_handler_except_handler::ExceptHandlerKind;
|
use crate::other::except_handler_except_handler::ExceptHandlerKind;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::statement::clause::{clause_header, ClauseHeader, ElseClause};
|
||||||
use crate::statement::{FormatRefWithRule, Stmt};
|
use crate::statement::{FormatRefWithRule, Stmt};
|
||||||
use crate::{FormatNodeRule, PyFormatter};
|
use crate::{FormatNodeRule, PyFormatter};
|
||||||
|
|
||||||
|
@ -55,8 +56,8 @@ impl FormatNodeRule<StmtTry> for FormatStmtTry {
|
||||||
let StmtTry {
|
let StmtTry {
|
||||||
body,
|
body,
|
||||||
handlers,
|
handlers,
|
||||||
orelse,
|
orelse: _,
|
||||||
finalbody,
|
finalbody: _,
|
||||||
is_star,
|
is_star,
|
||||||
range: _,
|
range: _,
|
||||||
} = item;
|
} = item;
|
||||||
|
@ -64,7 +65,7 @@ impl FormatNodeRule<StmtTry> for FormatStmtTry {
|
||||||
let comments_info = f.context().comments().clone();
|
let comments_info = f.context().comments().clone();
|
||||||
let mut dangling_comments = comments_info.dangling_comments(item);
|
let mut dangling_comments = comments_info.dangling_comments(item);
|
||||||
|
|
||||||
(_, dangling_comments) = format_case("try", body, None, dangling_comments, f)?;
|
(_, dangling_comments) = format_case(item, CaseKind::Try, None, dangling_comments, f)?;
|
||||||
let mut previous_node = body.last();
|
let mut previous_node = body.last();
|
||||||
|
|
||||||
for handler in handlers {
|
for handler in handlers {
|
||||||
|
@ -86,9 +87,9 @@ impl FormatNodeRule<StmtTry> for FormatStmtTry {
|
||||||
}
|
}
|
||||||
|
|
||||||
(previous_node, dangling_comments) =
|
(previous_node, dangling_comments) =
|
||||||
format_case("else", orelse, previous_node, dangling_comments, f)?;
|
format_case(item, CaseKind::Else, previous_node, dangling_comments, f)?;
|
||||||
|
|
||||||
format_case("finally", finalbody, previous_node, dangling_comments, f)?;
|
format_case(item, CaseKind::Finally, previous_node, dangling_comments, f)?;
|
||||||
|
|
||||||
write!(f, [comments::dangling_comments(dangling_comments)])
|
write!(f, [comments::dangling_comments(dangling_comments)])
|
||||||
}
|
}
|
||||||
|
@ -104,25 +105,39 @@ impl FormatNodeRule<StmtTry> for FormatStmtTry {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_case<'a>(
|
fn format_case<'a>(
|
||||||
name: &'static str,
|
try_statement: &StmtTry,
|
||||||
body: &Suite,
|
kind: CaseKind,
|
||||||
previous_node: Option<&Stmt>,
|
previous_node: Option<&Stmt>,
|
||||||
dangling_comments: &'a [SourceComment],
|
dangling_comments: &'a [SourceComment],
|
||||||
f: &mut PyFormatter,
|
f: &mut PyFormatter,
|
||||||
) -> FormatResult<(Option<&'a Stmt>, &'a [SourceComment])> {
|
) -> FormatResult<(Option<&'a Stmt>, &'a [SourceComment])> {
|
||||||
|
let body = match kind {
|
||||||
|
CaseKind::Try => &try_statement.body,
|
||||||
|
CaseKind::Else => &try_statement.orelse,
|
||||||
|
CaseKind::Finally => &try_statement.finalbody,
|
||||||
|
};
|
||||||
|
|
||||||
Ok(if let Some(last) = body.last() {
|
Ok(if let Some(last) = body.last() {
|
||||||
let case_comments_start =
|
let case_comments_start =
|
||||||
dangling_comments.partition_point(|comment| comment.slice().end() <= last.end());
|
dangling_comments.partition_point(|comment| comment.slice().end() <= last.end());
|
||||||
let (case_comments, rest) = dangling_comments.split_at(case_comments_start);
|
let (case_comments, rest) = dangling_comments.split_at(case_comments_start);
|
||||||
let partition_point =
|
let partition_point =
|
||||||
case_comments.partition_point(|comment| comment.line_position().is_own_line());
|
case_comments.partition_point(|comment| comment.line_position().is_own_line());
|
||||||
|
|
||||||
|
let (leading_case_comments, trailing_case_comments) =
|
||||||
|
case_comments.split_at(partition_point);
|
||||||
|
|
||||||
|
let header = match kind {
|
||||||
|
CaseKind::Try => ClauseHeader::Try(try_statement),
|
||||||
|
CaseKind::Else => ClauseHeader::OrElse(ElseClause::Try(try_statement)),
|
||||||
|
CaseKind::Finally => ClauseHeader::TryFinally(try_statement),
|
||||||
|
};
|
||||||
|
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
leading_alternate_branch_comments(&case_comments[..partition_point], previous_node),
|
clause_header(header, trailing_case_comments, &text(kind.keyword()))
|
||||||
text(name),
|
.with_leading_comments(leading_case_comments, previous_node),
|
||||||
text(":"),
|
|
||||||
trailing_comments(&case_comments[partition_point..]),
|
|
||||||
block_indent(&body.format())
|
block_indent(&body.format())
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
|
@ -131,3 +146,20 @@ fn format_case<'a>(
|
||||||
(None, dangling_comments)
|
(None, dangling_comments)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
enum CaseKind {
|
||||||
|
Try,
|
||||||
|
Else,
|
||||||
|
Finally,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CaseKind {
|
||||||
|
fn keyword(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
CaseKind::Try => "try",
|
||||||
|
CaseKind::Else => "else",
|
||||||
|
CaseKind::Finally => "finally",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use ruff_formatter::write;
|
use ruff_formatter::{format_args, write};
|
||||||
use ruff_python_ast::node::AstNode;
|
use ruff_python_ast::node::AstNode;
|
||||||
use ruff_python_ast::{Ranged, Stmt, StmtWhile};
|
use ruff_python_ast::{Ranged, Stmt, StmtWhile};
|
||||||
|
|
||||||
use crate::comments::{leading_alternate_branch_comments, trailing_comments, SourceComment};
|
use crate::comments::SourceComment;
|
||||||
use crate::expression::maybe_parenthesize_expression;
|
use crate::expression::maybe_parenthesize_expression;
|
||||||
use crate::expression::parentheses::Parenthesize;
|
use crate::expression::parentheses::Parenthesize;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::statement::clause::{clause_header, ClauseHeader, ElseClause};
|
||||||
use crate::FormatNodeRule;
|
use crate::FormatNodeRule;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -33,11 +34,15 @@ impl FormatNodeRule<StmtWhile> for FormatStmtWhile {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
text("while"),
|
clause_header(
|
||||||
space(),
|
ClauseHeader::While(item),
|
||||||
maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks),
|
trailing_condition_comments,
|
||||||
text(":"),
|
&format_args![
|
||||||
trailing_comments(trailing_condition_comments),
|
text("while"),
|
||||||
|
space(),
|
||||||
|
maybe_parenthesize_expression(test, item, Parenthesize::IfBreaks),
|
||||||
|
]
|
||||||
|
),
|
||||||
block_indent(&body.format())
|
block_indent(&body.format())
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
|
@ -52,9 +57,12 @@ impl FormatNodeRule<StmtWhile> for FormatStmtWhile {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
leading_alternate_branch_comments(leading, body.last()),
|
clause_header(
|
||||||
text("else:"),
|
ClauseHeader::OrElse(ElseClause::While(item)),
|
||||||
trailing_comments(trailing),
|
trailing,
|
||||||
|
&text("else")
|
||||||
|
)
|
||||||
|
.with_leading_comments(leading, body.last()),
|
||||||
block_indent(&orelse.format())
|
block_indent(&orelse.format())
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -4,11 +4,12 @@ use ruff_python_ast::{Ranged, StmtWith};
|
||||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||||
use ruff_text_size::TextRange;
|
use ruff_text_size::TextRange;
|
||||||
|
|
||||||
use crate::comments::{trailing_comments, SourceComment};
|
use crate::comments::SourceComment;
|
||||||
use crate::expression::parentheses::{
|
use crate::expression::parentheses::{
|
||||||
in_parentheses_only_soft_line_break_or_space, optional_parentheses, parenthesized,
|
in_parentheses_only_soft_line_break_or_space, optional_parentheses, parenthesized,
|
||||||
};
|
};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::statement::clause::{clause_header, ClauseHeader};
|
||||||
use crate::FormatNodeRule;
|
use crate::FormatNodeRule;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -16,16 +17,6 @@ pub struct FormatStmtWith;
|
||||||
|
|
||||||
impl FormatNodeRule<StmtWith> for FormatStmtWith {
|
impl FormatNodeRule<StmtWith> for FormatStmtWith {
|
||||||
fn fmt_fields(&self, item: &StmtWith, f: &mut PyFormatter) -> FormatResult<()> {
|
fn fmt_fields(&self, item: &StmtWith, f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
[
|
|
||||||
item.is_async
|
|
||||||
.then_some(format_args![text("async"), space()]),
|
|
||||||
text("with"),
|
|
||||||
space()
|
|
||||||
]
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// The `with` statement can have one dangling comment on the open parenthesis, like:
|
// The `with` statement can have one dangling comment on the open parenthesis, like:
|
||||||
// ```python
|
// ```python
|
||||||
// with ( # comment
|
// with ( # comment
|
||||||
|
@ -48,41 +39,57 @@ impl FormatNodeRule<StmtWith> for FormatStmtWith {
|
||||||
});
|
});
|
||||||
let (parenthesized_comments, colon_comments) = dangling_comments.split_at(partition_point);
|
let (parenthesized_comments, colon_comments) = dangling_comments.split_at(partition_point);
|
||||||
|
|
||||||
if !parenthesized_comments.is_empty() {
|
|
||||||
let joined = format_with(|f: &mut PyFormatter| {
|
|
||||||
f.join_comma_separated(item.body.first().unwrap().start())
|
|
||||||
.nodes(&item.items)
|
|
||||||
.finish()
|
|
||||||
});
|
|
||||||
|
|
||||||
parenthesized("(", &joined, ")")
|
|
||||||
.with_dangling_comments(parenthesized_comments)
|
|
||||||
.fmt(f)?;
|
|
||||||
} else if are_with_items_parenthesized(item, f.context())? {
|
|
||||||
optional_parentheses(&format_with(|f| {
|
|
||||||
let mut joiner = f.join_comma_separated(item.body.first().unwrap().start());
|
|
||||||
|
|
||||||
for item in &item.items {
|
|
||||||
joiner.entry_with_line_separator(
|
|
||||||
item,
|
|
||||||
&item.format(),
|
|
||||||
in_parentheses_only_soft_line_break_or_space(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
joiner.finish()
|
|
||||||
}))
|
|
||||||
.fmt(f)?;
|
|
||||||
} else {
|
|
||||||
f.join_with(format_args![text(","), space()])
|
|
||||||
.entries(item.items.iter().formatted())
|
|
||||||
.finish()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
text(":"),
|
clause_header(
|
||||||
trailing_comments(colon_comments),
|
ClauseHeader::With(item),
|
||||||
|
colon_comments,
|
||||||
|
&format_with(|f| {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[
|
||||||
|
item.is_async
|
||||||
|
.then_some(format_args![text("async"), space()]),
|
||||||
|
text("with"),
|
||||||
|
space()
|
||||||
|
]
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if !parenthesized_comments.is_empty() {
|
||||||
|
let joined = format_with(|f: &mut PyFormatter| {
|
||||||
|
f.join_comma_separated(item.body.first().unwrap().start())
|
||||||
|
.nodes(&item.items)
|
||||||
|
.finish()
|
||||||
|
});
|
||||||
|
|
||||||
|
parenthesized("(", &joined, ")")
|
||||||
|
.with_dangling_comments(parenthesized_comments)
|
||||||
|
.fmt(f)?;
|
||||||
|
} else if are_with_items_parenthesized(item, f.context())? {
|
||||||
|
optional_parentheses(&format_with(|f| {
|
||||||
|
let mut joiner =
|
||||||
|
f.join_comma_separated(item.body.first().unwrap().start());
|
||||||
|
|
||||||
|
for item in &item.items {
|
||||||
|
joiner.entry_with_line_separator(
|
||||||
|
item,
|
||||||
|
&item.format(),
|
||||||
|
in_parentheses_only_soft_line_break_or_space(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
joiner.finish()
|
||||||
|
}))
|
||||||
|
.fmt(f)?;
|
||||||
|
} else {
|
||||||
|
f.join_with(format_args![text(","), space()])
|
||||||
|
.entries(item.items.iter().formatted())
|
||||||
|
.finish()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
),
|
||||||
block_indent(&item.body.format())
|
block_indent(&item.body.format())
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
@ -15,6 +15,7 @@ use ruff_text_size::{TextRange, TextSize};
|
||||||
use crate::comments::format::{empty_lines, format_comment};
|
use crate::comments::format::{empty_lines, format_comment};
|
||||||
use crate::comments::{leading_comments, trailing_comments, SourceComment};
|
use crate::comments::{leading_comments, trailing_comments, SourceComment};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use crate::statement::clause::ClauseHeader;
|
||||||
use crate::statement::suite::SuiteChildStatement;
|
use crate::statement::suite::SuiteChildStatement;
|
||||||
|
|
||||||
/// Disables formatting for all statements between the `first_suppressed` that has a leading `fmt: off` comment
|
/// Disables formatting for all statements between the `first_suppressed` that has a leading `fmt: off` comment
|
||||||
|
@ -930,3 +931,28 @@ impl Format<PyFormatContext<'_>> for FormatSuppressedNode<'_> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
pub(crate) fn write_suppressed_clause_header(
|
||||||
|
header: ClauseHeader,
|
||||||
|
f: &mut PyFormatter,
|
||||||
|
) -> FormatResult<()> {
|
||||||
|
// Write the outer comments and format the node as verbatim
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[verbatim_text(
|
||||||
|
header.range(f.context().source())?,
|
||||||
|
ContainsNewlines::Detect
|
||||||
|
),]
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let comments = f.context().comments();
|
||||||
|
header.visit(&mut |child| {
|
||||||
|
for comment in comments.leading_trailing_comments(child) {
|
||||||
|
comment.mark_formatted();
|
||||||
|
}
|
||||||
|
comments.mark_verbatim_node_comments_formatted(child);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
@ -1,292 +0,0 @@
|
||||||
---
|
|
||||||
source: crates/ruff_python_formatter/tests/fixtures.rs
|
|
||||||
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/fmtskip8.py
|
|
||||||
---
|
|
||||||
## Input
|
|
||||||
|
|
||||||
```py
|
|
||||||
# Make sure a leading comment is not removed.
|
|
||||||
def some_func( unformatted, args ): # fmt: skip
|
|
||||||
print("I am some_func")
|
|
||||||
return 0
|
|
||||||
# Make sure this comment is not removed.
|
|
||||||
|
|
||||||
|
|
||||||
# Make sure a leading comment is not removed.
|
|
||||||
async def some_async_func( unformatted, args): # fmt: skip
|
|
||||||
print("I am some_async_func")
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
|
|
||||||
|
|
||||||
# Make sure a leading comment is not removed.
|
|
||||||
class SomeClass( Unformatted, SuperClasses ): # fmt: skip
|
|
||||||
def some_method( self, unformatted, args ): # fmt: skip
|
|
||||||
print("I am some_method")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
async def some_async_method( self, unformatted, args ): # fmt: skip
|
|
||||||
print("I am some_async_method")
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
|
|
||||||
|
|
||||||
# Make sure a leading comment is not removed.
|
|
||||||
if unformatted_call( args ): # fmt: skip
|
|
||||||
print("First branch")
|
|
||||||
# Make sure this is not removed.
|
|
||||||
elif another_unformatted_call( args ): # fmt: skip
|
|
||||||
print("Second branch")
|
|
||||||
else : # fmt: skip
|
|
||||||
print("Last branch")
|
|
||||||
|
|
||||||
|
|
||||||
while some_condition( unformatted, args ): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
|
|
||||||
|
|
||||||
for i in some_iter( unformatted, args ): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
|
|
||||||
|
|
||||||
async def test_async_for():
|
|
||||||
async for i in some_async_iter( unformatted, args ): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
|
|
||||||
|
|
||||||
try : # fmt: skip
|
|
||||||
some_call()
|
|
||||||
except UnformattedError as ex: # fmt: skip
|
|
||||||
handle_exception()
|
|
||||||
finally : # fmt: skip
|
|
||||||
finally_call()
|
|
||||||
|
|
||||||
|
|
||||||
with give_me_context( unformatted, args ): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
|
|
||||||
|
|
||||||
async def test_async_with():
|
|
||||||
async with give_me_async_context( unformatted, args ): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Black Differences
|
|
||||||
|
|
||||||
```diff
|
|
||||||
--- Black
|
|
||||||
+++ Ruff
|
|
||||||
@@ -1,62 +1,62 @@
|
|
||||||
# Make sure a leading comment is not removed.
|
|
||||||
-def some_func( unformatted, args ): # fmt: skip
|
|
||||||
+def some_func(unformatted, args): # fmt: skip
|
|
||||||
print("I am some_func")
|
|
||||||
return 0
|
|
||||||
# Make sure this comment is not removed.
|
|
||||||
|
|
||||||
|
|
||||||
# Make sure a leading comment is not removed.
|
|
||||||
-async def some_async_func( unformatted, args): # fmt: skip
|
|
||||||
+async def some_async_func(unformatted, args): # fmt: skip
|
|
||||||
print("I am some_async_func")
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
|
|
||||||
|
|
||||||
# Make sure a leading comment is not removed.
|
|
||||||
-class SomeClass( Unformatted, SuperClasses ): # fmt: skip
|
|
||||||
- def some_method( self, unformatted, args ): # fmt: skip
|
|
||||||
+class SomeClass(Unformatted, SuperClasses): # fmt: skip
|
|
||||||
+ def some_method(self, unformatted, args): # fmt: skip
|
|
||||||
print("I am some_method")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
- async def some_async_method( self, unformatted, args ): # fmt: skip
|
|
||||||
+ async def some_async_method(self, unformatted, args): # fmt: skip
|
|
||||||
print("I am some_async_method")
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
|
|
||||||
|
|
||||||
# Make sure a leading comment is not removed.
|
|
||||||
-if unformatted_call( args ): # fmt: skip
|
|
||||||
+if unformatted_call(args): # fmt: skip
|
|
||||||
print("First branch")
|
|
||||||
# Make sure this is not removed.
|
|
||||||
-elif another_unformatted_call( args ): # fmt: skip
|
|
||||||
+elif another_unformatted_call(args): # fmt: skip
|
|
||||||
print("Second branch")
|
|
||||||
-else : # fmt: skip
|
|
||||||
+else: # fmt: skip
|
|
||||||
print("Last branch")
|
|
||||||
|
|
||||||
|
|
||||||
-while some_condition( unformatted, args ): # fmt: skip
|
|
||||||
+while some_condition(unformatted, args): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
|
|
||||||
|
|
||||||
-for i in some_iter( unformatted, args ): # fmt: skip
|
|
||||||
+for i in some_iter(unformatted, args): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
|
|
||||||
|
|
||||||
async def test_async_for():
|
|
||||||
- async for i in some_async_iter( unformatted, args ): # fmt: skip
|
|
||||||
+ async for i in some_async_iter(unformatted, args): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
|
|
||||||
|
|
||||||
-try : # fmt: skip
|
|
||||||
+try: # fmt: skip
|
|
||||||
some_call()
|
|
||||||
-except UnformattedError as ex: # fmt: skip
|
|
||||||
+except UnformattedError as ex: # fmt: skip
|
|
||||||
handle_exception()
|
|
||||||
-finally : # fmt: skip
|
|
||||||
+finally: # fmt: skip
|
|
||||||
finally_call()
|
|
||||||
|
|
||||||
|
|
||||||
-with give_me_context( unformatted, args ): # fmt: skip
|
|
||||||
+with give_me_context(unformatted, args): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
|
|
||||||
|
|
||||||
async def test_async_with():
|
|
||||||
- async with give_me_async_context( unformatted, args ): # fmt: skip
|
|
||||||
+ async with give_me_async_context(unformatted, args): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Ruff Output
|
|
||||||
|
|
||||||
```py
|
|
||||||
# Make sure a leading comment is not removed.
|
|
||||||
def some_func(unformatted, args): # fmt: skip
|
|
||||||
print("I am some_func")
|
|
||||||
return 0
|
|
||||||
# Make sure this comment is not removed.
|
|
||||||
|
|
||||||
|
|
||||||
# Make sure a leading comment is not removed.
|
|
||||||
async def some_async_func(unformatted, args): # fmt: skip
|
|
||||||
print("I am some_async_func")
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
|
|
||||||
|
|
||||||
# Make sure a leading comment is not removed.
|
|
||||||
class SomeClass(Unformatted, SuperClasses): # fmt: skip
|
|
||||||
def some_method(self, unformatted, args): # fmt: skip
|
|
||||||
print("I am some_method")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
async def some_async_method(self, unformatted, args): # fmt: skip
|
|
||||||
print("I am some_async_method")
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
|
|
||||||
|
|
||||||
# Make sure a leading comment is not removed.
|
|
||||||
if unformatted_call(args): # fmt: skip
|
|
||||||
print("First branch")
|
|
||||||
# Make sure this is not removed.
|
|
||||||
elif another_unformatted_call(args): # fmt: skip
|
|
||||||
print("Second branch")
|
|
||||||
else: # fmt: skip
|
|
||||||
print("Last branch")
|
|
||||||
|
|
||||||
|
|
||||||
while some_condition(unformatted, args): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
|
|
||||||
|
|
||||||
for i in some_iter(unformatted, args): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
|
|
||||||
|
|
||||||
async def test_async_for():
|
|
||||||
async for i in some_async_iter(unformatted, args): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
|
|
||||||
|
|
||||||
try: # fmt: skip
|
|
||||||
some_call()
|
|
||||||
except UnformattedError as ex: # fmt: skip
|
|
||||||
handle_exception()
|
|
||||||
finally: # fmt: skip
|
|
||||||
finally_call()
|
|
||||||
|
|
||||||
|
|
||||||
with give_me_context(unformatted, args): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
|
|
||||||
|
|
||||||
async def test_async_with():
|
|
||||||
async with give_me_async_context(unformatted, args): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Black Output
|
|
||||||
|
|
||||||
```py
|
|
||||||
# Make sure a leading comment is not removed.
|
|
||||||
def some_func( unformatted, args ): # fmt: skip
|
|
||||||
print("I am some_func")
|
|
||||||
return 0
|
|
||||||
# Make sure this comment is not removed.
|
|
||||||
|
|
||||||
|
|
||||||
# Make sure a leading comment is not removed.
|
|
||||||
async def some_async_func( unformatted, args): # fmt: skip
|
|
||||||
print("I am some_async_func")
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
|
|
||||||
|
|
||||||
# Make sure a leading comment is not removed.
|
|
||||||
class SomeClass( Unformatted, SuperClasses ): # fmt: skip
|
|
||||||
def some_method( self, unformatted, args ): # fmt: skip
|
|
||||||
print("I am some_method")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
async def some_async_method( self, unformatted, args ): # fmt: skip
|
|
||||||
print("I am some_async_method")
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
|
|
||||||
|
|
||||||
# Make sure a leading comment is not removed.
|
|
||||||
if unformatted_call( args ): # fmt: skip
|
|
||||||
print("First branch")
|
|
||||||
# Make sure this is not removed.
|
|
||||||
elif another_unformatted_call( args ): # fmt: skip
|
|
||||||
print("Second branch")
|
|
||||||
else : # fmt: skip
|
|
||||||
print("Last branch")
|
|
||||||
|
|
||||||
|
|
||||||
while some_condition( unformatted, args ): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
|
|
||||||
|
|
||||||
for i in some_iter( unformatted, args ): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
|
|
||||||
|
|
||||||
async def test_async_for():
|
|
||||||
async for i in some_async_iter( unformatted, args ): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
|
|
||||||
|
|
||||||
try : # fmt: skip
|
|
||||||
some_call()
|
|
||||||
except UnformattedError as ex: # fmt: skip
|
|
||||||
handle_exception()
|
|
||||||
finally : # fmt: skip
|
|
||||||
finally_call()
|
|
||||||
|
|
||||||
|
|
||||||
with give_me_context( unformatted, args ): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
|
|
||||||
|
|
||||||
async def test_async_with():
|
|
||||||
async with give_me_async_context( unformatted, args ): # fmt: skip
|
|
||||||
print("Do something")
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/match.py
|
||||||
|
---
|
||||||
|
## Input
|
||||||
|
```py
|
||||||
|
def http_error(status):
|
||||||
|
match status : # fmt: skip
|
||||||
|
case 400 : # fmt: skip
|
||||||
|
return "Bad request"
|
||||||
|
case 404:
|
||||||
|
return "Not found"
|
||||||
|
case 418:
|
||||||
|
return "I'm a teapot"
|
||||||
|
case _:
|
||||||
|
return "Something's wrong with the internet"
|
||||||
|
|
||||||
|
# point is an (x, y) tuple
|
||||||
|
match point:
|
||||||
|
case (0, 0): # fmt: skip
|
||||||
|
print("Origin")
|
||||||
|
case (0, y):
|
||||||
|
print(f"Y={y}")
|
||||||
|
case (x, 0):
|
||||||
|
print(f"X={x}")
|
||||||
|
case (x, y):
|
||||||
|
print(f"X={x}, Y={y}")
|
||||||
|
case _:
|
||||||
|
raise ValueError("Not a point")
|
||||||
|
|
||||||
|
class Point:
|
||||||
|
x: int
|
||||||
|
y: int
|
||||||
|
|
||||||
|
def location(point):
|
||||||
|
match point:
|
||||||
|
case Point(x=0, y =0 ) : # fmt: skip
|
||||||
|
print("Origin is the point's location.")
|
||||||
|
case Point(x=0, y=y):
|
||||||
|
print(f"Y={y} and the point is on the y-axis.")
|
||||||
|
case Point(x=x, y=0):
|
||||||
|
print(f"X={x} and the point is on the x-axis.")
|
||||||
|
case Point():
|
||||||
|
print("The point is located somewhere else on the plane.")
|
||||||
|
case _:
|
||||||
|
print("Not a point")
|
||||||
|
|
||||||
|
|
||||||
|
match points:
|
||||||
|
case []:
|
||||||
|
print("No points in the list.")
|
||||||
|
case [
|
||||||
|
Point(0, 0)
|
||||||
|
]: # fmt: skip
|
||||||
|
print("The origin is the only point in the list.")
|
||||||
|
case [Point(x, y)]:
|
||||||
|
print(f"A single point {x}, {y} is in the list.")
|
||||||
|
case [Point(0, y1), Point(0, y2)]:
|
||||||
|
print(f"Two points on the Y axis at {y1}, {y2} are in the list.")
|
||||||
|
case _:
|
||||||
|
print("Something else is found in the list.")
|
||||||
|
|
||||||
|
|
||||||
|
match test_variable:
|
||||||
|
case (
|
||||||
|
'warning',
|
||||||
|
code,
|
||||||
|
40
|
||||||
|
): # fmt: skip
|
||||||
|
print("A warning has been received.")
|
||||||
|
case ('error', code, _):
|
||||||
|
print(f"An error {code} occurred.")
|
||||||
|
|
||||||
|
|
||||||
|
match point:
|
||||||
|
case Point(x, y) if x == y: # fmt: skip
|
||||||
|
print(f"The point is located on the diagonal Y=X at {x}.")
|
||||||
|
case Point(x, y):
|
||||||
|
print(f"Point is not on the diagonal.")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output
|
||||||
|
```py
|
||||||
|
def http_error(status):
|
||||||
|
match status : # fmt: skip
|
||||||
|
case 400 : # fmt: skip
|
||||||
|
return "Bad request"
|
||||||
|
case "NOT_YET_IMPLEMENTED_PatternMatchValue":
|
||||||
|
return "Not found"
|
||||||
|
case "NOT_YET_IMPLEMENTED_PatternMatchValue":
|
||||||
|
return "I'm a teapot"
|
||||||
|
case x as NOT_YET_IMPLEMENTED_PatternMatchAs:
|
||||||
|
return "Something's wrong with the internet"
|
||||||
|
|
||||||
|
|
||||||
|
# point is an (x, y) tuple
|
||||||
|
match point:
|
||||||
|
case (0, 0): # fmt: skip
|
||||||
|
print("Origin")
|
||||||
|
case [NOT_YET_IMPLEMENTED_PatternMatchSequence, 2]:
|
||||||
|
print(f"Y={y}")
|
||||||
|
case [NOT_YET_IMPLEMENTED_PatternMatchSequence, 2]:
|
||||||
|
print(f"X={x}")
|
||||||
|
case [NOT_YET_IMPLEMENTED_PatternMatchSequence, 2]:
|
||||||
|
print(f"X={x}, Y={y}")
|
||||||
|
case x as NOT_YET_IMPLEMENTED_PatternMatchAs:
|
||||||
|
raise ValueError("Not a point")
|
||||||
|
|
||||||
|
|
||||||
|
class Point:
|
||||||
|
x: int
|
||||||
|
y: int
|
||||||
|
|
||||||
|
|
||||||
|
def location(point):
|
||||||
|
match point:
|
||||||
|
case Point(x=0, y =0 ) : # fmt: skip
|
||||||
|
print("Origin is the point's location.")
|
||||||
|
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
|
||||||
|
print(f"Y={y} and the point is on the y-axis.")
|
||||||
|
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
|
||||||
|
print(f"X={x} and the point is on the x-axis.")
|
||||||
|
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
|
||||||
|
print("The point is located somewhere else on the plane.")
|
||||||
|
case x as NOT_YET_IMPLEMENTED_PatternMatchAs:
|
||||||
|
print("Not a point")
|
||||||
|
|
||||||
|
|
||||||
|
match points:
|
||||||
|
case [NOT_YET_IMPLEMENTED_PatternMatchSequence, 2]:
|
||||||
|
print("No points in the list.")
|
||||||
|
case [
|
||||||
|
Point(0, 0)
|
||||||
|
]: # fmt: skip
|
||||||
|
print("The origin is the only point in the list.")
|
||||||
|
case [NOT_YET_IMPLEMENTED_PatternMatchSequence, 2]:
|
||||||
|
print(f"A single point {x}, {y} is in the list.")
|
||||||
|
case [NOT_YET_IMPLEMENTED_PatternMatchSequence, 2]:
|
||||||
|
print(f"Two points on the Y axis at {y1}, {y2} are in the list.")
|
||||||
|
case x as NOT_YET_IMPLEMENTED_PatternMatchAs:
|
||||||
|
print("Something else is found in the list.")
|
||||||
|
|
||||||
|
|
||||||
|
match test_variable:
|
||||||
|
case (
|
||||||
|
'warning',
|
||||||
|
code,
|
||||||
|
40
|
||||||
|
): # fmt: skip
|
||||||
|
print("A warning has been received.")
|
||||||
|
case [NOT_YET_IMPLEMENTED_PatternMatchSequence, 2]:
|
||||||
|
print(f"An error {code} occurred.")
|
||||||
|
|
||||||
|
|
||||||
|
match point:
|
||||||
|
case Point(x, y) if x == y: # fmt: skip
|
||||||
|
print(f"The point is located on the diagonal Y=X at {x}.")
|
||||||
|
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
|
||||||
|
print(f"Point is not on the diagonal.")
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/or_else.py
|
||||||
|
---
|
||||||
|
## Input
|
||||||
|
```py
|
||||||
|
for item in container:
|
||||||
|
if search_something(item):
|
||||||
|
# Found it!
|
||||||
|
process(item)
|
||||||
|
break
|
||||||
|
# leading comment
|
||||||
|
else : #fmt: skip
|
||||||
|
# Didn't find anything..
|
||||||
|
not_found_in_container()
|
||||||
|
|
||||||
|
|
||||||
|
while i < 10:
|
||||||
|
print(i)
|
||||||
|
|
||||||
|
# leading comment
|
||||||
|
else : #fmt: skip
|
||||||
|
# Didn't find anything..
|
||||||
|
print("I was already larger than 9")
|
||||||
|
|
||||||
|
|
||||||
|
try : # fmt: skip
|
||||||
|
some_call()
|
||||||
|
except Exception : # fmt: skip
|
||||||
|
pass
|
||||||
|
except : # fmt: skip
|
||||||
|
handle_exception()
|
||||||
|
|
||||||
|
else : # fmt: skip
|
||||||
|
pass
|
||||||
|
finally : # fmt: skip
|
||||||
|
finally_call()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output
|
||||||
|
```py
|
||||||
|
for item in container:
|
||||||
|
if search_something(item):
|
||||||
|
# Found it!
|
||||||
|
process(item)
|
||||||
|
break
|
||||||
|
# leading comment
|
||||||
|
else : # fmt: skip
|
||||||
|
# Didn't find anything..
|
||||||
|
not_found_in_container()
|
||||||
|
|
||||||
|
|
||||||
|
while i < 10:
|
||||||
|
print(i)
|
||||||
|
|
||||||
|
# leading comment
|
||||||
|
else : # fmt: skip
|
||||||
|
# Didn't find anything..
|
||||||
|
print("I was already larger than 9")
|
||||||
|
|
||||||
|
|
||||||
|
try : # fmt: skip
|
||||||
|
some_call()
|
||||||
|
except Exception : # fmt: skip
|
||||||
|
pass
|
||||||
|
except : # fmt: skip
|
||||||
|
handle_exception()
|
||||||
|
|
||||||
|
else : # fmt: skip
|
||||||
|
pass
|
||||||
|
finally : # fmt: skip
|
||||||
|
finally_call()
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/parentheses.py
|
||||||
|
---
|
||||||
|
## Input
|
||||||
|
```py
|
||||||
|
if (
|
||||||
|
# a leading condition comment
|
||||||
|
len([1, 23, 3, 4, 5]) > 2 # trailing condition comment
|
||||||
|
# trailing own line comment
|
||||||
|
): # fmt: skip
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if ( # trailing open parentheses comment
|
||||||
|
# a leading condition comment
|
||||||
|
len([1, 23, 3, 4, 5]) > 2
|
||||||
|
) and ((((y)))): # fmt: skip
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if ( # trailing open parentheses comment
|
||||||
|
# a leading condition comment
|
||||||
|
len([1, 23, 3, 4, 5]) > 2
|
||||||
|
) and y: # fmt: skip
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output
|
||||||
|
```py
|
||||||
|
if (
|
||||||
|
# a leading condition comment
|
||||||
|
len([1, 23, 3, 4, 5]) > 2 # trailing condition comment
|
||||||
|
# trailing own line comment
|
||||||
|
): # fmt: skip
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if ( # trailing open parentheses comment
|
||||||
|
# a leading condition comment
|
||||||
|
len([1, 23, 3, 4, 5]) > 2
|
||||||
|
) and ((((y)))): # fmt: skip
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if ( # trailing open parentheses comment
|
||||||
|
# a leading condition comment
|
||||||
|
len([1, 23, 3, 4, 5]) > 2
|
||||||
|
) and y: # fmt: skip
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/type_params.py
|
||||||
|
---
|
||||||
|
## Input
|
||||||
|
```py
|
||||||
|
class TestTypeParam[ T ]: # fmt: skip
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TestTypeParam [ # trailing open paren comment
|
||||||
|
# leading comment
|
||||||
|
T # trailing type param comment
|
||||||
|
# trailing type param own line comment
|
||||||
|
]: # fmt: skip
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TestTrailingComment4[
|
||||||
|
T
|
||||||
|
] ( # trailing arguments open parenthesis comment
|
||||||
|
# leading argument comment
|
||||||
|
A # trailing argument comment
|
||||||
|
# trailing argument own line comment
|
||||||
|
): # fmt: skip
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test [
|
||||||
|
# comment
|
||||||
|
A,
|
||||||
|
|
||||||
|
# another
|
||||||
|
|
||||||
|
B,
|
||||||
|
] (): # fmt: skip
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output
|
||||||
|
```py
|
||||||
|
class TestTypeParam[ T ]: # fmt: skip
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TestTypeParam [ # trailing open paren comment
|
||||||
|
# leading comment
|
||||||
|
T # trailing type param comment
|
||||||
|
# trailing type param own line comment
|
||||||
|
]: # fmt: skip
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TestTrailingComment4[
|
||||||
|
T
|
||||||
|
] ( # trailing arguments open parenthesis comment
|
||||||
|
# leading argument comment
|
||||||
|
A # trailing argument comment
|
||||||
|
# trailing argument own line comment
|
||||||
|
): # fmt: skip
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test [
|
||||||
|
# comment
|
||||||
|
A,
|
||||||
|
|
||||||
|
# another
|
||||||
|
|
||||||
|
B,
|
||||||
|
] (): # fmt: skip
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue