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    def doc(self, decl, view):
 67
 68        return self._process(decl, view)
 69
 70    def summary(self, decl, view):
 71        """"""
 72
 73        return self.doc(decl, view).summary
 74
 75
 76    def details(self, decl, view):
 77        """"""
 78
 79        return self.doc(decl, view).details
 80
 81
 82class Formatter(Processor):
 83
 84    title = Parameter('Synopsis - Generated Documentation', 'title to put into html header')
 85    stylesheet = Parameter(os.path.join(config.datadir, 'html.css'), 'stylesheet to be used')
 86    directory_layout = Parameter(NestedDirectoryLayout(), 'how to lay out the output files')
 87    toc_in = Parameter([], 'list of table of content files to use for symbol lookup')
 88    toc_out = Parameter('', 'name of file into which to store the TOC')
 89    sxr_prefix = Parameter(None, 'path prefix (directory) under which to find sxr info')
 90
 91    index = Parameter([ModuleTree(), FileTree()], 'set of index views')
 92    detail = Parameter([ModuleIndex(), FileIndex()], 'set of detail views')
 93    content = Parameter([Scope(),
 94                         Source(),
 95                         XRef(),
 96                         FileDetails(),
 97                         InheritanceTree(),
 98                         InheritanceGraph(),
 99                         NameIndex()],
100                        'set of content views')
101
102    markup_formatters = Parameter({'javadoc':Javadoc(),
103                                   'rst':RST(),
104                                   'reStructuredText':RST()},
105                                  'Markup-specific formatters.')
106    graph_color = Parameter('#ffcc99', 'base color for inheritance graphs')
107
108    def process(self, ir, **kwds):
109
110        self.set_parameters(kwds)
111        if not self.output: raise MissingArgument('output')
112
113        self.ir = self.merge_input(ir)
114        # Make sure we operate on a single top-level node.
115        # (Python package, C++ global namespace, etc.)
116        if (len(self.ir.asg.declarations) != 1 or
117            not isinstance(self.ir.asg.declarations[0], ASG.Module)):
118            # Assume this is C++ in this case.
119            self.root = ASG.Module(None, -1, 'namespace', QualifiedName())
120            self.root.declarations = ir.asg.declarations
121        else:
122            self.root = self.ir.asg.declarations[0]
123
124        self.directory_layout.init(self)
125        self.documentation = DocCache(self, self.markup_formatters)
126
127        # Create the class tree (shared by inheritance graph / tree views).
128        self.class_tree = ClassTree()
129        for d in self.root.declarations:
130            d.accept(self.class_tree)
131
132        # Create the file tree (shared by file listing / tree views).
133        self.file_tree = make_file_tree(self.ir.files.values())
134
135        # Create the cross reference table (shared by XRef / Scope views)
136        self.xref = XRefPager(self.ir)
137
138        from Synopsis.DeclarationSorter import DeclarationSorter
139        self.sorter = DeclarationSorter()
140
141        # Make all views queryable through Formatter.has_view()
142        self.views = self.content + self.index + self.detail
143
144        frames = []
145        # If only content contains views don't use frames.
146        if self.index or self.detail:
147            Tags.using_frames = True
148
149            frames.append(Frame(self, self.index))
150            frames.append(Frame(self, self.detail))
151            frames.append(Frame(self, self.content))
152        else:
153            Tags.using_frames = False
154
155            frames.append(Frame(self, self.content, noframes = True))
156
157        self.__files = {} # map from filename to (view,scope)
158
159        # The table of content is by definition the TOC of the first
160        # view on the content frame.
161        self.toc = self.content[0].toc()
162        if self.verbose: print "TOC size:", self.toc.size()
163        if self.toc_out: self.toc.store(self.toc_out)
164        # load external references from toc files, if any
165        for t in self.toc_in: self.toc.load(t)
166
167        if self.verbose: print "HTML Formatter: Generating views..."
168
169        # Process the views.
170        if len(frames) > 1:
171            frameset = FrameSet()
172            frameset.process(self.output, self.directory_layout.index(),
173                             self.title,
174                             self.index[0].root()[0] or self.index[0].filename(),
175                             self.detail[0].root()[0] or self.detail[0].filename(),
176                             self.content[0].root()[0] or self.content[0].filename())
177        for frame in frames: frame.process()
178        return self.ir
179
180    def has_view(self, name):
181        """test whether the given view is part of the views list."""
182
183        return name in [x.__class__.__name__ for x in self.views]
184
185    def register_filename(self, filename, view, scope):
186        """Registers a file for later production. The first view to register
187        the filename gets to keep it."""
188
189        filename = str(filename)
190        if not self.__files.has_key(filename):
191            self.__files[filename] = (view, scope)
192
193    def filename_info(self, filename):
194        """Returns information about a registered file, as a (view,scope)
195        pair. Will return None if the filename isn't registered."""
196
197        return self.__files.get(filename)
198
199