Some prelim work on builder api.
[librarian.git] / src / librarian / elements / base.py
index 863436c..1f22929 100644 (file)
@@ -40,16 +40,16 @@ class WLElement(etree.ElementBase):
    
     CAN_HAVE_TEXT = True
     STRIP = False
    
     CAN_HAVE_TEXT = True
     STRIP = False
+    NUMBERING = None
 
     text_substitutions = [
         ('---', '—'),
         ('--', '–'),
 
     text_substitutions = [
         ('---', '—'),
         ('--', '–'),
-        #('...', '…'),  # Temporary turnoff for epub
+        ('...', '…'),
         (',,', '„'),
         ('"', '”'),
         ('\ufeff', ''),
         (',,', '„'),
         ('"', '”'),
         ('\ufeff', ''),
-
-        ("'", "\u2019"),    # This was enabled for epub.
+        ("'", "\u2019"),
     ]
 
     @property
     ]
 
     @property
@@ -85,6 +85,15 @@ class WLElement(etree.ElementBase):
         except AttributeError:
             return parent.in_context_of(setting)
 
         except AttributeError:
             return parent.in_context_of(setting)
 
+    def get_context_map(self, setting, key, default=None):
+        parent = self.getparent()
+        if parent is None:
+            return default
+        try:
+            return getattr(parent, setting)[key]
+        except AttributeError:
+            return parent.get_context_map(setting, key, default)
+
     def signal(self, signal):
         parent = self.getparent()
         if parent is not None:
     def signal(self, signal):
         parent = self.getparent()
         if parent is not None:
@@ -108,9 +117,9 @@ class WLElement(etree.ElementBase):
         text = text or ''
         for e, s in self.text_substitutions:
             text = text.replace(e, s)
         text = text or ''
         for e, s in self.text_substitutions:
             text = text.replace(e, s)
-            # FIXME: TEmporary turnoff
-#        text = re.sub(r'\s+', ' ', text)
-### TODO: Added now for epub
+
+        if getattr(builder, 'normalize_whitespace', False):
+            text = re.sub(r'\s+', ' ', text)
 
         if getattr(builder, 'hyphenator', None) is not None:
             newt = ''
 
         if getattr(builder, 'hyphenator', None) is not None:
             newt = ''
@@ -124,7 +133,8 @@ class WLElement(etree.ElementBase):
 
         return text
 
 
         return text
 
-    def _build_inner(self, builder, build_method):
+    def build_inner(self, builder):
+        build_method = builder.build_method_fn
         child_count = len(self)
         if self.CAN_HAVE_TEXT and self.text:
             text = self.normalize_text(self.text, builder)
         child_count = len(self)
         if self.CAN_HAVE_TEXT and self.text:
             text = self.normalize_text(self.text, builder)
@@ -134,57 +144,73 @@ class WLElement(etree.ElementBase):
                     text = text.rstrip()
             builder.push_text(text)
         for i, child in enumerate(self):
                     text = text.rstrip()
             builder.push_text(text)
         for i, child in enumerate(self):
+            real_child_count = 0
             if isinstance(child, WLElement):
                 getattr(child, build_method)(builder)
             if isinstance(child, WLElement):
                 getattr(child, build_method)(builder)
+                self.after_child(builder, real_child_count)
+                real_child_count += 1
+
+            # FIXME base builder api
+            elif getattr(builder, 'debug', False) and child.tag is etree.Comment:
+                builder.process_comment(child)
             if self.CAN_HAVE_TEXT and child.tail:
                 text = self.normalize_text(child.tail, builder)
                 if self.STRIP and i == child_count - 1:
                     text = text.rstrip()
                 builder.push_text(text)
 
             if self.CAN_HAVE_TEXT and child.tail:
                 text = self.normalize_text(child.tail, builder)
                 if self.STRIP and i == child_count - 1:
                     text = text.rstrip()
                 builder.push_text(text)
 
-    def _txt_build_inner(self, builder):
-        self._build_inner(builder, 'txt_build')
+    def after_child(self, builder, child_count):
+        fn = getattr(builder, 'after_child_fn', None)
+        if fn:
+            getattr(self, builder.after_child_fn)(builder, child_count)
+
+    def txt_after_child(self, builder, child_count):
+        pass
+
+    def txt_build_inner(self, builder):
+        self.build_inner(builder)
 
     def txt_build(self, builder):
 
     def txt_build(self, builder):
-        if hasattr(self, 'TXT_LEGACY_TOP_MARGIN'):
-            builder.push_legacy_margin(self.TXT_LEGACY_TOP_MARGIN)
-        else:
-            builder.push_margin(self.TXT_TOP_MARGIN)
+        builder.push_margin(self.TXT_TOP_MARGIN)
         builder.push_text(self.TXT_PREFIX, True)
         builder.push_text(self.TXT_PREFIX, True)
-        self._txt_build_inner(builder)
+        self.txt_build_inner(builder)
         builder.push_text(self.TXT_SUFFIX, True)
         builder.push_text(self.TXT_SUFFIX, True)
