I'm trying to help out with linux documentation. Right now, that uses a 
huge toolchain involving custom scripts and docbook. We'd like to use 
asciidoc, but in its current form, you either get to retain a dependency on 
docbook, along with all that entails, or the resulting html requires 
javascript to add a table of contents and other similar navigational aids.

Here's a set of patches which provide a general mechanism to divert output 
from a conf file section into an attribute, and then to insert that 
attribute into any location of the document.

-- 
You received this message because you are subscribed to the Google Groups 
"asciidoc" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/asciidoc.
For more options, visit https://groups.google.com/d/optout.
>From 66508ea3a243c01f1672b7c1b95e2795508ab769 Mon Sep 17 00:00:00 2001
From: Keith Packard <[email protected]>
Date: Fri, 12 Feb 2016 18:39:54 -0800
Subject: [PATCH 3/5] Add | operator to attribute processing.

The | operator operates like the = operator except that the
substitution is delayed until after the entire document has been
processed once so that the replacement value will contain the final
value of the attribute rather than the current value.

Signed-off-by: Keith Packard <[email protected]>
---
 asciidoc.py      | 22 ++++++++++++++++++----
 doc/asciidoc.txt |  5 +++++
 2 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/asciidoc.py b/asciidoc.py
index d1a8929..2c85e36 100755
--- a/asciidoc.py
+++ b/asciidoc.py
@@ -1002,7 +1002,7 @@ def system(name, args, is_macro=False, attrs=None):
         result = '\x07' + str(len(macros.passthroughs)-1) + '\x07'
     return result
 
-def subs_attrs(lines, dictionary=None):
+def subs_attrs(lines, dictionary=None, leavedivert=True):
     """Substitute 'lines' of text with attributes from the global
     document.attributes dictionary and from 'dictionary' ('dictionary'
     entries take precedence). Return a tuple of the substituted lines.  'lines'
@@ -1077,11 +1077,11 @@ def subs_attrs(lines, dictionary=None):
         # Expand conditional attributes.
         # Single name -- higher precedence.
         reo1 = re.compile(r'(?su)\{(?P<name>[^\\\W][-\w]*?)' \
-                          r'(?P<op>\=|\?|!|#|%|@|\$)' \
+                          r'(?P<op>\=|\?|!|#|%|@|\$|\|)' \
                           r'(?P<value>.*?)\}(?!\\)')
         # Multiple names (n1,n2,... or n1+n2+...) -- lower precedence.
         reo2 = re.compile(r'(?su)\{(?P<name>[^\\\W][-\w'+OR+AND+r']*?)' \
-                          r'(?P<op>\=|\?|!|#|%|@|\$)' \
+                          r'(?P<op>\=|\?|!|#|%|@|\$|\|)' \
                           r'(?P<value>.*?)\}(?!\\)')
         for reo in [reo1,reo2]:
             pos = 0
@@ -1128,6 +1128,11 @@ def subs_attrs(lines, dictionary=None):
                     elif op == '!': s = rval
                     elif op == '#': s = UNDEFINED   # So the line is dropped.
                     elif op == '%': s = rval
+                    elif op == '|':
+                        if leavedivert:
+                            s = '{' + name + '|}'
+                        else:
+                            s = rval
                     elif op in ('@','$'):
                         s = UNDEFINED               # So the line is dropped.
                     else:
@@ -1138,6 +1143,11 @@ def subs_attrs(lines, dictionary=None):
                     elif op == '!': s = ''
                     elif op == '#': s = rval
                     elif op == '%': s = UNDEFINED   # So the line is dropped.
+                    elif op == '|':
+                        if leavedivert:
+                            s = '{' + name + '|}'
+                        else:
+                            s = lval
                     elif op in ('@','$'):
                         v = re.split(r'(?<!\\):',rval)
                         if len(v) not in (2,3):
@@ -4420,6 +4430,7 @@ class Writer:
         self.fname = None                # Output file name.
         self.lines_out = 0               # Number of lines written.
         self.skip_blank_lines = False    # If True don't output blank lines.
+        self.lines = []
     def open(self,fname,bom=None):
         '''
         bom is optional byte order mark.
@@ -4436,10 +4447,13 @@ class Writer:
         self.lines_out = 0
     def close(self):
         if self.fname != '<stdout>':
+            lines = subs_attrs(self.lines, leavedivert=False)
+            for line in lines:
+                self.f.write((line or '') + self.newline)
             self.f.close()
     def write_line(self, line=None):
         if not (self.skip_blank_lines and (not line or not line.strip())):
-            self.f.write((line or '') + self.newline)
+            self.lines.append(line)
             self.lines_out = self.lines_out + 1
     def write(self,*args,**kwargs):
         """Iterates arguments, writes tuple and list arguments one line per
