manuskript/manuskript/ui/highlighters/basicHighlighter.py
Jan Wester ff2cbca028 Converted most print statements to use logging
Some snippets have yet to be converted due to the more complex nature
of those snippets, and to keep things neat a separate commit makes more
sense for those.
2021-04-08 18:44:28 +02:00

194 lines
8.3 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
import re
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QBrush, QTextCursor, QColor, QFont, QSyntaxHighlighter
from PyQt5.QtGui import QTextBlockFormat, QTextCharFormat
import manuskript.models.references as Ref
import manuskript.ui.style as S
from manuskript import settings
from manuskript import functions as F
import logging
LOGGER = logging.getLogger(__name__)
class BasicHighlighter(QSyntaxHighlighter):
def __init__(self, editor):
QSyntaxHighlighter.__init__(self, editor.document())
self.editor = editor
self._defaultBlockFormat = QTextBlockFormat()
self._defaultCharFormat = QTextCharFormat()
self.defaultTextColor = QColor(S.text)
self.backgroundColor = QColor(S.base)
self.markupColor = QColor(S.textLight)
self.linkColor = QColor(S.link)
self.spellingErrorColor = QColor(Qt.red)
# Matches during checking can be separated by their type (all of them listed here):
# https://languagetool.org/development/api/org/languagetool/rules/ITSIssueType.html
#
# These are the colors for actual spell-, grammar- and style-checking:
self._errorColors = {
'addition' : QColor(255, 215, 0), # gold
'characters' : QColor(135, 206, 235), # sky blue
'duplication' : QColor(0, 255, 255), # cyan / aqua
'formatting' : QColor(0, 128, 128), # teal
'grammar' : QColor(0, 0, 255), # blue
'inconsistency' : QColor(128, 128, 0), # olive
'inconsistententities' : QColor(46, 139, 87), # sea green
'internationalization' : QColor(255, 165, 0), # orange
'legal' : QColor(255, 69, 0), # orange red
'length' : QColor(47, 79, 79), # dark slate gray
'localespecificcontent' : QColor(188, 143, 143),# rosy brown
'localeviolation' : QColor(128, 0, 0), # maroon
'markup' : QColor(128, 0, 128), # purple
'misspelling' : QColor(255, 0, 0), # red
'mistranslation' : QColor(255, 0, 255), # magenta / fuchsia
'nonconformance' : QColor(255, 218, 185), # peach puff
'numbers' : QColor(65, 105, 225), # royal blue
'omission' : QColor(255, 20, 147), # deep pink
'other' : QColor(138, 43, 226), # blue violet
'patternproblem' : QColor(0, 128, 0), # green
'register' : QColor(112,128,144), # slate gray
'style' : QColor(0, 255, 0), # lime
'terminology' : QColor(0, 0, 128), # navy
'typographical' : QColor(255, 255, 0), # yellow
'uncategorized' : QColor(128, 128, 128), # gray
'untranslated' : QColor(210, 105, 30), # chocolate
'whitespace' : QColor(192, 192, 192) # silver
}
def setDefaultBlockFormat(self, bf):
self._defaultBlockFormat = bf
self.rehighlight()
def setDefaultCharFormat(self, cf):
self._defaultCharFormat = cf
self.rehighlight()
def setMisspelledColor(self, color):
self._errorColors['misspelled'] = color
def updateColorScheme(self, rehighlight=True):
"""
Generates a base set of colors that will take account of user
preferences, and use system style.
"""
# Reading user settings
opt = settings.textEditor
if not self.editor._fromTheme or not self.editor._themeData:
self.defaultTextColor = QColor(opt["fontColor"])
self.backgroundColor = (QColor(opt["background"])
if not opt["backgroundTransparent"]
else QColor(S.window))
self.markupColor = F.mixColors(self.defaultTextColor,
self.backgroundColor,
.3)
self.linkColor = QColor(S.link)
self.spellingErrorColor = QColor(opt["misspelled"])
self._defaultCharFormat.setForeground(QBrush(self.defaultTextColor))
# FullscreenEditor probably
else:
opt = self.editor._themeData
self.defaultTextColor = QColor(opt["Text/Color"])
self.backgroundColor = F.mixColors(
QColor(opt["Foreground/Color"]),
QColor(opt["Background/Color"]),
int(opt["Foreground/Opacity"])/100.)
self.markupColor = F.mixColors(self.defaultTextColor,
self.backgroundColor,
.3)
self.linkColor = QColor(S.link)
self.spellingErrorColor = QColor(opt["Text/Misspelled"])
if rehighlight:
self.rehighlight()
def highlightBlock(self, text):
"""Apply syntax highlighting to the given block of text.
"""
self.highlightBlockBefore(text)
self.doHighlightBlock(text)
self.highlightBlockAfter(text)
def doHighlightBlock(self, text):
"""
Virtual function to subclass.
"""
pass
def highlightBlockBefore(self, text):
"""Highlighting to do before anything else.
When subclassing BasicHighlighter, you must call highlightBlockBefore
before you do any custom highlighting. Or implement doHighlightBlock.
"""
#LOGGER.debug("undoSteps before: %s", self.currentBlock().document().availableUndoSteps())
c = QTextCursor(self.currentBlock())
#c.joinPreviousEditBlock()
bf = QTextBlockFormat(self._defaultBlockFormat)
if bf != c.blockFormat():
c.setBlockFormat(bf)
#c.endEditBlock()
#LOGGER.debug("undoSteps after: %s", self.currentBlock().document().availableUndoSteps())
# self.setFormat(0, len(text), self._defaultCharFormat)
def highlightBlockAfter(self, text):
"""Highlighting to do after everything else.
When subclassing BasicHighlighter, you must call highlightBlockAfter
after your custom highlighting. Or implement doHighlightBlock.
"""
# References
for txt in re.finditer(Ref.RegEx, text):
fmt = self.format(txt.start())
fmt.setFontFixedPitch(True)
fmt.setFontWeight(QFont.DemiBold)
if txt.group(1) == Ref.TextLetter:
fmt.setBackground(QBrush(Ref.TextHighlightColor))
elif txt.group(1) == Ref.CharacterLetter:
fmt.setBackground(QBrush(Ref.CharacterHighlightColor))
elif txt.group(1) == Ref.PlotLetter:
fmt.setBackground(QBrush(Ref.PlotHighlightColor))
elif txt.group(1) == Ref.WorldLetter:
fmt.setBackground(QBrush(Ref.WorldHighlightColor))
self.setFormat(txt.start(),
txt.end() - txt.start(),
fmt)
if hasattr(self.editor, "spellcheck") and self.editor.spellcheck and self.editor._dict:
# Spell checking
# Following algorithm would not check words at the end of line.
# This hacks adds a space to every line where the text cursor is not
# So that it doesn't spellcheck while typing, but still spellchecks at
# end of lines. See github's issue #166.
textedText = text
if self.currentBlock().position() + len(text) != \
self.editor.textCursor().position():
textedText = text + " "
# The text should only be checked once as a whole
for match in self.editor._dict.checkText(textedText):
if match.locqualityissuetype in self._errorColors:
highlight_color = self._errorColors[match.locqualityissuetype]
format = self.format(match.start)
format.setUnderlineColor(highlight_color)
# SpellCheckUnderline fails with some fonts
format.setUnderlineStyle(QTextCharFormat.WaveUnderline)
self.setFormat(match.start, match.end - match.start, format)