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)
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()
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)
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)
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)
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)
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)
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)
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)
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)
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
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