diff --git a/doc/asciidoc.txt b/doc/asciidoc.txt
index fd33c61..9cfe818 100644
--- a/doc/asciidoc.txt
+++ b/doc/asciidoc.txt
@@ -4443,6 +4443,11 @@ the following forms:
         defined otherwise the containing line is dropped.  `<value>`
         can contain simple attribute references.
 
+`{<names>|<value>}`::
+	This works exactly like `=`, except that substitution is
+	delayed until the entire document has been processed to
+	capture the final value rather than the current value.
+	
 `{<names>@<regexp>:<value1>[:<value2>]}`::
         `<value1>` is substituted if the value of attribute `<names>`
         matches the regular expression `<regexp>` otherwise `<value2>`
-- 
2.7.0

>From 8cee7b12b74983ce14fd14fecd47c1bdb07a5603 Mon Sep 17 00:00:00 2001
From: Keith Packard <[email protected]>
Date: Fri, 12 Feb 2016 18:42:05 -0800
Subject: [PATCH 4/5] Add diversions to conf sections

Diversions append text in the conf file section to an attribute rather
than emitting text directly. This can be used to generate a table of
contents or list of figures.

Signed-off-by: Keith Packard <[email protected]>
---
 asciidoc.py | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 81 insertions(+), 10 deletions(-)

diff --git a/asciidoc.py b/asciidoc.py
index 2c85e36..68871b7 100755
--- a/asciidoc.py
+++ b/asciidoc.py
@@ -1575,6 +1575,17 @@ class Document(object):
                     finished = False
                     AttributeList.translate()
         return result
+    def divert(self,dname,content):
+        """
+        Append 'content' to attribute 'dname'
+        """
+        value = ""
+        for s in content:
+            value = value + s + '\n'
+        if self.attributes.get(dname) is None:
+            self.attributes[dname] = value
+        else:
+            self.attributes[dname] += value
     def parse_header(self,doctype,backend):
         """
         Parses header, sets corresponding document attributes and finalizes
@@ -1656,10 +1667,14 @@ class Document(object):
                 # Translate 'preamble' (untitled elements between header
                 # and first section title).
                 if Lex.next() is not Title:
-                    stag,etag = config.section2tags('preamble')
+                    stag,etag,dtag = config.section2tags('preamble')
+                    if dtag:
+                        document.divert(dtag[0], dtag[1])
                     writer.write(stag,trace='preamble open')
                     Section.translate_body()
                     writer.write(etag,trace='preamble close')
+                    if dtag:
+                        document.divert(dtag[0], dtag[2])
             elif self.doctype == 'manpage' and 'name' in config.sections:
                 writer.write(config.subs_section('name',{}), trace='name')
         else:
@@ -2217,7 +2232,7 @@ class FloatingTitle(Title):
         AttributeList.consume(Title.attributes)
         template = 'floatingtitle'
         if template in config.sections:
-            stag,etag = config.section2tags(template,Title.attributes)
+            stag,etag,dtag = config.section2tags(template,Title.attributes)
             writer.write(stag,trace='floating title')
         else:
             message.warning('missing template section: [%s]' % template)
@@ -2226,6 +2241,7 @@ class FloatingTitle(Title):
 class Section:
     """Static methods and attributes only."""
     endtags = []  # Stack of currently open section (level,endtag) tuples.
+    enddiv = []   # Stack of currently open section (level,diversion) tuples.
     ids = []      # List of already used ids.
     def __init__(self):
         raise AssertionError,'no class instances allowed'
@@ -2234,10 +2250,17 @@ class Section:
         """Save section end."""
         Section.endtags.append((level,etag))
     @staticmethod
+    def savediv(level,dtag):
+        """Save section end."""
+        Section.enddiv.append((level,dtag))
+    @staticmethod
     def setlevel(level):
         """Set document level and write open section close tags up to level."""
         while Section.endtags and Section.endtags[-1][0] >= level:
             writer.write(Section.endtags.pop()[1],trace='section close')
+        while Section.enddiv and Section.enddiv[-1][0] >= level:
+            dtag = Section.enddiv.pop()[1]
+            document.divert(dtag[0], dtag[2])
         document.level = level
     @staticmethod
     def gen_id(title):
@@ -2310,8 +2333,11 @@ class Section:
         else:
             Title.attributes['sectnum'] = ''
         AttributeList.consume(Title.attributes)
-        stag,etag = config.section2tags(Title.sectname,Title.attributes)
+        stag,etag,dtag = config.section2tags(Title.sectname,Title.attributes)
         Section.savetag(Title.level,etag)
+        if dtag:
+            document.divert(dtag[0], dtag[1])
+            Section.savediv(Title.level,dtag)
         writer.write(stag,trace='section open: level %d: %s' %
                 (Title.level, Title.attributes['title']))
         Section.translate_body()
@@ -3079,10 +3105,14 @@ class DelimitedBlock(AbstractBlock):
             name = self.short_name()+' block'
             if 'sectionbody' in options:
                 # The body is treated like a section body.
-                stag,etag = config.section2tags(template,self.attributes)
+                stag,etag,dtag = config.section2tags(template,self.attributes)
+                if dtag:
+                    document.divert(dtag[0], dtag[1])
                 writer.write(stag,trace=name+' open')
                 Section.translate_body(self)
                 writer.write(etag,trace=name+' close')
+                if dtag:
+                    document.divert(dtag[0], dtag[2])
             else:
                 stag = config.section2tags(template,self.attributes,skipend=True)[0]
                 body = reader.read_until(self.delimiter,same_file=True)
@@ -5124,23 +5154,57 @@ class Config:
         for k,v in self.sections.items():
             self.sections[k] = self.expand_templates(v)
 
-    def section2tags(self, section, d={}, skipstart=False, skipend=False):
+    def section2tags(self, section, d={}, skipstart=False, skipend=False, skipdiv=False):
         """Perform attribute substitution on 'section' using document
-        attributes plus 'd' attributes. Return tuple (stag,etag) containing
-        pre and post | placeholder tags. 'skipstart' and 'skipend' are
-        used to suppress substitution."""
+        attributes plus 'd' attributes. Return tuple (stag,etag,dtag)
+        containing pre and post | placeholder tags and any preceding
+        diversion. 'skipstart', 'skipend' and 'skipdiv' are used to
+        suppress substitution."""
         assert section is not None
         if section in self.sections:
             body = self.sections[section]
         else:
             message.warning('missing section: [%s]' % section)
             body = ()
-        # Split macro body into start and end tag lists.
+        # Split macro body into start, end and diversion tag lists.
+        dstag = []
+        detag = []
+        dname = []
         stag = []
         etag = []
+        in_dstag = False
+        in_detag = False
         in_stag = True
         for s in body:
             if in_stag:
+                mo = re.match(r'\|\[(?P<dname>[^\]]*)\](?P<dstag>.*)', s)
+                if mo:
+                    if mo.group('dname'):
+                        dname = mo.group('dname')
+                    else:
+                        dname = 'diversion'
+                    if mo.group('dstag'):
+                        dstag = mo.group('dstag')
+                    in_dstag = True
+                    continue
+            if in_dstag:
+                mo = re.match(r'\|(?P<detag>.*)', s)
+                if mo:
+                    in_dstag = False
+                    in_detag = True
+                    if mo.group('detag'):
+                        detag = mo.group('detag')
+                else:
+                    dstag.append(s)
+                continue
+            if in_detag:
+                mo = re.match(r'\|', s)
+                if mo:
+                    in_detag = False
+                else:
+                    detag.append(s)
+                continue
+            if in_stag:
                 mo = re.match(r'(?P<stag>.*)\|(?P<etag>.*)',s)
                 if mo:
                     if mo.group('stag'):
@@ -5161,12 +5225,19 @@ class Config:
             stag = subs_attrs(stag, d)
         if not skipend:
             etag = subs_attrs(etag, d)
