diff --git a/src/plugins/cmakeprojectmanager/cmakehighlighter.cpp b/src/plugins/cmakeprojectmanager/cmakehighlighter.cpp index b42a57058624cc435ca20772be4fc59dcbb0d924..39172a36a41dec4455fc8e29b2e5d5ef8187fba0 100644 --- a/src/plugins/cmakeprojectmanager/cmakehighlighter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakehighlighter.cpp @@ -46,7 +46,7 @@ static bool isVariable(const QString &word) CMakeHighlighter::CMakeHighlighter(QTextDocument *document) : - QSyntaxHighlighter(document) + TextEditor::SyntaxHighlighter(document) { } diff --git a/src/plugins/cmakeprojectmanager/cmakehighlighter.h b/src/plugins/cmakeprojectmanager/cmakehighlighter.h index dce5acf05795c5597f9085e53b86a3cc7aafa353..5c76a218075b2288dfbbb3934604b3696d999749 100644 --- a/src/plugins/cmakeprojectmanager/cmakehighlighter.h +++ b/src/plugins/cmakeprojectmanager/cmakehighlighter.h @@ -30,6 +30,7 @@ #ifndef CMAKEHIGHLIGHTER_H #define CMAKEHIGHLIGHTER_H +#include <texteditor/syntaxhighlighter.h> #include <QtCore/QtAlgorithms> #include <QtGui/QSyntaxHighlighter> #include <QtGui/QTextCharFormat> @@ -40,7 +41,7 @@ namespace Internal { /* This is a simple syntax highlighter for CMake files. * It highlights variables, commands, strings and comments. * Multi-line strings and variables inside strings are also recognized. */ -class CMakeHighlighter : public QSyntaxHighlighter +class CMakeHighlighter : public TextEditor::SyntaxHighlighter { Q_OBJECT public: diff --git a/src/plugins/cppeditor/cpphighlighter.cpp b/src/plugins/cppeditor/cpphighlighter.cpp index 321025058e329b10e9409b7d06442db3f2ce994b..c98c35dcdc7d732e7e7e0004659e729a787e3f12 100644 --- a/src/plugins/cppeditor/cpphighlighter.cpp +++ b/src/plugins/cppeditor/cpphighlighter.cpp @@ -42,7 +42,7 @@ using namespace TextEditor; using namespace CPlusPlus; CppHighlighter::CppHighlighter(QTextDocument *document) : - QSyntaxHighlighter(document) + TextEditor::SyntaxHighlighter(document) { } diff --git a/src/plugins/cppeditor/cpphighlighter.h b/src/plugins/cppeditor/cpphighlighter.h index 9d7c0496fc592891f13240d01fcebc315a970185..d8fe769371a06c399085b16dfd167bf4906f1737 100644 --- a/src/plugins/cppeditor/cpphighlighter.h +++ b/src/plugins/cppeditor/cpphighlighter.h @@ -31,16 +31,17 @@ #define CPPHIGHLIGHTER_H #include "cppeditorenums.h" -#include <QtGui/QSyntaxHighlighter> +#include <texteditor/syntaxhighlighter.h> #include <QtGui/QTextCharFormat> #include <QtCore/QtAlgorithms> namespace CppEditor { + namespace Internal { class CPPEditor; -class CppHighlighter : public QSyntaxHighlighter +class CppHighlighter : public TextEditor::SyntaxHighlighter { Q_OBJECT diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp index bd3f5fdc57b5abdb647bc8b3efe644c9c7cb5dc4..a9a74a85fcc63cef8f39964d019e448c6f46ee49 100644 --- a/src/plugins/qmljseditor/qmljseditor.cpp +++ b/src/plugins/qmljseditor/qmljseditor.cpp @@ -60,6 +60,7 @@ #include <texteditor/textblockiterator.h> #include <texteditor/texteditorconstants.h> #include <texteditor/texteditorsettings.h> +#include <texteditor/syntaxhighlighter.h> #include <qmldesigner/qmldesignerconstants.h> #include <utils/changeset.h> #include <utils/uncommentselection.h> diff --git a/src/plugins/qmljseditor/qmljshighlighter.cpp b/src/plugins/qmljseditor/qmljshighlighter.cpp index e7965286d4f266e2767c6bb9eb6803502eb4a120..423598a4e6d8a1e4cc24be5c17ca6b4ca0b35122 100644 --- a/src/plugins/qmljseditor/qmljshighlighter.cpp +++ b/src/plugins/qmljseditor/qmljshighlighter.cpp @@ -39,7 +39,7 @@ using namespace QmlJSEditor; using namespace QmlJS; Highlighter::Highlighter(QTextDocument *parent) - : QSyntaxHighlighter(parent), + : TextEditor::SyntaxHighlighter(parent), m_qmlEnabled(true), m_inMultilineComment(false) { diff --git a/src/plugins/qmljseditor/qmljshighlighter.h b/src/plugins/qmljseditor/qmljshighlighter.h index 3c33ebe721266d726bf8901f276cd14c10e0cb60..0be4f9bbf453904e15aa22b5bad79606ea227751 100644 --- a/src/plugins/qmljseditor/qmljshighlighter.h +++ b/src/plugins/qmljseditor/qmljshighlighter.h @@ -39,10 +39,11 @@ #include <QtGui/QSyntaxHighlighter> #include <texteditor/basetextdocumentlayout.h> +#include <texteditor/syntaxhighlighter.h> namespace QmlJSEditor { -class QMLJSEDITOR_EXPORT Highlighter : public QSyntaxHighlighter +class QMLJSEDITOR_EXPORT Highlighter : public TextEditor::SyntaxHighlighter { Q_OBJECT diff --git a/src/plugins/qt4projectmanager/profilehighlighter.cpp b/src/plugins/qt4projectmanager/profilehighlighter.cpp index 69a7ca2bf4cea1517bc667659b042692823678a5..cf9e72d2ca06cc9cf3325c2e0c1ef6f3a22241a4 100644 --- a/src/plugins/qt4projectmanager/profilehighlighter.cpp +++ b/src/plugins/qt4projectmanager/profilehighlighter.cpp @@ -157,7 +157,7 @@ static bool isFunction(const QString &word) } ProFileHighlighter::ProFileHighlighter(QTextDocument *document) : - QSyntaxHighlighter(document) + TextEditor::SyntaxHighlighter(document) { } diff --git a/src/plugins/qt4projectmanager/profilehighlighter.h b/src/plugins/qt4projectmanager/profilehighlighter.h index 14f3401451bbb198d112e394540c71b5ab013937..87dadce27e147a3c2a034c52186121efc6e3b4ca 100644 --- a/src/plugins/qt4projectmanager/profilehighlighter.h +++ b/src/plugins/qt4projectmanager/profilehighlighter.h @@ -30,6 +30,7 @@ #ifndef PROFILEHIGHLIGHTER_H #define PROFILEHIGHLIGHTER_H +#include <texteditor/syntaxhighlighter.h> #include <QtCore/QtAlgorithms> #include <QtGui/QSyntaxHighlighter> #include <QtGui/QTextCharFormat> @@ -37,7 +38,7 @@ namespace Qt4ProjectManager { namespace Internal { -class ProFileHighlighter : public QSyntaxHighlighter +class ProFileHighlighter : public TextEditor::SyntaxHighlighter { Q_OBJECT public: diff --git a/src/plugins/texteditor/basetextdocument.cpp b/src/plugins/texteditor/basetextdocument.cpp index be2214d7fca7cc5ae21a8f8c63c0d3ece9a32e34..8f6654aaf4fdfa8d0d3a77d667c7010e9d8140c0 100644 --- a/src/plugins/texteditor/basetextdocument.cpp +++ b/src/plugins/texteditor/basetextdocument.cpp @@ -32,6 +32,7 @@ #include "basetextdocumentlayout.h" #include "basetexteditor.h" #include "storagesettings.h" +#include "syntaxhighlighter.h" #include <QtCore/QFile> #include <QtCore/QDir> @@ -357,7 +358,7 @@ void BaseTextDocument::reload(ReloadFlag flag, ChangeType type) } } -void BaseTextDocument::setSyntaxHighlighter(QSyntaxHighlighter *highlighter) +void BaseTextDocument::setSyntaxHighlighter(SyntaxHighlighter *highlighter) { if (m_highlighter) delete m_highlighter; diff --git a/src/plugins/texteditor/basetextdocument.h b/src/plugins/texteditor/basetextdocument.h index 7447d8167d58ab20af09f49c966dc35ff581b465..cd503276b333291e32d8d967b895e60cc05fdcd9 100644 --- a/src/plugins/texteditor/basetextdocument.h +++ b/src/plugins/texteditor/basetextdocument.h @@ -40,11 +40,12 @@ QT_BEGIN_NAMESPACE class QTextCursor; class QTextDocument; -class QSyntaxHighlighter; QT_END_NAMESPACE namespace TextEditor { +class SyntaxHighlighter; + class DocumentMarker : public ITextMarkable { Q_OBJECT @@ -101,8 +102,8 @@ public: virtual void reload(); QTextDocument *document() const { return m_document; } - void setSyntaxHighlighter(QSyntaxHighlighter *highlighter); - QSyntaxHighlighter *syntaxHighlighter() const { return m_highlighter; } + void setSyntaxHighlighter(SyntaxHighlighter *highlighter); + SyntaxHighlighter *syntaxHighlighter() const { return m_highlighter; } inline bool isBinaryData() const { return m_isBinaryData; } @@ -127,7 +128,7 @@ private: TabSettings m_tabSettings; QTextDocument *m_document; DocumentMarker *m_documentMarker; - QSyntaxHighlighter *m_highlighter; + SyntaxHighlighter *m_highlighter; enum LineTerminatorMode { LFLineTerminator, diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp index a21aba0b5a6d8b16be6d7cd6fc5bb14d0d7485ba..eb200ad79b7a9daccb3b03052e02c3917ad55284 100644 --- a/src/plugins/texteditor/basetexteditor.cpp +++ b/src/plugins/texteditor/basetexteditor.cpp @@ -39,6 +39,7 @@ #include "tabsettings.h" #include "texteditorconstants.h" #include "texteditorplugin.h" +#include "syntaxhighlighter.h" #include <aggregation/aggregate.h> #include <coreplugin/actionmanager/actionmanager.h> @@ -4940,7 +4941,7 @@ void BaseTextEditor::setDisplaySettings(const DisplaySettings &ds) setCenterOnScroll(ds.m_centerCursorOnScroll); if (d->m_displaySettings.m_visualizeWhitespace != ds.m_visualizeWhitespace) { - if (QSyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter()) + if (SyntaxHighlighter *highlighter = baseTextDocument()->syntaxHighlighter()) highlighter->rehighlight(); QTextOption option = document()->defaultTextOption(); if (ds.m_visualizeWhitespace) diff --git a/src/plugins/texteditor/generichighlighter/highlighter.cpp b/src/plugins/texteditor/generichighlighter/highlighter.cpp index 00f51ade2b6ec9cc69f7b4fea8b2a8ca06555608..98de86d7fa59c1646efeac9cd1f171861bd91b6a 100644 --- a/src/plugins/texteditor/generichighlighter/highlighter.cpp +++ b/src/plugins/texteditor/generichighlighter/highlighter.cpp @@ -53,7 +53,7 @@ namespace { const Highlighter::KateFormatMap Highlighter::m_kateFormats; Highlighter::Highlighter(QTextDocument *parent) : - QSyntaxHighlighter(parent), + TextEditor::SyntaxHighlighter(parent), m_regionDepth(0), m_indentationBasedFolding(false), m_tabSettings(0), diff --git a/src/plugins/texteditor/generichighlighter/highlighter.h b/src/plugins/texteditor/generichighlighter/highlighter.h index ee3b94019cb6cdfec7987cf67af8a35deb4a23c5..a7135982f213c77f54d9c5db95800ed784444978 100644 --- a/src/plugins/texteditor/generichighlighter/highlighter.h +++ b/src/plugins/texteditor/generichighlighter/highlighter.h @@ -31,6 +31,7 @@ #define HIGHLIGHTER_H #include "basetextdocumentlayout.h" +#include <texteditor/syntaxhighlighter.h> #include <QtCore/QString> #include <QtCore/QVector> @@ -52,8 +53,10 @@ class Context; class HighlightDefinition; class ProgressData; -class Highlighter : public QSyntaxHighlighter +class Highlighter : public TextEditor::SyntaxHighlighter { + Q_OBJECT + public: Highlighter(QTextDocument *parent = 0); virtual ~Highlighter(); diff --git a/src/plugins/texteditor/syntaxhighlighter.cpp b/src/plugins/texteditor/syntaxhighlighter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..59113d99f44994f756eb32b2563e6a4e61dc7008 --- /dev/null +++ b/src/plugins/texteditor/syntaxhighlighter.cpp @@ -0,0 +1,662 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "syntaxhighlighter.h" + +#include <qtextdocument.h> +#include <qtextlayout.h> +#include <qpointer.h> +#include <qtextobject.h> +#include <qtextcursor.h> +#include <qdebug.h> +#include <qtextedit.h> +#include <qtimer.h> + +using namespace TextEditor; + +class TextEditor::SyntaxHighlighterPrivate +{ + SyntaxHighlighter *q_ptr; + Q_DECLARE_PUBLIC(SyntaxHighlighter) +public: + inline SyntaxHighlighterPrivate() + : q_ptr(0), rehighlightPending(false), inReformatBlocks(false) + {} + + QPointer<QTextDocument> doc; + + void _q_reformatBlocks(int from, int charsRemoved, int charsAdded); + void reformatBlocks(int from, int charsRemoved, int charsAdded); + void reformatBlock(const QTextBlock &block); + + inline void rehighlight(QTextCursor &cursor, QTextCursor::MoveOperation operation) { + inReformatBlocks = true; + cursor.beginEditBlock(); + int from = cursor.position(); + cursor.movePosition(operation); + reformatBlocks(from, 0, cursor.position() - from); + cursor.endEditBlock(); + inReformatBlocks = false; + } + + inline void _q_delayedRehighlight() { + if (!rehighlightPending) + return; + rehighlightPending = false; + q_func()->rehighlight(); + } + + void applyFormatChanges(); + QVector<QTextCharFormat> formatChanges; + QTextBlock currentBlock; + bool rehighlightPending; + bool inReformatBlocks; +}; + +void SyntaxHighlighterPrivate::applyFormatChanges() +{ + bool formatsChanged = false; + + QTextLayout *layout = currentBlock.layout(); + + QList<QTextLayout::FormatRange> ranges = layout->additionalFormats(); + + const int preeditAreaStart = layout->preeditAreaPosition(); + const int preeditAreaLength = layout->preeditAreaText().length(); + + if (preeditAreaLength != 0) { + QList<QTextLayout::FormatRange>::Iterator it = ranges.begin(); + while (it != ranges.end()) { + if (it->start >= preeditAreaStart + && it->start + it->length <= preeditAreaStart + preeditAreaLength) { + ++it; + } else { + it = ranges.erase(it); + formatsChanged = true; + } + } + } else if (!ranges.isEmpty()) { + ranges.clear(); + formatsChanged = true; + } + + QTextCharFormat emptyFormat; + + QTextLayout::FormatRange r; + r.start = -1; + + int i = 0; + while (i < formatChanges.count()) { + + while (i < formatChanges.count() && formatChanges.at(i) == emptyFormat) + ++i; + + if (i >= formatChanges.count()) + break; + + r.start = i; + r.format = formatChanges.at(i); + + while (i < formatChanges.count() && formatChanges.at(i) == r.format) + ++i; + + if (i >= formatChanges.count()) + break; + + r.length = i - r.start; + + if (preeditAreaLength != 0) { + if (r.start >= preeditAreaStart) + r.start += preeditAreaLength; + else if (r.start + r.length >= preeditAreaStart) + r.length += preeditAreaLength; + } + + ranges << r; + formatsChanged = true; + r.start = -1; + } + + if (r.start != -1) { + r.length = formatChanges.count() - r.start; + + if (preeditAreaLength != 0) { + if (r.start >= preeditAreaStart) + r.start += preeditAreaLength; + else if (r.start + r.length >= preeditAreaStart) + r.length += preeditAreaLength; + } + + ranges << r; + formatsChanged = true; + } + + if (formatsChanged) { + layout->setAdditionalFormats(ranges); + doc->markContentsDirty(currentBlock.position(), currentBlock.length()); + } +} + +void SyntaxHighlighterPrivate::_q_reformatBlocks(int from, int charsRemoved, int charsAdded) +{ + if (!inReformatBlocks) + reformatBlocks(from, charsRemoved, charsAdded); +} + +void SyntaxHighlighterPrivate::reformatBlocks(int from, int charsRemoved, int charsAdded) +{ + rehighlightPending = false; + + QTextBlock block = doc->findBlock(from); + if (!block.isValid()) + return; + + int endPosition; + QTextBlock lastBlock = doc->findBlock(from + charsAdded + (charsRemoved > 0 ? 1 : 0)); + if (lastBlock.isValid()) + endPosition = lastBlock.position() + lastBlock.length(); + else + endPosition = doc->lastBlock().position() + doc->lastBlock().length(); //doc->docHandle()->length(); + + bool forceHighlightOfNextBlock = false; + + while (block.isValid() && (block.position() < endPosition || forceHighlightOfNextBlock)) { + const int stateBeforeHighlight = block.userState(); + + reformatBlock(block); + + forceHighlightOfNextBlock = (block.userState() != stateBeforeHighlight); + + block = block.next(); + } + + formatChanges.clear(); +} + +void SyntaxHighlighterPrivate::reformatBlock(const QTextBlock &block) +{ + Q_Q(SyntaxHighlighter); + + Q_ASSERT_X(!currentBlock.isValid(), "SyntaxHighlighter::reformatBlock()", "reFormatBlock() called recursively"); + + currentBlock = block; + + formatChanges.fill(QTextCharFormat(), block.length() - 1); + q->highlightBlock(block.text()); + applyFormatChanges(); + + currentBlock = QTextBlock(); +} + +/*! + \class SyntaxHighlighter + \reentrant + + \brief The SyntaxHighlighter class allows you to define syntax + highlighting rules, and in addition you can use the class to query + a document's current formatting or user data. + + \since 4.1 + + \ingroup richtext-processing + + The SyntaxHighlighter class is a base class for implementing + QTextEdit syntax highlighters. A syntax highligher automatically + highlights parts of the text in a QTextEdit, or more generally in + a QTextDocument. Syntax highlighters are often used when the user + is entering text in a specific format (for example source code) + and help the user to read the text and identify syntax errors. + + To provide your own syntax highlighting, you must subclass + SyntaxHighlighter and reimplement highlightBlock(). + + When you create an instance of your SyntaxHighlighter subclass, + pass it the QTextEdit or QTextDocument that you want the syntax + highlighting to be applied to. For example: + + \snippet doc/src/snippets/code/src_gui_text_SyntaxHighlighter.cpp 0 + + After this your highlightBlock() function will be called + automatically whenever necessary. Use your highlightBlock() + function to apply formatting (e.g. setting the font and color) to + the text that is passed to it. SyntaxHighlighter provides the + setFormat() function which applies a given QTextCharFormat on + the current text block. For example: + + \snippet doc/src/snippets/code/src_gui_text_SyntaxHighlighter.cpp 1 + + Some syntaxes can have constructs that span several text + blocks. For example, a C++ syntax highlighter should be able to + cope with \c{/}\c{*...*}\c{/} multiline comments. To deal with + these cases it is necessary to know the end state of the previous + text block (e.g. "in comment"). + + Inside your highlightBlock() implementation you can query the end + state of the previous text block using the previousBlockState() + function. After parsing the block you can save the last state + using setCurrentBlockState(). + + The currentBlockState() and previousBlockState() functions 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 setCurrentBlockState() function. Once the state is set the + QTextBlock keeps that value until it is set set again or until the + corresponding paragraph of text is deleted. + + For example, if you're writing a simple C++ syntax highlighter, + you might designate 1 to signify "in comment": + + \snippet doc/src/snippets/code/src_gui_text_SyntaxHighlighter.cpp 2 + + In the example above, we first set the current block state to + 0. Then, if the previous block ended within a comment, we higlight + from the beginning of the current block (\c {startIndex = + 0}). Otherwise, we search for the given start expression. If the + specified end expression cannot be found in the text block, we + change the current block state by calling setCurrentBlockState(), + and make sure that the rest of the block is higlighted. + + In addition you can query the current formatting and user data + using the format() and currentBlockUserData() functions + respectively. You can also attach user data to the current text + block using the setCurrentBlockUserData() function. + QTextBlockUserData can be used to store custom settings. In the + case of syntax highlighting, it is in particular interesting as + cache storage for information that you may figure out while + parsing the paragraph's text. For an example, see the + setCurrentBlockUserData() documentation. + + \sa QTextEdit, {Syntax Highlighter Example} +*/ + +/*! + Constructs a SyntaxHighlighter with the given \a parent. +*/ +SyntaxHighlighter::SyntaxHighlighter(QObject *parent) + : QObject(parent), d_ptr(new SyntaxHighlighterPrivate) +{ + d_ptr->q_ptr = this; +} + +/*! + Constructs a SyntaxHighlighter and installs it on \a parent. + The specified QTextDocument also becomes the owner of the + SyntaxHighlighter. +*/ +SyntaxHighlighter::SyntaxHighlighter(QTextDocument *parent) + : QObject(parent), d_ptr(new SyntaxHighlighterPrivate) +{ + d_ptr->q_ptr = this; + setDocument(parent); +} + +/*! + Constructs a SyntaxHighlighter and installs it on \a parent 's + QTextDocument. The specified QTextEdit also becomes the owner of + the SyntaxHighlighter. +*/ +SyntaxHighlighter::SyntaxHighlighter(QTextEdit *parent) + : QObject(parent), d_ptr(new SyntaxHighlighterPrivate) +{ + d_ptr->q_ptr = this; + setDocument(parent->document()); +} + +/*! + Destructor. Uninstalls this syntax highlighter from the text document. +*/ +SyntaxHighlighter::~SyntaxHighlighter() +{ + setDocument(0); +} + +/*! + Installs the syntax highlighter on the given QTextDocument \a doc. + A SyntaxHighlighter can only be used with one document at a time. +*/ +void SyntaxHighlighter::setDocument(QTextDocument *doc) +{ + Q_D(SyntaxHighlighter); + if (d->doc) { + disconnect(d->doc, SIGNAL(contentsChange(int,int,int)), + this, SLOT(_q_reformatBlocks(int,int,int))); + + QTextCursor cursor(d->doc); + cursor.beginEditBlock(); + for (QTextBlock blk = d->doc->begin(); blk.isValid(); blk = blk.next()) + blk.layout()->clearAdditionalFormats(); + cursor.endEditBlock(); + } + d->doc = doc; + if (d->doc) { + connect(d->doc, SIGNAL(contentsChange(int,int,int)), + this, SLOT(_q_reformatBlocks(int,int,int))); + d->rehighlightPending = true; + QTimer::singleShot(0, this, SLOT(_q_delayedRehighlight())); + } +} + +/*! + Returns the QTextDocument on which this syntax highlighter is + installed. +*/ +QTextDocument *SyntaxHighlighter::document() const +{ + Q_D(const SyntaxHighlighter); + return d->doc; +} + +/*! + \since 4.2 + + Reapplies the highlighting to the whole document. + + \sa rehighlightBlock() +*/ +void SyntaxHighlighter::rehighlight() +{ + Q_D(SyntaxHighlighter); + if (!d->doc) + return; + + QTextCursor cursor(d->doc); + d->rehighlight(cursor, QTextCursor::End); +} + +/*! + \since 4.6 + + Reapplies the highlighting to the given QTextBlock \a block. + + \sa rehighlight() +*/ +void SyntaxHighlighter::rehighlightBlock(const QTextBlock &block) +{ + Q_D(SyntaxHighlighter); + if (!d->doc || !block.isValid() || block.document() != d->doc) + return; + + const bool rehighlightPending = d->rehighlightPending; + + QTextCursor cursor(block); + d->rehighlight(cursor, QTextCursor::EndOfBlock); + + if (rehighlightPending) + d->rehighlightPending = rehighlightPending; +} + +/*! + \fn void SyntaxHighlighter::highlightBlock(const QString &text) + + Highlights the given text block. This function is called when + necessary by the rich text engine, i.e. on text blocks which have + changed. + + To provide your own syntax highlighting, you must subclass + SyntaxHighlighter and reimplement highlightBlock(). In your + reimplementation you should parse the block's \a text and call + setFormat() as often as necessary to apply any font and color + changes that you require. For example: + + \snippet doc/src/snippets/code/src_gui_text_SyntaxHighlighter.cpp 3 + + Some syntaxes can have constructs that span several text + blocks. For example, a C++ syntax highlighter should be able to + cope with \c{/}\c{*...*}\c{/} multiline comments. To deal with + these cases it is necessary to know the end state of the previous + text block (e.g. "in comment"). + + Inside your highlightBlock() implementation you can query the end + state of the previous text block using the previousBlockState() + function. After parsing the block you can save the last state + using setCurrentBlockState(). + + The currentBlockState() and previousBlockState() functions 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 setCurrentBlockState() function. Once the state is set the + QTextBlock keeps that value until it is set set again or until the + corresponding paragraph of text gets deleted. + + For example, if you're writing a simple C++ syntax highlighter, + you might designate 1 to signify "in comment". For a text block + that ended in the middle of a comment you'd set 1 using + setCurrentBlockState, and for other paragraphs you'd set 0. + In your parsing code if the return value of previousBlockState() + is 1, you would highlight the text as a C++ comment until you + reached the closing \c{*}\c{/}. + + \sa previousBlockState(), setFormat(), setCurrentBlockState() +*/ + +/*! + This function is applied to the syntax highlighter's current text + block (i.e. the text that is passed to the highlightBlock() + function). + + The specified \a format is applied to the text from the \a start + position for a length of \a count characters (if \a count is 0, + nothing is done). The formatting properties set in \a format are + merged at display time with the formatting information stored + directly in the document, for example as previously set with + QTextCursor's functions. Note that the document itself remains + unmodified by the format set through this function. + + \sa format(), highlightBlock() +*/ +void SyntaxHighlighter::setFormat(int start, int count, const QTextCharFormat &format) +{ + Q_D(SyntaxHighlighter); + if (start < 0 || start >= d->formatChanges.count()) + return; + + const int end = qMin(start + count, d->formatChanges.count()); + for (int i = start; i < end; ++i) + d->formatChanges[i] = format; +} + +/*! + \overload + + The specified \a color is applied to the current text block from + the \a start position for a length of \a count characters. + + The other attributes of the current text block, e.g. the font and + background color, are reset to default values. + + \sa format(), highlightBlock() +*/ +void SyntaxHighlighter::setFormat(int start, int count, const QColor &color) +{ + QTextCharFormat format; + format.setForeground(color); + setFormat(start, count, format); +} + +/*! + \overload + + The specified \a font is applied to the current text block from + the \a start position for a length of \a count characters. + + The other attributes of the current text block, e.g. the font and + background color, are reset to default values. + + \sa format(), highlightBlock() +*/ +void SyntaxHighlighter::setFormat(int start, int count, const QFont &font) +{ + QTextCharFormat format; + format.setFont(font); + setFormat(start, count, format); +} + +/*! + \fn QTextCharFormat SyntaxHighlighter::format(int position) const + + Returns the format at \a position inside the syntax highlighter's + current text block. +*/ +QTextCharFormat SyntaxHighlighter::format(int pos) const +{ + Q_D(const SyntaxHighlighter); + if (pos < 0 || pos >= d->formatChanges.count()) + return QTextCharFormat(); + return d->formatChanges.at(pos); +} + +/*! + Returns the end state of the text block previous to the + syntax highlighter's current block. If no value was + previously set, the returned value is -1. + + \sa highlightBlock(), setCurrentBlockState() +*/ +int SyntaxHighlighter::previousBlockState() const +{ + Q_D(const SyntaxHighlighter); + if (!d->currentBlock.isValid()) + return -1; + + const QTextBlock previous = d->currentBlock.previous(); + if (!previous.isValid()) + return -1; + + return previous.userState(); +} + +/*! + Returns the state of the current text block. If no value is set, + the returned value is -1. +*/ +int SyntaxHighlighter::currentBlockState() const +{ + Q_D(const SyntaxHighlighter); + if (!d->currentBlock.isValid()) + return -1; + + return d->currentBlock.userState(); +} + +/*! + Sets the state of the current text block to \a newState. + + \sa highlightBlock() +*/ +void SyntaxHighlighter::setCurrentBlockState(int newState) +{ + Q_D(SyntaxHighlighter); + if (!d->currentBlock.isValid()) + return; + + d->currentBlock.setUserState(newState); +} + +/*! + Attaches the given \a data to the current text block. The + ownership is passed to the underlying text document, i.e. the + provided QTextBlockUserData object will be deleted if the + corresponding text block gets deleted. + + QTextBlockUserData can be used to store custom settings. In the + case of syntax highlighting, it is in particular interesting as + cache storage for information that you may figure out while + parsing the paragraph's text. + + For example while parsing the text, you can keep track of + parenthesis characters that you encounter ('{[(' and the like), + and store their relative position and the actual QChar in a simple + class derived from QTextBlockUserData: + + \snippet doc/src/snippets/code/src_gui_text_SyntaxHighlighter.cpp 4 + + During cursor navigation in the associated editor, you can ask the + current QTextBlock (retrieved using the QTextCursor::block() + function) if it has a user data object set and cast it to your \c + BlockData object. Then you can check if the current cursor + position matches with a previously recorded parenthesis position, + and, depending on the type of parenthesis (opening or closing), + find the next opening or closing parenthesis on the same level. + + In this way you can do a visual parenthesis matching and highlight + from the current cursor position to the matching parenthesis. That + makes it easier to spot a missing parenthesis in your code and to + find where a corresponding opening/closing parenthesis is when + editing parenthesis intensive code. + + \sa QTextBlock::setUserData() +*/ +void SyntaxHighlighter::setCurrentBlockUserData(QTextBlockUserData *data) +{ + Q_D(SyntaxHighlighter); + if (!d->currentBlock.isValid()) + return; + + d->currentBlock.setUserData(data); +} + +/*! + Returns the QTextBlockUserData object previously attached to the + current text block. + + \sa QTextBlock::userData(), setCurrentBlockUserData() +*/ +QTextBlockUserData *SyntaxHighlighter::currentBlockUserData() const +{ + Q_D(const SyntaxHighlighter); + if (!d->currentBlock.isValid()) + return 0; + + return d->currentBlock.userData(); +} + +/*! + \since 4.4 + + Returns the current text block. +*/ +QTextBlock SyntaxHighlighter::currentBlock() const +{ + Q_D(const SyntaxHighlighter); + return d->currentBlock; +} + +#include "moc_syntaxhighlighter.cpp" diff --git a/src/plugins/texteditor/syntaxhighlighter.h b/src/plugins/texteditor/syntaxhighlighter.h new file mode 100644 index 0000000000000000000000000000000000000000..aadbfb46d2831a913aa5a8973d809e78da2e4505 --- /dev/null +++ b/src/plugins/texteditor/syntaxhighlighter.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TEXTEDITOR_SYNTAXHIGHLIGHTER_H +#define TEXTEDITOR_SYNTAXHIGHLIGHTER_H + +#include "texteditor_global.h" +#include <QtCore/qglobal.h> +#include <QtCore/qobject.h> +#include <QtGui/qtextobject.h> + +QT_BEGIN_NAMESPACE +class QTextDocument; +class QSyntaxHighlighterPrivate; +class QTextCharFormat; +class QFont; +class QColor; +class QTextBlockUserData; +class QTextEdit; +QT_END_NAMESPACE + +namespace TextEditor { + +class SyntaxHighlighterPrivate; + +class TEXTEDITOR_EXPORT SyntaxHighlighter : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(SyntaxHighlighter) +public: + SyntaxHighlighter(QObject *parent); + SyntaxHighlighter(QTextDocument *parent); + SyntaxHighlighter(QTextEdit *parent); + virtual ~SyntaxHighlighter(); + + void setDocument(QTextDocument *doc); + QTextDocument *document() const; + +public Q_SLOTS: + void rehighlight(); + void rehighlightBlock(const QTextBlock &block); + +protected: + virtual void highlightBlock(const QString &text) = 0; + + void setFormat(int start, int count, const QTextCharFormat &format); + void setFormat(int start, int count, const QColor &color); + void setFormat(int start, int count, const QFont &font); + QTextCharFormat format(int pos) const; + + int previousBlockState() const; + int currentBlockState() const; + void setCurrentBlockState(int newState); + + void setCurrentBlockUserData(QTextBlockUserData *data); + QTextBlockUserData *currentBlockUserData() const; + + QTextBlock currentBlock() const; + +private: + Q_DISABLE_COPY(SyntaxHighlighter) + Q_PRIVATE_SLOT(d_ptr, void _q_reformatBlocks(int from, int charsRemoved, int charsAdded)) + Q_PRIVATE_SLOT(d_ptr, void _q_delayedRehighlight()) + + QScopedPointer<SyntaxHighlighterPrivate> d_ptr; +}; + +} // end of namespace TextEditor + +#endif // TEXTEDITOR_SYNTAXHIGHLIGHTER_H diff --git a/src/plugins/texteditor/texteditor.pro b/src/plugins/texteditor/texteditor.pro index 931f5614903fb7b26adadd725989b337e23da659..55cb8c92c6ca1414aaad5808c5fa08d2ad907924 100644 --- a/src/plugins/texteditor/texteditor.pro +++ b/src/plugins/texteditor/texteditor.pro @@ -42,6 +42,7 @@ SOURCES += texteditorplugin.cpp \ normalindenter.cpp \ indenter.cpp \ quickfix.cpp \ + syntaxhighlighter.cpp \ generichighlighter/itemdata.cpp \ generichighlighter/specificrules.cpp \ generichighlighter/rule.cpp \ @@ -103,6 +104,7 @@ HEADERS += texteditorplugin.h \ normalindenter.h \ indenter.h \ quickfix.h \ + syntaxhighlighter.h \ generichighlighter/reuse.h \ generichighlighter/itemdata.h \ generichighlighter/specificrules.h \ diff --git a/src/plugins/vcsbase/baseannotationhighlighter.cpp b/src/plugins/vcsbase/baseannotationhighlighter.cpp index 7ce605845347599d1e0066adda4621ea7cc4b71b..b272ea03aedc4d274d3b4e02893f6a2fe7df7123 100644 --- a/src/plugins/vcsbase/baseannotationhighlighter.cpp +++ b/src/plugins/vcsbase/baseannotationhighlighter.cpp @@ -47,7 +47,7 @@ struct BaseAnnotationHighlighterPrivate { BaseAnnotationHighlighter::BaseAnnotationHighlighter(const ChangeNumbers &changeNumbers, QTextDocument *document) : - QSyntaxHighlighter(document), + TextEditor::SyntaxHighlighter(document), m_d(new BaseAnnotationHighlighterPrivate) { setChangeNumbers(changeNumbers); diff --git a/src/plugins/vcsbase/baseannotationhighlighter.h b/src/plugins/vcsbase/baseannotationhighlighter.h index 4119adb109812a67effac831e46b40b5504c7c39..2859745aed70a532201c1ad9c07f915a7e564ad2 100644 --- a/src/plugins/vcsbase/baseannotationhighlighter.h +++ b/src/plugins/vcsbase/baseannotationhighlighter.h @@ -31,7 +31,7 @@ #define BASEANNOTATIONHIGHLIGHTER_H #include "vcsbase_global.h" - +#include <texteditor/syntaxhighlighter.h> #include <QtCore/QMap> #include <QtCore/QSet> #include <QtGui/QSyntaxHighlighter> @@ -47,7 +47,7 @@ struct BaseAnnotationHighlighterPrivate; // 112: text1 <color 1> // 113: text2 <color 2> // 112: text3 <color 1> -class VCSBASE_EXPORT BaseAnnotationHighlighter : public QSyntaxHighlighter +class VCSBASE_EXPORT BaseAnnotationHighlighter : public TextEditor::SyntaxHighlighter { Q_OBJECT public: diff --git a/src/plugins/vcsbase/diffhighlighter.cpp b/src/plugins/vcsbase/diffhighlighter.cpp index 7628763d627427c953385a3481110de4e1a8ccef..6b1ca5dee154e1eacb18610d7199e62f28b440d9 100644 --- a/src/plugins/vcsbase/diffhighlighter.cpp +++ b/src/plugins/vcsbase/diffhighlighter.cpp @@ -88,7 +88,7 @@ DiffFormats DiffHighlighterPrivate::analyzeLine(const QString &text) const // --- DiffHighlighter DiffHighlighter::DiffHighlighter(const QRegExp &filePattern, QTextDocument *document) : - QSyntaxHighlighter(document), + TextEditor::SyntaxHighlighter(document), m_d(new DiffHighlighterPrivate(filePattern)) { } diff --git a/src/plugins/vcsbase/diffhighlighter.h b/src/plugins/vcsbase/diffhighlighter.h index a1a9c3d082a1cb0f09053e8bb7d091cc5f236149..346b87ea3dba1d69405e49a9b56b1107a3aa66d4 100644 --- a/src/plugins/vcsbase/diffhighlighter.h +++ b/src/plugins/vcsbase/diffhighlighter.h @@ -31,8 +31,7 @@ #define DIFFHIGHLIGHTER_H #include "vcsbase_global.h" - -#include <QtGui/QSyntaxHighlighter> +#include <texteditor/syntaxhighlighter.h> #include <QtGui/QTextCharFormat> #include <QtCore/QVector> @@ -66,7 +65,7 @@ struct DiffHighlighterPrivate; * \endcode * */ -class VCSBASE_EXPORT DiffHighlighter : public QSyntaxHighlighter +class VCSBASE_EXPORT DiffHighlighter : public TextEditor::SyntaxHighlighter { Q_OBJECT public: