1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
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
|