]> git.madduck.net Git - etc/vim.git/blob - test/script/block-padding-checker

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Squashed '.vim/bundle/ale/' content from commit 22185c4c
[etc/vim.git] / test / script / block-padding-checker
1 #!/usr/bin/env python
2 """
3 This script checks for missing or forbidden blank lines before or after
4 particular Vim commands. This script ensures that VimL scripts are padded
5 correctly, so they are easier to read.
6 """
7
8 import sys
9 import re
10
11 INDENTATION_RE = re.compile(r'^ *')
12 COMMENT_LINE_RE = re.compile(r'^ *"')
13 COMMAND_RE = re.compile(r'^ *([a-zA-Z\\]+)')
14 OPERATOR_END_RE = re.compile(r'(&&|\|\||\+|-|\*\| /)$')
15
16 START_BLOCKS = set(['if', 'for', 'while', 'try', 'function'])
17 END_BLOCKS = set(['endif', 'endfor', 'endwhile', 'endtry', 'endfunction'])
18 MIDDLE_BLOCKS = set(['else', 'elseif', 'catch', 'finally'])
19 TERMINATORS = set(['return', 'throw'])
20
21 WHITESPACE_BEFORE_SET = START_BLOCKS | TERMINATORS
22 WHITESPACE_FORBIDDEN_BEFORE_SET = END_BLOCKS | MIDDLE_BLOCKS
23 WHITESPACE_AFTER_SET = END_BLOCKS
24 WHITESPACE_FORBIDDEN_AFTER_SET = START_BLOCKS | MIDDLE_BLOCKS
25 SAME_INDENTATION_SET = set(['\\'])
26
27
28 def remove_comment_lines(line_iter):
29     for line_number, line in enumerate(line_iter, 1):
30         if not COMMENT_LINE_RE.match(line):
31             yield (line_number, line)
32
33
34 def check_lines(line_iter):
35     previous_indentation_level = None
36     previous_command = None
37     previous_line_blank = False
38
39     for line_number, line in remove_comment_lines(line_iter):
40         if len(line) == 0:
41             # Check for commands where we shouldn't have blank lines after
42             # them, like `else` or the start of blocks like `function`.
43             if (
44                 previous_command is not None
45                 and previous_command in WHITESPACE_FORBIDDEN_AFTER_SET
46             ):
47                 yield (
48                     line_number,
49                     'Blank line forbidden after `%s`' % (previous_command,)
50                 )
51
52             previous_line_blank = True
53             previous_command = None
54         else:
55             indentation_level = INDENTATION_RE.match(line).end()
56             command_match = COMMAND_RE.match(line)
57
58             if command_match:
59                 command = command_match.group(1)
60
61                 if (
62                     command in SAME_INDENTATION_SET
63                     and previous_indentation_level is not None
64                     and indentation_level != previous_indentation_level
65                 ):
66                     yield (
67                         line_number,
68                         'Line continuation should match previous indentation'
69                     )
70
71                 if (
72                     previous_indentation_level is not None
73                     and indentation_level != previous_indentation_level
74                     and abs(indentation_level - previous_indentation_level) != 4  # noqa
75                 ):
76                     yield (
77                         line_number,
78                         'Indentation should be 4 spaces'
79                     )
80
81                 # Check for commands requiring blank lines before them, if they
82                 # aren't at the start of a block.
83                 if (
84                     command in WHITESPACE_BEFORE_SET
85                     and previous_indentation_level is not None
86                     and indentation_level == previous_indentation_level
87                     and previous_line_blank is False
88                 ):
89                     yield (
90                         line_number,
91                         'Blank line required before `%s`' % (command,)
92                     )
93
94                 # Check for commands where we shouldn't have blank lines before
95                 # them, like `else` or the end of blocks like `endfunction`.
96                 if (
97                     command in WHITESPACE_FORBIDDEN_BEFORE_SET
98                     and previous_line_blank is True
99                 ):
100                     yield (
101                         line_number - 1,
102                         'Blank line forbidden before `%s`' % (command,)
103                     )
104
105                 # Check for commands requiring blank lines after them, if they
106                 # aren't at the end of a block.
107                 if (
108                     previous_command is not None
109                     and previous_command in WHITESPACE_AFTER_SET
110                     and previous_indentation_level is not None
111                     and indentation_level == previous_indentation_level
112                     and previous_line_blank is False
113                 ):
114                     yield (
115                         line_number - 1,
116                         'Blank line required after `%s`' % (command,)
117                     )
118
119                 previous_command = command
120                 previous_line_blank = False
121                 previous_indentation_level = indentation_level
122
123             if OPERATOR_END_RE.search(line):
124                 yield (
125                     line_number,
126                     'Put operators at the start of lines instead'
127                 )
128
129
130 def main():
131     status = 0
132
133     for filename in sys.argv[1:]:
134         with open(filename) as vim_file:
135             line_iter = (line.rstrip() for line in vim_file)
136
137             for line_number, message in check_lines(line_iter):
138                 print('%s:%d %s' % (filename, line_number, message))
139                 status = 1
140
141     sys.exit(status)
142
143
144 if __name__ == "__main__":
145     main()