Synopsis - Synopsis/Formatters/HTML/__init__.py

File: Synopsis/Formatters/HTML/__init__.py

      1#
      2# Copyright (C) 2003 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 import config
      9from Synopsis.Processor import *
     10from Synopsis import IR, ASG
     11from Synopsis.QualifiedName import *
     12from Synopsis.DocString import DocString
     13from Synopsis.FileTree import make_file_tree
     14from Synopsis.Formatters.TOC import TOC
     15from Synopsis.Formatters.ClassTree import ClassTree
     16from DirectoryLayout import *
     17from XRefPager import XRefPager
     18from Views import *
     19from Frame import Frame
     20from FrameSet import FrameSet
     21from Synopsis.Formatters.HTML.Markup.Javadoc import Javadoc
     22try:
     23    from Synopsis.Formatters.HTML.Markup.RST import RST
     24except ImportError:
     25    from Synopsis.Formatters.HTML.Markup import Formatter as RST
     26import Markup
     27import Tags
     28
     29import time
     30
     31class DocCache:
     32    """"""
     33
     34    def __init__(self, processor, markup_formatters):
     35
     36        self._processor = processor
     37        self._markup_formatters = markup_formatters
     38        # Make sure we have a default markup formatter.
     39        if '' not in self._markup_formatters:
     40            self._markup_formatters[''] = Markup.Formatter()
     41        for f in self._markup_formatters.values():
     42            f.init(self._processor)
     43        self._doc_cache = {}
     44
     45
     46    def _process(self, decl, view):
     47        """Return the documentation for the given declaration."""
     48
     49        key = id(decl)
     50        if key not in self._doc_cache:
     51            doc = decl.annotations.get('doc')
     52            if doc:
     53                formatter = self._markup_formatters.get(doc.markup,
     54                                                        self._markup_formatters[''])
     55                doc = formatter.format(decl, view)
     56            else:
     57                doc = Markup.Struct()
     58
     59            # FIXME: Unfortunately we can't easily cache these, as they may
     60            #        contain relative URLs that aren't valid across views.
     61            # self._doc_cache[key] = doc
     62            return doc
     63        else:
     64            return self._doc_cache[key]
     65
     66
     67    def summary(self, decl, view):
     68        """"""
     69
     70        doc = self._process(decl, view)
     71        return doc.summary
     72
     73
     74    def details(self, decl, view):
     75        """"""
     76
     77        doc = self._process(decl, view)
     78        return doc.details
     79
     80
     81class Formatter(Processor):
     82
     83    title = Parameter('Synopsis - Generated Documentation', 'title to put into html header')
     84    stylesheet = Parameter(os.path.join(config.datadir, 'html.css'), 'stylesheet to be used')
     85    directory_layout = Parameter(NestedDirectoryLayout(), 'how to lay out the output files')
     86    toc_in = Parameter([], 'list of table of content files to use for symbol lookup')
     87    toc_out = Parameter('', 'name of file into which to store the TOC')
     88    sxr_prefix = Parameter(None, 'path prefix (directory) under which to find sxr info')
     89
     90    index = Parameter([ModuleTree(), FileTree()], 'set of index views')
     91    detail = Parameter([ModuleIndex(), FileIndex()], 'set of detail views')
     92    content = Parameter([Scope(),
     93                         Source(),
     94                         XRef(),
     95                         FileDetails(),
     96                         InheritanceTree(),
     97                         InheritanceGraph(),
     98                         NameIndex()],
     99                        'set of content views')
    100
    101    markup_formatters = Parameter({'javadoc':Javadoc(),
    102                                   'rst':RST(),
    103                                   'reStructuredText':RST()},
    104                                  'Markup-specific formatters.')
    105    graph_color = Parameter('#ffcc99', 'base color for inheritance graphs')
    106
    107    def process(self, ir, **kwds):
    108
    109        self.set_parameters(kwds)
    110        if not self.output: raise MissingArgument('output')
    111
    112        self.ir = self.merge_input(ir)
    113        # Make sure we operate on a single top-level node.
    114        # (Python package, C++ global namespace, etc.)
    115        if (len(self.ir.asg.declarations) != 1 or
    116            not isinstance(self.ir.asg.declarations[0], ASG.Module)):
    117            # Assume this is C++ in this case.
    118            self.root = ASG.Module(None, -1, 'namespace', QualifiedName())
    119            self.root.declarations = ir.asg.declarations
    120        else:
    121            self.root = self.ir.asg.declarations[0]
    122
    123        self.directory_layout.init(self)
    124        self.documentation = DocCache(self, self.markup_formatters)
    125
    126        # Create the class tree (shared by inheritance graph / tree views).
    127        self.class_tree = ClassTree()
    128        for d in self.root.declarations:
    129            d.accept(self.class_tree)
    130
    131        # Create the file tree (shared by file listing / tree views).
    132        self.file_tree = make_file_tree(self.ir.files.values())
    133
    134        # Create the cross reference table (shared by XRef / Scope views)
    135        self.xref = XRefPager(self.ir)
    136
    137        from Synopsis.DeclarationSorter import DeclarationSorter
    138        self.sorter = DeclarationSorter()
    139
    140        # Make all views queryable through Formatter.has_view()
    141        self.views = self.content + self.index + self.detail
    142
    143        frames = []
    144        # If only content contains views don't use frames.
    145        if self.index or self.detail:
    146            Tags.using_frames = True
    147
    148            frames.append(Frame(self, self.index))
    149            frames.append(Frame(self, self.detail))
    150            frames.append(Frame(self, self.content))
    151        else:
    152            Tags.using_frames = False
    153
    154            frames.append(Frame(self, self.content, noframes = True))
    155
    156        self.__files = {} # map from filename to (view,scope)
    157
    158        # The table of content is by definition the TOC of the first
    159        # view on the content frame.
    160        self.toc = self.content[0].toc()
    161        if self.verbose: print "TOC size:", self.toc.size()
    162        if self.toc_out: self.toc.store(self.toc_out)
    163        # load external references from toc files, if any
    164        for t in self.toc_in: self.toc.load(t)
    165
    166        if self.verbose: print "HTML Formatter: Generating views..."
    167
    168        # Process the views.
    169        if len(frames) > 1:
    170            frameset = FrameSet()
    171            frameset.process(self.output, self.directory_layout.index(),
    172                             self.title,
    173                             self.index[0].root()[0] or self.index[0].filename(),
    174                             self.detail[0].root()[0] or self.detail[0].filename(),
    175                             self.content[0].root()[0] or self.content[0].filename())
    176        for frame in frames: frame.process()
    177        return self.ir
    178
    179    def has_view(self, name):
    180        """test whether the given view is part of the views list."""
    181
    182        return name in [x.__class__.__name__ for x in self.views]
    183
    184    def register_filename(self, filename, view, scope):
    185        """Registers a file for later production. The first view to register
    186        the filename gets to keep it."""
    187
    188        filename = str(filename)
    189        if not self.__files.has_key(filename):
    190            self.__files[filename] = (view, scope)
    191
    192    def filename_info(self, filename):
    193        """Returns information about a registered file, as a (view,scope)
    194        pair. Will return None if the filename isn't registered."""
    195
    196        return self.__files.get(filename)
    197
    198

Generated on Thu Apr 16 16:27:12 2009 by
synopsis (version devel)