ruff/crates/ruff_python_formatter/src/expression/parentheses.rs
2023-06-08 16:07:33 +00:00

100 lines
2.9 KiB
Rust

use crate::comments::Comments;
use crate::trivia::{first_non_trivia_token, first_non_trivia_token_rev, Token, TokenKind};
use ruff_python_ast::node::AnyNodeRef;
use rustpython_parser::ast::Ranged;
pub(crate) trait NeedsParentheses {
fn needs_parentheses(
&self,
parenthesize: Parenthesize,
source: &str,
comments: &Comments,
) -> Parentheses;
}
pub(super) fn default_expression_needs_parentheses(
node: AnyNodeRef,
parenthesize: Parenthesize,
source: &str,
comments: &Comments,
) -> Parentheses {
debug_assert!(
node.is_expression(),
"Should only be called for expressions"
);
// `Optional` or `Preserve` and expression has parentheses in source code.
if !parenthesize.is_if_breaks() && is_expression_parenthesized(node, source) {
Parentheses::Always
}
// `Optional` or `IfBreaks`: Add parentheses if the expression doesn't fit on a line but enforce
// parentheses if the expression has leading comments
else if !parenthesize.is_preserve() {
if comments.has_leading_comments(node) {
Parentheses::Always
} else {
Parentheses::Optional
}
} else {
//`Preserve` and expression has no parentheses in the source code
Parentheses::Never
}
}
/// Configures if the expression should be parenthesized.
#[derive(Copy, Clone, Debug, Default)]
pub enum Parenthesize {
/// Parenthesize the expression if it has parenthesis in the source.
#[default]
Preserve,
/// Parenthesizes the expression if it doesn't fit on a line OR if the expression is parenthesized in the source code.
Optional,
/// Parenthesizes the expression only if it doesn't fit on a line.
IfBreaks,
}
impl Parenthesize {
const fn is_if_breaks(self) -> bool {
matches!(self, Parenthesize::IfBreaks)
}
const fn is_preserve(self) -> bool {
matches!(self, Parenthesize::Preserve)
}
}
/// Whether it is necessary to add parentheses around an expression.
/// This is different from [`Parenthesize`] in that it is the resolved representation: It takes into account
/// whether there are parentheses in the source code or not.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Parentheses {
/// Always create parentheses
Always,
/// Only add parentheses when necessary because the expression breaks over multiple lines.
Optional,
/// Custom handling by the node's formatter implementation
Custom,
/// Never add parentheses
Never,
}
fn is_expression_parenthesized(expr: AnyNodeRef, contents: &str) -> bool {
matches!(
first_non_trivia_token(expr.end(), contents),
Some(Token {
kind: TokenKind::RParen,
..
})
) && matches!(
first_non_trivia_token_rev(expr.start(), contents),
Some(Token {
kind: TokenKind::LParen,
..
})
)
}