mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-02 22:55:08 +00:00
Implement basic rendering of remaining AST nodes (#3233)
This commit is contained in:
parent
51bca19c1d
commit
1c75071136
8 changed files with 527 additions and 48 deletions
|
@ -166,7 +166,7 @@ fn format_slice(
|
|||
upper: &SliceIndex,
|
||||
step: Option<&SliceIndex>,
|
||||
) -> FormatResult<()> {
|
||||
// // https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#slices
|
||||
// https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#slices
|
||||
let lower_is_simple = if let SliceIndexKind::Index { value } = &lower.node {
|
||||
is_simple_slice(value)
|
||||
} else {
|
||||
|
@ -242,6 +242,22 @@ fn format_slice(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn format_formatted_value(
|
||||
f: &mut Formatter<ASTFormatContext<'_>>,
|
||||
expr: &Expr,
|
||||
value: &Expr,
|
||||
_conversion: usize,
|
||||
format_spec: Option<&Expr>,
|
||||
) -> FormatResult<()> {
|
||||
write!(f, [text("!")])?;
|
||||
write!(f, [value.format()])?;
|
||||
if let Some(format_spec) = format_spec {
|
||||
write!(f, [text(":")])?;
|
||||
write!(f, [format_spec.format()])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_list(
|
||||
f: &mut Formatter<ASTFormatContext<'_>>,
|
||||
expr: &Expr,
|
||||
|
@ -703,6 +719,22 @@ fn format_attribute(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn format_named_expr(
|
||||
f: &mut Formatter<ASTFormatContext<'_>>,
|
||||
expr: &Expr,
|
||||
target: &Expr,
|
||||
value: &Expr,
|
||||
) -> FormatResult<()> {
|
||||
write!(f, [target.format()])?;
|
||||
write!(f, [text(":=")])?;
|
||||
write!(f, [space()])?;
|
||||
write!(f, [group(&format_args![value.format()])])?;
|
||||
|
||||
write!(f, [end_of_line_comments(expr)])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_bool_op(
|
||||
f: &mut Formatter<ASTFormatContext<'_>>,
|
||||
expr: &Expr,
|
||||
|
@ -825,7 +857,7 @@ impl Format<ASTFormatContext<'_>> for FormatExpr<'_> {
|
|||
|
||||
match &self.item.node {
|
||||
ExprKind::BoolOp { op, values } => format_bool_op(f, self.item, op, values),
|
||||
// ExprKind::NamedExpr { .. } => {}
|
||||
ExprKind::NamedExpr { target, value } => format_named_expr(f, self.item, target, value),
|
||||
ExprKind::BinOp { left, op, right } => format_bin_op(f, self.item, left, op, right),
|
||||
ExprKind::UnaryOp { op, operand } => format_unary_op(f, self.item, op, operand),
|
||||
ExprKind::Lambda { args, body } => format_lambda(f, self.item, args, body),
|
||||
|
@ -859,7 +891,6 @@ impl Format<ASTFormatContext<'_>> for FormatExpr<'_> {
|
|||
args,
|
||||
keywords,
|
||||
} => format_call(f, self.item, func, args, keywords),
|
||||
// ExprKind::FormattedValue { .. } => {}
|
||||
ExprKind::JoinedStr { values } => format_joined_str(f, self.item, values),
|
||||
ExprKind::Constant { value, kind } => {
|
||||
format_constant(f, self.item, value, kind.as_deref())
|
||||
|
@ -875,9 +906,11 @@ impl Format<ASTFormatContext<'_>> for FormatExpr<'_> {
|
|||
ExprKind::Slice { lower, upper, step } => {
|
||||
format_slice(f, self.item, lower, upper, step.as_ref())
|
||||
}
|
||||
_ => {
|
||||
unimplemented!("Implement ExprKind: {:?}", self.item.node)
|
||||
}
|
||||
ExprKind::FormattedValue {
|
||||
value,
|
||||
conversion,
|
||||
format_spec,
|
||||
} => format_formatted_value(f, self.item, value, *conversion, format_spec.as_deref()),
|
||||
}?;
|
||||
|
||||
// Any trailing comments come on the lines after.
|
||||
|
|
39
crates/ruff_python_formatter/src/format/match_case.rs
Normal file
39
crates/ruff_python_formatter/src/format/match_case.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
use ruff_formatter::prelude::*;
|
||||
use ruff_formatter::write;
|
||||
|
||||
use crate::context::ASTFormatContext;
|
||||
use crate::cst::MatchCase;
|
||||
use crate::format::builders::block;
|
||||
use crate::shared_traits::AsFormat;
|
||||
|
||||
pub struct FormatMatchCase<'a> {
|
||||
item: &'a MatchCase,
|
||||
}
|
||||
|
||||
impl AsFormat<ASTFormatContext<'_>> for MatchCase {
|
||||
type Format<'a> = FormatMatchCase<'a>;
|
||||
|
||||
fn format(&self) -> Self::Format<'_> {
|
||||
FormatMatchCase { item: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl Format<ASTFormatContext<'_>> for FormatMatchCase<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
|
||||
let MatchCase {
|
||||
pattern,
|
||||
guard,
|
||||
body,
|
||||
} = self.item;
|
||||
|
||||
write!(f, [text("case")])?;
|
||||
write!(f, [space(), pattern.format()])?;
|
||||
if let Some(guard) = &guard {
|
||||
write!(f, [space(), text("if"), space(), guard.format()])?;
|
||||
}
|
||||
write!(f, [text(":")])?;
|
||||
write!(f, [block_indent(&block(body))])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -9,8 +9,10 @@ mod comprehension;
|
|||
mod excepthandler;
|
||||
mod expr;
|
||||
mod helpers;
|
||||
mod match_case;
|
||||
mod numbers;
|
||||
mod operator;
|
||||
mod pattern;
|
||||
mod stmt;
|
||||
mod strings;
|
||||
mod unaryop;
|
||||
|
|
158
crates/ruff_python_formatter/src/format/pattern.rs
Normal file
158
crates/ruff_python_formatter/src/format/pattern.rs
Normal file
|
@ -0,0 +1,158 @@
|
|||
use ruff_formatter::prelude::*;
|
||||
use ruff_formatter::write;
|
||||
use ruff_text_size::TextSize;
|
||||
use rustpython_parser::ast::Constant;
|
||||
|
||||
use crate::context::ASTFormatContext;
|
||||
use crate::cst::{Pattern, PatternKind};
|
||||
use crate::shared_traits::AsFormat;
|
||||
|
||||
pub struct FormatPattern<'a> {
|
||||
item: &'a Pattern,
|
||||
}
|
||||
|
||||
impl AsFormat<ASTFormatContext<'_>> for Pattern {
|
||||
type Format<'a> = FormatPattern<'a>;
|
||||
|
||||
fn format(&self) -> Self::Format<'_> {
|
||||
FormatPattern { item: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl Format<ASTFormatContext<'_>> for FormatPattern<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<ASTFormatContext>) -> FormatResult<()> {
|
||||
let pattern = self.item;
|
||||
|
||||
match &pattern.node {
|
||||
PatternKind::MatchValue { value } => {
|
||||
write!(f, [value.format()])?;
|
||||
}
|
||||
PatternKind::MatchSingleton { value } => match value {
|
||||
Constant::None => write!(f, [text("None")])?,
|
||||
Constant::Bool(value) => {
|
||||
if *value {
|
||||
write!(f, [text("True")])?;
|
||||
} else {
|
||||
write!(f, [text("False")])?;
|
||||
}
|
||||
}
|
||||
_ => unreachable!("singleton pattern must be None or bool"),
|
||||
},
|
||||
PatternKind::MatchSequence { patterns } => {
|
||||
write!(f, [text("[")])?;
|
||||
if let Some(pattern) = patterns.first() {
|
||||
write!(f, [pattern.format()])?;
|
||||
}
|
||||
for pattern in patterns.iter().skip(1) {
|
||||
write!(f, [text(","), space(), pattern.format()])?;
|
||||
}
|
||||
write!(f, [text("]")])?;
|
||||
}
|
||||
PatternKind::MatchMapping {
|
||||
keys,
|
||||
patterns,
|
||||
rest,
|
||||
} => {
|
||||
write!(f, [text("{")])?;
|
||||
if let Some(pattern) = patterns.first() {
|
||||
write!(f, [keys[0].format(), text(":"), space(), pattern.format()])?;
|
||||
}
|
||||
for (key, pattern) in keys.iter().skip(1).zip(patterns.iter().skip(1)) {
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
text(","),
|
||||
space(),
|
||||
key.format(),
|
||||
text(":"),
|
||||
space(),
|
||||
pattern.format()
|
||||
]
|
||||
)?;
|
||||
}
|
||||
if let Some(rest) = &rest {
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
text(","),
|
||||
space(),
|
||||
text("**"),
|
||||
space(),
|
||||
dynamic_text(rest, TextSize::default())
|
||||
]
|
||||
)?;
|
||||
}
|
||||
write!(f, [text("}")])?;
|
||||
}
|
||||
PatternKind::MatchClass {
|
||||
cls,
|
||||
patterns,
|
||||
kwd_attrs,
|
||||
kwd_patterns,
|
||||
} => {
|
||||
write!(f, [cls.format()])?;
|
||||
if !patterns.is_empty() {
|
||||
write!(f, [text("(")])?;
|
||||
if let Some(pattern) = patterns.first() {
|
||||
write!(f, [pattern.format()])?;
|
||||
}
|
||||
for pattern in patterns.iter().skip(1) {
|
||||
write!(f, [text(","), space(), pattern.format()])?;
|
||||
}
|
||||
write!(f, [text(")")])?;
|
||||
}
|
||||
if !kwd_attrs.is_empty() {
|
||||
write!(f, [text("(")])?;
|
||||
if let Some(attr) = kwd_attrs.first() {
|
||||
write!(f, [dynamic_text(attr, TextSize::default())])?;
|
||||
}
|
||||
for attr in kwd_attrs.iter().skip(1) {
|
||||
write!(
|
||||
f,
|
||||
[text(","), space(), dynamic_text(attr, TextSize::default())]
|
||||
)?;
|
||||
}
|
||||
write!(f, [text(")")])?;
|
||||
}
|
||||
if !kwd_patterns.is_empty() {
|
||||
write!(f, [text("(")])?;
|
||||
if let Some(pattern) = kwd_patterns.first() {
|
||||
write!(f, [pattern.format()])?;
|
||||
}
|
||||
for pattern in kwd_patterns.iter().skip(1) {
|
||||
write!(f, [text(","), space(), pattern.format()])?;
|
||||
}
|
||||
write!(f, [text(")")])?;
|
||||
}
|
||||
}
|
||||
PatternKind::MatchStar { name } => {
|
||||
if let Some(name) = &name {
|
||||
write!(f, [text("*"), dynamic_text(name, TextSize::default())])?;
|
||||
} else {
|
||||
write!(f, [text("*_")])?;
|
||||
}
|
||||
}
|
||||
PatternKind::MatchAs { pattern, name } => {
|
||||
if let Some(pattern) = &pattern {
|
||||
write!(f, [pattern.format()])?;
|
||||
write!(f, [space()])?;
|
||||
write!(f, [text("as")])?;
|
||||
write!(f, [space()])?;
|
||||
}
|
||||
if let Some(name) = &name {
|
||||
write!(f, [dynamic_text(name, TextSize::default())])?;
|
||||
} else {
|
||||
write!(f, [text("_")])?;
|
||||
}
|
||||
}
|
||||
PatternKind::MatchOr { patterns } => {
|
||||
write!(f, [patterns[0].format()])?;
|
||||
for pattern in patterns.iter().skip(1) {
|
||||
write!(f, [space(), text("|"), space(), pattern.format()])?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -6,52 +6,67 @@ use ruff_text_size::TextSize;
|
|||
|
||||
use crate::context::ASTFormatContext;
|
||||
use crate::cst::{
|
||||
Alias, Arguments, Excepthandler, Expr, ExprKind, Keyword, Stmt, StmtKind, Withitem,
|
||||
Alias, Arguments, Excepthandler, Expr, ExprKind, Keyword, MatchCase, Operator, Stmt, StmtKind,
|
||||
Withitem,
|
||||
};
|
||||
use crate::format::builders::{block, join_names};
|
||||
use crate::format::comments::{end_of_line_comments, leading_comments, trailing_comments};
|
||||
use crate::format::helpers::is_self_closing;
|
||||
use crate::shared_traits::AsFormat;
|
||||
|
||||
fn format_break(f: &mut Formatter<ASTFormatContext<'_>>) -> FormatResult<()> {
|
||||
write!(f, [text("break")])
|
||||
}
|
||||
|
||||
fn format_pass(f: &mut Formatter<ASTFormatContext<'_>>, stmt: &Stmt) -> FormatResult<()> {
|
||||
// Write the statement body.
|
||||
write!(f, [text("pass")])?;
|
||||
|
||||
fn format_break(f: &mut Formatter<ASTFormatContext<'_>>, stmt: &Stmt) -> FormatResult<()> {
|
||||
write!(f, [text("break")])?;
|
||||
write!(f, [end_of_line_comments(stmt)])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_continue(f: &mut Formatter<ASTFormatContext<'_>>) -> FormatResult<()> {
|
||||
write!(f, [text("continue")])
|
||||
fn format_pass(f: &mut Formatter<ASTFormatContext<'_>>, stmt: &Stmt) -> FormatResult<()> {
|
||||
write!(f, [text("pass")])?;
|
||||
write!(f, [end_of_line_comments(stmt)])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_global(f: &mut Formatter<ASTFormatContext<'_>>, names: &[String]) -> FormatResult<()> {
|
||||
fn format_continue(f: &mut Formatter<ASTFormatContext<'_>>, stmt: &Stmt) -> FormatResult<()> {
|
||||
write!(f, [text("continue")])?;
|
||||
write!(f, [end_of_line_comments(stmt)])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_global(
|
||||
f: &mut Formatter<ASTFormatContext<'_>>,
|
||||
stmt: &Stmt,
|
||||
names: &[String],
|
||||
) -> FormatResult<()> {
|
||||
write!(f, [text("global")])?;
|
||||
if !names.is_empty() {
|
||||
write!(f, [space(), join_names(names)])?;
|
||||
}
|
||||
write!(f, [end_of_line_comments(stmt)])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_nonlocal(f: &mut Formatter<ASTFormatContext<'_>>, names: &[String]) -> FormatResult<()> {
|
||||
fn format_nonlocal(
|
||||
f: &mut Formatter<ASTFormatContext<'_>>,
|
||||
stmt: &Stmt,
|
||||
names: &[String],
|
||||
) -> FormatResult<()> {
|
||||
write!(f, [text("nonlocal")])?;
|
||||
if !names.is_empty() {
|
||||
write!(f, [space(), join_names(names)])?;
|
||||
}
|
||||
write!(f, [end_of_line_comments(stmt)])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_delete(f: &mut Formatter<ASTFormatContext<'_>>, targets: &[Expr]) -> FormatResult<()> {
|
||||
fn format_delete(
|
||||
f: &mut Formatter<ASTFormatContext<'_>>,
|
||||
stmt: &Stmt,
|
||||
targets: &[Expr],
|
||||
) -> FormatResult<()> {
|
||||
write!(f, [text("del")])?;
|
||||
|
||||
match targets.len() {
|
||||
0 => Ok(()),
|
||||
1 => write!(f, [space(), targets[0].format()]),
|
||||
0 => {}
|
||||
1 => write!(f, [space(), targets[0].format()])?,
|
||||
_ => {
|
||||
write!(
|
||||
f,
|
||||
|
@ -74,9 +89,11 @@ fn format_delete(f: &mut Formatter<ASTFormatContext<'_>>, targets: &[Expr]) -> F
|
|||
if_group_breaks(&text(")")),
|
||||
])
|
||||
]
|
||||
)
|
||||
)?;
|
||||
}
|
||||
}
|
||||
write!(f, [end_of_line_comments(stmt)])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_class_def(
|
||||
|
@ -223,6 +240,34 @@ fn format_assign(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn format_aug_assign(
|
||||
f: &mut Formatter<ASTFormatContext<'_>>,
|
||||
stmt: &Stmt,
|
||||
target: &Expr,
|
||||
op: &Operator,
|
||||
value: &Expr,
|
||||
) -> FormatResult<()> {
|
||||
write!(f, [target.format()])?;
|
||||
write!(f, [text(" "), op.format(), text("=")])?;
|
||||
if is_self_closing(value) {
|
||||
write!(f, [space(), group(&value.format())])?;
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
space(),
|
||||
group(&format_args![
|
||||
if_group_breaks(&text("(")),
|
||||
soft_block_indent(&value.format()),
|
||||
if_group_breaks(&text(")")),
|
||||
])
|
||||
]
|
||||
)?;
|
||||
}
|
||||
write!(f, [end_of_line_comments(stmt)])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_ann_assign(
|
||||
f: &mut Formatter<ASTFormatContext<'_>>,
|
||||
stmt: &Stmt,
|
||||
|
@ -268,7 +313,11 @@ fn format_for(
|
|||
body: &[Stmt],
|
||||
orelse: &[Stmt],
|
||||
_type_comment: Option<&str>,
|
||||
async_: bool,
|
||||
) -> FormatResult<()> {
|
||||
if async_ {
|
||||
write!(f, [text("async"), space()])?;
|
||||
}
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
|
@ -351,6 +400,19 @@ fn format_if(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn format_match(
|
||||
f: &mut Formatter<ASTFormatContext<'_>>,
|
||||
stmt: &Stmt,
|
||||
subject: &Expr,
|
||||
cases: &[MatchCase],
|
||||
) -> FormatResult<()> {
|
||||
write!(f, [text("match"), space(), subject.format(), text(":")])?;
|
||||
for case in cases {
|
||||
write!(f, [block_indent(&case.format())])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_raise(
|
||||
f: &mut Formatter<ASTFormatContext<'_>>,
|
||||
stmt: &Stmt,
|
||||
|
@ -585,7 +647,6 @@ fn format_with_(
|
|||
if async_ {
|
||||
write!(f, [text("async"), space()])?;
|
||||
}
|
||||
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
|
@ -609,7 +670,8 @@ fn format_with_(
|
|||
text(":"),
|
||||
block_indent(&block(body))
|
||||
]
|
||||
)
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct FormatStmt<'a> {
|
||||
|
@ -622,10 +684,10 @@ impl Format<ASTFormatContext<'_>> for FormatStmt<'_> {
|
|||
|
||||
match &self.item.node {
|
||||
StmtKind::Pass => format_pass(f, self.item),
|
||||
StmtKind::Break => format_break(f),
|
||||
StmtKind::Continue => format_continue(f),
|
||||
StmtKind::Global { names } => format_global(f, names),
|
||||
StmtKind::Nonlocal { names } => format_nonlocal(f, names),
|
||||
StmtKind::Break => format_break(f, self.item),
|
||||
StmtKind::Continue => format_continue(f, self.item),
|
||||
StmtKind::Global { names } => format_global(f, self.item, names),
|
||||
StmtKind::Nonlocal { names } => format_nonlocal(f, self.item, names),
|
||||
StmtKind::FunctionDef {
|
||||
name,
|
||||
args,
|
||||
|
@ -668,9 +730,11 @@ impl Format<ASTFormatContext<'_>> for FormatStmt<'_> {
|
|||
decorator_list,
|
||||
} => format_class_def(f, name, bases, keywords, body, decorator_list),
|
||||
StmtKind::Return { value } => format_return(f, self.item, value.as_ref()),
|
||||
StmtKind::Delete { targets } => format_delete(f, targets),
|
||||
StmtKind::Delete { targets } => format_delete(f, self.item, targets),
|
||||
StmtKind::Assign { targets, value, .. } => format_assign(f, self.item, targets, value),
|
||||
// StmtKind::AugAssign { .. } => {}
|
||||
StmtKind::AugAssign { target, op, value } => {
|
||||
format_aug_assign(f, self.item, target, op, value)
|
||||
}
|
||||
StmtKind::AnnAssign {
|
||||
target,
|
||||
annotation,
|
||||
|
@ -691,8 +755,24 @@ impl Format<ASTFormatContext<'_>> for FormatStmt<'_> {
|
|||
body,
|
||||
orelse,
|
||||
type_comment.as_deref(),
|
||||
false,
|
||||
),
|
||||
StmtKind::AsyncFor {
|
||||
target,
|
||||
iter,
|
||||
body,
|
||||
orelse,
|
||||
type_comment,
|
||||
} => format_for(
|
||||
f,
|
||||
self.item,
|
||||
target,
|
||||
iter,
|
||||
body,
|
||||
orelse,
|
||||
type_comment.as_deref(),
|
||||
true,
|
||||
),
|
||||
// StmtKind::AsyncFor { .. } => {}
|
||||
StmtKind::While { test, body, orelse } => {
|
||||
format_while(f, self.item, test, body, orelse)
|
||||
}
|
||||
|
@ -721,7 +801,7 @@ impl Format<ASTFormatContext<'_>> for FormatStmt<'_> {
|
|||
type_comment.as_ref().map(String::as_str),
|
||||
true,
|
||||
),
|
||||
// StmtKind::Match { .. } => {}
|
||||
StmtKind::Match { subject, cases } => format_match(f, self.item, subject, cases),
|
||||
StmtKind::Raise { exc, cause } => {
|
||||
format_raise(f, self.item, exc.as_deref(), cause.as_deref())
|
||||
}
|
||||
|
@ -752,11 +832,7 @@ impl Format<ASTFormatContext<'_>> for FormatStmt<'_> {
|
|||
names,
|
||||
level.as_ref(),
|
||||
),
|
||||
// StmtKind::Nonlocal { .. } => {}
|
||||
StmtKind::Expr { value } => format_expr(f, self.item, value),
|
||||
_ => {
|
||||
unimplemented!("Implement StmtKind: {:?}", self.item.node)
|
||||
}
|
||||
}?;
|
||||
|
||||
write!(f, [hard_line_break()])?;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue