Skip to content
Snippets Groups Projects
annotator.py 3.81 KiB
import logging
import re
from nomadcore.match_highlighter import MatchHighlighter, ANSI


LOGGER = logging.getLogger(__name__)


RE_EMPTY = re.compile(r"^\s*$")


class Annotator(object):
    def __init__(self, annotateFilename=None, coverageIgnore=None,
                 formatNameWidth=15, formatSourceWidth=20):
        self.matchHighlighter = MatchHighlighter()
        self.coverageIgnore = coverageIgnore
        if annotateFilename is None:
            self.annotateFile = None
        else:
            LOGGER.info("writing annotated input to " + annotateFilename)
            self.annotateFile = open(annotateFilename, 'w')
            self.annotateFile.write(
                "# this file contains ANSI colors; use 'less -R' to view it\n")
        self._formatNameWidth = formatNameWidth
        self._formatSourceWidth = formatSourceWidth
        self._update_annotate_format()
        self.counter = {}
        for counter in ['total', 'ignore', 'match', 'partial', 'unmatched']:
            self.counter[counter] = 0

    def _update_annotate_format(self):
        self._annotate_format = '%%%ds:%%04d %%%ds %%9s|%%s' % (
            self._formatSourceWidth, self._formatNameWidth)

    def set_formatSourceWidth(self, formatSourceWidth):
        self._formatSourceWidth = formatSourceWidth
        self._update_annotate_format()

    def set_formatNameWidth(self, formatNameWidth):
        self._formatNameWidth = formatNameWidth
        self._update_annotate_format()

    def annotate(self, match, line, matcher, targetStartEnd):
        if not self.annotateFile:
            return 1
        # classify match
        full = match and match.start() == 0 and match.end() == len(line)
        local_ignore = False
        global_ignore = False
        matcher_does_nothing = False
        if match:
            if matcher.coverageIgnore:
                local_ignore = True
            elif matcher.does_nothing:
                matcher_does_nothing = True
        else:
            m2 = RE_EMPTY.match(line)
            if m2:
                global_ignore = True
                match = m2
            elif self.coverageIgnore is not None:
                m2 = self.coverageIgnore.match(line)
                if m2:
                    global_ignore = True
                    match = m2

        # update counters
        self.counter['total'] += 1
        if local_ignore or global_ignore:
            self.counter['ignore'] += 1
        elif match:
            if full:
                self.counter['match'] += 1
            else:
                self.counter['partial'] += 1
        else:
            self.counter['unmatched'] += 1

        # setup match label
        matchlabel = ''
        if local_ignore:
            matchlabel = 'l_ign'
        elif global_ignore:
            matchlabel = 'g_ign'
        elif match:
            if matcher_does_nothing:
                matchlabel='n_'
            if not full:
                matchlabel += 'p_'
            matchlabel += 'end' if targetStartEnd else 'start'
        else:
            matchlabel = 'no'
        # highlight line
        if local_ignore or global_ignore:
            highlighted = self.matchHighlighter.highlight(
                match, line, linecolor=ANSI.FG_BLUE)
        elif matcher_does_nothing:
            highlighted = self.matchHighlighter.highlight(
                match, line, linecolor=ANSI.FG_MAGENTA)
        elif match:
            highlighted = self.matchHighlighter.highlight(match, line)
        else:
            highlighted = line
        # set matcher name
        if matcher.name:
            name=matcher.name[-self._formatNameWidth:]
        else:
            name='UNNAMED'
        defFile=matcher.defFile[-self._formatSourceWidth:]
        self.annotateFile.write(self._annotate_format % (
            defFile, matcher.defLine, name,
            matchlabel, highlighted,
        ))