-        if hasattr(self, 'TXT_LEGACY_BOTTOM_MARGIN'):
-            builder.push_legacy_margin(self.TXT_LEGACY_BOTTOM_MARGIN)
-        else:
-            builder.push_margin(self.TXT_BOTTOM_MARGIN)
+        builder.push_margin(self.TXT_BOTTOM_MARGIN)
 
 
-    def _html_build_inner(self, builder):
-        self._build_inner(builder, 'html_build')
+    def html_build_inner(self, builder):
+        self.build_inner(builder)
 
     def get_html_attr(self, builder):
         attr = self.HTML_ATTR.copy()
         if self.HTML_CLASS:
             attr['class'] = self.HTML_CLASS
 
     def get_html_attr(self, builder):
         attr = self.HTML_ATTR.copy()
         if self.HTML_CLASS:
             attr['class'] = self.HTML_CLASS
-        # always copy the id attribute (?)
-        if self.attrib.get('id'):
-            attr['id'] = self.attrib['id']
-        elif getattr(self, 'SHOULD_HAVE_ID', False) and '_compat_section_id' in self.attrib:
-            attr['id'] = self.attrib['_compat_section_id']
+        if builder.with_ids:
+            # always copy the id attribute (?)
+            if self.attrib.get('id'):
+                attr['id'] = self.attrib['id']
+            if self.attrib.get('_id'):
+                attr['id'] = self.attrib['_id']
         return attr
 
     def html_build(self, builder):
         return attr
 
     def html_build(self, builder):
+        # Do we need a number?
+        numbering = self.numbering
+        if numbering == 'main':
+            if builder.with_numbering and self.has_visible_numbering:
+                builder.add_visible_number(self)
+
         if self.HTML_TAG:
             builder.start_element(
                 self.HTML_TAG,
                 self.get_html_attr(builder),
             )
 
         if self.HTML_TAG:
             builder.start_element(
                 self.HTML_TAG,
                 self.get_html_attr(builder),
             )
 
-        self._html_build_inner(builder)
+        self.html_build_inner(builder)
         if self.HTML_TAG:
             builder.end_element()
 
         if self.HTML_TAG:
             builder.end_element()
 
-    def _epub_build_inner(self, builder):
-        self._build_inner(builder, 'epub_build')
+    def epub_build_inner(self, builder):
+        self.build_inner(builder)
 
     def get_epub_attr(self, builder):
         attr = self.EPUB_ATTR.copy()
 
     def get_epub_attr(self, builder):
         attr = self.EPUB_ATTR.copy()
@@ -198,7 +224,7 @@ class WLElement(etree.ElementBase):
         # TEMPORARY
         self.CAN_HAVE_TEXT = True
         self.STRIP = False
         # TEMPORARY
         self.CAN_HAVE_TEXT = True
         self.STRIP = False
-       
+
         start_chunk = self.EPUB_START_CHUNK and isinstance(self.getparent(), Master)
 
         if start_chunk:
         start_chunk = self.EPUB_START_CHUNK and isinstance(self.getparent(), Master)
 
         if start_chunk:
@@ -220,12 +246,17 @@ class WLElement(etree.ElementBase):
             attr = self.get_epub_attr(builder)
             if fragment:
                 attr['id'] = fragment
             attr = self.get_epub_attr(builder)
             if fragment:
                 attr['id'] = fragment
+            if builder.debug:
+                chunkno, sourceline = 0, self.sourceline
+                if builder.splits:
+                    chunkno, sourceline = len(builder.splits), sourceline - builder.splits[-1]
+                attr['data-debug'] = f'{chunkno}:{sourceline}'
             builder.start_element(
                 self.EPUB_TAG,
                 attr
             )
 
             builder.start_element(
                 self.EPUB_TAG,
                 attr
             )
 
-        self._epub_build_inner(builder)
+        self.epub_build_inner(builder)
         if self.EPUB_TAG:
             builder.end_element()
 
         if self.EPUB_TAG:
             builder.end_element()
 
@@ -308,15 +339,38 @@ class WLElement(etree.ElementBase):
 
         return snipelem
 
 
         return snipelem
 
+    @property
+    def numbering(self):
+        numbering = self.NUMBERING
+        if numbering is None or self.in_context_of('DISABLE_NUMBERING'):
+            return None
+        numbering = self.get_context_map('SUPPRESS_NUMBERING', numbering, numbering)
+        return numbering
+
+    @property
+    def id_prefix(self):
+        prefix = self.numbering
+        if prefix == 'main':
+            # TODO: self.context.main_numbering_prefix
+            prefix = 'f' # default numbering prefix
+        return prefix
+
+    def assign_id(self, document):
+        numbering = self.numbering
+        if numbering:
+            number = str(document.counters[numbering])
+            self.attrib['_id'] = self.id_prefix + number
+            document.counters[numbering] += 1
+
+            if numbering == 'main':
+                self.attrib['_visible_numbering'] = str(document.counters['_visible'])
+                document.counters['_visible'] += 1
+
+            if numbering == 'fn':
+                self.attrib['_visible_numbering'] = number
+
     def get_link(self):
     def get_link(self):
-        sec = getattr(self, 'SHOULD_HAVE_ID', False) and self.attrib.get('_compat_section_id')
-        if sec:
-            return sec
-        parent_index = self.getparent().index(self)
-        if parent_index:
-            return self.getparent()[parent_index - 1].get_link()
-        else:
-            return self.getparent().get_link()
+        return self.attrib.get('_id') or self.getparent().get_link()
 
 
 class Snippet(WLElement):
 
 
 class Snippet(WLElement):