diff options
Diffstat (limited to 'src/asciideck_to_manpage.erl')
-rw-r--r-- | src/asciideck_to_manpage.erl | 148 |
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]. |