js-beautify 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitmodules +4 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +27 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +13 -0
- data/Rakefile +29 -0
- data/VERSION +1 -0
- data/bin/js-beautify +5 -0
- data/init.sh +3 -0
- data/js-beautify-copy/.gitmodules +3 -0
- data/js-beautify-copy/Makefile +71 -0
- data/js-beautify-copy/README.md +39 -0
- data/js-beautify-copy/attic/beautify-cl/beautify-cl.js +142 -0
- data/js-beautify-copy/attic/bin/beautify_js +80 -0
- data/js-beautify-copy/attic/opera-userscript/beautifier.js +1087 -0
- data/js-beautify-copy/attic/opera-userscript/make_opera_userscript.sh +42 -0
- data/js-beautify-copy/attic/qtscript/jsbeautify.cpp +121 -0
- data/js-beautify-copy/attic/qtscript/jsbeautify.pro +5 -0
- data/js-beautify-copy/attic/qtscript/jsbeautify.qrc +6 -0
- data/js-beautify-copy/attic/qtscript/readme.txt +28 -0
- data/js-beautify-copy/attic/readme.txt +2 -0
- data/js-beautify-copy/attic/unmaintained/bbedit/jsBeautify_BBED.scpt +522 -0
- data/js-beautify-copy/attic/unmaintained/c-sharp/JSBeautify.cs +801 -0
- data/js-beautify-copy/attic/v8/README.txt +40 -0
- data/js-beautify-copy/attic/v8/beautify.h +2390 -0
- data/js-beautify-copy/attic/v8/jsbeautify.cpp +215 -0
- data/js-beautify-copy/beautify-css.js +198 -0
- data/js-beautify-copy/beautify-html.js +514 -0
- data/js-beautify-copy/beautify.js +1293 -0
- data/js-beautify-copy/favicon.png +0 -0
- data/js-beautify-copy/index.html +401 -0
- data/js-beautify-copy/jquery/jquery.cookie.js +96 -0
- data/js-beautify-copy/jquery/jquery.js +167 -0
- data/js-beautify-copy/license.txt +24 -0
- data/js-beautify-copy/php/jsbeautifier.php +1599 -0
- data/js-beautify-copy/php/test.php +476 -0
- data/js-beautify-copy/python/MANIFEST.in +2 -0
- data/js-beautify-copy/python/js-beautify +7 -0
- data/js-beautify-copy/python/js-beautify-profile +16 -0
- data/js-beautify-copy/python/js-beautify-test +10 -0
- data/js-beautify-copy/python/jsbeautifier/__init__.py +1166 -0
- data/js-beautify-copy/python/jsbeautifier/tests/__init__.py +1 -0
- data/js-beautify-copy/python/jsbeautifier/tests/testindentation.py +43 -0
- data/js-beautify-copy/python/jsbeautifier/tests/testjsbeautifier.py +464 -0
- data/js-beautify-copy/python/jsbeautifier/unpackers/README.specs.mkd +25 -0
- data/js-beautify-copy/python/jsbeautifier/unpackers/__init__.py +67 -0
- data/js-beautify-copy/python/jsbeautifier/unpackers/evalbased.py +39 -0
- data/js-beautify-copy/python/jsbeautifier/unpackers/javascriptobfuscator.py +58 -0
- data/js-beautify-copy/python/jsbeautifier/unpackers/myobfuscate.py +86 -0
- data/js-beautify-copy/python/jsbeautifier/unpackers/packer.py +104 -0
- data/js-beautify-copy/python/jsbeautifier/unpackers/tests/__init__.py +2 -0
- data/js-beautify-copy/python/jsbeautifier/unpackers/tests/test-myobfuscate-input.js +1 -0
- data/js-beautify-copy/python/jsbeautifier/unpackers/tests/test-myobfuscate-output.js +65 -0
- data/js-beautify-copy/python/jsbeautifier/unpackers/tests/test-packer-62-input.js +1 -0
- data/js-beautify-copy/python/jsbeautifier/unpackers/tests/test-packer-non62-input.js +1 -0
- data/js-beautify-copy/python/jsbeautifier/unpackers/tests/testjavascriptobfuscator.py +46 -0
- data/js-beautify-copy/python/jsbeautifier/unpackers/tests/testmyobfuscate.py +40 -0
- data/js-beautify-copy/python/jsbeautifier/unpackers/tests/testpacker.py +34 -0
- data/js-beautify-copy/python/jsbeautifier/unpackers/tests/testurlencode.py +36 -0
- data/js-beautify-copy/python/jsbeautifier/unpackers/urlencode.py +34 -0
- data/js-beautify-copy/python/setup.py +17 -0
- data/js-beautify-copy/tests/beautify-tests.js +489 -0
- data/js-beautify-copy/tests/run-tests +17 -0
- data/js-beautify-copy/tests/sanitytest.js +128 -0
- data/js-beautify-copy/unpackers/javascriptobfuscator_unpacker.js +103 -0
- data/js-beautify-copy/unpackers/myobfuscate_unpacker.js +81 -0
- data/js-beautify-copy/unpackers/p_a_c_k_e_r_unpacker.js +61 -0
- data/js-beautify-copy/unpackers/urlencode_unpacker.js +51 -0
- data/lib/js-beautify.rb +0 -0
- data/test/helper.rb +18 -0
- data/test/test_js-beautify.rb +7 -0
- data/update.sh +23 -0
- metadata +173 -0
|
@@ -0,0 +1,1166 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import getopt
|
|
3
|
+
import re
|
|
4
|
+
import string
|
|
5
|
+
|
|
6
|
+
#
|
|
7
|
+
# Originally written by Einar Lielmanis et al.,
|
|
8
|
+
# Conversion to python by Einar Lielmanis, einar@jsbeautifier.org,
|
|
9
|
+
# MIT licence, enjoy.
|
|
10
|
+
#
|
|
11
|
+
# Python is not my native language, feel free to push things around.
|
|
12
|
+
#
|
|
13
|
+
# Use either from command line (script displays its usage when run
|
|
14
|
+
# without any parameters),
|
|
15
|
+
#
|
|
16
|
+
#
|
|
17
|
+
# or, alternatively, use it as a module:
|
|
18
|
+
#
|
|
19
|
+
# import jsbeautifier
|
|
20
|
+
# res = jsbeautifier.beautify('your javascript string')
|
|
21
|
+
# res = jsbeautifier.beautify_file('some_file.js')
|
|
22
|
+
#
|
|
23
|
+
# you may specify some options:
|
|
24
|
+
#
|
|
25
|
+
# opts = jsbeautifier.default_options()
|
|
26
|
+
# opts.indent_size = 2
|
|
27
|
+
# res = jsbeautifier.beautify('some javascript', opts)
|
|
28
|
+
#
|
|
29
|
+
#
|
|
30
|
+
# Here are the available options: (read source)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class BeautifierOptions:
|
|
34
|
+
def __init__(self):
|
|
35
|
+
self.indent_size = 4
|
|
36
|
+
self.indent_char = ' '
|
|
37
|
+
self.indent_with_tabs = False
|
|
38
|
+
self.preserve_newlines = True
|
|
39
|
+
self.max_preserve_newlines = 10.
|
|
40
|
+
self.jslint_happy = False
|
|
41
|
+
self.brace_style = 'collapse'
|
|
42
|
+
self.keep_array_indentation = False
|
|
43
|
+
self.keep_function_indentation = False
|
|
44
|
+
self.eval_code = False
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def __repr__(self):
|
|
49
|
+
return \
|
|
50
|
+
"""indent_size = %d
|
|
51
|
+
indent_char = [%s]
|
|
52
|
+
preserve_newlines = %s
|
|
53
|
+
max_preserve_newlines = %d
|
|
54
|
+
jslint_happy = %s
|
|
55
|
+
indent_with_tabs = %s
|
|
56
|
+
brace_style = %s
|
|
57
|
+
keep_array_indentation = %s
|
|
58
|
+
eval_code = %s
|
|
59
|
+
""" % ( self.indent_size,
|
|
60
|
+
self.indent_char,
|
|
61
|
+
self.preserve_newlines,
|
|
62
|
+
self.max_preserve_newlines,
|
|
63
|
+
self.jslint_happy,
|
|
64
|
+
self.indent_with_tabs,
|
|
65
|
+
self.brace_style,
|
|
66
|
+
self.keep_array_indentation,
|
|
67
|
+
self.eval_code,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class BeautifierFlags:
|
|
72
|
+
def __init__(self, mode):
|
|
73
|
+
self.previous_mode = 'BLOCK'
|
|
74
|
+
self.mode = mode
|
|
75
|
+
self.var_line = False
|
|
76
|
+
self.var_line_tainted = False
|
|
77
|
+
self.var_line_reindented = False
|
|
78
|
+
self.in_html_comment = False
|
|
79
|
+
self.if_line = False
|
|
80
|
+
self.in_case = False
|
|
81
|
+
self.in_case_statement = False
|
|
82
|
+
self.eat_next_space = False
|
|
83
|
+
self.indentation_baseline = -1
|
|
84
|
+
self.indentation_level = 0
|
|
85
|
+
self.ternary_depth = 0
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def default_options():
|
|
89
|
+
return BeautifierOptions()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def beautify(string, opts = default_options() ):
|
|
93
|
+
b = Beautifier()
|
|
94
|
+
return b.beautify(string, opts)
|
|
95
|
+
|
|
96
|
+
def beautify_file(file_name, opts = default_options() ):
|
|
97
|
+
|
|
98
|
+
if file_name == '-': # stdin
|
|
99
|
+
f = sys.stdin
|
|
100
|
+
else:
|
|
101
|
+
try:
|
|
102
|
+
f = open(file_name)
|
|
103
|
+
except Exception as ex:
|
|
104
|
+
return 'The file could not be opened'
|
|
105
|
+
|
|
106
|
+
b = Beautifier()
|
|
107
|
+
return b.beautify(''.join(f.readlines()), opts)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def usage():
|
|
111
|
+
|
|
112
|
+
print("""Javascript beautifier (http://jsbeautifier.org/)
|
|
113
|
+
|
|
114
|
+
Usage: jsbeautifier.py [options] <infile>
|
|
115
|
+
|
|
116
|
+
<infile> can be "-", which means stdin.
|
|
117
|
+
<outfile> defaults to stdout
|
|
118
|
+
|
|
119
|
+
Input options:
|
|
120
|
+
|
|
121
|
+
-i, --stdin read input from stdin
|
|
122
|
+
|
|
123
|
+
Output options:
|
|
124
|
+
|
|
125
|
+
-s, --indent-size=NUMBER indentation size. (default 4).
|
|
126
|
+
-c, --indent-char=CHAR character to indent with. (default space).
|
|
127
|
+
-t, --indent-with-tabs Indent with tabs, overrides -s and -c
|
|
128
|
+
-d, --disable-preserve-newlines do not preserve existing line breaks.
|
|
129
|
+
-j, --jslint-happy more jslint-compatible output
|
|
130
|
+
-b, --brace-style=collapse brace style (collapse, expand, end-expand)
|
|
131
|
+
-k, --keep-array-indentation keep array indentation.
|
|
132
|
+
-o, --outfile=FILE specify a file to output to (default stdout)
|
|
133
|
+
-f, --keep-function-indentation Do not re-indent function bodies defined in var lines.
|
|
134
|
+
|
|
135
|
+
Rarely needed options:
|
|
136
|
+
|
|
137
|
+
--eval-code evaluate code if a JS interpreter is
|
|
138
|
+
installed. May be useful with some obfuscated
|
|
139
|
+
script but poses a potential security issue.
|
|
140
|
+
|
|
141
|
+
-l, --indent-level=NUMBER initial indentation level. (default 0).
|
|
142
|
+
|
|
143
|
+
-h, --help, --usage prints this help statement.
|
|
144
|
+
|
|
145
|
+
""")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class Beautifier:
|
|
153
|
+
|
|
154
|
+
def __init__(self, opts = default_options() ):
|
|
155
|
+
|
|
156
|
+
self.opts = opts
|
|
157
|
+
self.blank_state()
|
|
158
|
+
|
|
159
|
+
def blank_state(self):
|
|
160
|
+
|
|
161
|
+
# internal flags
|
|
162
|
+
self.flags = BeautifierFlags('BLOCK')
|
|
163
|
+
self.flag_store = []
|
|
164
|
+
self.wanted_newline = False
|
|
165
|
+
self.just_added_newline = False
|
|
166
|
+
self.do_block_just_closed = False
|
|
167
|
+
|
|
168
|
+
if self.opts.indent_with_tabs:
|
|
169
|
+
self.indent_string = "\t"
|
|
170
|
+
else:
|
|
171
|
+
self.indent_string = self.opts.indent_char * self.opts.indent_size
|
|
172
|
+
|
|
173
|
+
self.preindent_string = ''
|
|
174
|
+
self.last_word = '' # last TK_WORD seen
|
|
175
|
+
self.last_type = 'TK_START_EXPR' # last token type
|
|
176
|
+
self.last_text = '' # last token text
|
|
177
|
+
self.last_last_text = '' # pre-last token text
|
|
178
|
+
|
|
179
|
+
self.input = None
|
|
180
|
+
self.output = [] # formatted javascript gets built here
|
|
181
|
+
|
|
182
|
+
self.whitespace = ["\n", "\r", "\t", " "]
|
|
183
|
+
self.wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$'
|
|
184
|
+
self.digits = '0123456789'
|
|
185
|
+
self.punct = '+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |= ::'
|
|
186
|
+
self.punct += ' <?= <? ?> <%= <% %>'
|
|
187
|
+
self.punct = self.punct.split(' ')
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
# Words which always should start on a new line
|
|
191
|
+
self.line_starters = 'continue,try,throw,return,var,if,switch,case,default,for,while,break,function'.split(',')
|
|
192
|
+
self.set_mode('BLOCK')
|
|
193
|
+
|
|
194
|
+
global parser_pos
|
|
195
|
+
parser_pos = 0
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def beautify(self, s, opts = None ):
|
|
199
|
+
|
|
200
|
+
if opts != None:
|
|
201
|
+
self.opts = opts
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
if self.opts.brace_style not in ['expand', 'collapse', 'end-expand']:
|
|
205
|
+
raise(Exception('opts.brace_style must be "expand", "collapse" or "end-expand".'))
|
|
206
|
+
|
|
207
|
+
self.blank_state()
|
|
208
|
+
|
|
209
|
+
while s and s[0] in [' ', '\t']:
|
|
210
|
+
self.preindent_string += s[0]
|
|
211
|
+
s = s[1:]
|
|
212
|
+
|
|
213
|
+
self.input = self.unpack(s, opts.eval_code)
|
|
214
|
+
|
|
215
|
+
parser_pos = 0
|
|
216
|
+
while True:
|
|
217
|
+
token_text, token_type = self.get_next_token()
|
|
218
|
+
#print (token_text, token_type, self.flags.mode)
|
|
219
|
+
if token_type == 'TK_EOF':
|
|
220
|
+
break
|
|
221
|
+
|
|
222
|
+
handlers = {
|
|
223
|
+
'TK_START_EXPR': self.handle_start_expr,
|
|
224
|
+
'TK_END_EXPR': self.handle_end_expr,
|
|
225
|
+
'TK_START_BLOCK': self.handle_start_block,
|
|
226
|
+
'TK_END_BLOCK': self.handle_end_block,
|
|
227
|
+
'TK_WORD': self.handle_word,
|
|
228
|
+
'TK_SEMICOLON': self.handle_semicolon,
|
|
229
|
+
'TK_STRING': self.handle_string,
|
|
230
|
+
'TK_EQUALS': self.handle_equals,
|
|
231
|
+
'TK_OPERATOR': self.handle_operator,
|
|
232
|
+
'TK_BLOCK_COMMENT': self.handle_block_comment,
|
|
233
|
+
'TK_INLINE_COMMENT': self.handle_inline_comment,
|
|
234
|
+
'TK_COMMENT': self.handle_comment,
|
|
235
|
+
'TK_UNKNOWN': self.handle_unknown,
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
handlers[token_type](token_text)
|
|
239
|
+
|
|
240
|
+
self.last_last_text = self.last_text
|
|
241
|
+
self.last_type = token_type
|
|
242
|
+
self.last_text = token_text
|
|
243
|
+
|
|
244
|
+
sweet_code = self.preindent_string + re.sub('[\n ]+$', '', ''.join(self.output))
|
|
245
|
+
return sweet_code
|
|
246
|
+
|
|
247
|
+
def unpack(self, source, evalcode=False):
|
|
248
|
+
import jsbeautifier.unpackers as unpackers
|
|
249
|
+
try:
|
|
250
|
+
return unpackers.run(source, evalcode)
|
|
251
|
+
except unpackers.UnpackingError as error:
|
|
252
|
+
print('error:', error)
|
|
253
|
+
return ''
|
|
254
|
+
|
|
255
|
+
def trim_output(self, eat_newlines = False):
|
|
256
|
+
while len(self.output) \
|
|
257
|
+
and (
|
|
258
|
+
self.output[-1] == ' '\
|
|
259
|
+
or self.output[-1] == self.indent_string \
|
|
260
|
+
or self.output[-1] == self.preindent_string \
|
|
261
|
+
or (eat_newlines and self.output[-1] in ['\n', '\r'])):
|
|
262
|
+
self.output.pop()
|
|
263
|
+
|
|
264
|
+
def is_special_word(self, s):
|
|
265
|
+
return s in ['case', 'return', 'do', 'if', 'throw', 'else'];
|
|
266
|
+
|
|
267
|
+
def is_array(self, mode):
|
|
268
|
+
return mode in ['[EXPRESSION]', '[INDENDED-EXPRESSION]']
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def is_expression(self, mode):
|
|
272
|
+
return mode in ['[EXPRESSION]', '[INDENDED-EXPRESSION]', '(EXPRESSION)', '(FOR-EXPRESSION)', '(COND-EXPRESSION)']
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def append_newline_forced(self):
|
|
276
|
+
old_array_indentation = self.opts.keep_array_indentation
|
|
277
|
+
self.opts.keep_array_indentation = False
|
|
278
|
+
self.append_newline()
|
|
279
|
+
self.opts.keep_array_indentation = old_array_indentation
|
|
280
|
+
|
|
281
|
+
def append_newline(self, ignore_repeated = True):
|
|
282
|
+
|
|
283
|
+
self.flags.eat_next_space = False
|
|
284
|
+
|
|
285
|
+
if self.opts.keep_array_indentation and self.is_array(self.flags.mode):
|
|
286
|
+
return
|
|
287
|
+
|
|
288
|
+
self.flags.if_line = False
|
|
289
|
+
self.trim_output()
|
|
290
|
+
|
|
291
|
+
if len(self.output) == 0:
|
|
292
|
+
# no newline on start of file
|
|
293
|
+
return
|
|
294
|
+
|
|
295
|
+
if self.output[-1] != '\n' or not ignore_repeated:
|
|
296
|
+
self.just_added_newline = True
|
|
297
|
+
self.output.append('\n')
|
|
298
|
+
|
|
299
|
+
if self.preindent_string:
|
|
300
|
+
self.output.append(self.preindent_string)
|
|
301
|
+
|
|
302
|
+
for i in range(self.flags.indentation_level):
|
|
303
|
+
self.output.append(self.indent_string)
|
|
304
|
+
|
|
305
|
+
if self.flags.var_line and self.flags.var_line_reindented:
|
|
306
|
+
self.output.append(self.indent_string)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def append(self, s):
|
|
310
|
+
if s == ' ':
|
|
311
|
+
# do not add just a single space after the // comment, ever
|
|
312
|
+
if self.last_type == 'TK_COMMENT':
|
|
313
|
+
return self.append_newline()
|
|
314
|
+
|
|
315
|
+
# make sure only single space gets drawn
|
|
316
|
+
if self.flags.eat_next_space:
|
|
317
|
+
self.flags.eat_next_space = False
|
|
318
|
+
elif len(self.output) and self.output[-1] not in [' ', '\n', self.indent_string]:
|
|
319
|
+
self.output.append(' ')
|
|
320
|
+
else:
|
|
321
|
+
self.just_added_newline = False
|
|
322
|
+
self.flags.eat_next_space = False
|
|
323
|
+
self.output.append(s)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def indent(self):
|
|
327
|
+
self.flags.indentation_level = self.flags.indentation_level + 1
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def remove_indent(self):
|
|
331
|
+
if len(self.output) and self.output[-1] in [self.indent_string, self.preindent_string]:
|
|
332
|
+
self.output.pop()
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def set_mode(self, mode):
|
|
336
|
+
|
|
337
|
+
prev = BeautifierFlags('BLOCK')
|
|
338
|
+
|
|
339
|
+
if self.flags:
|
|
340
|
+
self.flag_store.append(self.flags)
|
|
341
|
+
prev = self.flags
|
|
342
|
+
|
|
343
|
+
self.flags = BeautifierFlags(mode)
|
|
344
|
+
|
|
345
|
+
if len(self.flag_store) == 1:
|
|
346
|
+
self.flags.indentation_level = 0
|
|
347
|
+
else:
|
|
348
|
+
self.flags.indentation_level = prev.indentation_level
|
|
349
|
+
if prev.var_line and prev.var_line_reindented:
|
|
350
|
+
self.flags.indentation_level = self.flags.indentation_level + 1
|
|
351
|
+
self.flags.previous_mode = prev.mode
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def restore_mode(self):
|
|
355
|
+
self.do_block_just_closed = self.flags.mode == 'DO_BLOCK'
|
|
356
|
+
if len(self.flag_store) > 0:
|
|
357
|
+
mode = self.flags.mode
|
|
358
|
+
self.flags = self.flag_store.pop()
|
|
359
|
+
self.flags.previous_mode = mode
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def get_next_token(self):
|
|
363
|
+
|
|
364
|
+
global parser_pos
|
|
365
|
+
|
|
366
|
+
self.n_newlines = 0
|
|
367
|
+
|
|
368
|
+
if parser_pos >= len(self.input):
|
|
369
|
+
return '', 'TK_EOF'
|
|
370
|
+
|
|
371
|
+
self.wanted_newline = False
|
|
372
|
+
c = self.input[parser_pos]
|
|
373
|
+
parser_pos += 1
|
|
374
|
+
|
|
375
|
+
keep_whitespace = self.opts.keep_array_indentation and self.is_array(self.flags.mode)
|
|
376
|
+
|
|
377
|
+
if keep_whitespace:
|
|
378
|
+
# slight mess to allow nice preservation of array indentation and reindent that correctly
|
|
379
|
+
# first time when we get to the arrays:
|
|
380
|
+
# var a = [
|
|
381
|
+
# ....'something'
|
|
382
|
+
# we make note of whitespace_count = 4 into flags.indentation_baseline
|
|
383
|
+
# so we know that 4 whitespaces in original source match indent_level of reindented source
|
|
384
|
+
#
|
|
385
|
+
# and afterwards, when we get to
|
|
386
|
+
# 'something,
|
|
387
|
+
# .......'something else'
|
|
388
|
+
# we know that this should be indented to indent_level + (7 - indentation_baseline) spaces
|
|
389
|
+
|
|
390
|
+
whitespace_count = 0
|
|
391
|
+
while c in self.whitespace:
|
|
392
|
+
if c == '\n':
|
|
393
|
+
self.trim_output()
|
|
394
|
+
self.output.append('\n')
|
|
395
|
+
self.just_added_newline = True
|
|
396
|
+
whitespace_count = 0
|
|
397
|
+
elif c == '\t':
|
|
398
|
+
whitespace_count += 4
|
|
399
|
+
elif c == '\r':
|
|
400
|
+
pass
|
|
401
|
+
else:
|
|
402
|
+
whitespace_count += 1
|
|
403
|
+
|
|
404
|
+
if parser_pos >= len(self.input):
|
|
405
|
+
return '', 'TK_EOF'
|
|
406
|
+
|
|
407
|
+
c = self.input[parser_pos]
|
|
408
|
+
parser_pos += 1
|
|
409
|
+
|
|
410
|
+
if self.flags.indentation_baseline == -1:
|
|
411
|
+
|
|
412
|
+
self.flags.indentation_baseline = whitespace_count
|
|
413
|
+
|
|
414
|
+
if self.just_added_newline:
|
|
415
|
+
for i in range(self.flags.indentation_level + 1):
|
|
416
|
+
self.output.append(self.indent_string)
|
|
417
|
+
|
|
418
|
+
if self.flags.indentation_baseline != -1:
|
|
419
|
+
for i in range(whitespace_count - self.flags.indentation_baseline):
|
|
420
|
+
self.output.append(' ')
|
|
421
|
+
|
|
422
|
+
else: # not keep_whitespace
|
|
423
|
+
while c in self.whitespace:
|
|
424
|
+
if c == '\n':
|
|
425
|
+
if self.opts.max_preserve_newlines == 0 or self.opts.max_preserve_newlines > self.n_newlines:
|
|
426
|
+
self.n_newlines += 1
|
|
427
|
+
|
|
428
|
+
if parser_pos >= len(self.input):
|
|
429
|
+
return '', 'TK_EOF'
|
|
430
|
+
|
|
431
|
+
c = self.input[parser_pos]
|
|
432
|
+
parser_pos += 1
|
|
433
|
+
|
|
434
|
+
if self.opts.preserve_newlines and self.n_newlines > 1:
|
|
435
|
+
for i in range(self.n_newlines):
|
|
436
|
+
self.append_newline(i == 0)
|
|
437
|
+
self.just_added_newline = True
|
|
438
|
+
|
|
439
|
+
self.wanted_newline = self.n_newlines > 0
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
if c in self.wordchar:
|
|
443
|
+
if parser_pos < len(self.input):
|
|
444
|
+
while self.input[parser_pos] in self.wordchar:
|
|
445
|
+
c = c + self.input[parser_pos]
|
|
446
|
+
parser_pos += 1
|
|
447
|
+
if parser_pos == len(self.input):
|
|
448
|
+
break
|
|
449
|
+
|
|
450
|
+
# small and surprisingly unugly hack for 1E-10 representation
|
|
451
|
+
if parser_pos != len(self.input) and self.input[parser_pos] in '+-' \
|
|
452
|
+
and re.match('^[0-9]+[Ee]$', c):
|
|
453
|
+
|
|
454
|
+
sign = self.input[parser_pos]
|
|
455
|
+
parser_pos += 1
|
|
456
|
+
t = self.get_next_token()
|
|
457
|
+
c += sign + t[0]
|
|
458
|
+
return c, 'TK_WORD'
|
|
459
|
+
|
|
460
|
+
if c == 'in': # in is an operator, need to hack
|
|
461
|
+
return c, 'TK_OPERATOR'
|
|
462
|
+
|
|
463
|
+
if self.wanted_newline and \
|
|
464
|
+
self.last_type != 'TK_OPERATOR' and\
|
|
465
|
+
self.last_type != 'TK_EQUALS' and\
|
|
466
|
+
not self.flags.if_line and \
|
|
467
|
+
(self.opts.preserve_newlines or self.last_text != 'var'):
|
|
468
|
+
self.append_newline()
|
|
469
|
+
|
|
470
|
+
return c, 'TK_WORD'
|
|
471
|
+
|
|
472
|
+
if c in '([':
|
|
473
|
+
return c, 'TK_START_EXPR'
|
|
474
|
+
|
|
475
|
+
if c in ')]':
|
|
476
|
+
return c, 'TK_END_EXPR'
|
|
477
|
+
|
|
478
|
+
if c == '{':
|
|
479
|
+
return c, 'TK_START_BLOCK'
|
|
480
|
+
|
|
481
|
+
if c == '}':
|
|
482
|
+
return c, 'TK_END_BLOCK'
|
|
483
|
+
|
|
484
|
+
if c == ';':
|
|
485
|
+
return c, 'TK_SEMICOLON'
|
|
486
|
+
|
|
487
|
+
if c == '/':
|
|
488
|
+
comment = ''
|
|
489
|
+
inline_comment = True
|
|
490
|
+
comment_mode = 'TK_INLINE_COMMENT'
|
|
491
|
+
if self.input[parser_pos] == '*': # peek /* .. */ comment
|
|
492
|
+
parser_pos += 1
|
|
493
|
+
if parser_pos < len(self.input):
|
|
494
|
+
while not (self.input[parser_pos] == '*' and \
|
|
495
|
+
parser_pos + 1 < len(self.input) and \
|
|
496
|
+
self.input[parser_pos + 1] == '/')\
|
|
497
|
+
and parser_pos < len(self.input):
|
|
498
|
+
c = self.input[parser_pos]
|
|
499
|
+
comment += c
|
|
500
|
+
if c in '\r\n':
|
|
501
|
+
comment_mode = 'TK_BLOCK_COMMENT'
|
|
502
|
+
parser_pos += 1
|
|
503
|
+
if parser_pos >= len(self.input):
|
|
504
|
+
break
|
|
505
|
+
parser_pos += 2
|
|
506
|
+
return '/*' + comment + '*/', comment_mode
|
|
507
|
+
if self.input[parser_pos] == '/': # peek // comment
|
|
508
|
+
comment = c
|
|
509
|
+
while self.input[parser_pos] not in '\r\n':
|
|
510
|
+
comment += self.input[parser_pos]
|
|
511
|
+
parser_pos += 1
|
|
512
|
+
if parser_pos >= len(self.input):
|
|
513
|
+
break
|
|
514
|
+
parser_pos += 1
|
|
515
|
+
if self.wanted_newline:
|
|
516
|
+
self.append_newline()
|
|
517
|
+
return comment, 'TK_COMMENT'
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
if c == "'" or c == '"' or \
|
|
522
|
+
(c == '/' and ((self.last_type == 'TK_WORD' and self.is_special_word(self.last_text)) or \
|
|
523
|
+
(self.last_type == 'TK_END_EXPR' and self.flags.previous_mode in ['(FOR-EXPRESSION)', '(COND-EXPRESSION)']) or \
|
|
524
|
+
(self.last_type in ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK', 'TK_END_BLOCK', 'TK_OPERATOR',
|
|
525
|
+
'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON']))):
|
|
526
|
+
sep = c
|
|
527
|
+
esc = False
|
|
528
|
+
resulting_string = c
|
|
529
|
+
in_char_class = False
|
|
530
|
+
|
|
531
|
+
if parser_pos < len(self.input):
|
|
532
|
+
if sep == '/':
|
|
533
|
+
# handle regexp
|
|
534
|
+
in_char_class = False
|
|
535
|
+
while esc or in_char_class or self.input[parser_pos] != sep:
|
|
536
|
+
resulting_string += self.input[parser_pos]
|
|
537
|
+
if not esc:
|
|
538
|
+
esc = self.input[parser_pos] == '\\'
|
|
539
|
+
if self.input[parser_pos] == '[':
|
|
540
|
+
in_char_class = True
|
|
541
|
+
elif self.input[parser_pos] == ']':
|
|
542
|
+
in_char_class = False
|
|
543
|
+
else:
|
|
544
|
+
esc = False
|
|
545
|
+
parser_pos += 1
|
|
546
|
+
if parser_pos >= len(self.input):
|
|
547
|
+
# incomplete regex when end-of-file reached
|
|
548
|
+
# bail out with what has received so far
|
|
549
|
+
return resulting_string, 'TK_STRING'
|
|
550
|
+
else:
|
|
551
|
+
# handle string
|
|
552
|
+
while esc or self.input[parser_pos] != sep:
|
|
553
|
+
resulting_string += self.input[parser_pos]
|
|
554
|
+
if not esc:
|
|
555
|
+
esc = self.input[parser_pos] == '\\'
|
|
556
|
+
else:
|
|
557
|
+
esc = False
|
|
558
|
+
parser_pos += 1
|
|
559
|
+
if parser_pos >= len(self.input):
|
|
560
|
+
# incomplete string when end-of-file reached
|
|
561
|
+
# bail out with what has received so far
|
|
562
|
+
return resulting_string, 'TK_STRING'
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
parser_pos += 1
|
|
566
|
+
resulting_string += sep
|
|
567
|
+
if sep == '/':
|
|
568
|
+
# regexps may have modifiers /regexp/MOD, so fetch those too
|
|
569
|
+
while parser_pos < len(self.input) and self.input[parser_pos] in self.wordchar:
|
|
570
|
+
resulting_string += self.input[parser_pos]
|
|
571
|
+
parser_pos += 1
|
|
572
|
+
return resulting_string, 'TK_STRING'
|
|
573
|
+
|
|
574
|
+
if c == '#':
|
|
575
|
+
|
|
576
|
+
# she-bang
|
|
577
|
+
if len(self.output) == 0 and len(self.input) > 1 and self.input[parser_pos] == '!':
|
|
578
|
+
resulting_string = c
|
|
579
|
+
while parser_pos < len(self.input) and c != '\n':
|
|
580
|
+
c = self.input[parser_pos]
|
|
581
|
+
resulting_string += c
|
|
582
|
+
parser_pos += 1
|
|
583
|
+
self.output.append(resulting_string.strip() + "\n")
|
|
584
|
+
self.append_newline()
|
|
585
|
+
return self.get_next_token()
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
# Spidermonkey-specific sharp variables for circular references
|
|
589
|
+
# https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
|
|
590
|
+
# http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
|
|
591
|
+
sharp = '#'
|
|
592
|
+
if parser_pos < len(self.input) and self.input[parser_pos] in self.digits:
|
|
593
|
+
while True:
|
|
594
|
+
c = self.input[parser_pos]
|
|
595
|
+
sharp += c
|
|
596
|
+
parser_pos += 1
|
|
597
|
+
if parser_pos >= len(self.input) or c == '#' or c == '=':
|
|
598
|
+
break
|
|
599
|
+
if c == '#' or parser_pos >= len(self.input):
|
|
600
|
+
pass
|
|
601
|
+
elif self.input[parser_pos] == '[' and self.input[parser_pos + 1] == ']':
|
|
602
|
+
sharp += '[]'
|
|
603
|
+
parser_pos += 2
|
|
604
|
+
elif self.input[parser_pos] == '{' and self.input[parser_pos + 1] == '}':
|
|
605
|
+
sharp += '{}'
|
|
606
|
+
parser_pos += 2
|
|
607
|
+
return sharp, 'TK_WORD'
|
|
608
|
+
|
|
609
|
+
if c == '<' and self.input[parser_pos - 1 : parser_pos + 3] == '<!--':
|
|
610
|
+
parser_pos += 3
|
|
611
|
+
c = '<!--'
|
|
612
|
+
while parser_pos < len(self.input) and self.input[parser_pos] != '\n':
|
|
613
|
+
c += self.input[parser_pos]
|
|
614
|
+
parser_pos += 1
|
|
615
|
+
self.flags.in_html_comment = True
|
|
616
|
+
return c, 'TK_COMMENT'
|
|
617
|
+
|
|
618
|
+
if c == '-' and self.flags.in_html_comment and self.input[parser_pos - 1 : parser_pos + 2] == '-->':
|
|
619
|
+
self.flags.in_html_comment = False
|
|
620
|
+
parser_pos += 2
|
|
621
|
+
if self.wanted_newline:
|
|
622
|
+
self.append_newline()
|
|
623
|
+
return '-->', 'TK_COMMENT'
|
|
624
|
+
|
|
625
|
+
if c in self.punct:
|
|
626
|
+
while parser_pos < len(self.input) and c + self.input[parser_pos] in self.punct:
|
|
627
|
+
c += self.input[parser_pos]
|
|
628
|
+
parser_pos += 1
|
|
629
|
+
if parser_pos >= len(self.input):
|
|
630
|
+
break
|
|
631
|
+
if c == '=':
|
|
632
|
+
return c, 'TK_EQUALS'
|
|
633
|
+
else:
|
|
634
|
+
return c, 'TK_OPERATOR'
|
|
635
|
+
return c, 'TK_UNKNOWN'
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
def handle_start_expr(self, token_text):
|
|
640
|
+
if token_text == '[':
|
|
641
|
+
if self.last_type == 'TK_WORD' or self.last_text == ')':
|
|
642
|
+
if self.last_text in self.line_starters:
|
|
643
|
+
self.append(' ')
|
|
644
|
+
self.set_mode('(EXPRESSION)')
|
|
645
|
+
self.append(token_text)
|
|
646
|
+
return
|
|
647
|
+
|
|
648
|
+
if self.flags.mode in ['[EXPRESSION]', '[INDENTED-EXPRESSION]']:
|
|
649
|
+
if self.last_last_text == ']' and self.last_text == ',':
|
|
650
|
+
# ], [ goes to a new line
|
|
651
|
+
if self.flags.mode == '[EXPRESSION]':
|
|
652
|
+
self.flags.mode = '[INDENTED-EXPRESSION]'
|
|
653
|
+
if not self.opts.keep_array_indentation:
|
|
654
|
+
self.indent()
|
|
655
|
+
self.set_mode('[EXPRESSION]')
|
|
656
|
+
if not self.opts.keep_array_indentation:
|
|
657
|
+
self.append_newline()
|
|
658
|
+
elif self.last_text == '[':
|
|
659
|
+
if self.flags.mode == '[EXPRESSION]':
|
|
660
|
+
self.flags.mode = '[INDENTED-EXPRESSION]'
|
|
661
|
+
if not self.opts.keep_array_indentation:
|
|
662
|
+
self.indent()
|
|
663
|
+
self.set_mode('[EXPRESSION]')
|
|
664
|
+
|
|
665
|
+
if not self.opts.keep_array_indentation:
|
|
666
|
+
self.append_newline()
|
|
667
|
+
else:
|
|
668
|
+
self.set_mode('[EXPRESSION]')
|
|
669
|
+
else:
|
|
670
|
+
self.set_mode('[EXPRESSION]')
|
|
671
|
+
else:
|
|
672
|
+
if self.last_text == 'for':
|
|
673
|
+
self.set_mode('(FOR-EXPRESSION)')
|
|
674
|
+
elif self.last_text in ['if', 'while']:
|
|
675
|
+
self.set_mode('(COND-EXPRESSION)')
|
|
676
|
+
else:
|
|
677
|
+
self.set_mode('(EXPRESSION)')
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
if self.last_text == ';' or self.last_type == 'TK_START_BLOCK':
|
|
681
|
+
self.append_newline()
|
|
682
|
+
elif self.last_type in ['TK_END_EXPR', 'TK_START_EXPR', 'TK_END_BLOCK'] or self.last_text == '.':
|
|
683
|
+
# do nothing on (( and )( and ][ and ]( and .(
|
|
684
|
+
if self.wanted_newline:
|
|
685
|
+
self.append_newline();
|
|
686
|
+
elif self.last_type not in ['TK_WORD', 'TK_OPERATOR']:
|
|
687
|
+
self.append(' ')
|
|
688
|
+
elif self.last_word == 'function' or self.last_word == 'typeof':
|
|
689
|
+
# function() vs function (), typeof() vs typeof ()
|
|
690
|
+
if self.opts.jslint_happy:
|
|
691
|
+
self.append(' ')
|
|
692
|
+
elif self.last_text in self.line_starters or self.last_text == 'catch':
|
|
693
|
+
self.append(' ')
|
|
694
|
+
|
|
695
|
+
self.append(token_text)
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
def handle_end_expr(self, token_text):
|
|
699
|
+
if token_text == ']':
|
|
700
|
+
if self.opts.keep_array_indentation:
|
|
701
|
+
if self.last_text == '}':
|
|
702
|
+
self.remove_indent()
|
|
703
|
+
self.append(token_text)
|
|
704
|
+
self.restore_mode()
|
|
705
|
+
return
|
|
706
|
+
else:
|
|
707
|
+
if self.flags.mode == '[INDENTED-EXPRESSION]':
|
|
708
|
+
if self.last_text == ']':
|
|
709
|
+
self.restore_mode()
|
|
710
|
+
self.append_newline()
|
|
711
|
+
self.append(token_text)
|
|
712
|
+
return
|
|
713
|
+
self.restore_mode()
|
|
714
|
+
self.append(token_text)
|
|
715
|
+
|
|
716
|
+
|
|
717
|
+
def handle_start_block(self, token_text):
|
|
718
|
+
if self.last_word == 'do':
|
|
719
|
+
self.set_mode('DO_BLOCK')
|
|
720
|
+
else:
|
|
721
|
+
self.set_mode('BLOCK')
|
|
722
|
+
|
|
723
|
+
if self.opts.brace_style == 'expand':
|
|
724
|
+
if self.last_type != 'TK_OPERATOR':
|
|
725
|
+
if self.last_text == '=' or (self.is_special_word(self.last_text) and self.last_text != 'else'):
|
|
726
|
+
self.append(' ')
|
|
727
|
+
else:
|
|
728
|
+
self.append_newline(True)
|
|
729
|
+
|
|
730
|
+
self.append(token_text)
|
|
731
|
+
self.indent()
|
|
732
|
+
else:
|
|
733
|
+
if self.last_type not in ['TK_OPERATOR', 'TK_START_EXPR']:
|
|
734
|
+
if self.last_type == 'TK_START_BLOCK':
|
|
735
|
+
self.append_newline()
|
|
736
|
+
else:
|
|
737
|
+
self.append(' ')
|
|
738
|
+
else:
|
|
739
|
+
# if TK_OPERATOR or TK_START_EXPR
|
|
740
|
+
if self.is_array(self.flags.previous_mode) and self.last_text == ',':
|
|
741
|
+
if self.last_last_text == '}':
|
|
742
|
+
self.append(' ')
|
|
743
|
+
else:
|
|
744
|
+
self.append_newline()
|
|
745
|
+
self.indent()
|
|
746
|
+
self.append(token_text)
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
def handle_end_block(self, token_text):
|
|
750
|
+
self.restore_mode()
|
|
751
|
+
if self.opts.brace_style == 'expand':
|
|
752
|
+
if self.last_text != '{':
|
|
753
|
+
self.append_newline()
|
|
754
|
+
else:
|
|
755
|
+
if self.last_type == 'TK_START_BLOCK':
|
|
756
|
+
if self.just_added_newline:
|
|
757
|
+
self.remove_indent()
|
|
758
|
+
else:
|
|
759
|
+
# {}
|
|
760
|
+
self.trim_output()
|
|
761
|
+
else:
|
|
762
|
+
if self.is_array(self.flags.mode) and self.opts.keep_array_indentation:
|
|
763
|
+
self.opts.keep_array_indentation = False
|
|
764
|
+
self.append_newline()
|
|
765
|
+
self.opts.keep_array_indentation = True
|
|
766
|
+
else:
|
|
767
|
+
self.append_newline()
|
|
768
|
+
|
|
769
|
+
self.append(token_text)
|
|
770
|
+
|
|
771
|
+
|
|
772
|
+
def handle_word(self, token_text):
|
|
773
|
+
if self.do_block_just_closed:
|
|
774
|
+
self.append(' ')
|
|
775
|
+
self.append(token_text)
|
|
776
|
+
self.append(' ')
|
|
777
|
+
self.do_block_just_closed = False
|
|
778
|
+
return
|
|
779
|
+
|
|
780
|
+
if token_text == 'function':
|
|
781
|
+
|
|
782
|
+
if self.flags.var_line:
|
|
783
|
+
self.flags.var_line_reindented = not self.opts.keep_function_indentation
|
|
784
|
+
if (self.just_added_newline or self.last_text == ';') and self.last_text != '{':
|
|
785
|
+
# make sure there is a nice clean space of at least one blank line
|
|
786
|
+
# before a new function definition
|
|
787
|
+
have_newlines = self.n_newlines
|
|
788
|
+
if not self.just_added_newline:
|
|
789
|
+
have_newlines = 0
|
|
790
|
+
if not self.opts.preserve_newlines:
|
|
791
|
+
have_newlines = 1
|
|
792
|
+
for i in range(2 - have_newlines):
|
|
793
|
+
self.append_newline(False)
|
|
794
|
+
|
|
795
|
+
if token_text == 'case' or (token_text == 'default' and self.flags.in_case_statement):
|
|
796
|
+
if self.last_text == ':':
|
|
797
|
+
self.remove_indent()
|
|
798
|
+
else:
|
|
799
|
+
self.flags.indentation_level -= 1
|
|
800
|
+
self.append_newline()
|
|
801
|
+
self.flags.indentation_level += 1
|
|
802
|
+
self.append(token_text)
|
|
803
|
+
self.flags.in_case = True
|
|
804
|
+
self.flags.in_case_statement = True
|
|
805
|
+
return
|
|
806
|
+
|
|
807
|
+
prefix = 'NONE'
|
|
808
|
+
|
|
809
|
+
if self.last_type == 'TK_END_BLOCK':
|
|
810
|
+
if token_text not in ['else', 'catch', 'finally']:
|
|
811
|
+
prefix = 'NEWLINE'
|
|
812
|
+
else:
|
|
813
|
+
if self.opts.brace_style in ['expand', 'end-expand']:
|
|
814
|
+
prefix = 'NEWLINE'
|
|
815
|
+
else:
|
|
816
|
+
prefix = 'SPACE'
|
|
817
|
+
self.append(' ')
|
|
818
|
+
elif self.last_type == 'TK_SEMICOLON' and self.flags.mode in ['BLOCK', 'DO_BLOCK']:
|
|
819
|
+
prefix = 'NEWLINE'
|
|
820
|
+
elif self.last_type == 'TK_SEMICOLON' and self.is_expression(self.flags.mode):
|
|
821
|
+
prefix = 'SPACE'
|
|
822
|
+
elif self.last_type == 'TK_STRING':
|
|
823
|
+
prefix = 'NEWLINE'
|
|
824
|
+
elif self.last_type == 'TK_WORD':
|
|
825
|
+
if self.last_text == 'else':
|
|
826
|
+
# eat newlines between ...else *** some_op...
|
|
827
|
+
# won't preserve extra newlines in this place (if any), but don't care that much
|
|
828
|
+
self.trim_output(True)
|
|
829
|
+
prefix = 'SPACE'
|
|
830
|
+
elif self.last_type == 'TK_START_BLOCK':
|
|
831
|
+
prefix = 'NEWLINE'
|
|
832
|
+
elif self.last_type == 'TK_END_EXPR':
|
|
833
|
+
self.append(' ')
|
|
834
|
+
prefix = 'NEWLINE'
|
|
835
|
+
|
|
836
|
+
if self.flags.if_line and self.last_type == 'TK_END_EXPR':
|
|
837
|
+
self.flags.if_line = False
|
|
838
|
+
|
|
839
|
+
if token_text in self.line_starters:
|
|
840
|
+
if self.last_text == 'else':
|
|
841
|
+
prefix = 'SPACE'
|
|
842
|
+
else:
|
|
843
|
+
prefix = 'NEWLINE'
|
|
844
|
+
|
|
845
|
+
if token_text == 'function' and self.last_text in ['get', 'set']:
|
|
846
|
+
prefix = 'SPACE'
|
|
847
|
+
|
|
848
|
+
if token_text in ['else', 'catch', 'finally']:
|
|
849
|
+
if self.last_type != 'TK_END_BLOCK' \
|
|
850
|
+
or self.opts.brace_style == 'expand' \
|
|
851
|
+
or self.opts.brace_style == 'end-expand':
|
|
852
|
+
self.append_newline()
|
|
853
|
+
else:
|
|
854
|
+
self.trim_output(True)
|
|
855
|
+
self.append(' ')
|
|
856
|
+
elif prefix == 'NEWLINE':
|
|
857
|
+
if token_text == 'function' and (self.last_type == 'TK_START_EXPR' or self.last_text in '=,'):
|
|
858
|
+
# no need to force newline on "function" -
|
|
859
|
+
# (function...
|
|
860
|
+
pass
|
|
861
|
+
elif token_text == 'function' and self.last_text == 'new':
|
|
862
|
+
self.append(' ')
|
|
863
|
+
elif self.is_special_word(self.last_text):
|
|
864
|
+
# no newline between return nnn
|
|
865
|
+
self.append(' ')
|
|
866
|
+
elif self.last_type != 'TK_END_EXPR':
|
|
867
|
+
if (self.last_type != 'TK_START_EXPR' or token_text != 'var') and self.last_text != ':':
|
|
868
|
+
# no need to force newline on VAR -
|
|
869
|
+
# for (var x = 0...
|
|
870
|
+
if token_text == 'if' and self.last_word == 'else' and self.last_text != '{':
|
|
871
|
+
self.append(' ')
|
|
872
|
+
else:
|
|
873
|
+
self.flags.var_line = False
|
|
874
|
+
self.flags.var_line_reindented = False
|
|
875
|
+
self.append_newline()
|
|
876
|
+
elif token_text in self.line_starters and self.last_text != ')':
|
|
877
|
+
self.flags.var_line = False
|
|
878
|
+
self.flags.var_line_reindented = False
|
|
879
|
+
self.append_newline()
|
|
880
|
+
elif self.is_array(self.flags.mode) and self.last_text == ',' and self.last_last_text == '}':
|
|
881
|
+
self.append_newline() # }, in lists get a newline
|
|
882
|
+
elif prefix == 'SPACE':
|
|
883
|
+
self.append(' ')
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
self.append(token_text)
|
|
887
|
+
self.last_word = token_text
|
|
888
|
+
|
|
889
|
+
if token_text == 'var':
|
|
890
|
+
self.flags.var_line = True
|
|
891
|
+
self.flags.var_line_reindented = False
|
|
892
|
+
self.flags.var_line_tainted = False
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
if token_text == 'if':
|
|
896
|
+
self.flags.if_line = True
|
|
897
|
+
|
|
898
|
+
if token_text == 'else':
|
|
899
|
+
self.flags.if_line = False
|
|
900
|
+
|
|
901
|
+
|
|
902
|
+
def handle_semicolon(self, token_text):
|
|
903
|
+
self.append(token_text)
|
|
904
|
+
self.flags.var_line = False
|
|
905
|
+
self.flags.var_line_reindented = False
|
|
906
|
+
if self.flags.mode == 'OBJECT':
|
|
907
|
+
# OBJECT mode is weird and doesn't get reset too well.
|
|
908
|
+
self.flags.mode = 'BLOCK'
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
def handle_string(self, token_text):
|
|
912
|
+
if self.last_type == 'TK_END_EXPR' and self.flags.previous_mode in ['(COND-EXPRESSION)', '(FOR-EXPRESSION)']:
|
|
913
|
+
self.append(' ')
|
|
914
|
+
if self.last_type in ['TK_STRING', 'TK_START_BLOCK', 'TK_END_BLOCK', 'TK_SEMICOLON']:
|
|
915
|
+
self.append_newline()
|
|
916
|
+
elif self.last_type == 'TK_WORD':
|
|
917
|
+
self.append(' ')
|
|
918
|
+
|
|
919
|
+
# Try to replace readable \x-encoded characters with their equivalent,
|
|
920
|
+
# if it is possible (e.g. '\x41\x42\x43\x01' becomes 'ABC\x01').
|
|
921
|
+
def unescape(match):
|
|
922
|
+
block, code = match.group(0, 1)
|
|
923
|
+
char = chr(int(code, 16))
|
|
924
|
+
if block.count('\\') == 1 and char in string.printable:
|
|
925
|
+
return char
|
|
926
|
+
return block
|
|
927
|
+
|
|
928
|
+
token_text = re.sub(r'\\{1,2}x([a-fA-F0-9]{2})', unescape, token_text)
|
|
929
|
+
|
|
930
|
+
self.append(token_text)
|
|
931
|
+
|
|
932
|
+
def handle_equals(self, token_text):
|
|
933
|
+
if self.flags.var_line:
|
|
934
|
+
# just got an '=' in a var-line, different line breaking rules will apply
|
|
935
|
+
self.flags.var_line_tainted = True
|
|
936
|
+
|
|
937
|
+
self.append(' ')
|
|
938
|
+
self.append(token_text)
|
|
939
|
+
self.append(' ')
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
def handle_operator(self, token_text):
|
|
943
|
+
space_before = True
|
|
944
|
+
space_after = True
|
|
945
|
+
|
|
946
|
+
if self.flags.var_line and token_text == ',' and self.is_expression(self.flags.mode):
|
|
947
|
+
# do not break on comma, for ( var a = 1, b = 2
|
|
948
|
+
self.flags.var_line_tainted = False
|
|
949
|
+
|
|
950
|
+
if self.flags.var_line and token_text == ',':
|
|
951
|
+
if self.flags.var_line_tainted:
|
|
952
|
+
self.append(token_text)
|
|
953
|
+
self.flags.var_line_reindented = True
|
|
954
|
+
self.flags.var_line_tainted = False
|
|
955
|
+
self.append_newline()
|
|
956
|
+
return
|
|
957
|
+
else:
|
|
958
|
+
self.flags.var_line_tainted = False
|
|
959
|
+
|
|
960
|
+
if self.is_special_word(self.last_text):
|
|
961
|
+
# return had a special handling in TK_WORD
|
|
962
|
+
self.append(' ')
|
|
963
|
+
self.append(token_text)
|
|
964
|
+
return
|
|
965
|
+
|
|
966
|
+
# hack for actionscript's import .*;
|
|
967
|
+
if token_text == '*' and self.last_type == 'TK_UNKNOWN' and not self.last_last_text.isdigit():
|
|
968
|
+
self.append(token_text)
|
|
969
|
+
return
|
|
970
|
+
|
|
971
|
+
|
|
972
|
+
if token_text == ':' and self.flags.in_case:
|
|
973
|
+
self.append(token_text)
|
|
974
|
+
self.append_newline()
|
|
975
|
+
self.flags.in_case = False
|
|
976
|
+
return
|
|
977
|
+
|
|
978
|
+
if token_text == '::':
|
|
979
|
+
# no spaces around the exotic namespacing syntax operator
|
|
980
|
+
self.append(token_text)
|
|
981
|
+
return
|
|
982
|
+
|
|
983
|
+
|
|
984
|
+
if token_text == ',':
|
|
985
|
+
if self.flags.var_line:
|
|
986
|
+
if self.flags.var_line_tainted:
|
|
987
|
+
# This never happens, as it's handled previously, right?
|
|
988
|
+
self.append(token_text)
|
|
989
|
+
self.append_newline()
|
|
990
|
+
self.flags.var_line_tainted = False
|
|
991
|
+
else:
|
|
992
|
+
self.append(token_text)
|
|
993
|
+
self.append(' ')
|
|
994
|
+
elif self.last_type == 'TK_END_BLOCK' and self.flags.mode != '(EXPRESSION)':
|
|
995
|
+
self.append(token_text)
|
|
996
|
+
if self.flags.mode == 'OBJECT' and self.last_text == '}':
|
|
997
|
+
self.append_newline()
|
|
998
|
+
else:
|
|
999
|
+
self.append(' ')
|
|
1000
|
+
else:
|
|
1001
|
+
if self.flags.mode == 'OBJECT':
|
|
1002
|
+
self.append(token_text)
|
|
1003
|
+
self.append_newline()
|
|
1004
|
+
else:
|
|
1005
|
+
# EXPR or DO_BLOCK
|
|
1006
|
+
self.append(token_text)
|
|
1007
|
+
self.append(' ')
|
|
1008
|
+
# comma handled
|
|
1009
|
+
return
|
|
1010
|
+
elif token_text in ['--', '++', '!'] \
|
|
1011
|
+
or (token_text in ['+', '-'] \
|
|
1012
|
+
and (self.last_type in ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR'] \
|
|
1013
|
+
or self.last_text in self.line_starters)):
|
|
1014
|
+
|
|
1015
|
+
space_before = False
|
|
1016
|
+
space_after = False
|
|
1017
|
+
|
|
1018
|
+
if self.last_text == ';' and self.is_expression(self.flags.mode):
|
|
1019
|
+
# for (;; ++i)
|
|
1020
|
+
# ^^
|
|
1021
|
+
space_before = True
|
|
1022
|
+
|
|
1023
|
+
if self.last_type == 'TK_WORD' and self.last_text in self.line_starters:
|
|
1024
|
+
space_before = True
|
|
1025
|
+
|
|
1026
|
+
if self.flags.mode == 'BLOCK' and self.last_text in ['{', ';']:
|
|
1027
|
+
# { foo: --i }
|
|
1028
|
+
# foo(): --bar
|
|
1029
|
+
self.append_newline()
|
|
1030
|
+
|
|
1031
|
+
elif token_text == '.':
|
|
1032
|
+
# decimal digits or object.property
|
|
1033
|
+
space_before = False
|
|
1034
|
+
|
|
1035
|
+
elif token_text == ':':
|
|
1036
|
+
if self.flags.ternary_depth == 0:
|
|
1037
|
+
if self.flags.mode == 'BLOCK':
|
|
1038
|
+
self.flags.mode = 'OBJECT'
|
|
1039
|
+
space_before = False
|
|
1040
|
+
else:
|
|
1041
|
+
self.flags.ternary_depth -= 1
|
|
1042
|
+
elif token_text == '?':
|
|
1043
|
+
self.flags.ternary_depth += 1
|
|
1044
|
+
|
|
1045
|
+
if space_before:
|
|
1046
|
+
self.append(' ')
|
|
1047
|
+
|
|
1048
|
+
self.append(token_text)
|
|
1049
|
+
|
|
1050
|
+
if space_after:
|
|
1051
|
+
self.append(' ')
|
|
1052
|
+
|
|
1053
|
+
|
|
1054
|
+
|
|
1055
|
+
def handle_block_comment(self, token_text):
|
|
1056
|
+
|
|
1057
|
+
lines = token_text.replace('\x0d', '').split('\x0a')
|
|
1058
|
+
# all lines start with an asterisk? that's a proper box comment
|
|
1059
|
+
if not any(l for l in lines[1:] if ( l.strip() == '' or (l.lstrip())[0] != '*')):
|
|
1060
|
+
self.append_newline()
|
|
1061
|
+
self.append(lines[0])
|
|
1062
|
+
for line in lines[1:]:
|
|
1063
|
+
self.append_newline()
|
|
1064
|
+
self.append(' ' + line.strip())
|
|
1065
|
+
else:
|
|
1066
|
+
# simple block comment: leave intact
|
|
1067
|
+
if len(lines) > 1:
|
|
1068
|
+
# multiline comment starts on a new line
|
|
1069
|
+
self.append_newline()
|
|
1070
|
+
else:
|
|
1071
|
+
# single line /* ... */ comment stays on the same line
|
|
1072
|
+
self.append(' ')
|
|
1073
|
+
for line in lines:
|
|
1074
|
+
self.append(line)
|
|
1075
|
+
self.append('\n')
|
|
1076
|
+
self.append_newline()
|
|
1077
|
+
|
|
1078
|
+
|
|
1079
|
+
def handle_inline_comment(self, token_text):
|
|
1080
|
+
self.append(' ')
|
|
1081
|
+
self.append(token_text)
|
|
1082
|
+
if self.is_expression(self.flags.mode):
|
|
1083
|
+
self.append(' ')
|
|
1084
|
+
else:
|
|
1085
|
+
self.append_newline_forced()
|
|
1086
|
+
|
|
1087
|
+
|
|
1088
|
+
def handle_comment(self, token_text):
|
|
1089
|
+
if self.last_type == 'TK_COMMENT':
|
|
1090
|
+
self.append_newline()
|
|
1091
|
+
if self.wanted_newline:
|
|
1092
|
+
self.append_newline(False)
|
|
1093
|
+
else:
|
|
1094
|
+
if self.wanted_newline:
|
|
1095
|
+
self.append_newline()
|
|
1096
|
+
else:
|
|
1097
|
+
self.append(' ')
|
|
1098
|
+
|
|
1099
|
+
self.append(token_text)
|
|
1100
|
+
self.append_newline_forced()
|
|
1101
|
+
|
|
1102
|
+
|
|
1103
|
+
def handle_unknown(self, token_text):
|
|
1104
|
+
if self.last_text in ['return', 'throw']:
|
|
1105
|
+
self.append(' ')
|
|
1106
|
+
|
|
1107
|
+
self.append(token_text)
|
|
1108
|
+
|
|
1109
|
+
|
|
1110
|
+
|
|
1111
|
+
|
|
1112
|
+
|
|
1113
|
+
def main():
|
|
1114
|
+
|
|
1115
|
+
argv = sys.argv[1:]
|
|
1116
|
+
|
|
1117
|
+
try:
|
|
1118
|
+
opts, args = getopt.getopt(argv, "s:c:o:djbkil:htf", ['indent-size=','indent-char=','outfile=', 'disable-preserve-newlines',
|
|
1119
|
+
'jslint-happy', 'brace-style=',
|
|
1120
|
+
'keep-array-indentation', 'indent-level=', 'help',
|
|
1121
|
+
'usage', 'stdin', 'eval-code', 'indent-with-tabs', 'keep-function-indentation'])
|
|
1122
|
+
except getopt.GetoptError:
|
|
1123
|
+
return usage()
|
|
1124
|
+
|
|
1125
|
+
js_options = default_options()
|
|
1126
|
+
|
|
1127
|
+
file = None
|
|
1128
|
+
outfile = 'stdout'
|
|
1129
|
+
if len(args) == 1:
|
|
1130
|
+
file = args[0]
|
|
1131
|
+
|
|
1132
|
+
for opt, arg in opts:
|
|
1133
|
+
if opt in ('--keep-array-indentation', '-k'):
|
|
1134
|
+
js_options.keep_array_indentation = True
|
|
1135
|
+
if opt in ('--keep-function-indentation','-f'):
|
|
1136
|
+
js_options.keep_function_indentation = True
|
|
1137
|
+
elif opt in ('--outfile', '-o'):
|
|
1138
|
+
outfile = arg
|
|
1139
|
+
elif opt in ('--indent-size', '-s'):
|
|
1140
|
+
js_options.indent_size = int(arg)
|
|
1141
|
+
elif opt in ('--indent-char', '-c'):
|
|
1142
|
+
js_options.indent_char = arg
|
|
1143
|
+
elif opt in ('--indent-with-tabs', '-t'):
|
|
1144
|
+
js_options.indent_with_tabs = True
|
|
1145
|
+
elif opt in ('--disable-preserve_newlines', '-d'):
|
|
1146
|
+
js_options.preserve_newlines = False
|
|
1147
|
+
elif opt in ('--jslint-happy', '-j'):
|
|
1148
|
+
js_options.jslint_happy = True
|
|
1149
|
+
elif opt in ('--eval-code'):
|
|
1150
|
+
js_options.eval_code = True
|
|
1151
|
+
elif opt in ('--brace-style', '-b'):
|
|
1152
|
+
js_options.brace_style = arg
|
|
1153
|
+
elif opt in ('--stdin', '-i'):
|
|
1154
|
+
file = '-'
|
|
1155
|
+
elif opt in ('--help', '--usage', '-h'):
|
|
1156
|
+
return usage()
|
|
1157
|
+
|
|
1158
|
+
if not file:
|
|
1159
|
+
return usage()
|
|
1160
|
+
else:
|
|
1161
|
+
if outfile == 'stdout':
|
|
1162
|
+
print(beautify_file(file, js_options))
|
|
1163
|
+
else:
|
|
1164
|
+
with open(outfile, 'w') as f:
|
|
1165
|
+
f.write(beautify_file(file, js_options) + '\n')
|
|
1166
|
+
|