Skip to content

Instantly share code, notes, and snippets.

@garazdawi
Last active March 12, 2020 15:28
Show Gist options
  • Save garazdawi/68527d92ae5b37c8f129bfbdfffdfa68 to your computer and use it in GitHub Desktop.
Save garazdawi/68527d92ae5b37c8f129bfbdfffdfa68 to your computer and use it in GitHub Desktop.
rewrite-seealso
#!/usr/bin/env escript
%% -*- erlang -*-
%%! +A 1 +SDio 1 +S 1 -mode minimal
%% This script can be used to convert <seealso> to <see*>
%% Invoked like this:
%% find lib/*/doc/src system/doc/*/ erts/doc/src/ '(' -name *.xml -o -name *.xmlsrc ')' -exec ./rewrite-seealso.escript {} \;
%% You need a OTP with built documentation for this to work
main([File]) ->
io:format("Parse: ~s~n",[File]),
{ok, B} = file:read_file(File),
CurrApp =
case string:lexemes(File,"/") of
["erts"|_] ->
"erts";
["lib",App|_] ->
{lib,App};
["system","doc",App|_] ->
{system,App}
end,
case get_link_type(B) of
T when T =:= ignore; CurrApp =:= {system,"xml"} ->
ok;
Type ->
_NewB = replace_seealso(B, {Type,CurrApp}),
file:write_file(File, _NewB),
%% io:format("~s",[_NewB]),
ok
end.
get_link_type(B) when is_binary(B) ->
case {re:run(B, <<"<erlref>">>),
re:run(B, <<"<appref>">>),
re:run(B, <<"<comref>">>),
re:run(B, <<"<cref>">>),
re:run(B, <<"<fileref>">>),
re:run(B, <<"<chapter>">>)} of
{{match,_},_,_,_,_,_} ->
"seeerl";
{_,{match,_},_,_,_,_} ->
"seeapp";
{_,_,{match,_},_,_,_} ->
"seecom";
{_,_,_,{match,_},_,_} ->
"seecref";
{_,_,_,_,{match,_},_} ->
"seefile";
{_,_,_,_,_,{match,_}} ->
"seeguide";
_ ->
{match, _} = re:run(B, <<"<application|<part|<specs|<internal">>),
ignore
end;
get_link_type(File) ->
{ok,B} = file:read_file(File),
get_link_type(B).
replace_seealso(Bin, [{Start,Len},Space,Marker,Content], {LinkType, CurrApp}) ->
ModFun =
fun(App,Mod) ->
case code:which(binary_to_atom(Mod)) of
non_existing ->
LMod = binary_to_list(Mod),
Pats =
case App of
"erts" ->
[lists:concat(["erts/doc/xml/",LMod,".xml"]),
lists:concat(["erts/doc/xml/",LMod,"_cmd.xml"])];
{system,SApp} ->
[lists:concat(["system/doc/xml/",SApp,"/",LMod,".xml"])];
{lib,LApp} ->
[lists:concat(["lib/",LApp,"/doc/xml/",LMod,".xml"]),
lists:concat(["lib/",LApp,"/doc/xml/",LMod,"_app.xml"]),
lists:concat(["lib/",LApp,"/doc/xml/",LMod,"_cmd.xml"])]
end,
case lists:flatmap(fun filelib:wildcard/1, Pats) of
[File] ->
get_link_type(File);
Else ->
io:format("Could not resolve link: ~s (~p)~n",
[part(Bin,Marker), Else]),
erlang:halt(1)
end;
_ ->
"seeerl"
end
end,
Rules =
[{"^([^:]+:)?([^#]+)?#[^/]+/.+",
%% app:mod#func/ari
fun(_) ->
"seemfa"
end},
{"^([^#]*#)type-(.*)",
%% #type-anchor
fun([_,Mod,Anchor]) ->
{[Mod,Anchor],"seetype"}
end},
{"^#.*",
%% #anchor
fun([_]) ->
%% A local link
LinkType
end},
{"^doc/([^:/]+):([^#]+)(#.+)?",
%% doc/system:guide#anchor
fun ([_,System,<<"users_guide">>|_]) ->
{["system/",System,":index"],"seeguide"};
([_,System,Guide|T]) ->
{["system/",System,":",Guide|T], ModFun({system,binary_to_list(System)}, Guide)}
end},
{"^([^:/]+):([^#]+)(#.+)?",
%% app:mod#anchor
fun ([_,App,<<"index">>|_]) ->
{[App,":index"],"seeapp"};
([_,App,<<"users_guide">>|_]) ->
{[App,":index"],"seeguide"};
([_,<<"erts">>,Mod|_]) ->
ModFun("erts",Mod);
([_,App,Mod|_]) ->
ModFun({lib,binary_to_list(App)},Mod)
end},
{"^([^#:/]+)(#.+)?",
%% mod#anchor
fun ([_,<<"index">>|_]) ->
{"index","seeapp"};
([_,<<"users_guide">>|_]) ->
{"index","seeguide"};
([_,Mod|_]) -> ModFun(CurrApp,Mod) end}
],
NewLink = run_rules(Bin, Rules, [Space,Marker,Content]),
% io:format("NewLink: ~p~n",[NewLink]),
Before = part(Bin, 0, Start),
After = part(Bin, Start + Len, byte_size(Bin) - (Start + Len)),
[Before,NewLink,After].
replace_seealso(B,State) ->
case re:run(B, <<"<seealso(\\s+)marker\\s*=\\s*\"([^\"]+)\">((?U).*)</seealso>">>,
[{capture,all},dotall,unicode]) of
{match,Match} ->
replace_seealso(replace_seealso(iolist_to_binary(B),Match,State),State);
nomatch ->
B
end.
run_rules(Bin, [{Pat,Fun}|T], [Space,Marker,Content] = M) ->
case re:run(binary:part(Bin,Marker),Pat,[{capture,all,binary},unicode]) of
{match,Match} ->
case Fun(Match) of
{NewMarker, LinkType} ->
["<",LinkType,part(Bin, Space),
"marker=\"",NewMarker,"\">",
part(Bin, Content),"</",LinkType,">"];
LinkType ->
["<",LinkType,part(Bin, Space),
"marker=\"",part(Bin, Marker),"\">",
part(Bin, Content),"</",LinkType,">"]
end;
nomatch ->
run_rules(Bin, T, M)
end;
run_rules(Bin, [], [_Space,Marker,_Content]) ->
io:format("No rule found for \"~s\"~n",[part(Bin,Marker)]),
erlang:halt().
part(_Bin,{-1,0}) ->
<<>>;
part(Bin, Part) ->
binary:part(Bin,Part).
part(Bin,Pos,Len) ->
part(Bin,{Pos,Len}).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment