From 15faf23e07dc206215841cb87e10a95d89ee0c25 Mon Sep 17 00:00:00 2001 From: Leandro Melo <leandro.melo@nokia.com> Date: Mon, 21 Jun 2010 13:37:53 +0200 Subject: [PATCH] Generic highlighter: Indentation based code folding. --- .../highlightdefinition.cpp | 9 +- .../generichighlighter/highlightdefinition.h | 5 ++ .../highlightdefinitionhandler.cpp | 9 ++ .../highlightdefinitionhandler.h | 1 + .../generichighlighter/highlighter.cpp | 90 ++++++++++++++----- .../generichighlighter/highlighter.h | 12 ++- src/plugins/texteditor/plaintexteditor.cpp | 11 +++ src/plugins/texteditor/plaintexteditor.h | 1 + .../basetextdocumentlayout.h | 8 +- .../highlighterengine/highlighterengine.pro | 3 +- .../highlighterengine/tabsettings.h | 46 ++++++++++ 11 files changed, 164 insertions(+), 31 deletions(-) create mode 100644 tests/auto/generichighlighter/highlighterengine/tabsettings.h diff --git a/src/plugins/texteditor/generichighlighter/highlightdefinition.cpp b/src/plugins/texteditor/generichighlighter/highlightdefinition.cpp index 4d6d92c8974..c06b49d1e0f 100644 --- a/src/plugins/texteditor/generichighlighter/highlightdefinition.cpp +++ b/src/plugins/texteditor/generichighlighter/highlightdefinition.cpp @@ -42,7 +42,8 @@ using namespace Internal; HighlightDefinition::HighlightDefinition() : m_delimiters(QLatin1String(".():!+,-<=>%&/;?[]^{|}~\\*, \t")), m_singleLineCommentAfterWhiteSpaces(false), - m_keywordCaseSensitivity(Qt::CaseSensitive) + m_keywordCaseSensitivity(Qt::CaseSensitive), + m_indentationBasedFolding(false) {} HighlightDefinition::~HighlightDefinition() @@ -165,3 +166,9 @@ void HighlightDefinition::setKeywordsSensitive(const QString &sensitivity) Qt::CaseSensitivity HighlightDefinition::keywordsSensitive() const { return m_keywordCaseSensitivity; } + +void HighlightDefinition::setIndentationBasedFolding(const QString &indentationBasedFolding) +{ m_indentationBasedFolding = toBool(indentationBasedFolding); } + +bool HighlightDefinition::isIndentationBasedFolding() const +{ return m_indentationBasedFolding; } diff --git a/src/plugins/texteditor/generichighlighter/highlightdefinition.h b/src/plugins/texteditor/generichighlighter/highlightdefinition.h index dd94838d870..ca7afd93761 100644 --- a/src/plugins/texteditor/generichighlighter/highlightdefinition.h +++ b/src/plugins/texteditor/generichighlighter/highlightdefinition.h @@ -80,6 +80,9 @@ public: void setMultiLineCommentRegion(const QString ®ion); const QString &multiLineCommentRegion() const; + void setIndentationBasedFolding(const QString &indentationBasedFolding); + bool isIndentationBasedFolding() const; + private: Q_DISABLE_COPY(HighlightDefinition) @@ -109,6 +112,8 @@ private: QString m_multiLineCommentRegion; Qt::CaseSensitivity m_keywordCaseSensitivity; + + bool m_indentationBasedFolding; }; } // namespace Internal diff --git a/src/plugins/texteditor/generichighlighter/highlightdefinitionhandler.cpp b/src/plugins/texteditor/generichighlighter/highlightdefinitionhandler.cpp index a157d944611..58a7b4bcd35 100644 --- a/src/plugins/texteditor/generichighlighter/highlightdefinitionhandler.cpp +++ b/src/plugins/texteditor/generichighlighter/highlightdefinitionhandler.cpp @@ -103,6 +103,8 @@ namespace { static const QLatin1String kLanguage("language"); static const QLatin1String kExtensions("extensions"); static const QLatin1String kIncludeAttrib("includeAttrib"); + static const QLatin1String kFolding("folding"); + static const QLatin1String kIndentationSensitive("indentationsensitive"); static const QLatin1String kHash("#"); static const QLatin1String kDoubleHash("##"); } @@ -145,6 +147,8 @@ bool HighlightDefinitionHandler::startElement(const QString &, commentElementStarted(atts); } else if (qName == kKeywords) { keywordsElementStarted(atts); + } else if (qName == kFolding) { + foldingElementStarted(atts); } else if (qName == kDetectChar) { detectCharStarted(atts); } else if (qName == kDetect2Chars) { @@ -290,6 +294,11 @@ void HighlightDefinitionHandler::keywordsElementStarted(const QXmlAttributes &at //@todo: wordWrapDelimiters? } +void HighlightDefinitionHandler::foldingElementStarted(const QXmlAttributes &atts) const +{ + m_definition->setIndentationBasedFolding(atts.value(kIndentationSensitive)); +} + void HighlightDefinitionHandler::detectCharStarted(const QXmlAttributes &atts) { DetectCharRule *rule = new DetectCharRule; diff --git a/src/plugins/texteditor/generichighlighter/highlightdefinitionhandler.h b/src/plugins/texteditor/generichighlighter/highlightdefinitionhandler.h index e08be97649f..672a3684886 100644 --- a/src/plugins/texteditor/generichighlighter/highlightdefinitionhandler.h +++ b/src/plugins/texteditor/generichighlighter/highlightdefinitionhandler.h @@ -65,6 +65,7 @@ private: void itemDataElementStarted(const QXmlAttributes &atts) const; void commentElementStarted(const QXmlAttributes &atts) const; void keywordsElementStarted(const QXmlAttributes &atts) const; + void foldingElementStarted(const QXmlAttributes &atts) const; void ruleElementStarted(const QXmlAttributes &atts, const QSharedPointer<Rule> &rule); // Specific rules. diff --git a/src/plugins/texteditor/generichighlighter/highlighter.cpp b/src/plugins/texteditor/generichighlighter/highlighter.cpp index aba84b8c4f8..9ec1046431c 100644 --- a/src/plugins/texteditor/generichighlighter/highlighter.cpp +++ b/src/plugins/texteditor/generichighlighter/highlighter.cpp @@ -35,6 +35,7 @@ #include "highlighterexception.h" #include "progressdata.h" #include "reuse.h" +#include "tabsettings.h" #include <QtCore/QLatin1String> #include <QtCore/QLatin1Char> @@ -54,6 +55,8 @@ const Highlighter::KateFormatMap Highlighter::m_kateFormats; Highlighter::Highlighter(QTextDocument *parent) : QSyntaxHighlighter(parent), m_regionDepth(0), + m_indentationBasedFolding(false), + m_tabSettings(0), m_persistentObservableStatesCounter(PersistentsStart), m_dynamicContextsCounter(0), m_isBroken(false) @@ -95,6 +98,12 @@ void Highlighter::setDefaultContext(const QSharedPointer<Context> &defaultConte { m_defaultContext = defaultContext; m_persistentObservableStates.insert(m_defaultContext->name(), Default); + m_indentationBasedFolding = defaultContext->definition()->isIndentationBasedFolding(); +} + +void Highlighter::setTabSettings(const TabSettings &ts) +{ + m_tabSettings = &ts; } void Highlighter::highlightBlock(const QString &text) @@ -118,11 +127,14 @@ void Highlighter::highlightBlock(const QString &text) false); m_contexts.clear(); - applyFolding(); + if (m_indentationBasedFolding) { + applyIndentationBasedFolding(text); + } else { + applyRegionBasedFolding(); - // Takes into the account any change that might have affected the region depth since - // the last time the state was set. - setCurrentBlockState(computeState(extractObservableState(currentBlockState()))); + // In the case region depth has changed since the last time the state was set. + setCurrentBlockState(computeState(extractObservableState(currentBlockState()))); + } } catch (const HighlighterException &) { m_isBroken = true; } @@ -220,24 +232,25 @@ void Highlighter::iterateThroughRules(const QString &text, if (rule->matchSucceed(text, length, progress)) { atLeastOneMatch = true; - // Code folding. - if (!rule->beginRegion().isEmpty()) { - blockData(currentBlockUserData())->m_foldingRegions.push(rule->beginRegion()); - ++m_regionDepth; - if (progress->isOpeningBraceMatchAtFirstNonSpace()) - ++blockData(currentBlockUserData())->m_foldingIndentDelta; - } - if (!rule->endRegion().isEmpty()) { - QStack<QString> *currentRegions = - &blockData(currentBlockUserData())->m_foldingRegions; - if (!currentRegions->isEmpty() && rule->endRegion() == currentRegions->top()) { - currentRegions->pop(); - --m_regionDepth; - if (progress->isClosingBraceMatchAtNonEnd()) - --blockData(currentBlockUserData())->m_foldingIndentDelta; + if (!m_indentationBasedFolding) { + if (!rule->beginRegion().isEmpty()) { + blockData(currentBlockUserData())->m_foldingRegions.push(rule->beginRegion()); + ++m_regionDepth; + if (progress->isOpeningBraceMatchAtFirstNonSpace()) + ++blockData(currentBlockUserData())->m_foldingIndentDelta; } + if (!rule->endRegion().isEmpty()) { + QStack<QString> *currentRegions = + &blockData(currentBlockUserData())->m_foldingRegions; + if (!currentRegions->isEmpty() && rule->endRegion() == currentRegions->top()) { + currentRegions->pop(); + --m_regionDepth; + if (progress->isClosingBraceMatchAtNonEnd()) + --blockData(currentBlockUserData())->m_foldingIndentDelta; + } + } + progress->clearBracesMatches(); } - progress->clearBracesMatches(); if (progress->isWillContinueLine()) { createWillContinueBlock(); @@ -524,7 +537,7 @@ int Highlighter::computeState(const int observableState) const return m_regionDepth << 12 | observableState; } -void Highlighter::applyFolding() const +void Highlighter::applyRegionBasedFolding() const { int folding = 0; BlockData *data = blockData(currentBlockUserData()); @@ -543,3 +556,38 @@ void Highlighter::applyFolding() const data->setFoldingEndIncluded(true); data->setFoldingIndent(folding); } + +void Highlighter::applyIndentationBasedFolding(const QString &text) const +{ + BlockData *data = blockData(currentBlockUserData()); + data->setFoldingEndIncluded(true); + + // If this line is empty, check its neighbours. They all might be part of the same block. + if (text.trimmed().isEmpty()) { + data->setFoldingIndent(0); + const int previousIndent = neighbouringNonEmptyBlockIndent(currentBlock().previous(), true); + if (previousIndent > 0) { + const int nextIndent = neighbouringNonEmptyBlockIndent(currentBlock().next(), false); + if (previousIndent == nextIndent) + data->setFoldingIndent(previousIndent); + } + } else { + data->setFoldingIndent(m_tabSettings->indentationColumn(text)); + } +} + +int Highlighter::neighbouringNonEmptyBlockIndent(QTextBlock block, const bool previous) const +{ + while (true) { + if (!block.isValid()) + return 0; + if (block.text().trimmed().isEmpty()) { + if (previous) + block = block.previous(); + else + block = block.next(); + } else { + return m_tabSettings->indentationColumn(block.text()); + } + } +} diff --git a/src/plugins/texteditor/generichighlighter/highlighter.h b/src/plugins/texteditor/generichighlighter/highlighter.h index 7ca7e9d649d..ee3b94019cb 100644 --- a/src/plugins/texteditor/generichighlighter/highlighter.h +++ b/src/plugins/texteditor/generichighlighter/highlighter.h @@ -42,6 +42,9 @@ #include <QtGui/QTextCharFormat> namespace TextEditor { + +struct TabSettings; + namespace Internal { class Rule; @@ -72,8 +75,9 @@ public: RegionMarker, Others }; - void configureFormat(TextFormatId id, const QTextCharFormat &format); + void configureFormat(TextFormatId id, const QTextCharFormat &format); + void setTabSettings(const TabSettings &ts); void setDefaultContext(const QSharedPointer<Context> &defaultContext); protected: @@ -118,7 +122,9 @@ private: const QSharedPointer<HighlightDefinition> &definition); void applyVisualWhitespaceFormat(const QString &text); - void applyFolding() const; + void applyRegionBasedFolding() const; + void applyIndentationBasedFolding(const QString &text) const; + int neighbouringNonEmptyBlockIndent(QTextBlock block, const bool previous) const; // Mapping from Kate format strings to format ids. struct KateFormatMap @@ -166,6 +172,8 @@ private: static int extractObservableState(const int state); int m_regionDepth; + bool m_indentationBasedFolding; + const TabSettings *m_tabSettings; int m_persistentObservableStatesCounter; int m_dynamicContextsCounter; diff --git a/src/plugins/texteditor/plaintexteditor.cpp b/src/plugins/texteditor/plaintexteditor.cpp index d945b166711..ce933292c04 100644 --- a/src/plugins/texteditor/plaintexteditor.cpp +++ b/src/plugins/texteditor/plaintexteditor.cpp @@ -153,6 +153,17 @@ void PlainTextEditor::setFontSettings(const FontSettings &fs) } } +void PlainTextEditor::setTabSettings(const TextEditor::TabSettings &ts) +{ + BaseTextEditor::setTabSettings(ts); + + if (baseTextDocument()->syntaxHighlighter()) { + Highlighter *highlighter = + static_cast<Highlighter *>(baseTextDocument()->syntaxHighlighter()); + highlighter->setTabSettings(ts); + } +} + void PlainTextEditor::fileChanged() { configure(Core::ICore::instance()->mimeDatabase()->findByFile(file()->fileName())); diff --git a/src/plugins/texteditor/plaintexteditor.h b/src/plugins/texteditor/plaintexteditor.h index d027ef07ff4..c57aba920c7 100644 --- a/src/plugins/texteditor/plaintexteditor.h +++ b/src/plugins/texteditor/plaintexteditor.h @@ -76,6 +76,7 @@ public: public slots: virtual void unCommentSelection(); virtual void setFontSettings(const FontSettings &fs); + virtual void setTabSettings(const TextEditor::TabSettings &); private slots: void fileChanged(); diff --git a/tests/auto/generichighlighter/highlighterengine/basetextdocumentlayout.h b/tests/auto/generichighlighter/highlighterengine/basetextdocumentlayout.h index f3433db6327..8687354a54e 100644 --- a/tests/auto/generichighlighter/highlighterengine/basetextdocumentlayout.h +++ b/tests/auto/generichighlighter/highlighterengine/basetextdocumentlayout.h @@ -30,14 +30,10 @@ #ifndef BASETEXTDOCUMENTLAYOUT_H #define BASETEXTDOCUMENTLAYOUT_H -/* - Since the text editor plugin directory is not included in the search list of the pro file, this - file replaces the "real" basetextdocumentlayout.h file. The objective is to provide a simple - TextBlockUserData and avoid "external" dependencies or intrusive defines. - */ - #include <QtGui/QTextBlockUserData> +// Replaces the "real" basetextdocumentlayout.h file. + struct TextBlockUserData : QTextBlockUserData { virtual ~TextBlockUserData(){} diff --git a/tests/auto/generichighlighter/highlighterengine/highlighterengine.pro b/tests/auto/generichighlighter/highlighterengine/highlighterengine.pro index dec3fa3bd89..7e60b51ea1e 100644 --- a/tests/auto/generichighlighter/highlighterengine/highlighterengine.pro +++ b/tests/auto/generichighlighter/highlighterengine/highlighterengine.pro @@ -18,7 +18,8 @@ SOURCES += tst_highlighterengine.cpp \ HEADERS += \ highlightermock.h \ basetextdocumentlayout.h \ - formats.h + formats.h \ + tabsettings.h INCLUDEPATH += $$GENERICHIGHLIGHTERDIR diff --git a/tests/auto/generichighlighter/highlighterengine/tabsettings.h b/tests/auto/generichighlighter/highlighterengine/tabsettings.h new file mode 100644 index 00000000000..b79bbdbaf81 --- /dev/null +++ b/tests/auto/generichighlighter/highlighterengine/tabsettings.h @@ -0,0 +1,46 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef TABSETTINGS_H +#define TABSETTINGS_H + +#include <QString> + +// Replaces the "real" tabsettings.h file. + +namespace TextEditor { + +struct TabSettings +{ + int indentationColumn(const QString &) const { return 0; } +}; + +} + +#endif // TABSETTINGS_H -- GitLab