Skip to content

Prepare for OTP 28 #512

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions lib/earmark/transform.ex
Original file line number Diff line number Diff line change
Expand Up @@ -233,16 +233,17 @@ defmodule Earmark.Transform do
def make_postprocessor(%{postprocessor: pp, registered_processors: rps}),
do: _make_postprocessor([pp | rps])

@line_end ~r{\n\r?}

@doc false
@spec postprocessed_ast([String.t()] | String.t(), Options.options()) ::
{:ok, Earmark.ast_node(), [Earmark.Error.t()]}
| {:error, Earmark.ast_node(), [Earmark.Error.t()]}
def postprocessed_ast(lines, options)

def postprocessed_ast(lines, options) when is_binary(lines),
do: lines |> String.split(@line_end) |> postprocessed_ast(options)
def postprocessed_ast(lines, options) when is_binary(lines) do
line_end_rgx = ~r{\n\r?}

lines |> String.split(line_end_rgx) |> postprocessed_ast(options)
end

# This is an optimisation (buuuuuh) but we want a minimal impact of postprocessing code when it is not required
# It is also a case of the mantra "Handle the simple case first" (yeeeeah)
Expand Down Expand Up @@ -356,12 +357,12 @@ defmodule Earmark.Transform do
defp _maybe_add_newline1(%Options{compact_output: true}), do: []
defp _maybe_add_newline1(_), do: ?\n

@crlf_rgx ~r{(?:\n\r?)+}
defp _maybe_compact(element, options)
defp _maybe_compact(element, %{compact_output: false}), do: element

defp _maybe_compact(element, _options) do
String.replace(element, @crlf_rgx, " ")
crlf_rgx = ~r{(?:\n\r?)+}
String.replace(element, crlf_rgx, " ")
end

defp to_html(ast, options) do
Expand Down Expand Up @@ -457,8 +458,6 @@ defmodule Earmark.Transform do
[]
end

