File: Formatter.py

package info (click to toggle)
synopsis 0.8.0-5
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 10,112 kB
  • ctags: 12,996
  • sloc: cpp: 34,254; ansic: 33,620; python: 10,975; sh: 7,261; xml: 6,369; makefile: 773; asm: 445
file content (307 lines) | stat: -rw-r--r-- 11,321 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
#
# Copyright (C) 2003 Stefan Seefeld
# All rights reserved.
# Licensed to the public under the terms of the GNU LGPL (>= 2),
# see the file COPYING for details.
#

from Synopsis import config
from Synopsis.Processor import Processor, Parameter
from Synopsis import AST
from Synopsis.FileTree import FileTree
from Synopsis.Formatters.TOC import TOC
from Synopsis.Formatters.ClassTree import ClassTree
from Synopsis.Formatters.XRef import CrossReferencer
from FileLayout import *
from TreeFormatter import *
from DeclarationStyle import *
from Views import *
import Comments
import Tags

import time

class Struct:
   "Dummy class. Initialise with keyword args."
   def __init__(self, **keys):
      for name, value in keys.items(): setattr(self, name, value)

class CommentFormatter:
   """A class that takes a Declaration and formats its comments into a string."""

   def __init__(self, formatters):

      # Cache the bound methods
      self.__format_methods = map(lambda f:f.format, formatters)
      self.__format_summary_methods = map(lambda f:f.format_summary, formatters)
      # Weed out the unneccessary calls to the empty base methods
      base = Comments.Formatter.format.im_func
      self.__format_methods = filter(
          lambda m, base=base: m.im_func is not base, self.__format_methods)
      base = Comments.Formatter.format_summary.im_func
      self.__format_summary_methods = filter(
          lambda m, base=base: m.im_func is not base, self.__format_summary_methods)

   def format(self, view, decl):
      """Formats the first comment of the given AST.Declaration.
      Note that the Linker.Comments.Summarizer CommentProcessor is supposed
      to have combined all comments first in the Linker stage.
      @return the formatted text
      """

      comments = decl.comments()
      if len(comments) == 0: return ''
      text = comments[0].text()
      if not text: return ''
      # Let each strategy format the text in turn
      for method in self.__format_methods:
         text = method(view, decl, text)
      return text

   def format_summary(self, view, decl):
      """Formats the summary of the first comment of the given
      AST.Declaration.
      Note that the Linker.Comments.Summarizer CommentProcessor is supposed
      to have combined all comments first in the Linker stage.
      @return the formatted summary text
      """

      comments = decl.comments()
      if len(comments) == 0: return ''
      text = comments[0].summary()
      if not text: return ''
      # Let each strategy format the text in turn
      for method in self.__format_summary_methods:
         text = method(view, decl, text)
      return text

