--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/eric7/E5Gui/EricGenericDiffHighlighter.py Sat May 22 18:51:46 2021 +0200 @@ -0,0 +1,172 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2015 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a syntax highlighter for diff outputs. +""" + +import re + +from PyQt6.QtGui import QSyntaxHighlighter, QTextCharFormat, QFont + +import Preferences + + +def TERMINAL(pattern): + """ + Function to mark a pattern as the final one to search for. + + @param pattern pattern to be marked (string) + @return marked pattern (string) + """ + return "__TERMINAL__:{0}".format(pattern) + +# Cache the results of re.compile for performance reasons +_REGEX_CACHE = {} + + +class EricGenericDiffHighlighter(QSyntaxHighlighter): + """ + Class implementing a generic diff highlighter. + """ + def __init__(self, doc): + """ + Constructor + + @param doc reference to the text document (QTextDocument) + """ + super().__init__(doc) + + self.regenerateRules() + + def __initColours(self): + """ + Private method to initialize the highlighter colours. + """ + self.textColor = Preferences.getDiffColour("TextColor") + self.addedColor = Preferences.getDiffColour("AddedColor") + self.removedColor = Preferences.getDiffColour("RemovedColor") + self.replacedColor = Preferences.getDiffColour("ReplacedColor") + self.contextColor = Preferences.getDiffColour("ContextColor") + self.headerColor = Preferences.getDiffColour("HeaderColor") + self.whitespaceColor = Preferences.getDiffColour("BadWhitespaceColor") + + def createRules(self, *rules): + """ + Public method to create the highlighting rules. + + @param rules set of highlighting rules (list of tuples of rule + pattern (string) and highlighting format (QTextCharFormat)) + """ + for _idx, ruleFormat in enumerate(rules): + rule, formats = ruleFormat + terminal = rule.startswith(TERMINAL('')) + if terminal: + rule = rule[len(TERMINAL('')):] + try: + regex = _REGEX_CACHE[rule] + except KeyError: + regex = _REGEX_CACHE[rule] = re.compile(rule) + self._rules.append((regex, formats, terminal)) + + def formats(self, line): + """ + Public method to determine the highlighting formats for a line of + text. + + @param line text line to be highlighted (string) + @return list of matched highlighting rules (list of tuples of match + object and format (QTextCharFormat)) + """ + matched = [] + for rx, formats, terminal in self._rules: + match = rx.match(line) + if not match: + continue + matched.append([match, formats]) + if terminal: + return matched + + return matched + + def makeFormat(self, fg=None, bg=None, bold=False): + """ + Public method to generate a format definition. + + @param fg foreground color (QColor) + @param bg background color (QColor) + @param bold flag indicating bold text (boolean) + @return format definiton (QTextCharFormat) + """ + font = Preferences.getEditorOtherFonts("MonospacedFont") + charFormat = QTextCharFormat() + charFormat.setFontFamilies([font.family()]) + charFormat.setFontPointSize(font.pointSize()) + + if fg: + charFormat.setForeground(fg) + + if bg: + charFormat.setBackground(bg) + + if bold: + charFormat.setFontWeight(QFont.Weight.Bold) + + return charFormat + + def highlightBlock(self, text): + """ + Public method to highlight a block of text. + + @param text text to be highlighted (string) + """ + formats = self.formats(text) + if not formats: + # nothing matched + self.setFormat(0, len(text), self.normalFormat) + return + + for match, formatStr in formats: + start = match.start() + groups = match.groups() + + # No groups in the regex, assume this is a single rule + # that spans the entire line + if not groups: + self.setFormat(0, len(text), formatStr) + continue + + # Groups exist, rule is a tuple corresponding to group + for groupIndex, group in enumerate(groups): + if not group: + # empty match + continue + + # allow None as a no-op format + length = len(group) + if formatStr[groupIndex]: + self.setFormat(start, start + length, + formatStr[groupIndex]) + start += length + + def regenerateRules(self): + """ + Public method to initialize or regenerate the syntax highlighter rules. + """ + self.normalFormat = self.makeFormat() + + self.__initColours() + + self._rules = [] + self.generateRules() + + def generateRules(self): + """ + Public method to generate the rule set. + + Note: This method must me implemented by derived syntax + highlighters. + """ + pass