+        if not skipdiv:
+            dstag = subs_attrs(dstag, d)
+            detag = subs_attrs(detag, d)
         # Put the {title} back.
         if title:
             stag = map(lambda x: x.replace(chr(0), title), stag)
             etag = map(lambda x: x.replace(chr(0), title), etag)
+            dstag = map(lambda x: x.replace(chr(0), title), dstag)
+            detag = map(lambda x: x.replace(chr(0), title), detag)
             d['title'] = title
-        return (stag,etag)
+        if dname:
+            dstag = (dname, dstag, detag)
+        return (stag,etag,dstag)
 
 
 #---------------------------------------------------------------------------
-- 
2.7.0

>From 11bae358340b9c4c534a67eeb4d2e5b6e4de5dea Mon Sep 17 00:00:00 2001
From: Keith Packard <[email protected]>
Date: Fri, 12 Feb 2016 19:07:14 -0800
Subject: [PATCH 5/5] xhtml11: Use new 'diversions' to generate table of
 contents

This produces a table of contents at the top of the resulting HTML
file without requiring javascript.

Signed-off-by: Keith Packard <[email protected]>
---
 xhtml11.conf | 50 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 48 insertions(+), 2 deletions(-)

diff --git a/xhtml11.conf b/xhtml11.conf
index 87ab469..6bd765e 100644
--- a/xhtml11.conf
+++ b/xhtml11.conf
@@ -485,6 +485,14 @@ cellspacing="0" cellpadding="4">
 |
 
 [sect1]
+|[tocdivert]
+<dl class="toc">
+<dt><span class="section"><a href="#{id}">{numbered?{sectnum} }{title}</a></span></dt>
+<dd>
+|
+</dd>
+</dl>
+|
 <div class="sect1{style? {style}}{role? {role}}">
 <h2{id? id="{id}"}>{numbered?{sectnum} }{title}</h2>
 <div class="sectionbody">
@@ -493,26 +501,64 @@ cellspacing="0" cellpadding="4">
 </div>
 
 [sect2]
+ifeval::[{toclevels}>=2]
+|[tocdivert]
+<dl class="toc">
+<dt><span class="section"><a href="#{id}">{numbered?{sectnum} }{title}</a></span></dt>
+<dd>
+|
+</dd>
+</dl>
+|
+endif::[]
 <div class="sect2{style? {style}}{role? {role}}">
 <h3{id? id="{id}"}>{numbered?{sectnum} }{title}</h3>
 |
 </div>
 
 [sect3]
+ifeval::[{toclevels}>=3]
+|[tocdivert]
+<dl class="toc">
+<dt><span class="section"><a href="#{id}">{numbered?{sectnum} }{title}</a></span></dt>
+<dd>
+|
+</dd>
+</dl>
+|
+endif::[]
 <div class="sect3{style? {style}}{role? {role}}">
 <h4{id? id="{id}"}>{numbered?{sectnum} }{title}</h4>
 |
 </div>
 
 [sect4]
+ifeval::[{toclevels}>=4]
+|[tocdivert]
+<dl class="toc">
+<dt><span class="section"><a href="#{id}">{title}</a></span></dt>
+<dd>
+|
+</dd>
+</dl>
+|
+endif::[]
 <div class="sect4{style? {style}}{role? {role}}">
 <h5{id? id="{id}"}>{title}</h5>
 |
 </div>
 
 [appendix]
+|[tocdivert]
+<dl class="toc">
+<dt><span class="appendix"><a href="#{id}">{appendix-caption} {appendix-number}: {title}</a></span></dt>
+<dd>
+|
+</dd>
+</dl>
+|
 <div class="sect1{style? {style}}{role? {role}}">
-<h2{id? id="{id}"}>{numbered?{sectnum} }{appendix-caption} {counter:appendix-number:A}: {title}</h2>
+<h2{id? id="{id}"}>{appendix-caption} {counter:appendix-number:A}: {title}</h2>
 <div class="sectionbody">
 |
 </div>
@@ -521,7 +567,7 @@ cellspacing="0" cellpadding="4">
 [toc]
 <div id="toc">
   <div id="toctitle">{toc-title}</div>
-  <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript>
+	{tocdivert|}
 </div>
 
 [header]
-- 
2.7.0

Reply via email to