@@ -52,7 +52,8 @@ defmodule Jason.Formatter do
52
52
"""
53
53
@ spec pretty_print ( iodata , opts ) :: binary
54
54
def pretty_print ( iodata , opts \\ [ ] ) do
55
- pretty_print_to_iodata ( iodata , opts )
55
+ iodata
56
+ |> pretty_print_to_iodata ( opts )
56
57
|> IO . iodata_to_binary ( )
57
58
end
58
59
@@ -67,13 +68,10 @@ defmodule Jason.Formatter do
67
68
opts = parse_opts ( opts , opts ( indent: " " , line: "\n " , record: nil , colon: " " ) )
68
69
opts = opts ( opts , record: opts ( opts , :record ) || opts ( opts , :line ) )
69
70
70
- depth = 0
71
- in_str = false
72
- in_bs = false
71
+ depth = :first
73
72
empty = false
74
- first = true
75
73
76
- { output , _state } = pp_iodata ( iodata , [ ] , depth , in_str , in_bs , empty , first , opts )
74
+ { output , _state } = pp_iodata ( iodata , [ ] , depth , empty , opts )
77
75
78
76
output
79
77
end
@@ -96,7 +94,8 @@ defmodule Jason.Formatter do
96
94
"""
97
95
@ spec minimize ( iodata , opts ) :: binary
98
96
def minimize ( iodata , opts \\ [ ] ) do
99
- minimize_to_iodata ( iodata , opts )
97
+ iodata
98
+ |> minimize_to_iodata ( opts )
100
99
|> IO . iodata_to_binary ( )
101
100
end
102
101
@@ -108,212 +107,166 @@ defmodule Jason.Formatter do
108
107
"""
109
108
@ spec minimize_to_iodata ( iodata , opts ) :: iodata
110
109
def minimize_to_iodata ( iodata , opts ) do
111
- opts = parse_opts ( opts , opts ( indent: [ ] , line: [ ] , record: "\n " , colon: [ ] ) )
110
+ opts = parse_opts ( opts , opts ( indent: "" , line: "" , record: "\n " , colon: "" ) )
112
111
113
- depth = 0
114
- in_str = false
115
- in_bs = false
112
+ depth = :first
116
113
empty = false
117
- first = true
118
114
119
- { output , _state } = pp_iodata ( iodata , [ ] , depth , in_str , in_bs , empty , first , opts )
115
+ { output , _state } = pp_iodata ( iodata , [ ] , depth , empty , opts )
120
116
121
117
output
122
118
end
123
119
124
120
defp parse_opts ( opts , defaults ) do
125
121
Enum . reduce ( opts , defaults , fn
126
- { :indent , indent } , opts -> opts ( opts , indent: indent )
127
- { :line_separator , line } , opts -> opts ( opts , line: line , record: opts ( opts , :record ) || line )
128
- { :record_separator , record } , opts -> opts ( opts , record: record )
129
- { :after_colon , colon } , opts -> opts ( opts , colon: colon )
122
+ { :indent , indent } , opts ->
123
+ opts ( opts , indent: IO . iodata_to_binary ( indent ) )
124
+
125
+ { :line_separator , line } , opts ->
126
+ line = IO . iodata_to_binary ( line )
127
+ opts ( opts , line: line , record: opts ( opts , :record ) || line )
128
+
129
+ { :record_separator , record } , opts ->
130
+ opts ( opts , record: IO . iodata_to_binary ( record ) )
131
+
132
+ { :after_colon , colon } , opts ->
133
+ opts ( opts , colon: IO . iodata_to_binary ( colon ) )
130
134
end )
131
135
end
132
136
137
+ @ spec tab ( String . t ( ) , non_neg_integer ) :: iodata ( )
133
138
## Returns an iolist containing `depth` instances of `opts[:indent]`
134
139
for depth <- 1 .. 16 do
135
140
defp tab ( " " , unquote ( depth ) ) , do: unquote ( String . duplicate ( " " , depth ) )
136
141
end
137
142
138
- defp tab ( [ ] , _ ) , do: ""
143
+ defp tab ( "" , _ ) , do: ""
139
144
defp tab ( indent , depth ) , do: List . duplicate ( indent , depth )
140
145
141
- @ typep pp_state :: {
142
- ## depth -- current nesting depth
143
- non_neg_integer ,
144
- ## in_str -- is the current byte in a string?
145
- boolean ,
146
- ## in_bs -- does the current byte follow a backslash in a string?
147
- boolean ,
148
- ## empty -- is the current object or array empty?
149
- boolean ,
150
- ## first -- is this the first object or array in the input?
151
- boolean
152
- }
153
-
154
- @ spec pp_iodata (
155
- ## input -- input data
156
- iodata ,
157
- ## output_acc -- output iolist (built in reverse order)
158
- iodata ,
159
- ## depth -- current nesting depth
160
- non_neg_integer ,
161
- ## in_str -- is the current byte in a string?
162
- boolean ,
163
- ## in_bs -- does the current byte follow a backslash in a string?
164
- boolean ,
165
- ## empty -- is the current object or array empty?
166
- boolean ,
167
- ## first -- is this the first object or array in the input?
168
- boolean ,
169
- opts
170
- ) :: { iodata , pp_state }
171
- defp pp_iodata ( input , output_acc , depth , in_str , in_bs , empty , first , opts )
172
-
173
- defp pp_iodata ( "" , output_acc , depth , in_str , in_bs , empty , first , opts ) do
174
- { output_acc , { depth , in_str , in_bs , empty , first , opts } }
175
- end
176
-
177
- defp pp_iodata ( [ ] , output_acc , depth , in_str , in_bs , empty , first , opts ) do
178
- { output_acc , { depth , in_str , in_bs , empty , first , opts } }
146
+ defp pp_iodata ( << >> , output_acc , depth , empty , opts ) do
147
+ { output_acc , & pp_iodata ( & 1 , & 2 , depth , empty , opts ) }
179
148
end
180
149
181
- defp pp_iodata (
182
- << byte :: size ( 8 ) , rest :: binary >> ,
183
- output_acc ,
184
- depth ,
185
- in_str ,
186
- in_bs ,
187
- empty ,
188
- first ,
189
- opts
190
- ) do
191
- pp_byte ( byte , rest , output_acc , depth , in_str , in_bs , empty , first , opts )
150
+ defp pp_iodata ( << byte , rest :: binary >> , output_acc , depth , empty , opts ) do
151
+ pp_byte ( byte , rest , output_acc , depth , empty , opts )
192
152
end
193
153
194
- defp pp_iodata ( byte , output_acc , depth , in_str , in_bs , empty , first , opts )
195
- when is_integer ( byte ) do
196
- pp_byte ( byte , [ ] , output_acc , depth , in_str , in_bs , empty , first , opts )
154
+ defp pp_iodata ( [ ] , output_acc , depth , empty , opts ) do
155
+ { output_acc , & pp_iodata ( & 1 , & 2 , depth , empty , opts ) }
197
156
end
198
157
199
- defp pp_iodata ( list , output_acc , depth , in_str , in_bs , empty , first , opts ) when is_list ( list ) do
200
- starting_state = { depth , in_str , in_bs , empty , first , opts }
201
-
202
- { list_output , end_state } =
203
- Enum . reduce ( list , { [ ] , starting_state } , fn item , { output_acc , state } ->
204
- { depth , in_str , in_bs , empty , first , opts } = state
205
- { item_output , new_state } = pp_iodata ( item , [ ] , depth , in_str , in_bs , empty , first , opts )
206
- { [ output_acc , item_output ] , new_state }
207
- end )
208
-
209
- { [ output_acc , list_output ] , end_state }
158
+ defp pp_iodata ( [ byte | rest ] , output_acc , depth , empty , opts ) when is_integer ( byte ) do
159
+ pp_byte ( byte , rest , output_acc , depth , empty , opts )
210
160
end
211
161
212
- @ spec pp_byte (
213
- ## byte -- current byte
214
- byte ,
215
- ## rest -- rest of input data
216
- iodata ,
217
- ## output -- output iolist (built in reverse order)
218
- iodata ,
219
- ## depth -- current nesting depth
220
- non_neg_integer ,
221
- ## in_str -- is the current byte in a string?
222
- boolean ,
223
- ## in_bs -- does the current byte follow a backslash in a string?
224
- boolean ,
225
- ## empty -- is the current object or array empty?
226
- boolean ,
227
- ## first -- is this the first object or array in the input?
228
- boolean ,
229
- opts
230
- ) :: { iodata , pp_state }
231
- defp pp_byte ( byte , rest , output , depth , in_str , in_bs , empty , first , opts )
232
-
233
- ## in string, following backslash
234
- defp pp_byte ( byte , rest , output , depth , true = in_str , true = _in_bs , empty , first , opts ) do
235
- in_bs = false
236
- pp_iodata ( rest , [ output , byte ] , depth , in_str , in_bs , empty , first , opts )
162
+ defp pp_iodata ( [ head | tail ] , output_acc , depth , empty , opts ) do
163
+ { output_acc , cont } = pp_iodata ( head , output_acc , depth , empty , opts )
164
+ cont . ( tail , output_acc )
237
165
end
238
166
239
- ## in string, backslash
240
- defp pp_byte ( byte , rest , output , depth , true = in_str , _in_bs , empty , first , opts )
241
- when byte in '\\ ' do
242
- in_bs = true
243
- pp_iodata ( rest , [ output , byte ] , depth , in_str , in_bs , empty , first , opts )
167
+ defp pp_byte ( byte , rest , output , depth , empty , opts ) when byte in ' \n \r \t ' do
168
+ pp_iodata ( rest , output , depth , empty , opts )
244
169
end
245
170
246
- ## in string, end quote
247
- defp pp_byte ( byte , rest , output , depth , true = _in_str , in_bs , empty , first , opts )
248
- when byte in '"' do
249
- in_str = false
250
- pp_iodata ( rest , [ output , byte ] , depth , in_str , in_bs , empty , first , opts )
251
- end
252
-
253
- ## in string, other character
254
- defp pp_byte ( byte , rest , output , depth , true = in_str , in_bs , empty , first , opts ) do
255
- pp_iodata ( rest , [ output , byte ] , depth , in_str , in_bs , empty , first , opts )
256
- end
257
-
258
- ## out of string, whitespace
259
- defp pp_byte ( byte , rest , output , depth , in_str , in_bs , empty , first , opts )
260
- when byte in ' \n \r \t ' do
261
- pp_iodata ( rest , output , depth , in_str , in_bs , empty , first , opts )
262
- end
263
-
264
- ## out of string, start block
265
- defp pp_byte ( byte , rest , output , depth , in_str , in_bs , empty , first , opts )
266
- when byte in '{[' do
267
- out =
171
+ defp pp_byte ( byte , rest , output , depth , empty , opts ) when byte in '{[' do
172
+ { out , depth } =
268
173
cond do
269
- first -> byte
270
- empty -> [ opts ( opts , :line ) , tab ( opts ( opts , :indent ) , depth ) , byte ]
271
- depth == 0 -> [ opts ( opts , :record ) , byte ]
272
- true -> byte
174
+ depth == : first -> { byte , 1 }
175
+ depth == 0 -> { [ opts ( opts , :record ) , byte ] , 1 }
176
+ empty -> { [ opts ( opts , :line ) , tab ( opts ( opts , :indent ) , depth ) , byte ] , depth + 1 }
177
+ true -> { byte , depth + 1 }
273
178
end
274
179
275
- first = false
276
180
empty = true
277
- depth = depth + 1
278
- pp_iodata ( rest , [ output , out ] , depth , in_str , in_bs , empty , first , opts )
181
+ pp_iodata ( rest , [ output , out ] , depth , empty , opts )
279
182
end
280
183
281
- ## out of string, end empty block
282
- defp pp_byte ( byte , rest , output , depth , in_str , in_bs , true = _empty , first , opts )
283
- when byte in '}]' do
184
+ defp pp_byte ( byte , rest , output , depth , true = _empty , opts ) when byte in '}]' do
284
185
empty = false
285
186
depth = depth - 1
286
- pp_iodata ( rest , [ output , byte ] , depth , in_str , in_bs , empty , first , opts )
187
+ pp_iodata ( rest , [ output , byte ] , depth , empty , opts )
287
188
end
288
189
289
- ## out of string, end non-empty block
290
- defp pp_byte ( byte , rest , output , depth , in_str , in_bs , false = empty , first , opts )
291
- when byte in '}]' do
190
+ defp pp_byte ( byte , rest , output , depth , false = empty , opts ) when byte in '}]' do
292
191
depth = depth - 1
293
192
out = [ opts ( opts , :line ) , tab ( opts ( opts , :indent ) , depth ) , byte ]
294
- pp_iodata ( rest , [ output , out ] , depth , in_str , in_bs , empty , first , opts )
193
+ pp_iodata ( rest , [ output , out ] , depth , empty , opts )
295
194
end
296
195
297
- ## out of string, comma
298
- defp pp_byte ( byte , rest , output , depth , in_str , in_bs , _empty , first , opts )
299
- when byte in ',' do
196
+ defp pp_byte ( byte , rest , output , depth , _empty , opts ) when byte in ',' do
300
197
empty = false
301
198
out = [ byte , opts ( opts , :line ) , tab ( opts ( opts , :indent ) , depth ) ]
302
- pp_iodata ( rest , [ output , out ] , depth , in_str , in_bs , empty , first , opts )
199
+ pp_iodata ( rest , [ output , out ] , depth , empty , opts )
303
200
end
304
201
305
- ## out of string, colon
306
- defp pp_byte ( byte , rest , output , depth , in_str , in_bs , empty , first , opts )
307
- when byte in ':' do
202
+ defp pp_byte ( byte , rest , output , depth , empty , opts ) when byte in ':' do
308
203
out = [ byte , opts ( opts , :colon ) ]
309
- pp_iodata ( rest , [ output , out ] , depth , in_str , in_bs , empty , first , opts )
204
+ pp_iodata ( rest , [ output , out ] , depth , empty , opts )
310
205
end
311
206
312
- ## out of string, other character (maybe start quote)
313
- defp pp_byte ( byte , rest , output , depth , _in_str , in_bs , empty , first , opts ) do
207
+ defp pp_byte ( byte , rest , output , depth , empty , opts ) do
314
208
out = if empty , do: [ opts ( opts , :line ) , tab ( opts ( opts , :indent ) , depth ) , byte ] , else: byte
315
- in_str = byte in '"'
316
209
empty = false
317
- pp_iodata ( rest , [ output , out ] , depth , in_str , in_bs , empty , first , opts )
210
+
211
+ if byte == ?" do
212
+ pp_string ( rest , [ output , out ] , _in_bs = false , & pp_iodata ( & 1 , & 2 , depth , empty , opts ) )
213
+ else
214
+ pp_iodata ( rest , [ output , out ] , depth , empty , opts )
215
+ end
216
+ end
217
+
218
+ defp pp_string ( << >> , output_acc , in_bs , cont ) do
219
+ { output_acc , & pp_string ( & 1 , & 2 , in_bs , cont ) }
220
+ end
221
+
222
+ defp pp_string ( << ?" , rest :: binary >> , output_acc , true = _in_bs , cont ) do
223
+ pp_string ( rest , [ output_acc , ?" ] , false , cont )
224
+ end
225
+
226
+ defp pp_string ( << ?" , rest :: binary >> , output_acc , false = _in_bs , cont ) do
227
+ cont . ( rest , [ output_acc , ?" ] )
228
+ end
229
+
230
+ defp pp_string ( << byte >> , output_acc , in_bs , cont ) do
231
+ in_bs = not in_bs and byte == ?\\
232
+ { [ output_acc , byte ] , & pp_string ( & 1 , & 2 , in_bs , cont ) }
233
+ end
234
+
235
+ defp pp_string ( binary , output_acc , _in_bs , cont ) when is_binary ( binary ) do
236
+ size = byte_size ( binary )
237
+
238
+ case :binary . match ( binary , "\" " ) do
239
+ :nomatch ->
240
+ skip = size - 2
241
+ << _ :: binary - size ( skip ) , prev , last >> = binary
242
+ in_bs = not ( prev == ?\\ and last == ?\\ ) or last == ?\\
243
+ { [ output_acc | binary ] , & pp_string ( & 1 , & 2 , in_bs , cont ) }
244
+
245
+ { pos , 1 } ->
246
+ { leading , tail } = :erlang . split_binary ( binary , pos + 1 )
247
+ output = [ output_acc | leading ]
248
+
249
+ case :binary . at ( binary , pos - 1 ) do
250
+ ?\\ -> pp_string ( tail , output , false , cont )
251
+ _ -> cont . ( tail , output )
252
+ end
253
+ end
254
+ end
255
+
256
+ defp pp_string ( [ ] , output_acc , in_bs , cont ) do
257
+ { output_acc , & pp_string ( & 1 , & 2 , in_bs , cont ) }
258
+ end
259
+
260
+ defp pp_string ( [ byte | rest ] , output_acc , in_bs , cont ) when is_integer ( byte ) do
261
+ cond do
262
+ in_bs -> pp_string ( rest , [ output_acc , byte ] , false , cont )
263
+ byte == ?" -> cont . ( rest , [ output_acc , byte ] )
264
+ true -> pp_string ( rest , [ output_acc , byte ] , byte == ?\\ , cont )
265
+ end
266
+ end
267
+
268
+ defp pp_string ( [ head | tail ] , output_acc , in_bs , cont ) do
269
+ { output_acc , cont } = pp_string ( head , output_acc , in_bs , cont )
270
+ cont . ( tail , output_acc )
318
271
end
319
272
end
0 commit comments