Beispiel #1
0
    def highlightBlock(self, p_str):
        # Skip comments!
        if "cl-comment" in p_str:
            return

        for exp, index, format in self.rules:
            iterator = exp.globalMatch(p_str)
            while iterator.hasNext():
                match = iterator.next()
                self.setFormat(match.capturedStart() + index,
                               match.capturedLength() - index, format)

        for pred in self.predicates:
            expression = QRegularExpression(pred[0] + " ")
            iterator = expression.globalMatch(p_str)
            while iterator.hasNext():
                match = iterator.next()
                line = match.captured()
                self.setFormat(match.capturedStart(), len(line),
                               self.format_predicate)

        for func in self.functions:
            expression = QRegularExpression(func[0] + " ")
            iterator = expression.globalMatch(p_str)
            while iterator.hasNext():
                match = iterator.next()
                line = match.captured()
                self.setFormat(match.capturedStart(), len(line),
                               self.format_function)
Beispiel #2
0
 def search_highlight(self):
     """The function that will be called whenever the
     search area is submitted. It will search within
     the text_field and highlights matches."""
     cursor = self.text_field.textCursor()
     char_format = QTextCharFormat()
     cursor.select(QTextCursor.Document)
     cursor.mergeCharFormat(char_format)
     cursor.clearSelection()
     char_format.setBackground(QBrush(QColor('yellow')))
     regex = QRegularExpression(self.ui.search_area.text())
     matches = regex.globalMatch(self.text_field.toPlainText())
     _matches = []
     while matches.hasNext():
         _matches.append(matches.next())
     self.search_matches = _matches
     self.ui.search_matches_label.setText('Matches: ' +
                                          str(len(self.search_matches)))
     self.ui.search_progress_bar.setRange(0, len(self.search_matches))
     if len(self.search_matches) > 100:
         self.ui.search_progress_bar.show()
     match_count = 1
     for match in self.search_matches:
         if match_count > 1000:
             # TODO: implement proper handling of > 1000 matches
             break
         self.ui.search_progress_bar.setValue(match_count)
         match_count += 1
         cursor.setPosition(match.capturedStart())
         cursor.setPosition(match.capturedEnd(), QTextCursor.KeepAnchor)
         cursor.mergeCharFormat(format)
     self.ui.search_progress_bar.hide()
Beispiel #3
0
 def highlightBlock(self, text):
     regExp = QRegularExpression("[0-9]")
     matchIterator = regExp.globalMatch(text)
     while matchIterator.hasNext():
         match = matchIterator.next()
         self.setFormat(match.capturedStart(), match.capturedLength(),
                        self.numberFormat)
    def highlight(self, regex: QRegularExpression, color: QColor = QColor(Qt.yellow)):
        if regex.pattern() == "":
            return

        self.moveCursor(QTextCursor.Start)
        matches = []
        it: QRegularExpressionMatchIterator = regex.globalMatch(self.toPlainText())
        while it.hasNext():
            match: QRegularExpressionMatch = it.next()
            if not match.hasMatch():
                continue
            begin = match.capturedStart()
            end = match.capturedEnd()

            matchSelection: QTextEdit.ExtraSelection = QTextEdit.ExtraSelection()
            fmt: QTextCharFormat = matchSelection.format
            fmt.setBackground(color)

            matchSelection.cursor = self.textCursor()
            matchSelection.cursor.setPosition(begin, QTextCursor.MoveAnchor)
            matchSelection.cursor.setPosition(end, QTextCursor.KeepAnchor)
            matches.append(matchSelection)

        self.setExtraSelections(matches)
        self.moveCursor(QTextCursor.Start)
Beispiel #5
0
 def highlightBlock(self, text):
     for pattern, fmt in self.highlightingRules:
         regex = QRegularExpression(pattern)
         i = regex.globalMatch(text)
         while i.hasNext():
             match = i.next()
             start = match.capturedStart()
             length = match.capturedLength()
             self.setFormat(start, length, fmt)
     self.setCurrentBlockState(0)
Beispiel #6
0
 def highlightBlock(self, text):
     for pattern, fmt in self.highlightingRules:
         regex = QRegularExpression(pattern)
         i = regex.globalMatch(text)
         while i.hasNext():
             match = i.next()
             start = match.capturedStart()
             length = match.capturedLength()
             self.setFormat(start, length, fmt)
     self.setCurrentBlockState(0)
Beispiel #7
0
    def highlightBlock(self, text):
        highlightFormat = QTextCharFormat()
        highlightFormat.setForeground(Qt.red)
        highlightFormat.setBackground(Qt.black)
        highlightFormat.setFontWeight(QFont.Bold)

        regex = QRegularExpression(self.query)
        it = regex.globalMatch(text)
        while(it.hasNext()):
            match = it.next()
            self.setFormat(match.capturedStart(),
                           match.capturedLength(), highlightFormat)
