2010-04-27 16:33:40 +02:00
|
|
|
/**************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** This file is part of Qt Creator
|
|
|
|
|
**
|
2012-01-26 18:33:46 +01:00
|
|
|
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
2010-04-27 16:33:40 +02:00
|
|
|
**
|
2012-07-19 12:26:56 +02:00
|
|
|
** Contact: http://www.qt-project.org/
|
2010-04-27 16:33:40 +02:00
|
|
|
**
|
|
|
|
|
**
|
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
|
**
|
2011-04-13 08:42:33 +02:00
|
|
|
** 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.
|
2010-04-27 16:33:40 +02:00
|
|
|
**
|
2010-12-17 16:01:08 +01:00
|
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
2011-04-13 08:42:33 +02:00
|
|
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
2010-12-17 16:01:08 +01:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
|
**
|
2011-04-13 08:42:33 +02:00
|
|
|
** Other Usage
|
|
|
|
|
**
|
|
|
|
|
** Alternatively, this file may be used in accordance with the terms and
|
|
|
|
|
** conditions contained in a signed written agreement between you and Nokia.
|
|
|
|
|
**
|
2010-04-27 16:33:40 +02:00
|
|
|
**
|
|
|
|
|
**************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "highlighter.h"
|
|
|
|
|
#include "highlightdefinition.h"
|
|
|
|
|
#include "context.h"
|
|
|
|
|
#include "rule.h"
|
|
|
|
|
#include "itemdata.h"
|
|
|
|
|
#include "highlighterexception.h"
|
|
|
|
|
#include "progressdata.h"
|
2010-05-03 16:39:47 +02:00
|
|
|
#include "reuse.h"
|
2010-06-21 13:37:53 +02:00
|
|
|
#include "tabsettings.h"
|
2010-05-03 16:39:47 +02:00
|
|
|
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QLatin1String>
|
|
|
|
|
#include <QLatin1Char>
|
2010-04-27 16:33:40 +02:00
|
|
|
|
2010-05-20 13:56:11 +02:00
|
|
|
using namespace TextEditor;
|
2010-04-27 16:33:40 +02:00
|
|
|
using namespace Internal;
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
static const QLatin1String kStay("#stay");
|
|
|
|
|
static const QLatin1String kPop("#pop");
|
|
|
|
|
static const QLatin1Char kBackSlash('\\');
|
|
|
|
|
static const QLatin1Char kHash('#');
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-31 13:56:46 +02:00
|
|
|
const Highlighter::KateFormatMap Highlighter::m_kateFormats;
|
|
|
|
|
|
2010-06-15 10:35:39 +02:00
|
|
|
Highlighter::Highlighter(QTextDocument *parent) :
|
2010-07-09 14:47:18 +02:00
|
|
|
TextEditor::SyntaxHighlighter(parent),
|
2010-06-18 16:30:56 +02:00
|
|
|
m_regionDepth(0),
|
2010-06-21 13:37:53 +02:00
|
|
|
m_indentationBasedFolding(false),
|
|
|
|
|
m_tabSettings(0),
|
2010-06-18 16:30:56 +02:00
|
|
|
m_persistentObservableStatesCounter(PersistentsStart),
|
2010-04-27 16:33:40 +02:00
|
|
|
m_dynamicContextsCounter(0),
|
2010-06-15 10:35:39 +02:00
|
|
|
m_isBroken(false)
|
|
|
|
|
{}
|
2010-04-27 16:33:40 +02:00
|
|
|
|
|
|
|
|
Highlighter::~Highlighter()
|
|
|
|
|
{}
|
|
|
|
|
|
2010-06-21 16:22:53 +02:00
|
|
|
Highlighter::BlockData::BlockData() : m_foldingIndentDelta(0), m_originalObservableState(-1)
|
2010-04-27 16:33:40 +02:00
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
Highlighter::BlockData::~BlockData()
|
|
|
|
|
{}
|
|
|
|
|
|
2010-05-31 13:56:46 +02:00
|
|
|
Highlighter::KateFormatMap::KateFormatMap()
|
|
|
|
|
{
|
|
|
|
|
m_ids.insert(QLatin1String("dsNormal"), Highlighter::Normal);
|
|
|
|
|
m_ids.insert(QLatin1String("dsKeyword"), Highlighter::Keyword);
|
|
|
|
|
m_ids.insert(QLatin1String("dsDataType"), Highlighter::DataType);
|
|
|
|
|
m_ids.insert(QLatin1String("dsDecVal"), Highlighter::Decimal);
|
|
|
|
|
m_ids.insert(QLatin1String("dsBaseN"), Highlighter::BaseN);
|
|
|
|
|
m_ids.insert(QLatin1String("dsFloat"), Highlighter::Float);
|
|
|
|
|
m_ids.insert(QLatin1String("dsChar"), Highlighter::Char);
|
|
|
|
|
m_ids.insert(QLatin1String("dsString"), Highlighter::String);
|
|
|
|
|
m_ids.insert(QLatin1String("dsComment"), Highlighter::Comment);
|
|
|
|
|
m_ids.insert(QLatin1String("dsOthers"), Highlighter::Others);
|
|
|
|
|
m_ids.insert(QLatin1String("dsAlert"), Highlighter::Alert);
|
|
|
|
|
m_ids.insert(QLatin1String("dsFunction"), Highlighter::Function);
|
|
|
|
|
m_ids.insert(QLatin1String("dsRegionMarker"), Highlighter::RegionMarker);
|
|
|
|
|
m_ids.insert(QLatin1String("dsError"), Highlighter::Error);
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-15 10:35:39 +02:00
|
|
|
void Highlighter::configureFormat(TextFormatId id, const QTextCharFormat &format)
|
2010-04-27 16:33:40 +02:00
|
|
|
{
|
2010-06-15 10:35:39 +02:00
|
|
|
m_creatorFormats[id] = format;
|
|
|
|
|
}
|
2010-04-27 16:33:40 +02:00
|
|
|
|
2010-06-15 10:35:39 +02:00
|
|
|
void Highlighter::setDefaultContext(const QSharedPointer<Context> &defaultContext)
|
|
|
|
|
{
|
|
|
|
|
m_defaultContext = defaultContext;
|
2010-06-18 16:30:56 +02:00
|
|
|
m_persistentObservableStates.insert(m_defaultContext->name(), Default);
|
2010-06-21 13:37:53 +02:00
|
|
|
m_indentationBasedFolding = defaultContext->definition()->isIndentationBasedFolding();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Highlighter::setTabSettings(const TabSettings &ts)
|
|
|
|
|
{
|
|
|
|
|
m_tabSettings = &ts;
|
2010-06-15 10:35:39 +02:00
|
|
|
}
|
2010-04-27 16:33:40 +02:00
|
|
|
|
2010-06-15 10:35:39 +02:00
|
|
|
void Highlighter::highlightBlock(const QString &text)
|
|
|
|
|
{
|
|
|
|
|
if (!m_defaultContext.isNull() && !m_isBroken) {
|
|
|
|
|
try {
|
2010-06-18 16:30:56 +02:00
|
|
|
if (!currentBlockUserData())
|
|
|
|
|
initializeBlockData();
|
2010-06-15 10:35:39 +02:00
|
|
|
setupDataForBlock(text);
|
2010-04-27 16:33:40 +02:00
|
|
|
|
2010-06-15 10:35:39 +02:00
|
|
|
handleContextChange(m_currentContext->lineBeginContext(),
|
|
|
|
|
m_currentContext->definition());
|
2010-04-27 16:33:40 +02:00
|
|
|
|
2010-06-15 10:35:39 +02:00
|
|
|
ProgressData progress;
|
|
|
|
|
const int length = text.length();
|
2010-06-17 15:14:00 +02:00
|
|
|
while (progress.offset() < length)
|
2010-06-15 10:35:39 +02:00
|
|
|
iterateThroughRules(text, length, &progress, false, m_currentContext->rules());
|
2010-04-27 16:33:40 +02:00
|
|
|
|
2010-06-15 10:35:39 +02:00
|
|
|
handleContextChange(m_currentContext->lineEndContext(),
|
|
|
|
|
m_currentContext->definition(),
|
|
|
|
|
false);
|
|
|
|
|
m_contexts.clear();
|
2010-06-18 16:30:56 +02:00
|
|
|
|
2010-06-21 13:37:53 +02:00
|
|
|
if (m_indentationBasedFolding) {
|
|
|
|
|
applyIndentationBasedFolding(text);
|
|
|
|
|
} else {
|
|
|
|
|
applyRegionBasedFolding();
|
2010-06-18 16:30:56 +02:00
|
|
|
|
2010-06-21 13:37:53 +02:00
|
|
|
// In the case region depth has changed since the last time the state was set.
|
|
|
|
|
setCurrentBlockState(computeState(extractObservableState(currentBlockState())));
|
|
|
|
|
}
|
2010-06-15 10:35:39 +02:00
|
|
|
} catch (const HighlighterException &) {
|
|
|
|
|
m_isBroken = true;
|
2010-04-27 16:33:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
2010-05-03 16:39:47 +02:00
|
|
|
|
2010-09-30 09:12:59 +02:00
|
|
|
applyFormatToSpaces(text, m_creatorFormats.value(VisualWhitespace));
|
2010-04-27 16:33:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Highlighter::setupDataForBlock(const QString &text)
|
|
|
|
|
{
|
2010-06-18 16:30:56 +02:00
|
|
|
if (extractObservableState(currentBlockState()) == WillContinue)
|
2010-04-27 16:33:40 +02:00
|
|
|
analyseConsistencyOfWillContinueBlock(text);
|
|
|
|
|
|
2010-06-18 16:30:56 +02:00
|
|
|
if (previousBlockState() == -1) {
|
|
|
|
|
m_regionDepth = 0;
|
2010-04-27 16:33:40 +02:00
|
|
|
setupDefault();
|
2010-06-18 16:30:56 +02:00
|
|
|
} else {
|
|
|
|
|
m_regionDepth = extractRegionDepth(previousBlockState());
|
|
|
|
|
const int observablePreviousState = extractObservableState(previousBlockState());
|
|
|
|
|
if (observablePreviousState == Default)
|
|
|
|
|
setupDefault();
|
|
|
|
|
else if (observablePreviousState == WillContinue)
|
|
|
|
|
setupFromWillContinue();
|
|
|
|
|
else if (observablePreviousState == Continued)
|
|
|
|
|
setupFromContinued();
|
|
|
|
|
else
|
|
|
|
|
setupFromPersistent();
|
|
|
|
|
|
|
|
|
|
blockData(currentBlockUserData())->m_foldingRegions =
|
|
|
|
|
blockData(currentBlock().previous().userData())->m_foldingRegions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assignCurrentContext();
|
2010-04-27 16:33:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Highlighter::setupDefault()
|
|
|
|
|
{
|
|
|
|
|
m_contexts.push_back(m_defaultContext);
|
|
|
|
|
|
2010-06-18 16:30:56 +02:00
|
|
|
setCurrentBlockState(computeState(Default));
|
2010-04-27 16:33:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Highlighter::setupFromWillContinue()
|
|
|
|
|
{
|
2010-06-18 16:30:56 +02:00
|
|
|
BlockData *previousData = blockData(currentBlock().previous().userData());
|
2012-08-07 14:23:49 +02:00
|
|
|
if (previousData->m_originalObservableState == Default ||
|
|
|
|
|
previousData->m_originalObservableState == -1) {
|
|
|
|
|
m_contexts.push_back(previousData->m_contextToContinue);
|
|
|
|
|
} else {
|
|
|
|
|
pushContextSequence(previousData->m_originalObservableState);
|
|
|
|
|
}
|
2010-04-27 16:33:40 +02:00
|
|
|
|
2010-06-18 16:30:56 +02:00
|
|
|
BlockData *data = blockData(currentBlock().userData());
|
|
|
|
|
data->m_originalObservableState = previousData->m_originalObservableState;
|
2010-04-27 16:33:40 +02:00
|
|
|
|
2010-06-18 16:30:56 +02:00
|
|
|
if (currentBlockState() == -1 || extractObservableState(currentBlockState()) == Default)
|
|
|
|
|
setCurrentBlockState(computeState(Continued));
|
2010-04-27 16:33:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Highlighter::setupFromContinued()
|
|
|
|
|
{
|
2010-06-18 16:30:56 +02:00
|
|
|
BlockData *previousData = blockData(currentBlock().previous().userData());
|
2010-04-27 16:33:40 +02:00
|
|
|
|
2010-06-18 16:30:56 +02:00
|
|
|
Q_ASSERT(previousData->m_originalObservableState != WillContinue &&
|
|
|
|
|
previousData->m_originalObservableState != Continued);
|
2010-04-27 16:33:40 +02:00
|
|
|
|
2010-06-18 16:30:56 +02:00
|
|
|
if (previousData->m_originalObservableState == Default ||
|
|
|
|
|
previousData->m_originalObservableState == -1) {
|
2010-04-27 16:33:40 +02:00
|
|
|
m_contexts.push_back(m_defaultContext);
|
2010-06-18 16:30:56 +02:00
|
|
|
} else {
|
|
|
|
|
pushContextSequence(previousData->m_originalObservableState);
|
|
|
|
|
}
|
2010-04-27 16:33:40 +02:00
|
|
|
|
2010-06-18 16:30:56 +02:00
|
|
|
setCurrentBlockState(computeState(previousData->m_originalObservableState));
|
2010-04-27 16:33:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Highlighter::setupFromPersistent()
|
|
|
|
|
{
|
2010-06-18 16:30:56 +02:00
|
|
|
pushContextSequence(extractObservableState(previousBlockState()));
|
2010-04-27 16:33:40 +02:00
|
|
|
|
|
|
|
|
setCurrentBlockState(previousBlockState());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Highlighter::iterateThroughRules(const QString &text,
|
|
|
|
|
const int length,
|
|
|
|
|
ProgressData *progress,
|
|
|
|
|
const bool childRule,
|
|
|
|
|
const QList<QSharedPointer<Rule> > &rules)
|
|
|
|
|
{
|
|
|
|
|
typedef QList<QSharedPointer<Rule> >::const_iterator RuleIterator;
|
|
|
|
|
|
|
|
|
|
bool contextChanged = false;
|
|
|
|
|
bool atLeastOneMatch = false;
|
|
|
|
|
|
|
|
|
|
RuleIterator it = rules.begin();
|
|
|
|
|
RuleIterator endIt = rules.end();
|
|
|
|
|
while (it != endIt && progress->offset() < length) {
|
|
|
|
|
int startOffset = progress->offset();
|
|
|
|
|
const QSharedPointer<Rule> &rule = *it;
|
|
|
|
|
if (rule->matchSucceed(text, length, progress)) {
|
|
|
|
|
atLeastOneMatch = true;
|
|
|
|
|
|
2010-06-21 13:37:53 +02:00
|
|
|
if (!m_indentationBasedFolding) {
|
|
|
|
|
if (!rule->beginRegion().isEmpty()) {
|
|
|
|
|
blockData(currentBlockUserData())->m_foldingRegions.push(rule->beginRegion());
|
|
|
|
|
++m_regionDepth;
|
|
|
|
|
if (progress->isOpeningBraceMatchAtFirstNonSpace())
|
|
|
|
|
++blockData(currentBlockUserData())->m_foldingIndentDelta;
|
2010-06-18 16:30:56 +02:00
|
|
|
}
|
2010-06-21 13:37:53 +02:00
|
|
|
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();
|
2010-06-18 16:30:56 +02:00
|
|
|
}
|
|
|
|
|
|
2010-06-17 15:27:28 +02:00
|
|
|
if (progress->isWillContinueLine()) {
|
2010-04-27 16:33:40 +02:00
|
|
|
createWillContinueBlock();
|
|
|
|
|
progress->setWillContinueLine(false);
|
|
|
|
|
} else {
|
2011-03-01 11:28:37 +01:00
|
|
|
if (rule->hasChildren())
|
|
|
|
|
iterateThroughRules(text, length, progress, true, rule->children());
|
2010-04-27 16:33:40 +02:00
|
|
|
|
|
|
|
|
if (!rule->context().isEmpty() && contextChangeRequired(rule->context())) {
|
|
|
|
|
m_currentCaptures = progress->captures();
|
|
|
|
|
changeContext(rule->context(), rule->definition());
|
|
|
|
|
contextChanged = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Format is not applied to child rules directly (but relative to the offset of their
|
|
|
|
|
// parent) nor to look ahead rules.
|
|
|
|
|
if (!childRule && !rule->isLookAhead()) {
|
|
|
|
|
if (rule->itemData().isEmpty())
|
|
|
|
|
applyFormat(startOffset, progress->offset() - startOffset,
|
|
|
|
|
m_currentContext->itemData(), m_currentContext->definition());
|
|
|
|
|
else
|
|
|
|
|
applyFormat(startOffset, progress->offset() - startOffset, rule->itemData(),
|
|
|
|
|
rule->definition());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// When there is a match of one child rule the others should be skipped. Otherwise
|
|
|
|
|
// the highlighting would be incorret in a case like 9ULLLULLLUULLULLUL, for example.
|
|
|
|
|
if (contextChanged || childRule) {
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
it = rules.begin();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
++it;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!childRule && !atLeastOneMatch) {
|
|
|
|
|
if (m_currentContext->isFallthrough()) {
|
|
|
|
|
handleContextChange(m_currentContext->fallthroughContext(),
|
|
|
|
|
m_currentContext->definition());
|
|
|
|
|
iterateThroughRules(text, length, progress, false, m_currentContext->rules());
|
|
|
|
|
} else {
|
|
|
|
|
applyFormat(progress->offset(), 1, m_currentContext->itemData(),
|
|
|
|
|
m_currentContext->definition());
|
2010-06-17 15:27:28 +02:00
|
|
|
if (progress->isOnlySpacesSoFar() && !text.at(progress->offset()).isSpace())
|
2010-06-17 15:14:00 +02:00
|
|
|
progress->setOnlySpacesSoFar(false);
|
2010-04-27 16:33:40 +02:00
|
|
|
progress->incrementOffset();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Highlighter::contextChangeRequired(const QString &contextName) const
|
|
|
|
|
{
|
|
|
|
|
if (contextName == kStay)
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Highlighter::changeContext(const QString &contextName,
|
|
|
|
|
const QSharedPointer<HighlightDefinition> &definition,
|
2010-06-18 16:30:56 +02:00
|
|
|
const bool assignCurrent)
|
2010-04-27 16:33:40 +02:00
|
|
|
{
|
|
|
|
|
if (contextName.startsWith(kPop)) {
|
|
|
|
|
QStringList list = contextName.split(kHash, QString::SkipEmptyParts);
|
2012-08-02 11:52:28 +02:00
|
|
|
for (int i = 0; i < list.size(); ++i) {
|
|
|
|
|
if (m_contexts.isEmpty())
|
|
|
|
|
throw HighlighterException();
|
2010-04-27 16:33:40 +02:00
|
|
|
m_contexts.pop_back();
|
2012-08-02 11:52:28 +02:00
|
|
|
}
|
2010-04-27 16:33:40 +02:00
|
|
|
|
2010-06-18 16:30:56 +02:00
|
|
|
if (extractObservableState(currentBlockState()) >= PersistentsStart) {
|
2010-06-14 14:33:28 +02:00
|
|
|
// One or more contexts were popped during during a persistent state.
|
2010-04-27 16:33:40 +02:00
|
|
|
const QString ¤tSequence = currentContextSequence();
|
2010-06-18 16:30:56 +02:00
|
|
|
if (m_persistentObservableStates.contains(currentSequence))
|
|
|
|
|
setCurrentBlockState(
|
|
|
|
|
computeState(m_persistentObservableStates.value(currentSequence)));
|
2010-04-27 16:33:40 +02:00
|
|
|
else
|
2010-06-18 16:30:56 +02:00
|
|
|
setCurrentBlockState(
|
|
|
|
|
computeState(m_leadingObservableStates.value(currentSequence)));
|
2010-04-27 16:33:40 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
const QSharedPointer<Context> &context = definition->context(contextName);
|
|
|
|
|
|
|
|
|
|
if (context->isDynamic())
|
|
|
|
|
pushDynamicContext(context);
|
|
|
|
|
else
|
|
|
|
|
m_contexts.push_back(context);
|
|
|
|
|
|
2010-06-03 12:22:47 +02:00
|
|
|
if (m_contexts.back()->lineEndContext() == kStay ||
|
2010-06-18 16:30:56 +02:00
|
|
|
extractObservableState(currentBlockState()) >= PersistentsStart) {
|
2010-04-27 16:33:40 +02:00
|
|
|
const QString ¤tSequence = currentContextSequence();
|
2010-06-03 12:22:47 +02:00
|
|
|
mapLeadingSequence(currentSequence);
|
|
|
|
|
if (m_contexts.back()->lineEndContext() == kStay) {
|
|
|
|
|
// A persistent context was pushed.
|
|
|
|
|
mapPersistentSequence(currentSequence);
|
2010-06-18 16:30:56 +02:00
|
|
|
setCurrentBlockState(
|
|
|
|
|
computeState(m_persistentObservableStates.value(currentSequence)));
|
2010-06-03 12:22:47 +02:00
|
|
|
}
|
2010-04-27 16:33:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-18 16:30:56 +02:00
|
|
|
if (assignCurrent)
|
|
|
|
|
assignCurrentContext();
|
2010-04-27 16:33:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Highlighter::handleContextChange(const QString &contextName,
|
|
|
|
|
const QSharedPointer<HighlightDefinition> &definition,
|
|
|
|
|
const bool setCurrent)
|
|
|
|
|
{
|
|
|
|
|
if (!contextName.isEmpty() && contextChangeRequired(contextName))
|
|
|
|
|
changeContext(contextName, definition, setCurrent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Highlighter::applyFormat(int offset,
|
|
|
|
|
int count,
|
2010-05-03 16:39:47 +02:00
|
|
|
const QString &itemDataName,
|
2010-04-27 16:33:40 +02:00
|
|
|
const QSharedPointer<HighlightDefinition> &definition)
|
|
|
|
|
{
|
|
|
|
|
if (count == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2010-05-03 16:39:47 +02:00
|
|
|
QSharedPointer<ItemData> itemData;
|
2010-04-27 16:33:40 +02:00
|
|
|
try {
|
2010-05-03 16:39:47 +02:00
|
|
|
itemData = definition->itemData(itemDataName);
|
2010-04-27 16:33:40 +02:00
|
|
|
} catch (const HighlighterException &) {
|
2010-05-20 13:56:11 +02:00
|
|
|
// There are some broken files. For instance, the Printf context in java.xml points to an
|
|
|
|
|
// inexistent Printf item data. These cases are considered to have normal text style.
|
2010-05-03 16:39:47 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-31 13:56:46 +02:00
|
|
|
TextFormatId formatId = m_kateFormats.m_ids.value(itemData->style());
|
|
|
|
|
if (formatId != Normal) {
|
2010-10-12 13:09:29 +02:00
|
|
|
QHash<TextFormatId, QTextCharFormat>::const_iterator cit =
|
|
|
|
|
m_creatorFormats.constFind(formatId);
|
|
|
|
|
if (cit != m_creatorFormats.constEnd()) {
|
|
|
|
|
QTextCharFormat format = cit.value();
|
|
|
|
|
if (itemData->isCustomized()) {
|
2011-04-19 15:42:14 +02:00
|
|
|
// Please notice that the following are applied every time for item data which have
|
2010-10-12 13:09:29 +02:00
|
|
|
// customizations. The configureFormats method could be used to provide a "one time"
|
2011-04-19 15:42:14 +02:00
|
|
|
// configuration, but it would probably require to traverse all item data from all
|
2010-10-12 13:09:29 +02:00
|
|
|
// definitions available/loaded (either to set the values or for some "notifying"
|
|
|
|
|
// strategy). This is because the highlighter does not really know on which
|
2011-04-19 15:42:14 +02:00
|
|
|
// definition(s) it is working. Since not many item data specify customizations I
|
2010-10-12 13:09:29 +02:00
|
|
|
// think this approach would fit better. If there are other ideas...
|
|
|
|
|
if (itemData->color().isValid())
|
|
|
|
|
format.setForeground(itemData->color());
|
|
|
|
|
if (itemData->isItalicSpecified())
|
|
|
|
|
format.setFontItalic(itemData->isItalic());
|
|
|
|
|
if (itemData->isBoldSpecified())
|
|
|
|
|
format.setFontWeight(toFontWeight(itemData->isBold()));
|
|
|
|
|
if (itemData->isUnderlinedSpecified())
|
|
|
|
|
format.setFontUnderline(itemData->isUnderlined());
|
2011-03-01 11:28:37 +01:00
|
|
|
if (itemData->isStrikeOutSpecified())
|
|
|
|
|
format.setFontStrikeOut(itemData->isStrikeOut());
|
2010-10-12 13:09:29 +02:00
|
|
|
}
|
2010-05-03 16:39:47 +02:00
|
|
|
|
2010-10-12 13:09:29 +02:00
|
|
|
setFormat(offset, count, format);
|
|
|
|
|
}
|
2010-05-18 11:37:53 +02:00
|
|
|
}
|
2010-05-03 16:39:47 +02:00
|
|
|
}
|
|
|
|
|
|
2010-04-27 16:33:40 +02:00
|
|
|
void Highlighter::createWillContinueBlock()
|
|
|
|
|
{
|
2010-06-18 16:30:56 +02:00
|
|
|
BlockData *data = blockData(currentBlockUserData());
|
|
|
|
|
const int currentObservableState = extractObservableState(currentBlockState());
|
|
|
|
|
if (currentObservableState == Continued) {
|
|
|
|
|
BlockData *previousData = blockData(currentBlock().previous().userData());
|
|
|
|
|
data->m_originalObservableState = previousData->m_originalObservableState;
|
|
|
|
|
} else if (currentObservableState != WillContinue) {
|
|
|
|
|
data->m_originalObservableState = currentObservableState;
|
2010-04-27 16:33:40 +02:00
|
|
|
}
|
|
|
|
|
data->m_contextToContinue = m_currentContext;
|
|
|
|
|
|
2010-06-18 16:30:56 +02:00
|
|
|
setCurrentBlockState(computeState(WillContinue));
|
2010-04-27 16:33:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Highlighter::analyseConsistencyOfWillContinueBlock(const QString &text)
|
|
|
|
|
{
|
|
|
|
|
if (currentBlock().next().isValid() && (
|
|
|
|
|
text.length() == 0 || text.at(text.length() - 1) != kBackSlash) &&
|
2010-06-18 16:30:56 +02:00
|
|
|
extractObservableState(currentBlock().next().userState()) != Continued) {
|
|
|
|
|
currentBlock().next().setUserState(computeState(Continued));
|
2010-04-27 16:33:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (text.length() == 0 || text.at(text.length() - 1) != kBackSlash) {
|
2010-06-18 16:30:56 +02:00
|
|
|
BlockData *data = blockData(currentBlockUserData());
|
2010-04-27 16:33:40 +02:00
|
|
|
data->m_contextToContinue.clear();
|
2010-06-18 16:30:56 +02:00
|
|
|
setCurrentBlockState(computeState(data->m_originalObservableState));
|
2010-04-27 16:33:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-03 12:22:47 +02:00
|
|
|
void Highlighter::mapPersistentSequence(const QString &contextSequence)
|
2010-04-27 16:33:40 +02:00
|
|
|
{
|
2010-06-18 16:30:56 +02:00
|
|
|
if (!m_persistentObservableStates.contains(contextSequence)) {
|
|
|
|
|
int newState = m_persistentObservableStatesCounter;
|
|
|
|
|
m_persistentObservableStates.insert(contextSequence, newState);
|
2010-04-27 16:33:40 +02:00
|
|
|
m_persistentContexts.insert(newState, m_contexts);
|
2010-06-18 16:30:56 +02:00
|
|
|
++m_persistentObservableStatesCounter;
|
2010-04-27 16:33:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-03 12:22:47 +02:00
|
|
|
void Highlighter::mapLeadingSequence(const QString &contextSequence)
|
|
|
|
|
{
|
2010-06-18 16:30:56 +02:00
|
|
|
if (!m_leadingObservableStates.contains(contextSequence))
|
|
|
|
|
m_leadingObservableStates.insert(contextSequence,
|
|
|
|
|
extractObservableState(currentBlockState()));
|
2010-06-03 12:22:47 +02:00
|
|
|
}
|
|
|
|
|
|
2010-04-27 16:33:40 +02:00
|
|
|
void Highlighter::pushContextSequence(int state)
|
|
|
|
|
{
|
|
|
|
|
const QVector<QSharedPointer<Context> > &contexts = m_persistentContexts.value(state);
|
|
|
|
|
for (int i = 0; i < contexts.size(); ++i)
|
|
|
|
|
m_contexts.push_back(contexts.at(i));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString Highlighter::currentContextSequence() const
|
|
|
|
|
{
|
|
|
|
|
QString sequence;
|
|
|
|
|
for (int i = 0; i < m_contexts.size(); ++i)
|
|
|
|
|
sequence.append(m_contexts.at(i)->id());
|
|
|
|
|
|
|
|
|
|
return sequence;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Highlighter::BlockData *Highlighter::initializeBlockData()
|
|
|
|
|
{
|
|
|
|
|
BlockData *data = new BlockData;
|
|
|
|
|
setCurrentBlockUserData(data);
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-18 16:30:56 +02:00
|
|
|
Highlighter::BlockData *Highlighter::blockData(QTextBlockUserData *userData)
|
|
|
|
|
{
|
|
|
|
|
return static_cast<BlockData *>(userData);
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-27 16:33:40 +02:00
|
|
|
void Highlighter::pushDynamicContext(const QSharedPointer<Context> &baseContext)
|
|
|
|
|
{
|
|
|
|
|
// A dynamic context is created from another context which serves as its basis. Then,
|
|
|
|
|
// its rules are updated according to the captures from the calling regular expression which
|
|
|
|
|
// triggered the push of the dynamic context.
|
|
|
|
|
QSharedPointer<Context> context(new Context(*baseContext));
|
|
|
|
|
context->configureId(m_dynamicContextsCounter);
|
|
|
|
|
context->updateDynamicRules(m_currentCaptures);
|
|
|
|
|
m_contexts.push_back(context);
|
|
|
|
|
++m_dynamicContextsCounter;
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-18 16:30:56 +02:00
|
|
|
void Highlighter::assignCurrentContext()
|
2010-04-27 16:33:40 +02:00
|
|
|
{
|
2010-05-06 10:10:04 +02:00
|
|
|
if (m_contexts.isEmpty()) {
|
2010-05-20 13:56:11 +02:00
|
|
|
// This is not supposed to happen. However, there are broken files (for example, php.xml)
|
|
|
|
|
// which will cause this behaviour. In such cases pushing the default context is enough to
|
|
|
|
|
// keep highlighter working.
|
2010-05-06 10:10:04 +02:00
|
|
|
m_contexts.push_back(m_defaultContext);
|
|
|
|
|
}
|
2010-04-27 16:33:40 +02:00
|
|
|
m_currentContext = m_contexts.back();
|
|
|
|
|
}
|
2010-06-18 16:30:56 +02:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-21 13:37:53 +02:00
|
|
|
void Highlighter::applyRegionBasedFolding() const
|
2010-06-18 16:30:56 +02:00
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
2010-06-21 13:37:53 +02:00
|
|
|
|
|
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|