Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/pydocstyle/D.py
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading