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