mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-23 13:05:06 +00:00
Format the comment content (#4786)
This commit is contained in:
parent
602b4b3519
commit
4cd4b37e74
5 changed files with 88 additions and 94 deletions
|
@ -1,9 +1,11 @@
|
|||
use crate::comments::SourceComment;
|
||||
use crate::context::NodeLevel;
|
||||
use crate::prelude::*;
|
||||
use crate::trivia::{lines_after, lines_before};
|
||||
use ruff_formatter::{format_args, write};
|
||||
use ruff_formatter::{format_args, write, FormatError, SourceCode};
|
||||
use ruff_python_ast::node::AnyNodeRef;
|
||||
use ruff_python_ast::prelude::AstNode;
|
||||
use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||
|
||||
/// Formats the leading comments of a node.
|
||||
pub(crate) fn leading_comments<T>(node: &T) -> FormatLeadingComments
|
||||
|
@ -30,10 +32,7 @@ impl Format<PyFormatContext<'_>> for FormatLeadingComments<'_> {
|
|||
let lines_after_comment = lines_after(f.context().contents(), slice.end());
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
source_text_slice(slice.range(), ContainsNewlines::No),
|
||||
empty_lines(lines_after_comment)
|
||||
]
|
||||
[format_comment(comment), empty_lines(lines_after_comment)]
|
||||
)?;
|
||||
|
||||
comment.mark_formatted();
|
||||
|
@ -64,7 +63,6 @@ impl Format<PyFormatContext<'_>> for FormatTrailingComments<'_> {
|
|||
|
||||
for trailing in comments.trailing_comments(self.node) {
|
||||
let slice = trailing.slice();
|
||||
let content = source_text_slice(slice.range(), ContainsNewlines::No);
|
||||
|
||||
let lines_before_comment = lines_before(f.context().contents(), slice.start());
|
||||
has_empty_lines_before |= lines_before_comment > 0;
|
||||
|
@ -81,7 +79,10 @@ impl Format<PyFormatContext<'_>> for FormatTrailingComments<'_> {
|
|||
f,
|
||||
[
|
||||
line_suffix(&format_with(|f| {
|
||||
write!(f, [empty_lines(lines_before_comment), content])
|
||||
write!(
|
||||
f,
|
||||
[empty_lines(lines_before_comment), format_comment(trailing)]
|
||||
)
|
||||
})),
|
||||
expand_parent()
|
||||
]
|
||||
|
@ -90,7 +91,7 @@ impl Format<PyFormatContext<'_>> for FormatTrailingComments<'_> {
|
|||
write!(
|
||||
f,
|
||||
[
|
||||
line_suffix(&format_args![space(), space(), content]),
|
||||
line_suffix(&format_args![space(), space(), format_comment(trailing)]),
|
||||
expand_parent()
|
||||
]
|
||||
)?;
|
||||
|
@ -132,7 +133,7 @@ impl Format<PyFormatContext<'_>> for FormatDanglingComments<'_> {
|
|||
write!(
|
||||
f,
|
||||
[
|
||||
source_text_slice(comment.slice().range(), ContainsNewlines::No),
|
||||
format_comment(comment),
|
||||
empty_lines(lines_after(f.context().contents(), comment.slice().end()))
|
||||
]
|
||||
)?;
|
||||
|
@ -146,6 +147,66 @@ impl Format<PyFormatContext<'_>> for FormatDanglingComments<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Formats the content of the passed comment.
|
||||
///
|
||||
/// * Adds a whitespace between `#` and the comment text except if the first character is a `#`, `:`, `'`, or `!`
|
||||
/// * Replaces non breaking whitespaces with regular whitespaces except if in front of a `types:` comment
|
||||
const fn format_comment(comment: &SourceComment) -> FormatComment {
|
||||
FormatComment { comment }
|
||||
}
|
||||
|
||||
struct FormatComment<'a> {
|
||||
comment: &'a SourceComment,
|
||||
}
|
||||
|
||||
impl Format<PyFormatContext<'_>> for FormatComment<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
|
||||
let slice = self.comment.slice();
|
||||
let comment_text = slice.text(SourceCode::new(f.context().contents()));
|
||||
|
||||
let trimmed = comment_text.trim_end();
|
||||
let trailing_whitespace_len = comment_text.text_len() - trimmed.text_len();
|
||||
|
||||
let Some(content) = trimmed.strip_prefix('#') else {
|
||||
return Err(FormatError::SyntaxError);
|
||||
};
|
||||
|
||||
// Fast path for correctly formatted comments:
|
||||
// * Start with a `#` and are followed by a space
|
||||
// * Have no trailing whitespace.
|
||||
if trailing_whitespace_len == TextSize::new(0) && content.starts_with(' ') {
|
||||
return source_text_slice(slice.range(), ContainsNewlines::No).fmt(f);
|
||||
}
|
||||
|
||||
write!(f, [source_position(slice.start()), text("#")])?;
|
||||
|
||||
// Starts with a non breaking space
|
||||
let start_offset =
|
||||
if content.starts_with('\u{A0}') && !content.trim_start().starts_with("type:") {
|
||||
// Replace non-breaking space with a space (if not followed by a normal space)
|
||||
"#\u{A0}".text_len()
|
||||
} else {
|
||||
'#'.text_len()
|
||||
};
|
||||
|
||||
// Add a space between the `#` and the text if the source contains none.
|
||||
if !content.is_empty() && !content.starts_with([' ', '!', ':', '#', '\'']) {
|
||||
write!(f, [space()])?;
|
||||
}
|
||||
|
||||
let start = slice.start() + start_offset;
|
||||
let end = slice.range().end() - trailing_whitespace_len;
|
||||
|
||||
write!(
|
||||
f,
|
||||
[
|
||||
source_text_slice(TextRange::new(start, end), ContainsNewlines::No),
|
||||
source_position(slice.end())
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper that inserts the appropriate number of empty lines before a comment, depending on the node level.
|
||||
// Top level: Up to two empty lines
|
||||
// parenthesized: A single empty line
|
||||
|
|
|
@ -349,7 +349,7 @@ instruction()#comment with bad spacing
|
|||
# yup
|
||||
for element in collection.select_elements()
|
||||
# right
|
||||
@@ -140,34 +132,26 @@
|
||||
@@ -140,28 +132,20 @@
|
||||
# and round and round we go
|
||||
# and round and round we go
|
||||
|
||||
|
@ -386,13 +386,6 @@ instruction()#comment with bad spacing
|
|||
#######################
|
||||
### SECTION COMMENT ###
|
||||
#######################
|
||||
|
||||
|
||||
-instruction() # comment with bad spacing
|
||||
+instruction() #comment with bad spacing
|
||||
|
||||
# END COMMENTS
|
||||
# MORE END COMMENTS
|
||||
```
|
||||
|
||||
## Ruff Output
|
||||
|
@ -551,7 +544,7 @@ class Test:
|
|||
#######################
|
||||
|
||||
|
||||
instruction() #comment with bad spacing
|
||||
instruction() # comment with bad spacing
|
||||
|
||||
# END COMMENTS
|
||||
# MORE END COMMENTS
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
---
|
||||
source: crates/ruff_python_formatter/src/lib.rs
|
||||
expression: snapshot
|
||||
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments8.py
|
||||
---
|
||||
## Input
|
||||
|
||||
```py
|
||||
# The percent-percent comments are Spyder IDE cells.
|
||||
# Both `#%%`` and `# %%` are accepted, so `black` standardises
|
||||
# to the latter.
|
||||
|
||||
#%%
|
||||
# %%
|
||||
```
|
||||
|
||||
## Black Differences
|
||||
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -2,5 +2,5 @@
|
||||
# Both `#%%`` and `# %%` are accepted, so `black` standardises
|
||||
# to the latter.
|
||||
|
||||
-# %%
|
||||
+#%%
|
||||
# %%
|
||||
```
|
||||
|
||||
## Ruff Output
|
||||
|
||||
```py
|
||||
# The percent-percent comments are Spyder IDE cells.
|
||||
# Both `#%%`` and `# %%` are accepted, so `black` standardises
|
||||
# to the latter.
|
||||
|
||||
#%%
|
||||
# %%
|
||||
```
|
||||
|
||||
## Black Output
|
||||
|
||||
```py
|
||||
# The percent-percent comments are Spyder IDE cells.
|
||||
# Both `#%%`` and `# %%` are accepted, so `black` standardises
|
||||
# to the latter.
|
||||
|
||||
# %%
|
||||
# %%
|
||||
```
|
||||
|
||||
|
|
@ -42,23 +42,19 @@ def function(a:int=42):
|
|||
+ # DEFAULT_TYPE_ATTRIBUTES,
|
||||
)
|
||||
-
|
||||
-result = 1 # A simple comment
|
||||
result = 1 # A simple comment
|
||||
-result = (1,) # Another one
|
||||
-
|
||||
-result = 1 # type: ignore
|
||||
-result = 1 # This comment is talking about type: ignore
|
||||
-square = Square(4) # type: Optional[Square]
|
||||
+result = ( 1, ) # Another one
|
||||
result = 1 # type: ignore
|
||||
result = 1 # This comment is talking about type: ignore
|
||||
square = Square(4) # type: Optional[Square]
|
||||
-
|
||||
-
|
||||
-def function(a: int = 42):
|
||||
- """This docstring is already formatted
|
||||
- a
|
||||
- b
|
||||
+result = 1 # A simple comment
|
||||
+result = ( 1, ) # Another one
|
||||
+result = 1 # type: ignore
|
||||
+result = 1 # This comment is talking about type: ignore
|
||||
+square = Square(4) # type: Optional[Square]
|
||||
+def function(a:int=42):
|
||||
+ """ This docstring is already formatted
|
||||
+ a
|
||||
|
@ -76,11 +72,11 @@ def function(a:int=42):
|
|||
from .config import ( ConfigTypeAttributes, Int, Path, # String,
|
||||
# DEFAULT_TYPE_ATTRIBUTES,
|
||||
)
|
||||
result = 1 # A simple comment
|
||||
result = ( 1, ) # Another one
|
||||
result = 1 # type: ignore
|
||||
result = 1 # This comment is talking about type: ignore
|
||||
square = Square(4) # type: Optional[Square]
|
||||
result = 1 # A simple comment
|
||||
result = ( 1, ) # Another one
|
||||
result = 1 # type: ignore
|
||||
result = 1 # This comment is talking about type: ignore
|
||||
square = Square(4) # type: Optional[Square]
|
||||
def function(a:int=42):
|
||||
""" This docstring is already formatted
|
||||
a
|
||||
|
|
|
@ -19,22 +19,19 @@ d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasu
|
|||
+++ Ruff
|
||||
@@ -1,4 +1,4 @@
|
||||
-a = "this is some code"
|
||||
-b = 5 # fmt:skip
|
||||
-c = 9 # fmt: skip
|
||||
-d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" # fmt:skip
|
||||
+a = "this is some code"
|
||||
+b = 5 #fmt:skip
|
||||
+c = 9 #fmt: skip
|
||||
+d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" #fmt:skip
|
||||
b = 5 # fmt:skip
|
||||
c = 9 # fmt: skip
|
||||
d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" # fmt:skip
|
||||
```
|
||||
|
||||
## Ruff Output
|
||||
|
||||
```py
|
||||
a = "this is some code"
|
||||
b = 5 #fmt:skip
|
||||
c = 9 #fmt: skip
|
||||
d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" #fmt:skip
|
||||
b = 5 # fmt:skip
|
||||
c = 9 # fmt: skip
|
||||
d = "thisisasuperlongstringthisisasuperlongstringthisisasuperlongstringthisisasuperlongstring" # fmt:skip
|
||||
```
|
||||
|
||||
## Black Output
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue