File: Synopsis/Formatters/HTML/Fragments/DeclarationFormatter.py
  1#
  2# Copyright (C) 2000 Stephen Davies
  3# Copyright (C) 2000 Stefan Seefeld
  4# All rights reserved.
  5# Licensed to the public under the terms of the GNU LGPL (>= 2),
  6# see the file COPYING for details.
  7#
  8
  9from Synopsis import ASG
 10from Synopsis.Formatters.HTML.Fragment import Fragment
 11from Synopsis.Formatters.HTML.Tags import *
 12from SourceLinker import SourceLinker
 13from XRefLinker import XRefLinker
 14
 15class DeclarationFormatter(Fragment):
 16    """Base class for SummaryFormatter and DetailFormatter.
 17    
 18    The two classes SummaryFormatter and DetailFormatter are actually
 19    very similar in operation, and so most of their methods are defined here.
 20    Both of them print out the definition of the declarations, including type,
 21    parameters, etc. Some things such as exception specifications are only
 22    printed out in the detailed version.
 23    """
 24
 25    def register(self, formatter):
 26
 27        super(DeclarationFormatter, self).register(formatter)
 28        if self.processor.has_view('XRef'):
 29            self.xref = XRefLinker()
 30            self.xref.register(formatter)
 31        else:
 32            self.xref = None
 33        if self.processor.has_view('Source'):
 34            self.source = SourceLinker()
 35            self.source.register(formatter)
 36        else:
 37            self.source = None
 38
 39    def format_parameters(self, parameters):
 40        """Returns formatted string for the given parameter list."""
 41
 42        return ', '.join([self.format_parameter(p) for p in parameters])
 43
 44    def format_declaration(self, decl):
 45        """The default is to return no type and just the declarations name for
 46        the name."""
 47
 48        return div('synopsis', self.label(decl.name))
 49
 50    def format_macro(self, decl):
 51        """"""
 52
 53        chunk = div('synopsis', self.label(decl.name))
 54        if self.xref: chunk += ' %s'%div('xref', self.xref.format_macro(decl))
 55        if self.source: chunk += ' %s'%div('source', self.source.format_macro(decl))
 56        return chunk
 57
 58    def format_forward(self, decl):
 59
 60        # treat template syntax like a premodifier
 61        if decl.template:
 62            templ = 'template <%s>'%(self.format_parameters(decl.template.parameters),)
 63            templ = div('template', templ)
 64            type = '%s %s'%(templ, decl.type)
 65        else:
 66            type = decl.type
 67
 68        if decl.specializations:
 69            # Treat it like a (non-forward declared) class template.
 70            name  = self.format_scope(decl)
 71        else:
 72            name = self.label(decl.name)
 73        chunk = div('synopsis', type + ' ' + name)
 74        if self.xref: chunk += ' %s'%div('xref', self.xref.format_forward(decl))
 75        if self.source: chunk += ' %s'%div('source', self.source.format_forward(decl))
 76        return chunk
 77
 78    def format_group(self, decl):
 79
 80        return ''
 81
 82    def format_scope(self, decl):
 83        """Scopes have their own views, so return a reference to it."""
 84
 85        name = decl.name
 86        link = rel(self.formatter.filename(),
 87                   self.directory_layout.scope(name))
 88        return href(link, escape(name[-1]))
 89
 90    def format_module(self, decl):
 91
 92        return self.format_scope(decl)
 93
 94    def format_meta_module(self, decl):
 95
 96        return self.format_module(decl)
 97
 98    def format_class(self, decl):
 99
100        chunk = div('synopsis', decl.type + ' ' + self.format_scope(decl))
101        if self.xref: chunk += ' %s'%div('xref', self.xref.format_class(decl))
102        if self.source: chunk += ' %s'%div('source', self.source.format_class(decl))
103        return chunk
104
105    def format_class_template(self, decl):
106
107        # treat template syntax like a premodifier
108        if decl.template:
109            templ = 'template <%s>'%(self.format_parameters(decl.template.parameters),)
110            templ = div('template', templ)
111            type = '%s %s'%(templ, decl.type)
112
113        name  = self.format_scope(decl)
114        chunk = div('synopsis', type + ' ' + name)
115        if self.xref: chunk += ' %s'%div('xref', self.xref.format_class(decl))
116        if self.source: chunk += ' %s'%div('source', self.source.format_class(decl))
117        return chunk
118
119
120    def format_typedef(self, decl):
121        "(typedef type, typedef name)"
122
123        type = self.format_type(decl.alias)
124        chunk = '%s'%div('synopsis', type + ' ' + self.label(decl.name))
125        if self.xref: chunk += ' %s'%div('xref', self.xref.format_class(decl))
126        if self.source: chunk += ' %s'%div('source', self.source.format_class(decl))
127        return chunk
128
129    def format_enumerator(self, decl):
130        """This is only called by formatEnum"""
131
132        self.format_declaration(decl)
133
134    def format_enum(self, decl):
135        "(enum name, list of enumerator names)"
136
137        type = self.label(decl.name)
138        name = ', '.join([e.name[-1] for e in decl.enumerators])
139        chunk = '%s'%div('synopsis', type + ' ' + name)
140        if self.xref: chunk += ' %s'%div('xref', self.xref.format_class(decl))
141        if self.source: chunk += ' %s'%div('source', self.source.format_class(decl))
142        return chunk
143
144    def format_variable(self, decl):
145
146        # TODO: deal with sizes
147        type = self.format_type(decl.vtype)
148        chunk = '%s'%div('synopsis', type + ' ' + self.label(decl.name))
149        if self.xref: chunk += ' %s'%div('xref', self.xref.format_class(decl))
150        if self.source: chunk += ' %s'%div('source', self.source.format_class(decl))
151        return chunk
152
153    def format_const(self, decl):
154        "(const type, const name = const value)"
155
156        type = self.format_type(decl.ctype)
157        name = self.label(decl.name) + " = " + escape(decl.value)
158        chunk = '%s'%div('synopsis', type + ' ' + name)
159        if self.xref: chunk += ' %s'%div('xref', self.xref.format_class(decl))
160        if self.source: chunk += ' %s'%div('source', self.source.format_class(decl))
161        return chunk
162
163    def format_function(self, decl):
164        "(return type, func + params + exceptions)"
165
166        premod = self.format_modifiers(decl.premodifier)
167        type = self.format_type(decl.return_type)
168        name = self.label(decl.name, decl.real_name)
169        # Special C++ functions  TODO: maybe move to a separate ASG formatter...
170        if decl.file.annotations['language'] == 'C++' and len(decl.real_name)>1:
171            lt = decl.real_name[-2].find('<') # check whether this is a template
172            sname = lt == -1 and decl.real_name[-2] or decl.real_name[-2][:lt]
173            if decl.real_name[-1] == sname: type = '<i>constructor</i>'
174            elif decl.real_name[-1] == '~'+sname: type = '<i>destructor</i>'
175            elif decl.real_name[-1] == '(conversion)': name = '(%s)'%type
176        params = self.format_parameters(decl.parameters)
177        postmod = self.format_modifiers(decl.postmodifier)
178        raises = self.format_exceptions(decl)
179        # prepend the type by the premodifier(s)
180        type = '%s %s'%(premod,type)
181        # Prevent linebreaks on shorter lines
182        if len(type) < 60:
183            type = replace_spaces(type)
184        if decl.type == 'attribute': name = '%s %s %s'%(name, postmod, raises)
185        else: name = '%s(%s) %s %s'%(name, params, postmod, raises)
186
187        chunk = '%s'%div('synopsis', type + ' ' + name)
188        if self.xref: chunk += ' %s'%div('xref', self.xref.format_class(decl))
189        if self.source: chunk += ' %s'%div('source', self.source.format_class(decl))
190
191        return chunk
192
193    def format_function_template(self, decl):
194        "(return type, func + params + exceptions)"
195
196        premod = self.format_modifiers(decl.premodifier)
197        type = self.format_type(decl.return_type)
198        name = self.label(decl.name, decl.real_name)
199        # Special C++ functions  TODO: maybe move to a separate ASG formatter...
200        if decl.file.annotations['language'] == 'C++' and len(decl.real_name)>1:
201            lt = decl.real_name[-2].find('<') # check whether this is a template
202            sname = lt == -1 and decl.real_name[-2] or decl.real_name[-2][:lt]
203            if decl.real_name[-1] == sname: type = '<i>constructor</i>'
204            elif decl.real_name[-1] == '~'+sname: type = '<i>destructor</i>'
205            elif decl.real_name[-1] == '(conversion)': name = '(%s)'%type
206        params = self.format_parameters(decl.parameters)
207        postmod = self.format_modifiers(decl.postmodifier)
208        raises = self.format_exceptions(decl)
209        # prepend the type by the premodifier(s)
210        type = '%s %s'%(premod,type)
211        # Prevent linebreaks on shorter lines
212        if len(type) < 60:
213            type = replace_spaces(type)
214        if decl.type == 'attribute': name = '%s %s %s'%(name, postmod, raises)
215        else: name = '%s(%s) %s %s'%(name, params, postmod, raises)
216        # treat template syntax like a premodifier
217        if decl.template:
218            templ = 'template &lt;%s&gt;'%(self.format_parameters(decl.template.parameters),)
219            templ = div('template', templ)
220            type = '%s %s'%(templ, type)
221
222        chunk = '%s'%div('synopsis', type + ' ' + name)
223        if self.xref: chunk += ' %s'%div('xref', self.xref.format_class(decl))
224        if self.source: chunk += ' %s'%div('source', self.source.format_class(decl))
225
226        return chunk
227
228    def format_operation(self, decl):
229
230        # Default operation is same as function, and quickest way is to assign:
231        return self.format_function(decl)
232
233    def format_operation_template(self, decl):
234
235        # Default operation is same as function, and quickest way is to assign:
236        return self.format_function_template(decl)
237
238    def format_parameter(self, parameter):
239        """Returns one string for the given parameter"""
240
241        text = []
242        # Premodifiers
243        text.extend([span('keyword', escape(m)) for m in parameter.premodifier])
244        # Param Type
245        id_holder = [parameter.name]
246        typestr = self.format_type(parameter.type, id_holder)
247        if typestr: text.append(typestr)
248        # Postmodifiers
249        text.extend([span('keyword', escape(m)) for m in parameter.postmodifier])
250        # Param name
251        if id_holder and len(parameter.name) != 0:
252            text.append(span('variable', escape(parameter.name)))
253        # Param value
254        if len(parameter.value) != 0:
255            text.append('= %s'%span('value', escape(parameter.value)))
256        return ' '.join(text)
257
258
259class DeclarationSummaryFormatter(DeclarationFormatter):
260    """Derives from BaseStrategy to provide summary-specific methods.
261    Currently the only one is format_exceptions"""
262
263    def format_exceptions(self, oper):
264        """Returns a reference to the detail if there are any exceptions."""
265
266        if len(oper.exceptions):
267            return self.reference(oper.name, ' raises')
268        return ''
269
270
271class DeclarationDetailFormatter(DeclarationFormatter):
272    """Provide detail-specific Declaration formatting."""
273
274    def format_exceptions(self, oper):
275        """Prints out the full exception spec"""
276
277        if len(oper.exceptions):
278            raises = span('keyword', 'raises')
279            exceptions = []
280            for exception in oper.exceptions:
281                exceptions.append(self.reference(exception.name))
282            exceptions = span('raises', ', '.join(exceptions))
283            return '%s (%s)'%(raises, exceptions)
284        else:
285            return ''
286
287    def format_enum(self, enum):
288
289        name = span('keyword', 'enum') + ' ' + self.label(enum.name)
290        enumors = ''.join([self.format_enumerator(e) for e in enum.enumerators])
291        return name + div('enum', enumors)
292
293    def format_enumerator(self, enumerator):
294
295        text = self.label(enumerator.name)
296        if len(enumerator.value):
297            value = ' = ' + span('value', escape(enumerator.value))
298        else:
299            value = ''
300        doc = self.processor.documentation.details(enumerator, self.view)
301        return div('enumerator','%s%s%s'%(text, value, doc))
302