Skip to content

Commit bd99175

Browse files
authored
Update D208 to preserve indentation offsets when fixing overindented 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.
1 parent 4c86b15 commit bd99175

7 files changed

+561
-7
lines changed

crates/ruff_linter/resources/test/fixtures/pydocstyle/D.py

+52
Original file line numberDiff line numberDiff line change
@@ -663,3 +663,55 @@ def sort_services(self):
663663
def newline_after_closing_quote(self):
664664
"We enforce a newline after the closing quote for a multi-line docstring \
665665
but continuations shouldn't be considered multi-line"
666+
667+
668+
669+
670+
def retain_extra_whitespace():
671+
"""Summary.
672+
673+
This is overindented
674+
And so is this, but it we should preserve the extra space on this line relative
675+
to the one before
676+
"""
677+
678+
679+
def retain_extra_whitespace_multiple():
680+
"""Summary.
681+
682+
This is overindented
683+
And so is this, but it we should preserve the extra space on this line relative
684+
to the one before
685+
This is also overindented
686+
And so is this, but it we should preserve the extra space on this line relative
687+
to the one before
688+
"""
689+
690+
691+
692+
def retain_extra_whitespace_deeper():
693+
"""Summary.
694+
695+
This is overindented
696+
And so is this, but it we should preserve the extra space on this line relative
697+
to the one before
698+
And the relative indent here should be preserved too
699+
"""
700+
701+
def retain_extra_whitespace_followed_by_same_offset():
702+
"""Summary.
703+
704+
This is overindented
705+
And so is this, but it we should preserve the extra space on this line relative
706+
This is overindented
707+
This is overindented
708+
"""
709+
710+
711+
def retain_extra_whitespace_not_overindented():
712+
"""Summary.
713+
714+
This is not overindented
715+
This is overindented, but since one line is not overindented this should not raise
716+
And so is this, but it we should preserve the extra space on this line relative
717+
"""

crates/ruff_linter/src/rules/pydocstyle/rules/indent.rs

+19-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use ruff_diagnostics::{Diagnostic, Edit, Fix};
33
use ruff_macros::{derive_message_formats, violation};
44
use ruff_python_ast::docstrings::{clean_space, leading_space};
55
use ruff_source_file::NewlineWithTrailingNewline;
6-
use ruff_text_size::Ranged;
6+
use ruff_text_size::{Ranged, TextSize};
77
use ruff_text_size::{TextLen, TextRange};
88

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

176177
for i in 0..lines.len() {
177178
// First lines and continuations doesn't need any indentation.
@@ -217,7 +218,13 @@ pub(crate) fn indent(checker: &mut Checker, docstring: &Docstring) {
217218
// the over-indentation status of every line.
218219
if i < lines.len() - 1 {
219220
if line_indent.len() > docstring.indentation.len() {
220-
over_indented_lines.push(TextRange::at(line.start(), line_indent.text_len()));
221+
over_indented_lines.push(line);
222+
223+
// Track the _smallest_ offset we see
224+
over_indented_offset = std::cmp::min(
225+
line_indent.text_len() - docstring.indentation.text_len(),
226+
over_indented_offset,
227+
);
221228
} else {
222229
is_over_indented = false;
223230
}
@@ -235,16 +242,21 @@ pub(crate) fn indent(checker: &mut Checker, docstring: &Docstring) {
235242
if checker.enabled(Rule::OverIndentation) {
236243
// If every line (except the last) is over-indented...
237244
if is_over_indented {
238-
for over_indented in over_indented_lines {
245+
for line in over_indented_lines {
246+
let line_indent = leading_space(line);
247+
let indent = clean_space(docstring.indentation);
248+
239249
// We report over-indentation on every line. This isn't great, but
240250
// enables fix.
241251
let mut diagnostic =
242-
Diagnostic::new(OverIndentation, TextRange::empty(over_indented.start()));
243-
let indent = clean_space(docstring.indentation);
252+
Diagnostic::new(OverIndentation, TextRange::empty(line.start()));
244253
let edit = if indent.is_empty() {
245-
Edit::range_deletion(over_indented)
254+
Edit::range_deletion(TextRange::at(line.start(), line_indent.text_len()))
246255
} else {
247-
Edit::range_replacement(indent, over_indented)
256+
Edit::range_replacement(
257+
indent.clone(),
258+
TextRange::at(line.start(), indent.text_len() + over_indented_offset),
259+
)
248260
};
249261
diagnostic.set_fix(Fix::safe_edit(edit));
250262
checker.diagnostics.push(diagnostic);

0 commit comments

Comments
 (0)