1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qsyntaxhighlighter.h"
44 #ifndef QT_NO_SYNTAXHIGHLIGHTER
45 #include <private/qobject_p.h>
46 #include <qtextdocument.h>
47 #include <private/qtextdocument_p.h>
48 #include <qtextlayout.h>
50 #include <qtextobject.h>
51 #include <qtextcursor.h>
57 class QSyntaxHighlighterPrivate : public QObjectPrivate
59 Q_DECLARE_PUBLIC(QSyntaxHighlighter)
61 inline QSyntaxHighlighterPrivate()
62 : rehighlightPending(false), inReformatBlocks(false)
65 QPointer<QTextDocument> doc;
67 void _q_reformatBlocks(int from, int charsRemoved, int charsAdded);
68 void reformatBlocks(int from, int charsRemoved, int charsAdded);
69 void reformatBlock(const QTextBlock &block);
71 inline void rehighlight(QTextCursor &cursor, QTextCursor::MoveOperation operation) {
72 inReformatBlocks = true;
73 cursor.beginEditBlock();
74 int from = cursor.position();
75 cursor.movePosition(operation);
76 reformatBlocks(from, 0, cursor.position() - from);
77 cursor.endEditBlock();
78 inReformatBlocks = false;
81 inline void _q_delayedRehighlight() {
82 if (!rehighlightPending)
84 rehighlightPending = false;
85 q_func()->rehighlight();
88 void applyFormatChanges();
89 QVector<QTextCharFormat> formatChanges;
90 QTextBlock currentBlock;
91 bool rehighlightPending;
92 bool inReformatBlocks;
95 void QSyntaxHighlighterPrivate::applyFormatChanges()
97 bool formatsChanged = false;
99 QTextLayout *layout = currentBlock.layout();
101 QList<QTextLayout::FormatRange> ranges = layout->additionalFormats();
103 const int preeditAreaStart = layout->preeditAreaPosition();
104 const int preeditAreaLength = layout->preeditAreaText().length();
106 if (preeditAreaLength != 0) {
107 QList<QTextLayout::FormatRange>::Iterator it = ranges.begin();
108 while (it != ranges.end()) {
109 if (it->start >= preeditAreaStart
110 && it->start + it->length <= preeditAreaStart + preeditAreaLength) {
113 it = ranges.erase(it);
114 formatsChanged = true;
117 } else if (!ranges.isEmpty()) {
119 formatsChanged = true;
122 QTextCharFormat emptyFormat;
124 QTextLayout::FormatRange r;
128 while (i < formatChanges.count()) {
130 while (i < formatChanges.count() && formatChanges.at(i) == emptyFormat)
133 if (i >= formatChanges.count())
137 r.format = formatChanges.at(i);
139 while (i < formatChanges.count() && formatChanges.at(i) == r.format)
142 if (i >= formatChanges.count())
145 r.length = i - r.start;
147 if (preeditAreaLength != 0) {
148 if (r.start >= preeditAreaStart)
149 r.start += preeditAreaLength;
150 else if (r.start + r.length >= preeditAreaStart)
151 r.length += preeditAreaLength;
155 formatsChanged = true;
160 r.length = formatChanges.count() - r.start;
162 if (preeditAreaLength != 0) {
163 if (r.start >= preeditAreaStart)
164 r.start += preeditAreaLength;
165 else if (r.start + r.length >= preeditAreaStart)
166 r.length += preeditAreaLength;
170 formatsChanged = true;
173 if (formatsChanged) {
174 layout->setAdditionalFormats(ranges);
175 doc->markContentsDirty(currentBlock.position(), currentBlock.length());
179 void QSyntaxHighlighterPrivate::_q_reformatBlocks(int from, int charsRemoved, int charsAdded)
181 if (!inReformatBlocks)
182 reformatBlocks(from, charsRemoved, charsAdded);
185 void QSyntaxHighlighterPrivate::reformatBlocks(int from, int charsRemoved, int charsAdded)
187 rehighlightPending = false;
189 QTextBlock block = doc->findBlock(from);
190 if (!block.isValid())
194 QTextBlock lastBlock = doc->findBlock(from + charsAdded + (charsRemoved > 0 ? 1 : 0));
195 if (lastBlock.isValid())
196 endPosition = lastBlock.position() + lastBlock.length();
198 endPosition = doc->docHandle()->length();
200 bool forceHighlightOfNextBlock = false;
202 while (block.isValid() && (block.position() < endPosition || forceHighlightOfNextBlock)) {
203 const int stateBeforeHighlight = block.userState();
205 reformatBlock(block);
207 forceHighlightOfNextBlock = (block.userState() != stateBeforeHighlight);
209 block = block.next();
212 formatChanges.clear();
215 void QSyntaxHighlighterPrivate::reformatBlock(const QTextBlock &block)
217 Q_Q(QSyntaxHighlighter);
219 Q_ASSERT_X(!currentBlock.isValid(), "QSyntaxHighlighter::reformatBlock()", "reFormatBlock() called recursively");
221 currentBlock = block;
223 formatChanges.fill(QTextCharFormat(), block.length() - 1);
224 q->highlightBlock(block.text());
225 applyFormatChanges();
227 currentBlock = QTextBlock();
231 \class QSyntaxHighlighter
235 \brief The QSyntaxHighlighter class allows you to define syntax
236 highlighting rules, and in addition you can use the class to query
237 a document's current formatting or user data.
241 \ingroup richtext-processing
243 The QSyntaxHighlighter class is a base class for implementing
244 QTextEdit syntax highlighters. A syntax highligher automatically
245 highlights parts of the text in a QTextEdit, or more generally in
246 a QTextDocument. Syntax highlighters are often used when the user
247 is entering text in a specific format (for example source code)
248 and help the user to read the text and identify syntax errors.
250 To provide your own syntax highlighting, you must subclass
251 QSyntaxHighlighter and reimplement highlightBlock().
253 When you create an instance of your QSyntaxHighlighter subclass,
254 pass it the QTextEdit or QTextDocument that you want the syntax
255 highlighting to be applied to. For example:
257 \snippet code/src_gui_text_qsyntaxhighlighter.cpp 0
259 After this your highlightBlock() function will be called
260 automatically whenever necessary. Use your highlightBlock()
261 function to apply formatting (e.g. setting the font and color) to
262 the text that is passed to it. QSyntaxHighlighter provides the
263 setFormat() function which applies a given QTextCharFormat on
264 the current text block. For example:
266 \snippet code/src_gui_text_qsyntaxhighlighter.cpp 1
268 Some syntaxes can have constructs that span several text
269 blocks. For example, a C++ syntax highlighter should be able to
270 cope with \c{/}\c{*...*}\c{/} multiline comments. To deal with
271 these cases it is necessary to know the end state of the previous
272 text block (e.g. "in comment").
274 Inside your highlightBlock() implementation you can query the end
275 state of the previous text block using the previousBlockState()
276 function. After parsing the block you can save the last state
277 using setCurrentBlockState().
279 The currentBlockState() and previousBlockState() functions return
280 an int value. If no state is set, the returned value is -1. You
281 can designate any other value to identify any given state using
282 the setCurrentBlockState() function. Once the state is set the
283 QTextBlock keeps that value until it is set set again or until the
284 corresponding paragraph of text is deleted.
286 For example, if you're writing a simple C++ syntax highlighter,
287 you might designate 1 to signify "in comment":
289 \snippet code/src_gui_text_qsyntaxhighlighter.cpp 2
291 In the example above, we first set the current block state to
292 0. Then, if the previous block ended within a comment, we higlight
293 from the beginning of the current block (\c {startIndex =
294 0}). Otherwise, we search for the given start expression. If the
295 specified end expression cannot be found in the text block, we
296 change the current block state by calling setCurrentBlockState(),
297 and make sure that the rest of the block is higlighted.
299 In addition you can query the current formatting and user data
300 using the format() and currentBlockUserData() functions
301 respectively. You can also attach user data to the current text
302 block using the setCurrentBlockUserData() function.
303 QTextBlockUserData can be used to store custom settings. In the
304 case of syntax highlighting, it is in particular interesting as
305 cache storage for information that you may figure out while
306 parsing the paragraph's text. For an example, see the
307 setCurrentBlockUserData() documentation.
309 \sa QTextEdit, {Syntax Highlighter Example}
313 Constructs a QSyntaxHighlighter with the given \a parent.
315 If the parent is a QTextEdit, it installs the syntaxhighlighter on the
316 parents document. The specified QTextEdit also becomes the owner of
317 the QSyntaxHighlighter.
319 QSyntaxHighlighter::QSyntaxHighlighter(QObject *parent)
320 : QObject(*new QSyntaxHighlighterPrivate, parent)
322 if (parent->inherits("QTextEdit")) {
323 QTextDocument *doc = qobject_cast<QTextDocument *>(parent->property("document").value<QObject *>());
330 Constructs a QSyntaxHighlighter and installs it on \a parent.
331 The specified QTextDocument also becomes the owner of the
334 QSyntaxHighlighter::QSyntaxHighlighter(QTextDocument *parent)
335 : QObject(*new QSyntaxHighlighterPrivate, parent)
341 Destructor. Uninstalls this syntax highlighter from the text document.
343 QSyntaxHighlighter::~QSyntaxHighlighter()
349 Installs the syntax highlighter on the given QTextDocument \a doc.
350 A QSyntaxHighlighter can only be used with one document at a time.
352 void QSyntaxHighlighter::setDocument(QTextDocument *doc)
354 Q_D(QSyntaxHighlighter);
356 disconnect(d->doc, SIGNAL(contentsChange(int,int,int)),
357 this, SLOT(_q_reformatBlocks(int,int,int)));
359 QTextCursor cursor(d->doc);
360 cursor.beginEditBlock();
361 for (QTextBlock blk = d->doc->begin(); blk.isValid(); blk = blk.next())
362 blk.layout()->clearAdditionalFormats();
363 cursor.endEditBlock();
367 connect(d->doc, SIGNAL(contentsChange(int,int,int)),
368 this, SLOT(_q_reformatBlocks(int,int,int)));
369 d->rehighlightPending = true;
370 QTimer::singleShot(0, this, SLOT(_q_delayedRehighlight()));
375 Returns the QTextDocument on which this syntax highlighter is
378 QTextDocument *QSyntaxHighlighter::document() const
380 Q_D(const QSyntaxHighlighter);
387 Reapplies the highlighting to the whole document.
389 \sa rehighlightBlock()
391 void QSyntaxHighlighter::rehighlight()
393 Q_D(QSyntaxHighlighter);
397 QTextCursor cursor(d->doc);
398 d->rehighlight(cursor, QTextCursor::End);
404 Reapplies the highlighting to the given QTextBlock \a block.
408 void QSyntaxHighlighter::rehighlightBlock(const QTextBlock &block)
410 Q_D(QSyntaxHighlighter);
411 if (!d->doc || !block.isValid() || block.document() != d->doc)
414 const bool rehighlightPending = d->rehighlightPending;
416 QTextCursor cursor(block);
417 d->rehighlight(cursor, QTextCursor::EndOfBlock);
419 if (rehighlightPending)
420 d->rehighlightPending = rehighlightPending;
424 \fn void QSyntaxHighlighter::highlightBlock(const QString &text)
426 Highlights the given text block. This function is called when
427 necessary by the rich text engine, i.e. on text blocks which have
430 To provide your own syntax highlighting, you must subclass
431 QSyntaxHighlighter and reimplement highlightBlock(). In your
432 reimplementation you should parse the block's \a text and call
433 setFormat() as often as necessary to apply any font and color
434 changes that you require. For example:
436 \snippet code/src_gui_text_qsyntaxhighlighter.cpp 3
438 Some syntaxes can have constructs that span several text
439 blocks. For example, a C++ syntax highlighter should be able to
440 cope with \c{/}\c{*...*}\c{/} multiline comments. To deal with
441 these cases it is necessary to know the end state of the previous
442 text block (e.g. "in comment").
444 Inside your highlightBlock() implementation you can query the end
445 state of the previous text block using the previousBlockState()
446 function. After parsing the block you can save the last state
447 using setCurrentBlockState().
449 The currentBlockState() and previousBlockState() functions return
450 an int value. If no state is set, the returned value is -1. You
451 can designate any other value to identify any given state using
452 the setCurrentBlockState() function. Once the state is set the
453 QTextBlock keeps that value until it is set set again or until the
454 corresponding paragraph of text gets deleted.
456 For example, if you're writing a simple C++ syntax highlighter,
457 you might designate 1 to signify "in comment". For a text block
458 that ended in the middle of a comment you'd set 1 using
459 setCurrentBlockState, and for other paragraphs you'd set 0.
460 In your parsing code if the return value of previousBlockState()
461 is 1, you would highlight the text as a C++ comment until you
462 reached the closing \c{*}\c{/}.
464 \sa previousBlockState(), setFormat(), setCurrentBlockState()
468 This function is applied to the syntax highlighter's current text
469 block (i.e. the text that is passed to the highlightBlock()
472 The specified \a format is applied to the text from the \a start
473 position for a length of \a count characters (if \a count is 0,
474 nothing is done). The formatting properties set in \a format are
475 merged at display time with the formatting information stored
476 directly in the document, for example as previously set with
477 QTextCursor's functions. Note that the document itself remains
478 unmodified by the format set through this function.
480 \sa format(), highlightBlock()
482 void QSyntaxHighlighter::setFormat(int start, int count, const QTextCharFormat &format)
484 Q_D(QSyntaxHighlighter);
485 if (start < 0 || start >= d->formatChanges.count())
488 const int end = qMin(start + count, d->formatChanges.count());
489 for (int i = start; i < end; ++i)
490 d->formatChanges[i] = format;
496 The specified \a color is applied to the current text block from
497 the \a start position for a length of \a count characters.
499 The other attributes of the current text block, e.g. the font and
500 background color, are reset to default values.
502 \sa format(), highlightBlock()
504 void QSyntaxHighlighter::setFormat(int start, int count, const QColor &color)
506 QTextCharFormat format;
507 format.setForeground(color);
508 setFormat(start, count, format);
514 The specified \a font is applied to the current text block from
515 the \a start position for a length of \a count characters.
517 The other attributes of the current text block, e.g. the font and
518 background color, are reset to default values.
520 \sa format(), highlightBlock()
522 void QSyntaxHighlighter::setFormat(int start, int count, const QFont &font)
524 QTextCharFormat format;
525 format.setFont(font);
526 setFormat(start, count, format);
530 \fn QTextCharFormat QSyntaxHighlighter::format(int position) const
532 Returns the format at \a position inside the syntax highlighter's
535 QTextCharFormat QSyntaxHighlighter::format(int pos) const
537 Q_D(const QSyntaxHighlighter);
538 if (pos < 0 || pos >= d->formatChanges.count())
539 return QTextCharFormat();
540 return d->formatChanges.at(pos);
544 Returns the end state of the text block previous to the
545 syntax highlighter's current block. If no value was
546 previously set, the returned value is -1.
548 \sa highlightBlock(), setCurrentBlockState()
550 int QSyntaxHighlighter::previousBlockState() const
552 Q_D(const QSyntaxHighlighter);
553 if (!d->currentBlock.isValid())
556 const QTextBlock previous = d->currentBlock.previous();
557 if (!previous.isValid())
560 return previous.userState();
564 Returns the state of the current text block. If no value is set,
565 the returned value is -1.
567 int QSyntaxHighlighter::currentBlockState() const
569 Q_D(const QSyntaxHighlighter);
570 if (!d->currentBlock.isValid())
573 return d->currentBlock.userState();
577 Sets the state of the current text block to \a newState.
581 void QSyntaxHighlighter::setCurrentBlockState(int newState)
583 Q_D(QSyntaxHighlighter);
584 if (!d->currentBlock.isValid())
587 d->currentBlock.setUserState(newState);
591 Attaches the given \a data to the current text block. The
592 ownership is passed to the underlying text document, i.e. the
593 provided QTextBlockUserData object will be deleted if the
594 corresponding text block gets deleted.
596 QTextBlockUserData can be used to store custom settings. In the
597 case of syntax highlighting, it is in particular interesting as
598 cache storage for information that you may figure out while
599 parsing the paragraph's text.
601 For example while parsing the text, you can keep track of
602 parenthesis characters that you encounter ('{[(' and the like),
603 and store their relative position and the actual QChar in a simple
604 class derived from QTextBlockUserData:
606 \snippet code/src_gui_text_qsyntaxhighlighter.cpp 4
608 During cursor navigation in the associated editor, you can ask the
609 current QTextBlock (retrieved using the QTextCursor::block()
610 function) if it has a user data object set and cast it to your \c
611 BlockData object. Then you can check if the current cursor
612 position matches with a previously recorded parenthesis position,
613 and, depending on the type of parenthesis (opening or closing),
614 find the next opening or closing parenthesis on the same level.
616 In this way you can do a visual parenthesis matching and highlight
617 from the current cursor position to the matching parenthesis. That
618 makes it easier to spot a missing parenthesis in your code and to
619 find where a corresponding opening/closing parenthesis is when
620 editing parenthesis intensive code.
622 \sa QTextBlock::setUserData()
624 void QSyntaxHighlighter::setCurrentBlockUserData(QTextBlockUserData *data)
626 Q_D(QSyntaxHighlighter);
627 if (!d->currentBlock.isValid())
630 d->currentBlock.setUserData(data);
634 Returns the QTextBlockUserData object previously attached to the
637 \sa QTextBlock::userData(), setCurrentBlockUserData()
639 QTextBlockUserData *QSyntaxHighlighter::currentBlockUserData() const
641 Q_D(const QSyntaxHighlighter);
642 if (!d->currentBlock.isValid())
645 return d->currentBlock.userData();
651 Returns the current text block.
653 QTextBlock QSyntaxHighlighter::currentBlock() const
655 Q_D(const QSyntaxHighlighter);
656 return d->currentBlock;
661 #include "moc_qsyntaxhighlighter.cpp"
663 #endif // QT_NO_SYNTAXHIGHLIGHTER