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.writers import UnfilteredWriter
+
+import sqlalchemy.types
### Subclasses of bits of docutils, to munge it into doing what I want
class HTMLFragmentWriter(HTMLWriter):
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.
### Text roles
def generic_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
- node = docutils.nodes.strong(text, rawtext, **options)
+ node = docutils.nodes.emphasis(rawtext, text, **options)
return [node], []
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('mechanic', generic_role)
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={}):
"""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)
+ @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:
- MoveClass.effect = MoveEffectProperty()
+ MoveClass.effect = MoveEffectProperty('effect')
some_move.effect # returns an RstString
some_move.effect.as_html # returns a chunk of HTML
lie and it doesn't yet.
"""
+ def __init__(self, effect_column):
+ self.effect_column = effect_column
+
def __get__(self, move, move_class):
# Attach a function for handling the `data` role
# XXX make this a little more fault-tolerant.. maybe..
newtext = getattr(move, text[5:])
return docutils.nodes.Text(newtext, rawtext)
- return RstString(move.move_effect.effect,
+ return RstString(getattr(move.move_effect, self.effect_column),
document_properties=dict(
_pokedex_handle_data=data_role_func))
+
+class RstTextColumn(sqlalchemy.types.TypeDecorator):
+ """Generic column type for reST text.
+
+ Do NOT use this for move effects! They need to know what move they belong
+ to so they can fill in, e.g., effect chances.
+ """
+ impl = sqlalchemy.types.Unicode
+
+ def process_bind_param(self, value, dialect):
+ return unicode(value)
+
+ def process_result_value(self, value, dialect):
+ return RstString(value)