diff --git a/crates/ruff_linter/resources/test/fixtures/pydocstyle/D208.py b/crates/ruff_linter/resources/test/fixtures/pydocstyle/D208.py new file mode 100644 index 0000000000000..f0515248abb5c --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/pydocstyle/D208.py @@ -0,0 +1,5 @@ +class Platform: + """ Remove sampler + Args: +     Returns: + """ diff --git a/crates/ruff_linter/src/rules/pydocstyle/mod.rs b/crates/ruff_linter/src/rules/pydocstyle/mod.rs index 205d16abd615f..5c08ee88dba7b 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/mod.rs +++ b/crates/ruff_linter/src/rules/pydocstyle/mod.rs @@ -46,6 +46,7 @@ mod tests { #[test_case(Rule::NoBlankLineBeforeFunction, Path::new("D.py"))] #[test_case(Rule::BlankLinesBetweenHeaderAndContent, Path::new("sections.py"))] #[test_case(Rule::OverIndentation, Path::new("D.py"))] + #[test_case(Rule::OverIndentation, Path::new("D208.py"))] #[test_case(Rule::NoSignature, Path::new("D.py"))] #[test_case(Rule::SurroundingWhitespace, Path::new("D.py"))] #[test_case(Rule::DocstringStartsWithThis, Path::new("D.py"))] diff --git a/crates/ruff_linter/src/rules/pydocstyle/rules/indent.rs b/crates/ruff_linter/src/rules/pydocstyle/rules/indent.rs index 497dd816e1367..f56bc1bed47b2 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/rules/indent.rs +++ b/crates/ruff_linter/src/rules/pydocstyle/rules/indent.rs @@ -172,7 +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); + let mut over_indented_offset = usize::MAX; for i in 0..lines.len() { // First lines and continuations doesn't need any indentation. @@ -220,9 +220,9 @@ pub(crate) fn indent(checker: &mut Checker, docstring: &Docstring) { if line_indent.len() > docstring.indentation.len() { over_indented_lines.push(line); - // Track the _smallest_ offset we see + // Track the _smallest_ offset we see, in terms of characters. over_indented_offset = std::cmp::min( - line_indent.text_len() - docstring.indentation.text_len(), + line_indent.chars().count() - docstring.indentation.chars().count(), over_indented_offset, ); } else { @@ -247,15 +247,23 @@ pub(crate) fn indent(checker: &mut Checker, docstring: &Docstring) { let indent = clean_space(docstring.indentation); // We report over-indentation on every line. This isn't great, but - // enables fix. + // enables the fix capability. let mut diagnostic = Diagnostic::new(OverIndentation, TextRange::empty(line.start())); let edit = if indent.is_empty() { - Edit::range_deletion(TextRange::at(line.start(), line_indent.text_len())) + Edit::deletion(line.start(), line_indent.text_len()) } else { + // Convert the character count to an offset within the source. + let offset = checker + .locator() + .after(line.start() + indent.text_len()) + .chars() + .take(over_indented_offset) + .map(TextLen::text_len) + .sum::(); Edit::range_replacement( indent.clone(), - TextRange::at(line.start(), indent.text_len() + over_indented_offset), + TextRange::at(line.start(), indent.text_len() + offset), ) }; diagnostic.set_fix(Fix::safe_edit(edit)); diff --git a/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__D208_D208.py.snap b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__D208_D208.py.snap new file mode 100644 index 0000000000000..ca8b68bcbeee5 --- /dev/null +++ b/crates/ruff_linter/src/rules/pydocstyle/snapshots/ruff_linter__rules__pydocstyle__tests__D208_D208.py.snap @@ -0,0 +1,57 @@ +--- +source: crates/ruff_linter/src/rules/pydocstyle/mod.rs +--- +D208.py:3:1: D208 [*] Docstring is over-indented + | +1 | class Platform: +2 | """ Remove sampler +3 | Args: + | D208 +4 |     Returns: +5 | """ + | + = help: Remove over-indentation + +ℹ Safe fix +1 1 | class Platform: +2 2 | """ Remove sampler +3 |- Args: + 3 |+ Args: +4 4 |     Returns: +5 5 | """ + +D208.py:4:1: D208 [*] Docstring is over-indented + | +2 | """ Remove sampler +3 | Args: +4 |     Returns: + | D208 +5 | """ + | + = help: Remove over-indentation + +ℹ Safe fix +1 1 | class Platform: +2 2 | """ Remove sampler +3 3 | Args: +4 |-     Returns: + 4 |+ Returns: +5 5 | """ + +D208.py:5:1: D208 [*] Docstring is over-indented + | +3 | Args: +4 |     Returns: +5 | """ + | D208 + | + = help: Remove over-indentation + +ℹ Safe fix +2 2 | """ Remove sampler +3 3 | Args: +4 4 |     Returns: +5 |- """ + 5 |+ """ + +