Logo Search packages:      
Sourcecode: zope-backtalk version File versions

CommentableDocument.py

##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
# 
##############################################################################
"""
Revision information:
$Id: CommentableDocument.py,v 1.3 2002/05/10 06:29:08 chrism Exp $
"""

import string, re
from StructuredText import ST, STDOM
from StructuredText.DocumentClass import flatten, StructuredTextSection,\
     StructuredTextExample, StructuredTextDescription, StructuredTextBullet,\
     StructuredTextNumbered, StructuredTextLink
from StructuredText.DocumentWithImages import DocumentWithImages
from ExampleRegistry import example_registry

################################################################
# Regexes and constants
################################################################

dbl_quoted_punc = '!#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
_DQUOTEDTEXT = r'("[ %s0-9\n\r%s]+")' % (string.letters, dbl_quoted_punc)
_ABSOLUTE_URL=(r'((http|https|ftp|mailto|file|about)[:/]+'
               '?[%s0-9_\@\.\,\?\!\/\:\;\-\#\~\=\&\%%\+]+)' % string.letters)
_ABS_AND_RELATIVE_URL=(r'([%s0-9_\@\.\,\?\!\/\:\;\-\#\~\=\&\%%\+]+)'
                       % string.letters)
_SPACES = r'(\s*)'
HREF_EXPR=re.compile(
    _DQUOTEDTEXT + "(:)" + _ABS_AND_RELATIVE_URL + _SPACES).search

letters = r'\w'

################################################################
# classes and functions that deal with converting a basic stx
# document with ids to a "colorized" stx document
################################################################

00048 class Comment(ST.StructuredTextParagraph):
    """ Represents a comment attached to a paragraph """
    def __init__(self, src, subs, **kw):
        t = []
        lines = string.split(src, '\n')
        firstline = lines[0]
        indent = indentation_plus(firstline)
        firstline = string.strip(firstline)[2:]
        t.append(firstline)
        for lineno in range(1, len(lines)):
            t.append(lines[lineno][indent:])
        apply(ST.StructuredTextParagraph.__init__,
              (self, string.join(t,'\n'), ()),
              kw)
    def getColorizableTexts(self): return ()
    def setColorizableTexts(self, src): pass # never color comments

00065 class CommentableExample(ST.StructuredTextParagraph):
    """Represents a section of document with literal text, as for examples,
    but also allows comments.  Renderer is passed in by """
    def __init__(self, src, normal, renderer, subs, **kw):
        self.renderer = renderer
        self.normal = normal
        apply(ST.StructuredTextParagraph.__init__,
              (self, src, subs),
              kw)

    def getColorizableTexts(self): return ()
    def setColorizableTexts(self, src): pass # never color examples

def Example(subs, **kw):
   """ return the proper type of example object depending on the source """
   indent = 0
   normal=[]; comments=[]
   flatten_example(subs, normal.append, comments.append)
   source = string.join(normal, '\n')
   for guard, func in example_registry.getColorizers():
       if guard(source, normal):
           renderer = func # guaranteed to get default renderer if not specific
           break
   return apply(CommentableExample, (source, normal, renderer, comments), kw)

00090 class CommentableDocumentClass(DocumentWithImages):
    """ A StructuredText Document with additional commenting behavior """
    paragraph_types = ['doc_comment'] + DocumentWithImages.paragraph_types
    text_types = DocumentWithImages.text_types[:]
    if 'doc_href2' in text_types:
        text_types.remove('doc_href2') # no stupid comma-separated hrefs

    # we need to redefine doc_href for 2.3-based systems that still
    # have the "old" doc_href which honors comma-style inline href
    # symbology.  We dont want this, so we replace the method wholesale.
    def doc_href(self, s,
                 expr=HREF_EXPR,
                 punctuation=re.compile(r"[\,\.\?\!\;]+").match):

        r=expr(s)

        if r:
            # need to grab the href part and the
            # beginning part
                        
            start,e = r.span(1)
            name    = s[start:e]
            name    = string.replace(name,'"','',2)
            st,end   = r.span(3)
            if punctuation(s[end-1:end]):
                end = end -1
            link    = s[st:end]
            
            # name is the href title, link is the target
            # of the href
            return (StructuredTextLink(name, href=link),
                    start, end)
            
        else:
            return None

    doc_href1 = doc_href
    
