diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/trailing_comments.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/trailing_comments.py index 86c33a0f4ed79..3e7002456cf6f 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/trailing_comments.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/trailing_comments.py @@ -1,5 +1,6 @@ # Pragma reserved width fixtures i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # noqa: This shouldn't break +i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # NoQA: This shouldn't break i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # type: This shouldn't break i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # pyright: This shouldn't break i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # pylint: This shouldn't break @@ -9,6 +10,7 @@ # Pragma fixtures for non-breaking space (lead by NBSP) i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # noqa: This shouldn't break +i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # NoQA: This shouldn't break i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # type: This shouldn't break i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # pyright: This shouldn't break i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # pylint: This shouldn't break diff --git a/crates/ruff_python_formatter/src/comments/format.rs b/crates/ruff_python_formatter/src/comments/format.rs index ac41d1dcebfb6..7be2fe3a9792f 100644 --- a/crates/ruff_python_formatter/src/comments/format.rs +++ b/crates/ruff_python_formatter/src/comments/format.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use ruff_formatter::{format_args, write, FormatError, FormatOptions, SourceCode}; use ruff_python_ast::node::{AnyNodeRef, AstNode}; use ruff_python_ast::PySourceType; -use ruff_python_trivia::{lines_after, lines_after_ignoring_trivia, lines_before}; +use ruff_python_trivia::{is_pragma, lines_after, lines_after_ignoring_trivia, lines_before}; use ruff_text_size::{Ranged, TextLen, TextRange}; use crate::comments::{CommentLinePosition, SourceComment}; @@ -370,17 +370,8 @@ impl Format> for FormatTrailingEndOfLineComment<'_> { let normalized_comment = normalize_comment(self.comment, source)?; - // Trim the normalized comment to detect excluded pragmas (strips NBSP). - let trimmed = strip_comment_prefix(&normalized_comment)?.trim_start(); - - let is_pragma = if let Some((maybe_pragma, _)) = trimmed.split_once(':') { - matches!(maybe_pragma, "noqa" | "type" | "pyright" | "pylint") - } else { - trimmed.starts_with("noqa") - }; - // Don't reserve width for excluded pragma comments. - let reserved_width = if is_pragma { + let reserved_width = if is_pragma(&normalized_comment) { 0 } else { // Start with 2 because of the two leading spaces. diff --git a/crates/ruff_python_formatter/tests/snapshots/format@trailing_comments.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@trailing_comments.py.snap index 7e6a4bdc161e4..50c7ab6296719 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@trailing_comments.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@trailing_comments.py.snap @@ -6,6 +6,7 @@ input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/trailing_c ```py # Pragma reserved width fixtures i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # noqa: This shouldn't break +i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # NoQA: This shouldn't break i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # type: This shouldn't break i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # pyright: This shouldn't break i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # pylint: This shouldn't break @@ -15,6 +16,7 @@ i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # nocover # Pragma fixtures for non-breaking space (lead by NBSP) i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # noqa: This shouldn't break +i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # NoQA: This shouldn't break i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # type: This shouldn't break i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # pyright: This shouldn't break i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # pylint: This shouldn't break @@ -39,6 +41,7 @@ i = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ```py # Pragma reserved width fixtures i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # noqa: This shouldn't break +i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # NoQA: This shouldn't break i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # type: This shouldn't break i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # pyright: This shouldn't break i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # pylint: This shouldn't break @@ -50,6 +53,7 @@ i = ( # Pragma fixtures for non-breaking space (lead by NBSP) i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # noqa: This shouldn't break +i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # NoQA: This shouldn't break i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) #  type: This shouldn't break i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # pyright: This shouldn't break i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # pylint: This shouldn't break diff --git a/crates/ruff_python_trivia/src/lib.rs b/crates/ruff_python_trivia/src/lib.rs index 9c9bb8158c74c..9fed887c17ff4 100644 --- a/crates/ruff_python_trivia/src/lib.rs +++ b/crates/ruff_python_trivia/src/lib.rs @@ -1,10 +1,12 @@ mod comment_ranges; mod cursor; +mod pragmas; pub mod textwrap; mod tokenizer; mod whitespace; pub use comment_ranges::CommentRanges; pub use cursor::*; +pub use pragmas::*; pub use tokenizer::*; pub use whitespace::*; diff --git a/crates/ruff_python_trivia/src/pragmas.rs b/crates/ruff_python_trivia/src/pragmas.rs new file mode 100644 index 0000000000000..1c672c25e3ebf --- /dev/null +++ b/crates/ruff_python_trivia/src/pragmas.rs @@ -0,0 +1,30 @@ +/// Returns `true` if a comment appears to be a pragma comment. +/// +/// ``` +/// assert!(ruff_python_trivia::is_pragma("# type: ignore")); +/// assert!(ruff_python_trivia::is_pragma("# noqa: F401")); +/// assert!(ruff_python_trivia::is_pragma("# noqa")); +/// assert!(ruff_python_trivia::is_pragma("# NoQA")); +/// assert!(ruff_python_trivia::is_pragma("# nosec")); +/// assert!(ruff_python_trivia::is_pragma("# nosec B602, B607")); +/// assert!(ruff_python_trivia::is_pragma("# isort: off")); +/// assert!(ruff_python_trivia::is_pragma("# isort: skip")); +/// ``` +pub fn is_pragma(comment: &str) -> bool { + let Some(content) = comment.strip_prefix('#') else { + return false; + }; + let trimmed = content.trim_start(); + + // Case-insensitive match against `noqa` (which doesn't require a trailing colon). + matches!( + trimmed.as_bytes(), + [b'n' | b'N', b'o' | b'O', b'q' | b'Q', b'a' | b'A', ..] + ) || + // Case-insensitive match against pragmas that don't require a trailing colon. + trimmed.starts_with("nosec") || + // Case-sensitive match against a variety of pragmas (which do require a trailing colon). + trimmed + .split_once(':') + .is_some_and(|(maybe_pragma, _)| matches!(maybe_pragma, "isort" | "type" | "pyright" | "pylint" | "flake8" | "ruff")) +}