Beispiel #8
0
class Highlighter(QSyntaxHighlighter):
    def __init__(self, document):
        super(Highlighter, self).__init__(document)
        color = QColor()
        color.setNamedColor('yellow')
        self.format = QTextCharFormat()
        self.format.setBackground(color)
        self.focousArea = None

        fcolor = QColor()
        fcolor.setNamedColor('magenta')
        self.focousFormat = QTextCharFormat()
        self.focousFormat.setBackground(fcolor)

        white = QColor()
        white.setNamedColor('white')
        self.normalFormat = QTextCharFormat()
        self.normalFormat.setBackground(white)

        self.expression = QRegularExpression()

    def setExpression(self, exp, caseSensitive, wholeWord):
        if wholeWord:
            exp = "\\b" + exp + "\\b"
            print(exp)
        if caseSensitive:
            self.expression = QRegularExpression(exp)
        else:
            self.expression = QRegularExpression(
                exp, QRegularExpression.CaseInsensitiveOption)

    def currentFocoused(self, start, size):
        if self.focousArea:
            self.setFormat(self.focousArea[0], self.focousArea[1],
                           self.normalFormat)
        self.focousArea = (start, size)
        self.rehighlight()

    def highlightBlock(self, text):
        matches = self.expression.globalMatch(text)
        while matches.hasNext():
            match = matches.next()
            self.setFormat(match.capturedStart(), match.capturedLength(),
                           self.format)

        if self.focousArea:
            self.setFormat(self.focousArea[0], self.focousArea[1],
                           self.focousFormat)
        # print("starting at ", match.capturedStart())
        # print(f"lenght = {match.capturedLength()}")

    def unformat(self, text):
        self.setFormat(0, len(text), self.normalFormat)
Beispiel #9
0
    def highlightBlock(self, text):
        rExpr = QRegularExpression(self.highStr)
        matches = rExpr.globalMatch(text)

        if not matches.isValid():
            return

        while matches.hasNext():
            match = matches.next()
            if not match.isValid():
                break
            self.setFormat(match.capturedStart(), match.capturedLength(),
                           self.fmt)
    def checkSpelling(self, text):
        if not self.dictionary:
            return

        wordFinder = QRegularExpression("[A-Za-z]+");
        wordIterator = wordFinder.globalMatch(text)
        while wordIterator.hasNext():
            match = wordIterator.next()
            if not self.dictionary.check(match.captured()):
                # update the word's current format
                spellingErrorformat = self.format(match.capturedStart())
                spellingErrorformat.setUnderlineColor(Qt.red)
                spellingErrorformat.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)

                # set the new format
                self.setFormat(match.capturedStart(), match.capturedLength(), spellingErrorformat)
Beispiel #11
0
class _Highlighter(QSyntaxHighlighter):
    def __init__(self, doc, pattern, color):
        super().__init__(doc)
        self._format = QTextCharFormat()
        self._format.setForeground(color)
        words = pattern.split()
        words.sort(key=len, reverse=True)
        pat = "|".join(re.escape(word) for word in words)
        self._expression = QRegularExpression(
            pat, QRegularExpression.CaseInsensitiveOption)

    def highlightBlock(self, text):
        """Override highlightBlock for custom highlighting."""
        match_iterator = self._expression.globalMatch(text)
        while match_iterator.hasNext():
            match = match_iterator.next()
            self.setFormat(match.capturedStart(), match.capturedLength(),
                           self._format)
Beispiel #12
0
    def highlightBlock(self, text):
        for expr in self.exprs:
            fmt = expr[1]
            rExpr = QRegularExpression(expr[0])

            if not rExpr.isValid():
                continue

            matches = rExpr.globalMatch(text)

            if not matches.isValid():
                continue

            while matches.hasNext():
                match = matches.next()
                instr = match.captured('instr')
                if instr:
                    self.setFormat(
                        match.capturedStart(), match.capturedLength(), fmt)
