Skip to content

Commit

Permalink
Fix formatter escapes handling
Browse files Browse the repository at this point in the history
Closes #49

Co-authored-by: JoeBew42 <joebew42@users.noreply.github.com>
  • Loading branch information
michalmuskala and joebew42 committed Jul 10, 2018
1 parent 00650f8 commit 794bbe4
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 21 deletions.
1 change: 1 addition & 0 deletions formatter_test_suite/nested-maps.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"@id": "http://a/b","http://a/c": [{"@id": "http://a/d"}]}]
1 change: 1 addition & 0 deletions formatter_test_suite/nested-maps.min.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"@id":"http://a/b","http://a/c":[{"@id":"http://a/d"}]}]
10 changes: 10 additions & 0 deletions formatter_test_suite/nested-maps.pretty.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"@id": "http://a/b",
"http://a/c": [
{
"@id": "http://a/d"
}
]
}
]
31 changes: 11 additions & 20 deletions lib/formatter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -216,36 +216,27 @@ defmodule Jason.Formatter do
{output_acc, &pp_string(&1, &2, in_bs, cont)}
end

defp pp_string(<<?", rest::binary>>, output_acc, true = _in_bs, cont) do
pp_string(rest, [output_acc, ?"], false, cont)
defp pp_string(<<byte, rest::binary>>, output_acc, true = _in_bs, cont) do
pp_string(rest, [output_acc, byte], false, cont)
end

defp pp_string(<<?", rest::binary>>, output_acc, false = _in_bs, cont) do
cont.(rest, [output_acc, ?"])
end

defp pp_string(<<byte>>, output_acc, in_bs, cont) do
in_bs = not in_bs and byte == ?\\
{[output_acc, byte], &pp_string(&1, &2, in_bs, cont)}
defp pp_string(<<?\\, rest::binary>>, output_acc, false = _in_bs, cont) do
pp_string(rest, [output_acc, ?\\], true, cont)
end

defp pp_string(binary, output_acc, _in_bs, cont) when is_binary(binary) do
size = byte_size(binary)

case :binary.match(binary, "\"") do
defp pp_string(binary, output_acc, false = _in_bs, cont) when is_binary(binary) do
case :binary.match(binary, ["\"", "\\"]) do
:nomatch ->
skip = size - 2
<<_::binary-size(skip), prev, last>> = binary
in_bs = not (prev == ?\\ and last == ?\\) or last == ?\\
{[output_acc | binary], &pp_string(&1, &2, in_bs, cont)}

{[output_acc | binary], &pp_string(&1, &2, false, cont)}
{pos, 1} ->
{leading, tail} = :erlang.split_binary(binary, pos + 1)
output = [output_acc | leading]

case :binary.at(binary, pos - 1) do
?\\ -> pp_string(tail, output, false, cont)
_ -> cont.(tail, output)
{head, tail} = :erlang.split_binary(binary, pos + 1)
case :binary.at(binary, pos) do
?\\ -> pp_string(tail, [output_acc | head], true, cont)
?" -> cont.(tail, [output_acc | head])
end
end
end
Expand Down
7 changes: 6 additions & 1 deletion test/formatter_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ defmodule Jason.FormatterTest do
"simple-object",
"multiple-objects",
"backslash-string",
"empty-nest"
"empty-nest",
"nested-maps"
]

for name <- @test_cases do
Expand Down Expand Up @@ -97,5 +98,9 @@ defmodule Jason.FormatterTest do
input = ["\"abc\\", "\\", ?"]
output = ~S|"abc\\"|
assert(minimize(input) == output)

input = ~s|["a\\\\"]|
output = ~s|[\n "a\\\\"\n]|
assert(pretty_print(input) == output)
end
end

0 comments on commit 794bbe4

Please sign in to comment.