File: Synopsis/Formatters/HTML/Markup/RST.py
  1#
  2# Copyright (C) 2006 Stefan Seefeld
  3# All rights reserved.
  4# Licensed to the public under the terms of the GNU LGPL (>= 2),
  5# see the file COPYING for details.
  6#
  7
  8from Synopsis.Formatters.HTML.Tags import *
  9from Synopsis.Formatters.HTML.Markup import *
 10from docutils.nodes import *
 11from docutils.core import *
 12from docutils.parsers.rst import roles
 13import re, StringIO
 14
 15
 16class SummaryExtractor(NodeVisitor):
 17    """A SummaryExtractor creates a document containing the first sentence of
 18    a source document."""
 19
 20    def __init__(self, document):
 21
 22        NodeVisitor.__init__(self, document)
 23        self.summary = None
 24
 25
 26    def visit_paragraph(self, node):
 27        """Copy the paragraph but only keep the first sentence."""
 28
 29        if self.summary is not None:
 30            return
 31
 32        summary_pieces = []
 33
 34        # Extract the first sentence.
 35        for child in node:
 36            if isinstance(child, Text):
 37                m = re.match(r'(\s*[\w\W]*?\.)(\s|$)', child.data)
 38                if m:
 39                    summary_pieces.append(Text(m.group(1)))
 40                    break
 41                else:
 42                    summary_pieces.append(Text(child))
 43            else:
 44                summary_pieces.append(child)
 45
 46        self.summary = self.document.copy()
 47        para = node.copy()
 48        para[:] = summary_pieces
 49        self.summary[:] = [para]
 50
 51
 52    def unknown_visit(self, node):
 53        'Ignore all unknown nodes'
 54
 55        pass
 56
 57
 58class RST(Formatter):
 59    """Format summary and detail documentation according to restructured text markup.
 60    """
 61
 62    def format(self, decl, view):
 63
 64        formatter = self
 65
 66        def ref(name, rawtext, text, lineno, inliner,
 67                options={}, content=[]):
 68
 69            name = utils.unescape(text)
 70            uri = formatter.lookup_symbol(name, decl.name[:-1])
 71            if uri:
 72                ref = rel(view.filename(), uri)
 73                node = reference(rawtext, name, refuri=ref, **options)
 74            else:
 75                node = emphasis(rawtext, name)
 76            return [node], []
 77
 78        roles.register_local_role('', ref)
 79
 80        errstream = StringIO.StringIO()
 81        settings = {}
 82        settings['halt_level'] = 2
 83        settings['warning_stream'] = errstream
 84        settings['traceback'] = True
 85
 86        doc = decl.annotations.get('doc')
 87        if doc:
 88            try:
 89                doctree = publish_doctree(doc.text, settings_overrides=settings)
 90                # Extract the summary.
 91                extractor = SummaryExtractor(doctree)
 92                doctree.walk(extractor)
 93
 94                reader = docutils.readers.doctree.Reader(parser_name='null')
 95
 96                # Publish the summary.
 97                if extractor.summary:
 98                    pub = Publisher(reader, None, None,
 99                                    source=io.DocTreeInput(extractor.summary),
100                                    destination_class=io.StringOutput)
101                    pub.set_writer('html')
102                    pub.process_programmatic_settings(None, None, None)
103                    dummy = pub.publish(enable_exit_status=None)
104                    summary = pub.writer.parts['html_body']
105                    # Hack to strip off some redundant blocks to make the output
106                    # more compact.
107                    if (summary.startswith('<div class="document">\n') and
108                        summary.endswith('</div>\n')):
109                        summary=summary[23:-7]
110                else:
111                    summary = ''
112
113                # Publish the details.
114                pub = Publisher(reader, None, None,
115                                source=io.DocTreeInput(doctree),
116                                destination_class=io.StringOutput)
117                pub.set_writer('html')
118                pub.process_programmatic_settings(None, None, None)
119                dummy = pub.publish(enable_exit_status=None)
120                details = pub.writer.parts['html_body']
121                # Hack to strip off some redundant blocks to make the output
122                # more compact.
123                if (details.startswith('<div class="document">\n') and
124                    details.endswith('</div>\n')):
125                    details=details[23:-7]
126
127                return Struct(summary, details)
128
129            except docutils.utils.SystemMessage, error:
130                xx, line, message = str(error).split(':', 2)
131                print 'In DocString attached to declaration at %s:%d:'%(decl.file.name,
132                                                                        decl.line)
133                print '  line %s:%s'%(line, message)
134
135        return Struct('', '')
136