Beispiel #13
0
class GuiDocHighlighter(QSyntaxHighlighter):

    BLOCK_NONE = 0
    BLOCK_TEXT = 1
    BLOCK_META = 2
    BLOCK_TITLE = 4

    def __init__(self, theDoc, theParent):
        QSyntaxHighlighter.__init__(self, theDoc)

        logger.debug("Initialising GuiDocHighlighter ...")
        self.mainConf = nw.CONFIG
        self.theDoc = theDoc
        self.theParent = theParent
        self.theTheme = theParent.theTheme
        self.theIndex = theParent.theIndex
        self.theDict = None
        self.theHandle = None
        self.spellCheck = False
        self.spellRx = None
        self.hRules = []
        self.hStyles = {}

        self.colHead = QColor(0, 0, 0)
        self.colHeadH = QColor(0, 0, 0)
        self.colEmph = QColor(0, 0, 0)
        self.colDialN = QColor(0, 0, 0)
        self.colDialD = QColor(0, 0, 0)
        self.colDialS = QColor(0, 0, 0)
        self.colHidden = QColor(0, 0, 0)
        self.colKey = QColor(0, 0, 0)
        self.colVal = QColor(0, 0, 0)
        self.colSpell = QColor(0, 0, 0)
        self.colTagErr = QColor(0, 0, 0)
        self.colRepTag = QColor(0, 0, 0)

        self.initHighlighter()

        logger.debug("GuiDocHighlighter initialisation complete")

        return

    def initHighlighter(self):
        """Initialise the syntax highlighter, setting all the colour
        rules and building the regexes.
        """
        logger.debug("Setting up highlighting rules")

        self.colHead = QColor(*self.theTheme.colHead)
        self.colHeadH = QColor(*self.theTheme.colHeadH)
        self.colDialN = QColor(*self.theTheme.colDialN)
        self.colDialD = QColor(*self.theTheme.colDialD)
        self.colDialS = QColor(*self.theTheme.colDialS)
        self.colHidden = QColor(*self.theTheme.colHidden)
        self.colKey = QColor(*self.theTheme.colKey)
        self.colVal = QColor(*self.theTheme.colVal)
        self.colSpell = QColor(*self.theTheme.colSpell)
        self.colTagErr = QColor(*self.theTheme.colTagErr)
        self.colRepTag = QColor(*self.theTheme.colRepTag)
        self.colMod = QColor(*self.theTheme.colMod)
        self.colTrail = QColor(*self.theTheme.colEmph)
        self.colTrail.setAlpha(64)

        self.colEmph = None
        if self.mainConf.highlightEmph:
            self.colEmph = QColor(*self.theTheme.colEmph)

        self.hStyles = {
            "header1": self._makeFormat(self.colHead, "bold", 1.8),
            "header2": self._makeFormat(self.colHead, "bold", 1.6),
            "header3": self._makeFormat(self.colHead, "bold", 1.4),
            "header4": self._makeFormat(self.colHead, "bold", 1.2),
            "header1h": self._makeFormat(self.colHeadH, "bold", 1.8),
            "header2h": self._makeFormat(self.colHeadH, "bold", 1.6),
            "header3h": self._makeFormat(self.colHeadH, "bold", 1.4),
            "header4h": self._makeFormat(self.colHeadH, "bold", 1.2),
            "bold": self._makeFormat(self.colEmph, "bold"),
            "italic": self._makeFormat(self.colEmph, "italic"),
            "strike": self._makeFormat(self.colHidden, "strike"),
            "trailing": self._makeFormat(self.colTrail, "background"),
            "nobreak": self._makeFormat(self.colTrail, "background"),
            "dialogue1": self._makeFormat(self.colDialN),
            "dialogue2": self._makeFormat(self.colDialD),
            "dialogue3": self._makeFormat(self.colDialS),
            "replace": self._makeFormat(self.colRepTag),
            "hidden": self._makeFormat(self.colHidden),
            "keyword": self._makeFormat(self.colKey),
            "modifier": self._makeFormat(self.colMod),
            "value": self._makeFormat(self.colVal, "underline"),
        }

        self.hRules = []

        # Trailing Spaces, 2+
        self.hRules.append((r"[ ]{2,}$", {
            0: self.hStyles["trailing"],
        }))

        # Non-Breaking Spaces
        self.hRules.append(
            ("[%s%s]+" % (nwUnicode.U_NBSP, nwUnicode.U_THNBSP), {
                0: self.hStyles["nobreak"],
            }))

        # Quoted Strings
        if self.mainConf.highlightQuotes:
            fmtDbl = self.mainConf.fmtDoubleQuotes
            fmtSng = self.mainConf.fmtSingleQuotes

            # Straight Quotes
            if fmtDbl != ["\"", "\""]:
                self.hRules.append(("(\\B\")(.*?)(\"\\B)", {
                    0: self.hStyles["dialogue1"],
                }))

            # Double Quotes
            dblEnd = "|$" if self.mainConf.allowOpenDQuote else ""
            self.hRules.append(
                (f"(\\B{fmtDbl[0]})(.*?)({fmtDbl[1]}\\B{dblEnd})", {
                    0: self.hStyles["dialogue2"],
                }))

            # Single Quotes
            sngEnd = "|$" if self.mainConf.allowOpenSQuote else ""
            self.hRules.append(
                (f"(\\B{fmtSng[0]})(.*?)({fmtSng[1]}\\B{sngEnd})", {
                    0: self.hStyles["dialogue3"],
                }))

        # Markdown
        self.hRules.append((nwRegEx.FMT_EI, {
            1: self.hStyles["hidden"],
            2: self.hStyles["italic"],
            3: self.hStyles["hidden"],
        }))
        self.hRules.append((nwRegEx.FMT_EB, {
            1: self.hStyles["hidden"],
            2: self.hStyles["bold"],
            3: self.hStyles["hidden"],
        }))
        self.hRules.append((nwRegEx.FMT_ST, {
            1: self.hStyles["hidden"],
            2: self.hStyles["strike"],
            3: self.hStyles["hidden"],
        }))

        # Auto-Replace Tags
        self.hRules.append((r"<(\S+?)>", {
            0: self.hStyles["replace"],
        }))

        # Build a QRegExp for each highlight pattern
        self.rxRules = []
        for regEx, regRules in self.hRules:
            hReg = QRegularExpression(regEx)
            hReg.setPatternOptions(
                QRegularExpression.UseUnicodePropertiesOption)
            self.rxRules.append((hReg, regRules))

        # Build a QRegExp for spell checker
        # Include additional characters that the highlighter should
        # consider to be word separators
        wordSep = r"\-_\+/"
        wordSep += nwUnicode.U_ENDASH
        wordSep += nwUnicode.U_EMDASH
        self.spellRx = QRegularExpression(r"\b[^\s" + wordSep + r"]+\b")
        self.spellRx.setPatternOptions(
            QRegularExpression.UseUnicodePropertiesOption)

        return True

    ##
    #  Setters
    ##

    def setDict(self, theDict):
        """Set the dictionary object for spell check underlines lookup.
        """
        self.theDict = theDict
        return True

    def setSpellCheck(self, theMode):
        """Enable/disable the real time spell checker.
        """
        self.spellCheck = theMode
        return True

    def setHandle(self, theHandle):
        """Set the handle of the currently highlighted document. This is
        needed for the index lookup for validating tags and references.
        """
        self.theHandle = theHandle
        return True

    ##
    #  Methods
    ##

    def rehighlightByType(self, theType):
        """Loop through all blocks and rehighlight those of a given
        content type.
        """
        qDocument = self.document()
        nBlocks = qDocument.blockCount()
        bfTime = time()
        for i in range(nBlocks):
            theBlock = qDocument.findBlockByNumber(i)
            if theBlock.userState() & theType > 0:
                self.rehighlightBlock(theBlock)
        afTime = time()
        logger.debug("Document highlighted in %.3f ms" % (1000 *
                                                          (afTime - bfTime)))
        return

    ##
    #  Highlight Block
    ##

    def highlightBlock(self, theText):
        """Highlight a single block. Prefer to check first character for
        all formats that are defined by their initial characters. This
        is significantly faster than running the regex checks used for
        text paragraphs.
        """
        self.setCurrentBlockState(self.BLOCK_NONE)
        if self.theHandle is None or not theText:
            return

        if theText.startswith("@"):  # Keywords and commands
            self.setCurrentBlockState(self.BLOCK_META)
            tItem = self.theParent.theProject.projTree[self.theHandle]
            isValid, theBits, thePos = self.theIndex.scanThis(theText)
            isGood = self.theIndex.checkThese(theBits, tItem)
            if isValid:
                for n, theBit in enumerate(theBits):
                    xPos = thePos[n]
                    xLen = len(theBit)
                    if isGood[n]:
                        if n == 0:
                            self.setFormat(xPos, xLen, self.hStyles["keyword"])
                        else:
                            self.setFormat(xPos, xLen, self.hStyles["value"])
                    else:
                        kwFmt = self.format(xPos)
                        kwFmt.setUnderlineColor(self.colTagErr)
                        kwFmt.setUnderlineStyle(
                            QTextCharFormat.SpellCheckUnderline)
                        self.setFormat(xPos, xLen, kwFmt)

            # We never want to run the spell checker on keyword/values,
            # so we force a return here
            return

        elif theText.startswith("# "):  # Header 1
            self.setCurrentBlockState(self.BLOCK_TITLE)
            self.setFormat(0, 1, self.hStyles["header1h"])
            self.setFormat(1, len(theText), self.hStyles["header1"])

        elif theText.startswith("## "):  # Header 2
            self.setCurrentBlockState(self.BLOCK_TITLE)
            self.setFormat(0, 2, self.hStyles["header2h"])
            self.setFormat(2, len(theText), self.hStyles["header2"])

        elif theText.startswith("### "):  # Header 3
            self.setCurrentBlockState(self.BLOCK_TITLE)
            self.setFormat(0, 3, self.hStyles["header3h"])
            self.setFormat(3, len(theText), self.hStyles["header3"])

        elif theText.startswith("#### "):  # Header 4
            self.setCurrentBlockState(self.BLOCK_TITLE)
            self.setFormat(0, 4, self.hStyles["header4h"])
            self.setFormat(4, len(theText), self.hStyles["header4"])

        elif theText.startswith("%"):  # Comments
            self.setCurrentBlockState(self.BLOCK_TEXT)
            toCheck = theText[1:].lstrip()
            synTag = toCheck[:9].lower()
            tLen = len(theText)
            cLen = len(toCheck)
            cOff = tLen - cLen
            if synTag == "synopsis:":
                self.setFormat(0, cOff + 9, self.hStyles["modifier"])
                self.setFormat(cOff + 9, tLen, self.hStyles["hidden"])
            else:
                self.setFormat(0, tLen, self.hStyles["hidden"])

        else:  # Text Paragraph
            self.setCurrentBlockState(self.BLOCK_TEXT)
            for rX, xFmt in self.rxRules:
                rxItt = rX.globalMatch(theText, 0)
                while rxItt.hasNext():
                    rxMatch = rxItt.next()
                    for xM in xFmt:
                        xPos = rxMatch.capturedStart(xM)
                        xLen = rxMatch.capturedLength(xM)
                        for x in range(xPos, xPos + xLen):
                            spFmt = self.format(x)
                            if spFmt != self.hStyles["hidden"]:
                                spFmt.merge(xFmt[xM])
                                self.setFormat(x, 1, spFmt)

        if self.theDict is None or not self.spellCheck:
            return

        rxSpell = self.spellRx.globalMatch(theText, 0)
        while rxSpell.hasNext():
            rxMatch = rxSpell.next()
            if not self.theDict.checkWord(rxMatch.captured(0)):
                if rxMatch.captured(0).isupper() or rxMatch.captured(
                        0).isnumeric():
                    continue
                xPos = rxMatch.capturedStart(0)
                xLen = rxMatch.capturedLength(0)
                for x in range(xPos, xPos + xLen):
                    spFmt = self.format(x)
                    spFmt.setUnderlineColor(self.colSpell)
                    spFmt.setUnderlineStyle(
                        QTextCharFormat.SpellCheckUnderline)
                    self.setFormat(x, 1, spFmt)

        return

    ##
    #  Internal Functions
    ##

    def _makeFormat(self, fmtCol=None, fmtStyle=None, fmtSize=None):
        """Generate a valid character format to be applied to the text
        that is to be highlighted.
        """
        theFormat = QTextCharFormat()

        if fmtCol is not None:
            theFormat.setForeground(fmtCol)

        if fmtStyle is not None:
            if "bold" in fmtStyle:
                theFormat.setFontWeight(QFont.Bold)
            if "italic" in fmtStyle:
                theFormat.setFontItalic(True)
            if "strike" in fmtStyle:
                theFormat.setFontStrikeOut(True)
            if "underline" in fmtStyle:
                theFormat.setFontUnderline(True)
            if "background" in fmtStyle:
                theFormat.setBackground(QBrush(fmtCol, Qt.SolidPattern))

        if fmtSize is not None:
            theFormat.setFontPointSize(
                int(round(fmtSize * self.mainConf.textSize)))

        return theFormat
