Skip to content

Commit

Permalink
Update D208 to preserve indentation offsets when fixing overindente…
Browse files Browse the repository at this point in the history
…d lines (#8699)

Closes #8695

We track the smallest offset seen for overindented lines then only
reduce the indentation of the lines that far to preserve indentation in
other lines. This rule's behavior now matches our formatter, which is
nice.

We may want to gate this with preview.
  • Loading branch information
zanieb committed Nov 17, 2023
1 parent 4c86b15 commit bd99175
Show file tree
Hide file tree
Showing 7 changed files with 561 additions and 7 deletions.
52 changes: 52 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/pydocstyle/D.py
Expand Up @@ -663,3 +663,55 @@ def sort_services(self):
def newline_after_closing_quote(self):
"We enforce a newline after the closing quote for a multi-line docstring \
but continuations shouldn't be considered multi-line"




def retain_extra_whitespace():
"""Summary.
This is overindented
And so is this, but it we should preserve the extra space on this line relative
to the one before
"""


def retain_extra_whitespace_multiple():
"""Summary.
This is overindented
And so is this, but it we should preserve the extra space on this line relative
to the one before
This is also overindented
And so is this, but it we should preserve the extra space on this line relative
to the one before
"""



def retain_extra_whitespace_deeper():
"""Summary.
This is overindented
And so is this, but it we should preserve the extra space on this line relative
to the one before
And the relative indent here should be preserved too
"""

def retain_extra_whitespace_followed_by_same_offset():
"""Summary.
This is overindented
And so is this, but it we should preserve the extra space on this line relative
This is overindented
This is overindented
"""


def retain_extra_whitespace_not_overindented():
"""Summary.
This is not overindented
This is overindented, but since one line is not overindented this should not raise
And so is this, but it we should preserve the extra space on this line relative
"""
26 changes: 19 additions & 7 deletions crates/ruff_linter/src/rules/pydocstyle/rules/indent.rs
Expand Up @@ -3,7 +3,7 @@ use ruff_diagnostics::{Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::docstrings::{clean_space, leading_space};
use ruff_source_file::NewlineWithTrailingNewline;
use ruff_text_size::Ranged;
use ruff_text_size::{Ranged, TextSize};
use ruff_text_size::{TextLen, TextRange};

use crate::checkers::ast::Checker;
Expand Down Expand Up @@ -172,6 +172,7 @@ pub(crate) fn indent(checker: &mut Checker, docstring: &Docstring) {
let mut has_seen_tab = docstring.indentation.contains('\t');
let mut is_over_indented = true;
let mut over_indented_lines = vec![];
let mut over_indented_offset = TextSize::from(u32::MAX);

for i in 0..lines.len() {
// First lines and continuations doesn't need any indentation.
Expand Down Expand Up @@ -217,7 +218,13 @@ pub(crate) fn indent(checker: &mut Checker, docstring: &Docstring) {
// the over-indentation status of every line.
if i < lines.len() - 1 {
if line_indent.len() > docstring.indentation.len() {
over_indented_lines.push(TextRange::at(line.start(), line_indent.text_len()));
over_indented_lines.push(line);

// Track the _smallest_ offset we see
over_indented_offset = std::cmp::min(
line_indent.text_len() - docstring.indentation.text_len(),
over_indented_offset,
);
} else {
is_over_indented = false;
}
Expand All @@ -235,16 +242,21 @@ pub(crate) fn indent(checker: &mut Checker, docstring: &Docstring) {
if checker.enabled(Rule::OverIndentation) {
// If every line (except the last) is over-indented...
if is_over_indented {
for over_indented in over_indented_lines {
for line in over_indented_lines {
let line_indent = leading_space(line);
let indent = clean_space(docstring.indentation);

// We report over-indentation on every line. This isn't great, but
// enables fix.
let mut diagnostic =
Diagnostic::new(OverIndentation, TextRange::empty(over_indented.start()));
let indent = clean_space(docstring.indentation);
Diagnostic::new(OverIndentation, TextRange::empty(line.start()));
let edit = if indent.is_empty() {
Edit::range_deletion(over_indented)
Edit::range_deletion(TextRange::at(line.start(), line_indent.text_len()))
} else {
Edit::range_replacement(indent, over_indented)
Edit::range_replacement(
indent.clone(),
TextRange::at(line.start(), indent.text_len() + over_indented_offset),
)
};
diagnostic.set_fix(Fix::safe_edit(edit));
checker.diagnostics.push(diagnostic);
Expand Down

0 comments on commit bd99175

Please sign in to comment.