corrade-nucleus-nucleons – Blame information for rev 4

Subversion Repositories:
Rev:
Rev Author Line No. Line
2 office 1 from __future__ import print_function
2 import sys
3 import os
4 import io
5 import getopt
6 import re
7 import string
8 import errno
9 import copy
10 from jsbeautifier.__version__ import __version__
11  
12 #
13 # The MIT License (MIT)
14  
15 # Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
16  
17 # Permission is hereby granted, free of charge, to any person
18 # obtaining a copy of this software and associated documentation files
19 # (the "Software"), to deal in the Software without restriction,
20 # including without limitation the rights to use, copy, modify, merge,
21 # publish, distribute, sublicense, and/or sell copies of the Software,
22 # and to permit persons to whom the Software is furnished to do so,
23 # subject to the following conditions:
24  
25 # The above copyright notice and this permission notice shall be
26 # included in all copies or substantial portions of the Software.
27  
28 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
32 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
33 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35 # SOFTWARE.
36 #
37 # Originally written by Einar Lielmanis et al.,
38 # Conversion to python by Einar Lielmanis, einar@jsbeautifier.org,
39 # Parsing improvement for brace-less and semicolon-less statements
40 # by Liam Newman <bitwiseman@gmail.com>
41 # Python is not my native language, feel free to push things around.
42 #
43 # Use either from command line (script displays its usage when run
44 # without any parameters),
45 #
46 #
47 # or, alternatively, use it as a module:
48 #
49 # import jsbeautifier
50 # res = jsbeautifier.beautify('your javascript string')
51 # res = jsbeautifier.beautify_file('some_file.js')
52 #
53 # you may specify some options:
54 #
55 # opts = jsbeautifier.default_options()
56 # opts.indent_size = 2
57 # res = jsbeautifier.beautify('some javascript', opts)
58 #
59 #
60 # Here are the available options: (read source)
61  
62  
63 class BeautifierOptions:
64 def __init__(self):
65 self.indent_size = 4
66 self.indent_char = ' '
67 self.indent_with_tabs = False
68 self.eol = 'auto'
69 self.preserve_newlines = True
70 self.max_preserve_newlines = 10
71 self.space_in_paren = False
72 self.space_in_empty_paren = False
73 self.e4x = False
74 self.jslint_happy = False
75 self.space_after_anon_function = False
76 self.brace_style = 'collapse'
77 self.keep_array_indentation = False
78 self.keep_function_indentation = False
79 self.eval_code = False
80 self.unescape_strings = False
81 self.wrap_line_length = 0
82 self.break_chained_methods = False
83 self.end_with_newline = False
84 self.comma_first = False
85 self.operator_position = 'before-newline'
86  
87 self.css = None
88 self.js = None
89 self.html = None
90  
91 # For testing of beautify ignore:start directive
92 self.test_output_raw = False
93 self.editorconfig = False
94  
95  
96  
97 def mergeOpts(self, targetType):
98 finalOpts = copy.copy(self)
99  
100 local = getattr(finalOpts, targetType)
101 if (local):
102 delattr(finalOpts, targetType)
103 for key in local:
104 setattr(finalOpts, key, local[key])
105  
106 return finalOpts
107  
108 def __repr__(self):
109 return \
110 """indent_size = %d
111 indent_char = [%s]
112 preserve_newlines = %s
113 max_preserve_newlines = %d
114 space_in_paren = %s
115 jslint_happy = %s
116 space_after_anon_function = %s
117 indent_with_tabs = %s
118 brace_style = %s
119 keep_array_indentation = %s
120 eval_code = %s
121 wrap_line_length = %s
122 unescape_strings = %s
123 """ % ( self.indent_size,
124 self.indent_char,
125 self.preserve_newlines,
126 self.max_preserve_newlines,
127 self.space_in_paren,
128 self.jslint_happy,
129 self.space_after_anon_function,
130 self.indent_with_tabs,
131 self.brace_style,
132 self.keep_array_indentation,
133 self.eval_code,
134 self.wrap_line_length,
135 self.unescape_strings,
136 )
137  
138  
139 class BeautifierFlags:
140 def __init__(self, mode):
141 self.mode = mode
142 self.parent = None
143 self.last_text = ''
144 self.last_word = ''
145 self.declaration_statement = False
146 self.declaration_assignment = False
147 self.multiline_frame = False
148 self.inline_frame = False
149 self.if_block = False
150 self.else_block = False
151 self.do_block = False
152 self.do_while = False
153 self.import_block = False
154 self.in_case = False
155 self.in_case_statement = False
156 self.case_body = False
157 self.indentation_level = 0
158 self.line_indent_level = 0
159 self.start_line_index = 0
160 self.ternary_depth = 0
161  
162 def apply_base(self, flags_base, added_newline):
163 next_indent_level = flags_base.indentation_level
164 if not added_newline and \
165 flags_base.line_indent_level > next_indent_level:
166 next_indent_level = flags_base.line_indent_level
167  
168 self.parent = flags_base
169 self.last_text = flags_base.last_text
170 self.last_word = flags_base.last_word
171 self.indentation_level = next_indent_level
172  
173 class Acorn:
174 def __init__(self):
175 # This is not pretty, but given how we did the version import
176 # it is the only way to do this without having setup.py fail on a missing six dependency.
177 self.six = __import__("six")
178 # This section of code was translated to python from acorn (javascript).
179 #
180 # Acorn was written by Marijn Haverbeke and released under an MIT
181 # license. The Unicode regexps (for identifiers and whitespace) were
182 # taken from [Esprima](http://esprima.org) by Ariya Hidayat.
183 #
184 # Git repositories for Acorn are available at
185 #
186 # http://marijnhaverbeke.nl/git/acorn
187 # https://github.com/marijnh/acorn.git
188  
189 # ## Character categories
190  
191 # Big ugly regular expressions that match characters in the
192 # whitespace, identifier, and identifier-start categories. These
193 # are only applied when a character is found to actually have a
194 # code point above 128.
195  
196 self.nonASCIIwhitespace = re.compile(self.six.u("[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]"))
197 self.nonASCIIidentifierStartChars = self.six.u("\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc")
198 self.nonASCIIidentifierChars = self.six.u("\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f")
199 self.nonASCIIidentifierStart = re.compile("[" + self.nonASCIIidentifierStartChars + "]")
200 self.nonASCIIidentifier = re.compile("[" + self.nonASCIIidentifierStartChars + self.nonASCIIidentifierChars + "]")
201  
202 # Whether a single character denotes a newline.
203  
204 self.newline = re.compile(self.six.u("[\n\r\u2028\u2029]"))
205  
206 # Matches a whole line break (where CRLF is considered a single
207 # line break). Used to count lines.
208  
209 # in javascript, these two differ
210 # in python they are the same, different methods are called on them
211 self.lineBreak = re.compile(self.six.u("\r\n|[\n\r\u2028\u2029]"))
212 self.allLineBreaks = self.lineBreak
213  
214  
215 # Test whether a given character code starts an identifier.
216 def isIdentifierStart(self, code):
217 if code < 65:
218 return code in [36, 64] # permit $ (36) and @ (64). @ is used in ES7 decorators.
219 if code < 91:
220 return True # 65 through 91 are uppercase letters
221 if code < 97:
222 return code == 95 # permit _ (95)
223 if code < 123:
224 return True # 97 through 123 are lowercase letters
225 return code >= 0xaa and self.nonASCIIidentifierStart.match(self.six.unichr(code)) != None
226  
227 # Test whether a given character is part of an identifier.
228 def isIdentifierChar(self, code):
229 if code < 48:
230 return code == 36
231 if code < 58:
232 return True
233 if code < 65:
234 return False
235 if code < 91:
236 return True
237 if code < 97:
238 return code == 95
239 if code < 123:
240 return True
241 return code >= 0xaa and self.nonASCIIidentifier.match(self.six.unichr(code)) != None
242  
243 class Token:
244 def __init__(self, type, text, newlines = 0, whitespace_before = '', mode = None, parent = None):
245 self.type = type
246 self.text = text
247 self.comments_before = []
248 self.newlines = newlines
249 self.wanted_newline = newlines > 0
250 self.whitespace_before = whitespace_before
251 self.parent = None
252 self.opened = None
253 self.directives = None
254  
255  
256 def default_options():
257 return BeautifierOptions()
258  
259  
260 def beautify(string, opts = default_options() ):
261 b = Beautifier()
262 return b.beautify(string, opts)
263  
264 def set_file_editorconfig_opts(filename, js_options):
265 from editorconfig import get_properties, EditorConfigError
266 try:
267 _ecoptions = get_properties(os.path.abspath(filename))
268  
269 if _ecoptions.get("indent_style") == "tab":
270 js_options.indent_with_tabs = True
271 elif _ecoptions.get("indent_style") == "space":
272 js_options.indent_with_tabs = False
273  
274 if _ecoptions.get("indent_size"):
275 js_options.indent_size = int(_ecoptions["indent_size"])
276  
277 if _ecoptions.get("max_line_length"):
278 if _ecoptions.get("max_line_length") == "off":
279 js_options.wrap_line_length = 0
280 else:
281 js_options.wrap_line_length = int(_ecoptions["max_line_length"])
282  
283 if _ecoptions.get("insert_final_newline") == 'true':
284 js_options.end_with_newline = True
285 elif _ecoptions.get("insert_final_newline") == 'false':
286 js_options.end_with_newline = False
287  
288 if _ecoptions.get("end_of_line"):
289 if _ecoptions["end_of_line"] == "cr":
290 js_options.eol = '\r'
291 elif _ecoptions["end_of_line"] == "lf":
292 js_options.eol = '\n'
293 elif _ecoptions["end_of_line"] == "crlf":
294 js_options.eol = '\r\n'
295  
296 except EditorConfigError as ex:
297 # do not error on bad editor config
298 print("Error loading EditorConfig. Ignoring.", file=sys.stderr)
299  
300  
301 def beautify_file(file_name, opts = default_options() ):
302 input_string = ''
303 if file_name == '-': # stdin
304 try:
305 if sys.stdin.isatty():
306 raise Exception()
307  
308 stream = sys.stdin
309 input_string = ''.join(stream.readlines())
310 except Exception as ex:
311 print("Must pipe input or define at least one file.", file=sys.stderr)
312 usage(sys.stderr)
313 raise Exception()
314 else:
315 stream = io.open(file_name, 'rt', newline='')
316 input_string = ''.join(stream.readlines())
317  
318 return beautify(input_string, opts)
319  
320  
321 def usage(stream=sys.stdout):
322  
323 print("jsbeautifier.py@" + __version__ + """
324  
325 Javascript beautifier (http://jsbeautifier.org/)
326  
327 Usage: jsbeautifier.py [options] <infile>
328  
329 <infile> can be "-", which means stdin.
330 <outfile> defaults to stdout
331  
332 Input options:
333  
334 -i, --stdin Read input from stdin
335  
336 Output options:
337  
338 -s, --indent-size=NUMBER Indentation size. (default 4).
339 -c, --indent-char=CHAR Character to indent with. (default space).
340 -e, --eol=STRING Character(s) to use as line terminators.
341 (default first newline in file, otherwise "\\n")
342 -t, --indent-with-tabs Indent with tabs, overrides -s and -c
343 -d, --disable-preserve-newlines Do not preserve existing line breaks.
344 -P, --space-in-paren Add padding spaces within paren, ie. f( a, b )
345 -E, --space-in-empty-paren Add a single space inside empty paren, ie. f( )
346 -j, --jslint-happy More jslint-compatible output
347 -a, --space_after_anon_function Add a space before an anonymous function's parens, ie. function ()
348 -b, --brace-style=collapse Brace style (collapse, expand, end-expand, none)(,preserve-inline)
349 -k, --keep-array-indentation Keep array indentation.
350 -r, --replace Write output in-place, replacing input
351 -o, --outfile=FILE Specify a file to output to (default stdout)
352 -f, --keep-function-indentation Do not re-indent function bodies defined in var lines.
353 -x, --unescape-strings Decode printable chars encoded in \\xNN notation.
354 -X, --e4x Pass E4X xml literals through untouched
355 -w, --wrap-line-length Attempt to wrap line when it exceeds this length.
356 NOTE: Line continues until next wrap point is found.
357 -n, --end_with_newline End output with newline
358 --editorconfig Enable setting configuration from EditorConfig
359  
360 Rarely needed options:
361  
362 --eval-code evaluate code if a JS interpreter is
363 installed. May be useful with some obfuscated
364 script but poses a potential security issue.
365  
366 -l, --indent-level=NUMBER Initial indentation level. (default 0).
367  
368 -h, --help, --usage Prints this help statement.
369 -v, --version Show the version
370  
371 """, file=stream)
372 if stream == sys.stderr:
373 return 1
374 else:
375 return 0
376  
377 OPERATOR_POSITION = {
378 'before_newline': 'before-newline',
379 'after_newline': 'after-newline',
380 'preserve_newline': 'preserve-newline'
381 }
382 OPERATOR_POSITION_BEFORE_OR_PRESERVE = [OPERATOR_POSITION['before_newline'], OPERATOR_POSITION['preserve_newline']];
383  
384 def sanitizeOperatorPosition(opPosition):
385 if not opPosition:
386 return OPERATOR_POSITION['before_newline']
387 elif opPosition not in OPERATOR_POSITION.values():
388 raise ValueError("Invalid Option Value: The option 'operator_position' must be one of the following values\n" +
389 str(OPERATOR_POSITION.values()) +
390 "\nYou passed in: '" + opPosition + "'")
391  
392 return opPosition
393  
394 class MODE:
395 BlockStatement, Statement, ObjectLiteral, ArrayLiteral, \
396 ForInitializer, Conditional, Expression = range(7)
397  
398 class Beautifier:
399  
400 def __init__(self, opts = default_options() ):
401  
402 self.opts = copy.copy(opts)
403 self.acorn = Acorn()
404 self.blank_state()
405  
406 def blank_state(self, js_source_text = None):
407  
408 # internal flags
409 self.flags = None
410 self.previous_flags = None
411 self.flag_store = []
412 self.tokens = []
413 self.token_pos = 0
414  
415  
416 # force opts.space_after_anon_function to true if opts.jslint_happy
417 if self.opts.jslint_happy:
418 self.opts.space_after_anon_function = True
419  
420 if self.opts.indent_with_tabs:
421 self.opts.indent_char = "\t"
422 self.opts.indent_size = 1
423  
424 if self.opts.eol == 'auto':
425 self.opts.eol = '\n'
426 if self.acorn.lineBreak.search(js_source_text or ''):
427 self.opts.eol = self.acorn.lineBreak.search(js_source_text).group()
428  
429 self.opts.eol = self.opts.eol.replace('\\r', '\r').replace('\\n', '\n')
430  
431 self.indent_string = self.opts.indent_char * self.opts.indent_size
432  
433 self.baseIndentString = ''
434 self.last_type = 'TK_START_BLOCK' # last token type
435 self.last_last_text = '' # pre-last token text
436  
437 preindent_index = 0;
438 if not js_source_text == None and len(js_source_text) > 0:
439 while preindent_index < len(js_source_text) and \
440 js_source_text[preindent_index] in [' ', '\t'] :
441 self.baseIndentString += js_source_text[preindent_index]
442 preindent_index += 1
443 js_source_text = js_source_text[preindent_index:]
444  
445 self.output = Output(self.indent_string, self.baseIndentString)
446 # If testing the ignore directive, start with output disable set to true
447 self.output.raw = self.opts.test_output_raw;
448  
449 self.set_mode(MODE.BlockStatement)
450 return js_source_text
451  
452 def beautify(self, s, opts = None ):
453  
454 if opts != None:
455 opts = opts.mergeOpts('js')
456 self.opts = copy.copy(opts)
457  
458  
459 #Compat with old form
460 if self.opts.brace_style == 'collapse-preserve-inline':
461 self.opts.brace_style = 'collapse,preserve-inline'
462  
463 split = re.compile("[^a-zA-Z0-9_\-]+").split(self.opts.brace_style)
464 self.opts.brace_style = split[0]
465 self.opts.brace_preserve_inline = (True if bool(split[1] == 'preserve-inline') else None) if len(split) > 1 else False
466  
467 if self.opts.brace_style not in ['expand', 'collapse', 'end-expand', 'none']:
468 raise(Exception('opts.brace_style must be "expand", "collapse", "end-expand", or "none".'))
469  
470 if self.opts.brace_preserve_inline == None:
471 raise(Exception('opts.brace_style second item must be "preserve-inline"'))
472  
473 s = self.blank_state(s)
474  
475 input = self.unpack(s, self.opts.eval_code)
476  
477 self.handlers = {
478 'TK_START_EXPR': self.handle_start_expr,
479 'TK_END_EXPR': self.handle_end_expr,
480 'TK_START_BLOCK': self.handle_start_block,
481 'TK_END_BLOCK': self.handle_end_block,
482 'TK_WORD': self.handle_word,
483 'TK_RESERVED': self.handle_word,
484 'TK_SEMICOLON': self.handle_semicolon,
485 'TK_STRING': self.handle_string,
486 'TK_EQUALS': self.handle_equals,
487 'TK_OPERATOR': self.handle_operator,
488 'TK_COMMA': self.handle_comma,
489 'TK_BLOCK_COMMENT': self.handle_block_comment,
490 'TK_COMMENT': self.handle_comment,
491 'TK_DOT': self.handle_dot,
492 'TK_UNKNOWN': self.handle_unknown,
493 'TK_EOF': self.handle_eof
494 }
495  
496 self.tokens = Tokenizer(input, self.opts, self.indent_string).tokenize()
497 self.token_pos = 0
498  
499 current_token = self.get_token()
500 while current_token != None:
501 self.handlers[current_token.type](current_token)
502  
503 self.last_last_text = self.flags.last_text
504 self.last_type = current_token.type
505 self.flags.last_text = current_token.text
506 self.token_pos += 1
507 current_token = self.get_token()
508  
509  
510 sweet_code = self.output.get_code()
511 if self.opts.end_with_newline:
512 sweet_code += '\n'
513  
514 if not self.opts.eol == '\n':
515 sweet_code = sweet_code.replace('\n', self.opts.eol)
516  
517 return sweet_code
518  
519  
520 def handle_whitespace_and_comments(self, local_token, preserve_statement_flags = False):
521 newlines = local_token.newlines
522 keep_whitespace = self.opts.keep_array_indentation and self.is_array(self.flags.mode)
523  
524 for comment_token in local_token.comments_before:
525 # The cleanest handling of inline comments is to treat them as though they aren't there.
526 # Just continue formatting and the behavior should be logical.
527 # Also ignore unknown tokens. Again, this should result in better behavior.
528 self.handle_whitespace_and_comments(comment_token, preserve_statement_flags)
529 self.handlers[comment_token.type](comment_token, preserve_statement_flags)
530  
531  
532 if keep_whitespace:
533 for i in range(newlines):
534 self.print_newline(i > 0, preserve_statement_flags)
535 else: # not keep_whitespace
536 if self.opts.max_preserve_newlines != 0 and newlines > self.opts.max_preserve_newlines:
537 newlines = self.opts.max_preserve_newlines
538  
539 if self.opts.preserve_newlines and newlines > 1:
540 self.print_newline(False, preserve_statement_flags)
541 for i in range(1, newlines):
542 self.print_newline(True, preserve_statement_flags)
543  
544  
545 def unpack(self, source, evalcode=False):
546 import jsbeautifier.unpackers as unpackers
547 try:
548 return unpackers.run(source, evalcode)
549 except unpackers.UnpackingError as error:
550 return source
551  
552 def is_special_word(self, s):
553 return s in ['case', 'return', 'do', 'if', 'throw', 'else']
554  
555 def is_array(self, mode):
556 return mode == MODE.ArrayLiteral
557  
558  
559 def is_expression(self, mode):
560 return mode in [MODE.Expression, MODE.ForInitializer, MODE.Conditional]
561  
562  
563 _newline_restricted_tokens = ['break','continue','return', 'throw']
564 def allow_wrap_or_preserved_newline(self, current_token, force_linewrap = False):
565 # never wrap the first token of a line.
566 if self.output.just_added_newline():
567 return
568  
569 shouldPreserveOrForce = (self.opts.preserve_newlines and current_token.wanted_newline) or force_linewrap
570 operatorLogicApplies = self.flags.last_text in Tokenizer.positionable_operators or current_token.text in Tokenizer.positionable_operators
571  
572 if operatorLogicApplies:
573 shouldPrintOperatorNewline = (self.flags.last_text in Tokenizer.positionable_operators and self.opts.operator_position in OPERATOR_POSITION_BEFORE_OR_PRESERVE) \
574 or current_token.text in Tokenizer.positionable_operators
575 shouldPreserveOrForce = shouldPreserveOrForce and shouldPrintOperatorNewline
576  
577 if shouldPreserveOrForce:
578 self.print_newline(preserve_statement_flags = True)
579 elif self.opts.wrap_line_length > 0:
580 if self.last_type == 'TK_RESERVED' and self.flags.last_text in self._newline_restricted_tokens:
581 # These tokens should never have a newline inserted between
582 # them and the following expression.
583 return
584 proposed_line_length = self.output.current_line.get_character_count() + len(current_token.text)
585 if self.output.space_before_token:
586 proposed_line_length += 1
587  
588 if proposed_line_length >= self.opts.wrap_line_length:
589 self.print_newline(preserve_statement_flags = True)
590  
591  
592 def print_newline(self, force_newline = False, preserve_statement_flags = False):
593 if not preserve_statement_flags:
594 if self.flags.last_text != ';' and self.flags.last_text != ',' and self.flags.last_text != '=' and self.last_type != 'TK_OPERATOR':
595 next_token = self.get_token(1)
596 while (self.flags.mode == MODE.Statement and
597 not (self.flags.if_block and next_token and next_token.type == 'TK_RESERVED' and next_token.text == 'else') and
598 not self.flags.do_block):
599 self.restore_mode()
600  
601 if self.output.add_new_line(force_newline):
602 self.flags.multiline_frame = True
603  
604 def print_token_line_indentation(self, current_token):
605 if self.output.just_added_newline():
606 line = self.output.current_line
607 if self.opts.keep_array_indentation and self.is_array(self.flags.mode) and current_token.wanted_newline:
608 line.push(current_token.whitespace_before)
609 self.output.space_before_token = False
610 elif self.output.set_indent(self.flags.indentation_level):
611 self.flags.line_indent_level = self.flags.indentation_level
612  
613  
614 def print_token(self, current_token, s=None):
615 if self.output.raw:
616 self.output.add_raw_token(current_token)
617 return
618  
619 if self.opts.comma_first and self.last_type == 'TK_COMMA' and self.output.just_added_newline():
620 if self.output.previous_line.last() == ',':
621 # if the comma was already at the start of the line,
622 # pull back onto that line and reprint the indentation
623 popped = self.output.previous_line.pop()
624 if self.output.previous_line.is_empty():
625 self.output.previous_line.push(popped)
626 self.output.trim(True)
627 self.output.current_line.pop()
628 self.output.trim()
629  
630 # add the comma in front of the next token
631 self.print_token_line_indentation(current_token)
632 self.output.add_token(',')
633 self.output.space_before_token = True
634  
635 if s == None:
636 s = current_token.text
637  
638 self.print_token_line_indentation(current_token)
639 self.output.add_token(s);
640  
641  
642 def indent(self):
643 self.flags.indentation_level += 1
644  
645 def deindent(self):
646 allow_deindent = self.flags.indentation_level > 0 and ((self.flags.parent == None) or self.flags.indentation_level > self.flags.parent.indentation_level)
647  
648 if allow_deindent:
649 self.flags.indentation_level -= 1
650  
651 def set_mode(self, mode):
652 if self.flags:
653 self.flag_store.append(self.flags)
654 self.previous_flags = self.flags
655 else:
656 self.previous_flags = BeautifierFlags(mode)
657  
658 self.flags = BeautifierFlags(mode)
659 self.flags.apply_base(self.previous_flags, self.output.just_added_newline())
660 self.flags.start_line_index = self.output.get_line_number();
661  
662 def restore_mode(self):
663 if len(self.flag_store) > 0:
664 self.previous_flags = self.flags
665 self.flags = self.flag_store.pop()
666 if self.previous_flags.mode == MODE.Statement:
667 self.output.remove_redundant_indentation(self.previous_flags)
668  
669  
670 def start_of_object_property(self):
671 return self.flags.parent.mode == MODE.ObjectLiteral and self.flags.mode == MODE.Statement and \
672 ((self.flags.last_text == ':' and self.flags.ternary_depth == 0) or (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['get', 'set']))
673  
674 def start_of_statement(self, current_token):
675 if (
676 (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['var', 'let', 'const'] and current_token.type == 'TK_WORD') \
677 or (self.last_type == 'TK_RESERVED' and self.flags.last_text== 'do') \
678 or (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['return', 'throw'] and not current_token.wanted_newline) \
679 or (self.last_type == 'TK_RESERVED' and self.flags.last_text == 'else' \
680 and not (current_token.type == 'TK_RESERVED' and current_token.text == 'if' and not len(current_token.comments_before))) \
681 or (self.last_type == 'TK_END_EXPR' and (self.previous_flags.mode == MODE.ForInitializer or self.previous_flags.mode == MODE.Conditional)) \
682 or (self.last_type == 'TK_WORD' and self.flags.mode == MODE.BlockStatement \
683 and not self.flags.in_case
684 and not (current_token.text == '--' or current_token.text == '++')
685 and self.last_last_text != 'function'
686 and current_token.type != 'TK_WORD' and current_token.type != 'TK_RESERVED') \
687 or (self.flags.mode == MODE.ObjectLiteral and \
688 ((self.flags.last_text == ':' and self.flags.ternary_depth == 0) or (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['get', 'set'])))
689 ):
690  
691 self.set_mode(MODE.Statement)
692 self.indent()
693  
694 self.handle_whitespace_and_comments(current_token, True);
695  
696 # Issue #276:
697 # If starting a new statement with [if, for, while, do], push to a new line.
698 # if (a) if (b) if(c) d(); else e(); else f();
699 if not self.start_of_object_property():
700 self.allow_wrap_or_preserved_newline(current_token, current_token.type == 'TK_RESERVED' and current_token.text in ['do', 'for', 'if', 'while'])
701  
702 return True
703 else:
704 return False
705  
706 def get_token(self, offset = 0):
707 index = self.token_pos + offset
708 if index < 0 or index >= len(self.tokens):
709 return None
710 else:
711 return self.tokens[index]
712  
713  
714 def handle_start_expr(self, current_token):
715 if self.start_of_statement(current_token):
716 # The conditional starts the statement if appropriate.
717 pass
718 else:
719 self.handle_whitespace_and_comments(current_token)
720  
721 next_mode = MODE.Expression
722  
723 if current_token.text == '[':
724 if self.last_type == 'TK_WORD' or self.flags.last_text == ')':
725 if self.last_type == 'TK_RESERVED' and self.flags.last_text in Tokenizer.line_starters:
726 self.output.space_before_token = True
727 self.set_mode(next_mode)
728 self.print_token(current_token)
729 self.indent()
730 if self.opts.space_in_paren:
731 self.output.space_before_token = True
732 return
733  
734 next_mode = MODE.ArrayLiteral
735  
736 if self.is_array(self.flags.mode):
737 if self.flags.last_text == '[' or (
738 self.flags.last_text == ',' and (self.last_last_text == ']' or self.last_last_text == '}')):
739 # ], [ goes to a new line
740 # }, [ goes to a new line
741 if not self.opts.keep_array_indentation:
742 self.print_newline()
743  
744 else:
745 if self.last_type == 'TK_RESERVED' and self.flags.last_text == 'for':
746 next_mode = MODE.ForInitializer
747 elif self.last_type == 'TK_RESERVED' and self.flags.last_text in ['if', 'while']:
748 next_mode = MODE.Conditional
749 else:
750 next_mode = MODE.Expression
751  
752  
753 if self.flags.last_text == ';' or self.last_type == 'TK_START_BLOCK':
754 self.print_newline()
755 elif self.last_type in ['TK_END_EXPR', 'TK_START_EXPR', 'TK_END_BLOCK'] or self.flags.last_text == '.':
756 # do nothing on (( and )( and ][ and ]( and .(
757 # TODO: Consider whether forcing this is required. Review failing tests when removed.
758 self.allow_wrap_or_preserved_newline(current_token, current_token.wanted_newline)
759  
760 elif not (self.last_type == 'TK_RESERVED' and current_token.text == '(') and self.last_type not in ['TK_WORD', 'TK_OPERATOR']:
761 self.output.space_before_token = True
762 elif (self.last_type == 'TK_RESERVED' and (self.flags.last_word == 'function' or self.flags.last_word == 'typeof')) or \
763 (self.flags.last_text == '*' and (
764 self.last_last_text in ['function', 'yield'] or
765 (self.flags.mode == MODE.ObjectLiteral and self.last_last_text in ['{', ',']))):
766 # function() vs function (), typeof() vs typeof ()
767 # function*() vs function* (), yield*() vs yield* ()
768 if self.opts.space_after_anon_function:
769 self.output.space_before_token = True
770 elif self.last_type == 'TK_RESERVED' and (self.flags.last_text in Tokenizer.line_starters or self.flags.last_text == 'catch'):
771 # TODO: option space_before_conditional
772 self.output.space_before_token = True
773  
774 elif current_token.text == '(' and self.last_type == 'TK_RESERVED' and self.flags.last_word == 'await':
775 self.output.space_before_token = True
776  
777  
778 # Support of this kind of newline preservation:
779 # a = (b &&
780 # (c || d));
781 if self.last_type in ['TK_EQUALS', 'TK_OPERATOR']:
782 if not self.start_of_object_property():
783 self.allow_wrap_or_preserved_newline(current_token)
784  
785  
786 # Support preserving wrapped arrow function expressions
787 # a.b('c',
788 # () => d.e
789 # )
790 if current_token.text == '(' and self.last_type not in ['TK_WORD', 'TK_RESERVED']:
791 self.allow_wrap_or_preserved_newline(current_token)
792  
793  
794 self.set_mode(next_mode)
795 self.print_token(current_token)
796  
797 if self.opts.space_in_paren:
798 self.output.space_before_token = True
799  
800 # In all cases, if we newline while inside an expression it should be indented.
801 self.indent()
802  
803  
804  
805 def handle_end_expr(self, current_token):
806 # statements inside expressions are not valid syntax, but...
807 # statements must all be closed when their container closes
808 while self.flags.mode == MODE.Statement:
809 self.restore_mode()
810  
811 self.handle_whitespace_and_comments(current_token)
812  
813 if self.flags.multiline_frame:
814 self.allow_wrap_or_preserved_newline(current_token, current_token.text == ']' and self.is_array(self.flags.mode) and not self.opts.keep_array_indentation)
815  
816 if self.opts.space_in_paren:
817 if self.last_type == 'TK_START_EXPR' and not self.opts.space_in_empty_paren:
818 # empty parens are always "()" and "[]", not "( )" or "[ ]"
819 self.output.space_before_token = False
820 self.output.trim()
821 else:
822 self.output.space_before_token = True
823  
824 if current_token.text == ']' and self.opts.keep_array_indentation:
825 self.print_token(current_token)
826 self.restore_mode()
827 else:
828 self.restore_mode()
829 self.print_token(current_token)
830  
831 self.output.remove_redundant_indentation(self.previous_flags)
832  
833 # do {} while () // no statement required after
834 if self.flags.do_while and self.previous_flags.mode == MODE.Conditional:
835 self.previous_flags.mode = MODE.Expression
836 self.flags.do_block = False
837 self.flags.do_while = False
838  
839 def handle_start_block(self, current_token):
840 self.handle_whitespace_and_comments(current_token)
841  
842 # Check if this is a BlockStatement that should be treated as a ObjectLiteral
843 next_token = self.get_token(1)
844 second_token = self.get_token(2)
845 if second_token != None and \
846 ((second_token.text in [':', ','] and next_token.type in ['TK_STRING', 'TK_WORD', 'TK_RESERVED']) \
847 or (next_token.text in ['get', 'set', '...'] and second_token.type in ['TK_WORD', 'TK_RESERVED'])):
848 # We don't support TypeScript,but we didn't break it for a very long time.
849 # We'll try to keep not breaking it.
850 if not self.last_last_text in ['class','interface']:
851 self.set_mode(MODE.ObjectLiteral)
852 else:
853 self.set_mode(MODE.BlockStatement)
854 elif self.last_type == 'TK_OPERATOR' and self.flags.last_text == '=>':
855 # arrow function: (param1, paramN) => { statements }
856 self.set_mode(MODE.BlockStatement)
857 elif self.last_type in ['TK_EQUALS', 'TK_START_EXPR', 'TK_COMMA', 'TK_OPERATOR'] or \
858 (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['return', 'throw', 'import', 'default']):
859 # Detecting shorthand function syntax is difficult by scanning forward,
860 # so check the surrounding context.
861 # If the block is being returned, imported, export default, passed as arg,
862 # assigned with = or assigned in a nested object, treat as an ObjectLiteral.
863 self.set_mode(MODE.ObjectLiteral)
864 else:
865 self.set_mode(MODE.BlockStatement)
866  
867 empty_braces = (not next_token == None) and len(next_token.comments_before) == 0 and next_token.text == '}'
868 empty_anonymous_function = empty_braces and self.flags.last_word == 'function' and \
869 self.last_type == 'TK_END_EXPR'
870  
871 if self.opts.brace_preserve_inline: # check for inline, set inline_frame if so
872 # search forward for newline wanted inside this block
873 index = 0
874 check_token = None
875 self.flags.inline_frame = True
876 do_loop = True
877 while (do_loop):
878 index += 1
879 check_token = self.get_token(index)
880 if check_token.wanted_newline:
881 self.flags.inline_frame = False
882  
883 do_loop = (check_token.type != 'TK_EOF' and
884 not (check_token.type == 'TK_END_BLOCK' and check_token.opened == current_token))
885  
886 if (self.opts.brace_style == 'expand' or \
887 (self.opts.brace_style == 'none' and current_token.wanted_newline)) and \
888 not self.flags.inline_frame:
889 if self.last_type != 'TK_OPERATOR' and \
890 (empty_anonymous_function or
891 self.last_type == 'TK_EQUALS' or
892 (self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text) and self.flags.last_text != 'else')):
893 self.output.space_before_token = True
894 else:
895 self.print_newline(preserve_statement_flags = True)
896 else: # collapse || inline_frame
897 if self.is_array(self.previous_flags.mode) and (self.last_type == 'TK_START_EXPR' or self.last_type == 'TK_COMMA'):
898 # if we're preserving inline,
899 # allow newline between comma and next brace.
900 if self.flags.inline_frame:
901 self.allow_wrap_or_preserved_newline(current_token)
902 self.flags.inline_frame = True
903 self.previous_flags.multiline_frame = self.previous_flags.multiline_frame or self.flags.multiline_frame
904 self.flags.multiline_frame = False
905 elif self.last_type == 'TK_COMMA':
906 self.output.space_before_token = True
907  
908 elif self.last_type not in ['TK_OPERATOR', 'TK_START_EXPR']:
909 if self.last_type == 'TK_START_BLOCK' and not self.flags.inline_frame:
910 self.print_newline()
911 else:
912 self.output.space_before_token = True
913  
914 self.print_token(current_token)
915 self.indent()
916  
917  
918 def handle_end_block(self, current_token):
919 # statements must all be closed when their container closes
920 self.handle_whitespace_and_comments(current_token)
921  
922 while self.flags.mode == MODE.Statement:
923 self.restore_mode()
924  
925 empty_braces = self.last_type == 'TK_START_BLOCK'
926  
927 if self.flags.inline_frame and not empty_braces: # try inline_frame (only set if opt.braces-preserve-inline) first
928 self.output.space_before_token = True;
929 elif self.opts.brace_style == 'expand':
930 if not empty_braces:
931 self.print_newline()
932 else:
933 # skip {}
934 if not empty_braces:
935 if self.is_array(self.flags.mode) and self.opts.keep_array_indentation:
936 self.opts.keep_array_indentation = False
937 self.print_newline()
938 self.opts.keep_array_indentation = True
939 else:
940 self.print_newline()
941  
942 self.restore_mode()
943 self.print_token(current_token)
944  
945  
946 def handle_word(self, current_token):
947 if current_token.type == 'TK_RESERVED':
948 if current_token.text in ['set', 'get'] and self.flags.mode != MODE.ObjectLiteral:
949 current_token.type = 'TK_WORD'
950 elif current_token.text in ['as', 'from'] and not self.flags.import_block:
951 current_token.type = 'TK_WORD'
952 elif self.flags.mode == MODE.ObjectLiteral:
953 next_token = self.get_token(1)
954 if next_token.text == ':':
955 current_token.type = 'TK_WORD'
956  
957 if self.start_of_statement(current_token):
958 # The conditional starts the statement if appropriate.
959 if self.last_type == 'TK_RESERVED' and self.flags.last_text in ['var', 'let', 'const'] and current_token.type == 'TK_WORD':
960 self.flags.declaration_statement = True
961  
962 elif current_token.wanted_newline and \
963 not self.is_expression(self.flags.mode) and \
964 (self.last_type != 'TK_OPERATOR' or (self.flags.last_text == '--' or self.flags.last_text == '++')) and \
965 self.last_type != 'TK_EQUALS' and \
966 (self.opts.preserve_newlines or not (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['var', 'let', 'const', 'set', 'get'])):
967 self.handle_whitespace_and_comments(current_token)
968 self.print_newline()
969 else:
970 self.handle_whitespace_and_comments(current_token)
971  
972  
973 if self.flags.do_block and not self.flags.do_while:
974 if current_token.type == 'TK_RESERVED' and current_token.text == 'while':
975 # do {} ## while ()
976 self.output.space_before_token = True
977 self.print_token(current_token)
978 self.output.space_before_token = True
979 self.flags.do_while = True
980 return
981 else:
982 # do {} should always have while as the next word.
983 # if we don't see the expected while, recover
984 self.print_newline()
985 self.flags.do_block = False
986  
987 # if may be followed by else, or not
988 # Bare/inline ifs are tricky
989 # Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e();
990 if self.flags.if_block:
991 if (not self.flags.else_block) and (current_token.type == 'TK_RESERVED' and current_token.text == 'else'):
992 self.flags.else_block = True
993 else:
994 while self.flags.mode == MODE.Statement:
995 self.restore_mode()
996  
997 self.flags.if_block = False
998  
999 if current_token.type == 'TK_RESERVED' and (current_token.text == 'case' or (current_token.text == 'default' and self.flags.in_case_statement)):
1000 self.print_newline()
1001 if self.flags.case_body or self.opts.jslint_happy:
1002 self.flags.case_body = False
1003 self.deindent()
1004 self.print_token(current_token)
1005 self.flags.in_case = True
1006 self.flags.in_case_statement = True
1007 return
1008  
1009 if self.last_type in ['TK_COMMA', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']:
1010 if not self.start_of_object_property():
1011 self.allow_wrap_or_preserved_newline(current_token)
1012  
1013 if current_token.type == 'TK_RESERVED' and current_token.text == 'function':
1014 if (self.flags.last_text in ['}', ';'] or
1015 (self.output.just_added_newline() and not (self.flags.last_text in ['(', '[', '{', ':', '=', ','] or self.last_type == 'TK_OPERATOR'))):
1016 # make sure there is a nice clean space of at least one blank line
1017 # before a new function definition, except in arrays
1018 if not self.output.just_added_blankline() and len(current_token.comments_before) == 0:
1019 self.print_newline()
1020 self.print_newline(True)
1021  
1022 if self.last_type == 'TK_RESERVED' or self.last_type == 'TK_WORD':
1023 if self.last_type == 'TK_RESERVED' and self.flags.last_text in ['get', 'set', 'new', 'return', 'export', 'async']:
1024 self.output.space_before_token = True
1025 elif self.last_type == 'TK_RESERVED' and self.flags.last_text == 'default' and self.last_last_text == 'export':
1026 self.output.space_before_token = True
1027 else:
1028 self.print_newline()
1029 elif self.last_type == 'TK_OPERATOR' or self.flags.last_text == '=':
1030 # foo = function
1031 self.output.space_before_token = True
1032 elif not self.flags.multiline_frame and (self.is_expression(self.flags.mode) or self.is_array(self.flags.mode)):
1033 # (function
1034 pass
1035 else:
1036 self.print_newline()
1037  
1038 self.print_token(current_token)
1039 self.flags.last_word = current_token.text
1040 return
1041  
1042 prefix = 'NONE'
1043  
1044 if self.last_type == 'TK_END_BLOCK':
1045 if self.previous_flags.inline_frame:
1046 prefix = 'SPACE'
1047 elif not (current_token.type == 'TK_RESERVED' and current_token.text in ['else', 'catch', 'finally', 'from']):
1048 prefix = 'NEWLINE'
1049 else:
1050 if self.opts.brace_style in ['expand', 'end-expand'] or \
1051 (self.opts.brace_style == 'none' and current_token.wanted_newline):
1052 prefix = 'NEWLINE'
1053 else:
1054 prefix = 'SPACE'
1055 self.output.space_before_token = True
1056 elif self.last_type == 'TK_SEMICOLON' and self.flags.mode == MODE.BlockStatement:
1057 # TODO: Should this be for STATEMENT as well?
1058 prefix = 'NEWLINE'
1059 elif self.last_type == 'TK_SEMICOLON' and self.is_expression(self.flags.mode):
1060 prefix = 'SPACE'
1061 elif self.last_type == 'TK_STRING':
1062 prefix = 'NEWLINE'
1063 elif self.last_type == 'TK_RESERVED' or self.last_type == 'TK_WORD' or \
1064 (self.flags.last_text == '*' and (
1065 self.last_last_text in ['function', 'yield'] or
1066 (self.flags.mode == MODE.ObjectLiteral and self.last_last_text in ['{', ',']))):
1067 prefix = 'SPACE'
1068 elif self.last_type == 'TK_START_BLOCK':
1069 if self.flags.inline_frame:
1070 prefix = 'SPACE'
1071 else:
1072 prefix = 'NEWLINE'
1073 elif self.last_type == 'TK_END_EXPR':
1074 self.output.space_before_token = True
1075 prefix = 'NEWLINE'
1076  
1077 if current_token.type == 'TK_RESERVED' and current_token.text in Tokenizer.line_starters and self.flags.last_text != ')':
1078 if self.flags.inline_frame or self.flags.last_text == 'else ' or self.flags.last_text == 'export':
1079 prefix = 'SPACE'
1080 else:
1081 prefix = 'NEWLINE'
1082  
1083 if current_token.type == 'TK_RESERVED' and current_token.text in ['else', 'catch', 'finally']:
1084 if ((not (self.last_type == 'TK_END_BLOCK' and self.previous_flags.mode == MODE.BlockStatement)) \
1085 or self.opts.brace_style == 'expand' \
1086 or self.opts.brace_style == 'end-expand' \
1087 or (self.opts.brace_style == 'none' and current_token.wanted_newline)) \
1088 and not self.flags.inline_frame:
1089 self.print_newline()
1090 else:
1091 self.output.trim(True)
1092 # If we trimmed and there's something other than a close block before us
1093 # put a newline back in. Handles '} // comment' scenario.
1094 if self.output.current_line.last() != '}':
1095 self.print_newline()
1096  
1097 self.output.space_before_token = True
1098  
1099 elif prefix == 'NEWLINE':
1100 if self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text):
1101 # no newline between return nnn
1102 self.output.space_before_token = True
1103 elif self.last_type != 'TK_END_EXPR':
1104 if (self.last_type != 'TK_START_EXPR' or not (current_token.type == 'TK_RESERVED' and current_token.text in ['var', 'let', 'const'])) and self.flags.last_text != ':':
1105 # no need to force newline on VAR -
1106 # for (var x = 0...
1107 if current_token.type == 'TK_RESERVED' and current_token.text == 'if' and self.flags.last_text == 'else':
1108 self.output.space_before_token = True
1109 else:
1110 self.print_newline()
1111 elif current_token.type == 'TK_RESERVED' and current_token.text in Tokenizer.line_starters and self.flags.last_text != ')':
1112 self.print_newline()
1113 elif self.flags.multiline_frame and self.is_array(self.flags.mode) and self.flags.last_text == ',' and self.last_last_text == '}':
1114 self.print_newline() # }, in lists get a newline
1115 elif prefix == 'SPACE':
1116 self.output.space_before_token = True
1117  
1118  
1119 self.print_token(current_token)
1120 self.flags.last_word = current_token.text
1121  
1122 if current_token.type == 'TK_RESERVED':
1123 if current_token.text == 'do':
1124 self.flags.do_block = True
1125 elif current_token.text == 'if':
1126 self.flags.if_block = True
1127 elif current_token.text == 'import':
1128 self.flags.import_block = True
1129 elif current_token.text == 'from' and self.flags.import_block:
1130 self.flags.import_block = False
1131  
1132  
1133 def handle_semicolon(self, current_token):
1134 if self.start_of_statement(current_token):
1135 # The conditional starts the statement if appropriate.
1136 # Semicolon can be the start (and end) of a statement
1137 self.output.space_before_token = False
1138 else:
1139 self.handle_whitespace_and_comments(current_token)
1140  
1141 next_token = self.get_token(1)
1142 while (self.flags.mode == MODE.Statement and
1143 not (self.flags.if_block and next_token and next_token.type == 'TK_RESERVED' and next_token.text == 'else') and
1144 not self.flags.do_block):
1145 self.restore_mode()
1146  
1147 if self.flags.import_block:
1148 self.flags.import_block = False
1149  
1150 self.print_token(current_token)
1151  
1152  
1153 def handle_string(self, current_token):
1154 if self.start_of_statement(current_token):
1155 # The conditional starts the statement if appropriate.
1156 # One difference - strings want at least a space before
1157 self.output.space_before_token = True
1158 else:
1159 self.handle_whitespace_and_comments(current_token)
1160  
1161 if self.last_type == 'TK_RESERVED' or self.last_type == 'TK_WORD' or self.flags.inline_frame:
1162 self.output.space_before_token = True
1163 elif self.last_type in ['TK_COMMA', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']:
1164 if not self.start_of_object_property():
1165 self.allow_wrap_or_preserved_newline(current_token)
1166 else:
1167 self.print_newline()
1168  
1169 self.print_token(current_token)
1170  
1171  
1172 def handle_equals(self, current_token):
1173 if self.start_of_statement(current_token):
1174 # The conditional starts the statement if appropriate.
1175 pass
1176 else:
1177 self.handle_whitespace_and_comments(current_token)
1178  
1179  
1180 if self.flags.declaration_statement:
1181 # just got an '=' in a var-line, different line breaking rules will apply
1182 self.flags.declaration_assignment = True
1183  
1184 self.output.space_before_token = True
1185 self.print_token(current_token)
1186 self.output.space_before_token = True
1187  
1188  
1189 def handle_comma(self, current_token):
1190 self.handle_whitespace_and_comments(current_token, True)
1191  
1192 self.print_token(current_token)
1193 self.output.space_before_token = True
1194  
1195 if self.flags.declaration_statement:
1196 if self.is_expression(self.flags.parent.mode):
1197 # do not break on comma, for ( var a = 1, b = 2
1198 self.flags.declaration_assignment = False
1199  
1200 if self.flags.declaration_assignment:
1201 self.flags.declaration_assignment = False
1202 self.print_newline(preserve_statement_flags = True)
1203 elif self.opts.comma_first:
1204 # for comma-first, we want to allow a newline before the comma
1205 # to turn into a newline after the comma, which we will fixup later
1206 self.allow_wrap_or_preserved_newline(current_token)
1207  
1208 elif self.flags.mode == MODE.ObjectLiteral \
1209 or (self.flags.mode == MODE.Statement and self.flags.parent.mode == MODE.ObjectLiteral):
1210 if self.flags.mode == MODE.Statement:
1211 self.restore_mode()
1212  
1213 if not self.flags.inline_frame:
1214 self.print_newline()
1215 elif self.opts.comma_first:
1216 # EXPR or DO_BLOCK
1217 # for comma-first, we want to allow a newline before the comma
1218 # to turn into a newline after the comma, which we will fixup later
1219 self.allow_wrap_or_preserved_newline(current_token)
1220  
1221  
1222 def handle_operator(self, current_token):
1223 isGeneratorAsterisk = current_token.text == '*' and \
1224 ((self.last_type == 'TK_RESERVED' and self.flags.last_text in ['function', 'yield']) or
1225 (self.last_type in ['TK_START_BLOCK', 'TK_COMMA', 'TK_END_BLOCK', 'TK_SEMICOLON']))
1226 isUnary = current_token.text in ['+', '-'] \
1227 and (self.last_type in ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR'] \
1228 or self.flags.last_text in Tokenizer.line_starters or self.flags.last_text == ',')
1229  
1230 if self.start_of_statement(current_token):
1231 # The conditional starts the statement if appropriate.
1232 pass
1233 else:
1234 preserve_statement_flags = not isGeneratorAsterisk
1235 self.handle_whitespace_and_comments(current_token, preserve_statement_flags)
1236  
1237 if self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text):
1238 # return had a special handling in TK_WORD
1239 self.output.space_before_token = True
1240 self.print_token(current_token)
1241 return
1242  
1243 # hack for actionscript's import .*;
1244 if current_token.text == '*' and self.last_type == 'TK_DOT':
1245 self.print_token(current_token)
1246 return
1247  
1248 if current_token.text == '::':
1249 # no spaces around the exotic namespacing syntax operator
1250 self.print_token(current_token)
1251 return
1252  
1253 # Allow line wrapping between operators when operator_position is
1254 # set to before or preserve
1255 if self.last_type == 'TK_OPERATOR' and self.opts.operator_position in OPERATOR_POSITION_BEFORE_OR_PRESERVE:
1256 self.allow_wrap_or_preserved_newline(current_token)
1257  
1258 if current_token.text == ':' and self.flags.in_case:
1259 self.flags.case_body = True
1260 self.indent()
1261 self.print_token(current_token)
1262 self.print_newline()
1263 self.flags.in_case = False
1264 return
1265  
1266 space_before = True
1267 space_after = True
1268 in_ternary = False
1269  
1270 if current_token.text == ':':
1271 if self.flags.ternary_depth == 0:
1272 # Colon is invalid javascript outside of ternary and object, but do our best to guess what was meant.
1273 space_before = False
1274 else:
1275 self.flags.ternary_depth -= 1
1276 in_ternary = True
1277 elif current_token.text == '?':
1278 self.flags.ternary_depth += 1
1279  
1280 # let's handle the operator_position option prior to any conflicting logic
1281 if (not isUnary) and (not isGeneratorAsterisk) and \
1282 self.opts.preserve_newlines and current_token.text in Tokenizer.positionable_operators:
1283  
1284 isColon = current_token.text == ':'
1285 isTernaryColon = isColon and in_ternary
1286 isOtherColon = isColon and not in_ternary
1287  
1288 if self.opts.operator_position == OPERATOR_POSITION['before_newline']:
1289 # if the current token is : and it's not a ternary statement then we set space_before to false
1290 self.output.space_before_token = not isOtherColon
1291  
1292 self.print_token(current_token)
1293  
1294 if (not isColon) or isTernaryColon:
1295 self.allow_wrap_or_preserved_newline(current_token)
1296  
1297 self.output.space_before_token = True
1298  
1299 return
1300  
1301 elif self.opts.operator_position == OPERATOR_POSITION['after_newline']:
1302 # if the current token is anything but colon, or (via deduction) it's a colon and in a ternary statement,
1303 # then print a newline.
1304 self.output.space_before_token = True
1305  
1306 if (not isColon) or isTernaryColon:
1307 if self.get_token(1).wanted_newline:
1308 self.print_newline(preserve_statement_flags = True)
1309 else:
1310 self.allow_wrap_or_preserved_newline(current_token)
1311 else:
1312 self.output.space_before_token = False
1313  
1314 self.print_token(current_token)
1315  
1316 self.output.space_before_token = True
1317 return
1318  
1319 elif self.opts.operator_position == OPERATOR_POSITION['preserve_newline']:
1320 if not isOtherColon:
1321 self.allow_wrap_or_preserved_newline(current_token)
1322  
1323 # if we just added a newline, or the current token is : and it's not a ternary statement,
1324 # then we set space_before to false
1325 self.output.space_before_token = not (self.output.just_added_newline() or isOtherColon)
1326  
1327 self.print_token(current_token)
1328  
1329 self.output.space_before_token = True
1330 return
1331  
1332 if isGeneratorAsterisk:
1333 self.allow_wrap_or_preserved_newline(current_token)
1334 space_before = False
1335 next_token = self.get_token(1)
1336 space_after = next_token and next_token.type in ['TK_WORD','TK_RESERVED']
1337 elif current_token.text == '...':
1338 self.allow_wrap_or_preserved_newline(current_token)
1339 space_before = self.last_type == 'TK_START_BLOCK'
1340 space_after = False
1341 elif current_token.text in ['--', '++', '!', '~'] or isUnary:
1342 space_before = False
1343 space_after = False
1344  
1345 # http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1
1346 # if there is a newline between -- or ++ and anything else we should preserve it.
1347 if current_token.wanted_newline and (current_token.text == '--' or current_token.text == '++'):
1348 self.print_newline(preserve_statement_flags = True)
1349  
1350 if self.flags.last_text == ';' and self.is_expression(self.flags.mode):
1351 # for (;; ++i)
1352 # ^^
1353 space_before = True
1354  
1355 if self.last_type == 'TK_RESERVED':
1356 space_before = True
1357 elif self.last_type == 'TK_END_EXPR':
1358 space_before = not (self.flags.last_text == ']' and current_token.text in ['--', '++'])
1359 elif self.last_type == 'TK_OPERATOR':
1360 # a++ + ++b
1361 # a - -b
1362 space_before = current_token.text in ['--', '-','++', '+'] and self.flags.last_text in ['--', '-','++', '+']
1363 # + and - are not unary when preceeded by -- or ++ operator
1364 # a-- + b
1365 # a * +b
1366 # a - -b
1367 if current_token.text in ['-', '+'] and self.flags.last_text in ['--', '++']:
1368 space_after = True
1369  
1370 if (((self.flags.mode == MODE.BlockStatement and not self.flags.inline_frame) or self.flags.mode == MODE.Statement)
1371 and self.flags.last_text in ['{', ';']):
1372 # { foo: --i }
1373 # foo(): --bar
1374 self.print_newline()
1375  
1376 if space_before:
1377 self.output.space_before_token = True
1378  
1379 self.print_token(current_token)
1380  
1381 if space_after:
1382 self.output.space_before_token = True
1383  
1384  
1385  
1386 def handle_block_comment(self, current_token, preserve_statement_flags):
1387 if self.output.raw:
1388 self.output.add_raw_token(current_token)
1389 if current_token.directives and current_token.directives.get('preserve') == 'end':
1390 # If we're testing the raw output behavior, do not allow a directive to turn it off.
1391 self.output.raw = self.opts.test_output_raw
1392 return
1393  
1394 if current_token.directives:
1395 self.print_newline(preserve_statement_flags = preserve_statement_flags)
1396 self.print_token(current_token)
1397 if current_token.directives.get('preserve') == 'start':
1398 self.output.raw = True
1399  
1400 self.print_newline(preserve_statement_flags = True)
1401 return
1402  
1403 # inline block
1404 if not self.acorn.newline.search(current_token.text) and not current_token.wanted_newline:
1405 self.output.space_before_token = True
1406 self.print_token(current_token)
1407 self.output.space_before_token = True
1408 return
1409  
1410 lines = self.acorn.allLineBreaks.split(current_token.text)
1411 javadoc = False
1412 starless = False
1413 last_indent = current_token.whitespace_before
1414 last_indent_length = len(last_indent)
1415  
1416 # block comment starts with a new line
1417 self.print_newline(preserve_statement_flags = preserve_statement_flags)
1418 if len(lines) > 1:
1419 javadoc = not any(l for l in lines[1:] if ( l.strip() == '' or (l.lstrip())[0] != '*'))
1420 starless = all(l.startswith(last_indent) or l.strip() == '' for l in lines[1:])
1421  
1422 # first line always indented
1423 self.print_token(current_token, lines[0])
1424 for line in lines[1:]:
1425 self.print_newline(preserve_statement_flags = True)
1426 if javadoc:
1427 # javadoc: reformat and re-indent
1428 self.print_token(current_token, ' ' + line.lstrip())
1429 elif starless and len(line) > last_indent_length:
1430 # starless: re-indent non-empty content, avoiding trim
1431 self.print_token(current_token, line[last_indent_length:])
1432 else:
1433 # normal comments output raw
1434 self.output.add_token(line)
1435  
1436 self.print_newline(preserve_statement_flags = preserve_statement_flags)
1437  
1438 def handle_comment(self, current_token, preserve_statement_flags):
1439 if current_token.wanted_newline:
1440 self.print_newline(preserve_statement_flags = preserve_statement_flags)
1441  
1442 if not current_token.wanted_newline:
1443 self.output.trim(True)
1444  
1445 self.output.space_before_token = True
1446 self.print_token(current_token)
1447 self.print_newline(preserve_statement_flags = preserve_statement_flags)
1448  
1449  
1450 def handle_dot(self, current_token):
1451 if self.start_of_statement(current_token):
1452 # The conditional starts the statement if appropriate.
1453 pass
1454 else:
1455 self.handle_whitespace_and_comments(current_token, True)
1456  
1457 if self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text):
1458 self.output.space_before_token = True
1459 else:
1460 # allow preserved newlines before dots in general
1461 # force newlines on dots after close paren when break_chained - for bar().baz()
1462 self.allow_wrap_or_preserved_newline(current_token,
1463 self.flags.last_text == ')' and self.opts.break_chained_methods)
1464  
1465 self.print_token(current_token)
1466  
1467 def handle_unknown(self, current_token, preserve_statement_flags):
1468 self.print_token(current_token)
1469 if current_token.text[-1] == '\n':
1470 self.print_newline(preserve_statement_flags = preserve_statement_flags)
1471  
1472 def handle_eof(self, current_token):
1473 # Unwind any open statements
1474 while self.flags.mode == MODE.Statement:
1475 self.restore_mode()
1476  
1477 self.handle_whitespace_and_comments(current_token)
1478  
1479  
1480  
1481 def mkdir_p(path):
1482 try:
1483 if path:
1484 os.makedirs(path)
1485 except OSError as exc: # Python >2.5
1486 if exc.errno == errno.EEXIST and os.path.isdir(path):
1487 pass
1488 else:
1489 raise Exception()
1490  
1491 # Using object instead of string to allow for later expansion of info about each line
1492 class OutputLine:
1493 def __init__(self, parent):
1494 self.__parent = parent
1495 self.__character_count = 0
1496 self.__indent_count = -1
1497  
1498 self.__items = []
1499 self.__empty = True
1500  
1501 def get_character_count(self):
1502 return self.__character_count
1503  
1504 def is_empty(self):
1505 return self.__empty
1506  
1507 def set_indent(self, level):
1508 self.__character_count = self.__parent.baseIndentLength + level * self.__parent.indent_length
1509 self.__indent_count = level;
1510  
1511 def last(self):
1512 if not self.is_empty():
1513 return self.__items[-1]
1514 else:
1515 return None
1516  
1517 def push(self, input):
1518 self.__items.append(input)
1519 self.__character_count += len(input)
1520 self.__empty = False
1521  
1522  
1523 def pop(self):
1524 item = None
1525 if not self.is_empty():
1526 item = self.__items.pop()
1527 self.__character_count -= len(item)
1528 self.__empty = len(self.__items) == 0
1529 return item
1530  
1531 def remove_indent(self):
1532 if self.__indent_count > 0:
1533 self.__indent_count -= 1
1534 self.__character_count -= self.__parent.indent_length
1535  
1536 def trim(self):
1537 while self.last() == ' ':
1538 item = self._items.pop()
1539 self.__character_count -= 1
1540 self.__empty = len(self.__items) == 0
1541  
1542 def toString(self):
1543 result = ''
1544 if not self.is_empty():
1545 if self.__indent_count >= 0:
1546 result = self.__parent.indent_cache[self.__indent_count]
1547 result += ''.join(self.__items)
1548 return result
1549  
1550  
1551 class Output:
1552 def __init__(self, indent_string, baseIndentString = ''):
1553  
1554 self.indent_string = indent_string
1555 self.baseIndentString = baseIndentString
1556 self.indent_cache = [ baseIndentString ]
1557 self.baseIndentLength = len(baseIndentString)
1558 self.indent_length = len(indent_string)
1559 self.raw = False
1560 self.lines = []
1561 self.previous_line = None
1562 self.current_line = None
1563 self.space_before_token = False
1564 self.add_outputline()
1565  
1566 def add_outputline(self):
1567 self.previous_line = self.current_line
1568 self.current_line = OutputLine(self)
1569 self.lines.append(self.current_line)
1570  
1571 def get_line_number(self):
1572 return len(self.lines)
1573  
1574 def add_new_line(self, force_newline):
1575 if len(self.lines) == 1 and self.just_added_newline():
1576 # no newline on start of file
1577 return False
1578  
1579 if force_newline or not self.just_added_newline():
1580 if not self.raw:
1581 self.add_outputline()
1582 return True
1583 return False
1584  
1585 def get_code(self):
1586 sweet_code = "\n".join(line.toString() for line in self.lines)
1587 return re.sub('[\r\n\t ]+$', '', sweet_code)
1588  
1589 def set_indent(self, level):
1590 # Never indent your first output indent at the start of the file
1591 if len(self.lines) > 1:
1592 while level >= len(self.indent_cache):
1593 self.indent_cache.append(self.indent_cache[-1] + self.indent_string)
1594  
1595  
1596 self.current_line.set_indent(level)
1597 return True
1598 self.current_line.set_indent(0)
1599 return False
1600  
1601 def add_raw_token(self, token):
1602 for _ in range(token.newlines):
1603 self.add_outputline()
1604  
1605 self.current_line.push(token.whitespace_before)
1606 self.current_line.push(token.text)
1607 self.space_before_token = False
1608  
1609 def add_token(self, printable_token):
1610 self.add_space_before_token()
1611 self.current_line.push(printable_token)
1612  
1613 def add_space_before_token(self):
1614 if self.space_before_token and not self.just_added_newline():
1615 self.current_line.push(' ')
1616 self.space_before_token = False
1617  
1618 def remove_redundant_indentation(self, frame):
1619 # This implementation is effective but has some issues:
1620 # - can cause line wrap to happen too soon due to indent removal
1621 # after wrap points are calculated
1622 # These issues are minor compared to ugly indentation.
1623  
1624 if frame.multiline_frame or frame.mode == MODE.ForInitializer or frame.mode == MODE.Conditional:
1625 return
1626  
1627 # remove one indent from each line inside this section
1628 index = frame.start_line_index
1629 while index < len(self.lines):
1630 self.lines[index].remove_indent()
1631 index += 1
1632  
1633 def trim(self, eat_newlines = False):
1634 self.current_line.trim()
1635  
1636 while eat_newlines and len(self.lines) > 1 and self.current_line.is_empty():
1637 self.lines.pop()
1638 self.current_line = self.lines[-1]
1639 self.current_line.trim()
1640  
1641 if len(self.lines) > 1:
1642 self.previous_line = self.lines[-2]
1643 else:
1644 self.previous_line = None
1645  
1646 def just_added_newline(self):
1647 return self.current_line.is_empty()
1648  
1649 def just_added_blankline(self):
1650 if self.just_added_newline():
1651 if len(self.lines) == 1:
1652 return True
1653  
1654 line = self.lines[-2]
1655 return line.is_empty()
1656  
1657 return False
1658  
1659 class InputScanner:
1660 def __init__(self, input):
1661 self.__input = input
1662 self.__input_length = len(self.__input)
1663 self.__position = 0
1664  
1665 def back(self):
1666 self.__position -= 1
1667  
1668 def hasNext(self):
1669 return self.__position < self.__input_length
1670  
1671 def next(self):
1672 val = None
1673 if self.hasNext():
1674 val = self.__input[self.__position]
1675 self.__position += 1
1676  
1677 return val;
1678  
1679 def peek(self, index = 0):
1680 val = None
1681 index += self.__position;
1682 if index >= 0 and index < self.__input_length:
1683 val = self.__input[index];
1684  
1685 return val;
1686  
1687 def peekCharCode(self, index = 0):
1688 val = 0
1689 index += self.__position;
1690 if index >= 0 and index < self.__input_length:
1691 val = ord(self.__input[index])
1692  
1693 return val
1694  
1695 def test(self, pattern, index = 0):
1696 index += self.__position;
1697 return index >= 0 and index < self.__input_length and pattern.match(self.__input, index)
1698  
1699 def testChar(self, pattern, index = 0):
1700 val = self.peek(index)
1701 return val != None and pattern.match(val)
1702  
1703 def match(self, pattern):
1704 pattern_match = None
1705 if self.hasNext():
1706 pattern_match = pattern.match(self.__input, self.__position)
1707 if pattern_match:
1708 self.__position += len(pattern_match.group(0));
1709  
1710 return pattern_match
1711  
1712  
1713 class Tokenizer:
1714  
1715 whitespace = ["\n", "\r", "\t", " "]
1716 digit = re.compile('[0-9]')
1717 digit_bin = re.compile('[01]')
1718 digit_oct = re.compile('[01234567]')
1719 digit_hex = re.compile('[0123456789abcdefABCDEF]')
1720  
1721 positionable_operators = '!= !== % & && * ** + - / : < << <= == === > >= >> >>> ? ^ | ||'.split(' ')
1722 punct = (positionable_operators +
1723 # non-positionable operators - these do not follow operator position settings
1724 '! %= &= *= **= ++ += , -- -= /= :: <<= = => >>= >>>= ^= |= ~ ...'.split(' '))
1725  
1726 # Words which always should start on a new line
1727 line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,import,export'.split(',')
1728 reserved_words = line_starters + ['do', 'in', 'of', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof', 'yield', 'async', 'await', 'from', 'as']
1729  
1730 def __init__ (self, input_string, opts, indent_string):
1731 self.input = InputScanner(input_string)
1732 self.opts = opts
1733 self.indent_string = indent_string
1734 self.acorn = Acorn()
1735 # /* ... */ comment ends with nearest */ or end of file
1736 self.block_comment_pattern = re.compile('([\s\S]*?)((?:\*\/)|$)')
1737  
1738 # comment ends just before nearest linefeed or end of file
1739 self.comment_pattern = re.compile(self.acorn.six.u('([^\n\r\u2028\u2029]*)'))
1740  
1741 self.directives_block_pattern = re.compile('\/\* beautify( \w+[:]\w+)+ \*\/')
1742 self.directive_pattern = re.compile(' (\w+)[:](\w+)')
1743 self.directives_end_ignore_pattern = re.compile('([\s\S]*?)((?:\/\*\sbeautify\signore:end\s\*\/)|$)')
1744  
1745 self.template_pattern = re.compile('((<\?php|<\?=)[\s\S]*?\?>)|(<%[\s\S]*?%>)')
1746  
1747 def tokenize(self):
1748 self.in_html_comment = False
1749 self.tokens = []
1750  
1751 next = None
1752 last = None
1753 open = None
1754 open_stack = []
1755 comments = []
1756  
1757 while not (not last == None and last.type == 'TK_EOF'):
1758 token_values = self.__tokenize_next()
1759 next = Token(token_values[1], token_values[0], self.n_newlines, self.whitespace_before_token)
1760  
1761 while next.type == 'TK_COMMENT' or next.type == 'TK_BLOCK_COMMENT' or next.type == 'TK_UNKNOWN':
1762 if next.type == 'TK_BLOCK_COMMENT':
1763 next.directives = token_values[2]
1764  
1765 comments.append(next)
1766 token_values = self.__tokenize_next()
1767 next = Token(token_values[1], token_values[0], self.n_newlines, self.whitespace_before_token)
1768  
1769 if len(comments) > 0:
1770 next.comments_before = comments
1771 comments = []
1772  
1773 if next.type == 'TK_START_BLOCK' or next.type == 'TK_START_EXPR':
1774 next.parent = last
1775 open_stack.append(open)
1776 open = next
1777 elif (next.type == 'TK_END_BLOCK' or next.type == 'TK_END_EXPR') and \
1778 (not open == None and ( \
1779 (next.text == ']' and open.text == '[') or \
1780 (next.text == ')' and open.text == '(') or \
1781 (next.text == '}' and open.text == '{'))):
1782 next.parent = open.parent
1783 next.opened = open
1784 open = open_stack.pop()
1785  
1786 self.tokens.append(next)
1787 last = next
1788 return self.tokens
1789  
1790 def get_directives (self, text):
1791 if not self.directives_block_pattern.match(text):
1792 return None
1793  
1794 directives = {}
1795 directive_match = self.directive_pattern.search(text)
1796 while directive_match:
1797 directives[directive_match.group(1)] = directive_match.group(2)
1798 directive_match = self.directive_pattern.search(text, directive_match.end())
1799  
1800 return directives
1801  
1802  
1803 def __tokenize_next(self):
1804  
1805 whitespace_on_this_line = []
1806 self.n_newlines = 0
1807 self.whitespace_before_token = ''
1808  
1809 c = self.input.next()
1810  
1811 if c == None:
1812 return '', 'TK_EOF'
1813  
1814 if len(self.tokens) > 0:
1815 last_token = self.tokens[-1]
1816 else:
1817 # For the sake of tokenizing we can pretend that there was on open brace to start
1818 last_token = Token('TK_START_BLOCK', '{')
1819  
1820 while c in self.whitespace:
1821 if self.acorn.newline.match(c):
1822 # treat \r\n as one newline
1823 if not (c == '\n' and self.input.peek(-2) == '\r'):
1824 self.n_newlines += 1
1825 whitespace_on_this_line = []
1826 else:
1827 whitespace_on_this_line.append(c)
1828  
1829 c = self.input.next()
1830  
1831 if c == None:
1832 return '', 'TK_EOF'
1833  
1834 if len(whitespace_on_this_line) != 0:
1835 self.whitespace_before_token = ''.join(whitespace_on_this_line)
1836  
1837 if self.digit.match(c) or (c == '.' and self.input.testChar(self.digit)):
1838 allow_decimal = True
1839 allow_e = True
1840 local_digit = self.digit
1841  
1842 if c == '0' and self.input.testChar(re.compile('[XxOoBb]')):
1843 # switch to hex/oct/bin number, no decimal or e, just hex/oct/bin digits
1844 allow_decimal = False
1845 allow_e = False
1846 if self.input.testChar(re.compile('[Bb]')):
1847 local_digit = self.digit_bin
1848 elif self.input.testChar(re.compile('[Oo]')):
1849 local_digit = self.digit_oct
1850 else:
1851 local_digit = self.digit_hex
1852 c += self.input.next()
1853 elif c == '.':
1854 # Already have a decimal for this literal, don't allow another
1855 allow_decimal = False
1856 else:
1857 # we know this first loop will run. It keeps the logic simpler.
1858 c = ''
1859 self.input.back()
1860  
1861 # Add the digits
1862 while self.input.testChar(local_digit):
1863 c += self.input.next()
1864  
1865 if allow_decimal and self.input.peek() == '.':
1866 c += self.input.next()
1867 allow_decimal = False
1868  
1869 # a = 1.e-7 is valid, so we test for . then e in one loop
1870 if allow_e and self.input.testChar(re.compile('[Ee]')):
1871 c += self.input.next()
1872  
1873 if self.input.testChar(re.compile('[+-]')):
1874 c += self.input.next()
1875  
1876 allow_e = False
1877 allow_decimal = False
1878  
1879 return c, 'TK_WORD'
1880  
1881 if self.acorn.isIdentifierStart(self.input.peekCharCode(-1)):
1882 if self.input.hasNext():
1883 while self.acorn.isIdentifierChar(self.input.peekCharCode()):
1884 c += self.input.next()
1885 if not self.input.hasNext():
1886 break
1887  
1888 if not (last_token.type == 'TK_DOT' \
1889 or (last_token.type == 'TK_RESERVED' and last_token.text in ['set', 'get'])) \
1890 and c in self.reserved_words:
1891 if c == 'in' or c == 'of': # in and of are operators, need to hack
1892 return c, 'TK_OPERATOR'
1893  
1894 return c, 'TK_RESERVED'
1895  
1896 return c, 'TK_WORD'
1897  
1898 if c in '([':
1899 return c, 'TK_START_EXPR'
1900  
1901 if c in ')]':
1902 return c, 'TK_END_EXPR'
1903  
1904 if c == '{':
1905 return c, 'TK_START_BLOCK'
1906  
1907 if c == '}':
1908 return c, 'TK_END_BLOCK'
1909  
1910 if c == ';':
1911 return c, 'TK_SEMICOLON'
1912  
1913 if c == '/':
1914 comment = ''
1915 inline_comment = True
1916 if self.input.peek() == '*': # peek /* .. */ comment
1917 self.input.next()
1918 comment_match = self.input.match(self.block_comment_pattern)
1919 comment = '/*' + comment_match.group(0)
1920  
1921 directives = self.get_directives(comment)
1922 if directives and directives.get('ignore') == 'start':
1923 comment_match = self.input.match(self.directives_end_ignore_pattern)
1924 comment += comment_match.group(0)
1925 comment = re.sub(self.acorn.allLineBreaks, '\n', comment)
1926 return comment, 'TK_BLOCK_COMMENT', directives
1927  
1928 if self.input.peek() == '/': # peek // comment
1929 self.input.next()
1930 comment_match = self.input.match(self.comment_pattern)
1931 comment = '//' + comment_match.group(0)
1932 return comment, 'TK_COMMENT'
1933  
1934 startXmlRegExp = re.compile('<()([-a-zA-Z:0-9_.]+|{[\s\S]+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{[\s\S]+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*(\'[^\']*\'|"[^"]*"|{[\s\S]+?}))*\s*(/?)\s*>')
1935  
1936 self.has_char_escapes = False
1937  
1938 if c == '`' or c == "'" or c == '"' or \
1939 ( \
1940 (c == '/') or \
1941 (self.opts.e4x and c == "<" and self.input.test(startXmlRegExp, -1)) \
1942 ) and ( \
1943 (last_token.type == 'TK_RESERVED' and last_token.text in ['return', 'case', 'throw', 'else', 'do', 'typeof', 'yield']) or \
1944 (last_token.type == 'TK_END_EXPR' and last_token.text == ')' and \
1945 last_token.parent and last_token.parent.type == 'TK_RESERVED' and last_token.parent.text in ['if', 'while', 'for']) or \
1946 (last_token.type in ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK', 'TK_END_BLOCK', 'TK_OPERATOR', \
1947 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA'])):
1948 sep = c
1949 esc = False
1950 esc1 = 0
1951 esc2 = 0
1952 resulting_string = c
1953 in_char_class = False
1954  
1955 if sep == '/':
1956 # handle regexp
1957 in_char_class = False
1958 while self.input.hasNext() and \
1959 (esc or in_char_class or self.input.peek()!= sep) and \
1960 not self.input.testChar(self.acorn.newline):
1961 resulting_string += self.input.peek()
1962 if not esc:
1963 esc = self.input.peek() == '\\'
1964 if self.input.peek() == '[':
1965 in_char_class = True
1966 elif self.input.peek() == ']':
1967 in_char_class = False
1968 else:
1969 esc = False
1970 self.input.next()
1971  
1972 elif self.opts.e4x and sep == '<':
1973 # handle e4x xml literals
1974 xmlRegExp = re.compile('[\s\S]*?<(\/?)([-a-zA-Z:0-9_.]+|{[\s\S]+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{[\s\S]+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*(\'[^\']*\'|"[^"]*"|{[\s\S]+?}))*\s*(/?)\s*>')
1975 self.input.back()
1976 xmlStr = ""
1977 match = self.input.match(xmlRegExp)
1978 if match:
1979 rootTag = match.group(2)
1980 rootTag = re.sub(r'^{\s+', '{', re.sub(r'\s+}$', '}', rootTag))
1981 isCurlyRoot = rootTag.startswith('{')
1982 depth = 0
1983 while (match):
1984 isEndTag = match.group(1)
1985 tagName = match.group(2)
1986 isSingletonTag = (match.groups()[-1] != "") or (match.group(2)[0:8] == "![CDATA[")
1987 if not isSingletonTag and (
1988 tagName == rootTag or (isCurlyRoot and re.sub(r'^{\s+', '{', re.sub(r'\s+}$', '}', tagName)))):
1989 if isEndTag:
1990 depth -= 1
1991 else:
1992 depth += 1
1993  
1994 xmlStr += match.group(0)
1995 if depth <= 0:
1996 break
1997  
1998 match = self.input.match(xmlRegExp)
1999  
2000  
2001 # if we didn't close correctly, keep unformatted.
2002 if not match:
2003 xmlStr += self.input.match(re.compile('[\s\S]*')).group(0)
2004  
2005 xmlStr = re.sub(self.acorn.allLineBreaks, '\n', xmlStr)
2006 return xmlStr, 'TK_STRING'
2007  
2008 else:
2009  
2010 # handle string
2011 def parse_string(self, resulting_string, delimiter, allow_unescaped_newlines = False, start_sub = None):
2012 esc = False
2013 while self.input.hasNext():
2014 current_char = self.input.peek()
2015 if not (esc or (current_char != delimiter and
2016 (allow_unescaped_newlines or not self.acorn.newline.match(current_char)))):
2017 break
2018  
2019 # Handle \r\n linebreaks after escapes or in template strings
2020 if (esc or allow_unescaped_newlines) and self.acorn.newline.match(current_char):
2021 if current_char == '\r' and self.input.peek(1) == '\n':
2022 self.input.next()
2023 current_char = self.input.peek()
2024  
2025 resulting_string += '\n'
2026 else:
2027 resulting_string += current_char
2028  
2029 if esc:
2030 if current_char == 'x' or current_char == 'u':
2031 self.has_char_escapes = True
2032  
2033 esc = False
2034 else:
2035 esc = current_char == '\\'
2036  
2037 self.input.next()
2038  
2039 if start_sub and resulting_string.endswith(start_sub):
2040 if delimiter == '`':
2041 resulting_string = parse_string(self, resulting_string, '}', allow_unescaped_newlines, '`')
2042 else:
2043 resulting_string = parse_string(self, resulting_string, '`', allow_unescaped_newlines, '${')
2044  
2045 if self.input.hasNext():
2046 resulting_string += self.input.next()
2047  
2048 return resulting_string
2049  
2050 if sep == '`':
2051 resulting_string = parse_string(self, resulting_string, '`', True, '${')
2052 else:
2053 resulting_string = parse_string(self, resulting_string, sep)
2054  
2055  
2056 if self.has_char_escapes and self.opts.unescape_strings:
2057 resulting_string = self.unescape_string(resulting_string)
2058  
2059 if self.input.peek() == sep:
2060 resulting_string += self.input.next()
2061  
2062 if sep == '/':
2063 # regexps may have modifiers /regexp/MOD, so fetch those too
2064 # Only [gim] are valid, but if the user puts in garbage, do what we can to take it.
2065 while self.input.hasNext() and self.acorn.isIdentifierStart(self.input.peekCharCode()):
2066 resulting_string += self.input.next()
2067  
2068 resulting_string = re.sub(self.acorn.allLineBreaks, '\n', resulting_string)
2069  
2070 return resulting_string, 'TK_STRING'
2071  
2072 if c == '#':
2073  
2074 # she-bang
2075 if len(self.tokens) == 0 and self.input.peek() == '!':
2076 resulting_string = c
2077 while self.input.hasNext() and c != '\n':
2078 c = self.input.next()
2079 resulting_string += c
2080 return resulting_string.strip() + '\n', 'TK_UNKNOWN'
2081  
2082  
2083 # Spidermonkey-specific sharp variables for circular references
2084 # https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
2085 # http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
2086 sharp = '#'
2087 if self.input.hasNext() and self.input.testChar(self.digit):
2088 while True:
2089 c = self.input.next()
2090 sharp += c
2091 if (not self.input.hasNext()) or c == '#' or c == '=':
2092 break
2093 if c == '#':
2094 pass
2095 elif self.input.peek() == '[' and self.input.peek(1) == ']':
2096 sharp += '[]'
2097 self.input.next()
2098 self.input.next()
2099 elif self.input.peek() == '{' and self.input.peek(1) == '}':
2100 sharp += '{}'
2101 self.input.next()
2102 self.input.next()
2103 return sharp, 'TK_WORD'
2104  
2105 if c == '<' and self.input.peek() in ['?', '%']:
2106 self.input.back()
2107 template_match = self.input.match(self.template_pattern)
2108 if template_match:
2109 c = template_match.group(0)
2110 c = re.sub(self.acorn.allLineBreaks, '\n', c)
2111 return c, 'TK_STRING'
2112  
2113  
2114 if c == '<' and self.input.match(re.compile('\!--')):
2115 c = '<!--'
2116 while self.input.hasNext() and not self.input.testChar(self.acorn.newline):
2117 c += self.input.next()
2118  
2119 self.in_html_comment = True
2120 return c, 'TK_COMMENT'
2121  
2122 if c == '-' and self.in_html_comment and self.input.match(re.compile('->')):
2123 self.in_html_comment = False
2124 return '-->', 'TK_COMMENT'
2125  
2126 if c == '.':
2127 if self.input.peek() == '.' and self.input.peek(1) == '.':
2128 c += self.input.next() + self.input.next()
2129 return c, 'TK_OPERATOR'
2130  
2131 return c, 'TK_DOT'
2132  
2133 if c in self.punct:
2134 while self.input.hasNext() and c + self.input.peek() in self.punct:
2135 c += self.input.next()
2136 if not self.input.hasNext():
2137 break
2138  
2139 if c == ',':
2140 return c, 'TK_COMMA'
2141 if c == '=':
2142 return c, 'TK_EQUALS'
2143  
2144 return c, 'TK_OPERATOR'
2145  
2146 return c, 'TK_UNKNOWN'
2147  
2148 def unescape_string(self, s):
2149 # You think that a regex would work for this
2150 # return s.replace(/\\x([0-9a-f]{2})/gi, function(match, val) {
2151 # return String.fromCharCode(parseInt(val, 16));
2152 # })
2153 # However, dealing with '\xff', '\\xff', '\\\xff' makes this more fun.
2154 out = self.acorn.six.u('')
2155 escaped = 0
2156  
2157 input_scan = InputScanner(s)
2158 matched = None
2159  
2160 while input_scan.hasNext():
2161 # Keep any whitespace, non-slash characters
2162 # also keep slash pairs.
2163 matched = input_scan.match(re.compile(r'([\s]|[^\\]|\\\\)+'))
2164  
2165 if matched:
2166 out += matched.group(0)
2167  
2168 if input_scan.peek() != '\\':
2169 continue
2170  
2171 input_scan.next()
2172 if input_scan.peek() == 'x':
2173 matched = input_scan.match(re.compile('x([0-9A-Fa-f]{2})'))
2174 elif input_scan.peek() == 'u':
2175 matched = input_scan.match(re.compile('u([0-9A-Fa-f]{4})'));
2176 else:
2177 out += '\\'
2178 if input_scan.hasNext():
2179 out += input_scan.next()
2180 continue
2181  
2182 # If there's some error decoding, return the original string
2183 if not matched:
2184 return s
2185  
2186 escaped = int(matched.group(1), 16)
2187  
2188 if escaped > 0x7e and escaped <= 0xff and matched.group(0).startswith('x'):
2189 # we bail out on \x7f..\xff,
2190 # leaving whole string escaped,
2191 # as it's probably completely binary
2192 return s
2193 elif escaped >= 0x00 and escaped < 0x20:
2194 # leave 0x00...0x1f escaped
2195 out += '\\' + matched.group(0)
2196 continue
2197 elif escaped == 0x22 or escaped == 0x27 or escaped == 0x5c:
2198 # single-quote, apostrophe, backslash - escape these
2199 out += ('\\' + chr(escaped))
2200 else:
2201 out += self.acorn.six.unichr(escaped)
2202  
2203 return out
2204  
2205 def isFileDifferent(filepath, expected):
2206 try:
2207 return (''.join(io.open(filepath, 'rt', newline='').readlines()) != expected)
2208 except:
2209 return True
2210  
2211  
2212 def main():
2213  
2214 argv = sys.argv[1:]
2215  
2216 try:
2217 opts, args = getopt.getopt(argv, "s:c:e:o:rdEPjabkil:xhtfvXnCO:w:",
2218 ['indent-size=','indent-char=','eol=''outfile=', 'replace', 'disable-preserve-newlines',
2219 'space-in-paren', 'space-in-empty-paren', 'jslint-happy', 'space-after-anon-function',
2220 'brace-style=', 'keep-array-indentation', 'indent-level=', 'unescape-strings',
2221 'help', 'usage', 'stdin', 'eval-code', 'indent-with-tabs', 'keep-function-indentation', 'version',
2222 'e4x', 'end-with-newline','comma-first','operator-position=','wrap-line-length','editorconfig'])
2223 except getopt.GetoptError as ex:
2224 print(ex, file=sys.stderr)
2225 return usage(sys.stderr)
2226  
2227 js_options = default_options()
2228  
2229 file = None
2230 outfile = 'stdout'
2231 replace = False
2232 if len(args) == 1:
2233 file = args[0]
2234  
2235 for opt, arg in opts:
2236 if opt in ('--keep-array-indentation', '-k'):
2237 js_options.keep_array_indentation = True
2238 if opt in ('--keep-function-indentation','-f'):
2239 js_options.keep_function_indentation = True
2240 elif opt in ('--outfile', '-o'):
2241 outfile = arg
2242 elif opt in ('--replace', '-r'):
2243 replace = True
2244 elif opt in ('--indent-size', '-s'):
2245 js_options.indent_size = int(arg)
2246 elif opt in ('--indent-char', '-c'):
2247 js_options.indent_char = arg
2248 elif opt in ('--eol', '-e'):
2249 js_options.eol = arg
2250 elif opt in ('--indent-with-tabs', '-t'):
2251 js_options.indent_with_tabs = True
2252 elif opt in ('--disable-preserve-newlines', '-d'):
2253 js_options.preserve_newlines = False
2254 elif opt in ('--space-in-paren', '-P'):
2255 js_options.space_in_paren = True
2256 elif opt in ('--space-in-empty-paren', '-E'):
2257 js_options.space_in_empty_paren = True
2258 elif opt in ('--jslint-happy', '-j'):
2259 js_options.jslint_happy = True
2260 elif opt in ('--space_after_anon_function', '-a'):
2261 js_options.space_after_anon_function = True
2262 elif opt in ('--eval-code'):
2263 js_options.eval_code = True
2264 elif opt in ('--brace-style', '-b'):
2265 js_options.brace_style = arg
2266 elif opt in ('--unescape-strings', '-x'):
2267 js_options.unescape_strings = True
2268 elif opt in ('--e4x', '-X'):
2269 js_options.e4x = True
2270 elif opt in ('--end-with-newline', '-n'):
2271 js_options.end_with_newline = True
2272 elif opt in ('--comma-first', '-C'):
2273 js_options.comma_first = True
2274 elif opt in ('--operator-position', '-O'):
2275 js_options.operator_position = sanitizeOperatorPosition(arg)
2276 elif opt in ('--wrap-line-length ', '-w'):
2277 js_options.wrap_line_length = int(arg)
2278 elif opt in ('--stdin', '-i'):
2279 file = '-'
2280 elif opt in ('--editorconfig'):
2281 js_options.editorconfig = True
2282 elif opt in ('--version', '-v'):
2283 return print(__version__)
2284 elif opt in ('--help', '--usage', '-h'):
2285 return usage()
2286  
2287  
2288 if not file:
2289 file = '-'
2290  
2291 try:
2292 if outfile == 'stdout' and replace and not file == '-':
2293 outfile = file
2294  
2295 # Editorconfig used only on files, not stdin
2296 if getattr(js_options, 'editorconfig'):
2297 editorconfig_filepath = file
2298  
2299 if editorconfig_filepath == '-':
2300 if outfile != 'stdout':
2301 editorconfig_filepath = outfile
2302 else:
2303 fileType = 'js'
2304 editorconfig_filepath = 'stdin.' + fileType
2305  
2306 # debug("EditorConfig is enabled for ", editorconfig_filepath);
2307 js_options = copy.copy(js_options)
2308 set_file_editorconfig_opts(editorconfig_filepath, js_options)
2309  
2310 pretty = beautify_file(file, js_options)
2311  
2312 if outfile == 'stdout':
2313 # python automatically converts newlines in text to "\r\n" when on windows
2314 # switch to binary to prevent this
2315 if sys.platform == "win32":
2316 import msvcrt
2317 msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
2318  
2319 sys.stdout.write(pretty)
2320 else:
2321 if isFileDifferent(outfile, pretty):
2322 mkdir_p(os.path.dirname(outfile))
2323  
2324 # python automatically converts newlines in text to "\r\n" when on windows
2325 # set newline to empty to prevent this
2326 with io.open(outfile, 'wt', newline='') as f:
2327 print('writing ' + outfile, file=sys.stderr)
2328 try:
2329 f.write(pretty)
2330 except TypeError:
2331 # This is not pretty, but given how we did the version import
2332 # it is the only way to do this without having setup.py fail on a missing six dependency.
2333 six = __import__("six")
2334 f.write(six.u(pretty))
2335  
2336  
2337 except Exception as ex:
2338 print(ex, file=sys.stderr)
2339 return 1
2340  
2341 # Success
2342 return 0