From 9b16a9ab92d620b3c761bc158a4cb2e63af66536 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Fri, 13 Oct 2023 17:40:09 +0800 Subject: [PATCH 1/2] Add a checker for excessive indentation. --- sphinxlint/checkers.py | 32 ++++++++ .../fixtures/xfail/excessive-indentation.rst | 75 +++++++++++++++++++ .../fixtures/xpass/excessive-indentation.rst | 54 +++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 tests/fixtures/xfail/excessive-indentation.rst create mode 100644 tests/fixtures/xpass/excessive-indentation.rst diff --git a/sphinxlint/checkers.py b/sphinxlint/checkers.py index 791cac6ca..221f6c317 100644 --- a/sphinxlint/checkers.py +++ b/sphinxlint/checkers.py @@ -485,6 +485,38 @@ def check_block(block_lineno, block): yield from errors +_find_leading_spaces = re.compile(r'^\s*').match +# finds lines that start with bullets, numbers, and directives' ".." +_find_list_starters = re.compile(r'^\s*(?:[-+*•‣⁃]|\d+[).]|\(\d+\)|#\.|\.\.)\s+' + r'(?!index)').match + + +@checker(".rst") +def check_excessive_indentation(file, lines, options=None): + """Check for nested blocks indented more than they should. + + |Unnecessarily indented list: + | + | * this will be rendered in a blockquote + | + | .. note: this too + """ + errors = [] + last_ind_level = 0 + for lineno, line in enumerate(lines, start=1): + if not line.strip(): + continue + curr_ind_level = len(_find_leading_spaces(line).group()) + # look for nested lists/directives with excessive indentation + if curr_ind_level > last_ind_level and _find_list_starters(line): + errors.append((lineno, "Excessive indentation in nested section")) + # update the indentation level to ignore "* ", "1. ", ".. ", etc. + if m := _find_list_starters(line): + curr_ind_level = len(m.group()) + last_ind_level = curr_ind_level + yield from errors + + _has_dangling_hyphen = re.compile(r".*[a-z]-$").match diff --git a/tests/fixtures/xfail/excessive-indentation.rst b/tests/fixtures/xfail/excessive-indentation.rst new file mode 100644 index 000000000..b983a3f72 --- /dev/null +++ b/tests/fixtures/xfail/excessive-indentation.rst @@ -0,0 +1,75 @@ +.. expect: 15: Excessive indentation in nested section (excessive-indentation) +.. expect: 20: Excessive indentation in nested section (excessive-indentation) +.. expect: 26: Excessive indentation in nested section (excessive-indentation) +.. expect: 30: Excessive indentation in nested section (excessive-indentation) +.. expect: 35: Excessive indentation in nested section (excessive-indentation) +.. expect: 42: Excessive indentation in nested section (excessive-indentation) +.. expect: 49: Excessive indentation in nested section (excessive-indentation) +.. expect: 51: Excessive indentation in nested section (excessive-indentation) +.. expect: 53: Excessive indentation in nested section (excessive-indentation) +.. expect: 57: Excessive indentation in nested section (excessive-indentation) +.. expect: 71: Excessive indentation in nested section (excessive-indentation) + +The most common mistakes is indenting lists and directives under a paragraph: + + * This list shouldn't be indented + * Otherwise it's rendered inside a blockquote + +Directives also shouldn't be indented under a paragraph: + + .. note:: like this one + + + +* Nested lists should be indented properly + + * the bullet of this list should have been under the N + +* Same goes for directives + + .. note:: this should have been under the S + + +.. note:: + + * the opposite is also true + * lists nested under directives + * should be indented properly + * (but maybe they don't have to?) + +.. note:: + + .. note:: this is also not allowed atm, but maybe it should + + +There are also other types of lists: + +* bullet lists + + - bullet lists with different bullets + + 1. numbered lists + + 2) more numbered lists + + (3) numbered lists with more parentheses + + #. autonumbered lists + + +Each of these should give an error, since they are all indented wrong. + +Numbered lists that start with letters (a. b. c. ...) +and roman numerals (I. II. III. ...) are only supported by Sphinx 7+ +so we just ignore them + + +This is a known false positive: + + >>> import sys + >>> sys.stdout.writelines(result) + 1. Beautiful is better than ugly. + - 2. Explicit is better than implicit. + - 3. Simple is better than complex. + + 3. Simple is better than complex. + ? ++ diff --git a/tests/fixtures/xpass/excessive-indentation.rst b/tests/fixtures/xpass/excessive-indentation.rst new file mode 100644 index 000000000..67beeb976 --- /dev/null +++ b/tests/fixtures/xpass/excessive-indentation.rst @@ -0,0 +1,54 @@ +A paragraph indented under another paragraph will create a blockquote +and that's usually ok: + + This is an intentional blockquote + + +In some cases you also have indented directives like ".. index:" that +don't produce any output, so we ignore those too: + + .. index:: ignore + +This is also because they might appear in a list like: + +(1) + .. index:: first item + + This is the first item of the list + +(2) + .. index:: second item + + This is the second item of the list + + +Properly indented nested lists and directives are also ok: + +* A list item that contains multiple lines or paragraphs + should work fine as long as the indentation level is correct + + In this case everything is ok. + +* The nested list should be indented under the T: + + * like this + * and this + +* The start of the list is where the T is: + + * so this should be indented further + * and this too + + +* We can also have a directive nested inside a list: + + .. note:: like this + + +.. note:: + + Having text properly indented under a directive is ok + +.. note:: + + .. note:: Nesting directives is fine too if they are properly indented From 7345443c808e96db19b8bf21d1bc38c3e67eacfa Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Fri, 13 Oct 2023 23:49:26 +0800 Subject: [PATCH 2/2] Skip literal blocks. --- sphinxlint/checkers.py | 3 ++- tests/fixtures/xfail/excessive-indentation.rst | 13 +------------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/sphinxlint/checkers.py b/sphinxlint/checkers.py index 221f6c317..46312ee59 100644 --- a/sphinxlint/checkers.py +++ b/sphinxlint/checkers.py @@ -503,7 +503,8 @@ def check_excessive_indentation(file, lines, options=None): """ errors = [] last_ind_level = 0 - for lineno, line in enumerate(lines, start=1): + for lineno, line in enumerate(hide_non_rst_blocks(lines), start=1): + # print(line, end='') if not line.strip(): continue curr_ind_level = len(_find_leading_spaces(line).group()) diff --git a/tests/fixtures/xfail/excessive-indentation.rst b/tests/fixtures/xfail/excessive-indentation.rst index b983a3f72..aea80d1b9 100644 --- a/tests/fixtures/xfail/excessive-indentation.rst +++ b/tests/fixtures/xfail/excessive-indentation.rst @@ -8,7 +8,7 @@ .. expect: 51: Excessive indentation in nested section (excessive-indentation) .. expect: 53: Excessive indentation in nested section (excessive-indentation) .. expect: 57: Excessive indentation in nested section (excessive-indentation) -.. expect: 71: Excessive indentation in nested section (excessive-indentation) + The most common mistakes is indenting lists and directives under a paragraph: @@ -62,14 +62,3 @@ Each of these should give an error, since they are all indented wrong. Numbered lists that start with letters (a. b. c. ...) and roman numerals (I. II. III. ...) are only supported by Sphinx 7+ so we just ignore them - - -This is a known false positive: - - >>> import sys - >>> sys.stdout.writelines(result) - 1. Beautiful is better than ugly. - - 2. Explicit is better than implicit. - - 3. Simple is better than complex. - + 3. Simple is better than complex. - ? ++