From d7d563bf6e4b30e4a82f26a2ef2b1dd99faa833b Mon Sep 17 00:00:00 2001 From: Bastian Kleineidam Date: Tue, 26 Apr 2011 21:31:45 +0200 Subject: [PATCH] Add Qt syntax highlighting. --- Makefile | 1 + doc/changelog.txt | 4 ++ linkcheck/gui/editor.py | 8 +--- linkcheck/gui/editor_qsci.py | 10 +++++ linkcheck/gui/editor_qt.py | 18 +++++--- linkcheck/gui/syntax.py | 83 ++++++++++++++++++++++++++++++++++++ 6 files changed, 112 insertions(+), 12 deletions(-) create mode 100644 linkcheck/gui/syntax.py diff --git a/Makefile b/Makefile index 5bd81301..d3533fb5 100644 --- a/Makefile +++ b/Makefile @@ -142,6 +142,7 @@ doccheck: linkcheck/gui/properties.py \ linkcheck/gui/settings.py \ linkcheck/gui/statistics.py \ + linkcheck/gui/syntax.py \ linkcheck/gui/updater.py \ linkcheck/gui/urlmodel.py \ linkcheck/gui/urlsave.py \ diff --git a/doc/changelog.txt b/doc/changelog.txt index f11b75e9..17dd4175 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -1,5 +1,9 @@ 6.9 "" (released xx.xx.2011) +Features: +- gui: Add syntax highlighting for Qt editor in case QScintilla + is not installed. + 6.8 "Ghost in the shell" (released 26.4.2011) diff --git a/linkcheck/gui/editor.py b/linkcheck/gui/editor.py index a0853a22..be4e816d 100644 --- a/linkcheck/gui/editor.py +++ b/linkcheck/gui/editor.py @@ -50,13 +50,7 @@ class EditorWindow (QtGui.QDialog, Ui_EditorDialog): def setContentType (self, content_type): """Choose a lexer according to given content type.""" lexerclass = ContentTypeLexers.get(content_type.lower()) - if lexerclass: - lexer = lexerclass() - lexer.setFont(self.editor.font()) - self.editor.setLexer(lexer) - else: - # use no styling - self.editor.setLexer() + self.editor.highlight(lexerclass) def setText (self, text, line=1, col=1): """Set editor text and jump to given line and column.""" diff --git a/linkcheck/gui/editor_qsci.py b/linkcheck/gui/editor_qsci.py index 409fe702..936e8b0d 100644 --- a/linkcheck/gui/editor_qsci.py +++ b/linkcheck/gui/editor_qsci.py @@ -97,3 +97,13 @@ class Editor (Qsci.QsciScintilla): # folding margin colors (foreground,background) self.setFoldMarginColors(QtGui.QColor("#f5f5dc"), QtGui.QColor("#aaaaaa")) + + def highlight (self, lexerclass): + """Set syntax highlighter.""" + if lexerclass: + lexer = lexerclass() + lexer.setFont(self.font()) + self.setLexer(lexer) + else: + # use no styling + self.setLexer() diff --git a/linkcheck/gui/editor_qt.py b/linkcheck/gui/editor_qt.py index 0052a28b..a5e46c18 100644 --- a/linkcheck/gui/editor_qt.py +++ b/linkcheck/gui/editor_qt.py @@ -18,9 +18,14 @@ Text editor implemented with Qt """ from PyQt4 import QtGui, QtCore +from . import syntax -# Empty lexer dictionary indicating no support for syntax highlighting -ContentTypeLexers = {} +# Map MIME type to QSyntaxHighlighter class +ContentTypeLexers = { + "text/html": syntax.HtmlHighlighter, + "application/xml": syntax.XmlHighlighter, + "text/plain+ini": syntax.IniHighlighter, +} class LineNumberArea (QtGui.QWidget): """Display line numbers.""" @@ -50,9 +55,12 @@ class Editor (QtGui.QPlainTextEdit): self.updateLineNumberAreaWidth(0) self.highlightCurrentLine() - def setLexer (self): - """Does nothing.""" - pass + def highlight (self, lexerclass): + """Set syntax highlighter.""" + if lexerclass: + self.lexer = lexerclass(self.document()) + else: + self.lexer = None def setText (self, text): """Set editor text.""" diff --git a/linkcheck/gui/syntax.py b/linkcheck/gui/syntax.py new file mode 100644 index 00000000..23d2a1c1 --- /dev/null +++ b/linkcheck/gui/syntax.py @@ -0,0 +1,83 @@ +# syntax.py + +from PyQt4 import QtCore, QtGui + + +def format(color, style=''): + """Return a QTextCharFormat with the given attributes.""" + format = QtGui.QTextCharFormat() + format.setForeground(getattr(QtCore.Qt, color)) + if 'bold' in style: + format.setFontWeight(QtGui.QFont.Bold) + if 'italic' in style: + format.setFontItalic(True) + return format + + +class Highlighter (QtGui.QSyntaxHighlighter): + """Base class for all highlighters.""" + + def __init__ (self, document): + """Initialize rules and styles.""" + super(Highlighter, self).__init__(document) + self.rules = [] + self.styles = {} + + def highlightBlock(self, text): + """Highlight a text block.""" + for expression, format in self.rules: + # get first match + index = expression.indexIn(text) + while index >= 0: + length = expression.matchedLength() + self.setFormat(index, length, format) + # jump to next match + index = expression.indexIn(text, index + length) + self.setCurrentBlockState(0) + + def addRule (self, pattern, style): + """Add a rule pattern with given style.""" + self.rules.append((QtCore.QRegExp(pattern), self.styles[style])) + + +class XmlHighlighter (Highlighter): + """XML syntax highlighter.""" + + def __init__(self, document): + """Set XML syntax rules.""" + super(XmlHighlighter, self).__init__(document) + self.styles.update({ + 'keyword': format('darkBlue'), + 'attribute': format('darkGreen'), + 'comment': format('darkYellow'), + 'string': format('darkMagenta'), + }) + # keywords + for reg in ('/>', '>', ']*-->", 'comment') + +# Treat HTML as XML +HtmlHighlighter = XmlHighlighter + +class IniHighlighter (Highlighter): + """INI syntax highlighter.""" + + def __init__(self, document): + """Set INI syntax rules.""" + super(IniHighlighter, self).__init__(document) + self.styles.update({ + 'section': format('darkBlue'), + 'property': format('darkGreen'), + 'comment': format('darkYellow'), + }) + self.addRule(r'\b\[[a-zA-Z0-9_]+\]\b', 'section') + self.addRule(r'\b[a-zA-Z0-9_]+\](?=\s*\=)', 'property') + self.addRule(r'#[^\n]*', 'comment')