Packages
kirala_markdown
1.0.2
Markdown parser and html renderer written in Gleam language both Erlang and Javascript target.
Current section
Files
Jump to
Current section
Files
src/kirala@markdown@html_renderer.erl
-module(kirala@markdown@html_renderer).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([html_encode_/2, html_encode/1, emit/2, emit_text/2, convert_tokens_/2, convert_bytes/1, convert/1, convert_bytes_outline/1, convert_outline/1, convert_bytes_digest/1, convert_digest/1]).
-spec url_of(
gleam@dict:dict(binary(), binary()),
kirala@markdown@parser:url_data()
) -> binary().
url_of(Symbols, Url) ->
case Url of
{url_plain, Str} ->
Str;
{url_foot_note, Id} ->
case gleam@dict:get(Symbols, Id) of
{ok, Str@1} ->
Str@1;
_ ->
Id
end
end.
-spec html_encode_(binary(), binary()) -> binary().
html_encode_(Src, Acc) ->
case Src of
<<""/utf8>> ->
Acc;
_ ->
{Newacc, Rest@4} = case Src of
<<"&"/utf8, Rest/binary>> ->
{<<Acc/binary, "&"/utf8>>, Rest};
<<"<"/utf8, Rest@1/binary>> ->
{<<Acc/binary, "<"/utf8>>, Rest@1};
<<">"/utf8, Rest@2/binary>> ->
{<<Acc/binary, ">"/utf8>>, Rest@2};
_ ->
{Chr, Rest@3} = kirala@markdown@parser:pop_grapheme(Src),
{<<Acc/binary, Chr/binary>>, Rest@3}
end,
html_encode_(Rest@4, Newacc)
end.
-spec html_encode(binary()) -> binary().
html_encode(Src) ->
_pipe = html_encode_(Src, <<""/utf8>>),
kirala@markdown@parser:ret_string(_pipe).
-spec emit(gleam@dict:dict(binary(), binary()), kirala@markdown@parser:token()) -> binary().
emit(Symbols, T) ->
case T of
{text, Text} ->
html_encode(Text);
{bold, T@1} ->
kirala@markdown@strfmt_ffi:text(
[<<"<strong>"/utf8>>, emit(Symbols, T@1), <<"</strong>"/utf8>>]
);
{italic, T@2} ->
kirala@markdown@strfmt_ffi:text(
[<<"<em>"/utf8>>, emit(Symbols, T@2), <<"</em>"/utf8>>]
);
{strike_through, T@3} ->
kirala@markdown@strfmt_ffi:text(
[<<"<s>"/utf8>>, emit(Symbols, T@3), <<"</s>"/utf8>>]
);
{marked_text, T@4} ->
kirala@markdown@strfmt_ffi:text(
[<<"<mark>"/utf8>>, emit(Symbols, T@4), <<"</mark>"/utf8>>]
);
{inserted_text, T@5} ->
kirala@markdown@strfmt_ffi:text(
[<<"<ins>"/utf8>>, emit(Symbols, T@5), <<"</ins>"/utf8>>]
);
{line_indent, _, Ts} ->
Str = begin
_pipe = gleam@list:map(Ts, fun(E) -> emit(Symbols, E) end),
gleam@string:concat(_pipe)
end,
case gleam@string:length(gleam@string:trim(Str)) =:= 0 of
true ->
<<"<br>"/utf8>>;
_ ->
Str
end;
{line, Ts} ->
Str = begin
_pipe = gleam@list:map(Ts, fun(E) -> emit(Symbols, E) end),
gleam@string:concat(_pipe)
end,
case gleam@string:length(gleam@string:trim(Str)) =:= 0 of
true ->
<<"<br>"/utf8>>;
_ ->
Str
end;
{h, Id, Level, Title} ->
kirala@markdown@strfmt_ffi:text(
[<<"<h"/utf8>>,
kirala@markdown@strfmt_ffi:str(Level),
<<" id='H"/utf8>>,
kirala@markdown@strfmt_ffi:str(Id),
<<"'>"/utf8>>,
emit(Symbols, Title),
<<"</h"/utf8>>,
kirala@markdown@strfmt_ffi:str(Level),
<<">\n"/utf8>>]
);
{url, {url_foot_note, Url}} ->
kirala@markdown@strfmt_ffi:text(
[<<"<sup><a href='#"/utf8>>,
Url,
<<"'>"/utf8>>,
Url,
<<"</a></sup>"/utf8>>]
);
{url, Url@1} ->
kirala@markdown@strfmt_ffi:text(
[<<"<a href='"/utf8>>,
url_of(Symbols, Url@1),
<<"'>"/utf8>>,
url_of(Symbols, Url@1),
<<"</a>"/utf8>>]
);
{url_link, Caption, Url@2} ->
Clen = gleam@string:length(Caption) - 1,
Tcaption = case gleam@string:starts_with(Caption, <<"\\"/utf8>>) of
true ->
html_encode(gleam@string:slice(Caption, 1, Clen));
_ ->
Caption
end,
kirala@markdown@strfmt_ffi:text(
[<<"<a href='"/utf8>>,
url_of(Symbols, Url@2),
<<"'>"/utf8>>,
Tcaption,
<<"</a>"/utf8>>]
);
{img_link, Caption@1, Alt, Url@3} ->
kirala@markdown@strfmt_ffi:text(
[<<"<img src='"/utf8>>,
url_of(Symbols, Url@3),
<<"' alt='"/utf8>>,
Alt,
<<"'>"/utf8>>,
Caption@1,
<<"</a>"/utf8>>]
);
{foot_note, Id@1, T@6} ->
kirala@markdown@strfmt_ffi:text(
[<<"<div id='"/utf8>>,
Id@1,
<<"'>※ "/utf8>>,
emit(Symbols, T@6),
<<"</div>"/utf8>>]
);
{foot_note_url_def, Id@2, Url@4, Alt@1} ->
kirala@markdown@strfmt_ffi:text(
[<<"["/utf8>>,
Id@2,
<<"]:"/utf8>>,
Url@4,
<<" \""/utf8>>,
Alt@1,
<<"\"<br>"/utf8>>]
);
{code_line, Code} ->
kirala@markdown@strfmt_ffi:text([Code, <<"\n"/utf8>>]);
{code_span, Code@1} ->
kirala@markdown@strfmt_ffi:text(
[<<"<code>"/utf8>>, Code@1, <<"</code>"/utf8>>]
);
{code_block, Syntax, Filename, Code@2} ->
case Syntax of
<<"csv"/utf8>> ->
kirala@markdown@strfmt_ffi:text(
[<<"<div><nav class='nav'>"/utf8>>,
Filename,
<<"</nav><pre class='"/utf8>>,
Syntax,
<<"'>"/utf8>>,
Code@2,
<<"</pre>\n</div>"/utf8>>]
);
<<"tsv"/utf8>> ->
kirala@markdown@strfmt_ffi:text(
[<<"<div><nav class='nav'>"/utf8>>,
Filename,
<<"</nav><pre class='"/utf8>>,
Syntax,
<<"'>"/utf8>>,
Code@2,
<<"</pre>\n</div>"/utf8>>]
);
_ ->
kirala@markdown@strfmt_ffi:text(
[<<"<div class='highlight'><nav class='nav'>"/utf8>>,
Filename,
<<"</nav><pre><code class='"/utf8>>,
Syntax,
<<"'>"/utf8>>,
Code@2,
<<"</code></pre>\n</div>"/utf8>>]
)
end;
{definition_is, Obj, Verb} ->
kirala@markdown@strfmt_ffi:text(
[<<"<dl><dt class='line'>"/utf8>>,
Obj,
<<"</dt><dd class='line'>"/utf8>>,
emit(Symbols, Verb),
<<"</dd></dl>"/utf8>>]
);
{note, Title@1, T@7} ->
kirala@markdown@strfmt_ffi:text(
[<<"<div class='"/utf8>>,
Title@1,
<<"'>"/utf8>>,
emit(Symbols, T@7),
<<"</div>"/utf8>>]
);
{block_quote, Level@1, Code@3} ->
emit(Symbols, Code@3);
{list_item, std_list, Level@2, T@8} ->
kirala@markdown@strfmt_ffi:text(
[<<"<li>"/utf8>>, emit(Symbols, T@8), <<"</li>\n"/utf8>>]
);
{list_item, checked_list, Check, T@9} ->
kirala@markdown@strfmt_ffi:text(
[<<"<li><input type='checkbox' checked='1'/>"/utf8>>,
emit(Symbols, T@9),
<<"</li>\n"/utf8>>]
);
{list_item, unchecked_list, Check@1, T@10} ->
kirala@markdown@strfmt_ffi:text(
[<<"<li><input type='checkbox' />"/utf8>>,
emit(Symbols, T@10),
<<"</li>\n"/utf8>>]
);
{list_item, ordered_list, Level@3, T@11} ->
kirala@markdown@strfmt_ffi:text(
[<<"<li>"/utf8>>, emit(Symbols, T@11), <<"</li>\n"/utf8>>]
);
{table, Header, Align, Rows} ->
Theader = begin
_pipe@1 = gleam@list:map(
Header,
fun(T@12) ->
kirala@markdown@strfmt_ffi:text(
[<<"<th>"/utf8>>,
emit(Symbols, T@12),
<<"</th>"/utf8>>]
)
end
),
gleam@string:concat(_pipe@1)
end,
Trows = begin
_pipe@3 = gleam@list:map(
Rows,
fun(Row) ->
Cols = Row,
Trow = begin
_pipe@2 = gleam@list:map(
Cols,
fun(Col) ->
kirala@markdown@strfmt_ffi:text(
[<<"<td>"/utf8>>,
emit(Symbols, Col),
<<"</td>"/utf8>>]
)
end
),
gleam@string:join(_pipe@2, <<"\n"/utf8>>)
end,
kirala@markdown@strfmt_ffi:text(
[<<"<tr>"/utf8>>,
kirala@markdown@strfmt_ffi:str(Trow),
<<"</th>"/utf8>>]
)
end
),
gleam@string:concat(_pipe@3)
end,
kirala@markdown@strfmt_ffi:text(
[<<"<section class='table'><table class='table table-light table-striped table-sm'><thead><tr>"/utf8>>,
Theader,
<<"</tr><thead><tbody>"/utf8>>,
Trows,
<<"<tbody></table></section>"/utf8>>]
);
hr ->
<<"<hr>"/utf8>>;
_ ->
kirala@markdown@strfmt_ffi:text([kirala@markdown@strfmt_ffi:str(T)])
end.
-spec emit_text(
gleam@dict:dict(binary(), binary()),
kirala@markdown@parser:token()
) -> binary().
emit_text(Symbols, T) ->
case T of
{text, Text} ->
html_encode(Text);
{bold, T@1} ->
emit(Symbols, T@1);
{italic, T@1} ->
emit(Symbols, T@1);
{strike_through, T@1} ->
emit(Symbols, T@1);
{marked_text, T@1} ->
emit(Symbols, T@1);
{inserted_text, T@1} ->
emit(Symbols, T@1);
{line, Ts} ->
Str = begin
_pipe = gleam@list:map(Ts, fun(E) -> emit(Symbols, E) end),
gleam@string:concat(_pipe)
end,
case gleam@string:length(gleam@string:trim(Str)) =:= 0 of
true ->
<<"<br>"/utf8>>;
_ ->
Str
end;
{url, {url_foot_note, Url}} ->
Url;
{url, {url_plain, Url@1}} ->
Url@1;
{url_link, Caption, Url@2} ->
Clen = gleam@string:length(Caption) - 1,
Tcaption = case gleam@string:starts_with(Caption, <<"\\"/utf8>>) of
true ->
html_encode(gleam@string:slice(Caption, 1, Clen));
_ ->
Caption
end,
Tcaption;
_ ->
<<""/utf8>>
end.
-spec convert_(
integer(),
binary(),
list(kirala@markdown@parser:token()),
gleam@dict:dict(binary(), binary())
) -> {list(kirala@markdown@parser:token()), gleam@dict:dict(binary(), binary())}.
convert_(Lineno, Bytes, Acc, Symbols) ->
case gleam@string:length(kirala@markdown@parser:ret_string_trim(Bytes)) of
Len when Len =:= 0 ->
{gleam@list:reverse(Acc), Symbols};
_ ->
{token_res, T, Rest} = kirala@markdown@parser:parse(Lineno, Bytes),
New_symbols = case T of
{foot_note_url_def, Id, Url, Alt} ->
gleam@dict:insert(Symbols, Id, Url);
_ ->
Symbols
end,
convert_(Lineno + 1, Rest, [T | Acc], New_symbols)
end.
-spec ntimes(integer(), binary()) -> binary().
ntimes(N, Str) ->
_pipe = gleam@list:map(gleam@list:range(0, N), fun(E) -> Str end),
gleam@string:concat(_pipe).
-spec convert_tokens_(
list(kirala@markdown@parser:token()),
gleam@dict:dict(binary(), binary())
) -> binary().
convert_tokens_(Tokens, Symbols) ->
{_, Strlist} = gleam@list:fold(
Tokens,
{hr, []},
fun(Acx, T) ->
{Prev, Acc} = Acx,
case {Prev, T} of
{{list_item, _, Indent1, T1}, {list_item, _, Indent2, T2}} when Indent1 =:= Indent2 ->
{T, [[emit(Symbols, T)] | Acc]};
{{list_item, _, Indent1@1, T1@1},
{list_item, _, Indent2@1, T2@1}} when Indent1@1 > Indent2@1 ->
{T,
[[ntimes(Indent1@1 - Indent2@1, <<"</ul>"/utf8>>),
emit(Symbols, T)] |
Acc]};
{{list_item, _, Indent1@2, T1@2},
{list_item, _, Indent2@2, T2@2}} when Indent1@2 < Indent2@2 ->
{T,
[[ntimes(Indent2@2 - Indent1@2, <<"<ul>"/utf8>>),
emit(Symbols, T)] |
Acc]};
{{list_item, _, Indent1@3, T1@3}, _} ->
{T,
[[ntimes(Indent1@3, <<"</ul>"/utf8>>), emit(Symbols, T)] |
Acc]};
{_, {list_item, _, Indent2@3, T2@3}} ->
{T,
[[ntimes(Indent2@3, <<"<ul>"/utf8>>), emit(Symbols, T)] |
Acc]};
{{block_quote, Indent1@4, T1@4}, {block_quote, Indent2@4, T2@4}} when Indent1@4 =:= Indent2@4 ->
{T, [[emit(Symbols, T)] | Acc]};
{{block_quote, Indent1@5, T1@5}, {block_quote, Indent2@5, T2@5}} when Indent1@5 > Indent2@5 ->
{T,
[[ntimes(
Indent1@5 - Indent2@5,
<<"</blockquote>"/utf8>>
),
emit(Symbols, T)] |
Acc]};
{{block_quote, Indent1@6, T1@6}, {block_quote, Indent2@6, T2@6}} when Indent1@6 < Indent2@6 ->
{T,
[[ntimes(Indent2@6 - Indent1@6, <<"<blockquote>"/utf8>>),
emit(Symbols, T)] |
Acc]};
{{block_quote, Indent1@7, T1@7}, _} ->
{T,
[[ntimes(Indent1@7, <<"</blockquote>"/utf8>>),
emit(Symbols, T)] |
Acc]};
{_, {block_quote, Indent2@7, T2@7}} ->
{T,
[[ntimes(Indent2@7, <<"<blockquote>"/utf8>>),
emit(Symbols, T)] |
Acc]};
{{code_line, T1@8}, {code_line, T2@8}} ->
{T, [[emit(Symbols, T)] | Acc]};
{{code_line, T1@9}, _} ->
{T, [[<<"</code></pre>"/utf8>>, emit(Symbols, T)] | Acc]};
{_, {code_line, T2@9}} ->
{T, [[<<"<pre><code>"/utf8>>, emit(Symbols, T)] | Acc]};
{_, {definition_of, T2@10}} ->
{T,
[[<<"<dl><dt>"/utf8>>,
emit(Symbols, T2@10),
<<"</dt>"/utf8>>] |
Acc]};
{_, {definition, T2@11}} ->
Tlines = begin
_pipe = gleam@list:map(
T2@11,
fun(E) -> [emit(Symbols, E), <<"<br>"/utf8>>] end
),
_pipe@1 = gleam@list:flatten(_pipe),
gleam@string:concat(_pipe@1)
end,
{T,
[[<<"<div><dd>"/utf8>>, Tlines, <<"</dd></div>"/utf8>>] |
Acc]};
{{definition, T1@10}, _} ->
{T, [[<<"</dl>"/utf8>>, emit(Symbols, T)] | Acc]};
_ ->
{T, [[emit(Symbols, T)] | Acc]}
end
end
),
_pipe@2 = Strlist,
_pipe@3 = gleam@list:reverse(_pipe@2),
_pipe@4 = gleam@list:flatten(_pipe@3),
gleam@string:concat(_pipe@4).
-spec convert_bytes(binary()) -> binary().
convert_bytes(Bytes) ->
{Tokens, Symbols} = convert_(0, Bytes, [], gleam@dict:new()),
convert_tokens_(Tokens, Symbols).
-spec convert(binary()) -> binary().
convert(Src) ->
convert_bytes(Src).
-spec convert_bytes_outline(binary()) -> binary().
convert_bytes_outline(Bytes) ->
{Tokens, Symbols} = convert_(0, Bytes, [], gleam@dict:new()),
Filteredtokens = begin
_pipe = gleam@list:fold(Tokens, [], fun(Acc, T) -> case T of
{h, Id, Level, Title} ->
[{list_item,
std_list,
Level,
{url_link,
emit_text(Symbols, Title),
{url_plain,
kirala@markdown@strfmt_ffi:text(
[<<"#H"/utf8>>,
kirala@markdown@strfmt_ffi:str(
Id
)]
)}}} |
Acc];
_ ->
Acc
end end),
gleam@list:reverse(_pipe)
end,
convert_tokens_(Filteredtokens, Symbols).
-spec convert_outline(binary()) -> binary().
convert_outline(Src) ->
convert_bytes_outline(Src).
-spec convert_bytes_digest(binary()) -> binary().
convert_bytes_digest(Bytes) ->
{Tokens, Symbols} = convert_(0, Bytes, [], gleam@dict:new()),
Filteredtokens = begin
_pipe = gleam@list:fold(Tokens, [], fun(Acc, T) -> case T of
{h, Id, Level, Title} ->
[[emit_text(Symbols, Title), <<"<br>\n"/utf8>>] | Acc];
{line, _} ->
[[emit_text(Symbols, T), <<"<br>\n"/utf8>>] | Acc];
_ ->
Acc
end end),
_pipe@1 = gleam@list:reverse(_pipe),
gleam@list:flatten(_pipe@1)
end,
gleam@string:concat(Filteredtokens).
-spec convert_digest(binary()) -> binary().
convert_digest(Src) ->
convert_bytes_digest(Src).