ale/test/script/block-padding-checker

146 lines
5.1 KiB
Python
Executable File

#!/usr/bin/env python
"""
This script checks for missing or forbidden blank lines before or after
particular Vim commands. This script ensures that VimL scripts are padded
correctly, so they are easier to read.
"""
import sys
import re
INDENTATION_RE = re.compile(r'^ *')
COMMENT_LINE_RE = re.compile(r'^ *"')
COMMAND_RE = re.compile(r'^ *([a-zA-Z\\]+)')
OPERATOR_END_RE = re.compile(r'(&&|\|\||\+|-|\*\| /)$')
START_BLOCKS = set(['if', 'for', 'while', 'try', 'function'])
END_BLOCKS = set(['endif', 'endfor', 'endwhile', 'endtry', 'endfunction'])
MIDDLE_BLOCKS = set(['else', 'elseif', 'catch', 'finally'])
TERMINATORS = set(['return', 'throw'])
WHITESPACE_BEFORE_SET = START_BLOCKS | TERMINATORS
WHITESPACE_FORBIDDEN_BEFORE_SET = END_BLOCKS | MIDDLE_BLOCKS
WHITESPACE_AFTER_SET = END_BLOCKS
WHITESPACE_FORBIDDEN_AFTER_SET = START_BLOCKS | MIDDLE_BLOCKS
SAME_INDENTATION_SET = set(['\\'])
def remove_comment_lines(line_iter):
for line_number, line in enumerate(line_iter, 1):
if not COMMENT_LINE_RE.match(line):
yield (line_number, line)
def check_lines(line_iter):
previous_indentation_level = None
previous_command = None
previous_line_blank = False
for line_number, line in remove_comment_lines(line_iter):
if len(line) == 0:
# Check for commands where we shouldn't have blank lines after
# them, like `else` or the start of blocks like `function`.
if (
previous_command is not None
and previous_command in WHITESPACE_FORBIDDEN_AFTER_SET
):
yield (
line_number,
'Blank line forbidden after `%s`' % (previous_command,)
)
previous_line_blank = True
previous_command = None
else:
indentation_level = INDENTATION_RE.match(line).end()
command_match = COMMAND_RE.match(line)
if command_match:
command = command_match.group(1)
if (
command in SAME_INDENTATION_SET
and previous_indentation_level is not None
and indentation_level != previous_indentation_level
):
yield (
line_number,
'Line continuation should match previous indentation'
)
if (
previous_indentation_level is not None
and indentation_level != previous_indentation_level
and abs(indentation_level - previous_indentation_level) != 4 # noqa
):
yield (
line_number,
'Indentation should be 4 spaces'
)
# Check for commands requiring blank lines before them, if they
# aren't at the start of a block.
if (
command in WHITESPACE_BEFORE_SET
and previous_indentation_level is not None
and indentation_level == previous_indentation_level
and previous_line_blank is False
):
yield (
line_number,
'Blank line required before `%s`' % (command,)
)
# Check for commands where we shouldn't have blank lines before
# them, like `else` or the end of blocks like `endfunction`.
if (
command in WHITESPACE_FORBIDDEN_BEFORE_SET
and previous_line_blank is True
):
yield (
line_number - 1,
'Blank line forbidden before `%s`' % (command,)
)
# Check for commands requiring blank lines after them, if they
# aren't at the end of a block.
if (
previous_command is not None
and previous_command in WHITESPACE_AFTER_SET
and previous_indentation_level is not None
and indentation_level == previous_indentation_level
and previous_line_blank is False
):
yield (
line_number - 1,
'Blank line required after `%s`' % (command,)
)
previous_command = command
previous_line_blank = False
previous_indentation_level = indentation_level
if OPERATOR_END_RE.search(line):
yield (
line_number,
'Put operators at the start of lines instead'
)
def main():
status = 0
for filename in sys.argv[1:]:
with open(filename) as vim_file:
line_iter = (line.rstrip() for line in vim_file)
for line_number, message in check_lines(line_iter):
print('%s:%d %s' % (filename, line_number, message))
status = 1
sys.exit(status)
if __name__ == "__main__":
main()