Skip to content

Commit

Permalink
Preserve trailing statement semicolons when using fmt: skip
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaReiser committed Oct 29, 2023
1 parent e799f90 commit 44d4cac
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
x = 1; # fmt: skip

x = 1 ; # fmt: skip

x = 1 \
; # fmt: skip

x = 1 # ; # fmt: skip

_; #unrelated semicolon
26 changes: 25 additions & 1 deletion crates/ruff_python_formatter/src/statement/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use ruff_formatter::{FormatOwnedWithRule, FormatRefWithRule};
use ruff_python_ast::Stmt;
use ruff_python_ast::{AnyNodeRef, Stmt};
use ruff_python_trivia::{SimpleToken, SimpleTokenKind, SimpleTokenizer};
use ruff_text_size::{Ranged, TextRange};

use crate::prelude::*;

Expand Down Expand Up @@ -81,3 +83,25 @@ impl<'ast> IntoFormat<PyFormatContext<'ast>> for Stmt {
FormatOwnedWithRule::new(self, FormatStmt)
}
}

/// Returns the range of the semicolon terminating the statement or `None` if the statement
/// isn't terminated by a semicolon.
pub(super) fn trailing_semicolon(node: AnyNodeRef, source: &str) -> Option<TextRange> {
debug_assert!(node.is_statement());

let tokenizer = SimpleTokenizer::starts_at(node.end(), source);

let next_token = tokenizer
.take_while(|token| !token.kind().is_comment())
.find(|token| !token.kind().is_trivia());

if let Some(SimpleToken {
kind: SimpleTokenKind::Semi,
range,
}) = next_token
{
Some(range)
} else {
None
}
}
12 changes: 11 additions & 1 deletion crates/ruff_python_formatter/src/verbatim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use crate::comments::{leading_comments, trailing_comments, SourceComment};
use crate::prelude::*;
use crate::statement::clause::ClauseHeader;
use crate::statement::suite::SuiteChildStatement;
use crate::statement::trailing_semicolon;

/// Disables formatting for all statements between the `first_suppressed` that has a leading `fmt: off` comment
/// and the first trailing or leading `fmt: on` comment. The statements are formatted as they appear in the source code.
Expand Down Expand Up @@ -902,14 +903,23 @@ impl Format<PyFormatContext<'_>> for FormatSuppressedNode<'_> {
}
}

// Some statements may end with a semicolon. Preserve the semicolon
let semicolon_range = self
.node
.is_statement()
.then(|| trailing_semicolon(self.node, f.context().source()))
.flatten();
let verbatim_range = semicolon_range.map_or(self.node.range(), |semicolon| {
TextRange::new(self.node.start(), semicolon.end())
});
comments.mark_verbatim_node_comments_formatted(self.node);

// Write the outer comments and format the node as verbatim
write!(
f,
[
leading_comments(node_comments.leading),
verbatim_text(self.node),
verbatim_text(verbatim_range),
trailing_comments(node_comments.trailing)
]
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
source: crates/ruff_python_formatter/tests/fixtures.rs
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/fmt_skip/trailing_semi.py
---
## Input
```py
x = 1; # fmt: skip
x = 1 ; # fmt: skip
x = 1 \
; # fmt: skip
x = 1 # ; # fmt: skip
_; #unrelated semicolon
```

## Output
```py
x = 1; # fmt: skip
x = 1 ; # fmt: skip
x = 1 \
; # fmt: skip
x = 1 # ; # fmt: skip
_ # unrelated semicolon
```



6 changes: 5 additions & 1 deletion crates/ruff_python_trivia/src/tokenizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ pub enum SimpleTokenKind {
}

impl SimpleTokenKind {
const fn is_trivia(self) -> bool {
pub const fn is_trivia(self) -> bool {
matches!(
self,
SimpleTokenKind::Whitespace
Expand All @@ -482,6 +482,10 @@ impl SimpleTokenKind {
| SimpleTokenKind::Continuation
)
}

pub const fn is_comment(self) -> bool {
matches!(self, SimpleTokenKind::Comment)
}
}

/// Simple zero allocation tokenizer handling most tokens.
Expand Down

0 comments on commit 44d4cac

Please sign in to comment.