Skip to content
Snippets Groups Projects
Forked from nomad-lab / python-common
324 commits behind the upstream repository.
match_highlighter.py 2.35 KiB
from builtins import range
from builtins import object
import logging
from   operator import itemgetter
import re
logger = logging.getLogger("nomadcore.match_highlighter")
class ANSI(object):
    RESET='\033[0m'
    RESET_COLOR='\033[39;49m'
    BEGIN_INVERT='\033[7m'
    END_INVERT='\033[27m'
    FG_RED='\033[31m'

class MatchHighlighter(object):
    """ANSI-Color highlighting for capturing groups in regex matches"""

    def __init__(self, highlightMode):
        self.highlightMode=highlightMode
    
    def highlight(self,match,lineOrig):
        # shortcut: there is nothing to highlight
        if (not match) or (self.highlightMode==0):
            return(lineOrig)
        # list with the highlight switching events
        # tuples: (pos, event, ansi)
        #   event:
        #     matchstart=-1
        #          close= 0
        #           open= 1
        #       matchend= 2
        #    ansi: escape code
        ansiSwitch = list()
        # mark whole area of match by fg/bg inversion
        ansiSwitch.append([match.start(),-1,ANSI.BEGIN_INVERT])
        ansiSwitch.append([match.end(),   2,ANSI.END_INVERT])
        # capture groups
        ngroups=len(match.groups())
        for groupi in range(1,ngroups+1):
            s,e=match.span(groupi)
            # logger.error("i:%d %3d %3d '%s'" % (groupi, s, e,
            #    match.group(groupi)))
            if match.group(groupi) is not None:
                ansiSwitch.append([s,1,ANSI.FG_RED])
                ansiSwitch.append([e,0,ANSI.RESET_COLOR])
        # sort by position, then by event
        ansiSwitch=sorted(ansiSwitch, key=itemgetter(0,1))
        if ansiSwitch[-1][0]>len(lineOrig):
            raise Exception('line_exceed',"match exceeds line")
        # append line remainder if necessary
        if ansiSwitch[-1][0]<len(lineOrig):
            ansiSwitch.append([len(lineOrig),3,''])
        # logger.error(ansiSwitch)
        # construct highlighted Line
        lastPos=0
        highlightedLine=''
        for i in range(len(ansiSwitch)):
            if ansiSwitch[i][0]>lastPos:
                highlightedLine+=lineOrig[lastPos:ansiSwitch[i][0]]
                lastPos=ansiSwitch[i][0]
            highlightedLine+=ansiSwitch[i][2]
        # always insert reset at end or before final newline
        highlightedLine=re.sub(r"(\n)$",ANSI.RESET + "\g<1>",highlightedLine)
        return(highlightedLine)