1. John Mulligan
  2. muyhomepage2

Commits

John Mulligan  committed c9e3b2f

basic support for text formatting

  • Participants
  • Parent commits 79050d8
  • Branches default

Comments (0)

Files changed (3)

File muyhomepage2/pagehandlers/standard.py

View file
 
 from muyhomepage2.pagehandlers import handler
+from muyhomepage2.text import wikirst
 
 class StandardHandler(handler.PageHandler):
     def content(self):
-        return # text.rstwiki.format(self.page.body)
+        return wikirst.format(self.page.body)
 
     def template(self):
         return 'page.html'

File muyhomepage2/text/__init__.py

Empty file added.

File muyhomepage2/text/wikirst.py

View file
+# TODO cleanup
+
+import re
+import docutils
+import docutils.core, docutils.writers, docutils.nodes
+
+from muyhomepage2 import util
+
+
+def format(text, enc='utf-8'):
+    output = ReStructuredText(text).format()
+    return output.decode(enc)
+
+
+
+#  ~~~~~~~~~~~~~~~
+
+class RestTranslator(docutils.nodes.GenericNodeVisitor):
+	
+    def __init__( self, document, parent_object=None ):
+        docutils.nodes.GenericNodeVisitor.__init__( self, document )
+        
+        self.parent_object = parent_object
+        self.content = []
+        self.invisible = False
+        self.linked = False
+
+        self.freeze_text = False
+        
+        
+    def unknown_visit( self, node ):
+        print node
+        
+    def unknown_departure( self, node ):
+        print node
+        
+    
+    def default_visit( self, node ):
+        if node.__class__ is not docutils.nodes.Text:
+            return
+        
+        if self.invisible:
+            return
+
+        if self.alterText and not self.freeze_text:
+            temp = self.alterText( self.prepContent( node.astext() ) )
+            self.appendContent( temp )
+            return
+
+        self.appendContent( self.prepContent( node.astext() ) )
+        return
+
+
+
+    def prepContent( self, txt ):
+        txt = txt.replace( '&', '&' )
+        txt = txt.replace( '<', '&lt;' )
+        txt = txt.replace( '>', '&gt;' )
+        
+        return txt
+
+
+    def appendContent( self, txt ):
+        self.content.append( txt )
+    
+
+    def default_departure( self, node ):
+        pass
+
+
+        
+    
+    
+    def enterTag( self, tag, attrs ):
+        #FIXME - does AttributeBuilder protect agains bad characters?
+        self.content.append( '<%s%s>' % ( tag, str(attrs) )  )
+        
+        
+    def exitTag( self, tag ):
+        self.content.append( '</%s>' % tag )
+    
+    
+    def astext( self ):
+        return ''.join( self.content )
+    
+    
+    def ezElement( cls, rst_elem, htm_elem, add_attrs=None ):
+
+        if add_attrs is None:
+            add_attrs = {}
+
+        def visit_node( self_, node ):
+            attrs = AttributeBuilder( node )
+            attrs.update( add_attrs )
+            self_.enterTag( htm_elem, attrs )
+
+        def depart_node( self_, node ):
+            self_.exitTag( htm_elem )
+        
+        setattr( cls, 'visit_%s' % rst_elem,  visit_node  )
+        setattr( cls, 'depart_%s' % rst_elem, depart_node )
+
+
+    ezElement = classmethod( ezElement )
+
+
+
+    # = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+
+    
+    def visit_reference( self, node ):
+        
+        #attrs = self.genAttrs( node )
+        #attrs['href'] = node.attributes.get( 'refuri', '' )
+        attrs = AttributeBuilder( node )
+        attrs.map( 'refuri', 'href' )
+
+        self.linked = True
+        self.enterTag( 'a', attrs )
+
+    def depart_reference( self, node ):
+        self.exitTag( 'a' )
+        self.linked = False
+
+
+
+    def visit_image( self, node ):
+        #attrs = self.genAttrs( node, allow=('width','height','alt','align') )
+        #
+        #attrs[ 'src' ] = node.attributes.get( 'uri', '' )
+
+        attrs = AttributeBuilder( node )
+        attrs.allow( 'width', 'height', 'alt', 'align' )
+        attrs.map( 'uri', 'src' )
+
+        if 'scale' in node.attributes:
+            scale = int( node.attributes['scale'] ) 
+            
+            if 'width' in attrs:
+                attrs['width'] = ( int( attrs['width'] ) * scale ) // 100
+
+            if 'height' in attrs:
+                attrs['height'] = ( int( attrs['height'] ) * scale ) // 100
+        
+
+        self.enterTag( 'img', attrs )
+
+    def depart_image( self, node ):
+        self.exitTag( 'img' )
+        
+
+    def visit_paragraph( self, node ):
+        self.enterTag( 'p', AttributeBuilder(node) )
+    
+    def depart_paragraph( self, node ):
+        self.exitTag( 'p' )
+
+
+    # reST comments must be _invisible_ to the output
+    def visit_comment( self, node ):
+        self.invisible = True
+
+    def depart_comment( self, node ):
+        self.invisible = False
+
+
+
+RestTranslator.ezElement( 'document', 'div', {'class':'generated-text'} )
+
+RestTranslator.ezElement( 'raw', 'pre' )
+RestTranslator.ezElement( 'strong', 'strong' )
+RestTranslator.ezElement( 'emphasis', 'em' )
+RestTranslator.ezElement( 'superscript', 'super' )
+RestTranslator.ezElement( 'subscript', 'sub' )
+RestTranslator.ezElement( 'block_quote', 'blockquote' )
+RestTranslator.ezElement( 'bullet_list', 'ul' )
+RestTranslator.ezElement( 'enumerated_list', 'ol' )
+RestTranslator.ezElement( 'title', 'h3' )
+RestTranslator.ezElement( 'list_item', 'li' )
+RestTranslator.ezElement( 'literal', 'tt', {'class':'literal'} )
+RestTranslator.ezElement( 'literal_block', 'pre', {'class':'literal'} )
+RestTranslator.ezElement( 'definition_list', 'dl' )
+RestTranslator.ezElement( 'term', 'dt' )
+RestTranslator.ezElement( 'definition', 'dd' )
+RestTranslator.ezElement( 'classifier', 'span', {'class':'classifier'} )
+
+
+
+class AttributeBuilder (object):
+
+    DEF_MAP = {
+        'class' : 'class',
+    }
+
+    def __init__( self, node, force=None ):
+        self.node = node
+
+        self._map = {}
+        self._map.update( self.DEF_MAP )
+
+
+    def keys( self ):
+        return self._map.keys()
+
+    def update( self, dct ):
+        for key in dct:
+            self[ key ] = dct[ key ]
+
+    def values( self ):
+        return list( self.itervalues() )
+
+    def itervalues( self ):
+        for key, value in self.iteritems():
+            yield value
+
+    def iteritems( self ):
+        for key in self.keys():
+            
+            try:
+                val = self[ key ]
+            except KeyError:
+                continue
+
+            if val:
+                yield key, val
+
+    def items( self ):
+        return list( self.iteritems() )
+
+    
+    def __repr__( self ):
+        object.__repr__( self )
+
+    def __str__( self ):
+        pat = '%s="%s"'
+        out = ' '.join( [pat % item for item in self.iteritems()] )
+
+        if out:
+            return ' ' + out
+
+        return ''
+
+
+    def __getitem__( self, key ):
+        rtr = '_retrv_%s' % key
+
+        try:
+            return getattr( self, rtr )( key )
+        except AttributeError:
+            return self._retrv_Default( key )
+
+
+    def __setitem__( self, key, value ):
+        # a forced set makes it allowed
+        self.allow( key )
+
+        inj = '_inject_%s' % key 
+
+        try:
+            getattr( self, inj )( key, value )
+        except:
+            self._inject_Default( key, value )
+
+    def allow( self, *args ):
+        for arg in args:
+            self._map[ arg ] = arg
+
+    def map( self, internal, external ):
+        self._map[ external ] = internal
+
+
+    def _retrv_Default( self, key ):
+        return self.node.attributes[ self._map[key] ]
+
+    def _inject_Default( self, key, value ):
+        self.node.attributes[ self._map[key] ] = value
+
+
+    def _retrv_class( self, key ):
+        return ' '.join( self.node.attributes['classes'] )
+
+    def _inject_class( self, key, value ):
+        return self.node.attributes['classes'].append( value )
+
+
+
+
+
+class RestWriter (docutils.writers.Writer):
+
+    def __init__( self, translator, parent_object=None ):
+        docutils.writers.Writer.__init__( self )
+
+        self.Translator = translator
+        self.parent_object = parent_object
+
+    def translate( self ):
+        self.visitor = self.Translator( self.document, self.parent_object )
+        self.document.walkabout( self.visitor )
+
+        self.output = self.visitor.astext()
+
+
+
+class ReStructuredText(object):
+    def __init__(self, text, translator=None, writer=None):
+        self.text = text
+        self.translator = translator or RestTranslator
+        self.translator.alterText = self.wikiMorph
+        self.writer = writer or RestWriter
+        self.findcmd = re.compile(self.pattern)
+
+    def format(self):
+        writer = self.writer(self.translator, self)
+        return docutils.core.publish_string(self.text, writer=writer)
+
+    pattern = r"(\[{2}|\{{2})(.*?)(\]{2}|\}{2})"
+
+    def wikiMorph(self, text):
+        text = self.findcmd.sub(self.handlecmd, text)
+        return text
+
+    def handlecmd(self, match):
+        brStart, content, brEnd = match.groups()
+        if brStart == '{{':
+            return self.bracecmd(content)
+        else:
+            return self.bracketcmd(content)
+
+    def bracketcmd(self, content):
+        if '|' in content:
+            target, text = content.split('|', 1)
+        else:
+            target = text = content
+        target = './%s' % util.encodetitle(target)
+        return '<a href="%s" class="wikilink">%s</a>' % (target, text)
+        assert 1 == 2, content
+
+    def bracecmd(self, content):
+        return content
+
+
+BRACKET_CMD = 'bracket'
+BRACE_CMD   = 'brace'