forked from qt-creator/qt-creator
Generic highlighter: Code folding support.
This commit is contained in:
@@ -53,7 +53,8 @@ const Highlighter::KateFormatMap Highlighter::m_kateFormats;
|
|||||||
|
|
||||||
Highlighter::Highlighter(QTextDocument *parent) :
|
Highlighter::Highlighter(QTextDocument *parent) :
|
||||||
QSyntaxHighlighter(parent),
|
QSyntaxHighlighter(parent),
|
||||||
m_persistentStatesCounter(PersistentsStart),
|
m_regionDepth(0),
|
||||||
|
m_persistentObservableStatesCounter(PersistentsStart),
|
||||||
m_dynamicContextsCounter(0),
|
m_dynamicContextsCounter(0),
|
||||||
m_isBroken(false)
|
m_isBroken(false)
|
||||||
{}
|
{}
|
||||||
@@ -61,7 +62,7 @@ Highlighter::Highlighter(QTextDocument *parent) :
|
|||||||
Highlighter::~Highlighter()
|
Highlighter::~Highlighter()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
Highlighter::BlockData::BlockData()
|
Highlighter::BlockData::BlockData() : m_foldingIndentDelta(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
Highlighter::BlockData::~BlockData()
|
Highlighter::BlockData::~BlockData()
|
||||||
@@ -93,13 +94,15 @@ void Highlighter::configureFormat(TextFormatId id, const QTextCharFormat &format
|
|||||||
void Highlighter::setDefaultContext(const QSharedPointer<Context> &defaultContext)
|
void Highlighter::setDefaultContext(const QSharedPointer<Context> &defaultContext)
|
||||||
{
|
{
|
||||||
m_defaultContext = defaultContext;
|
m_defaultContext = defaultContext;
|
||||||
m_persistentStates.insert(m_defaultContext->name(), Default);
|
m_persistentObservableStates.insert(m_defaultContext->name(), Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Highlighter::highlightBlock(const QString &text)
|
void Highlighter::highlightBlock(const QString &text)
|
||||||
{
|
{
|
||||||
if (!m_defaultContext.isNull() && !m_isBroken) {
|
if (!m_defaultContext.isNull() && !m_isBroken) {
|
||||||
try {
|
try {
|
||||||
|
if (!currentBlockUserData())
|
||||||
|
initializeBlockData();
|
||||||
setupDataForBlock(text);
|
setupDataForBlock(text);
|
||||||
|
|
||||||
handleContextChange(m_currentContext->lineBeginContext(),
|
handleContextChange(m_currentContext->lineBeginContext(),
|
||||||
@@ -114,6 +117,12 @@ void Highlighter::highlightBlock(const QString &text)
|
|||||||
m_currentContext->definition(),
|
m_currentContext->definition(),
|
||||||
false);
|
false);
|
||||||
m_contexts.clear();
|
m_contexts.clear();
|
||||||
|
|
||||||
|
applyFolding();
|
||||||
|
|
||||||
|
// 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())));
|
||||||
} catch (const HighlighterException &) {
|
} catch (const HighlighterException &) {
|
||||||
m_isBroken = true;
|
m_isBroken = true;
|
||||||
}
|
}
|
||||||
@@ -124,60 +133,70 @@ void Highlighter::highlightBlock(const QString &text)
|
|||||||
|
|
||||||
void Highlighter::setupDataForBlock(const QString &text)
|
void Highlighter::setupDataForBlock(const QString &text)
|
||||||
{
|
{
|
||||||
if (currentBlockState() == WillContinue)
|
if (extractObservableState(currentBlockState()) == WillContinue)
|
||||||
analyseConsistencyOfWillContinueBlock(text);
|
analyseConsistencyOfWillContinueBlock(text);
|
||||||
|
|
||||||
if (previousBlockState() == Default || previousBlockState() == -1)
|
if (previousBlockState() == -1) {
|
||||||
|
m_regionDepth = 0;
|
||||||
setupDefault();
|
setupDefault();
|
||||||
else if (previousBlockState() == WillContinue)
|
} else {
|
||||||
setupFromWillContinue();
|
m_regionDepth = extractRegionDepth(previousBlockState());
|
||||||
else if (previousBlockState() == Continued)
|
const int observablePreviousState = extractObservableState(previousBlockState());
|
||||||
setupFromContinued();
|
if (observablePreviousState == Default)
|
||||||
else
|
setupDefault();
|
||||||
setupFromPersistent();
|
else if (observablePreviousState == WillContinue)
|
||||||
|
setupFromWillContinue();
|
||||||
|
else if (observablePreviousState == Continued)
|
||||||
|
setupFromContinued();
|
||||||
|
else
|
||||||
|
setupFromPersistent();
|
||||||
|
|
||||||
setCurrentContext();
|
blockData(currentBlockUserData())->m_foldingRegions =
|
||||||
|
blockData(currentBlock().previous().userData())->m_foldingRegions;
|
||||||
|
}
|
||||||
|
|
||||||
|
assignCurrentContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Highlighter::setupDefault()
|
void Highlighter::setupDefault()
|
||||||
{
|
{
|
||||||
m_contexts.push_back(m_defaultContext);
|
m_contexts.push_back(m_defaultContext);
|
||||||
|
|
||||||
setCurrentBlockState(Default);
|
setCurrentBlockState(computeState(Default));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Highlighter::setupFromWillContinue()
|
void Highlighter::setupFromWillContinue()
|
||||||
{
|
{
|
||||||
BlockData *previousData = static_cast<BlockData *>(currentBlock().previous().userData());
|
BlockData *previousData = blockData(currentBlock().previous().userData());
|
||||||
m_contexts.push_back(previousData->m_contextToContinue);
|
m_contexts.push_back(previousData->m_contextToContinue);
|
||||||
|
|
||||||
if (!currentBlockUserData()) {
|
BlockData *data = blockData(currentBlock().userData());
|
||||||
BlockData *data = initializeBlockData();
|
data->m_originalObservableState = previousData->m_originalObservableState;
|
||||||
data->m_originalState = previousData->m_originalState;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentBlockState() == Default || currentBlockState() == -1)
|
if (currentBlockState() == -1 || extractObservableState(currentBlockState()) == Default)
|
||||||
setCurrentBlockState(Continued);
|
setCurrentBlockState(computeState(Continued));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Highlighter::setupFromContinued()
|
void Highlighter::setupFromContinued()
|
||||||
{
|
{
|
||||||
BlockData *previousData = static_cast<BlockData *>(currentBlock().previous().userData());
|
BlockData *previousData = blockData(currentBlock().previous().userData());
|
||||||
|
|
||||||
Q_ASSERT(previousData->m_originalState != WillContinue &&
|
Q_ASSERT(previousData->m_originalObservableState != WillContinue &&
|
||||||
previousData->m_originalState != Continued);
|
previousData->m_originalObservableState != Continued);
|
||||||
|
|
||||||
if (previousData->m_originalState == Default || previousData->m_originalState == -1)
|
if (previousData->m_originalObservableState == Default ||
|
||||||
|
previousData->m_originalObservableState == -1) {
|
||||||
m_contexts.push_back(m_defaultContext);
|
m_contexts.push_back(m_defaultContext);
|
||||||
else
|
} else {
|
||||||
pushContextSequence(previousData->m_originalState);
|
pushContextSequence(previousData->m_originalObservableState);
|
||||||
|
}
|
||||||
|
|
||||||
setCurrentBlockState(previousData->m_originalState);
|
setCurrentBlockState(computeState(previousData->m_originalObservableState));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Highlighter::setupFromPersistent()
|
void Highlighter::setupFromPersistent()
|
||||||
{
|
{
|
||||||
pushContextSequence(previousBlockState());
|
pushContextSequence(extractObservableState(previousBlockState()));
|
||||||
|
|
||||||
setCurrentBlockState(previousBlockState());
|
setCurrentBlockState(previousBlockState());
|
||||||
}
|
}
|
||||||
@@ -197,11 +216,29 @@ void Highlighter::iterateThroughRules(const QString &text,
|
|||||||
RuleIterator endIt = rules.end();
|
RuleIterator endIt = rules.end();
|
||||||
while (it != endIt && progress->offset() < length) {
|
while (it != endIt && progress->offset() < length) {
|
||||||
int startOffset = progress->offset();
|
int startOffset = progress->offset();
|
||||||
|
|
||||||
const QSharedPointer<Rule> &rule = *it;
|
const QSharedPointer<Rule> &rule = *it;
|
||||||
if (rule->matchSucceed(text, length, progress)) {
|
if (rule->matchSucceed(text, length, progress)) {
|
||||||
atLeastOneMatch = true;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
progress->clearBracesMatches();
|
||||||
|
|
||||||
if (progress->isWillContinueLine()) {
|
if (progress->isWillContinueLine()) {
|
||||||
createWillContinueBlock();
|
createWillContinueBlock();
|
||||||
progress->setWillContinueLine(false);
|
progress->setWillContinueLine(false);
|
||||||
@@ -263,20 +300,22 @@ bool Highlighter::contextChangeRequired(const QString &contextName) const
|
|||||||
|
|
||||||
void Highlighter::changeContext(const QString &contextName,
|
void Highlighter::changeContext(const QString &contextName,
|
||||||
const QSharedPointer<HighlightDefinition> &definition,
|
const QSharedPointer<HighlightDefinition> &definition,
|
||||||
const bool setCurrent)
|
const bool assignCurrent)
|
||||||
{
|
{
|
||||||
if (contextName.startsWith(kPop)) {
|
if (contextName.startsWith(kPop)) {
|
||||||
QStringList list = contextName.split(kHash, QString::SkipEmptyParts);
|
QStringList list = contextName.split(kHash, QString::SkipEmptyParts);
|
||||||
for (int i = 0; i < list.size(); ++i)
|
for (int i = 0; i < list.size(); ++i)
|
||||||
m_contexts.pop_back();
|
m_contexts.pop_back();
|
||||||
|
|
||||||
if (currentBlockState() >= PersistentsStart) {
|
if (extractObservableState(currentBlockState()) >= PersistentsStart) {
|
||||||
// One or more contexts were popped during during a persistent state.
|
// One or more contexts were popped during during a persistent state.
|
||||||
const QString ¤tSequence = currentContextSequence();
|
const QString ¤tSequence = currentContextSequence();
|
||||||
if (m_persistentStates.contains(currentSequence))
|
if (m_persistentObservableStates.contains(currentSequence))
|
||||||
setCurrentBlockState(m_persistentStates.value(currentSequence));
|
setCurrentBlockState(
|
||||||
|
computeState(m_persistentObservableStates.value(currentSequence)));
|
||||||
else
|
else
|
||||||
setCurrentBlockState(m_leadingStates.value(currentSequence));
|
setCurrentBlockState(
|
||||||
|
computeState(m_leadingObservableStates.value(currentSequence)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const QSharedPointer<Context> &context = definition->context(contextName);
|
const QSharedPointer<Context> &context = definition->context(contextName);
|
||||||
@@ -287,19 +326,20 @@ void Highlighter::changeContext(const QString &contextName,
|
|||||||
m_contexts.push_back(context);
|
m_contexts.push_back(context);
|
||||||
|
|
||||||
if (m_contexts.back()->lineEndContext() == kStay ||
|
if (m_contexts.back()->lineEndContext() == kStay ||
|
||||||
currentBlockState() >= PersistentsStart) {
|
extractObservableState(currentBlockState()) >= PersistentsStart) {
|
||||||
const QString ¤tSequence = currentContextSequence();
|
const QString ¤tSequence = currentContextSequence();
|
||||||
mapLeadingSequence(currentSequence);
|
mapLeadingSequence(currentSequence);
|
||||||
if (m_contexts.back()->lineEndContext() == kStay) {
|
if (m_contexts.back()->lineEndContext() == kStay) {
|
||||||
// A persistent context was pushed.
|
// A persistent context was pushed.
|
||||||
mapPersistentSequence(currentSequence);
|
mapPersistentSequence(currentSequence);
|
||||||
setCurrentBlockState(m_persistentStates.value(currentSequence));
|
setCurrentBlockState(
|
||||||
|
computeState(m_persistentObservableStates.value(currentSequence)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setCurrent)
|
if (assignCurrent)
|
||||||
setCurrentContext();
|
assignCurrentContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Highlighter::handleContextChange(const QString &contextName,
|
void Highlighter::handleContextChange(const QString &contextName,
|
||||||
@@ -373,50 +413,49 @@ void Highlighter::applyVisualWhitespaceFormat(const QString &text)
|
|||||||
|
|
||||||
void Highlighter::createWillContinueBlock()
|
void Highlighter::createWillContinueBlock()
|
||||||
{
|
{
|
||||||
if (!currentBlockUserData())
|
BlockData *data = blockData(currentBlockUserData());
|
||||||
initializeBlockData();
|
const int currentObservableState = extractObservableState(currentBlockState());
|
||||||
|
if (currentObservableState == Continued) {
|
||||||
BlockData *data = static_cast<BlockData *>(currentBlockUserData());
|
BlockData *previousData = blockData(currentBlock().previous().userData());
|
||||||
if (currentBlockState() == Continued) {
|
data->m_originalObservableState = previousData->m_originalObservableState;
|
||||||
BlockData *previousData = static_cast<BlockData *>(currentBlock().previous().userData());
|
} else if (currentObservableState != WillContinue) {
|
||||||
data->m_originalState = previousData->m_originalState;
|
data->m_originalObservableState = currentObservableState;
|
||||||
} else if (currentBlockState() != WillContinue) {
|
|
||||||
data->m_originalState = currentBlockState();
|
|
||||||
}
|
}
|
||||||
data->m_contextToContinue = m_currentContext;
|
data->m_contextToContinue = m_currentContext;
|
||||||
|
|
||||||
setCurrentBlockState(WillContinue);
|
setCurrentBlockState(computeState(WillContinue));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Highlighter::analyseConsistencyOfWillContinueBlock(const QString &text)
|
void Highlighter::analyseConsistencyOfWillContinueBlock(const QString &text)
|
||||||
{
|
{
|
||||||
if (currentBlock().next().isValid() && (
|
if (currentBlock().next().isValid() && (
|
||||||
text.length() == 0 || text.at(text.length() - 1) != kBackSlash) &&
|
text.length() == 0 || text.at(text.length() - 1) != kBackSlash) &&
|
||||||
currentBlock().next().userState() != Continued) {
|
extractObservableState(currentBlock().next().userState()) != Continued) {
|
||||||
currentBlock().next().setUserState(Continued);
|
currentBlock().next().setUserState(computeState(Continued));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (text.length() == 0 || text.at(text.length() - 1) != kBackSlash) {
|
if (text.length() == 0 || text.at(text.length() - 1) != kBackSlash) {
|
||||||
BlockData *data = static_cast<BlockData *>(currentBlockUserData());
|
BlockData *data = blockData(currentBlockUserData());
|
||||||
data->m_contextToContinue.clear();
|
data->m_contextToContinue.clear();
|
||||||
setCurrentBlockState(data->m_originalState);
|
setCurrentBlockState(computeState(data->m_originalObservableState));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Highlighter::mapPersistentSequence(const QString &contextSequence)
|
void Highlighter::mapPersistentSequence(const QString &contextSequence)
|
||||||
{
|
{
|
||||||
if (!m_persistentStates.contains(contextSequence)) {
|
if (!m_persistentObservableStates.contains(contextSequence)) {
|
||||||
int newState = m_persistentStatesCounter;
|
int newState = m_persistentObservableStatesCounter;
|
||||||
m_persistentStates.insert(contextSequence, newState);
|
m_persistentObservableStates.insert(contextSequence, newState);
|
||||||
m_persistentContexts.insert(newState, m_contexts);
|
m_persistentContexts.insert(newState, m_contexts);
|
||||||
++m_persistentStatesCounter;
|
++m_persistentObservableStatesCounter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Highlighter::mapLeadingSequence(const QString &contextSequence)
|
void Highlighter::mapLeadingSequence(const QString &contextSequence)
|
||||||
{
|
{
|
||||||
if (!m_leadingStates.contains(contextSequence))
|
if (!m_leadingObservableStates.contains(contextSequence))
|
||||||
m_leadingStates.insert(contextSequence, currentBlockState());
|
m_leadingObservableStates.insert(contextSequence,
|
||||||
|
extractObservableState(currentBlockState()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Highlighter::pushContextSequence(int state)
|
void Highlighter::pushContextSequence(int state)
|
||||||
@@ -442,6 +481,11 @@ Highlighter::BlockData *Highlighter::initializeBlockData()
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Highlighter::BlockData *Highlighter::blockData(QTextBlockUserData *userData)
|
||||||
|
{
|
||||||
|
return static_cast<BlockData *>(userData);
|
||||||
|
}
|
||||||
|
|
||||||
void Highlighter::pushDynamicContext(const QSharedPointer<Context> &baseContext)
|
void Highlighter::pushDynamicContext(const QSharedPointer<Context> &baseContext)
|
||||||
{
|
{
|
||||||
// A dynamic context is created from another context which serves as its basis. Then,
|
// A dynamic context is created from another context which serves as its basis. Then,
|
||||||
@@ -454,7 +498,7 @@ void Highlighter::pushDynamicContext(const QSharedPointer<Context> &baseContext)
|
|||||||
++m_dynamicContextsCounter;
|
++m_dynamicContextsCounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Highlighter::setCurrentContext()
|
void Highlighter::assignCurrentContext()
|
||||||
{
|
{
|
||||||
if (m_contexts.isEmpty()) {
|
if (m_contexts.isEmpty()) {
|
||||||
// This is not supposed to happen. However, there are broken files (for example, php.xml)
|
// This is not supposed to happen. However, there are broken files (for example, php.xml)
|
||||||
@@ -464,3 +508,38 @@ void Highlighter::setCurrentContext()
|
|||||||
}
|
}
|
||||||
m_currentContext = m_contexts.back();
|
m_currentContext = m_contexts.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Highlighter::extractRegionDepth(const int state)
|
||||||
|
{
|
||||||
|
return state >> 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Highlighter::extractObservableState(const int state)
|
||||||
|
{
|
||||||
|
return state & 0xFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Highlighter::computeState(const int observableState) const
|
||||||
|
{
|
||||||
|
return m_regionDepth << 12 | observableState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Highlighter::applyFolding() const
|
||||||
|
{
|
||||||
|
int folding = 0;
|
||||||
|
BlockData *data = blockData(currentBlockUserData());
|
||||||
|
BlockData *previousData = blockData(currentBlock().previous().userData());
|
||||||
|
if (previousData) {
|
||||||
|
folding = extractRegionDepth(previousBlockState());
|
||||||
|
if (data->m_foldingIndentDelta != 0) {
|
||||||
|
folding += data->m_foldingIndentDelta;
|
||||||
|
if (data->m_foldingIndentDelta > 0)
|
||||||
|
data->setFoldingStartIncluded(true);
|
||||||
|
else
|
||||||
|
previousData->setFoldingEndIncluded(false);
|
||||||
|
data->m_foldingIndentDelta = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data->setFoldingEndIncluded(true);
|
||||||
|
data->setFoldingIndent(folding);
|
||||||
|
}
|
||||||
|
@@ -34,6 +34,7 @@
|
|||||||
|
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
#include <QtCore/QVector>
|
#include <QtCore/QVector>
|
||||||
|
#include <QtCore/QStack>
|
||||||
#include <QtCore/QSharedPointer>
|
#include <QtCore/QSharedPointer>
|
||||||
#include <QtCore/QStringList>
|
#include <QtCore/QStringList>
|
||||||
|
|
||||||
@@ -92,14 +93,14 @@ private:
|
|||||||
const bool childRule,
|
const bool childRule,
|
||||||
const QList<QSharedPointer<Rule> > &rules);
|
const QList<QSharedPointer<Rule> > &rules);
|
||||||
|
|
||||||
void setCurrentContext();
|
void assignCurrentContext();
|
||||||
bool contextChangeRequired(const QString &contextName) const;
|
bool contextChangeRequired(const QString &contextName) const;
|
||||||
void handleContextChange(const QString &contextName,
|
void handleContextChange(const QString &contextName,
|
||||||
const QSharedPointer<HighlightDefinition> &definition,
|
const QSharedPointer<HighlightDefinition> &definition,
|
||||||
const bool setCurrent = true);
|
const bool setCurrent = true);
|
||||||
void changeContext(const QString &contextName,
|
void changeContext(const QString &contextName,
|
||||||
const QSharedPointer<HighlightDefinition> &definition,
|
const QSharedPointer<HighlightDefinition> &definition,
|
||||||
const bool setCurrent = true);
|
const bool assignCurrent = true);
|
||||||
|
|
||||||
QString currentContextSequence() const;
|
QString currentContextSequence() const;
|
||||||
void mapPersistentSequence(const QString &contextSequence);
|
void mapPersistentSequence(const QString &contextSequence);
|
||||||
@@ -117,6 +118,8 @@ private:
|
|||||||
const QSharedPointer<HighlightDefinition> &definition);
|
const QSharedPointer<HighlightDefinition> &definition);
|
||||||
void applyVisualWhitespaceFormat(const QString &text);
|
void applyVisualWhitespaceFormat(const QString &text);
|
||||||
|
|
||||||
|
void applyFolding() const;
|
||||||
|
|
||||||
// Mapping from Kate format strings to format ids.
|
// Mapping from Kate format strings to format ids.
|
||||||
struct KateFormatMap
|
struct KateFormatMap
|
||||||
{
|
{
|
||||||
@@ -131,27 +134,40 @@ private:
|
|||||||
BlockData();
|
BlockData();
|
||||||
virtual ~BlockData();
|
virtual ~BlockData();
|
||||||
|
|
||||||
int m_originalState;
|
int m_foldingIndentDelta;
|
||||||
|
int m_originalObservableState;
|
||||||
|
QStack<QString> m_foldingRegions;
|
||||||
QSharedPointer<Context> m_contextToContinue;
|
QSharedPointer<Context> m_contextToContinue;
|
||||||
};
|
};
|
||||||
BlockData *initializeBlockData();
|
BlockData *initializeBlockData();
|
||||||
|
static BlockData *blockData(QTextBlockUserData *userData);
|
||||||
|
|
||||||
// Block states
|
// Block states are composed by the region depth (used for code folding) and what I call
|
||||||
|
// observable states. Observable states occupy the 12 least significant bits. They might have
|
||||||
|
// the following values:
|
||||||
// - Default [0]: Nothing special.
|
// - Default [0]: Nothing special.
|
||||||
// - WillContinue [1]: When there is match of the LineContinue rule (backslash as the last
|
// - WillContinue [1]: When there is match of the LineContinue rule (backslash as the last
|
||||||
// character).
|
// character).
|
||||||
// - Continued [2]: Blocks that happen after a WillContinue block and continued from their
|
// - Continued [2]: Blocks that happen after a WillContinue block and continue from their
|
||||||
// context until the next line end.
|
// context until the next line end.
|
||||||
// - Persistent(s) [Anything >= 3]: Correspond to persistent contexts which last until a pop
|
// - Persistent(s) [Anything >= 3]: Correspond to persistent contexts which last until a pop
|
||||||
// occurs due to a matching rule. Every sequence of persistent contexts seen so far is
|
// occurs due to a matching rule. Every sequence of persistent contexts seen so far is
|
||||||
// associated with a number (incremented by a unit each time).
|
// associated with a number (incremented by a unit each time).
|
||||||
enum BlockState {
|
// Region depths occupy the remaining bits.
|
||||||
|
enum ObservableBlockState {
|
||||||
Default = 0,
|
Default = 0,
|
||||||
WillContinue,
|
WillContinue,
|
||||||
Continued,
|
Continued,
|
||||||
PersistentsStart
|
PersistentsStart
|
||||||
};
|
};
|
||||||
int m_persistentStatesCounter;
|
int computeState(const int observableState) const;
|
||||||
|
|
||||||
|
static int extractRegionDepth(const int state);
|
||||||
|
static int extractObservableState(const int state);
|
||||||
|
|
||||||
|
int m_regionDepth;
|
||||||
|
|
||||||
|
int m_persistentObservableStatesCounter;
|
||||||
int m_dynamicContextsCounter;
|
int m_dynamicContextsCounter;
|
||||||
|
|
||||||
bool m_isBroken;
|
bool m_isBroken;
|
||||||
@@ -160,11 +176,11 @@ private:
|
|||||||
QSharedPointer<Context> m_currentContext;
|
QSharedPointer<Context> m_currentContext;
|
||||||
QVector<QSharedPointer<Context> > m_contexts;
|
QVector<QSharedPointer<Context> > m_contexts;
|
||||||
|
|
||||||
// Mapping from context sequences to the persistent state they represent.
|
// Mapping from context sequences to the observable persistent state they represent.
|
||||||
QHash<QString, int> m_persistentStates;
|
QHash<QString, int> m_persistentObservableStates;
|
||||||
// Mapping from context sequences to the non-persistent state that led to them.
|
// Mapping from context sequences to the non-persistent observable state that led to them.
|
||||||
QHash<QString, int> m_leadingStates;
|
QHash<QString, int> m_leadingObservableStates;
|
||||||
// Mapping from persistent states to context sequences (the actual "stack").
|
// Mapping from observable persistent states to context sequences (the actual "stack").
|
||||||
QHash<int, QVector<QSharedPointer<Context> > > m_persistentContexts;
|
QHash<int, QVector<QSharedPointer<Context> > > m_persistentContexts;
|
||||||
|
|
||||||
// Captures used in dynamic rules.
|
// Captures used in dynamic rules.
|
||||||
|
@@ -38,7 +38,9 @@ ProgressData::ProgressData() :
|
|||||||
m_offset(0),
|
m_offset(0),
|
||||||
m_savedOffset(-1),
|
m_savedOffset(-1),
|
||||||
m_onlySpacesSoFar(true),
|
m_onlySpacesSoFar(true),
|
||||||
m_willContinueLine(false)
|
m_willContinueLine(false),
|
||||||
|
m_openingBraceMatchAtFirstNonSpace(false),
|
||||||
|
m_closingBraceMatchAtNonEnd(false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void ProgressData::setOffset(const int offset)
|
void ProgressData::setOffset(const int offset)
|
||||||
@@ -69,6 +71,26 @@ void ProgressData::setOnlySpacesSoFar(const bool onlySpaces)
|
|||||||
bool ProgressData::isOnlySpacesSoFar() const
|
bool ProgressData::isOnlySpacesSoFar() const
|
||||||
{ return m_onlySpacesSoFar; }
|
{ return m_onlySpacesSoFar; }
|
||||||
|
|
||||||
|
void ProgressData::setOpeningBraceMatchAtFirstNonSpace(const bool match)
|
||||||
|
{ m_openingBraceMatchAtFirstNonSpace = match; }
|
||||||
|
|
||||||
|
bool ProgressData::isOpeningBraceMatchAtFirstNonSpace() const
|
||||||
|
{ return m_openingBraceMatchAtFirstNonSpace; }
|
||||||
|
|
||||||
|
void ProgressData::setClosingBraceMatchAtNonEnd(const bool match)
|
||||||
|
{ m_closingBraceMatchAtNonEnd = match; }
|
||||||
|
|
||||||
|
bool ProgressData::isClosingBraceMatchAtNonEnd() const
|
||||||
|
{ return m_closingBraceMatchAtNonEnd; }
|
||||||
|
|
||||||
|
void ProgressData::clearBracesMatches()
|
||||||
|
{
|
||||||
|
if (m_openingBraceMatchAtFirstNonSpace)
|
||||||
|
m_openingBraceMatchAtFirstNonSpace = false;
|
||||||
|
if (m_closingBraceMatchAtNonEnd)
|
||||||
|
m_closingBraceMatchAtNonEnd = false;
|
||||||
|
}
|
||||||
|
|
||||||
void ProgressData::setWillContinueLine(const bool willContinue)
|
void ProgressData::setWillContinueLine(const bool willContinue)
|
||||||
{ m_willContinueLine = willContinue; }
|
{ m_willContinueLine = willContinue; }
|
||||||
|
|
||||||
|
@@ -52,6 +52,14 @@ public:
|
|||||||
void setOnlySpacesSoFar(const bool onlySpaces);
|
void setOnlySpacesSoFar(const bool onlySpaces);
|
||||||
bool isOnlySpacesSoFar() const;
|
bool isOnlySpacesSoFar() const;
|
||||||
|
|
||||||
|
void setOpeningBraceMatchAtFirstNonSpace(const bool match);
|
||||||
|
bool isOpeningBraceMatchAtFirstNonSpace() const;
|
||||||
|
|
||||||
|
void setClosingBraceMatchAtNonEnd(const bool match);
|
||||||
|
bool isClosingBraceMatchAtNonEnd() const;
|
||||||
|
|
||||||
|
void clearBracesMatches();
|
||||||
|
|
||||||
void setWillContinueLine(const bool willContinue);
|
void setWillContinueLine(const bool willContinue);
|
||||||
bool isWillContinueLine() const;
|
bool isWillContinueLine() const;
|
||||||
|
|
||||||
@@ -62,6 +70,8 @@ private:
|
|||||||
int m_offset;
|
int m_offset;
|
||||||
int m_savedOffset;
|
int m_savedOffset;
|
||||||
bool m_onlySpacesSoFar;
|
bool m_onlySpacesSoFar;
|
||||||
|
bool m_openingBraceMatchAtFirstNonSpace;
|
||||||
|
bool m_closingBraceMatchAtNonEnd;
|
||||||
bool m_willContinueLine;
|
bool m_willContinueLine;
|
||||||
QStringList m_captures;
|
QStringList m_captures;
|
||||||
};
|
};
|
||||||
|
@@ -58,6 +58,8 @@ const QLatin1Char Rule::kN('n');
|
|||||||
const QLatin1Char Rule::kR('r');
|
const QLatin1Char Rule::kR('r');
|
||||||
const QLatin1Char Rule::kT('t');
|
const QLatin1Char Rule::kT('t');
|
||||||
const QLatin1Char Rule::kV('v');
|
const QLatin1Char Rule::kV('v');
|
||||||
|
const QLatin1Char Rule::kOpeningBrace('{');
|
||||||
|
const QLatin1Char Rule::kClosingBrace('}');
|
||||||
|
|
||||||
Rule::Rule(bool consumesNonSpace) :
|
Rule::Rule(bool consumesNonSpace) :
|
||||||
m_lookAhead(false), m_firstNonSpace(false), m_column(-1), m_consumesNonSpace(consumesNonSpace)
|
m_lookAhead(false), m_firstNonSpace(false), m_column(-1), m_consumesNonSpace(consumesNonSpace)
|
||||||
|
@@ -124,6 +124,8 @@ protected:
|
|||||||
static const QLatin1Char kR;
|
static const QLatin1Char kR;
|
||||||
static const QLatin1Char kT;
|
static const QLatin1Char kT;
|
||||||
static const QLatin1Char kV;
|
static const QLatin1Char kV;
|
||||||
|
static const QLatin1Char kOpeningBrace;
|
||||||
|
static const QLatin1Char kClosingBrace;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual bool doMatchSucceed(const QString &text,
|
virtual bool doMatchSucceed(const QString &text,
|
||||||
|
@@ -86,7 +86,18 @@ bool DetectCharRule::doMatchSucceed(const QString &text,
|
|||||||
const int length,
|
const int length,
|
||||||
ProgressData *progress) const
|
ProgressData *progress) const
|
||||||
{
|
{
|
||||||
return matchCharacter(text, length, progress, m_char);
|
if (matchCharacter(text, length, progress, m_char)) {
|
||||||
|
// This is to make code folding have a control flow style look in the case of braces.
|
||||||
|
// Naturally, this assumes that language definitions use braces with this meaning.
|
||||||
|
if (m_char == kOpeningBrace && progress->isOnlySpacesSoFar() && !isLookAhead()) {
|
||||||
|
progress->setOpeningBraceMatchAtFirstNonSpace(true);
|
||||||
|
} else if (m_char == kClosingBrace &&
|
||||||
|
!text.right(length - progress->offset()).trimmed().isEmpty()) {
|
||||||
|
progress->setClosingBraceMatchAtNonEnd(true);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect2Chars
|
// Detect2Chars
|
||||||
|
@@ -162,7 +162,10 @@ void PlainTextEditor::configure(const Core::MimeType &mimeType)
|
|||||||
{
|
{
|
||||||
Highlighter *highlighter = new Highlighter();
|
Highlighter *highlighter = new Highlighter();
|
||||||
baseTextDocument()->setSyntaxHighlighter(highlighter);
|
baseTextDocument()->setSyntaxHighlighter(highlighter);
|
||||||
|
|
||||||
m_isMissingSyntaxDefinition = true;
|
m_isMissingSyntaxDefinition = true;
|
||||||
|
setCodeFoldingSupported(false);
|
||||||
|
setCodeFoldingVisible(false);
|
||||||
|
|
||||||
QString definitionId;
|
QString definitionId;
|
||||||
if (!mimeType.isNull()) {
|
if (!mimeType.isNull()) {
|
||||||
@@ -185,6 +188,9 @@ void PlainTextEditor::configure(const Core::MimeType &mimeType)
|
|||||||
m_commentDefinition.setMultiLineStart(definition->multiLineCommentStart());
|
m_commentDefinition.setMultiLineStart(definition->multiLineCommentStart());
|
||||||
m_commentDefinition.setMultiLineEnd(definition->multiLineCommentEnd());
|
m_commentDefinition.setMultiLineEnd(definition->multiLineCommentEnd());
|
||||||
|
|
||||||
|
setCodeFoldingSupported(true);
|
||||||
|
setCodeFoldingVisible(true);
|
||||||
|
|
||||||
m_isMissingSyntaxDefinition = false;
|
m_isMissingSyntaxDefinition = false;
|
||||||
}
|
}
|
||||||
} else if (file()) {
|
} else if (file()) {
|
||||||
|
@@ -50,7 +50,8 @@ PlainTextEditorFactory::PlainTextEditorFactory(QObject *parent)
|
|||||||
m_actionHandler = new TextEditorActionHandler(
|
m_actionHandler = new TextEditorActionHandler(
|
||||||
QLatin1String(TextEditor::Constants::C_TEXTEDITOR),
|
QLatin1String(TextEditor::Constants::C_TEXTEDITOR),
|
||||||
TextEditorActionHandler::Format |
|
TextEditorActionHandler::Format |
|
||||||
TextEditorActionHandler::UnCommentSelection);
|
TextEditorActionHandler::UnCommentSelection |
|
||||||
|
TextEditorActionHandler::UnCollapseAll);
|
||||||
m_mimeTypes << QLatin1String(TextEditor::Constants::C_TEXTEDITOR_MIMETYPE_TEXT);
|
m_mimeTypes << QLatin1String(TextEditor::Constants::C_TEXTEDITOR_MIMETYPE_TEXT);
|
||||||
|
|
||||||
connect(Core::EditorManager::instance(), SIGNAL(currentEditorChanged(Core::IEditor*)),
|
connect(Core::EditorManager::instance(), SIGNAL(currentEditorChanged(Core::IEditor*)),
|
||||||
|
@@ -32,13 +32,19 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
Since the text editor plugin directory is not included in the search list of the pro file, this
|
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 simply use
|
file replaces the "real" basetextdocumentlayout.h file. The objective is to provide a simple
|
||||||
QTextBlockUserData instead of TextEditor::TextBlockUserData to avoid "external"
|
TextBlockUserData and avoid "external" dependencies or intrusive defines.
|
||||||
dependencies or intrusive defines.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QtGui/QTextBlockUserData>
|
#include <QtGui/QTextBlockUserData>
|
||||||
|
|
||||||
typedef QTextBlockUserData TextBlockUserData;
|
struct TextBlockUserData : QTextBlockUserData
|
||||||
|
{
|
||||||
|
virtual ~TextBlockUserData(){}
|
||||||
|
|
||||||
|
void setFoldingStartIncluded(const bool) {}
|
||||||
|
void setFoldingEndIncluded(const bool) {}
|
||||||
|
void setFoldingIndent(const int) {}
|
||||||
|
};
|
||||||
|
|
||||||
#endif // BASETEXTDOCUMENTLAYOUT_H
|
#endif // BASETEXTDOCUMENTLAYOUT_H
|
||||||
|
@@ -109,7 +109,8 @@ void HighlighterMock::highlightBlock(const QString &text)
|
|||||||
|
|
||||||
if (m_states.size() <= m_statesCounter)
|
if (m_states.size() <= m_statesCounter)
|
||||||
QFAIL("Expected state for current block not set.");
|
QFAIL("Expected state for current block not set.");
|
||||||
QCOMPARE(currentBlockState(), m_states.at(m_statesCounter++));
|
const int observableState = currentBlockState() & 0xFFF;
|
||||||
|
QCOMPARE(observableState, m_states.at(m_statesCounter++));
|
||||||
|
|
||||||
if (m_formatSequence.size() <= m_formatsCounter)
|
if (m_formatSequence.size() <= m_formatsCounter)
|
||||||
QFAIL("Expected highlight sequence for current block not set.");
|
QFAIL("Expected highlight sequence for current block not set.");
|
||||||
|
Reference in New Issue
Block a user