3 from PyQt4
.QtCore
import Qt
4 from PyQt4
.QtGui
import QFont
5 from PyQt4
.QtGui
import QSyntaxHighlighter
6 from PyQt4
.QtGui
import QTextCharFormat
7 from PyQt4
.QtGui
import QColor
9 class GenericSyntaxHighligher(QSyntaxHighlighter
):
11 def __init__(self
, doc
):
12 QSyntaxHighlighter
.__init
__(self
, doc
)
15 FINAL_STR
= '__FINAL__:'
16 def final(self
, pattern
=''):
18 Denotes that a pattern is the final pattern that should
19 be matched. If this pattern matches no other formats
20 will be applied, even if they would have matched.
22 return GenericSyntaxHighligher
.FINAL_STR
+ pattern
24 def create_rules(self
, *rules
):
26 raise Exception('create_rules requires an even '
27 'number of arguments.')
28 for idx
, rule
in enumerate(rules
):
31 formats
= rules
[idx
+1]
32 terminal
= rule
.startswith(self
.final())
34 rule
= rule
[len(self
.final()):]
35 regex
= re
.compile(rule
)
36 self
.__rules
.append((regex
, formats
, terminal
,))
38 def get_formats(self
, line
):
40 for regex
, fmts
, terminal
in self
.__rules
:
41 match
= regex
.match(line
)
43 matched
.append([match
, fmts
])
44 if terminal
: return matched
47 def mkformat(self
, fg
=None, bg
=None, bold
=False):
48 format
= QTextCharFormat()
49 if fg
: format
.setForeground(fg
)
50 if bg
: format
.setBackground(bg
)
51 if bold
: format
.setFontWeight(QFont
.Bold
)
54 def highlightBlock(self
, qstr
):
55 ascii
= qstr
.toAscii().data()
57 formats
= self
.get_formats(ascii
)
58 if not formats
: return
59 for match
, fmts
in formats
:
62 groups
= match
.groups()
64 # No groups in the regex, assume this is a single rule
65 # that spans the entire line
67 self
.setFormat(0, len(ascii
), fmts
)
70 # Groups exist, rule is a tuple corresponding to group
71 for grpidx
, group
in enumerate(groups
):
73 if not group
: continue
74 # allow None as a no-op format
77 self
.setFormat(start
, start
+length
,
81 class DiffSyntaxHighlighter(GenericSyntaxHighligher
):
82 def __init__(self
, doc
,whitespace
=True):
83 GenericSyntaxHighligher
.__init
__(self
,doc
)
85 diffstat
= self
.mkformat(Qt
.blue
, bold
=True)
86 diffstat_add
= self
.mkformat(Qt
.darkGreen
, bold
=True)
87 diffstat_remove
= self
.mkformat(Qt
.red
, bold
=True)
89 bg_green
= QColor(Qt
.green
)
90 bg_green
.setAlpha(128)
92 bg_red
= QColor(Qt
.red
)
95 diff_begin
= self
.mkformat(Qt
.darkCyan
, bold
=True)
96 diff_head
= self
.mkformat(Qt
.darkYellow
)
97 diff_add
= self
.mkformat(bg
=bg_green
)
98 diff_remove
= self
.mkformat(bg
=bg_red
)
101 bad_ws
= self
.mkformat(Qt
.black
, Qt
.red
)
103 # We specify the whitespace rule last so that it is
104 # applied after the diff addition/removal rules.
105 # The rules for the header
106 diff_bgn_rgx
= self
.final('^@@|^\+\+\+|^---')
107 diff_hd1_rgx
= self
.final('^diff --git')
108 diff_hd2_rgx
= self
.final('^index \S+\.\.\S+')
109 diff_hd3_rgx
= self
.final('^new file mode')
110 diff_add_rgx
= self
.final('^\+')
111 diff_rmv_rgx
= self
.final('^-')
112 diff_sts_rgx
= ('(.+\|.+?)(\d+)(.+?)([\+]*?)([-]*?)$')
113 diff_sum_rgx
= ('(\s+\d+ files changed[^\d]*)'
114 '(:?\d+ insertions[^\d]*)'
115 '(:?\d+ deletions.*)$')
117 self
.create_rules(diff_bgn_rgx
, diff_begin
,
118 diff_hd1_rgx
, diff_head
,
119 diff_hd2_rgx
, diff_head
,
120 diff_hd3_rgx
, diff_head
,
121 diff_add_rgx
, diff_add
,
122 diff_rmv_rgx
, diff_remove
,
123 diff_sts_rgx
, (None, diffstat
,
126 diff_sum_rgx
, (diffstat
,
130 self
.create_rules('(..*?)(\s+)$', (None, bad_ws
))
132 class LogSyntaxHighlighter(GenericSyntaxHighligher
):
133 def __init__(self
, doc
):
134 GenericSyntaxHighligher
.__init
__(self
,doc
)
136 blue
= self
.mkformat(Qt
.blue
, bold
=True)
137 black
= self
.mkformat(Qt
.black
, bold
=True)
138 dark_cyan
= self
.mkformat(Qt
.darkCyan
, bold
=True)
140 blue_black_rgx
= '^([^:]+:)(.*)$'
141 dark_cyan_rgx
= self
.final('^\w{3}\W+\w{3}\W+\d+\W+'
144 self
.create_rules(dark_cyan_rgx
, dark_cyan
,
145 blue_black_rgx
, (blue
, black
))
147 if __name__
== '__main__':
149 from PyQt4
import QtCore
, QtGui
150 class SyntaxTestDialog(QtGui
.QDialog
):
151 def __init__(self
, parent
):
152 QtGui
.QDialog
.__init
__(self
, parent
)
154 def setupUi(self
, dialog
):
155 dialog
.resize(QtCore
.QSize(QtCore
.QRect(0,0,720,512).size()).expandedTo(dialog
.minimumSizeHint()))
156 self
.vboxlayout
= QtGui
.QVBoxLayout(dialog
)
157 self
.vboxlayout
.setObjectName('vboxlayout')
158 self
.output_text
= QtGui
.QTextEdit(dialog
)
160 font
.setFamily('Monospace')
161 font
.setPointSize(13)
162 self
.output_text
.setFont(font
)
163 self
.output_text
.setAcceptDrops(False)
164 self
.vboxlayout
.addWidget(self
.output_text
)
165 DiffSyntaxHighlighter(self
.output_text
.document())
166 app
= QtGui
.QApplication(sys
.argv
)
167 dialog
= SyntaxTestDialog(app
.activeWindow())