SourceChildren_Macro.py

Back to SourceChildren macro

Remark/Macros/

# -*- coding: utf-8 -*-

# Description: SourceChildren macro
# Detail: Generates links to source code children.

from __future__ import print_function

import os.path
import string

from Remark.Macro_Registry import registerMacro
from Remark.FileSystem import unixRelativePath, escapeMarkdown
from Remark.FileSystem import withoutFileExtension, markdownRegion
from Remark.DocumentType_Registry import outputDocumentName

class SourceChildren_Macro(object):
    def name(self):
        return 'SourceChildren'

    def expand(self, parameter, remark):
        document = remark.document
        documentTree = remark.documentTree
        scope = remark.scopeStack.top()

        # Variables
        self.rootName = scope.getString('SourceChildren.root_document', document.fileName)
        self.className = scope.getString('SourceChildren.class_name', 'SourceChildren')
        self.title = scope.getString('SourceChildren.title', 'Files')

        text = []

        def prefixOf(left, right):
            return str.find(withoutFileExtension(right), 
                               withoutFileExtension(left)) == 0

        def same(left, right):
            return withoutFileExtension(left) == withoutFileExtension(right)

        # Gather the list of source files.

        sortedMap = [x for x in document.childSet.values() 
                    if (x.tagString('document_type') == 'CppCodeView' or
                        x.tagString('document_type') == 'CodeView')]

        if len(sortedMap) == 0:
            # There are no source files.
            return text

        # Sort the list alphabetically w.r.t. the relative file names.

        sortedMap.sort(key = lambda x: x.relativeName)

        # Given an alphabetically sorted list of source files,
        # group all files that have the same name without
        # extension. The group shares the same description.

        outputDirectory = document.relativeDirectory
        beginIndex = 0
        groupSet = []
        description = ''
        detail = ''
        for i in range(0, len(sortedMap)):
            sourceDocument = sortedMap[i]
            reference = sortedMap[beginIndex]

            # Note that it is really the _description_ we want to
            # use here, rather than the link description.
            desc = sourceDocument.tagString('description')
            if desc != '':
                # If there are multiple descriptions, one is chosen
                # arbitrarily and a warning is emitted.
                if description != '':
                    message = ['Multiple descriptions for a document-group.',
                              'Current: ' + description,
                              'New: ' + desc]
                    remark.reportWarning(message, 'ambiguous-input')
                description = desc

            det = sourceDocument.tagString('detail')
            if det != '':
                # If there are multiple details, one is chosen
                # arbitrarily and a warning is emitted.
                if detail != '':
                    message = ['Multiple details for a document-group. ',
                              'Current: ' + detail,
                              'New: ' + det]
                    remark.reportWarning(message, 'ambiguous-input')
                detail = det

            if i == len(sortedMap) - 1 or not same(sortedMap[i + 1].relativeName, reference.relativeName):
                groupSet.append([description, detail, sortedMap[beginIndex : i + 1]])
                beginIndex = i + 1
                description = ''
                detail = ''

        # If a group does not have a description, it can be
        # joined together with a preceding described group, on the condition 
        # that without extensions the names in the preceding group are 
        # prefixes of the names in the following group.
        i = 1
        while i < len(groupSet):
            joined = False;
            for j in range(i - 1, -1, -1):
                if groupSet[i][0] == '' and prefixOf(groupSet[j][2][0].relativeName, groupSet[i][2][0].relativeName):
                    #print('Join', groupSet[j][2][0].relativeName, groupSet[i][2][0].relativeName)
                    groupSet[j][2] += groupSet[i][2]
                    groupSet[i : i + 1] = []
                    joined = True;
                    break;
            if not joined: 
                i += 1

        # Set default descriptions for those
        # groups that do not have a description.
        for group in groupSet:
            if group[0] == '':
                message = ['Description missing for the document-group']
                for child in group[2]:
                    message.append(child.fileName)
                remark.reportWarning(message, 'missing-description')

        # Find the first group that has no description.
        i = 0
        while i < len(groupSet):
            if groupSet[i][0] == '':
                break
            i += 1

        # Join all groups without a description to the
        # first such group.
        if i < len(groupSet):
            j = i + 1
            while j < len(groupSet):
                if groupSet[j][0] == '':
                    groupSet[i][2] += groupSet[j][2]
                    groupSet[j : j + 1] = []
                else:
                    j += 1;

        # Output the groups
        #for group in groupSet:
        #    for child in group[2]:
        #        print(child.relativeName)
        #    print('')

        # Order the groups in alphabetical order w.r.t.
        # their descriptions. 
        groupSet.sort(key = lambda x: x[0])

        # Move the unnamed group to the end.
        if len(groupSet) > 0 and groupSet[0][0] == '':
            groupSet.append(groupSet[0])
            groupSet[0 : 1] = []

        defaultDescription = '?';

        # Output the links in the groups together
        # with a description for the group.

        # The title is output only if its non-empty.
        if len(self.title.strip()) > 0:
            # Output the title
            text.append('')
            text.append(self.title)
            text.append('---')

        text.append('')
        for group in groupSet:
            # Output description for the group.
            description = group[0]
            if description == '':
                description = defaultDescription

            text.append('')
            text.append('### ' + description)
            text.append('')

            # Output detailed description for the group
            # if it's present.
            detail = group[1]
            if detail != '':
                text.append('')
                text.append('_' + detail + '_')
                text.append('')

            # Output the links in the group as a list.
            for child in group[2]:
                text.append('* ' + remark.remarkLink(escapeMarkdown(child.fileName),
                            document, child))

        return markdownRegion(
            remark.convert(text), 
            {'class' : self.className});

    def expandOutput(self):
        return False

    def htmlHead(self, remark):
        return []                

    def postConversion(self, remark):
        None

registerMacro('SourceChildren', SourceChildren_Macro())