3737# Note that an "API" can be anything. This extension only cares that the text
3838# in the "[apidef]" role matches the text in the "[api]" role.
3939#
40+ # Sometimes you want an API name to link to something that is not an [apidef]
41+ # title. In this case, you can define the Asciidoc attribute "api-xrefs", which
42+ # has a syntax like:
43+ #
44+ # :api-xrefs: API1=ID1 API2=ID2 ...
45+ #
46+ # The IDs (ID1, ID2, etc.) must be defined someplace in the Asciidoc document.
47+ # Each of these IDs is used as the target for the associated API (API1, API2,
48+ # etc.)
49+ #
4050# Typically, projects using this extension also provide some custom CSS styling
4151# for the "apidef" and "api" HTML classes.
4252
4353class AddApiXrefs < Extensions ::Postprocessor
4454 include Asciidoctor ::Logging
4555
56+ Id = /<\w + id="([\w :-]*)"/
4657 ApiSpan = /<span class="api">([\w :]*)<\/ span>/
4758 ApiDefSpan = /<span class="apidef">([\w :]*)<\/ span>/
4859 ApiIdDiv = /<div id="([\w :-]*)"/
4960
5061 def process document , output
5162
5263 if document . basebackend? 'html'
64+ # Get all of the IDs in the HTML document, so we can do error checking
65+ # below.
66+ all_ids = output . scan ( Id ) . to_h { |m | [ m . first , true ] }
67+
68+ # Parse the api-xrefs attribute. Make sure each ID is defined someplace
69+ # in the document. Populate "api_id_array" with this information.
70+ api_id_array = [ ]
71+ if document . attr? 'api-xrefs'
72+ ( document . attr 'api-xrefs' ) . scan ( /([\w :-]*)=([\w :-]*)/ ) do |api , id |
73+ if not all_ids . key? ( id )
74+ logger . error "Id '#{ id } ' from api-xrefs is not defined"
75+ else
76+ api_id_array . push ( [ api , id ] )
77+ end
78+ end
79+ end
80+
5381 # Scan through all the HTML lines looking for the "[apidef]" definitions.
5482 # A typical definition looks like this:
5583 #
@@ -58,16 +86,19 @@ def process document, output
5886 # <div class="title"><span class="apidef">NAME</span></div>
5987 #
6088 # where the <a> element above may or may not be present, depending on
61- # whether the listing block also uses the "synopsis" extension.
89+ # whether the listing block also uses the "synopsis" extension. Add
90+ # these also to "api_id_array".
6291 #
63- api_id_array = output . lines . each_cons ( 3 ) . filter_map do |prev2 , prev1 , cur |
92+ output . lines . each_cons ( 3 ) do |prev2 , prev1 , cur |
6493 api = cur . scan ( ApiDefSpan ) . first
6594 api_id = prev1 . scan ( ApiIdDiv ) . first || prev2 . scan ( ApiIdDiv ) . first
66- [ api . first , api_id . first ] if api && api_id
95+ if api && api_id
96+ api_id_array . push ( [ api . first , api_id . first ] )
97+ end
6798 end
6899
69- # Diagnose duplicate id definitions, and create a hash mapping each API
70- # name to its ID.
100+ # Diagnose duplicate apidef definitions, and create a hash mapping each
101+ # API name to its ID.
71102 api_to_id = { }
72103 api_id_array . map do |api , api_id |
73104 if api_to_id . key? ( api )
0 commit comments