00128     def doc_comment(self, paragraph):
        """ checks to see if the paragraph is a comment, and turns it
        in to a Comment object if so """
        top = paragraph.getColorizableTexts()[0]
        if not string.strip(top)[0:2] == "% ": return None
        subs = paragraph.getSubparagraphs()
        return Comment(top, subs)

    def doc_bullet(self, paragraph, expr = re.compile(r'\s*[-*o]\s+').match):
        top=paragraph.getColorizableTexts()[0]
        m=expr(top)

        if not m:
            return None
            
        subs=paragraph.getSubparagraphs()
        if top[-2:]=='::':
            subs=[Example(subs)]
            top=top[:-1]
        return StructuredTextBullet(top[m.span()[1]:], subs,
                                    indent=paragraph.indent,
                                    bullet=top[:m.span()[1]]
                                    )

    def doc_numbered(self, paragraph, expr=re.compile(
        r'(\s*[%s]\.)|(\s*[0-9]+\.)|(\s*[0-9]+\s+)' % letters).match):
        top=paragraph.getColorizableTexts()[0]
        m=expr(top)
        if not m: return None
        subs=paragraph.getSubparagraphs()
        if top[-2:]=='::':
            subs=[Example(subs)]
            top=top[:-1]
        return StructuredTextNumbered(top[m.span()[1]:], subs,
                                      indent=paragraph.indent,
                                      number=top[:m.span()[1]])
    
    def doc_description(
        self, paragraph,
        delim = re.compile(r'\s+--\s+').search,
        nb=re.compile(r'[^\000- ]').search,
        ):

        top=paragraph.getColorizableTexts()[0]
        d=delim(top)
        if not d: return None
        start, end = d.span()
        title=top[:start]
        if string.find(title, '\n') >= 0: return None
        if not nb(title): return None
        d=top[start:end]
        top=top[end:]

        subs=paragraph.getSubparagraphs()
        if top[-2:]=='::':
            subs=[Example(subs)]
            top=top[:-1]

        return StructuredTextDescription(
            title, top, subs,
            indent=paragraph.indent,
            delim=d)
    
    def doc_header(self, paragraph):
        subs=paragraph.getSubparagraphs()
        if not subs: return None
        top=paragraph.getColorizableTexts()[0]
        if not string.strip(top): return None
        if top[-2:]=='::':
           subs=Example(subs)
           if string.strip(top)=='::': return subs
           # copy attrs when returning a paragraph
           kw = {}
           atts = getattr(paragraph, '_attributes', [])
           for att in atts: kw[att] = getattr(paragraph, att)
           return apply(ST.StructuredTextParagraph, (top[:-1], [subs]), kw)

        if string.find(top,'\n') >= 0: return None
        return StructuredTextSection(top, subs, indent=paragraph.indent)

    try:
        #capabilities test for Zope 2.4 and above
        import RestrictedPython
        del RestrictedPython
    except:
        # we need to monkeypatch Zope 2.3.X.
        def color_paragraphs(self, raw_paragraphs,
                             type=type, sequence_types=(type([]), type(())),
                             st=type('')):
            result=[]
            for paragraph in raw_paragraphs:
                if paragraph.getNodeName() != 'StructuredTextParagraph':
                    result.append(paragraph)
                    continue

                for pt in self.paragraph_types:
                    if type(pt) is st:
                        # grab the corresponding function
                        pt=getattr(self, pt)
                    # evaluate the paragraph
                    r=pt(paragraph)
                    if r:
                        if type(r) not in sequence_types:
                            r=r,
                        new_paragraphs=r
                        for paragraph in new_paragraphs:
                            paragraph.setSubparagraphs(
                                self.color_paragraphs(
                                paragraph.getSubparagraphs())
                                )
                        break
                else:
                    # copy, retain attributes
                    kw = {}
                    atts = getattr(paragraph, '_attributes', [])
                    for att in atts: kw[att] = getattr(paragraph, att)
                    subs = self.color_paragraphs(paragraph.getSubparagraphs())
                    new_paragraphs=apply(ST.StructuredTextParagraph,
                             (paragraph.getColorizableTexts()[0], subs), kw),

                # color the inline StructuredText types
                # for each StructuredTextParagraph
                for paragraph in new_paragraphs:

                    if paragraph.getNodeName() is "StructuredTextTable":
                        cells = paragraph.getColumns()
                        text = paragraph.getColorizableTexts()
                        text = map(ST.StructuredText,text)
                        text = map(self.__call__,text)
                        for t in range(len(text)):
                            text[t] = text[t].getSubparagraphs()
                        paragraph.setColorizableTexts(text)

                    paragraph.setColorizableTexts(
                        map(self.color_text,
                            paragraph.getColorizableTexts()
                            ))
                    result.append(paragraph)

            return result

CommentableDocument = CommentableDocumentClass()


################################################################
# Utility functions
################################################################

def indentation_plus(t, plus=0, spaces_expr=re.compile(r'^(\s*)').match):
    """ we would just use the indention utility from ST, but it's
    not always there under every ST version """
    m = spaces_expr(t)
    if m:
        start, end = m.span()
        plus = end-start + plus
    return plus

def flatten_example(subs, n_out, c_out):
    """ Examples are normally terminal nodes in stx.   However, in
    BackTalk, examples may have comments.  We need to pick out the comments
    from the subnodes of the example."""
    comment_nodes = []
    for sub in subs:
        # first we run through the subs list looking for comment nodes
        # if we find one, we squirrel away the node in a temp list so as to
        # not confuse the comment nodes with example text in the next
        # pass
        if sub.getNodeType()==STDOM.TEXT_NODE:
            text = sub.getNodeValue()
            if string.strip(text)[0:2] == '% ':
                # this is a comment node
                comment = flatten_comment(sub)
                c_out(Comment(comment, ()))
                comment_nodes.append(sub)
    for sub in subs:
        # next we do another pass, skipping the comment nodes we collected
        # in the first pass
        if sub in comment_nodes:
            continue
        if sub.getNodeType()==STDOM.TEXT_NODE:
            text = sub.getNodeValue()
            for tmp in string.split(text, '\n'):
                n_out(tmp)
            n_out('') # end all examples with a carriage return
        else:
            for subsub in sub.getChildNodes():
                flatten_example([subsub], n_out, c_out)

def flatten_comment(obj, tmp=None):
    if tmp is None:
        tmp = []
    append = tmp.append
    if obj.getNodeType()==STDOM.TEXT_NODE:
        text = obj.getNodeValue()
        append(text)
    else:
        for child in obj.getChildNodes():
            flatten_comment(child, tmp)
    val = string.join(tmp, '\n\n')
    return val
    


Generated by  Doxygen 1.6.0   Back to index