Skip to content

Commit

Permalink
Implement an iterator for universal newlines
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Mar 12, 2023
1 parent 12a6fc7 commit ff35877
Show file tree
Hide file tree
Showing 31 changed files with 281 additions and 87 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions crates/ruff/src/autofix/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use ruff_diagnostics::Fix;
use ruff_python_ast::helpers;
use ruff_python_ast::helpers::to_absolute;
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
use ruff_python_ast::whitespace::LinesWithTrailingNewline;
use ruff_python_ast::whitespace::NewlineWithTrailingNewline;

use crate::cst::helpers::compose_module_path;
use crate::cst::matchers::match_module;
Expand Down Expand Up @@ -100,7 +100,7 @@ fn is_lone_child(child: &Stmt, parent: &Stmt, deleted: &[&Stmt]) -> Result<bool>
/// of a multi-statement line.
fn trailing_semicolon(stmt: &Stmt, locator: &Locator) -> Option<Location> {
let contents = locator.skip(stmt.end_location.unwrap());
for (row, line) in LinesWithTrailingNewline::from(contents).enumerate() {
for (row, line) in NewlineWithTrailingNewline::from(contents).enumerate() {
let trimmed = line.trim();
if trimmed.starts_with(';') {
let column = line
Expand All @@ -123,7 +123,7 @@ fn trailing_semicolon(stmt: &Stmt, locator: &Locator) -> Option<Location> {
fn next_stmt_break(semicolon: Location, locator: &Locator) -> Location {
let start_location = Location::new(semicolon.row(), semicolon.column() + 1);
let contents = locator.skip(start_location);
for (row, line) in LinesWithTrailingNewline::from(contents).enumerate() {
for (row, line) in NewlineWithTrailingNewline::from(contents).enumerate() {
let trimmed = line.trim();
// Skip past any continuations.
if trimmed.starts_with('\\') {
Expand Down
3 changes: 2 additions & 1 deletion crates/ruff/src/checkers/noqa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use rustpython_parser::ast::Location;

use ruff_diagnostics::{Diagnostic, Fix};
use ruff_python_ast::types::Range;
use ruff_python_ast::whitespace::UniversalNewlineIterator;

use crate::codes::NoqaCode;
use crate::noqa;
Expand Down Expand Up @@ -38,7 +39,7 @@ pub fn check_noqa(
// Indices of diagnostics that were ignored by a `noqa` directive.
let mut ignored_diagnostics = vec![];

let lines: Vec<&str> = contents.lines().collect();
let lines: Vec<&str> = UniversalNewlineIterator::from(contents).collect();
for lineno in commented_lines {
match extract_file_exemption(lines[lineno - 1]) {
Exemption::All => {
Expand Down
3 changes: 2 additions & 1 deletion crates/ruff/src/checkers/physical_lines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::path::Path;

use ruff_diagnostics::Diagnostic;
use ruff_python_ast::source_code::Stylist;
use ruff_python_ast::whitespace::UniversalNewlineIterator;

use crate::registry::Rule;
use crate::rules::flake8_executable::helpers::{extract_shebang, ShebangDirective};
Expand Down Expand Up @@ -56,7 +57,7 @@ pub fn check_physical_lines(

let mut commented_lines_iter = commented_lines.iter().peekable();
let mut doc_lines_iter = doc_lines.iter().peekable();
for (index, line) in contents.lines().enumerate() {
for (index, line) in UniversalNewlineIterator::from(contents).enumerate() {
while commented_lines_iter
.next_if(|lineno| &(index + 1) == *lineno)
.is_some()
Expand Down
5 changes: 3 additions & 2 deletions crates/ruff/src/noqa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use rustpython_parser::ast::Location;
use ruff_diagnostics::Diagnostic;
use ruff_python_ast::source_code::{LineEnding, Locator};
use ruff_python_ast::types::Range;
use ruff_python_ast::whitespace::UniversalNewlineIterator;

use crate::codes::NoqaCode;
use crate::registry::{AsRule, Rule};
Expand Down Expand Up @@ -181,7 +182,7 @@ fn add_noqa_inner(
// Codes that are globally exempted (within the current file).
let mut file_exemptions: Vec<NoqaCode> = vec![];

let lines: Vec<&str> = contents.lines().collect();
let lines: Vec<&str> = UniversalNewlineIterator::from(contents).collect();
for lineno in commented_lines {
match extract_file_exemption(lines[lineno - 1]) {
Exemption::All => {
Expand Down Expand Up @@ -263,7 +264,7 @@ fn add_noqa_inner(

let mut count: usize = 0;
let mut output = String::new();
for (lineno, line) in contents.lines().enumerate() {
for (lineno, line) in lines.into_iter().enumerate() {
match matches_by_line.get(&lineno) {
None => {
output.push_str(line);
Expand Down
5 changes: 2 additions & 3 deletions crates/ruff/src/rules/flake8_simplify/rules/ast_if.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use ruff_python_ast::helpers::{
has_comments_in, unparse_expr, unparse_stmt,
};
use ruff_python_ast::types::Range;
use ruff_python_ast::whitespace::UniversalNewlineIterator;

use crate::checkers::ast::Checker;
use crate::registry::AsRule;
Expand Down Expand Up @@ -281,9 +282,7 @@ pub fn nested_if_statements(
if fixable && checker.patch(diagnostic.kind.rule()) {
match fix_if::fix_nested_if_statements(checker.locator, checker.stylist, stmt) {
Ok(fix) => {
if fix
.content
.lines()
if UniversalNewlineIterator::from(&fix.content)
.all(|line| line.len() <= checker.settings.line_length)
{
diagnostic.amend(fix);
Expand Down
5 changes: 2 additions & 3 deletions crates/ruff/src/rules/flake8_simplify/rules/ast_with.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use ruff_diagnostics::{AutofixKind, Availability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::{first_colon_range, has_comments_in};
use ruff_python_ast::types::Range;
use ruff_python_ast::whitespace::UniversalNewlineIterator;

use crate::checkers::ast::Checker;
use crate::registry::AsRule;
Expand Down Expand Up @@ -113,9 +114,7 @@ pub fn multiple_with_statements(
with_stmt,
) {
Ok(fix) => {
if fix
.content
.lines()
if UniversalNewlineIterator::from(&fix.content)
.all(|line| line.len() <= checker.settings.line_length)
{
diagnostic.amend(fix);
Expand Down
3 changes: 2 additions & 1 deletion crates/ruff/src/rules/isort/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use rustpython_parser::{lexer, Mode, Tok};

use ruff_python_ast::helpers::is_docstring_stmt;
use ruff_python_ast::source_code::Locator;
use ruff_python_ast::whitespace::UniversalNewlineIterator;

use super::types::TrailingComma;

Expand Down Expand Up @@ -62,7 +63,7 @@ pub fn has_comment_break(stmt: &Stmt, locator: &Locator) -> bool {
// # Direct comment.
// def f(): pass
let mut seen_blank = false;
for line in locator.take(stmt.location).lines().rev() {
for line in UniversalNewlineIterator::from(locator.take(stmt.location)).rev() {
let line = line.trim();
if seen_blank {
if line.starts_with('#') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::source_code::Locator;
use ruff_python_ast::types::Range;
use ruff_python_ast::whitespace::UniversalNewlineIterator;

/// ## What it does
/// Checks for invalid escape sequences.
Expand Down Expand Up @@ -76,7 +77,7 @@ pub fn invalid_escape_sequence(
let body = &text[(quote_pos + quote.len())..(text.len() - quote.len())];

if !prefix.contains('r') {
for (row_offset, line) in body.lines().enumerate() {
for (row_offset, line) in UniversalNewlineIterator::from(body).enumerate() {
let chars: Vec<char> = line.chars().collect();
for col_offset in 0..chars.len() {
if chars[col_offset] != '\\' {
Expand Down
8 changes: 4 additions & 4 deletions crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::{match_leading_content, match_trailing_content, unparse_stmt};
use ruff_python_ast::source_code::Stylist;
use ruff_python_ast::types::{Range, ScopeKind};
use ruff_python_ast::whitespace::leading_space;
use ruff_python_ast::whitespace::{leading_space, UniversalNewlineIterator};

use crate::checkers::ast::Checker;
use crate::registry::AsRule;
Expand Down Expand Up @@ -85,9 +85,9 @@ pub fn lambda_assignment(checker: &mut Checker, target: &Expr, value: &Expr, stm
));
let indentation = &leading_space(first_line);
let mut indented = String::new();
for (idx, line) in function(id, args, body, checker.stylist)
.lines()
.enumerate()
for (idx, line) in
UniversalNewlineIterator::from(&function(id, args, body, checker.stylist))
.enumerate()
{
if idx == 0 {
indented.push_str(line);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::source_code::Stylist;
use ruff_python_ast::types::Range;
use ruff_python_ast::whitespace::UniversalNewlineIterator;

/// ## What it does
/// Checks for files missing a new line at the end of the file.
Expand Down Expand Up @@ -41,12 +42,13 @@ pub fn no_newline_at_end_of_file(
contents: &str,
autofix: bool,
) -> Option<Diagnostic> {
if !contents.ends_with('\n') {
if !contents.ends_with(['\n', '\r']) {
// Note: if `lines.last()` is `None`, then `contents` is empty (and so we don't
// want to raise W292 anyway).
if let Some(line) = contents.lines().last() {
if let Some(line) = UniversalNewlineIterator::from(contents).last() {
// Both locations are at the end of the file (and thus the same).
let location = Location::new(contents.lines().count(), line.len());
let location =
Location::new(UniversalNewlineIterator::from(contents).count(), line.len());
let mut diagnostic =
Diagnostic::new(NoNewLineAtEndOfFile, Range::new(location, location));
if autofix {
Expand Down
3 changes: 2 additions & 1 deletion crates/ruff/src/rules/pydocstyle/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::collections::BTreeSet;

use ruff_python_ast::cast;
use ruff_python_ast::helpers::{map_callable, to_call_path};
use ruff_python_ast::whitespace::UniversalNewlineIterator;

use crate::checkers::ast::Checker;
use crate::docstrings::definition::{Definition, DefinitionKind};
Expand All @@ -10,7 +11,7 @@ use crate::docstrings::definition::{Definition, DefinitionKind};
pub fn logical_line(content: &str) -> Option<usize> {
// Find the first logical line.
let mut logical_line = None;
for (i, line) in content.lines().enumerate() {
for (i, line) in UniversalNewlineIterator::from(content).enumerate() {
if line.trim().is_empty() {
// Empty line. If this is the line _after_ the first logical line, stop.
if logical_line.is_some() {
Expand Down
5 changes: 3 additions & 2 deletions crates/ruff/src/rules/pydocstyle/rules/blank_after_summary.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use ruff_diagnostics::{AutofixKind, Availability, Diagnostic, Fix, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::types::Range;
use ruff_python_ast::whitespace::UniversalNewlineIterator;

use crate::checkers::ast::Checker;
use crate::docstrings::definition::Docstring;
Expand Down Expand Up @@ -45,7 +46,7 @@ pub fn blank_after_summary(checker: &mut Checker, docstring: &Docstring) {

let mut lines_count = 1;
let mut blanks_count = 0;
for line in body.trim().lines().skip(1) {
for line in UniversalNewlineIterator::from(body.trim()).skip(1) {
lines_count += 1;
if line.trim().is_empty() {
blanks_count += 1;
Expand All @@ -64,7 +65,7 @@ pub fn blank_after_summary(checker: &mut Checker, docstring: &Docstring) {
if blanks_count > 1 {
// Find the "summary" line (defined as the first non-blank line).
let mut summary_line = 0;
for line in body.lines() {
for line in UniversalNewlineIterator::from(body) {
if line.trim().is_empty() {
summary_line += 1;
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::types::Range;
use ruff_python_ast::whitespace::UniversalNewlineIterator;

use crate::checkers::ast::Checker;
use crate::docstrings::definition::{DefinitionKind, Docstring};
Expand Down Expand Up @@ -74,8 +75,7 @@ pub fn blank_before_after_class(checker: &mut Checker, docstring: &Docstring) {
.locator
.slice(Range::new(parent.location, docstring.expr.location));

let blank_lines_before = before
.lines()
let blank_lines_before = UniversalNewlineIterator::from(before)
.rev()
.skip(1)
.take_while(|line| line.trim().is_empty())
Expand Down Expand Up @@ -137,16 +137,14 @@ pub fn blank_before_after_class(checker: &mut Checker, docstring: &Docstring) {
parent.end_location.unwrap(),
));

let all_blank_after = after
.lines()
let all_blank_after = UniversalNewlineIterator::from(after)
.skip(1)
.all(|line| line.trim().is_empty() || line.trim_start().starts_with('#'));
if all_blank_after {
return;
}

let blank_lines_after = after
.lines()
let blank_lines_after = UniversalNewlineIterator::from(after)
.skip(1)
.take_while(|line| line.trim().is_empty())
.count();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use regex::Regex;
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::types::Range;
use ruff_python_ast::whitespace::UniversalNewlineIterator;

use crate::checkers::ast::Checker;
use crate::docstrings::definition::{DefinitionKind, Docstring};
Expand Down Expand Up @@ -66,8 +67,7 @@ pub fn blank_before_after_function(checker: &mut Checker, docstring: &Docstring)
.locator
.slice(Range::new(parent.location, docstring.expr.location));

let blank_lines_before = before
.lines()
let blank_lines_before = UniversalNewlineIterator::from(before)
.rev()
.skip(1)
.take_while(|line| line.trim().is_empty())
Expand Down Expand Up @@ -101,25 +101,22 @@ pub fn blank_before_after_function(checker: &mut Checker, docstring: &Docstring)
));

// If the docstring is only followed by blank and commented lines, abort.
let all_blank_after = after
.lines()
let all_blank_after = UniversalNewlineIterator::from(after)
.skip(1)
.all(|line| line.trim().is_empty() || line.trim_start().starts_with('#'));
if all_blank_after {
return;
}

// Count the number of blank lines after the docstring.
let blank_lines_after = after
.lines()
let blank_lines_after = UniversalNewlineIterator::from(after)
.skip(1)
.take_while(|line| line.trim().is_empty())
.count();

// Avoid violations for blank lines followed by inner functions or classes.
if blank_lines_after == 1
&& after
.lines()
&& UniversalNewlineIterator::from(after)
.skip(1 + blank_lines_after)
.find(|line| !line.trim_start().starts_with('#'))
.map_or(false, |line| INNER_FUNCTION_OR_CLASS_REGEX.is_match(line))
Expand Down
5 changes: 3 additions & 2 deletions crates/ruff/src/rules/pydocstyle/rules/ends_with_period.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::str::leading_quote;
use ruff_python_ast::types::Range;
use ruff_python_ast::whitespace::UniversalNewlineIterator;

use crate::checkers::ast::Checker;
use crate::docstrings::definition::Docstring;
Expand Down Expand Up @@ -31,7 +32,7 @@ pub fn ends_with_period(checker: &mut Checker, docstring: &Docstring) {
let contents = docstring.contents;
let body = docstring.body;

if let Some(first_line) = body.trim().lines().next() {
if let Some(first_line) = UniversalNewlineIterator::from(body.trim()).next() {
let trimmed = first_line.trim();

// Avoid false-positives: `:param`, etc.
Expand All @@ -55,7 +56,7 @@ pub fn ends_with_period(checker: &mut Checker, docstring: &Docstring) {
}

if let Some(index) = logical_line(body) {
let line = body.lines().nth(index).unwrap();
let line = UniversalNewlineIterator::from(body).nth(index).unwrap();
let trimmed = line.trim_end();

if !trimmed.ends_with('.') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::str::leading_quote;
use ruff_python_ast::types::Range;
use ruff_python_ast::whitespace::UniversalNewlineIterator;

use crate::checkers::ast::Checker;
use crate::docstrings::definition::Docstring;
Expand Down Expand Up @@ -31,7 +32,7 @@ pub fn ends_with_punctuation(checker: &mut Checker, docstring: &Docstring) {
let contents = docstring.contents;
let body = docstring.body;

if let Some(first_line) = body.trim().lines().next() {
if let Some(first_line) = UniversalNewlineIterator::from(body.trim()).next() {
let trimmed = first_line.trim();

// Avoid false-positives: `:param`, etc.
Expand All @@ -55,7 +56,7 @@ pub fn ends_with_punctuation(checker: &mut Checker, docstring: &Docstring) {
}

if let Some(index) = logical_line(body) {
let line = body.lines().nth(index).unwrap();
let line = UniversalNewlineIterator::from(body).nth(index).unwrap();
let trimmed = line.trim_end();
if !(trimmed.ends_with('.') || trimmed.ends_with('!') || trimmed.ends_with('?')) {
let mut diagnostic = Diagnostic::new(EndsInPunctuation, Range::from(docstring.expr));
Expand Down
Loading

0 comments on commit ff35877

Please sign in to comment.