X-Git-Url: http://git.veekun.com/zzz-pokedex.git/blobdiff_plain/2efa91d9daf718debf9ed920efaf02a155054cac..e24448693777204a13a3daf90a7b98ad419f9111:/pokedex/db/rst.py diff --git a/pokedex/db/rst.py b/pokedex/db/rst.py index e70af2c..baedf0e 100644 --- a/pokedex/db/rst.py +++ b/pokedex/db/rst.py @@ -33,12 +33,17 @@ are, apparently, global. 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): @@ -50,6 +55,53 @@ 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. @@ -63,12 +115,14 @@ class UnicodeOutput(Output): ### 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) @@ -84,7 +138,7 @@ roles.register_local_role('data', data_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={}): @@ -128,16 +182,35 @@ class RstString(object): """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""" +
Error in markup! Raw source is below.
+{0}
+ """.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
@@ -147,6 +220,9 @@ class MoveEffectProperty(object):
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..
@@ -155,6 +231,20 @@ class MoveEffectProperty(object):
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)