Overhauled evolution. #42
[zzz-pokedex.git] / pokedex / db / rst.py
index d3d6322..baedf0e 100644 (file)
@@ -33,12 +33,15 @@ are, apparently, global.
     return a reST node.
 """
 
     return a reST node.
 """
 
+import cgi
+
 from docutils.frontend import OptionParser
 from docutils.io import Output
 import docutils.nodes
 from docutils.parsers.rst import Parser, roles
 import docutils.utils
 from docutils.writers.html4css1 import Writer as HTMLWriter
 from docutils.frontend import OptionParser
 from docutils.io import Output
 import docutils.nodes
 from docutils.parsers.rst import Parser, roles
 import docutils.utils
 from docutils.writers.html4css1 import Writer as HTMLWriter
+from docutils.writers import UnfilteredWriter
 
 import sqlalchemy.types
 
 
 import sqlalchemy.types
 
@@ -52,6 +55,53 @@ class HTMLFragmentWriter(HTMLWriter):
         subs = self.interpolation_dict()
         return subs['body']
 
         subs = self.interpolation_dict()
         return subs['body']
 
+
+class TextishTranslator(docutils.nodes.SparseNodeVisitor):
+    """A simple translator that tries to return plain text that still captures
+    the spirit of the original (basic) formatting.
+
+    This will probably not be useful for anything complicated; it's only meant
+    for extremely simple text.
+    """
+
+    def __init__(self, document):
+        self.document = document
+        self.translated = u''
+
+    def visit_Text(self, node):
+        """Text is left alone."""
+        self.translated += node.astext()
+
+    def depart_paragraph(self, node):
+        """Append a blank line after a paragraph, unless it's the last of its
+        siblings.
+        """
+        if not node.parent:
+            return
+
+        # Loop over siblings.  If we see a sibling after we see this node, then
+        # append the blank line
+        seen_node = False
+        for sibling in node.parent:
+            if sibling is node:
+                seen_node = True
+                continue
+
+            if seen_node:
+                self.translated += u'\n\n'
+                return
+
+class TextishWriter(UnfilteredWriter):
+    """Translates reST back into plain text, aka more reST.  Difference is that
+    custom roles are handled, so you get "50% chance" instead of junk.
+    """
+
+    def translate(self):
+        visitor = TextishTranslator(self.document)
+        self.document.walkabout(visitor)
+        self.output = visitor.translated
+
+
 class UnicodeOutput(Output):
     """reST Unicode output.  The distribution only has a StringOutput, and I
     want me some Unicode.
 class UnicodeOutput(Output):
     """reST Unicode output.  The distribution only has a StringOutput, and I
     want me some Unicode.
@@ -70,6 +120,7 @@ def generic_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
 
 roles.register_local_role('ability', generic_role)
 roles.register_local_role('item', generic_role)
 
 roles.register_local_role('ability', generic_role)
 roles.register_local_role('item', generic_role)
+roles.register_local_role('location', generic_role)
 roles.register_local_role('move', generic_role)
 roles.register_local_role('type', generic_role)
 roles.register_local_role('pokemon', generic_role)
 roles.register_local_role('move', generic_role)
 roles.register_local_role('type', generic_role)
 roles.register_local_role('pokemon', generic_role)
@@ -87,7 +138,7 @@ roles.register_local_role('data', data_role)
 
 class RstString(object):
     """Wraps a reStructuredText string.  Stringifies to the original text, but
 
 class RstString(object):
     """Wraps a reStructuredText string.  Stringifies to the original text, but
-    may be translated to HTML with .to_html().
+    may be translated to HTML with .as_html().
     """
 
     def __init__(self, source_text, document_properties={}):
     """
 
     def __init__(self, source_text, document_properties={}):
@@ -131,11 +182,30 @@ class RstString(object):
         """Returns the string as HTML4."""
 
         document = self.rest_document
         """Returns the string as HTML4."""
 
         document = self.rest_document
-        destination = UnicodeOutput()
 
 
+        # Check for errors; don't want to leave the default error message cruft
+        # in here
+        if document.next_node(condition=docutils.nodes.system_message):
+            # Boo!  Cruft.
+            return u"""
+                <p><em>Error in markup!  Raw source is below.</em></p>
+                <pre>{0}</pre>
+            """.format( cgi.escape(self.source_text) )
+
+        destination = UnicodeOutput()
         writer = HTMLFragmentWriter()
         return writer.write(document, destination)
 
         writer = HTMLFragmentWriter()
         return writer.write(document, destination)
 
+    @property
+    def as_text(self):
+        """Returns the string mostly unchanged, save for our custom roles."""
+
+        document = self.rest_document
+
+        destination = UnicodeOutput()
+        writer = TextishWriter()
+        return writer.write(document, destination)
+
 
 class MoveEffectProperty(object):
     """Property that wraps a move effect.  Used like this:
 
 class MoveEffectProperty(object):
     """Property that wraps a move effect.  Used like this: