File: Synopsis/Formatters/HTML/Markup/__init__.py
  1#
  2# Copyright (C) 2006 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"""Markup formatters."""
  8
  9from Synopsis.Processor import Parametrized, Parameter
 10from Synopsis import ASG
 11from Synopsis.QualifiedName import *
 12from Synopsis.Formatters.HTML.Tags import escape
 13import re
 14
 15class Struct:
 16
 17    def __init__(self, summary = '', details = ''):
 18        self.summary = summary
 19        self.details = details
 20
 21    def has_details(self):
 22
 23        return self.summary and self.summary != self.details
 24
 25class Formatter(Parametrized):
 26    """Interface class that takes a 'doc' annotation and formats its
 27    text. Markup-specific subclasses should provide appropriate format methods."""
 28
 29    def init(self, processor):
 30
 31        self.processor = processor
 32
 33    def format(self, decl, view):
 34        """Format the declaration's documentation.
 35        @param view the View to use for references and determining the correct
 36        relative filename.
 37        @param decl the declaration
 38        @returns Struct containing summary / details pair.
 39        """
 40
 41        doc = decl.annotations.get('doc')
 42        text = doc and escape(doc.text) or ''
 43        m = re.match(r'(\s*[\w\W]*?\.)(\s|$)', text)
 44        summary = m and m.group(1) or ''
 45        return Struct(summary, text)
 46
 47    def lookup_symbol(self, symbol, scope):
 48        """Given a symbol and a scope, returns an URL.
 49        Various methods are tried to resolve the symbol. First the
 50        parameters are taken off, then we try to split the symbol using '.' or
 51        '::'. The params are added back, and then we try to match this scoped
 52        name against the current scope. If that fails, then we recursively try
 53        enclosing scopes.
 54        """
 55
 56        # Remove params
 57        index = symbol.find('(')
 58        if index >= 0:
 59            params = symbol[index:]
 60            symbol = symbol[:index]
 61        else:
 62            params = ''
 63        if '.' in symbol:
 64            symbol = QualifiedPythonName(symbol.split('.'))
 65        else:
 66            symbol = QualifiedCxxName(symbol.split('::'))
 67        # Add params back
 68        symbol = symbol[:-1] + (symbol[-1] + params,)
 69        # Find in all scopes
 70        while 1:
 71            entry = self._lookup_symbol_in(symbol, scope)
 72            if entry:
 73                return entry.link
 74            if len(scope) == 0: break
 75            scope = scope[:-1]
 76        # Not found
 77        return None
 78
 79
 80    def _lookup_symbol_in(self, symbol, scope):
 81
 82        paren = symbol[-1].find('(')
 83        if paren != -1:
 84            return self._find_method_entry(symbol[-1], scope + symbol[:-1])
 85        else:
 86            return self.processor.toc.lookup(scope + symbol)
 87
 88
 89    def _find_method_entry(self, name, scope):
 90
 91        try:
 92            scope = self.processor.ir.asg.types[scope]
 93        except KeyError:
 94            return None
 95        if not isinstance(scope, ASG.DeclaredTypeId):
 96            return None
 97        scope = scope.declaration
 98        if not isinstance(scope, ASG.Scope):
 99            return None
100        # For now disregard parameters during lookup.
101        name = name[:name.find('(')]
102        for d in scope.declarations:
103            if isinstance(d, ASG.Function):
104                if d.real_name[-1] == name:
105                    return self.processor.toc.lookup(d.name)
106        return None
107