[pydocstyle] Avoid non-character breaks in over-indentation (D208) (#8866)

Closes https://github.com/astral-sh/ruff/issues/8844.
This commit is contained in:
Charlie Marsh 2023-11-27 21:47:35 -08:00 committed by GitHub
parent 60eb11fa50
commit ed14fd9163
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 77 additions and 6 deletions

View file

@ -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"))]

View file

@ -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::<TextSize>();
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));

View file

@ -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 |+ """