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