Packages
earmark
1.4.47
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?"}]
"""
@spec merge_atts(Earmark.ast_attributes(), map() | Keyword.t()) :: Earmark.ast_attributes()
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 from an AST node or a list of attributes
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([{"class", "link"}], "class")
"link"
iex(7)> find_att_in_node([{"class", "link"}], "target")
nil
"""
@spec find_att_in_node(Earmark.ast_node() | Earmark.ast_attributes(), binary()) :: any()
def find_att_in_node(node_or_atts, att), do: _find_att(node_or_atts, att)
@doc """
Convenience function to access an attribute from an AST node or a list of attributes with a default value.
iex(8)> find_att_in_node({"a", [{"class", "link"}], [], %{}}, "target", :default)
:default
iex(9)> find_att_in_node([{"class", "link"}], "target", :default)
:default
"""
@spec find_att_in_node(Earmark.ast_node() | Earmark.ast_attributes(), binary(), any()) :: any()
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"}}
"""
@spec merge_atts_in_node(Earmark.ast_node(), map() | Keyword.t()) :: Earmark.ast_node()
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"}
"""
@spec node_only_fn((Earmark.ast_node() -> any())) :: (Earmark.ast_node() | binary() -> any())
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