• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2
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.
6
7"""Pretty-prints the contents of a GYP file."""
8
9from __future__ import print_function
10
11import sys
12import re
13
14
15# Regex to remove comments when we're counting braces.
16COMMENT_RE = re.compile(r'\s*#.*')
17
18# Regex to remove quoted strings when we're counting braces.
19# It takes into account quoted quotes, and makes sure that the quotes match.
20# NOTE: It does not handle quotes that span more than one line, or
21# cases where an escaped quote is preceded by an escaped backslash.
22QUOTE_RE_STR = r'(?P<q>[\'"])(.*?)(?<![^\\][\\])(?P=q)'
23QUOTE_RE = re.compile(QUOTE_RE_STR)
24
25
26def comment_replace(matchobj):
27  return matchobj.group(1) + matchobj.group(2) + '#' * len(matchobj.group(3))
28
29
30def mask_comments(input):
31  """Mask the quoted strings so we skip braces inside quoted strings."""
32  search_re = re.compile(r'(.*?)(#)(.*)')
33  return [search_re.sub(comment_replace, line) for line in input]
34
35
36def quote_replace(matchobj):
37  return "%s%s%s%s" % (matchobj.group(1),
38                       matchobj.group(2),
39                       'x'*len(matchobj.group(3)),
40                       matchobj.group(2))
41
42
43def mask_quotes(input):
44  """Mask the quoted strings so we skip braces inside quoted strings."""
45  search_re = re.compile(r'(.*?)' + QUOTE_RE_STR)
46  return [search_re.sub(quote_replace, line) for line in input]
47
48
49def do_split(input, masked_input, search_re):
50  output = []
51  mask_output = []
52  for (line, masked_line) in zip(input, masked_input):
53    m = search_re.match(masked_line)
54    while m:
55      split = len(m.group(1))
56      line = line[:split] + r'\n' + line[split:]
57      masked_line = masked_line[:split] + r'\n' + masked_line[split:]
58      m = search_re.match(masked_line)
59    output.extend(line.split(r'\n'))
60    mask_output.extend(masked_line.split(r'\n'))
61  return (output, mask_output)
62
63
64def split_double_braces(input):
65  """Masks out the quotes and comments, and then splits appropriate
66  lines (lines that matche the double_*_brace re's above) before
67  indenting them below.
68
69  These are used to split lines which have multiple braces on them, so
70  that the indentation looks prettier when all laid out (e.g. closing
71  braces make a nice diagonal line).
72  """
73  double_open_brace_re = re.compile(r'(.*?[\[\{\(,])(\s*)([\[\{\(])')
74  double_close_brace_re = re.compile(r'(.*?[\]\}\)],?)(\s*)([\]\}\)])')
75
76  masked_input = mask_quotes(input)
77  masked_input = mask_comments(masked_input)
78
79  (output, mask_output) = do_split(input, masked_input, double_open_brace_re)
80  (output, mask_output) = do_split(output, mask_output, double_close_brace_re)
81
82  return output
83
84
85def count_braces(line):
86  """keeps track of the number of braces on a given line and returns the result.
87
88  It starts at zero and subtracts for closed braces, and adds for open braces.
89  """
90  open_braces = ['[', '(', '{']
91  close_braces = [']', ')', '}']
92  closing_prefix_re = re.compile(r'(.*?[^\s\]\}\)]+.*?)([\]\}\)],?)\s*$')
93  cnt = 0
94  stripline = COMMENT_RE.sub(r'', line)
95  stripline = QUOTE_RE.sub(r"''", stripline)
96  for char in stripline:
97    for brace in open_braces:
98      if char == brace:
99        cnt += 1
100    for brace in close_braces:
101      if char == brace:
102        cnt -= 1
103
104  after = False
105  if cnt > 0:
106    after = True
107
108  # This catches the special case of a closing brace having something
109  # other than just whitespace ahead of it -- we don't want to
110  # unindent that until after this line is printed so it stays with
111  # the previous indentation level.
112  if cnt < 0 and closing_prefix_re.match(stripline):
113    after = True
114  return (cnt, after)
115
116
117def prettyprint_input(lines):
118  """Does the main work of indenting the input based on the brace counts."""
119  indent = 0
120  basic_offset = 2
121  last_line = ""
122  for line in lines:
123    if COMMENT_RE.match(line):
124      print(line)
125    else:
126      line = line.strip('\r\n\t ')  # Otherwise doesn't strip \r on Unix.
127      if len(line) > 0:
128        (brace_diff, after) = count_braces(line)
129        if brace_diff != 0:
130          if after:
131            print(" " * (basic_offset * indent) + line)
132            indent += brace_diff
133          else:
134            indent += brace_diff
135            print(" " * (basic_offset * indent) + line)
136        else:
137          print(" " * (basic_offset * indent) + line)
138      else:
139        print("")
140      last_line = line
141
142
143def main():
144  if len(sys.argv) > 1:
145    data = open(sys.argv[1]).read().splitlines()
146  else:
147    data = sys.stdin.read().splitlines()
148  # Split up the double braces.
149  lines = split_double_braces(data)
150
151  # Indent and print the output.
152  prettyprint_input(lines)
153  return 0
154
155
156if __name__ == '__main__':
157  sys.exit(main())
158