class Formatter(Processor):

   title = Parameter('Synopsis - Generated Documentation', 'title to put into html header')
   stylesheet = Parameter(os.path.join(config.datadir, 'html.css'), 'stylesheet to be used')
   datadir = Parameter('', 'alternative data directory')
   file_layout = Parameter(NestedFileLayout(), 'how to lay out the output files')
   toc_in = Parameter([], 'list of table of content files to use for symbol lookup')
   toc_out = Parameter('', 'name of file into which to store the TOC')

   views = Parameter([FramesIndex(),
                      Scope(),
                      ModuleListing(),
                      ModuleIndexer(),
                      FileListing(),
                      FileIndexer(),
                      FileDetails(),
                      InheritanceTree(),
                      InheritanceGraph(),
                      NameIndex()],
                      '')
   
   comment_formatters = Parameter([Comments.QuoteHTML(),
                                   Comments.Section()],
                                  '')
   
   tree_formatter = Parameter(TreeFormatter(), 'define how to lay out tree views')

   def process(self, ast, **kwds):

      self.set_parameters(kwds)
      self.ast = self.merge_input(ast)

      # if not set, use default...
      if not self.datadir: self.datadir = config.datadir

      self.file_layout.init(self)
      self.decl_style = Style()
      for f in self.comment_formatters:
         f.init(self)
      self.comments = CommentFormatter(self.comment_formatters)
      # Create the Class Tree (TODO: only if needed...)
      self.class_tree = ClassTree()
      # Create the File Tree (TODO: only if needed...)
      self.file_tree = FileTree()
      self.file_tree.set_ast(self.ast)

      self.xref = CrossReferencer()

      #if not self.sorter:
      import ScopeSorter
      self.sorter = ScopeSorter.ScopeSorter()


      Tags.using_frames = self.has_view('FramesIndex')

      self.main_view = None
      self.contents_view = None
      self.index_view = None
      self.using_module_index = False
      
      declarations = self.ast.declarations()

      # Build class tree
      for d in declarations:
         d.accept(self.class_tree)

      self.__roots = [] #views with roots, list of Structs
      self.__global = None # The global scope
      self.__files = {} # map from filename to (view,scope)

      for view in self.views:
         view.register(self)

      root = AST.Module(None,-1,"C++","Global",())
      root.declarations()[:] = declarations

      # Create table of contents index
      start = self.calculate_start(root)
      self.toc = self.get_toc(start)
      if self.verbose: print "HTML Formatter: Initialising TOC"

      # Add all declarations to the namespace tree
      # for d in declarations:
      #	d.accept(self.toc)
	
      if self.verbose: print "TOC size:",self.toc.size()
      if self.toc_out: self.toc.store(self.toc_out)
    
      # load external references from toc files, if any
      for t in self.toc_in: self.toc.load(t)
   
      if self.verbose: print "HTML Formatter: Generating views..."

      # Create the views
      self.__global = root
      start = self.calculate_start(root)
      if self.verbose: print "Registering filenames...",
      for view in self.views:
         view.register_filenames(start)
      if self.verbose: print "Done."
      for view in self.views:
         if self.verbose:
            print "Time for %s:"%view.__class__.__name__,
            sys.stdout.flush()
            start_time = time.time()
         view.process(start)
         if self.verbose:
            print "%f"%(time.time() - start_time)

      return self.ast

   def has_view(self, name):
      """test whether the given view is part of the views list."""

      return reduce(lambda x, y: x or y,
                    map(lambda x: x.__class__.__name__ == name, self.views))

   def get_toc(self, start):
      """Returns the table of content to link into from the outside"""

      ### FIXME : how should this work ?
      ### We need to point to one of the views...
      return self.views[1].get_toc(start)

   def set_main_view(self, view):
      """Call this method to set the main index.html view. First come first served
      -- whatever module the user puts first in the list that sets this is
      it."""

      if not self.main_view:
         self.main_view = view

   def set_contents_view(self, view):
      """Call this method to set the contents view. First come first served
      -- whatever module the user puts first in the list that sets this is
      it. This is the frame in the top-left if you use the default frameset."""

      if not self.contents_view: self.contents_view = view
    
   def set_index_view(self, view):
      """Call this method to set the index view. First come first served
      -- whatever module the user puts first in the list that sets this is
      it. This is the frame on the left if you use the default frameset."""

      if not self.index_view: self.index_view = view

   def set_using_module_index(self):
      """Sets the using_module_index flag. This will cause the an
      intermediate level of links intended to go in the left frame."""
      self.using_module_index = True

   def global_scope(self):
      "Return the global scope"

      return self.__global

   def calculate_start(self, root, namespace=None):
      "Calculates the start scope using the 'namespace' config var"

      scope_names = string.split(namespace or '', "::")
      #scope_names = string.split(namespace or config.namespace, "::")
      start = root # The running result
      self.sorter.set_scope(root)
      scope = [] # The running name of the start
      for scope_name in scope_names:
         if not scope_name: break
         scope.append(scope_name)
         try:
            child = self.sorter.child(tuple(scope))
            if isinstance(child, AST.Scope):
               start = child
               self.sorter.set_scope(start)
            else:
               raise TypeError, 'calculate_start: Not a Scope'
         except:
            # Don't continue if scope traversal failed!
            import traceback
            traceback.print_exc()
            print "Fatal: Couldn't find child scope",scope
            print "Children:",map(lambda x:x.name(), self.sorter.children())
            sys.exit(3)
      return start

   def add_root_view(self, file, label, target, visibility):
      """Adds a named link to the list of root views. Called from the
      constructors of View objects. The root views are displayed at the top
      of every view, depending on their visibility (higher = more visible).
      @param file	    the filename, to be used when generating the link
      @param label	    the label of the view
      @param target       target frame
      @param visibility   should be a number such as 1 or 2. Visibility 2 is
      shown on all views, 1 only on views with lots of
      room. For example, views for the top-left frame
      only show visibility 2 views."""

      self.__roots.append(Struct(file=file, label=label, target=target, visibility=visibility))

   def navigation_bar(self, origin, visibility=1):
      """Formats the list of root views to HTML. The origin specifies the
      generated view itself (which shouldn't be linked), such that the relative
      links can be generated. Only root views of 'visibility' or
      above are included."""

      # If not using frames, show all headings on all views!
      if not self.has_view('FramesIndex'):
         visibility = 1
      #filter out roots that are visible
      roots = filter(lambda x,v=visibility: x.visibility >= v, self.__roots)
      #a function generating a link
      other = lambda x: span('root-other', href(rel(origin, x.file), x.label, target=x.target))
      #a function simply printing label
      current = lambda x: span('root-current', x.label)
      # generate the header
      roots = map(lambda x: x.file==origin and current(x) or other(x), roots)
      return div('navigation', string.join(roots, '\n'))+'\n'

   def register_filename(self, filename, view, scope):
      """Registers a file for later production. The first view to register
      the filename gets to keep it."""

      filename = str(filename)
      if not self.__files.has_key(filename):
         self.__files[filename] = (view, scope)

   def filename_info(self, filename):
      """Returns information about a registered file, as a (view,scope)
      pair. Will return None if the filename isn't registered."""

      return self.__files.get(filename)