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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
|
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example richtext/syntaxhighlighter
\title Syntax Highlighter Example
\ingroup examples-richtext
\brief The Syntax Highlighter example shows how to perform
simple syntax highlighting.
\brief The Syntax Highlighter example shows how to perform simple syntax
highlighting by subclassing the QSyntaxHighlighter class.
\image syntaxhighlighter-example.png
The Syntax Highlighter application displays C++ files with custom
syntax highlighting.
The example consists of two classes:
\list
\li The \c Highlighter class defines and applies the
highlighting rules.
\li The \c MainWindow widget is the application's main window.
\endlist
We will first review the \c Highlighter class to see how you can
customize the QSyntaxHighlighter class to fit your preferences,
then we will take a look at the relevant parts of the \c
MainWindow class to see how you can use your custom highlighter
class in an application.
\section1 Highlighter Class Definition
\snippet richtext/syntaxhighlighter/highlighter.h 0
To provide your own syntax highlighting, you must subclass
QSyntaxHighlighter, reimplement the \l
{QSyntaxHighlighter::highlightBlock()}{highlightBlock()} function,
and define your own highlighting rules.
We have chosen to store our highlighting rules using a private
struct: A rule consists of a QRegularExpression pattern and a
QTextCharFormat instance. The various rules are then stored using a
QList.
The QTextCharFormat class provides formatting information for
characters in a QTextDocument specifying the visual properties of
the text, as well as information about its role in a hypertext
document. In this example, we will only define the font weight and
color using the QTextCharFormat::setFontWeight() and
QTextCharFormat::setForeground() functions.
\section1 Highlighter Class Implementation
When subclassing the QSyntaxHighlighter class you must pass the
parent parameter to the base class constructor. The parent is the
text document upon which the syntax highlighting will be
applied. In this example, we have also chosen to define our
highlighting rules in the constructor:
\snippet richtext/syntaxhighlighter/highlighter.cpp 0
\snippet richtext/syntaxhighlighter/highlighter.cpp 1
First we define a keyword rule which recognizes the most common
C++ keywords. We give the \c keywordFormat a bold, dark blue
font. For each keyword, we assign the keyword and the specified
format to a HighlightingRule object and append the object to our
list of rules.
\snippet richtext/syntaxhighlighter/highlighter.cpp 2
\codeline
\snippet richtext/syntaxhighlighter/highlighter.cpp 4
\codeline
\snippet richtext/syntaxhighlighter/highlighter.cpp 5
Then we create a format that we will apply to Qt class names. The
class names will be rendered with a dark magenta color and a bold
style. We specify a string pattern that is actually a regular
expression capturing all Qt class names. Then we assign the
regular expression and the specified format to a HighlightingRule
object and append the object to our list of rules.
We also define highlighting rules for quotations and functions
using the same approach: The patterns have the form of regular
expressions and are stored in HighlightingRule objects with the
associated format.
\snippet richtext/syntaxhighlighter/highlighter.cpp 3
\codeline
\snippet richtext/syntaxhighlighter/highlighter.cpp 6
The C++ language has two variations of comments: The single line
comment (\c //) and the multiline comment (\c{/*...*}\c{/}). The single
line comment can easily be defined through a highlighting rule
similar to the previous ones. But the multiline comment needs
special care due to the design of the QSyntaxHighlighter class.
After a QSyntaxHighlighter object is created, its \l
{QSyntaxHighlighter::highlightBlock()}{highlightBlock()} function
will be called automatically whenever it is necessary by the rich
text engine, highlighting the given text block. The problem
appears when a comment spans several text blocks. We will take a
closer look at how this problem can be solved when reviewing the
implementation of the \c Highlighter::highlightBlock()
function. At this point we only specify the multiline comment's
color.
\snippet richtext/syntaxhighlighter/highlighter.cpp 7
The highlightBlock() function is called automatically whenever it
is necessary by the rich text engine, i.e. when there are text
blocks that have changed.
First we apply the syntax highlighting rules that we stored in the
\c highlightingRules list. For each rule (i.e. for each
HighlightingRule object) we search for the pattern in the given
text block using the QString::indexOf() function. When the first
occurrence of the pattern is found, we use the
QRegularExpressionMatch::capturedLength() function to determine the string
that will be formatted. QRegularExpressionMatch::capturedLength() returns
the length of the last matched string, or 0 if there was no match.
To perform the actual formatting the QSyntaxHighlighter class
provides the \l {QSyntaxHighlighter::setFormat()}{setFormat()}
function. This function operates on the text block that is passed
as argument to the \c highlightBlock() function. The specified
format is applied to the text from the given start position for
the given length. The formatting properties set in the given
format are merged at display time with the formatting information
stored directly in the document. Note that the document itself
remains unmodified by the format set through this function.
This process is repeated until the last occurrence of the pattern
in the current text block is found.
\snippet richtext/syntaxhighlighter/highlighter.cpp 8
To deal with constructs that can span several text blocks (like
the C++ multiline comment), it is necessary to know the end state
of the previous text block (e.g. "in comment"). Inside your \c
highlightBlock() implementation you can query the end state of the
previous text block using the
QSyntaxHighlighter::previousBlockState() function. After parsing
the block you can save the last state using
QSyntaxHighlighter::setCurrentBlockState().
The \l
{QSyntaxHighlighter::previousBlockState()}{previousBlockState()}
function return an int value. If no state is set, the returned
value is -1. You can designate any other value to identify any
given state using the \l
{QSyntaxHighlighter::setCurrentBlockState()}{setCurrentBlockState()}
function. Once the state is set, the QTextBlock keeps that value
until it is set again or until the corresponding paragraph of text
is deleted.
In this example we have chosen to use 0 to represent the "not in
comment" state, and 1 for the "in comment" state. When the stored
syntax highlighting rules are applied we initialize the current
block state to 0.
\snippet richtext/syntaxhighlighter/highlighter.cpp 9
If the previous block state was "in comment" (\c
{previousBlockState() == 1}), we start the search for an end
expression at the beginning of the text block. If the
previousBlockState() returns 0, we start the search at the
location of the first occurrence of a start expression.
\snippet richtext/syntaxhighlighter/highlighter.cpp 10
\snippet richtext/syntaxhighlighter/highlighter.cpp 11
When an end expression is found, we calculate the length of the
comment and apply the multiline comment format. Then we search for
the next occurrence of the start expression and repeat the
process. If no end expression can be found in the current text
block we set the current block state to 1, i.e. "in comment".
This completes the \c Highlighter class implementation; it is now
ready for use.
\section1 MainWindow Class Definition
Using a QSyntaxHighlighter subclass is simple; just provide your
application with an instance of the class and pass it the document
upon which you want the highlighting to be applied.
\snippet richtext/syntaxhighlighter/mainwindow.h 0
In this example we declare a pointer to a \c Highlighter instance
which we later will initialize in the private \c setupEditor()
function.
\section1 MainWindow Class Implementation
The constructor of the main window is straight forward. We first
set up the menus, then we initialize the editor and make it the
central widget of the application. Finally we set the main
window's title.
\snippet richtext/syntaxhighlighter/mainwindow.cpp 0
We initialize and install the \c Highlighter object in the private
setupEditor() convenience function:
\snippet richtext/syntaxhighlighter/mainwindow.cpp 1
First we create the font we want to use in the editor, then we
create the editor itself which is an instance of the QTextEdit
class. Before we initialize the editor with the \c MainWindow
class definition file, we create a \c Highlighter instance passing
the editor's document as argument. This is the document that the
highlighting will be applied to. Then we are done.
A QSyntaxHighlighter object can only be installed on one document
at the time, but you can easily reinstall the highlighter on
another document using the QSyntaxHighlighter::setDocument()
function. The QSyntaxHighlighter class also provides the \l
{QSyntaxHighlighter::document()}{document()} function which
returns the currently set document.
*/
|