Skip to content

Commit

Permalink
Preserve newlines inside HTML code (#97)
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed Feb 21, 2022
1 parent 358ee5b commit 75963d5
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 23 deletions.
2 changes: 1 addition & 1 deletion lib/earmark_parser/helpers/html_parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ defmodule EarmarkParser.Helpers.HtmlParser do
tag_tpl |> Tuple.append(Enum.reverse(lines)) |> Tuple.append(@verbatim)
end
defp _parse_rest([last_line], {tag, _}=tag_tpl, lines) do
case Regex.run(~r{\A</#{tag}>\s*(.*)}, last_line) do
case Regex.run(~r{\A\s*</#{tag}>\s*(.*)}, last_line) do
nil -> tag_tpl |> Tuple.append(Enum.reverse([last_line|lines])) |> Tuple.append(@verbatim)
[_, ""] -> tag_tpl |> Tuple.append(Enum.reverse(lines)) |> Tuple.append(@verbatim)
[_, suffix] -> [tag_tpl |> Tuple.append(Enum.reverse(lines)) |> Tuple.append(@verbatim), suffix]
Expand Down
26 changes: 18 additions & 8 deletions lib/earmark_parser/parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -667,46 +667,56 @@ defmodule EarmarkParser.Parser do
###################################################################

defp _html_match_to_closing(opener, rest, annotation),
do: _find_closing_tags([opener], rest, [String.trim_leading(opener.line)], annotation)
do: _find_closing_tags([opener], rest, [opener.line], [], annotation)

defp _find_closing_tags(needed, input, html_lines, text_lines, annotation)

defp _find_closing_tags(needed, input, html_lines, annotation)
# No more open tags, happy case
defp _find_closing_tags([], rest, html_lines, annotation),
defp _find_closing_tags([], rest, html_lines, [], annotation),
do: {html_lines, rest, [], annotation}

# run out of input, unhappy case
defp _find_closing_tags(needed, [], html_lines, annotation),
do: {html_lines, [], needed, annotation}
defp _find_closing_tags(needed, [], html_lines, text_lines, annotation),
do: {_add_text_lines(html_lines, text_lines), [], needed, annotation}

# still more lines, still needed closing
defp _find_closing_tags(
needed = [needed_hd | needed_tl],
[rest_hd | rest_tl],
html_lines,
text_lines,
annotation
) do
cond do
_closes_tag?(rest_hd, needed_hd) ->
_find_closing_tags(
needed_tl,
rest_tl,
[String.trim_leading(rest_hd.line) | html_lines],
[rest_hd.line | _add_text_lines(html_lines, text_lines)],
[],
_override_annotation(annotation, rest_hd)
)

_opens_tag?(rest_hd) ->
_find_closing_tags(
[rest_hd | needed],
rest_tl,
[String.trim_leading(rest_hd.line) | html_lines],
[rest_hd.line | _add_text_lines(html_lines, text_lines)],
[],
annotation
)

true ->
_find_closing_tags(needed, rest_tl, [rest_hd.line | html_lines], annotation)
_find_closing_tags(needed, rest_tl, html_lines, [rest_hd.line | text_lines], annotation)
end
end

defp _add_text_lines(html_lines, []),
do: html_lines

defp _add_text_lines(html_lines, text_lines),
do: [text_lines |> Enum.reverse() |> Enum.join("\n") | html_lines]

###########
# Helpers #
###########
Expand Down
19 changes: 10 additions & 9 deletions test/acceptance/ast/html/block/annotated_block_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ defmodule Acceptance.Ast.Html.Block.AnnotatedBlockTest do
"<table>\n <tr>\n <td>\n hi\n </td>\n </tr>\n</table>\n\nokay.\n"

ast = [
{"table", [], [" <tr>", " <td>", " hi", " </td>", " </tr>"], @verbatim},
{"table", [], [" <tr>\n <td>\n hi\n </td>\n </tr>"], @verbatim},
p("okay.")
]

Expand All @@ -25,7 +25,7 @@ defmodule Acceptance.Ast.Html.Block.AnnotatedBlockTest do
"<table> *: my_table\n <tr>\n <td>\n hi\n </td>\n </tr>\n</table>\n\nokay.\n"

ast = [
{"table", [], [" <tr>", " <td>", " hi", " </td>", " </tr>"], annotated(@verbatim, "*: my_table")},
{"table", [], [" <tr>\n <td>\n hi\n </td>\n </tr>"], annotated(@verbatim, "*: my_table")},
p("okay.")
]

Expand All @@ -36,14 +36,15 @@ defmodule Acceptance.Ast.Html.Block.AnnotatedBlockTest do

test "div (ine?) -- non-regression" do
markdown = "<div>\n *hello*\n <foo><a>\n</div>\n"
ast = [vtag("div", [" *hello*", " <foo><a>"])]
ast = [vtag("div", [" *hello*\n <foo><a>"])]
messages = []

assert as_ast(markdown, annotations: @annotations) == {:ok, ast, messages}
end

test "div (ine?)" do
markdown = "<div>*:my-div\n *hello*\n <foo><a>\n</div>\n"
ast = [vtag_annotated("div", [" *hello*", " <foo><a>"], "*:my-div")]
ast = [vtag_annotated("div", [" *hello*\n <foo><a>"], "*:my-div")]
messages = []

assert as_ast(markdown, annotations: @annotations) == {:ok, ast, messages}
Expand All @@ -66,23 +67,23 @@ defmodule Acceptance.Ast.Html.Block.AnnotatedBlockTest do

test "even block elements -- non-regression" do
markdown = "<div>\n```elixir\ndefmodule Mine do\n```\n</div>"
ast = [vtag("div", ["```elixir", "defmodule Mine do", "```"])]
ast = [vtag("div", ["```elixir\ndefmodule Mine do\n```"])]
messages = []

assert as_ast(markdown, annotations: @annotations) == {:ok, ast, messages}
end

test "even non-closed block elements" do
markdown = "<div>\n```elixir\ndefmodule Mine do\n</div>"
ast = [vtag("div", ["```elixir", "defmodule Mine do"])]
ast = [vtag("div", ["```elixir\ndefmodule Mine do"])]
messages = []

assert as_ast(markdown, annotations: @annotations) == {:ok, ast, messages}
end

test "even block elements" do
markdown = "<div>\n```elixir\ndefmodule Mine do\n```\n</div>*:at_the_end"
ast = [vtag_annotated("div", ["```elixir", "defmodule Mine do", "```"], "*:at_the_end")]
ast = [vtag_annotated("div", ["```elixir\ndefmodule Mine do\n```"], "*:at_the_end")]
messages = []

assert as_ast(markdown, annotations: @annotations) == {:ok, ast, messages}
Expand Down Expand Up @@ -260,15 +261,15 @@ defmodule Acceptance.Ast.Html.Block.AnnotatedBlockTest do

test "this is not closing" do
markdown = "<div>\nline\n</hello></div>"
ast = [{"div", [], ["line", "</hello></div>"], @verbatim}]
ast = [{"div", [], ["line\n</hello></div>"], @verbatim}]
messages = [{:warning, 1, "Failed to find closing <div>"}]

assert as_ast(markdown, annotations: @annotations) == {:error, ast, messages}
end

test "therefore the div continues" do
markdown = "<div>\nline\n</hello></div>\n</div>"
ast = [vtag("div", ["line", "</hello></div>"])]
ast = [vtag("div", ["line\n</hello></div>"])]
messages = []

assert as_ast(markdown, annotations: @annotations) == {:ok, ast, messages}
Expand Down
25 changes: 20 additions & 5 deletions test/acceptance/ast/html/block/unannotated_block_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ defmodule Acceptance.Ast.Html.Block.UnannotatedBlockTest do
"<table>\n <tr>\n <td>\n hi\n </td>\n </tr>\n</table>\n\nokay.\n"

ast = [
{"table", [], [" <tr>", " <td>", " hi", " </td>", " </tr>"], @verbatim},
{"table", [], [" <tr>\n <td>\n hi\n </td>\n </tr>"], @verbatim},
p("okay.")
]

Expand All @@ -22,7 +22,22 @@ defmodule Acceptance.Ast.Html.Block.UnannotatedBlockTest do

test "div (ine?)" do
markdown = "<div>\n *hello*\n <foo><a>\n</div>\n"
ast = [vtag("div", [" *hello*", " <foo><a>"])]
ast = [vtag("div", [" *hello*\n <foo><a>"])]
messages = []

assert as_ast(markdown) == {:ok, ast, messages}
end

test "code" do
markdown = """
<code>
> hello
> world
</code>
"""

ast = [vtag("code", ["> hello\n\n> world"])]
messages = []

assert as_ast(markdown) == {:ok, ast, messages}
Expand All @@ -38,7 +53,7 @@ defmodule Acceptance.Ast.Html.Block.UnannotatedBlockTest do

test "even block elements" do
markdown = "<div>\n```elixir\ndefmodule Mine do\n```\n</div>"
ast = [vtag("div", ["```elixir", "defmodule Mine do", "```"])]
ast = [vtag("div", ["```elixir\ndefmodule Mine do\n```"])]
messages = []

assert as_ast(markdown) == {:ok, ast, messages}
Expand Down Expand Up @@ -216,15 +231,15 @@ defmodule Acceptance.Ast.Html.Block.UnannotatedBlockTest do

test "this is not closing" do
markdown = "<div>\nline\n</hello></div>"
ast = [{"div", [], ["line", "</hello></div>"], @verbatim}]
ast = [{"div", [], ["line\n</hello></div>"], @verbatim}]
messages = [{:warning, 1, "Failed to find closing <div>"}]

assert as_ast(markdown) == {:error, ast, messages}
end

test "therefore the div continues" do
markdown = "<div>\nline\n</hello></div>\n</div>"
ast = [vtag("div", ["line", "</hello></div>"])]
ast = [vtag("div", ["line\n</hello></div>"])]
messages = []

assert as_ast(markdown) == {:ok, ast, messages}
Expand Down

0 comments on commit 75963d5

Please sign in to comment.