Commit 15faf23e authored by Leandro Melo's avatar Leandro Melo
Browse files

Generic highlighter: Indentation based code folding.

parent ec249304
......@@ -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; }
......@@ -80,6 +80,9 @@ public:
void setMultiLineCommentRegion(const QString &region);
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
......
......@@ -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;
......
......@@ -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.
......
......@@ -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());
}
}
}
......@@ -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;
......
......@@ -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()));
......
......@@ -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();
......
......@@ -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(){}
......
......@@ -18,7 +18,8 @@ SOURCES += tst_highlighterengine.cpp \
HEADERS += \
highlightermock.h \
basetextdocumentlayout.h \
formats.h
formats.h \
tabsettings.h
INCLUDEPATH += $$GENERICHIGHLIGHTERDIR
......
/**************************************************************************
**
** 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
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment