Packages

AST parser and generator for Markdown

Current section

Files

Jump to
earmark_parser lib earmark_parser context.ex
Raw

lib/earmark_parser/context.ex

defmodule EarmarkParser.Context do
@moduledoc false
alias EarmarkParser.Options
@type t :: %__MODULE__{
options: EarmarkParser.Options.t(),
links: map(),
footnotes: map(),
referenced_footnote_ids: MapSet.t(String.t()),
value: String.t() | [String.t()]
}
defstruct options: %EarmarkParser.Options{},
links: Map.new(),
rules: nil,
footnotes: Map.new(),
referenced_footnote_ids: MapSet.new([]),
value: []
##############################################################################
# Handle adding option specific rules and processors #
##############################################################################
@doc false
def modify_value(%__MODULE__{value: value} = context, fun) do
nv = fun.(value)
%{context | value: nv}
end
@doc false
def prepend(context1, ast_or_context, context2_or_nil \\ nil)
def prepend(%__MODULE__{} = context1, %__MODULE__{} = context2, nil) do
context1
|> _merge_contexts(context2)
|> _prepend(context2.value)
end
def prepend(%__MODULE__{} = context1, ast, nil) do
context1
|> _prepend(ast)
end
def prepend(%__MODULE__{} = context1, ast, %__MODULE__{} = context2) do
context1
|> _merge_contexts(context2)
|> _prepend(ast)
end
defp _merge_contexts(
%__MODULE__{referenced_footnote_ids: orig} = context1,
%__MODULE__{referenced_footnote_ids: new} = context2
) do
context_ = _merge_messages(context1, context2)
%{context_ | referenced_footnote_ids: MapSet.union(orig, new)}
end
defp _merge_messages(context, context_or_messages)
defp _merge_messages(context, %__MODULE__{options: %Options{messages: messages}}) do
_merge_messages(context, messages)
end
defp _merge_messages(context, messages) do
%{context | options: %{context.options | messages: MapSet.union(context.options.messages, messages)}}
end
defp _prepend(ctxt, []) do
ctxt
end
defp _prepend(%{value: value} = ctxt, {:comment, _, _, _} = ct) do
%{ctxt | value: [ct | value]}
end
defp _prepend(%{value: value} = ctxt, tuple) when is_tuple(tuple) do
%{ctxt | value: [tuple | value] |> List.flatten()}
end
defp _prepend(%{value: value} = ctxt, list) when is_list(list) do
%{ctxt | value: List.flatten(list ++ value)}
end
@doc """
Convenience method to prepend to the value list
"""
def set_value(%__MODULE__{} = ctx, value) do
%{ctx | value: value}
end
def clear_value(%__MODULE__{} = ctx) do
%{ctx | value: []}
end
# this is called by the command line processor to update
# the inline-specific rules in light of any options
def update_context(context = %EarmarkParser.Context{options: options}) do
%{context | rules: rules_for(options)}
end
# ( "[" .*? "]"n or anything w/o {"[", "]"}* or "]" ) *
# "
# @href ~S{\s*<?(.*?)>?(?:\s+['"](.*?)['"])?\s*}
defp basic_rules do
[
br: ~r<^ {2,}\n(?!\s*$)>,
text: ~r<^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)>
]
end
defp rules_for(options) do
subsup =
if options.sub_sup do
"~^"
else
""
end
math =
if options.math do
"$"
else
""
end
rule_updates =
if options.gfm do
rules = [
text: ~r{^[\s\S]+?(?=~~|[\\<!\[_*`#{math}#{subsup}]|https?://| \{2,\}\n|$)}
]
if options.breaks do
break_updates = [
br: ~r{^ *\n(?!\s*$)},
text: ~r{^[\s\S]+?(?=~~|[\\<!\[_*`#{math}#{subsup}]|https?://| *\n|$)}
]
Keyword.merge(rules, break_updates)
else
rules
end
else
[]
end
Keyword.merge(basic_rules(), rule_updates)
|> Enum.into(%{})
end
end
# SPDX-License-Identifier: Apache-2.0