Packages
earmark
1.4.37
1.5.0-pre1
retired
1.5.0-pre
retired
1.4.49
retired
1.4.48
retired
1.4.47
retired
1.4.46
retired
1.4.45
retired
1.4.44
retired
1.4.43
retired
1.4.42
retired
1.4.41
retired
1.4.40
retired
1.4.39
retired
1.4.38
retired
1.4.37
retired
1.4.36
retired
1.4.35
retired
1.4.34
retired
1.4.33
retired
1.4.32
retired
1.4.31
retired
1.4.30
retired
1.4.29
retired
1.4.28
retired
1.4.27
retired
1.4.26
retired
1.4.25
retired
1.4.24
retired
1.4.23
retired
1.4.22
retired
1.4.21
retired
1.4.20
retired
1.4.19
retired
1.4.18
retired
1.4.17
retired
1.4.16
retired
1.4.16-pre2
retired
1.4.16-pre1
retired
1.4.16-pre
retired
1.4.15
retired
1.4.14
retired
1.4.13
retired
1.4.12
retired
1.4.10
retired
1.4.9
retired
1.4.8
retired
1.4.7
retired
1.4.6
retired
1.4.5
retired
1.4.4
retired
1.4.3
retired
1.4.2
retired
1.4.1
retired
1.4.0
retired
1.3.6
retired
1.3.5
retired
1.3.4
retired
1.3.3
retired
1.3.2
retired
1.3.1
retired
1.3.0
retired
1.2.6
retired
1.2.5
retired
1.2.4
retired
1.2.3
retired
1.2.2
retired
1.2.1
retired
1.2.0
retired
1.1.1
retired
1.1.0
retired
1.0.3
retired
1.0.2
retired
1.0.1
retired
1.0.0
retired
0.2.1
retired
0.2.0
retired
0.1.19
retired
0.1.18
retired
0.1.17
retired
0.1.16
retired
0.1.15
retired
0.1.14
retired
0.1.13
retired
0.1.12
retired
0.1.10
retired
0.1.9
retired
0.1.8
retired
0.1.7
retired
0.1.6
retired
0.1.5
retired
0.1.4
retired
0.1.3
retired
0.1.2
retired
0.1.1
retired
0.1.0
retired
Earmark is a pure-Elixir Markdown converter. It is intended to be used as a library (just call Earmark.as_html), but can also be used as a command-line tool (run mix escript.build first). Output generation is pluggable.
Retired package: Deprecated - Earmark is no longer maintained. Migrate to a replacement, for example MDEx (https://hex.pm/packages/mdex).
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/earmark/ast_tools.ex
defmodule Earmark.AstTools do
@moduledoc """
Tools for AST manipulation
"""
@doc """
A helper to merge attributes in their cannonical representation
iex(1)> merge_atts([{"href", "url"}], target: "_blank")
[{"href", "url"}, {"target", "_blank"}]
iex(2)> merge_atts([{"href", "url"}, {"target", "nonsense"}], %{"target" => "_blank"})
[{"href", "url"}, {"target", "_blank nonsense"}]
iex(3)> merge_atts([{"href", "url"}, {"target", "nonsense"}, {"alt", "nowhere"}],
...(3)> [{"target", "_blank"}, title: "where?"])
[{"alt", "nowhere"}, {"href", "url"}, {"target", "_blank nonsense"}, {"title", "where?"}]
"""
def merge_atts(attrs, new)
def merge_atts(attrs, new) do
attrs
|> Enum.into(%{})
|> Map.merge(_stringyfy(new), &_combine_atts/3)
|> Enum.into([])
end
@doc """
Convenience function to access an attribute
iex(4)> find_att_in_node({"a", [{"class", "link"}], [], %{}}, "class")
"link"
iex(5)> find_att_in_node({"a", [{"class", "link"}], [], %{}}, "target")
nil
iex(6)> find_att_in_node({"a", [{"class", "link"}], [], %{}}, "target", :default)
:default
iex(7)> find_att_in_node([{"class", "link"}], "class")
"link"
iex(8)> find_att_in_node([{"class", "link"}], "target")
nil
iex(9)> find_att_in_node([{"class", "link"}], "target", :default)
:default
"""
def find_att_in_node(node_or_atts, att), do: _find_att(node_or_atts, att)
def find_att_in_node(node_or_atts, att, default), do: _find_att_with_default(node_or_atts, att, default)
@doc """
A convenience function that extracts the original attributes to be merged with new attributes
and puts the result into the node again
iex(10)> merge_atts_in_node({"img", [{"src", "there"}, {"alt", "there"}], [], %{some: "meta"}}, alt: "here")
{"img", [{"alt", "here there"}, {"src", "there"}], [], %{some: "meta"}}
"""
def merge_atts_in_node({tag, atts, content, meta}, new_atts) do
{tag, merge_atts(atts, new_atts), content, meta}
end
@doc """
Wrap a function that can only be called on nodes
iex(11)> f = fn {t, _, _, _} -> t end
...(11)> f_ = node_only_fn(f)
...(11)> {f_.({"p", [], [], %{}}), f_.("text")}
{"p", "text"}
"""
def node_only_fn(fun), do:
fn {_, _, _, _} = quad -> fun.(quad)
text -> text
end
defp _combine_atts(_key, original, new), do: "#{new} #{original}"
defp _find_att(node_or_atts, att)
defp _find_att({_, atts, _, _}, att), do: _find_att(atts, att)
defp _find_att(atts, att) do
atts
|> Enum.find_value(fn {key, value} -> if att == key, do: value end)
end
defp _find_att_with_default(node_or_atts, att, default)
defp _find_att_with_default({_, atts, _, _}, att, default), do: _find_att_with_default(atts, att, default)
defp _find_att_with_default(atts, att, default) do
atts
|> Enum.find_value(default, fn {key, value} -> if att == key, do: value end)
end
defp _stringyfy(mappy)
defp _stringyfy(map) when is_map(map) do
map |> Enum.into([]) |> _stringyfy()
end
defp _stringyfy(list) when is_list(list) do
list |> Enum.map(fn {k, v} -> {to_string(k), v} end) |> Enum.into(%{})
end
end
# SPDX-License-Identifier: Apache-2.0