aboutsummaryrefslogtreecommitdiffstats
path: root/src/asciideck_to_manpage.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/asciideck_to_manpage.erl')
-rw-r--r--src/asciideck_to_manpage.erl148
1 files changed, 148 insertions, 0 deletions
diff --git a/src/asciideck_to_manpage.erl b/src/asciideck_to_manpage.erl
new file mode 100644
index 0000000..dbdb74d
--- /dev/null
+++ b/src/asciideck_to_manpage.erl
@@ -0,0 +1,148 @@
+%% Copyright (c) 2016, Loïc Hoguin <[email protected]>
+%%
+%% Permission to use, copy, modify, and/or distribute this software for any
+%% purpose with or without fee is hereby granted, provided that the above
+%% copyright notice and this permission notice appear in all copies.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+-module(asciideck_to_manpage).
+
+-export([translate/1]).
+
+translate(AST) ->
+ Output = man(AST),
+ file:write_file("/tmp/man.3.gz", zlib:gzip(Output)),
+ ok.
+
+man([{title, #{level := 0}, Title0, _Ann}|AST]) ->
+ [Title, << Section:1/binary, _/bits >>] = binary:split(Title0, <<"(">>),
+ Extra1 = "2016-10-17", %% @todo
+ Extra2 = "Project 1.0", %% @todo
+ Extra3 = "Project Function Reference", %% @todo
+ [
+ ".TH \"", Title, "\" \"", Section, "\" \"",
+ Extra1, "\" \"", Extra2, "\" \"", Extra3, "\"\n"
+ ".ta T 4n\n\\&\n",
+ man(AST, [])
+ ].
+
+man([], Acc) ->
+ lists:reverse(Acc);
+man([{title, #{level := 1}, Title, _Ann}|Tail], Acc) ->
+ man(Tail, [[".SH ", string:to_upper(binary_to_list(Title)), "\n"]|Acc]);
+man([{title, #{level := 2}, Title, _Ann}|Tail], Acc) ->
+ man(Tail, [[".SS ", Title, "\n"]|Acc]);
+man([{p, _Attrs, Text, _Ann}|Tail], Acc) ->
+ man(Tail, [[".LP\n", man_format(Text), "\n.sp\n"]|Acc]);
+man([{listing, Attrs, Listing, _Ann}|Tail], Acc0) ->
+ Acc1 = case Attrs of
+ #{title := Title} ->
+ [[".PP\n\\fB", Title, "\\fR\n"]|Acc0];
+ _ ->
+ Acc0
+ end,
+ Acc = [[
+ ".if n \\{\\\n"
+ ".RS 4\n"
+ ".\\}\n"
+ ".nf\n",
+ Listing,
+ "\n"
+ ".fi\n"
+ ".if n \\{\\\n"
+ ".RE\n"
+ ".\\}\n"]|Acc1],
+ man(Tail, Acc);
+man([{ul, _Attrs, Items, _Ann}|Tail], Acc0) ->
+ Acc = man_ul(Items, Acc0),
+ man(Tail, Acc);
+man([{ll, _Attrs, Items, _Ann}|Tail], Acc0) ->
+ Acc = man_ll(Items, Acc0),
+ man(Tail, Acc);
+%% @todo Attributes.
+%% Currently acts as if options="headers" was always set.
+man([{table, _TAttrs, [{row, RowAttrs, Headers0, RowAnn}|Rows0], _TAnn}|Tail], Acc0) ->
+ Headers = [{cell, CAttrs, [{p, Attrs, [{em, P}], Ann}], CAnn}
+ || {cell, CAttrs, [{p, Attrs, P, Ann}], CAnn} <- Headers0],
+ Rows = [{row, RowAttrs, Headers, RowAnn}|Rows0],
+ Acc = [[
+ ".TS\n"
+ "allbox tab(:);\n",
+ man_table_style(Rows, []),
+ man_table_contents(Rows),
+ ".TE\n"
+ ".sp 1\n"]|Acc0],
+ man(Tail, Acc);
+%% Skip everything we don't understand.
+man([_Ignore|Tail], Acc) ->
+ io:format("Ignore ~p~n", [_Ignore]), %% @todo lol io:format
+ man(Tail, Acc).
+
+man_ll([], Acc) ->
+ Acc;
+man_ll([{li, #{label := Label}, [{p, _PAttrs, Text, _PAnn}], _LiAnn}|Tail], Acc0) ->
+ Acc = [[
+ ".PP\n"
+ "\\fB", Label, "\\fR\n",
+ ".RS 4\n",
+ man_format(Text), "\n"
+ ".RE\n"]|Acc0],
+ man_ll(Tail, Acc).
+
+man_ul([], Acc) ->
+ Acc;
+man_ul([{li, _LiAttrs, [{p, _PAttrs, Text, _PAnn}], _LiAnn}|Tail], Acc0) ->
+ Acc = [[
+ ".ie n \\{\\\n"
+ ".RS 2\n"
+ "\\h'-02'\\(bu\\h'+01'\\c\n"
+ ".\\}\n"
+ ".el \\{\\\n"
+ ".RS 4\n"
+ ".sp -1\n"
+ ".IP \\(bu 2.3\n"
+ ".\\}\n",
+ man_format(Text), "\n"
+ ".RE\n"]|Acc0],
+ man_ul(Tail, Acc).
+
+man_table_style([], [_|Acc]) ->
+ lists:reverse([".\n"|Acc]);
+man_table_style([{row, _, Cols, _}|Tail], Acc) ->
+ man_table_style(Tail, [$\n, man_table_style_cols(Cols, [])|Acc]).
+
+man_table_style_cols([], [_|Acc]) ->
+ lists:reverse(Acc);
+man_table_style_cols([{cell, _, _, _}|Tail], Acc) ->
+ man_table_style_cols(Tail, [$\s, "lt"|Acc]).
+
+man_table_contents(Rows) ->
+ [man_table_contents_cols(Cols, []) || {row, _, Cols, _} <- Rows].
+
+man_table_contents_cols([], [_|Acc]) ->
+ lists:reverse(["\n"|Acc]);
+man_table_contents_cols([{cell, _CAttrs, [{p, _PAttrs, Text, _PAnn}], _CAnn}|Tail], Acc) ->
+ man_table_contents_cols(Tail, [$:, "\nT}", man_format(Text), "T{\n"|Acc]).
+
+man_format(Text) when is_binary(Text) ->
+ Text;
+man_format({link, Link, Text}) ->
+ case re:run(Text, "^([-_:.a-zA-Z]*)(\\([0-9]\\))$", [{capture, all, binary}]) of
+ nomatch -> [Text, " (", Link, ")"];
+ {match, [_, ManPage, ManSection]} -> ["\\fB", ManPage, "\\fR", ManSection]
+ end;
+man_format({em, Text}) ->
+ ["\\fB", man_format(Text), "\\fR"];
+%% We are already using a monospace font.
+%% @todo Maybe there's a readable formatting we could use to differentiate from normal text?
+man_format({mono, Text}) ->
+ man_format(Text);
+man_format(Text) when is_list(Text) ->
+ [man_format(T) || T <- Text].