mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 20:10:09 +00:00
Enable autofix for over- and under-indented docstrings (#451)
This commit is contained in:
parent
f832f88c75
commit
36fe8b76d4
6 changed files with 230 additions and 91 deletions
|
@ -296,8 +296,8 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com
|
||||||
| D204 | OneBlankLineAfterClass | 1 blank line required after class docstring | 🛠 |
|
| D204 | OneBlankLineAfterClass | 1 blank line required after class docstring | 🛠 |
|
||||||
| D205 | BlankLineAfterSummary | 1 blank line required between summary line and description | 🛠 |
|
| D205 | BlankLineAfterSummary | 1 blank line required between summary line and description | 🛠 |
|
||||||
| D206 | IndentWithSpaces | Docstring should be indented with spaces, not tabs | |
|
| D206 | IndentWithSpaces | Docstring should be indented with spaces, not tabs | |
|
||||||
| D207 | NoUnderIndentation | Docstring is under-indented | |
|
| D207 | NoUnderIndentation | Docstring is under-indented | 🛠 |
|
||||||
| D208 | NoOverIndentation | Docstring is over-indented | |
|
| D208 | NoOverIndentation | Docstring is over-indented | 🛠 |
|
||||||
| D209 | NewLineAfterLastParagraph | Multi-line docstring closing quotes should be on a separate line | 🛠 |
|
| D209 | NewLineAfterLastParagraph | Multi-line docstring closing quotes should be on a separate line | 🛠 |
|
||||||
| D210 | NoSurroundingWhitespace | No whitespaces allowed surrounding docstring text | 🛠 |
|
| D210 | NoSurroundingWhitespace | No whitespaces allowed surrounding docstring text | 🛠 |
|
||||||
| D211 | NoBlankLineBeforeClass | No blank lines allowed before class docstring | 🛠 |
|
| D211 | NoBlankLineBeforeClass | No blank lines allowed before class docstring | 🛠 |
|
||||||
|
@ -399,7 +399,6 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com
|
||||||
| ---- | ---- | ------- | --- |
|
| ---- | ---- | ------- | --- |
|
||||||
| M001 | UnusedNOQA | Unused `noqa` directive | 🛠 |
|
| M001 | UnusedNOQA | Unused `noqa` directive | 🛠 |
|
||||||
|
|
||||||
|
|
||||||
## Editor Integrations
|
## Editor Integrations
|
||||||
|
|
||||||
### PyCharm
|
### PyCharm
|
||||||
|
|
|
@ -1219,7 +1219,9 @@ impl CheckKind {
|
||||||
| CheckKind::NoBlankLineBeforeClass(_)
|
| CheckKind::NoBlankLineBeforeClass(_)
|
||||||
| CheckKind::NoBlankLineBeforeFunction(_)
|
| CheckKind::NoBlankLineBeforeFunction(_)
|
||||||
| CheckKind::NoBlankLinesBetweenHeaderAndContent(_)
|
| CheckKind::NoBlankLinesBetweenHeaderAndContent(_)
|
||||||
|
| CheckKind::NoOverIndentation
|
||||||
| CheckKind::NoSurroundingWhitespace
|
| CheckKind::NoSurroundingWhitespace
|
||||||
|
| CheckKind::NoUnderIndentation
|
||||||
| CheckKind::OneBlankLineAfterClass(_)
|
| CheckKind::OneBlankLineAfterClass(_)
|
||||||
| CheckKind::OneBlankLineBeforeClass(_)
|
| CheckKind::OneBlankLineBeforeClass(_)
|
||||||
| CheckKind::PPrintFound
|
| CheckKind::PPrintFound
|
||||||
|
|
|
@ -32,3 +32,11 @@ pub fn indentation<'a>(checker: &'a mut Checker, docstring: &Expr) -> &'a str {
|
||||||
end_location: Location::new(range.location.row(), range.location.column()),
|
end_location: Location::new(range.location.row(), range.location.column()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Replace any non-whitespace characters from an indentation string.
|
||||||
|
pub fn clean(indentation: &str) -> String {
|
||||||
|
indentation
|
||||||
|
.chars()
|
||||||
|
.map(|char| if char.is_whitespace() { char } else { ' ' })
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
|
@ -385,23 +385,10 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut has_seen_tab = false;
|
|
||||||
let mut has_seen_over_indent = false;
|
|
||||||
let mut has_seen_under_indent = false;
|
|
||||||
|
|
||||||
let docstring_indent = helpers::indentation(checker, docstring).to_string();
|
let docstring_indent = helpers::indentation(checker, docstring).to_string();
|
||||||
if !has_seen_tab {
|
let mut has_seen_tab = docstring_indent.contains('\t');
|
||||||
if docstring_indent.contains('\t') {
|
let mut is_over_indented = true;
|
||||||
if checker.settings.enabled.contains(&CheckCode::D206) {
|
let mut over_indented_lines = vec![];
|
||||||
checker.add_check(Check::new(
|
|
||||||
CheckKind::IndentWithSpaces,
|
|
||||||
Range::from_located(docstring),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
has_seen_tab = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in 0..lines.len() {
|
for i in 0..lines.len() {
|
||||||
// First lines and continuations doesn't need any indentation.
|
// First lines and continuations doesn't need any indentation.
|
||||||
if i == 0 || lines[i - 1].ends_with('\\') {
|
if i == 0 || lines[i - 1].ends_with('\\') {
|
||||||
|
@ -415,39 +402,106 @@ pub fn indent(checker: &mut Checker, definition: &Definition) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let line_indent = helpers::leading_space(lines[i]);
|
let line_indent = helpers::leading_space(lines[i]);
|
||||||
if !has_seen_tab {
|
|
||||||
if line_indent.contains('\t') {
|
// We only report tab indentation once, so only check if we haven't seen a tab yet.
|
||||||
|
has_seen_tab = has_seen_tab || line_indent.contains('\t');
|
||||||
|
|
||||||
|
if checker.settings.enabled.contains(&CheckCode::D207) {
|
||||||
|
// We report under-indentation on every line. This isn't great, but enables
|
||||||
|
// autofix.
|
||||||
|
if line_indent.len() < docstring_indent.len() {
|
||||||
|
let mut check = Check::new(
|
||||||
|
CheckKind::NoUnderIndentation,
|
||||||
|
Range {
|
||||||
|
location: Location::new(docstring.location.row() + i, 1),
|
||||||
|
end_location: Location::new(docstring.location.row() + i, 1),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
||||||
|
check.amend(Fix::replacement(
|
||||||
|
helpers::clean(&docstring_indent),
|
||||||
|
Location::new(docstring.location.row() + i, 1),
|
||||||
|
Location::new(docstring.location.row() + i, 1 + line_indent.len()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
checker.add_check(check);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like pydocstyle, we only report over-indentation if either: (1) every line
|
||||||
|
// (except, optionally, the last line) is over-indented, or (2) the last line (which
|
||||||
|
// contains the closing quotation marks) is over-indented. We can't know if we've
|
||||||
|
// achieved that condition until we've viewed all the lines, so for now, just track
|
||||||
|
// the over-indentation status of every line.
|
||||||
|
if i < lines.len() - 1 {
|
||||||
|
if line_indent.len() > docstring_indent.len() {
|
||||||
|
over_indented_lines.push(i);
|
||||||
|
} else {
|
||||||
|
is_over_indented = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if checker.settings.enabled.contains(&CheckCode::D206) {
|
if checker.settings.enabled.contains(&CheckCode::D206) {
|
||||||
|
if has_seen_tab {
|
||||||
checker.add_check(Check::new(
|
checker.add_check(Check::new(
|
||||||
CheckKind::IndentWithSpaces,
|
CheckKind::IndentWithSpaces,
|
||||||
Range::from_located(docstring),
|
Range::from_located(docstring),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
has_seen_tab = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !has_seen_over_indent {
|
|
||||||
if line_indent.len() > docstring_indent.len() {
|
|
||||||
if checker.settings.enabled.contains(&CheckCode::D208) {
|
if checker.settings.enabled.contains(&CheckCode::D208) {
|
||||||
checker.add_check(Check::new(
|
// If every line (except the last) is over-indented...
|
||||||
|
if is_over_indented {
|
||||||
|
for i in over_indented_lines {
|
||||||
|
let line_indent = helpers::leading_space(lines[i]);
|
||||||
|
if line_indent.len() > docstring_indent.len() {
|
||||||
|
// We report over-indentation on every line. This isn't great, but
|
||||||
|
// enables autofix.
|
||||||
|
let mut check = Check::new(
|
||||||
CheckKind::NoOverIndentation,
|
CheckKind::NoOverIndentation,
|
||||||
Range::from_located(docstring),
|
Range {
|
||||||
|
location: Location::new(docstring.location.row() + i, 1),
|
||||||
|
end_location: Location::new(docstring.location.row() + i, 1),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply)
|
||||||
|
{
|
||||||
|
check.amend(Fix::replacement(
|
||||||
|
helpers::clean(&docstring_indent),
|
||||||
|
Location::new(docstring.location.row() + i, 1),
|
||||||
|
Location::new(
|
||||||
|
docstring.location.row() + i,
|
||||||
|
1 + line_indent.len(),
|
||||||
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
has_seen_over_indent = true;
|
checker.add_check(check);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !has_seen_under_indent {
|
// If the last line is over-indented...
|
||||||
if line_indent.len() < docstring_indent.len() {
|
if !lines.is_empty() {
|
||||||
if checker.settings.enabled.contains(&CheckCode::D207) {
|
let i = lines.len() - 1;
|
||||||
checker.add_check(Check::new(
|
let line_indent = helpers::leading_space(lines[i]);
|
||||||
CheckKind::NoUnderIndentation,
|
if line_indent.len() > docstring_indent.len() {
|
||||||
Range::from_located(docstring),
|
let mut check = Check::new(
|
||||||
|
CheckKind::NoOverIndentation,
|
||||||
|
Range {
|
||||||
|
location: Location::new(docstring.location.row() + i, 1),
|
||||||
|
end_location: Location::new(docstring.location.row() + i, 1),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
||||||
|
check.amend(Fix::replacement(
|
||||||
|
helpers::clean(&docstring_indent),
|
||||||
|
Location::new(docstring.location.row() + i, 1),
|
||||||
|
Location::new(docstring.location.row() + i, 1 + line_indent.len()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
has_seen_under_indent = true;
|
checker.add_check(check);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -481,8 +535,10 @@ pub fn newline_after_last_paragraph(checker: &mut Checker, definition: &Definiti
|
||||||
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply)
|
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply)
|
||||||
{
|
{
|
||||||
// Insert a newline just before the end-quote(s).
|
// Insert a newline just before the end-quote(s).
|
||||||
let mut content = "\n".to_string();
|
let content = format!(
|
||||||
content.push_str(helpers::indentation(checker, docstring));
|
"\n{}",
|
||||||
|
helpers::clean(helpers::indentation(checker, docstring))
|
||||||
|
);
|
||||||
check.amend(Fix::insertion(
|
check.amend(Fix::insertion(
|
||||||
content,
|
content,
|
||||||
Location::new(
|
Location::new(
|
||||||
|
@ -857,10 +913,11 @@ fn blanks_and_section_underline(
|
||||||
);
|
);
|
||||||
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
||||||
// Add a dashed line (of the appropriate length) under the section header.
|
// Add a dashed line (of the appropriate length) under the section header.
|
||||||
let mut content = "".to_string();
|
let content = format!(
|
||||||
content.push_str(helpers::indentation(checker, docstring));
|
"{}{}\n",
|
||||||
content.push_str(&"-".repeat(context.section_name.len()));
|
helpers::clean(helpers::indentation(checker, docstring)),
|
||||||
content.push('\n');
|
"-".repeat(context.section_name.len())
|
||||||
|
);
|
||||||
check.amend(Fix::insertion(
|
check.amend(Fix::insertion(
|
||||||
content,
|
content,
|
||||||
Location::new(docstring.location.row() + context.original_index + 1, 1),
|
Location::new(docstring.location.row() + context.original_index + 1, 1),
|
||||||
|
@ -890,10 +947,11 @@ fn blanks_and_section_underline(
|
||||||
);
|
);
|
||||||
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
||||||
// Add a dashed line (of the appropriate length) under the section header.
|
// Add a dashed line (of the appropriate length) under the section header.
|
||||||
let mut content = "".to_string();
|
let content = format!(
|
||||||
content.push_str(helpers::indentation(checker, docstring));
|
"{}{}\n",
|
||||||
content.push_str(&"-".repeat(context.section_name.len()));
|
helpers::clean(helpers::indentation(checker, docstring)),
|
||||||
content.push('\n');
|
"-".repeat(context.section_name.len())
|
||||||
|
);
|
||||||
check.amend(Fix::insertion(
|
check.amend(Fix::insertion(
|
||||||
content,
|
content,
|
||||||
Location::new(docstring.location.row() + context.original_index + 1, 1),
|
Location::new(docstring.location.row() + context.original_index + 1, 1),
|
||||||
|
@ -965,10 +1023,11 @@ fn blanks_and_section_underline(
|
||||||
);
|
);
|
||||||
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
||||||
// Replace the existing underline with a line of the appropriate length.
|
// Replace the existing underline with a line of the appropriate length.
|
||||||
let mut content = "".to_string();
|
let content = format!(
|
||||||
content.push_str(helpers::indentation(checker, docstring));
|
"{}{}\n",
|
||||||
content.push_str(&"-".repeat(context.section_name.len()));
|
helpers::clean(helpers::indentation(checker, docstring)),
|
||||||
content.push('\n');
|
"-".repeat(context.section_name.len())
|
||||||
|
);
|
||||||
check.amend(Fix::replacement(
|
check.amend(Fix::replacement(
|
||||||
content,
|
content,
|
||||||
Location::new(
|
Location::new(
|
||||||
|
@ -1003,7 +1062,7 @@ fn blanks_and_section_underline(
|
||||||
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
||||||
// Replace the existing indentation with whitespace of the appropriate length.
|
// Replace the existing indentation with whitespace of the appropriate length.
|
||||||
check.amend(Fix::replacement(
|
check.amend(Fix::replacement(
|
||||||
indentation,
|
helpers::clean(&indentation),
|
||||||
Location::new(
|
Location::new(
|
||||||
docstring.location.row()
|
docstring.location.row()
|
||||||
+ context.original_index
|
+ context.original_index
|
||||||
|
@ -1144,7 +1203,7 @@ fn common_section(
|
||||||
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
if matches!(checker.autofix, fixer::Mode::Generate | fixer::Mode::Apply) {
|
||||||
// Replace the existing indentation with whitespace of the appropriate length.
|
// Replace the existing indentation with whitespace of the appropriate length.
|
||||||
check.amend(Fix::replacement(
|
check.amend(Fix::replacement(
|
||||||
indentation,
|
helpers::clean(&indentation),
|
||||||
Location::new(docstring.location.row() + context.original_index, 1),
|
Location::new(docstring.location.row() + context.original_index, 1),
|
||||||
Location::new(
|
Location::new(
|
||||||
docstring.location.row() + context.original_index,
|
docstring.location.row() + context.original_index,
|
||||||
|
|
|
@ -4,26 +4,70 @@ expression: checks
|
||||||
---
|
---
|
||||||
- kind: NoUnderIndentation
|
- kind: NoUnderIndentation
|
||||||
location:
|
location:
|
||||||
row: 225
|
row: 227
|
||||||
column: 5
|
column: 1
|
||||||
end_location:
|
end_location:
|
||||||
row: 229
|
row: 227
|
||||||
column: 8
|
column: 1
|
||||||
fix: ~
|
fix:
|
||||||
|
patch:
|
||||||
|
content: " "
|
||||||
|
location:
|
||||||
|
row: 227
|
||||||
|
column: 1
|
||||||
|
end_location:
|
||||||
|
row: 227
|
||||||
|
column: 1
|
||||||
|
applied: false
|
||||||
- kind: NoUnderIndentation
|
- kind: NoUnderIndentation
|
||||||
location:
|
location:
|
||||||
row: 235
|
row: 238
|
||||||
column: 5
|
column: 1
|
||||||
end_location:
|
end_location:
|
||||||
row: 239
|
row: 238
|
||||||
column: 4
|
column: 1
|
||||||
fix: ~
|
fix:
|
||||||
|
patch:
|
||||||
|
content: " "
|
||||||
|
location:
|
||||||
|
row: 238
|
||||||
|
column: 1
|
||||||
|
end_location:
|
||||||
|
row: 238
|
||||||
|
column: 1
|
||||||
|
applied: false
|
||||||
- kind: NoUnderIndentation
|
- kind: NoUnderIndentation
|
||||||
location:
|
location:
|
||||||
row: 433
|
row: 435
|
||||||
column: 37
|
column: 1
|
||||||
|
end_location:
|
||||||
|
row: 435
|
||||||
|
column: 1
|
||||||
|
fix:
|
||||||
|
patch:
|
||||||
|
content: " "
|
||||||
|
location:
|
||||||
|
row: 435
|
||||||
|
column: 1
|
||||||
|
end_location:
|
||||||
|
row: 435
|
||||||
|
column: 5
|
||||||
|
applied: false
|
||||||
|
- kind: NoUnderIndentation
|
||||||
|
location:
|
||||||
|
row: 436
|
||||||
|
column: 1
|
||||||
end_location:
|
end_location:
|
||||||
row: 436
|
row: 436
|
||||||
column: 8
|
column: 1
|
||||||
fix: ~
|
fix:
|
||||||
|
patch:
|
||||||
|
content: " "
|
||||||
|
location:
|
||||||
|
row: 436
|
||||||
|
column: 1
|
||||||
|
end_location:
|
||||||
|
row: 436
|
||||||
|
column: 5
|
||||||
|
applied: false
|
||||||
|
|
||||||
|
|
|
@ -4,26 +4,53 @@ expression: checks
|
||||||
---
|
---
|
||||||
- kind: NoOverIndentation
|
- kind: NoOverIndentation
|
||||||
location:
|
location:
|
||||||
row: 245
|
row: 247
|
||||||
column: 5
|
column: 1
|
||||||
end_location:
|
end_location:
|
||||||
row: 249
|
row: 247
|
||||||
|
column: 1
|
||||||
|
fix:
|
||||||
|
patch:
|
||||||
|
content: " "
|
||||||
|
location:
|
||||||
|
row: 247
|
||||||
|
column: 1
|
||||||
|
end_location:
|
||||||
|
row: 247
|
||||||
column: 8
|
column: 8
|
||||||
fix: ~
|
applied: false
|
||||||
- kind: NoOverIndentation
|
- kind: NoOverIndentation
|
||||||
location:
|
location:
|
||||||
row: 255
|
row: 259
|
||||||
column: 5
|
column: 1
|
||||||
end_location:
|
end_location:
|
||||||
row: 259
|
row: 259
|
||||||
column: 12
|
column: 1
|
||||||
fix: ~
|
fix:
|
||||||
|
patch:
|
||||||
|
content: " "
|
||||||
|
location:
|
||||||
|
row: 259
|
||||||
|
column: 1
|
||||||
|
end_location:
|
||||||
|
row: 259
|
||||||
|
column: 9
|
||||||
|
applied: false
|
||||||
- kind: NoOverIndentation
|
- kind: NoOverIndentation
|
||||||
location:
|
location:
|
||||||
row: 265
|
row: 267
|
||||||
column: 5
|
column: 1
|
||||||
end_location:
|
end_location:
|
||||||
row: 269
|
row: 267
|
||||||
column: 8
|
column: 1
|
||||||
fix: ~
|
fix:
|
||||||
|
patch:
|
||||||
|
content: " "
|
||||||
|
location:
|
||||||
|
row: 267
|
||||||
|
column: 1
|
||||||
|
end_location:
|
||||||
|
row: 267
|
||||||
|
column: 9
|
||||||
|
applied: false
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue