summaryrefslogtreecommitdiff
path: root/lib/erl_docgen
diff options
context:
space:
mode:
Diffstat (limited to 'lib/erl_docgen')
-rw-r--r--lib/erl_docgen/Makefile2
-rw-r--r--lib/erl_docgen/doc/src/Makefile78
-rw-r--r--lib/erl_docgen/doc/src/block_tags.xml16
-rw-r--r--lib/erl_docgen/doc/src/doc_storage.xml61
-rw-r--r--lib/erl_docgen/doc/src/inline_tags.xml40
-rw-r--r--lib/erl_docgen/doc/src/part.xml1
-rw-r--r--lib/erl_docgen/priv/bin/chunk.escript30
-rw-r--r--lib/erl_docgen/priv/bin/specs_gen.escript5
-rw-r--r--lib/erl_docgen/priv/css/Makefile9
-rw-r--r--lib/erl_docgen/priv/dtd/Makefile7
-rw-r--r--lib/erl_docgen/priv/dtd/bookinsidecover.dtd37
-rw-r--r--lib/erl_docgen/priv/dtd/cites.dtd36
-rw-r--r--lib/erl_docgen/priv/dtd/common.dtd31
-rw-r--r--lib/erl_docgen/priv/dtd/fascicules.dtd36
-rw-r--r--lib/erl_docgen/priv/dtd/report.dtd141
-rw-r--r--lib/erl_docgen/priv/dtd/terms.dtd37
-rw-r--r--lib/erl_docgen/priv/images/Makefile9
-rw-r--r--lib/erl_docgen/priv/js/flipmenu/Makefile10
-rw-r--r--lib/erl_docgen/priv/xsl/db_html.xsl136
-rw-r--r--lib/erl_docgen/priv/xsl/db_pdf.xsl2
-rw-r--r--lib/erl_docgen/src/Makefile3
-rw-r--r--lib/erl_docgen/src/docgen_edoc_xml_cb.erl2
-rw-r--r--lib/erl_docgen/src/docgen_xml_to_chunk.erl757
-rw-r--r--lib/erl_docgen/src/erl_docgen.app.src3
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">&lt;list&gt;</seealso>,
<seealso marker="#taglistTAG">&lt;taglist&gt;</seealso>,
<seealso marker="#codeincludeTAG">&lt;codeinclude&gt;</seealso> and
- <seealso marker="#erlevalTAG">&lt;erleval&gt;</seealso>.
</p>
<section>
@@ -131,21 +130,6 @@ start(Pid) ->
</section>
<section>
- <marker id="erlevalTAG"></marker>
- <title>&lt;erleval&gt; - 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>&lt;list&gt; - 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>&lt;name&gt;</c> tags must be parseable to figure out the arity of the function.</item>
+ <item>It is not allowed to mix <c>&lt;name&gt;</c> tags with #PCDATA and attributes.</item>
+ <item>All <c>&lt;name&gt;</c> tags within <c>&lt;func&gt;</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>&lt;term&gt;, &lt;termdef&gt; - 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>&lt;termdef&gt;</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>&lt;cite&gt;, &lt;citedef&gt; - Bibliography</title>
-
- <p>Works the same way as <c>&lt;term&gt;</c> and
- <c>&lt;termdef&gt;</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 "&#x0026;" >
-<!ENTITY gt "&#x003E;" >
-<!ENTITY lt "&#x003C;" >
-
-<!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 "&#x0026;" >
-<!ENTITY gt "&#x003E;" >
-<!ENTITY lt "&#x003C;" >
-
-<!-- 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 "&#x0026;" >
-<!ENTITY gt "&#x003E;" >
-<!ENTITY lt "&#x003C;" >
-
-<!-- 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 "&#x0026;" >
-<!ENTITY gt "&#x003E;" >
-<!ENTITY lt "&#x003C;" >
-
-<!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 "&#x0026;" >
-<!ENTITY gt "&#x003E;" >
-<!ENTITY lt "&#x003C;" >
-
-<!-- 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 = [{"&nbsp;",[160]},
+ {"&times;",[215]},
+ {"&plusmn;",[177]},
+ {"&ouml;","ö"},
+ {"&auml;","ä"},
+ {"&aring;","å"}
+ ],
+
+ 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,[<<"&lt;">>]} -> ok;
+ {match,[<<"&gt;">>]} -> 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,[]},