Skip to content

Commit 17a44c0

Browse files
cnpryerMichaReiser
andauthored
Exclude pragma comments from measured line width (#7008)
Co-authored-by: Micha Reiser <[email protected]>
1 parent 376d3ca commit 17a44c0

7 files changed

+132
-291
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,29 @@
1+
# Pragma reserved width fixtures
2+
i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # noqa: This shouldn't break
3+
i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # type: This shouldn't break
4+
i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # pyright: This shouldn't break
5+
i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # pylint: This shouldn't break
6+
i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # noqa This shouldn't break
7+
i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # nocoverage: This should break
8+
9+
10+
# Pragma fixtures for non-breaking space (lead by NBSP)
11+
i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # noqa: This shouldn't break
12+
i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # type: This shouldn't break
13+
i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # pyright: This shouldn't break
14+
i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # pylint: This shouldn't break
15+
i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # noqa This shouldn't break
16+
i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # nocoverage: This should break
17+
18+
119
# As of adding this fixture Black adds a space before the non-breaking space if part of a type pragma.
220
# https://github.com/psf/black/blob/b4dca26c7d93f930bbd5a7b552807370b60d4298/src/black/comments.py#L122-L129
3-
i2 = "" #  type: Add space before leading NBSP followed by spaces
4-
i3 = "" #type: A space is added
5-
i4 = "" #  type: Add space before leading NBSP followed by a space
6-
i5 = "" # type: Add space before leading NBSP
21+
i = "" #  type: Add space before leading NBSP followed by spaces
22+
i = "" #type: A space is added
23+
i = "" #  type: Add space before leading NBSP followed by a space
24+
i = "" # type: Add space before leading NBSP
25+
i = "" #  type: Add space before two leading NBSP
26+
27+
28+
# A noqa as `#\u{A0}\u{A0}noqa` becomes `# \u{A0}noqa`
29+
i = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" #  noqa

crates/ruff_python_formatter/src/comments/format.rs

+38-15
Original file line numberDiff line numberDiff line change
@@ -357,17 +357,33 @@ impl Format<PyFormatContext<'_>> for FormatTrailingEndOfLineComment<'_> {
357357

358358
let normalized_comment = normalize_comment(self.comment, source)?;
359359

360-
// Start with 2 because of the two leading spaces.
361-
let mut reserved_width = 2;
362-
363-
// SAFE: The formatted file is <= 4GB, and each comment should as well.
364-
#[allow(clippy::cast_possible_truncation)]
365-
for c in normalized_comment.chars() {
366-
reserved_width += match c {
367-
'\t' => f.options().tab_width().value(),
368-
c => c.width().unwrap_or(0) as u32,
360+
// Trim the normalized comment to detect excluded pragmas (strips NBSP).
361+
let trimmed = strip_comment_prefix(&normalized_comment)?.trim_start();
362+
363+
let is_pragma = if let Some((maybe_pragma, _)) = trimmed.split_once(':') {
364+
matches!(maybe_pragma, "noqa" | "type" | "pyright" | "pylint")
365+
} else {
366+
trimmed.starts_with("noqa")
367+
};
368+
369+
// Don't reserve width for excluded pragma comments.
370+
let reserved_width = if is_pragma {
371+
0
372+
} else {
373+
// Start with 2 because of the two leading spaces.
374+
let mut width = 2;
375+
376+
// SAFETY: The formatted file is <= 4GB, and each comment should as well.
377+
#[allow(clippy::cast_possible_truncation)]
378+
for c in normalized_comment.chars() {
379+
width += match c {
380+
'\t' => f.options().tab_width().value(),
381+
c => c.width().unwrap_or(0) as u32,
382+
}
369383
}
370-
}
384+
385+
width
386+
};
371387

372388
write!(
373389
f,
@@ -442,11 +458,7 @@ fn normalize_comment<'a>(
442458

443459
let trimmed = comment_text.trim_end();
444460

445-
let Some(content) = trimmed.strip_prefix('#') else {
446-
return Err(FormatError::syntax_error(
447-
"Didn't find expected comment token `#`",
448-
));
449-
};
461+
let content = strip_comment_prefix(trimmed)?;
450462

451463
if content.is_empty() {
452464
return Ok(Cow::Borrowed("#"));
@@ -476,6 +488,17 @@ fn normalize_comment<'a>(
476488
Ok(Cow::Owned(std::format!("# {}", content.trim_start())))
477489
}
478490

491+
/// A helper for stripping '#' from comments.
492+
fn strip_comment_prefix(comment_text: &str) -> FormatResult<&str> {
493+
let Some(content) = comment_text.strip_prefix('#') else {
494+
return Err(FormatError::syntax_error(
495+
"Didn't find expected comment token `#`",
496+
));
497+
};
498+
499+
Ok(content)
500+
}
501+
479502
/// Format the empty lines between a node and its trailing comments.
480503
///
481504
/// For example, given:

crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__comments6.py.snap

+3-8
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*ite
156156
)
157157
158158
159-
@@ -108,11 +112,20 @@
159+
@@ -108,11 +112,18 @@
160160
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
161161
)
162162
@@ -176,10 +176,7 @@ aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*ite
176176
+ ], # type: ignore
177177
)
178178
179-
-aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*items))) # type: ignore[arg-type]
180-
+aaaaaaaaaaaaa, bbbbbbbbb = map(
181-
+ list, map(itertools.chain.from_iterable, zip(*items))
182-
+) # type: ignore[arg-type]
179+
aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*items))) # type: ignore[arg-type]
183180
```
184181

185182
## Ruff Output
@@ -313,9 +310,7 @@ call_to_some_function_asdf(
313310
], # type: ignore
314311
)
315312
316-
aaaaaaaaaaaaa, bbbbbbbbb = map(
317-
list, map(itertools.chain.from_iterable, zip(*items))
318-
) # type: ignore[arg-type]
313+
aaaaaaaaaaaaa, bbbbbbbbb = map(list, map(itertools.chain.from_iterable, zip(*items))) # type: ignore[arg-type]
319314
```
320315

321316
## Black Output

crates/ruff_python_formatter/tests/snapshots/black_compatibility@simple_cases__expression.py.snap

+1-14
Original file line numberDiff line numberDiff line change
@@ -300,17 +300,6 @@ last_call()
300300
) # note: no trailing comma pre-3.6
301301
call(*gidgets[:2])
302302
call(a, *gidgets[:2])
303-
@@ -142,7 +143,9 @@
304-
xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore
305-
sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)
306-
)
307-
-xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore
308-
+xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[
309-
+ ..., List[SomeClass]
310-
+] = classmethod( # type: ignore
311-
sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)
312-
)
313-
xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod(
314303
```
315304
316305
## Ruff Output
@@ -461,9 +450,7 @@ very_long_variable_name_filters: t.List[
461450
xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore
462451
sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)
463452
)
464-
xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[
465-
..., List[SomeClass]
466-
] = classmethod( # type: ignore
453+
xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore
467454
sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)
468455
)
469456
xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod(

0 commit comments

Comments
 (0)