diff options
Diffstat (limited to 'lib/erl_docgen')
24 files changed, 887 insertions, 602 deletions
diff --git a/lib/erl_docgen/Makefile b/lib/erl_docgen/Makefile index 30ff2bf16e..7e9cc824ec 100644 --- a/lib/erl_docgen/Makefile +++ b/lib/erl_docgen/Makefile @@ -36,4 +36,6 @@ SPECIAL_TARGETS = # include $(ERL_TOP)/make/otp_subdir.mk +DIA_PLT_APPS=edoc xmerl +include $(ERL_TOP)/make/app_targets.mk diff --git a/lib/erl_docgen/doc/src/Makefile b/lib/erl_docgen/doc/src/Makefile index d6d2550425..33eb44a049 100644 --- a/lib/erl_docgen/doc/src/Makefile +++ b/lib/erl_docgen/doc/src/Makefile @@ -29,11 +29,6 @@ VSN=$(ERL_DOCGEN_VSN) APPLICATION=erl_docgen # ---------------------------------------------------- -# Release directory specification -# ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) - -# ---------------------------------------------------- # Target Specs # ---------------------------------------------------- XML_APPLICATION_FILES = ref_man.xml @@ -52,7 +47,8 @@ XML_CHAPTER_FILES = \ inline_tags.xml \ header_tags.xml \ character_entities.xml \ - block_tags.xml + block_tags.xml \ + doc_storage.xml BOOK_FILES = book.xml @@ -64,75 +60,9 @@ TECHNICAL_DESCR_FILES = EXAMPLE_FILES = \ example.txt -GIF_FILES = \ +IMAGE_FILES = \ man.gif # ---------------------------------------------------- -HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \ - $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html) \ - $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html) - -INFO_FILE = ../../info - -MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6) - -HTML_REF_MAN_FILE = $(HTMLDIR)/index.html - -TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf - -# ---------------------------------------------------- -# FLAGS -# ---------------------------------------------------- -XML_FLAGS += -DVIPS_FLAGS += - -# ---------------------------------------------------- -# Targets -# ---------------------------------------------------- -docs: pdf html man - -$(HTMLDIR)/%.gif: %.gif - $(INSTALL_DATA) $< $@ - -$(HTMLDIR)/example.txt: example.txt - $(INSTALL_DATA) $< $@ - -$(TOP_PDF_FILE): $(XML_FILES) - -pdf: $(TOP_PDF_FILE) - -html: gifs examples $(HTML_REF_MAN_FILE) - -clean clean_docs: - rm -rf $(HTMLDIR)/* - rm -rf $(XMLDIR) - rm -f $(MAN6DIR)/* - rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) - rm -f errs core *~ - rm -f $(JD_HTML) $(JD_PACK) - -man: $(MAN6_FILES) - -gifs: $(GIF_FILES:%=$(HTMLDIR)/%) - -examples: $(GIF_FILES:%=$(HTMLDIR)/%) - -debug opt: - -# ---------------------------------------------------- -# Release Target -# ---------------------------------------------------- -include $(ERL_TOP)/make/otp_release_targets.mk - -release_docs_spec: docs - $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" - $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" - $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" - $(INSTALL_DATA) $(HTMLDIR)/* \ - "$(RELSYSDIR)/doc/html" - $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" - $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" - $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6" - -release_spec: +include $(ERL_TOP)/make/doc.mk diff --git a/lib/erl_docgen/doc/src/block_tags.xml b/lib/erl_docgen/doc/src/block_tags.xml index ceed9305f4..bcaec0fbd1 100644 --- a/lib/erl_docgen/doc/src/block_tags.xml +++ b/lib/erl_docgen/doc/src/block_tags.xml @@ -42,7 +42,6 @@ <seealso marker="#listTAG"><list></seealso>, <seealso marker="#taglistTAG"><taglist></seealso>, <seealso marker="#codeincludeTAG"><codeinclude></seealso> and - <seealso marker="#erlevalTAG"><erleval></seealso>. </p> <section> @@ -131,21 +130,6 @@ start(Pid) -> </section> <section> - <marker id="erlevalTAG"></marker> - <title><erleval> - Erlang Evaluation</title> - - <p>Include the result from evaluating an Erlang expression. Example: - </p> - <code><![CDATA[ -<erleval expr="{A,b,C}={a,b,c}. "/> - ]]></code> - <p>results in:</p> - <erleval expr="{A,b,C}={a,b,c}. "></erleval> - - <p>Note the '.' and space after the expression.</p> - </section> - - <section> <marker id="listTAG"></marker> <title><list> - List</title> diff --git a/lib/erl_docgen/doc/src/doc_storage.xml b/lib/erl_docgen/doc/src/doc_storage.xml new file mode 100644 index 0000000000..9eee863e3d --- /dev/null +++ b/lib/erl_docgen/doc/src/doc_storage.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1997</year><year>2016</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + </legalnotice> + <title>Documentation Storage</title> + <prepared></prepared> + <docno></docno> + <date></date> + <rev></rev> + <file>doc_storage.xml</file> + </header> + + <section> + <title>EEP-48: Documentation storage and format</title> + <p><url href="https://www.erlang.org/erlang-enhancement-proposals/eep-0048.html">EEP-48</url> + defines a common documentation storage format for module documentation in the Erlang/OTP + ecosystem. Erl_Docgen can generate documentation in this format from XML files following + the DTD's descibed in the other User's Guides in this application.</p> + <p>Some special considerations have to be taken when writing documentation that + should also be available through EEP-48 style storage.</p> + <list> + <item>The <c>#PCDATA</c> within <c><name></c> tags must be parseable to figure out the arity of the function.</item> + <item>It is not allowed to mix <c><name></c> tags with #PCDATA and attributes.</item> + <item>All <c><name></c> tags within <c><func></c> has to have a <c>since</c> attribute.</item> + <item>All callback function documentations have to start with a <c>Module</c> prefix.</item> + </list> + </section> + + <section> + <title>Erlang Documentation Format</title> + <p>When generating documentation for generic storage</p> + </section> + + <section> + <title>See Also</title> + <p> + <seealso marker="stdlib:shell_docs"><c>shell_docs(3)</c></seealso>, + <seealso marker="kernel:code#get_doc-1"><c>code:get_doc(3)</c></seealso> + </p> + </section> + +</chapter> diff --git a/lib/erl_docgen/doc/src/inline_tags.xml b/lib/erl_docgen/doc/src/inline_tags.xml index 25b0cd4d87..2b5239c855 100644 --- a/lib/erl_docgen/doc/src/inline_tags.xml +++ b/lib/erl_docgen/doc/src/inline_tags.xml @@ -171,45 +171,5 @@ </p> </section> - <section> - <marker id="termTAG"></marker> - <marker id="termdefTAG"></marker> - <title><term>, <termdef> - Glossary</title> - - <p>Used to highlight a term with a local (for this document only) or - global definition. The identity of the term is given by - the <c>id</c> attribute.</p> - - <p>For a locally defined term, the tag contains a - <c><termdef></c>, which in turn contains an explanation of - the term as plain text. Example:</p> - <pre><![CDATA[ -<term id="HTML"><termdef>Hyper-Text Markup Language</termdef></term> - ]]></pre> - - <p>In the generated HTML, it is the term name which will be visible. - For locally defined terms, the id and the name are the same. - The name has a hypertext link to the definition in the glossary. - Example:</p> - <pre><![CDATA[ -<term id="HTML"><termdef>Hyper-Text Markup Language</termdef></term> - ]]></pre> - <p>results in: <term id="HTML"><termdef>Hyper-Text Markup Language</termdef></term> - </p> - - <p>If a term is defined both locally and globally, the global - definition takes precedence.</p> - </section> - - <section> - <marker id="citeTAG"></marker> - <marker id="citedefTAG"></marker> - <title><cite>, <citedef> - Bibliography</title> - - <p>Works the same way as <c><term></c> and - <c><termdef></c>, but for a bibliography list rather than - a glossary.</p> - - </section> </chapter> diff --git a/lib/erl_docgen/doc/src/part.xml b/lib/erl_docgen/doc/src/part.xml index 0e97af7169..91ff979af5 100644 --- a/lib/erl_docgen/doc/src/part.xml +++ b/lib/erl_docgen/doc/src/part.xml @@ -40,5 +40,6 @@ <xi:include href="block_tags.xml"/> <xi:include href="inline_tags.xml"/> <xi:include href="character_entities.xml"/> + <xi:include href="doc_storage.xml"/> </part> diff --git a/lib/erl_docgen/priv/bin/chunk.escript b/lib/erl_docgen/priv/bin/chunk.escript new file mode 100644 index 0000000000..a9ea6b9b73 --- /dev/null +++ b/lib/erl_docgen/priv/bin/chunk.escript @@ -0,0 +1,30 @@ +#!/usr/bin/env escript +%% -*- erlang -*- +%%! +A 1 +SDio 1 +S 1 -mode minimal +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2020. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%%---------------------------------------------------------------------- +%% File : chunk.escript +%% +%% Created : 1 Nov 2018 by Kenneth Lundin <uabkeld@elxa31hr002> +%% +%% Trampoline to xml to chunk creation. +%%---------------------------------------------------------------------- + +main(Args) -> + docgen_xml_to_chunk:main(Args). diff --git a/lib/erl_docgen/priv/bin/specs_gen.escript b/lib/erl_docgen/priv/bin/specs_gen.escript index 859f3c21f5..96b63aa667 100644 --- a/lib/erl_docgen/priv/bin/specs_gen.escript +++ b/lib/erl_docgen/priv/bin/specs_gen.escript @@ -48,7 +48,8 @@ main(Args) -> parse(["-o"++Dir | Opts], InclFs, _, Module) -> parse(Opts, InclFs, Dir, Module); parse(["-I"++I | Opts], InclFs, Dir, Module) -> - parse(Opts, [I | InclFs], Dir, Module); + Is = filelib:wildcard(I), + parse(Opts, Is ++ InclFs, Dir, Module); parse(["-module", Module | Opts], InclFs, Dir, _) -> parse(Opts, InclFs, Dir, Module); parse([File], InclFs, Dir, no_module) -> @@ -131,7 +132,7 @@ write_text(Text, File, Dir) -> ok; {error, R} -> R1 = file:format_error(R), - io:format("could not write file '~s': ~s\n", [File, R1]), + io:format("could not write file '~s': ~s\n", [OutFile, R1]), halt(2) end. diff --git a/lib/erl_docgen/priv/css/Makefile b/lib/erl_docgen/priv/css/Makefile index e3d2ee7e3f..da2ef5ad15 100644 --- a/lib/erl_docgen/priv/css/Makefile +++ b/lib/erl_docgen/priv/css/Makefile @@ -53,7 +53,9 @@ CSS_FILES = \ # ---------------------------------------------------- debug opt: -docs: +DOC_TARGETS?=html + +docs: $(DOC_TARGETS) clean: $(RM) $(TARGET_FILES) @@ -64,16 +66,17 @@ clean: # ---------------------------------------------------- include $(ERL_TOP)/make/otp_release_targets.mk - release_spec: opt $(INSTALL_DIR) "$(RELSYSDIR)/priv/css" $(INSTALL_DATA) $(CSS_FILES) "$(RELSYSDIR)/priv/css" -release_docs_spec: +release_html_spec: html $(INSTALL_DIR) "$(RELEASE_PATH)/doc" $(INSTALL_DATA) $(CSS_FILES) ../nyi.html "$(RELEASE_PATH)/doc" +release_docs_spec: $(DOC_TARGETS:%=release_%_spec) + release_tests_spec: diff --git a/lib/erl_docgen/priv/dtd/Makefile b/lib/erl_docgen/priv/dtd/Makefile index e35e5f8826..fd8d8a43c7 100644 --- a/lib/erl_docgen/priv/dtd/Makefile +++ b/lib/erl_docgen/priv/dtd/Makefile @@ -46,7 +46,6 @@ DTD_FILES = \ fileref.dtd \ xhtml1-frameset.dtd \ appref.dtd \ - cites.dtd \ common.image.dtd \ cref.dtd \ part.dtd \ @@ -55,13 +54,9 @@ DTD_FILES = \ common.dtd \ common.refs.dtd \ erlref.dtd \ - report.dtd \ xhtml1-transitional.dtd \ - bookinsidecover.dtd \ common.entities.dtd \ - common.table.dtd \ - fascicules.dtd \ - terms.dtd + common.table.dtd ENT_FILES = \ xhtml-special.ent \ diff --git a/lib/erl_docgen/priv/dtd/bookinsidecover.dtd b/lib/erl_docgen/priv/dtd/bookinsidecover.dtd deleted file mode 100644 index ae22c45884..0000000000 --- a/lib/erl_docgen/priv/dtd/bookinsidecover.dtd +++ /dev/null @@ -1,37 +0,0 @@ -<!-- - ``Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - The Initial Developer of the Original Code is Ericsson AB. - Portions created by Ericsson are Copyright 1999-2007, Ericsson AB. - All Rights Reserved.'' - - $Id$ ---> -<!ENTITY % ISOlat1 SYSTEM "xhtml-lat1.ent" > -%ISOlat1; - -<!ENTITY amp "&" > -<!ENTITY gt ">" > -<!ENTITY lt "<" > - -<!ELEMENT bookinsidecover (#PCDATA|br|theheader|vfill|tt|bold)* > - -<!ELEMENT tt (#PCDATA|br|theheader|vfill)* > -<!ELEMENT bold (#PCDATA|br|theheader|vfill)* > -<!ELEMENT vfill EMPTY > -<!ELEMENT theheader EMPTY > -<!ATTLIST theheader tag (title|prepared|responsible|docno| - approved|checked|date|rev|file| - none) "none" > - -<!ELEMENT br EMPTY > diff --git a/lib/erl_docgen/priv/dtd/cites.dtd b/lib/erl_docgen/priv/dtd/cites.dtd deleted file mode 100644 index 4558947db0..0000000000 --- a/lib/erl_docgen/priv/dtd/cites.dtd +++ /dev/null @@ -1,36 +0,0 @@ -<!-- - ``Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - The Initial Developer of the Original Code is Ericsson AB. - Portions created by Ericsson are Copyright 1999-2007, Ericsson AB. - All Rights Reserved.'' - - $Id$ ---> -<!ENTITY % ISOlat1 SYSTEM "xhtml-lat1.ent" > -%ISOlat1; - -<!ENTITY amp "&" > -<!ENTITY gt ">" > -<!ENTITY lt "<" > - -<!-- Structure --> - -<!ELEMENT cites (cite)* > -<!ELEMENT cite (id, shortdef, def, resp?) > -<!ELEMENT id (#PCDATA) > -<!ELEMENT shortdef (#PCDATA) > -<!ELEMENT def (#PCDATA|c|i|em)* > -<!ELEMENT resp (#PCDATA) > -<!ELEMENT c (#PCDATA) > -<!ELEMENT em (#PCDATA|c)* > diff --git a/lib/erl_docgen/priv/dtd/common.dtd b/lib/erl_docgen/priv/dtd/common.dtd index 0ccd52068b..ca680c15b6 100644 --- a/lib/erl_docgen/priv/dtd/common.dtd +++ b/lib/erl_docgen/priv/dtd/common.dtd @@ -22,9 +22,8 @@ <!ENTITY % common.entities SYSTEM "common.entities.dtd" > %common.entities; -<!ENTITY % block "p|pre|code|list|taglist|codeinclude| - erleval" > -<!ENTITY % inline "#PCDATA|c|i|em|strong|term|cite|br|path|seealso| +<!ENTITY % block "p|pre|code|list|taglist|codeinclude" > +<!ENTITY % inline "#PCDATA|c|i|em|strong|term|br|seealso| url|marker|anno|image" > <!-- XXX --> <!ELEMENT p (%inline;)* > @@ -43,33 +42,16 @@ <!ELEMENT strong (#PCDATA|c|anno)* > <!ELEMENT anno (#PCDATA) > -<!-- XXX --> -<!ELEMENT term (termdef?) > -<!ATTLIST term id CDATA #REQUIRED > -<!ELEMENT termdef (#PCDATA) > -<!ELEMENT cite (citedef?) > -<!ATTLIST cite id CDATA #REQUIRED > -<!ELEMENT citedef (ctitle,cauthor,chowpublished) > -<!ELEMENT ctitle (#PCDATA) > -<!ELEMENT cauthor (#PCDATA) > -<!ELEMENT chowpublished (#PCDATA) > - <!-- XXX --> <!ELEMENT br EMPTY > -<!-- Path --> - -<!ELEMENT path (#PCDATA) > -<!ATTLIST path unix CDATA "" - windows CDATA "" > - <!-- List --> <!ELEMENT list (item+) > <!ATTLIST list type (ordered|bulleted) "bulleted" > -<!ELEMENT taglist (tag,item+)+ > +<!ELEMENT taglist (marker*,tag,item+)+ > <!ELEMENT tag (#PCDATA|c|i|em|br|seealso|url|marker|anno)* > -<!ELEMENT item (%inline;|%block;|warning|note|dont|do|quote)* > +<!ELEMENT item (%inline;|%block;|warning|note|dont|do|quote|table)* > <!-- References --> @@ -86,8 +68,3 @@ <!ATTLIST codeinclude file CDATA #REQUIRED tag CDATA "" type (erl|c|none) "none" > - -<!-- ErlEval --> - -<!ELEMENT erleval EMPTY > -<!ATTLIST erleval expr CDATA #REQUIRED > diff --git a/lib/erl_docgen/priv/dtd/fascicules.dtd b/lib/erl_docgen/priv/dtd/fascicules.dtd deleted file mode 100644 index 073d0cc1d9..0000000000 --- a/lib/erl_docgen/priv/dtd/fascicules.dtd +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!-- - ``Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - The Initial Developer of the Original Code is Ericsson AB. - Portions created by Ericsson are Copyright 1999-2007, Ericsson AB. - All Rights Reserved.'' - - $Id$ ---> - -<!ENTITY % ISOlat1 SYSTEM "xhtml-lat1.ent" > -%ISOlat1; - -<!ENTITY amp "&" > -<!ENTITY gt ">" > -<!ENTITY lt "<" > - -<!-- Structure --> - -<!ELEMENT fascicules (fascicule)+ > -<!ELEMENT fascicule (#PCDATA) > -<!ATTLIST fascicule file CDATA #REQUIRED - href CDATA #REQUIRED - entry (yes|no) "no" > - diff --git a/lib/erl_docgen/priv/dtd/report.dtd b/lib/erl_docgen/priv/dtd/report.dtd deleted file mode 100644 index 3dd1c3d347..0000000000 --- a/lib/erl_docgen/priv/dtd/report.dtd +++ /dev/null @@ -1,141 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!-- - ``Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - The Initial Developer of the Original Code is Ericsson AB. - Portions created by Ericsson are Copyright 1999-2007, Ericsson AB. - All Rights Reserved.'' - - $Id$ ---> - -<!ENTITY % ISOlat1 SYSTEM "xhtml-lat1.ent" > -%ISOlat1; - -<!ENTITY amp "&" > -<!ENTITY gt ">" > -<!ENTITY lt "<" > - -<!ENTITY % header "title,prepared,responsible,docno,approved, - checked,date,rev,file" > -<!ENTITY % block "p|pre|code|list|taglist|erlinclude| - codeinclude|erleval" > -<!ENTITY % inline "#PCDATA|i|b|c|em|term|cite|br|path|seealso| - url|marker" > - -<!-- Structure --> - -<!ELEMENT report (header,section+) > -<!ELEMENT header (title,prepared,responsible?,docno,approved?, - checked?,date,rev,file?) > -<!ELEMENT title (#PCDATA) > -<!ELEMENT prepared (#PCDATA) > -<!ELEMENT responsible (#PCDATA) > -<!ELEMENT docno (#PCDATA) > -<!ELEMENT approved (#PCDATA) > -<!ELEMENT checked (#PCDATA) > -<!ELEMENT date (#PCDATA) > -<!ELEMENT rev (#PCDATA) > -<!ELEMENT file (#PCDATA) > - -<!ELEMENT section (marker*,title, - (%block;|quote|warning|note|dont|do|br|image|marker| - table|section)*) > -<!ELEMENT p (%inline;|index)* > -<!ELEMENT pre (#PCDATA|seealso|url|input)* > -<!ELEMENT input (#PCDATA|seealso|url)* > -<!ELEMENT code (#PCDATA) > -<!ATTLIST code type (erl|c|none) "none" > -<!ELEMENT quote (p)* > -<!ELEMENT warning (%block;|quote|br|image|marker|table)* > -<!ELEMENT note (%block;|quote|br|image|marker|table)* > -<!ELEMENT dont (%block;|quote|br|image|marker|table)* > -<!ELEMENT do (%block;|quote|br|image|marker|table)* > -<!ELEMENT i (#PCDATA|b|c|em)* > -<!ELEMENT b (#PCDATA|i|c|em)* > -<!ELEMENT c (#PCDATA) > -<!ELEMENT em (#PCDATA|i|b|c)* > -<!ELEMENT term (termdef?) > -<!ATTLIST term id CDATA #REQUIRED > -<!ELEMENT termdef (#PCDATA) > -<!ELEMENT cite (citedef?) > -<!ATTLIST cite id CDATA #REQUIRED > -<!ELEMENT citedef (ctitle,cauthor,chowpublished) > -<!ELEMENT ctitle (#PCDATA) > -<!ELEMENT cauthor (#PCDATA) > -<!ELEMENT chowpublished (#PCDATA) > -<!ELEMENT br EMPTY > - -<!-- Path --> - -<!ELEMENT path (#PCDATA) > -<!ATTLIST path unix CDATA "" - windows CDATA "" > - -<!-- List --> - -<!ELEMENT list (item+) > -<!ATTLIST list type (ordered|bulleted) "bulleted" > -<!ELEMENT taglist (tag,item)+ > -<!ELEMENT tag (#PCDATA|i|b|c|em|seealso|url)* > -<!ELEMENT item (%inline;|%block;)* > - -<!-- Image --> - -<!ELEMENT image (icaption?) > -<!ATTLIST image file CDATA #REQUIRED > -<!ELEMENT icaption (#PCDATA) > - -<!-- References --> - -<!ELEMENT seealso (#PCDATA) > -<!ATTLIST seealso marker CDATA #REQUIRED > -<!ELEMENT url (#PCDATA) > -<!ATTLIST url href CDATA #REQUIRED > -<!ELEMENT marker EMPTY > -<!ATTLIST marker id CDATA #REQUIRED > - -<!-- Table --> - -<!ELEMENT table (row+,tcaption?) > -<!ATTLIST table width CDATA "0" - colspec CDATA "" > -<!ELEMENT row (cell+) > -<!ELEMENT cell (%inline;)* > -<!ATTLIST cell align (left|center|right) "left" - valign (top|middle|bottom) "middle" > -<!ELEMENT tcaption (#PCDATA) > - -<!-- ErlInclude --> - -<!ELEMENT erlinclude EMPTY > -<!ATTLIST erlinclude file CDATA #REQUIRED - tag CDATA #REQUIRED > - -<!-- CodeInclude --> - -<!ELEMENT codeinclude EMPTY > -<!ATTLIST codeinclude file CDATA #REQUIRED - tag CDATA "" - type (erl|c|none) "none" > - -<!-- ErlEval --> - -<!ELEMENT erleval EMPTY > -<!ATTLIST erleval expr CDATA #REQUIRED > - -<!-- Index FOR COMPATIBILITY --> - -<!ELEMENT index EMPTY > -<!ATTLIST index txt CDATA #REQUIRED > - diff --git a/lib/erl_docgen/priv/dtd/terms.dtd b/lib/erl_docgen/priv/dtd/terms.dtd deleted file mode 100644 index c2965eb61c..0000000000 --- a/lib/erl_docgen/priv/dtd/terms.dtd +++ /dev/null @@ -1,37 +0,0 @@ -<!-- - ``Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - The Initial Developer of the Original Code is Ericsson AB. - Portions created by Ericsson are Copyright 1999-2007, Ericsson AB. - All Rights Reserved.'' - - $Id$ ---> -<!ENTITY % ISOlat1 SYSTEM "xhtml-lat1.ent" > -%ISOlat1; - -<!ENTITY amp "&" > -<!ENTITY gt ">" > -<!ENTITY lt "<" > - -<!-- Structure --> - -<!ELEMENT terms (term)* > -<!ELEMENT term (id, shortdef, def, resp?) > -<!ELEMENT id (#PCDATA) > -<!ELEMENT shortdef (#PCDATA) > -<!ELEMENT def (#PCDATA|c|i|em)* > -<!ELEMENT resp (#PCDATA) > -<!ELEMENT c (#PCDATA) > -<!ELEMENT em (#PCDATA|c)* > - diff --git a/lib/erl_docgen/priv/images/Makefile b/lib/erl_docgen/priv/images/Makefile index cd98399b6a..b0263524fb 100644 --- a/lib/erl_docgen/priv/images/Makefile +++ b/lib/erl_docgen/priv/images/Makefile @@ -54,7 +54,9 @@ PNG_FILES = \ # ---------------------------------------------------- debug opt: -docs: +DOC_TARGETS?=html + +docs: $(DOC_TARGETS) clean: $(RM) $(TARGET_FILES) @@ -70,11 +72,12 @@ release_spec: opt $(INSTALL_DIR) "$(RELSYSDIR)/priv/images" $(INSTALL_DATA) $(GIF_FILES) $(PNG_FILES) "$(RELSYSDIR)/priv/images" - -release_docs_spec: +release_html_spec: $(INSTALL_DIR) "$(RELEASE_PATH)/doc" $(INSTALL_DATA) $(PNG_FILES) "$(RELEASE_PATH)/doc" +release_docs_spec: $(DOC_TARGETS:%=release_%_spec) + release_tests_spec: diff --git a/lib/erl_docgen/priv/js/flipmenu/Makefile b/lib/erl_docgen/priv/js/flipmenu/Makefile index ad6d4acb6c..be0bed74fb 100644 --- a/lib/erl_docgen/priv/js/flipmenu/Makefile +++ b/lib/erl_docgen/priv/js/flipmenu/Makefile @@ -56,7 +56,9 @@ JS_FILES = \ # ---------------------------------------------------- debug opt: -docs: +DOC_TARGETS?=html + +docs: $(DOC_TARGETS) clean: $(RM) $(TARGET_FILES) @@ -67,17 +69,17 @@ clean: # ---------------------------------------------------- include $(ERL_TOP)/make/otp_release_targets.mk - release_spec: opt $(INSTALL_DIR) "$(RELSYSDIR)/priv/js/flipmenu" $(INSTALL_DATA) $(JS_FILES) $(GIF_FILES) "$(RELSYSDIR)/priv/js/flipmenu" - -release_docs_spec: +release_html_spec: html $(INSTALL_DIR) "$(RELEASE_PATH)/doc/js/flipmenu" $(INSTALL_DATA) $(JS_FILES) $(GIF_FILES) "$(RELEASE_PATH)/doc/js/flipmenu" $(INSTALL_DATA) ../highlight.js ../highlight.pack.js "$(RELEASE_PATH)/doc/js/" +release_docs_spec: $(DOC_TARGETS:%=release_%_spec) + release_tests_spec: diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl index aee496b948..8408d634bd 100644 --- a/lib/erl_docgen/priv/xsl/db_html.xsl +++ b/lib/erl_docgen/priv/xsl/db_html.xsl @@ -2532,30 +2532,6 @@ </xsl:template> - <xsl:template match="term"> - <xsl:value-of select="@id"/> - <!-- xsl:choose> - <xsl:when test="boolean(termdef)"> - <xsl:choose> - <xsl:when test="ancestor::parts"> - <a href="users_guide_glossary.html#{@id}"><xsl:value-of select="@id"/></a> - </xsl:when> - <xsl:when test="ancestor::applications"> - <a href="ref_man_glossary.html#{@id}"><xsl:value-of select="@id"/></a> - </xsl:when> - </xsl:choose> - </xsl:when> - <xsl:otherwise> - <a href="{$topdocdir}/glossary.html#{@id}"><xsl:value-of select="@id"/></a> - </xsl:otherwise> - </xsl:choose --> - </xsl:template> - - <xsl:template match="cite"> - <xsl:value-of select="@id"/> - </xsl:template> - - <!-- Release Notes --> <xsl:template match="releasenotes"> @@ -2609,118 +2585,6 @@ </div> </xsl:template> - <!-- Glossary --> - <xsl:template name="glossary"> - <xsl:param name="type"/> - <xsl:document href="{$outdir}/{$type}_glossary.html" method="html" encoding="UTF-8" indent="yes" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"> - <html> - <head> - <link rel="stylesheet" href="{$topdocdir}/otp_doc.css" type="text/css"/> - <title>Erlang Documentation -- <xsl:value-of select="header/title"/></title> - </head> - <body bgcolor="white" text="#000000" link="#0000ff" vlink="#ff00ff" alink="#ff0000"> - - <div id="container"> - <script id="js" type="text/javascript" language="JavaScript" src="{$topdocdir}/js/flipmenu/flipmenu.js"/> - <script id="js2" type="text/javascript" src="{$topdocdir}/js/erlresolvelinks.js"></script> - - <!-- Generate menu --> - <xsl:call-template name="menu"/> - - <div id="content"> - <div class="innertube"> - <h1>Glossary</h1> - </div> - - <dl> - <xsl:for-each select="descendant::term"> - <xsl:sort select="@id"/> - <xsl:if test="boolean(termdef)"> - <dt><a name="{@id}"><strong><xsl:value-of select="@id"/></strong></a></dt> - <dd><xsl:value-of select="termdef"/></dd> - </xsl:if> - </xsl:for-each> - </dl> - - <div class="footer"> - <hr/> - <p> - <xsl:value-of select="$copyright"/> - <xsl:value-of select="header/copyright/year[1]"/> - <xsl:text>-</xsl:text> - <xsl:value-of select="header/copyright/year[2]"/> - <xsl:text> </xsl:text> - <xsl:value-of select="header/copyright/holder"/> - </p> - </div> - - </div> - </div> - - </body> - </html> - - </xsl:document> - </xsl:template> - - <!-- Bibliography --> - <xsl:template name="bibliography"> - - <xsl:param name="type"/> - <xsl:document href="{$outdir}/{$type}_bibliography.html" method="html" encoding="UTF-8" indent="yes" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"> - <html> - <head> - <link rel="stylesheet" href="{$topdocdir}/otp_doc.css" type="text/css"/> - <title>Erlang Documentation -- <xsl:value-of select="header/title"/></title> - </head> - <body bgcolor="white" text="#000000" link="#0000ff" vlink="#ff00ff" alink="#ff0000"> - - <div id="container"> - <script id="js" type="text/javascript" language="JavaScript" src="{$topdocdir}/js/flipmenu/flipmenu.js"/> - <script id="js2" type="text/javascript" src="{$topdocdir}/js/erlresolvelinks.js"></script> - - <!-- Generate menu --> - <xsl:call-template name="menu"/> - - <div id="content"> - <div class="innertube"> - <h1>Bibliography</h1> - </div> - - <table> - <xsl:for-each select="descendant::cite"> - <xsl:sort select="@id"/> - <xsl:if test="boolean(citedef)"> - <tr> - <td><xsl:value-of select="@id"/></td> - <td><xsl:value-of select="citedef"/></td> - </tr> - </xsl:if> - </xsl:for-each> - </table> - - <div class="footer"> - <hr/> - <p> - <xsl:value-of select="$copyright"/> - <xsl:value-of select="header/copyright/year[1]"/> - <xsl:text>-</xsl:text> - <xsl:value-of select="header/copyright/year[2]"/> - <xsl:text> </xsl:text> - <xsl:value-of select="header/copyright/holder"/> - </p> - </div> - - </div> - </div> - - </body> - </html> - - </xsl:document> - </xsl:template> - - <!-- Special templates to calculate the arity of functions --> <xsl:template name="calc-arity"> <xsl:param name="string"/> diff --git a/lib/erl_docgen/priv/xsl/db_pdf.xsl b/lib/erl_docgen/priv/xsl/db_pdf.xsl index 7080394298..0e559f6067 100644 --- a/lib/erl_docgen/priv/xsl/db_pdf.xsl +++ b/lib/erl_docgen/priv/xsl/db_pdf.xsl @@ -980,7 +980,7 @@ </fo:block> - <xsl:apply-templates select="section|quote|warning|note|br|image|marker|table|p|pre|code|list|taglist|codeinclude|erleval"> + <xsl:apply-templates select="section|quote|warning|note|br|image|marker|table|p|pre|code|list|taglist|codeinclude"> <xsl:with-param name="partnum" select="$partnum"/> <xsl:with-param name="chapnum"><xsl:number/></xsl:with-param> </xsl:apply-templates> diff --git a/lib/erl_docgen/src/Makefile b/lib/erl_docgen/src/Makefile index 82d051e9bb..4c6f542ebb 100644 --- a/lib/erl_docgen/src/Makefile +++ b/lib/erl_docgen/src/Makefile @@ -38,7 +38,8 @@ RELSYSDIR = $(RELEASE_PATH)/lib/erl_docgen-$(VSN) MODULES = \ docgen_otp_specs \ docgen_edoc_xml_cb \ - docgen_xmerl_xml_cb + docgen_xmerl_xml_cb \ + docgen_xml_to_chunk HRL_FILES = diff --git a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl index 5342d02947..3354597de8 100644 --- a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl +++ b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl @@ -781,7 +781,7 @@ functions(Fs) -> function(_Name, E=#xmlElement{content = Es}) -> TypeSpec = get_content(typespec, Es), [?NL,{func, [ ?NL, - {name, + {name, [{since,""}], case funcheader(TypeSpec) of [] -> signature(get_content(args, Es), diff --git a/lib/erl_docgen/src/docgen_xml_to_chunk.erl b/lib/erl_docgen/src/docgen_xml_to_chunk.erl new file mode 100644 index 0000000000..32bfe980ef --- /dev/null +++ b/lib/erl_docgen/src/docgen_xml_to_chunk.erl @@ -0,0 +1,757 @@ +%% -*- erlang -*- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2020. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%%---------------------------------------------------------------------- +%% File : docgen_xml_to_chunk +%% +%% Created : 1 Nov 2018 by Kenneth Lundin <uabkeld@elxa31hr002> +%% +%% Does translation of Erlang XML docs to EEP-48 doc chunks. +%%---------------------------------------------------------------------- +-module(docgen_xml_to_chunk). +-export([main/1]). + +-include_lib("kernel/include/eep48.hrl"). + +main([FromBeam, _Escript, ToChunk]) -> + %% The given module is not documented, generate a hidden beam chunk file + Name = filename:basename(filename:rootname(FromBeam)) ++ ".erl", + + EmptyDocs = #docs_v1{ anno = erl_anno:set_file(Name, erl_anno:new(0)), + module_doc = hidden, docs = []}, + ok = file:write_file(ToChunk, term_to_binary(EmptyDocs,[compressed])), + ok; +main([FromXML, FromBeam, _Escript, ToChunk]) -> + _ = erlang:process_flag(max_heap_size,20 * 1000 * 1000), + case docs(FromXML, FromBeam) of + {error, Reason} -> + io:format("Failed to create chunks: ~p~n",[Reason]), + erlang:halt(1); + {docs_v1,_,_,_,_,#{ source := S },[]} when + %% This is a list of all modules that do are known not have any functions + S =/= "../xml/gen_fsm.xml", + S =/= "../xml/shell_default.xml", + S =/= "../xml/user.xml", + S =/= "../xml/wxClipboardTextEvent.xml", + S =/= "../xml/wxDisplayChangedEvent.xml", + S =/= "../xml/wxGBSizerItem.xml", + S =/= "../xml/wxGraphicsBrush.xml", + S =/= "../xml/wxGraphicsFont.xml", + S =/= "../xml/wxGraphicsPen.xml", + S =/= "../xml/wxInitDialogEvent.xml", + S =/= "../xml/wxMaximizeEvent.xml", + S =/= "../xml/wxMouseCaptureLostEvent.xml", + S =/= "../xml/wxPaintEvent.xml", + S =/= "../xml/wxPreviewCanvas.xml", + S =/= "../xml/wxSysColourChangedEvent.xml", + S =/= "../xml/wxTaskBarIconEvent.xml", + S =/= "../xml/wxWindowCreateEvent.xml", + S =/= "../xml/wxWindowDestroyEvent.xml", + S =/= "../xml/wxDataObject.xml" + -> + io:format("Failed to create chunks: no functions found ~s~n",[S]), + erlang:halt(1), + ok; + Docs -> + ok = file:write_file(ToChunk, term_to_binary(Docs,[compressed])) + end. + +%% Error handling +%%---------------------------------------------------------------------- + +-define(error(Reason), + throw({dom_error, Reason})). + +%%---------------------------------------------------------------------- + +%%====================================================================== +%% Records +%%====================================================================== + +%%---------------------------------------------------------------------- +%% State record for the validator +%%---------------------------------------------------------------------- +-record(state, { + tags=[], %% Tag stack + cno=[], %% Current node number + namespaces = [], %% NameSpace stack + dom=[] %% DOM structure + }). + +%%====================================================================== +%% External functions +%%====================================================================== + +%%---------------------------------------------------------------------- +%% Function: initial_state() -> Result +%% Parameters: +%% Result: +%% Description: +%%---------------------------------------------------------------------- +initial_state() -> + #state{}. + +%%---------------------------------------------------------------------- +%% Function: get_dom(State) -> Result +%% Parameters: +%% Result: +%% Description: +%%---------------------------------------------------------------------- +get_dom(#state{dom=Dom}) -> + Dom. + +%%---------------------------------------------------------------------- +%% Function: event(Event, LineNo, State) -> Result +%% Parameters: +%% Result: +%% Description: +%%---------------------------------------------------------------------- +event(Event, _LineNo, State) -> + build_dom(Event, State). + + +%%====================================================================== +%% Internal functions +%%====================================================================== + +%%---------------------------------------------------------------------- +%% Function : build_dom(Event, State) -> Result +%% Parameters: Event = term() +%% State = #xmerl_sax_simple_dom_state{} +%% Result : #xmerl_sax_simple_dom_state{} | +%% Description: +%%---------------------------------------------------------------------- + +%% Document +%%---------------------------------------------------------------------- +build_dom(startDocument, State) -> + State#state{dom=[startDocument]}; +build_dom(endDocument, + #state{dom=[{Tag, Attributes, Content} |D]} = State) -> + case D of + [startDocument] -> + State#state{dom=[{Tag, Attributes, + lists:reverse(Content)}]}; + [Decl, startDocument] -> + State#state{dom=[Decl, {Tag, Attributes, + lists:reverse(Content)}]}; + _ -> + %% endDocument is also sent by the parser when a fault occur to tell + %% the event receiver that no more input will be sent + State + end; + +%% Element +%%---------------------------------------------------------------------- +build_dom({startElement, _Uri, LocalName, _QName, Attributes}, + #state{tags=T, dom=D} = State) -> + + A = parse_attributes(LocalName, Attributes), + CName = list_to_atom(LocalName), + + State#state{tags=[CName |T], + dom=[{CName, + lists:reverse(A), + [] + } | D]}; +build_dom({endElement, _Uri, LocalName, _QName}, + #state{tags=[_ |T], + dom=[{CName, CAttributes, CContent}, + {PName, PAttributes, PContent} = _Parent | D]} = State) -> + case list_to_atom(LocalName) of + CName -> + SectionDepth = length([E || E <- T, E =:= section]), + MappedCName = + case CName of + title -> + lists:nth(SectionDepth+1,[h1,h2,h3]); + section when SectionDepth > 0 -> + p; + CName -> CName + end, + + State#state{tags=T, + dom=[{PName, PAttributes, + [{MappedCName, CAttributes, + lists:reverse(CContent)} + |PContent] + } | D]}; + _ -> + ?error("Got end of element: " ++ LocalName ++ " but expected: " ++ + CName) + end; + +%% Text +%%---------------------------------------------------------------------- +build_dom({characters, String}, + #state{dom=[{Name, Attributes, Content}| D]} = State) -> + HtmlEnts = [{" ",[160]}, + {"×",[215]}, + {"±",[177]}, + {"ö","ö"}, + {"ä","ä"}, + {"å","å"} + ], + + NoHtmlEnt = + lists:foldl( + fun({Pat,Sub},Str) -> + re:replace(Str,Pat,Sub,[global,unicode]) + end,String,HtmlEnts), + + case re:run(NoHtmlEnt,"&[a-z]*;",[{capture,first,binary},unicode]) of + nomatch -> ok; + {match,[<<"<">>]} -> ok; + {match,[<<">">>]} -> ok; + Else -> throw({found_illigal_thing,Else,String}) + end, + NewContent = + [unicode:characters_to_binary(NoHtmlEnt,utf8)| Content], + State#state{dom=[{Name, Attributes, NewContent} | D]}; + +build_dom({ignorableWhitespace, String}, + #state{dom=[{Name,_,_} = _E|_]} = State) -> + case lists:member(Name, + [p,pre,input,code,quote,warning, + note,dont,do,c,i,em,strong, + seealso,tag,item]) of + true -> +% io:format("Keep ign white: ~p ~p~n",[String, _E]), + build_dom({characters, String}, State); + false -> + State + end; + +build_dom({startEntity, SysId}, State) -> + io:format("startEntity:~p~n",[SysId]), + State; + +%% Default +%%---------------------------------------------------------------------- +build_dom(_E, State) -> + State. + +%%---------------------------------------------------------------------- +%% Function : parse_attributes(ElName, Attributes) -> Result +%% Parameters: +%% Result : +%% Description: +%%---------------------------------------------------------------------- +parse_attributes(ElName, Attributes) -> + parse_attributes(ElName, Attributes, 1, []). + +parse_attributes(_, [], _, Acc) -> + Acc; +parse_attributes(ElName, [{_Uri, _Prefix, LocalName, AttrValue} |As], N, Acc) -> + parse_attributes(ElName, As, N+1, [{list_to_atom(LocalName), AttrValue} |Acc]). + +docs(OTPXml, FromBEAM)-> + case xmerl_sax_parser:file(OTPXml, + [skip_external_dtd, + {event_fun,fun event/3}, + {event_state,initial_state()}]) of + {ok,Tree,_} -> + {ok, {Module, Chunks}} = beam_lib:chunks(FromBEAM,[exports,abstract_code]), + Dom = get_dom(Tree), + NewDom = transform(Dom,[]), + Chunk = to_chunk(NewDom, OTPXml, Module, proplists:get_value(abstract_code, Chunks)), + verify_chunk(Module,proplists:get_value(exports, Chunks), Chunk), + Chunk; + Else -> + {error,Else} + end. + +verify_chunk(M, Exports, #docs_v1{ docs = Docs } = Doc) -> + + %% Make sure that each documented function actually is exported + Exported = [begin + FA = {F,A}, + {M,F,A,lists:member(FA,Exports)} + end || {{function,F,A},_,_,_,_} <- Docs], + lists:foreach(fun({_M,_F,_A,true}) -> + ok + end,Exported), + + try + shell_docs:validate(Doc) + catch Err -> + throw({maps:get(<<"en">>,Doc#docs_v1.module_doc), Err}) + end. + +%% skip <erlref> but transform and keep its content +transform([{erlref,_Attr,Content}|T],Acc) -> + Module = [Mod || Mod = {module,_,_} <- Content], + NewContent = Content -- Module, + [{module,SinceAttr,[Mname]}] = Module, + Since = case proplists:get_value(since,SinceAttr) of + undefined -> []; + [] -> []; + Vsn -> [{since,Vsn}] + end, + transform([{module,[{name,Mname}|Since],NewContent}|T],Acc); + +%% skip <header> and all of its content +transform([{header,_Attr,_Content}|T],Acc) -> + transform(T,Acc); +transform([{section,Attr,Content}|T],Acc) -> + transform(T,[{section,Attr,transform(Content,[])}|Acc]); + +%% transform <list><item> to <ul><li> or <ol><li> depending on type attribute +transform([{list,Attr,Content}|T],Acc) -> + transform([transform_list(Attr,Content)|T],Acc); + +%% transform <taglist>(tag,item+)+ to <dl>(dt,item+)+ +transform([{taglist,Attr,Content}|T],Acc) -> + transform([transform_taglist(Attr,Content)|T],Acc); + +%% remove <anno> as it is only used to validate specs vs xml src +transform([{anno,[],Content}|T],Acc) -> + transform([Content|T],Acc); + +%% transform <c> to <code> +transform([{c,[],Content}|T],Acc) -> + transform(T, [{code,[],transform(Content,[])}|Acc]); + +%% transform <code> to <pre><code> +transform([{code,Attr,Content}|T],Acc) -> + transform(T, [{pre,[],[{code,Attr,transform(Content,[])}]}|Acc]); +%% transform <pre> to <pre><code> +transform([{pre,Attr,Content}|T],Acc) -> + transform(T, [{pre,[],[{code,Attr,transform(Content,[])}]}|Acc]); + +%% transform <funcs> with <func> as children +transform([{funcs,_Attr,Content}|T],Acc) -> + Fns = {functions,[],transform_funcs(Content, [])}, + transform(T,[Fns|Acc]); +%% transform <datatypes> with <datatype> as children +transform([{datatypes,_Attr,Content}|T],Acc) -> + Dts = transform(Content, []), + transform(T,[{datatypes,[],Dts}|Acc]); +transform([{datatype,_Attr,Content}|T],Acc) -> + transform(T,transform_datatype(Content, []) ++ Acc); +%% Ignore <datatype_title> +transform([{datatype_title,_Attr,_Content}|T],Acc) -> + transform(T,Acc); +%% transform <desc>Content</desc> to Content +transform([{desc,_Attr,Content}|T],Acc) -> + transform(T,[transform(Content,[])|Acc]); +transform([{strong,Attr,Content}|T],Acc) -> + transform([{em,Attr,Content}|T],Acc); +%% transform <marker id="name"/> to <a id="name"/>.... +transform([{marker,Attr,Content}|T],Acc) -> + transform(T,[{a,Attr,transform(Content,[])}|Acc]); +%% transform <url href="external URL"> Content</url> to <a href.... +transform([{url,Attr,Content}|T],Acc) -> + transform(T,[{a,Attr,transform(Content,[])}|Acc]); +%% transform note/warning/do/don't to <p class="thing"> +transform([{What,[],Content}|T],Acc) + when What =:= note; What =:= warning; What =:= do; What =:= dont -> + WhatP = {p,[{class,atom_to_list(What)}], transform(Content,[])}, + transform(T,[WhatP|Acc]); + +transform([{type,_,[]}|_] = Dom,Acc) -> + %% Types are laid out sequentially in the source xml so we need to + %% parse them like that here too. + case transform_types(Dom,[]) of + {[],T} -> + transform(T,Acc); + {Types,T} -> + %% We sort the types here because in the source xml + %% the description and the declaration do not have + %% to be next to each other. But we want to have that + %% for the doc chunks. + NameSort = fun({li,A,_},{li,B,_}) -> + NameA = proplists:get_value(name,A), + NameB = proplists:get_value(name,B), + if NameA == NameB -> + length(A) =< length(B); + true -> + NameA < NameB + end + end, + transform(T,[{ul,[{class,"types"}],lists:sort(NameSort,Types)}|Acc]) + end; +transform([{type_desc,Attr,_Content}|T],Acc) -> + %% We skip any type_desc with the variable attribute + true = proplists:is_defined(variable, Attr), + transform(T,Acc); +transform([{type,[],Content}|T],Acc) -> + transform(T,[{ul,[{class,"types"}],transform(Content,[])}|Acc]); +transform([{v,[],Content}|T],Acc) -> + transform(T, [{li,[{class,"type"}],transform(Content,[])}|Acc]); +transform([{d,[],Content}|T],Acc) -> + transform(T, [{li,[{class,"description"}],transform(Content,[])}|Acc]); + +transform([Tag = {seealso,_Attr,_Content}|T],Acc) -> + transform([transform_seealso(Tag)|T],Acc); + +transform([{term,Attr,[]}|T],Acc) -> + transform([list_to_binary(proplists:get_value(id,Attr))|T],Acc); + +transform([{fsummary,_,_}|T],Acc) -> + %% We skip fsummary as it many times is just a duplicate of the + %% first line of the docs. + transform(T,Acc); + +transform([{input,_,Content}|T],Acc) -> + %% Just remove input as it is not used by anything + transform(T,[transform(Content,[])|Acc]); + +%% Tag and Attr is used as is but Content is transformed +transform([{Tag,Attr,Content}|T],Acc) -> + transform(T,[{Tag,Attr,transform(Content,[])}|Acc]); +transform([Binary|T],Acc) -> + transform(T,[Binary|Acc]); +transform([],Acc) -> + lists:flatten(lists:reverse(Acc)). + +transform_list([{type,"ordered"}],Content) -> + {ol,[],[{li,A2,C2}||{item,A2,C2}<-Content]}; +transform_list(_,Content) -> + {ul,[],[{li,A2,C2}||{item,A2,C2}<-Content]}. + +transform_types([{type,Attr,[]}|T],Acc) -> + case proplists:is_defined(name,Attr) of + true -> + transform_types(T, [{li,Attr,[]}|Acc]); + false -> + true = proplists:is_defined(variable, Attr), + transform_types(T, Acc) + end; +transform_types([{type_desc,Attr,Content}|T],Acc) -> + case proplists:is_defined(name,Attr) of + true -> + TypeDesc = transform(Content,[]), + transform_types(T, [{li,Attr ++ [{class,"description"}],TypeDesc}|Acc]); + false -> + true = proplists:is_defined(variable, Attr), + transform_types(T, Acc) + end; +transform_types([{type,_,_}|_T],_Acc) -> + throw(mixed_type_declarations); +transform_types(Dom,Acc) -> + {lists:reverse(Acc),Dom}. + +transform_taglist(Attr,Content) -> + Items = + lists:map(fun({tag,A,C}) -> + {dt,A,C}; + ({item,A,C}) -> + {dd,A,C} + end, Content), + {dl,Attr,Items}. + +%% if we have {func,[],[{name,...},{name,....},...]} +%% we convert it to one {func,[],[{name,...}] per arity lowest first. +transform_funcs([Func|T],Acc) -> + transform_funcs(T,func2func(Func) ++ Acc); +transform_funcs([],Acc) -> + lists:reverse(Acc). + +func2func({func,Attr,Contents}) -> + + ContentsNoName = [NC||NC <- Contents, element(1,NC) /= name], + + EditLink = + case proplists:get_value(ghlink,Attr) of + undefined -> + #{}; + GhLink -> + #{ edit_url => + iolist_to_binary(["https://github.com/erlang/otp/edit/",GhLink]) } + end, + + VerifyNameList = + fun(NameList, Test) -> + %% Assert that we don't mix ways to write <name> + [begin + ok = Test(C), + {proplists:get_value(name,T),proplists:get_value(arity,T)} + end || {name,T,C} <- NameList] + end, + + NameList = [Name || {name,_,_} = Name <- Contents], + + %% "Since" is hard to accurately as there can be multiple <name> per <func> and they + %% can refer to the same or other arities. This should be improved in the future but + %% for now we set since to a comma separated list of all since attributes. + SinceMD = + case [proplists:get_value(since, SinceAttr) || + {name,SinceAttr,_} <- NameList, proplists:get_value(since, SinceAttr) =/= []] of + [] -> EditLink; + Sinces -> + EditLink#{ since => unicode:characters_to_binary( + lists:join(",",lists:usort(Sinces))) } + end, + + Functions = + case NameList of + [{name,_,[]}|_] -> + %% Spec style function docs + TagsToFA = + fun(Tags) -> + {proplists:get_value(name,Tags), + proplists:get_value(arity,Tags)} + end, + + _ = VerifyNameList(NameList,fun([]) -> ok end), + + FAs = [TagsToFA(FAttr) || {name,FAttr,[]} <- NameList ], + FAClauses = lists:usort([{TagsToFA(FAttr),proplists:get_value(clause_i,FAttr)} + || {name,FAttr,[]} <- NameList ]), + Signature = [iolist_to_binary([F,"/",A]) || {F,A} <- FAs], + lists:map( + fun({F,A}) -> + Specs = [{func_to_atom(CF),list_to_integer(CA),C} + || {{CF,CA},C} <- FAClauses, + F =:= CF, A =:= CA], + {function,[{name,F},{arity,list_to_integer(A)}, + {signature,Signature}, + {meta,SinceMD#{ signature => Specs }}], + ContentsNoName} + end, lists:usort(FAs)); + NameList -> + %% Manual style function docs + FAs = lists:flatten([func_to_tuple(NameString) || {name, _Attr, NameString} <- NameList]), + + _ = VerifyNameList(NameList,fun([_|_]) -> ok end), + + Signature = [strip_tags(NameString) || {name, _Attr, NameString} <- NameList], + [{function,[{name,F},{arity,A}, + {signature,Signature}, + {meta,SinceMD}],ContentsNoName} + || {F,A} <- lists:usort(FAs)] + end, + transform(Functions,[]). + +func_to_tuple(Chars) -> + try + [Name,Args] = string:split(strip_tags(Chars),"("), + Arities = parse_args(unicode:characters_to_list(Args)), + [{unicode:characters_to_list(Name),Arity} || Arity <- Arities] + catch E:R:ST -> + io:format("Failed to parse: ~p~n",[Chars]), + erlang:raise(E,R,ST) + end. + +%% This function parses a documentation <name> attribute to figure +%% out the arities if that function. Example: +%% "start([go,Mode] [,Extra])" returns [1, 2]. +%% +%% This assumes that when a single <name> describes many arities +%% the arities are listed with [, syntax. +parse_args(")" ++ _) -> + [0]; +parse_args(Args) -> + parse_args(unicode:characters_to_list(Args),1,[]). +parse_args([$[,$,|T],Arity,[]) -> + parse_args(T,Arity,[$[]) ++ parse_args(T,Arity+1,[]); +parse_args([$,|T],Arity,[]) -> + parse_args(T,Arity+1,[]); +parse_args([Open|T],Arity,Stack) + when Open =:= $[; Open =:= ${; Open =:= $( -> + parse_args(T,Arity,[Open|Stack]); +parse_args([$]|T],Arity,[$[|Stack]) -> + parse_args(T,Arity,Stack); +parse_args([$}|T],Arity,[${|Stack]) -> + parse_args(T,Arity,Stack); +parse_args([$)|T],Arity,[$(|Stack]) -> + parse_args(T,Arity,Stack); +parse_args([$)|_T],Arity,[]) -> + [Arity]; +parse_args([_H|T],Arity,Stack) -> + parse_args(T,Arity,Stack). + +strip_tags([{_Tag,_Attr,Content}|T]) -> + [Content | strip_tags(T)]; +strip_tags([H|T]) when not is_tuple(H) -> + [H | strip_tags(T)]; +strip_tags([]) -> + []. + +transform_datatype(Dom,_Acc) -> + ContentsNoName = transform([NC||NC <- Dom, element(1,NC) /= name],[]), + [case N of + {name,NameAttr,[]} -> + {datatype,NameAttr,ContentsNoName}; + {name,[],Content} -> + [{Name,Arity}] = func_to_tuple(Content), + Signature = strip_tags(Content), + {datatype,[{name,Name},{n_vars,integer_to_list(Arity)}, + {signature,Signature}],ContentsNoName} + end || N = {name,_,_} <- Dom]. + +transform_seealso({seealso,Attr,_Content}) -> + {a, Attr, _Content}. + +to_chunk(Dom, Source, Module, AST) -> + [{module,MAttr,Mcontent}] = Dom, + + ModuleDocs = lists:flatmap( + fun({Tag,_,Content}) when Tag =:= description; + Tag =:= section -> + Content; + ({_,_,_}) -> + [] + end, Mcontent), + + TypeMeta = add_types(AST, maps:from_list([{source,Source}|MAttr])), + + TypeMap = maps:get(types, TypeMeta, []), + + Anno = erl_anno:set_file(atom_to_list(Module)++".erl",erl_anno:new(0)), + + Types = lists:flatten([Types || {datatypes,[],Types} <- Mcontent]), + + TypeEntries = + lists:map( + fun({datatype,Attr,Descr}) -> + TypeName = func_to_atom(proplists:get_value(name,Attr)), + TypeArity = case proplists:get_value(n_vars,Attr) of + undefined -> + find_type_arity(TypeName, TypeMap); + Arity -> + list_to_integer(Arity) + end, + TypeArgs = lists:join(",",[lists:concat(["Arg",I]) || I <- lists:seq(1,TypeArity)]), + PlaceholderSig = io_lib:format("-type ~p(~s) :: term().",[TypeName,TypeArgs]), + TypeSignature = proplists:get_value( + signature,Attr,[iolist_to_binary(PlaceholderSig)]), + MetaSig = + case maps:get({TypeName, TypeArity}, TypeMap, undefined) of + undefined -> + #{}; + Sig -> + #{ signature => [Sig] } + end, + docs_v1_entry(type, Anno, TypeName, TypeArity, TypeSignature, MetaSig, Descr) + end, Types), + + Functions = lists:flatten([Functions || {functions,[],Functions} <- Mcontent]), + + FuncEntrys = + lists:flatmap( + fun({function,Attr,Fdoc}) -> + case func_to_atom(proplists:get_value(name,Attr)) of + callback -> + []; + Name -> + Arity = proplists:get_value(arity,Attr), + Signature = proplists:get_value(signature,Attr), + FMeta = proplists:get_value(meta,Attr), + MetaWSpec = add_spec(AST,FMeta), + [docs_v1_entry(function, Anno, Name, Arity, Signature, MetaWSpec, Fdoc)] + end + end, Functions), + + docs_v1(ModuleDocs, Anno, TypeMeta, FuncEntrys ++ TypeEntries). + +docs_v1(DocContents, Anno, Metadata, Docs) -> + #docs_v1{ anno = Anno, + module_doc = #{<<"en">> => shell_docs:normalize(DocContents)}, + metadata = maps:merge(Metadata, (#docs_v1{})#docs_v1.metadata), + docs = Docs }. + +docs_v1_entry(Kind, Anno, Name, Arity, Signature, Metadata, DocContents) -> + AnnoWLine = + case Metadata of + #{ signature := [Sig|_] } -> + erl_anno:set_line(element(2, Sig), Anno); + _NoSignature -> + Anno + end, + {{Kind, Name, Arity}, AnnoWLine, lists:flatten(Signature), + #{ <<"en">> => shell_docs:normalize(DocContents)}, Metadata}. + +%% A special list_to_atom that handles +%% 'and' +%% Destroy +%% 'begin' +func_to_atom(List) -> + case erl_scan:string(List) of + {ok,[{atom,_,Fn}],_} -> Fn; + {ok,[{var,_,Fn}],_} -> Fn; + {ok,[{Fn,_}],_} -> Fn; + {ok,[{var,_,_},{':',_},_],_} -> + callback + end. + +-define(IS_TYPE(TO),(TO =:= type orelse TO =:= opaque)). + +add_spec(no_abstract_code, Meta) -> + Meta; +add_spec({raw_abstract_v1, AST}, Meta = #{ signature := Specs } ) -> + Meta#{ signature := add_spec_clauses(AST, merge_clauses(Specs,#{})) }; +add_spec(_, Meta) -> + Meta. + +add_types(no_abstract_code, Meta) -> + Meta; +add_types({raw_abstract_v1, AST}, Meta) -> + Meta#{ types => + maps:from_list( + [{{Name,length(Args)},T} || T = {attribute,_,TO,{Name, _, Args}} <- AST, + ?IS_TYPE(TO)]) }. + +add_spec_clauses(AST, [{{F,A},Clauses}|T]) -> + [filter_clauses(find_spec(AST,F,A),Clauses) | add_spec_clauses(AST,T)]; +add_spec_clauses(_AST, []) -> + []. + +filter_clauses(Spec,[undefined]) -> + Spec; +filter_clauses({attribute,Ln,spec,{FA,Clauses}},ClauseIds) -> + {_,FilteredClauses} = + lists:foldl( + fun({TO,_,_,_} = C,{Cnt,Acc}) when ?IS_TYPE(TO) -> + case lists:member(integer_to_list(Cnt),ClauseIds) of + true -> + {Cnt+1,[C | Acc]}; + false -> + {Cnt+1,Acc} + end + end, {1, []}, Clauses), + {attribute,Ln,spec,{FA,lists:reverse(FilteredClauses)}}. + +merge_clauses([{F,A,Clause}|T],Acc) -> + merge_clauses(T,Acc#{ {F,A} => [Clause | maps:get({F,A},Acc,[])]}); +merge_clauses([],Acc) -> + maps:to_list(Acc). + +find_type_arity(Name, [{{Name,_},{attribute,_,TO,{Name,_,Args}}}|_T]) when ?IS_TYPE(TO) -> + length(Args); +find_type_arity(Name, [_|T]) -> + find_type_arity(Name,T); +find_type_arity(Name, Map) when is_map(Map) -> + find_type_arity(Name, maps:to_list(Map)). + +find_spec(AST, Func, Arity) -> + Specs = lists:filter(fun({attribute,_,spec,{{F,A},_}}) -> + F =:= Func andalso A =:= Arity; + ({attribute,_,spec,{{_,F,A},_}}) -> + F =:= Func andalso A =:= Arity; + (_) -> + false + end, AST), + case Specs of + [S] -> + S; + [] -> + io:format("Could not find spec for ~p/~p~n",[Func,Arity]), + exit(1) + end. diff --git a/lib/erl_docgen/src/erl_docgen.app.src b/lib/erl_docgen/src/erl_docgen.app.src index 171c697585..c2641f30df 100644 --- a/lib/erl_docgen/src/erl_docgen.app.src +++ b/lib/erl_docgen/src/erl_docgen.app.src @@ -3,7 +3,8 @@ {vsn, "%VSN%"}, {modules, [docgen_otp_specs, docgen_edoc_xml_cb, - docgen_xmerl_xml_cb + docgen_xmerl_xml_cb, + docgen_xml_to_chunk ] }, {registered,[]}, |