class GuiDocHighlighter(QSyntaxHighlighter):

    def __init__(self, theDoc, theParent):
        QSyntaxHighlighter.__init__(self, theDoc)

        logger.debug("Initialising DocHighlighter ...")
        self.mainConf   = nw.CONFIG
        self.theDoc     = theDoc
        self.theParent  = theParent
        self.theTheme   = theParent.theTheme
        self.theIndex   = theParent.theIndex
        self.theDict    = None
        self.theHandle  = None
        self.spellCheck = False
        self.spellRx    = None
        self.hRules     = []
        self.hStyles    = {}

        self.colHead    = QColor(0,0,0)
        self.colHeadH   = QColor(0,0,0)
        self.colEmph    = QColor(0,0,0)
        self.colDialN   = QColor(0,0,0)
        self.colDialD   = QColor(0,0,0)
        self.colDialS   = QColor(0,0,0)
        self.colComm    = QColor(0,0,0)
        self.colKey     = QColor(0,0,0)
        self.colVal     = QColor(0,0,0)
        self.colSpell   = QColor(0,0,0)
        self.colTagErr  = QColor(0,0,0)
        self.colRepTag  = QColor(0,0,0)

        self.initHighlighter()

        logger.debug("DocHighlighter initialisation complete")

        return

    def initHighlighter(self):

        logger.debug("Setting up highlighting rules")

        self.colHead   = QColor(*self.theTheme.colHead)
        self.colHeadH  = QColor(*self.theTheme.colHeadH)
        self.colEmph   = QColor(*self.theTheme.colEmph)
        self.colDialN  = QColor(*self.theTheme.colDialN)
        self.colDialD  = QColor(*self.theTheme.colDialD)
        self.colDialS  = QColor(*self.theTheme.colDialS)
        self.colComm   = QColor(*self.theTheme.colComm)
        self.colKey    = QColor(*self.theTheme.colKey)
        self.colVal    = QColor(*self.theTheme.colVal)
        self.colSpell  = QColor(*self.theTheme.colSpell)
        self.colTagErr = QColor(*self.theTheme.colTagErr)
        self.colRepTag = QColor(*self.theTheme.colRepTag)

        self.hStyles = {
            "header1"   : self._makeFormat(self.colHead, "bold",1.8),
            "header2"   : self._makeFormat(self.colHead, "bold",1.6),
            "header3"   : self._makeFormat(self.colHead, "bold",1.4),
            "header4"   : self._makeFormat(self.colHead, "bold",1.2),
            "header1h"  : self._makeFormat(self.colHeadH,"bold",1.8),
            "header2h"  : self._makeFormat(self.colHeadH,"bold",1.6),
            "header3h"  : self._makeFormat(self.colHeadH,"bold",1.4),
            "header4h"  : self._makeFormat(self.colHeadH,"bold",1.2),
            "bold"      : self._makeFormat(self.colEmph, "bold"),
            "italic"    : self._makeFormat(self.colEmph, "italic"),
            "strike"    : self._makeFormat(self.colEmph, "strike"),
            "underline" : self._makeFormat(self.colEmph, "underline"),
            "dialogue1" : self._makeFormat(self.colDialN),
            "dialogue2" : self._makeFormat(self.colDialD),
            "dialogue3" : self._makeFormat(self.colDialS),
            "replace"   : self._makeFormat(self.colRepTag),
            "hidden"    : self._makeFormat(self.colComm),
            "keyword"   : self._makeFormat(self.colKey),
            "value"     : self._makeFormat(self.colVal),
        }

        # Headers
        self.hRules = []
        self.hRules.append((
            r"^(#{1}) (.*)[^\n]", {
                0 : self.hStyles["header1"],
                1 : self.hStyles["header1h"],
            }
        ))
        self.hRules.append((
            r"^(#{2}) (.*)[^\n]", {
                0 : self.hStyles["header2"],
                1 : self.hStyles["header2h"],
            }
        ))
        self.hRules.append((
            r"^(#{3}) (.*)[^\n]", {
                0 : self.hStyles["header3"],
                1 : self.hStyles["header3h"],
            }
        ))
        self.hRules.append((
            r"^(#{4}) (.*)[^\n]", {
                0 : self.hStyles["header4"],
                1 : self.hStyles["header4h"],
            }
        ))

        # Keyword/Value
        # self.hRules.append((
        #     r"^(@.+?)\s*:\s*(.+?)$", {
        #         1 : self.hStyles["keyword"],
        #         2 : self.hStyles["value"],
        #     }
        # ))

        # Comments
        self.hRules.append((
            r"^%.*$", {
                0 : self.hStyles["hidden"],
            }
        ))

        # Markdown
        self.hRules.append((
            r"(?<![\w|\\])([\*]{2})(?!\s)(?m:(.+?))(?<![\s|\\])(\1)(?!\w)", {
                1 : self.hStyles["hidden"],
                2 : self.hStyles["bold"],
                3 : self.hStyles["hidden"],
            }
        ))
        self.hRules.append((
            r"(?<![\w|_|\\])([_])(?!\s|\1)(?m:(.+?))(?<![\s|\\])(\1)(?!\w)", {
                1 : self.hStyles["hidden"],
                2 : self.hStyles["italic"],
                3 : self.hStyles["hidden"],
            }
        ))
        self.hRules.append((
            r"(?<![\w|\\])([_]{2})(?!\s)(?m:(.+?))(?<![\s|\\])(\1)(?!\w)", {
                1 : self.hStyles["hidden"],
                2 : self.hStyles["underline"],
                3 : self.hStyles["hidden"],
            }
        ))

        # Quoted Strings
        self.hRules.append((
            "{:s}(.+?){:s}".format('"','"'), {
                0 : self.hStyles["dialogue1"],
            }
        ))
        self.hRules.append((
            "{:s}(.+?){:s}".format(*self.mainConf.fmtDoubleQuotes), {
                0 : self.hStyles["dialogue2"],
            }
        ))
        self.hRules.append((
            "{:s}(.+?){:s}".format(*self.mainConf.fmtSingleQuotes), {
                0 : self.hStyles["dialogue3"],
            }
        ))

        self.hRules.append((
            "<(\S+?)>", {
                0 : self.hStyles["replace"],
            }
        ))

        # Build a QRegExp for each pattern and for the spell checker
        self.rules   = [(QRegularExpression(a),b) for (a,b) in self.hRules]
        self.spellRx = QRegularExpression(r"\b[^\s]+\b")

        return True

    ##
    #  Setters
    ##

    def setDict(self, theDict):
        self.theDict = theDict
        return True

    def setSpellCheck(self, theMode):
        self.spellCheck = theMode
        return True

    def setHandle(self, theHandle):
        self.theHandle = theHandle
        return True

    ##
    #  Highlight Block
    ##

    def highlightBlock(self, theText):

        if self.theHandle is None:
            return

        if theText.startswith("@"):
            # Highlighting of keywords and commands
            tItem = self.theParent.theProject.getItem(self.theHandle)
            isValid, theBits, thePos = self.theIndex.scanThis(theText)
            isGood = self.theIndex.checkThese(theBits, tItem)
            if isValid:
                for n in range(len(theBits)):
                    xPos = thePos[n]
                    xLen = len(theBits[n])
                    if isGood[n]:
                        if n == 0:
                            self.setFormat(xPos, xLen, self.hStyles["keyword"])
                        else:
                            self.setFormat(xPos, xLen, self.hStyles["value"])
                    else:
                        kwFmt = self.format(xPos)
                        kwFmt.setUnderlineColor(self.colTagErr)
                        kwFmt.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
                        self.setFormat(xPos, xLen, kwFmt)

        else:
            # Other text just uses regex
            for rX, xFmt in self.rules:
                rxItt = rX.globalMatch(theText, 0)
                while rxItt.hasNext():
                    rxMatch = rxItt.next()
                    for xM in xFmt.keys():
                        xPos = rxMatch.capturedStart(xM)
                        xLen = rxMatch.capturedLength(xM)
                        self.setFormat(xPos, xLen, xFmt[xM])

        if self.theDict is None or not self.spellCheck or theText.startswith("@"):
            return

        rxSpell = self.spellRx.globalMatch(theText.replace("_"," "), 0)
        while rxSpell.hasNext():
            rxMatch = rxSpell.next()
            if not self.theDict.checkWord(rxMatch.captured(0)):
                if rxMatch.captured(0) == rxMatch.captured(0).upper():
                    continue
                xPos = rxMatch.capturedStart(0)
                xLen = rxMatch.capturedLength(0)
                for x in range(xLen):
                    spFmt = self.format(xPos+x)
                    spFmt.setUnderlineColor(self.colSpell)
                    spFmt.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
                    self.setFormat(xPos+x, 1, spFmt)

        return

    ##
    #  Internal Functions
    ##

    def _makeFormat(self, fmtCol=None, fmtStyle=None, fmtSize=None):
        theFormat = QTextCharFormat()

        if fmtCol is not None:
            theFormat.setForeground(fmtCol)

        if fmtStyle is not None:
            if "bold" in fmtStyle:
                theFormat.setFontWeight(QFont.Bold)
            if "italic" in fmtStyle:
                theFormat.setFontItalic(True)
            if "strike" in fmtStyle:
                theFormat.setFontStrikeOut(True)
            if "underline" in fmtStyle:
                theFormat.setFontUnderline(True)

        if fmtSize is not None:
            theFormat.setFontPointSize(round(fmtSize*self.mainConf.textSize))

        return theFormat