@dbl1_rgx ~r{(^|[-–—/\(\[\{"”“\s])'}
@dbl2_rgx ~r{(^|[-–—/\(\[\{‘\s])\"}
defp escape(element, %{smartypants: true} = options) do
# Unfortunately these regexes still have to be left.
# It doesn't seem possible to make _escape_to_iodata1
Expand All @@ -467,8 +466,8 @@ defmodule Earmark.Transform do
# it outweights the performance benefit.
element =
element
|> replace(@dbl1_rgx, "\\1‘")
|> replace(@dbl2_rgx, "\\1“")
|> replace(~r{(^|[-–—/\(\[\{"”“\s])'}, "\\1‘")
|> replace(~r{(^|[-–—/\(\[\{‘\s])\"}, "\\1“")

escape = Map.get(options, :escape, true)
_escape_to_iodata1(element, 0, element, [], true, escape, 0)
Expand Down
90 changes: 52 additions & 38 deletions lib/earmark_parser/ast/inline.ex
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,19 @@ defmodule Earmark.Parser.Ast.Inline do
# Converters
#
######################
@escape_rule ~r{^\\([\\`*\{\}\[\]()\#+\-.!_>])}
def converter_for_escape({src, lnb, context, use_linky?}) do
if match = Regex.run(@escape_rule, src) do
escape_rule = ~r{^\\([\\`*\{\}\[\]()\#+\-.!_>])}

if match = Regex.run(escape_rule, src) do
[match, escaped] = match
{behead(src, match), lnb, prepend(context, escaped), use_linky?}
end
end

@autolink_rgx ~r{^<([^ >]+(@|:\/)[^ >]+)>}
def converter_for_autolink({src, lnb, context, use_linky?}) do
if match = Regex.run(@autolink_rgx, src) do
autolink_rgx = ~r{^<([^ >]+(@|:\/)[^ >]+)>}

if match = Regex.run(autolink_rgx, src) do
[match, link, protocol] = match
{href, text} = convert_autolink(link, protocol)
out = render_link(href, text)
Expand Down Expand Up @@ -126,11 +128,12 @@ defmodule Earmark.Parser.Ast.Inline do
end
end

@link_text ~S{(?:\[[^]]*\]|[^][]|\])*}
@reflink ~r{^!?\[(#{@link_text})\]\s*\[([^]]*)\]}x
def converter_for_reflink({src, lnb, context, use_linky?}) do
link_text = ~S{(?:\[[^]]*\]|[^][]|\])*}
reflink = ~r{^!?\[(#{link_text})\]\s*\[([^]]*)\]}x

if use_linky? do
if match = Regex.run(@reflink, src) do
if match = Regex.run(reflink, src) do
{match_, alt_text, id} =
case match do
[match__, id, ""] -> {match__, id, id}
Expand Down Expand Up @@ -169,10 +172,11 @@ defmodule Earmark.Parser.Ast.Inline do
end
end

@nolink ~r{^!?\[((?:\[[^]]*\]|[^][])*)\]}
def converter_for_nolink({src, lnb, context, use_linky?}) do
nolink = ~r{^!?\[((?:\[[^]]*\]|[^][])*)\]}

if use_linky? do
case Regex.run(@nolink, src) do
case Regex.run(nolink, src) do
[match, id] ->
case reference_link(context, match, id, id, lnb) do
{:ok, out} -> {behead(src, match), lnb, prepend(context, out), use_linky?}
Expand All @@ -188,71 +192,78 @@ defmodule Earmark.Parser.Ast.Inline do
################################
# Simple Tags: em, strong, del #
################################
@strikethrough_rgx ~r{\A~~(?=\S)([\s\S]*?\S)~~}
def converter_for_strikethrough_gfm({src, _, _, _} = conv_tuple) do
if match = Regex.run(@strikethrough_rgx, src) do
strikethrough_rgx = ~r{\A~~(?=\S)([\s\S]*?\S)~~}

if match = Regex.run(strikethrough_rgx, src) do
_converter_for_simple_tag(conv_tuple, match, "del")
end
end

@strong_rgx ~r{\A__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)}
def converter_for_strong({src, _, _, _} = conv_tuple) do
if match = Regex.run(@strong_rgx, src) do
strong_rgx = ~r{\A__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)}

if match = Regex.run(strong_rgx, src) do
_converter_for_simple_tag(conv_tuple, match, "strong")
end
end

@emphasis_rgx ~r{\A\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)}
def converter_for_em({src, _, _, _} = conv_tuple) do
if match = Regex.run(@emphasis_rgx, src) do
emphasis_rgx = ~r{\A\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)}

if match = Regex.run(emphasis_rgx, src) do
_converter_for_simple_tag(conv_tuple, match, "em")
end
end

@sub_rgx ~r{\A~(?=\S)(.*?\S)~}
def converter_for_sub({src, _, %{options: %{sub_sup: true}}, _} = conv_tuple) do
if match = Regex.run(@sub_rgx, src) do
sub_rgx = ~r{\A~(?=\S)(.*?\S)~}

if match = Regex.run(sub_rgx, src) do
_converter_for_simple_tag(conv_tuple, match, "sub")
end
end

def converter_for_sub(_), do: nil

@sup_rgx ~r{\A\^(?=\S)(.*?\S)\^}
def converter_for_sup({src, _, %{options: %{sub_sup: true}}, _} = conv_tuple) do
if match = Regex.run(@sup_rgx, src) do
sup_rgx = ~r{\A\^(?=\S)(.*?\S)\^}

if match = Regex.run(sup_rgx, src) do
_converter_for_simple_tag(conv_tuple, match, "sup")
end
end

def converter_for_sup(_), do: nil

@squash_ws ~r{\s+}
@code ~r{^
(`+) # $1 = Opening run of `
(.+?) # $2 = The code block
(?<!`)
\1 # Matching closer
(?!`)
}xs
def converter_for_code({src, lnb, context, use_linky?}) do
if match = Regex.run(@code, src) do
squash_ws = ~r{\s+}

code = ~r{^
(`+) # $1 = Opening run of `
(.+?) # $2 = The code block
(?<!`)
\1 # Matching closer
(?!`)
}xs

if match = Regex.run(code, src) do
[match, _, content] = match
# Commonmark
content1 =
content
|> String.trim()
|> String.replace(@squash_ws, " ")
|> String.replace(squash_ws, " ")

out = codespan(content1)
{behead(src, match), lnb, prepend(context, out), use_linky?}
end
end

@inline_ial ~r<^\s*\{:\s*(.*?)\s*}>

def converter_for_inline_ial({src, lnb, context, use_linky?}) do
if match = Regex.run(@inline_ial, src) do
inline_ial = ~r<^\s*\{:\s*(.*?)\s*}>

if match = Regex.run(inline_ial, src) do
[match, ial] = match
{context1, ial_attrs} = parse_attrs(context, ial, lnb)
new_tags = augment_tag_with_ial(context.value, ial_attrs, match)
Expand All @@ -267,15 +278,16 @@ defmodule Earmark.Parser.Ast.Inline do
end
end

@line_ending ~r{\r\n?|\n}
@spec converter_for_text(conversion_data()) :: conversion_data()
def converter_for_text({src, lnb, context, _}) do
line_ending = ~r{\r\n?|\n}

matched =
case Regex.run(context.rules.text, src) do
[match] -> match
end

line_count = matched |> String.split(@line_ending) |> Enum.count()
line_count = matched |> String.split(line_ending) |> Enum.count()

ast = hard_line_breaks(matched, context.options.gfm)
ast = walk_ast(ast, &gruber_line_breaks/1)
Expand Down Expand Up @@ -319,22 +331,24 @@ defmodule Earmark.Parser.Ast.Inline do
{link, link}
end

@gruber_line_break Regex.compile!(" {2,}(?>\n)", "m")
defp gruber_line_breaks(text) do
gruber_line_break = ~r/ {2,}(?>\n)/m

text
|> String.split(@gruber_line_break)
|> String.split(gruber_line_break)
|> Enum.intersperse(emit("br"))
|> _remove_leading_empty()
end

@gfm_hard_line_break ~r{\\\n}
defp hard_line_breaks(text, gfm)
defp hard_line_breaks(text, false), do: text
defp hard_line_breaks(text, nil), do: text

defp hard_line_breaks(text, _) do
gfm_hard_line_break = ~r{\\\n}

text
|> String.split(@gfm_hard_line_break)
|> String.split(gfm_hard_line_break)
|> Enum.intersperse(emit("br"))
|> _remove_leading_empty()
end
Expand Down
12 changes: 6 additions & 6 deletions lib/earmark_parser/ast/renderer/html_renderer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ defmodule Earmark.Parser.Ast.Renderer.HtmlRenderer do
tag_ = if annotation, do: annotate(tag, annotation), else: tag
prepend(context, [tag_|rest])
end

@html_comment_start ~r{\A\s*<!--}
@html_comment_end ~r{-->.*\z}

def render_html_comment_line(line) do
html_comment_start = ~r{\A\s*<!--}
html_comment_end = ~r{-->.*\z}

line
|> String.replace(@html_comment_start, "")
|> String.replace(@html_comment_end, "")
|> String.replace(html_comment_start, "")
|> String.replace(html_comment_end, "")
end

end
5 changes: 3 additions & 2 deletions lib/earmark_parser/ast_renderer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -152,16 +152,17 @@ defmodule Earmark.Parser.AstRenderer do
#########
# Lists #
#########
@start_rgx ~r{\A\d+}
defp render_block(
%Block.List{type: type, bullet: bullet, blocks: items, attrs: attrs},
context,
_loose?
) do
start_rgx = ~r{\A\d+}

context1 = render(items, clear_value(context))

start_map =
case bullet && Regex.run(@start_rgx, bullet) do
case bullet && Regex.run(start_rgx, bullet) do
nil -> %{}
["1"] -> %{}
[start1] -> %{start: _normalize_start(start1)}
Expand Down
10 changes: 6 additions & 4 deletions lib/earmark_parser/helpers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ defmodule Earmark.Parser.Helpers do
Regex.replace(~r{(.*?)\t}, line, &expander/2)
end

@trailing_ial_rgx ~r< (?<!^)(?'ial'{: \s* [^}]+ \s* }) \s* \z >x
@doc ~S"""
Returns a tuple containing a potentially present IAL and the line w/o the IAL

Expand All @@ -24,7 +23,8 @@ defmodule Earmark.Parser.Helpers do
{nil, "{:.line-ial}"}
"""
def extract_ial(line) do
case Regex.split(@trailing_ial_rgx, line, include_captures: true, parts: 2, on: [:ial]) do
regex = ~r< (?<!^)(?'ial'{: \s* [^}]+ \s* }) \s* \z >x
case Regex.split(regex, line, include_captures: true, parts: 2, on: [:ial]) do
[_] -> {nil, line}
[line_, "{:" <> ial, _] ->
ial_ =
Expand Down Expand Up @@ -76,9 +76,11 @@ defmodule Earmark.Parser.Helpers do
convert non-entity ampersands.
"""

@amp_rgx ~r{&(?!#?\w+;)}
def escape(html) do
regex = ~r{&(?!#?\w+;)}

def escape(html), do: _escape(Regex.replace(@amp_rgx, html, "&amp;"))
_escape(Regex.replace(regex, html, "&amp;"))
end


defp _escape(html) do
Expand Down
10 changes: 6 additions & 4 deletions lib/earmark_parser/helpers/ast_helpers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,11 @@ defmodule Earmark.Parser.Helpers.AstHelpers do
lines |> Enum.join("\n")
end

@remove_escapes ~r{ \\ (?! \\ ) }x
@doc false
def render_image(text, href, title) do
alt = text |> escape() |> String.replace(@remove_escapes, "")
regex = ~r{ \\ (?! \\ ) }x

alt = text |> escape() |> String.replace(regex, "")

if title do
emit("img", [], src: href, alt: alt, title: title)
Expand All @@ -89,10 +90,11 @@ defmodule Earmark.Parser.Helpers.AstHelpers do
# add attributes to the outer tag in a block #
##############################################

@verbatims ~r<%[\da-f]{2}>i
defp _encode(url) do
regex = ~r<%[\da-f]{2}>i

url
|> String.split(@verbatims, include_captures: true)
|> String.split(regex, include_captures: true)
|> Enum.chunk_every(2)
|> Enum.map(&_encode_chunk/1)
|> IO.chardata_to_string()
Expand Down
Loading
Loading