1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
from qtpy.QtCore import QRegExp
from qtpy.QtGui import (QColor, QTextCharFormat, QFont, QSyntaxHighlighter)
import keyword
def format(color, style=''):
"""Return a QTextCharFormat with the given attributes.
"""
_color = QColor()
_color.setNamedColor(color)
_format = QTextCharFormat()
_format.setForeground(_color)
if 'bold' in style:
_format.setFontWeight(QFont.Bold)
if 'italic' in style:
_format.setFontItalic(True)
return _format
# Syntax styles that can be shared by all languages
STYLES = {
'keyword': format('blue', 'bold'),
'operator': format('red'),
'brace': format('darkGray'),
'defclass': format('black', 'bold'),
'string': format('magenta'),
'string2': format('darkMagenta'),
'comment': format('darkGreen', 'italic'),
'self': format('black', 'italic'),
'numbers': format('brown'),
'inprompt': format('darkBlue', 'bold'),
'outprompt': format('darkRed', 'bold'),
}
class PromptHighlighter(object):
def __init__(self, formats=None):
self.styles = styles = dict(STYLES, **(formats or {}))
self.rules = [
# Match the prompt incase of a console
(QRegExp(r'IN[^\:]*'), 0, styles['inprompt']),
(QRegExp(r'OUT[^\:]*'), 0, styles['outprompt']),
# Numeric literals
(QRegExp(r'\b[+-]?[0-9]+\b'), 0, styles['numbers']),
]
def highlight(self, text):
for expression, nth, format in self.rules:
index = expression.indexIn(text, 0)
while index >= 0:
index = expression.pos(nth)
length = len(expression.cap(nth))
yield (index, length, format)
index = expression.indexIn(text, index + length)
class PythonHighlighter(QSyntaxHighlighter):
"""Syntax highlighter for the Python language.
"""
# Python keywords
keywords = keyword.kwlist
def __init__(self, document, formats=None):
QSyntaxHighlighter.__init__(self, document)
self.styles = styles = dict(STYLES, **(formats or {}))
# Multi-line strings (expression, flag, style)
# FIXME: The triple-quotes in these two lines will mess up the
# syntax highlighting from this point onward
self.tri_single = (QRegExp("'''"), 1, styles['string2'])
self.tri_double = (QRegExp('"""'), 2, styles['string2'])
rules = []
# Keyword, operator, and brace rules
rules += [(r'\b%s\b' % w, 0, styles['keyword'])
for w in PythonHighlighter.keywords]
# All other rules
rules += [
# 'self'
# (r'\bself\b', 0, STYLES['self']),
# Double-quoted string, possibly containing escape sequences
(r'"[^"\\]*(\\.[^"\\]*)*"', 0, styles['string']),
# Single-quoted string, possibly containing escape sequences
(r"'[^'\\]*(\\.[^'\\]*)*'", 0, styles['string']),
# 'def' followed by an identifier
(r'\bdef\b\s*(\w+)', 1, styles['defclass']),
# 'class' followed by an identifier
(r'\bclass\b\s*(\w+)', 1, styles['defclass']),
# From '#' until a newline
(r'#[^\n]*', 0, styles['comment']),
# Numeric literals
(r'\b[+-]?[0-9]+[lL]?\b', 0, styles['numbers']),
(r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b', 0, styles['numbers']),
(r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', 0,
styles['numbers']),
]
# Build a QRegExp for each pattern
self.rules = [(QRegExp(pat), index, fmt)
for (pat, index, fmt) in rules]
def highlightBlock(self, text):
"""Apply syntax highlighting to the given block of text.
"""
# Do other syntax formatting
for expression, nth, format in self.rules:
index = expression.indexIn(text, 0)
while index >= 0:
# We actually want the index of the nth match
index = expression.pos(nth)
length = len(expression.cap(nth))
self.setFormat(index, length, format)
index = expression.indexIn(text, index + length)
self.setCurrentBlockState(0)
# Do multi-line strings
in_multiline = self.match_multiline(text, *self.tri_single)
if not in_multiline:
in_multiline = self.match_multiline(text, *self.tri_double)
def match_multiline(self, text, delimiter, in_state, style):
"""Do highlighting of multi-line strings. ``delimiter`` should be a
``QRegExp`` for triple-single-quotes or triple-double-quotes, and
``in_state`` should be a unique integer to represent the corresponding
state changes when inside those strings. Returns True if we're still
inside a multi-line string when this function is finished.
"""
# If inside triple-single quotes, start at 0
if self.previousBlockState() == in_state:
start = 0
add = 0
# Otherwise, look for the delimiter on this line
else:
start = delimiter.indexIn(text)
# Move past this match
add = delimiter.matchedLength()
# As long as there's a delimiter match on this line...
while start >= 0:
# Look for the ending delimiter
end = delimiter.indexIn(text, start + add)
# Ending delimiter on this line?
if end >= add:
length = end - start + add + delimiter.matchedLength()
self.setCurrentBlockState(0)
# No; multi-line string
else:
self.setCurrentBlockState(in_state)
length = len(text) - start + add
# Apply formatting
self.setFormat(start, length, style)
# Look for the next match
start = delimiter.indexIn(text, start + length)
# Return True if still inside a multi-line string, False otherwise
return self.currentBlockState() == in_state
|