corrade-nucleus-nucleons – Blame information for rev 4
?pathlinks?
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 |