Roll src/third_party/WebKit f36d5e0:68b67cd (svn 193299:193303)
[chromium-blink-merge.git] / tools / json_schema_compiler / code.py
blobe454e5d444039808553ae73e2f74823f81133fb3
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 class Code(object):
6 """A convenience object for constructing code.
8 Logically each object should be a block of code. All methods except |Render|
9 and |IsEmpty| return self.
10 """
11 def __init__(self, indent_size=2, comment_length=80):
12 self._code = []
13 self._indent_size = indent_size
14 self._comment_length = comment_length
15 self._line_prefixes = []
17 def Append(self, line='',
18 substitute=True,
19 indent_level=None,
20 new_line=True,
21 strip_right=True):
22 """Appends a line of code at the current indent level or just a newline if
23 line is not specified.
25 substitute: indicated whether this line should be affected by
26 code.Substitute().
27 new_line: whether this should be added as a new line, or should be appended
28 to the last line of the code.
29 strip_right: whether or not trailing whitespace should be stripped.
30 """
32 prefix = indent_level * ' ' if indent_level else ''.join(
33 self._line_prefixes)
35 if strip_right:
36 line = line.rstrip()
38 if not new_line and self._code:
39 self._code[-1].value += line
40 else:
41 self._code.append(Line(prefix + line, substitute=substitute))
42 return self
44 def IsEmpty(self):
45 """Returns True if the Code object is empty.
46 """
47 return not bool(self._code)
49 def Concat(self, obj, new_line=True):
50 """Concatenate another Code object onto this one. Trailing whitespace is
51 stripped.
53 Appends the code at the current indent level. Will fail if there are any
54 un-interpolated format specifiers eg %s, %(something)s which helps
55 isolate any strings that haven't been substituted.
56 """
57 if not isinstance(obj, Code):
58 raise TypeError(type(obj))
59 assert self is not obj
60 if not obj._code:
61 return self
63 for line in obj._code:
64 try:
65 # line % () will fail if any substitution tokens are left in line
66 if line.substitute:
67 line.value %= ()
68 except TypeError:
69 raise TypeError('Unsubstituted value when concatting\n' + line.value)
70 except ValueError:
71 raise ValueError('Stray % character when concatting\n' + line.value)
72 first_line = obj._code.pop(0)
73 self.Append(first_line.value, first_line.substitute, new_line=new_line)
74 for line in obj._code:
75 self.Append(line.value, line.substitute)
77 return self
79 def Cblock(self, code):
80 """Concatenates another Code object |code| onto this one followed by a
81 blank line, if |code| is non-empty."""
82 if not code.IsEmpty():
83 self.Concat(code).Append()
84 return self
86 def Sblock(self, line=None, line_prefix=None, new_line=True):
87 """Starts a code block.
89 Appends a line of code and then increases the indent level. If |line_prefix|
90 is present, it will be treated as the extra prefix for the code block.
91 Otherwise, the prefix will be the default indent level.
92 """
93 if line is not None:
94 self.Append(line, new_line=new_line)
95 self._line_prefixes.append(line_prefix or ' ' * self._indent_size)
96 return self
98 def Eblock(self, line=None):
99 """Ends a code block by decreasing and then appending a line (or a blank
100 line if not given).
102 # TODO(calamity): Decide if type checking is necessary
103 #if not isinstance(line, basestring):
104 # raise TypeError
105 self._line_prefixes.pop()
106 if line is not None:
107 self.Append(line)
108 return self
110 def Comment(self, comment, comment_prefix='// ',
111 wrap_indent=0, new_line=True):
112 """Adds the given string as a comment.
114 Will split the comment if it's too long. Use mainly for variable length
115 comments. Otherwise just use code.Append('// ...') for comments.
117 Unaffected by code.Substitute().
119 # Helper function to trim a comment to the maximum length, and return one
120 # line and the remainder of the comment.
121 def trim_comment(comment, max_len):
122 if len(comment) <= max_len:
123 return comment, ''
124 last_space = comment.rfind(' ', 0, max_len + 1)
125 if last_space != -1:
126 line = comment[0:last_space]
127 comment = comment[last_space + 1:]
128 else:
129 line = comment[0:max_len]
130 comment = comment[max_len:]
131 return line, comment
133 # First line has the full maximum length.
134 if not new_line and self._code:
135 max_len = self._comment_length - len(self._code[-1].value)
136 else:
137 max_len = (self._comment_length - len(''.join(self._line_prefixes)) -
138 len(comment_prefix))
139 line, comment = trim_comment(comment, max_len)
140 self.Append(comment_prefix + line, substitute=False, new_line=new_line)
142 # Any subsequent lines be subject to the wrap indent.
143 max_len = (self._comment_length - len(''.join(self._line_prefixes)) -
144 len(comment_prefix) - wrap_indent)
145 while len(comment):
146 line, comment = trim_comment(comment, max_len)
147 self.Append(comment_prefix + (' ' * wrap_indent) + line, substitute=False)
149 return self
151 def Substitute(self, d):
152 """Goes through each line and interpolates using the given dict.
154 Raises type error if passed something that isn't a dict
156 Use for long pieces of code using interpolation with the same variables
157 repeatedly. This will reduce code and allow for named placeholders which
158 are more clear.
160 if not isinstance(d, dict):
161 raise TypeError('Passed argument is not a dictionary: ' + d)
162 for i, line in enumerate(self._code):
163 if self._code[i].substitute:
164 # Only need to check %s because arg is a dict and python will allow
165 # '%s %(named)s' but just about nothing else
166 if '%s' in self._code[i].value or '%r' in self._code[i].value:
167 raise TypeError('"%s" or "%r" found in substitution. '
168 'Named arguments only. Use "%" to escape')
169 self._code[i].value = line.value % d
170 self._code[i].substitute = False
171 return self
173 def Render(self):
174 """Renders Code as a string.
176 return '\n'.join([l.value for l in self._code])
179 class Line(object):
180 """A line of code.
182 def __init__(self, value, substitute=True):
183 self.value = value
184 self.substitute = substitute