3 # Copyright (c) 2012 Google Inc. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
7 """Pretty-prints the contents of a GYP file."""
13 # Regex to remove comments when we're counting braces.
14 COMMENT_RE = re.compile(r'\s*#.*')
16 # Regex to remove quoted strings when we're counting braces.
17 # It takes into account quoted quotes, and makes sure that the quotes match.
18 # NOTE: It does not handle quotes that span more than one line, or
19 # cases where an escaped quote is preceeded by an escaped backslash.
20 QUOTE_RE_STR = r'(?P<q>[\'"])(.*?)(?<![^\\][\\])(?P=q)'
21 QUOTE_RE = re.compile(QUOTE_RE_STR)
24 def comment_replace(matchobj):
25 return matchobj.group(1) + matchobj.group(2) + '#' * len(matchobj.group(3))
28 def mask_comments(input):
29 """Mask the quoted strings so we skip braces inside quoted strings."""
30 search_re = re.compile(r'(.*?)(#)(.*)')
31 return [search_re.sub(comment_replace, line) for line in input]
34 def quote_replace(matchobj):
35 return "%s%s%s%s" % (matchobj.group(1),
37 'x'*len(matchobj.group(3)),
41 def mask_quotes(input):
42 """Mask the quoted strings so we skip braces inside quoted strings."""
43 search_re = re.compile(r'(.*?)' + QUOTE_RE_STR)
44 return [search_re.sub(quote_replace, line) for line in input]
47 def do_split(input, masked_input, search_re):
50 for (line, masked_line) in zip(input, masked_input):
51 m = search_re.match(masked_line)
53 split = len(m.group(1))
54 line = line[:split] + r'\n' + line[split:]
55 masked_line = masked_line[:split] + r'\n' + masked_line[split:]
56 m = search_re.match(masked_line)
57 output.extend(line.split(r'\n'))
58 mask_output.extend(masked_line.split(r'\n'))
59 return (output, mask_output)
62 def split_double_braces(input):
63 """Masks out the quotes and comments, and then splits appropriate
64 lines (lines that matche the double_*_brace re's above) before
67 These are used to split lines which have multiple braces on them, so
68 that the indentation looks prettier when all laid out (e.g. closing
69 braces make a nice diagonal line).
71 double_open_brace_re = re.compile(r'(.*?[\[\{\(,])(\s*)([\[\{\(])')
72 double_close_brace_re = re.compile(r'(.*?[\]\}\)],?)(\s*)([\]\}\)])')
74 masked_input = mask_quotes(input)
75 masked_input = mask_comments(masked_input)
77 (output, mask_output) = do_split(input, masked_input, double_open_brace_re)
78 (output, mask_output) = do_split(output, mask_output, double_close_brace_re)
83 def count_braces(line):
84 """keeps track of the number of braces on a given line and returns the result.
86 It starts at zero and subtracts for closed braces, and adds for open braces.
88 open_braces = ['[', '(', '{']
89 close_braces = [']', ')', '}']
90 closing_prefix_re = re.compile(r'(.*?[^\s\]\}\)]+.*?)([\]\}\)],?)\s*$')
92 stripline = COMMENT_RE.sub(r'', line)
93 stripline = QUOTE_RE.sub(r"''", stripline)
94 for char in stripline:
95 for brace in open_braces:
98 for brace in close_braces:
106 # This catches the special case of a closing brace having something
107 # other than just whitespace ahead of it -- we don't want to
108 # unindent that until after this line is printed so it stays with
109 # the previous indentation level.
110 if cnt < 0 and closing_prefix_re.match(stripline):
115 def prettyprint_input(lines):
116 """Does the main work of indenting the input based on the brace counts."""
121 if COMMENT_RE.match(line):
124 line = line.strip('\r\n\t ') # Otherwise doesn't strip \r on Unix.
126 (brace_diff, after) = count_braces(line)
129 print " " * (basic_offset * indent) + line
133 print " " * (basic_offset * indent) + line
135 print " " * (basic_offset * indent) + line
142 if len(sys.argv) > 1:
143 data = open(sys.argv[1]).read().splitlines()
145 data = sys.stdin.read().splitlines()
146 # Split up the double braces.
147 lines = split_double_braces(data)
149 # Indent and print the output.
150 prettyprint_input(lines)
154 if __name__ == '__main__':