Beispiel #15
0
class GuiDocHighlighter(QSyntaxHighlighter):
    def __init__(self, theDoc, theParent):
        QSyntaxHighlighter.__init__(self, theDoc)

        logger.debug("Initialising DocHighlighter ...")
        self.mainConf = nw.CONFIG
        self.theDoc = theDoc
        self.theParent = theParent
        self.theTheme = theParent.theTheme
        self.theIndex = theParent.theIndex
        self.theDict = None
        self.theHandle = None
        self.spellCheck = False
        self.spellRx = None
        self.hRules = []
        self.hStyles = {}

        self.colHead = QColor(0, 0, 0)
        self.colHeadH = QColor(0, 0, 0)
        self.colEmph = QColor(0, 0, 0)
        self.colDialN = QColor(0, 0, 0)
        self.colDialD = QColor(0, 0, 0)
        self.colDialS = QColor(0, 0, 0)
        self.colComm = QColor(0, 0, 0)
        self.colKey = QColor(0, 0, 0)
        self.colVal = QColor(0, 0, 0)
        self.colSpell = QColor(0, 0, 0)
        self.colTagErr = QColor(0, 0, 0)
        self.colRepTag = QColor(0, 0, 0)

        self.initHighlighter()

        logger.debug("DocHighlighter initialisation complete")

        return

    def initHighlighter(self):

        logger.debug("Setting up highlighting rules")

        self.colHead = QColor(*self.theTheme.colHead)
        self.colHeadH = QColor(*self.theTheme.colHeadH)
        self.colEmph = QColor(*self.theTheme.colEmph)
        self.colDialN = QColor(*self.theTheme.colDialN)
        self.colDialD = QColor(*self.theTheme.colDialD)
        self.colDialS = QColor(*self.theTheme.colDialS)
        self.colComm = QColor(*self.theTheme.colComm)
        self.colKey = QColor(*self.theTheme.colKey)
        self.colVal = QColor(*self.theTheme.colVal)
        self.colSpell = QColor(*self.theTheme.colSpell)
        self.colTagErr = QColor(*self.theTheme.colTagErr)
        self.colRepTag = QColor(*self.theTheme.colRepTag)
        self.colTrail = QColor(*self.theTheme.colEmph, 64)

        self.hStyles = {
            "header1": self._makeFormat(self.colHead, "bold", 1.8),
            "header2": self._makeFormat(self.colHead, "bold", 1.6),
            "header3": self._makeFormat(self.colHead, "bold", 1.4),
            "header4": self._makeFormat(self.colHead, "bold", 1.2),
            "header1h": self._makeFormat(self.colHeadH, "bold", 1.8),
            "header2h": self._makeFormat(self.colHeadH, "bold", 1.6),
            "header3h": self._makeFormat(self.colHeadH, "bold", 1.4),
            "header4h": self._makeFormat(self.colHeadH, "bold", 1.2),
            "bold": self._makeFormat(self.colEmph, "bold"),
            "italic": self._makeFormat(self.colEmph, "italic"),
            "strike": self._makeFormat(self.colEmph, "strike"),
            "underline": self._makeFormat(self.colEmph, "underline"),
            "trailing": self._makeFormat(self.colTrail, "background"),
            "nobreak": self._makeFormat(self.colTrail, "background"),
            "dialogue1": self._makeFormat(self.colDialN),
            "dialogue2": self._makeFormat(self.colDialD),
            "dialogue3": self._makeFormat(self.colDialS),
            "replace": self._makeFormat(self.colRepTag),
            "hidden": self._makeFormat(self.colComm),
            "keyword": self._makeFormat(self.colKey),
            "value": self._makeFormat(self.colVal),
        }

        self.hRules = []

        # Trailing Spaces, 2+
        self.hRules.append((r"[ ]{2,}$", {
            0: self.hStyles["trailing"],
        }))

        # Non-breaking Space
        self.hRules.append(("[%s]+" % nwUnicode.U_NBSP, {
            0: self.hStyles["nobreak"],
        }))

        # Markdown
        self.hRules.append(
            (r"(?<![\w|\\])([\*]{2})(?!\s)(?m:(.+?))(?<![\s|\\])(\1)(?!\w)", {
                1: self.hStyles["hidden"],
                2: self.hStyles["bold"],
                3: self.hStyles["hidden"],
            }))
        self.hRules.append(
            (r"(?<![\w|_|\\])([_])(?!\s|\1)(?m:(.+?))(?<![\s|\\])(\1)(?!\w)", {
                1: self.hStyles["hidden"],
                2: self.hStyles["italic"],
                3: self.hStyles["hidden"],
            }))
        self.hRules.append(
            (r"(?<![\w|\\])([_]{2})(?!\s)(?m:(.+?))(?<![\s|\\])(\1)(?!\w)", {
                1: self.hStyles["hidden"],
                2: self.hStyles["underline"],
                3: self.hStyles["hidden"],
            }))

        # Quoted Strings
        self.hRules.append(("{:s}(.+?){:s}".format('"', '"'), {
            0: self.hStyles["dialogue1"],
        }))
        self.hRules.append(
            ("{:s}(.+?){:s}".format(*self.mainConf.fmtDoubleQuotes), {
                0: self.hStyles["dialogue2"],
            }))
        self.hRules.append(
            ("{:s}(.+?){:s}".format(*self.mainConf.fmtSingleQuotes), {
                0: self.hStyles["dialogue3"],
            }))

        self.hRules.append((r"<(\S+?)>", {
            0: self.hStyles["replace"],
        }))

        # Build a QRegExp for each highlight pattern
        self.rxRules = []
        for regEx, regRules in self.hRules:
            hReg = QRegularExpression(regEx)
            hReg.setPatternOptions(
                QRegularExpression.UseUnicodePropertiesOption)
            self.rxRules.append((hReg, regRules))

        # Build a QRegExp for spell checker
        # Include additional characters that the highlighter should
        # consider to be word separators
        wordSep = "_+"
        wordSep += nwUnicode.U_EMDASH
        self.spellRx = QRegularExpression("\\b[^\\s%s]+\\b" % wordSep)
        self.spellRx.setPatternOptions(
            QRegularExpression.UseUnicodePropertiesOption)

        return True

    ##
    #  Setters
    ##

    def setDict(self, theDict):
        self.theDict = theDict
        return True

    def setSpellCheck(self, theMode):
        self.spellCheck = theMode
        return True

    def setHandle(self, theHandle):
        self.theHandle = theHandle
        return True

    ##
    #  Highlight Block
    ##

    def highlightBlock(self, theText):
        """Highlight a single block. Prefer to check first character for
        all formats that are defined by their initial characters. This
        is significantly faster than running the regex checks we use for
        text paragraphs.
        """

        if self.theHandle is None or not theText:
            return

        if theText.startswith("@"):  # Keywords and commands
            tItem = self.theParent.theProject.getItem(self.theHandle)
            isValid, theBits, thePos = self.theIndex.scanThis(theText)
            isGood = self.theIndex.checkThese(theBits, tItem)
            if isValid:
                for n in range(len(theBits)):
                    xPos = thePos[n]
                    xLen = len(theBits[n])
                    if isGood[n]:
                        if n == 0:
                            self.setFormat(xPos, xLen, self.hStyles["keyword"])
                        else:
                            self.setFormat(xPos, xLen, self.hStyles["value"])
                    else:
                        kwFmt = self.format(xPos)
                        kwFmt.setUnderlineColor(self.colTagErr)
                        kwFmt.setUnderlineStyle(
                            QTextCharFormat.SpellCheckUnderline)
                        self.setFormat(xPos, xLen, kwFmt)

            # We never want to run the spell checker on keyword/values,
            # so we force a return here
            return

        elif theText.startswith("# "):  # Header 1
            self.setFormat(0, 1, self.hStyles["header1h"])
            self.setFormat(1, len(theText), self.hStyles["header1"])

        elif theText.startswith("## "):  # Header 2
            self.setFormat(0, 2, self.hStyles["header2h"])
            self.setFormat(2, len(theText), self.hStyles["header2"])

        elif theText.startswith("### "):  # Header 3
            self.setFormat(0, 3, self.hStyles["header3h"])
            self.setFormat(3, len(theText), self.hStyles["header3"])

        elif theText.startswith("#### "):  # Header 4
            self.setFormat(0, 4, self.hStyles["header4h"])
            self.setFormat(4, len(theText), self.hStyles["header4"])

        elif theText.startswith("%"):  # Comments
            self.setFormat(0, len(theText), self.hStyles["hidden"])

        else:  # Text Paragraph
            for rX, xFmt in self.rxRules:
                rxItt = rX.globalMatch(theText, 0)
                while rxItt.hasNext():
                    rxMatch = rxItt.next()
                    for xM in xFmt.keys():
                        xPos = rxMatch.capturedStart(xM)
                        xLen = rxMatch.capturedLength(xM)
                        self.setFormat(xPos, xLen, xFmt[xM])

        if self.theDict is None or not self.spellCheck:
            return

        rxSpell = self.spellRx.globalMatch(theText, 0)
        while rxSpell.hasNext():
            rxMatch = rxSpell.next()
            if not self.theDict.checkWord(rxMatch.captured(0)):
                if rxMatch.captured(0) == rxMatch.captured(0).upper():
                    continue
                xPos = rxMatch.capturedStart(0)
                xLen = rxMatch.capturedLength(0)
                for x in range(xLen):
                    spFmt = self.format(xPos + x)
                    spFmt.setUnderlineColor(self.colSpell)
                    spFmt.setUnderlineStyle(
                        QTextCharFormat.SpellCheckUnderline)
                    self.setFormat(xPos + x, 1, spFmt)

        return

    ##
    #  Internal Functions
    ##

    def _makeFormat(self, fmtCol=None, fmtStyle=None, fmtSize=None):
        """Generate a valid character format to be applied to the text
        that is to be highlighted.
        """

        theFormat = QTextCharFormat()

        if fmtCol is not None:
            theFormat.setForeground(fmtCol)

        if fmtStyle is not None:
            if "bold" in fmtStyle:
                theFormat.setFontWeight(QFont.Bold)
            if "italic" in fmtStyle:
                theFormat.setFontItalic(True)
            if "strike" in fmtStyle:
                theFormat.setFontStrikeOut(True)
            if "underline" in fmtStyle:
                theFormat.setFontUnderline(True)
            if "background" in fmtStyle:
                theFormat.setBackground(QBrush(fmtCol, Qt.SolidPattern))

        if fmtSize is not None:
            theFormat.setFontPointSize(round(fmtSize * self.mainConf.textSize))

        return theFormat