mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-26 11:52:54 +00:00
604 lines
20 KiB
C++
604 lines
20 KiB
C++
/*
|
|
* This file is part of KDevelop
|
|
*
|
|
* Copyright 2007-2010 David Nolden <david.nolden.kdevelop@art-master.de>
|
|
* Copyright 2006 Hamish Rodda <rodda@kde.org>
|
|
* Copyright 2009 Milian Wolff <mail@milianw.de>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Library General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public
|
|
* License along with this program; if not, write to the
|
|
* Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "codehighlighting.h"
|
|
|
|
#include <KTextEditor/Document>
|
|
|
|
#include "../../interfaces/icore.h"
|
|
#include "../../interfaces/ilanguagecontroller.h"
|
|
#include "../../interfaces/icompletionsettings.h"
|
|
#include "../../interfaces/foregroundlock.h"
|
|
|
|
#include "../duchain/declaration.h"
|
|
#include "../duchain/types/functiontype.h"
|
|
#include "../duchain/types/enumeratortype.h"
|
|
#include "../duchain/types/typealiastype.h"
|
|
#include "../duchain/types/enumerationtype.h"
|
|
#include "../duchain/types/structuretype.h"
|
|
#include "../duchain/functiondefinition.h"
|
|
#include "../duchain/use.h"
|
|
|
|
#include "colorcache.h"
|
|
#include "configurablecolors.h"
|
|
#include <duchain/parsingenvironment.h>
|
|
#include <backgroundparser/backgroundparser.h>
|
|
#include <ktexteditor/movinginterface.h>
|
|
#include <duchain/dumpchain.h>
|
|
#include <backgroundparser/urlparselock.h>
|
|
|
|
using namespace KTextEditor;
|
|
|
|
static const float highlightingZDepth = -500;
|
|
|
|
#define ifDebug(x)
|
|
|
|
namespace KDevelop {
|
|
|
|
///@todo Don't highlighting everything, only what is visible on-demand
|
|
|
|
CodeHighlighting::CodeHighlighting( QObject * parent )
|
|
: QObject(parent), m_localColorization(true), m_globalColorization(true), m_dataMutex(QMutex::Recursive)
|
|
{
|
|
qRegisterMetaType<KDevelop::IndexedString>("KDevelop::IndexedString");
|
|
|
|
adaptToColorChanges();
|
|
|
|
connect(ColorCache::self(), SIGNAL(colorsGotChanged()),
|
|
this, SLOT(adaptToColorChanges()));
|
|
}
|
|
|
|
CodeHighlighting::~CodeHighlighting( )
|
|
{
|
|
qDeleteAll(m_highlights.values());
|
|
}
|
|
|
|
void CodeHighlighting::adaptToColorChanges()
|
|
{
|
|
QMutexLocker lock(&m_dataMutex);
|
|
// disable local highlighting if the ratio is set to 0
|
|
m_localColorization = ICore::self()->languageController()->completionSettings()->localColorizationLevel() > 0;
|
|
// disable global highlighting if the ratio is set to 0
|
|
m_globalColorization = ICore::self()->languageController()->completionSettings()->globalColorizationLevel() > 0;
|
|
|
|
m_declarationAttributes.clear();
|
|
m_definitionAttributes.clear();
|
|
m_depthAttributes.clear();
|
|
m_referenceAttributes.clear();
|
|
}
|
|
|
|
KTextEditor::Attribute::Ptr CodeHighlighting::attributeForType( Types type, Contexts context, const QColor &color ) const
|
|
{
|
|
QMutexLocker lock(&m_dataMutex);
|
|
KTextEditor::Attribute::Ptr a;
|
|
switch (context) {
|
|
case DefinitionContext:
|
|
a = m_definitionAttributes[type];
|
|
break;
|
|
|
|
case DeclarationContext:
|
|
a = m_declarationAttributes[type];
|
|
break;
|
|
|
|
case ReferenceContext:
|
|
a = m_referenceAttributes[type];
|
|
break;
|
|
}
|
|
|
|
if ( !a || color.isValid() ) {
|
|
|
|
a = KTextEditor::Attribute::Ptr(new KTextEditor::Attribute(*ColorCache::self()->defaultColors()->getAttribute(type)));
|
|
|
|
if ( context == DefinitionContext || context == DeclarationContext ) {
|
|
a->setFontBold();
|
|
}
|
|
|
|
if( color.isValid() ) {
|
|
a->setForeground(color);
|
|
// a->setBackground(QColor(mix(0xffffff-color, backgroundColor(), 255-backgroundTinting)));
|
|
} else {
|
|
switch (context) {
|
|
case DefinitionContext:
|
|
m_definitionAttributes.insert(type, a);
|
|
break;
|
|
case DeclarationContext:
|
|
m_declarationAttributes.insert(type, a);
|
|
break;
|
|
case ReferenceContext:
|
|
m_referenceAttributes.insert(type, a);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return a;
|
|
}
|
|
|
|
ColorMap emptyColorMap() {
|
|
ColorMap ret(ColorCache::self()->validColorCount()+1, 0);
|
|
return ret;
|
|
}
|
|
|
|
CodeHighlightingInstance* CodeHighlighting::createInstance() const
|
|
{
|
|
return new CodeHighlightingInstance(this);
|
|
}
|
|
|
|
bool CodeHighlighting::hasHighlighting(IndexedString url) const
|
|
{
|
|
DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(url);
|
|
if(tracker)
|
|
{
|
|
QMutexLocker lock(&m_dataMutex);
|
|
return m_highlights.contains(tracker) && !m_highlights[tracker]->m_highlightedRanges.isEmpty();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CodeHighlighting::highlightDUChain(ReferencedTopDUContext context)
|
|
{
|
|
ENSURE_CHAIN_NOT_LOCKED
|
|
|
|
IndexedString url;
|
|
|
|
{
|
|
DUChainReadLocker lock;
|
|
if (!context)
|
|
return;
|
|
|
|
url = context->url();
|
|
}
|
|
|
|
// This prevents the background-parser from updating the top-context while we're working with it
|
|
UrlParseLock urlLock(context->url());
|
|
|
|
DUChainReadLocker lock;
|
|
|
|
qint64 revision = context->parsingEnvironmentFile()->modificationRevision().revision;
|
|
|
|
kDebug() << "highlighting du chain" << url.toUrl();
|
|
|
|
if ( !m_localColorization && !m_globalColorization ) {
|
|
kDebug() << "highlighting disabled";
|
|
QMetaObject::invokeMethod(this, "clearHighlightingForDocument", Qt::QueuedConnection, Q_ARG(KDevelop::IndexedString, url));
|
|
return;
|
|
}
|
|
|
|
CodeHighlightingInstance* instance = createInstance();
|
|
|
|
lock.unlock();
|
|
|
|
instance->highlightDUChain(context.data());
|
|
|
|
DocumentHighlighting* highlighting = new DocumentHighlighting;
|
|
highlighting->m_document = url;
|
|
highlighting->m_waitingRevision = revision;
|
|
highlighting->m_waiting = instance->m_highlight;
|
|
qSort(highlighting->m_waiting.begin(), highlighting->m_waiting.end());
|
|
|
|
QMetaObject::invokeMethod(this, "applyHighlighting", Qt::QueuedConnection, Q_ARG(void*, highlighting));
|
|
|
|
delete instance;
|
|
}
|
|
|
|
void CodeHighlightingInstance::highlightDUChain(TopDUContext* context)
|
|
{
|
|
m_contextClasses.clear();
|
|
m_useClassCache = true;
|
|
|
|
//Highlight
|
|
highlightDUChain(context, QHash<Declaration*, uint>(), emptyColorMap());
|
|
|
|
m_functionColorsForDeclarations.clear();
|
|
m_functionDeclarationsForColors.clear();
|
|
|
|
m_useClassCache = false;
|
|
m_contextClasses.clear();
|
|
}
|
|
|
|
void CodeHighlightingInstance::highlightDUChain(DUContext* context, QHash<Declaration*, uint> colorsForDeclarations, ColorMap declarationsForColors)
|
|
{
|
|
DUChainReadLocker lock;
|
|
|
|
TopDUContext* top = context->topContext();
|
|
|
|
//Merge the colors from the function arguments
|
|
foreach( const DUContext::Import &imported, context->importedParentContexts() ) {
|
|
if(!imported.context(top) || (imported.context(top)->type() != DUContext::Other && imported.context(top)->type() != DUContext::Function))
|
|
continue;
|
|
//For now it's enough simply copying them, because we only pass on colors within function bodies.
|
|
if (m_functionColorsForDeclarations.contains(imported.context(top)))
|
|
colorsForDeclarations = m_functionColorsForDeclarations[imported.context(top)];
|
|
if (m_functionDeclarationsForColors.contains(imported.context(top)))
|
|
declarationsForColors = m_functionDeclarationsForColors[imported.context(top)];
|
|
}
|
|
|
|
QList<Declaration*> takeFreeColors;
|
|
|
|
foreach (Declaration* dec, context->localDeclarations()) {
|
|
if (!useRainbowColor(dec)) {
|
|
highlightDeclaration(dec, QColor(QColor::Invalid));
|
|
continue;
|
|
}
|
|
//Initially pick a color using the hash, so the chances are good that the same identifier gets the same color always.
|
|
uint colorNum = dec->identifier().hash() % ColorCache::self()->validColorCount();
|
|
|
|
if( declarationsForColors[colorNum] ) {
|
|
takeFreeColors << dec; //Use one of the colors that stays free
|
|
continue;
|
|
}
|
|
|
|
colorsForDeclarations[dec] = colorNum;
|
|
declarationsForColors[colorNum] = dec;
|
|
|
|
highlightDeclaration(dec, ColorCache::self()->generatedColor(colorNum));
|
|
}
|
|
|
|
foreach( Declaration* dec, takeFreeColors ) {
|
|
uint colorNum = dec->identifier().hash() % ColorCache::self()->validColorCount();
|
|
uint oldColorNum = colorNum;
|
|
while( declarationsForColors[colorNum] ) {
|
|
colorNum = (colorNum+1) % ColorCache::self()->validColorCount();
|
|
if( colorNum == oldColorNum ) {
|
|
colorNum = ColorCache::self()->validColorCount();
|
|
break;
|
|
}
|
|
}
|
|
if(colorNum != ColorCache::self()->validColorCount()) {
|
|
//If no color could be found, use default color,, not black
|
|
colorsForDeclarations[dec] = colorNum;
|
|
declarationsForColors[colorNum] = dec;
|
|
highlightDeclaration(dec, ColorCache::self()->generatedColor(colorNum));
|
|
}else{
|
|
highlightDeclaration(dec, QColor(QColor::Invalid));
|
|
}
|
|
}
|
|
|
|
for(int a = 0; a < context->usesCount(); ++a) {
|
|
Declaration* decl = context->topContext()->usedDeclarationForIndex(context->uses()[a].m_declarationIndex);
|
|
QColor color(QColor::Invalid);
|
|
if( colorsForDeclarations.contains(decl) )
|
|
color = ColorCache::self()->generatedColor(colorsForDeclarations[decl]);
|
|
highlightUse(context, a, color);
|
|
}
|
|
|
|
if(context->type() == DUContext::Other || context->type() == DUContext::Function) {
|
|
m_functionColorsForDeclarations[IndexedDUContext(context)] = colorsForDeclarations;
|
|
m_functionDeclarationsForColors[IndexedDUContext(context)] = declarationsForColors;
|
|
}
|
|
|
|
QVector< DUContext* > children = context->childContexts();
|
|
|
|
lock.unlock(); // Periodically release the lock, so that the UI won't be blocked too much
|
|
|
|
foreach (DUContext* child, children)
|
|
highlightDUChain(child, colorsForDeclarations, declarationsForColors );
|
|
}
|
|
|
|
KTextEditor::Attribute::Ptr CodeHighlighting::attributeForDepth(int depth) const
|
|
{
|
|
while (depth >= m_depthAttributes.count()) {
|
|
KTextEditor::Attribute::Ptr a(new KTextEditor::Attribute());
|
|
a->setBackground(QColor(Qt::white).dark(100 + (m_depthAttributes.count() * 25)));
|
|
a->setBackgroundFillWhitespace(true);
|
|
if (depth % 2)
|
|
a->setOutline(Qt::red);
|
|
m_depthAttributes.append(a);
|
|
}
|
|
|
|
return m_depthAttributes[depth];
|
|
}
|
|
|
|
KDevelop::Declaration* CodeHighlightingInstance::localClassFromCodeContext(KDevelop::DUContext* context) const
|
|
{
|
|
if(!context)
|
|
return 0;
|
|
|
|
if(m_contextClasses.contains(context))
|
|
return m_contextClasses[context];
|
|
|
|
DUContext* startContext = context;
|
|
|
|
while( context->parentContext() && context->type() == DUContext::Other && context->parentContext()->type() == DUContext::Other )
|
|
{ //Move context to the top context of type "Other". This is needed because every compound-statement creates a new sub-context.
|
|
context = context->parentContext();
|
|
}
|
|
|
|
///Step 1: Find the function-declaration for the function we are in
|
|
Declaration* functionDeclaration = 0;
|
|
|
|
if( FunctionDefinition* def = dynamic_cast<FunctionDefinition*>(context->owner()) ) {
|
|
|
|
if(m_contextClasses.contains(context))
|
|
return m_contextClasses[context];
|
|
|
|
functionDeclaration = def->declaration(startContext->topContext());
|
|
}
|
|
|
|
if( !functionDeclaration && context->owner() )
|
|
functionDeclaration = context->owner();
|
|
|
|
if(!functionDeclaration) {
|
|
if(m_useClassCache)
|
|
m_contextClasses[context] = 0;
|
|
return 0;
|
|
}
|
|
|
|
Declaration* decl = functionDeclaration->context()->owner();
|
|
|
|
if(m_useClassCache)
|
|
m_contextClasses[context] = decl;
|
|
|
|
return decl;
|
|
}
|
|
|
|
CodeHighlightingInstance::Types CodeHighlightingInstance::typeForDeclaration(Declaration * dec, DUContext* context) const
|
|
{
|
|
/**
|
|
* We highlight in 3 steps by priority:
|
|
* 1. Is the item in the local class or an inherited class? If yes, highlight.
|
|
* 2. What kind of item is it? If it's a type/function/enumerator, highlight by type.
|
|
* 3. Else, highlight by scope.
|
|
*
|
|
* */
|
|
|
|
// if(ClassMemberDeclaration* classMember = dynamic_cast<ClassMemberDeclaration*>(dec))
|
|
// if(!Cpp::isAccessible(context, classMember))
|
|
// return ErrorVariableType;
|
|
|
|
if(!dec)
|
|
return ErrorVariableType;
|
|
|
|
Types type = LocalVariableType;
|
|
if(dec->kind() == Declaration::Namespace)
|
|
return NamespaceType;
|
|
|
|
if (context && dec->context() && dec->context()->type() == DUContext::Class) {
|
|
//It is a use.
|
|
//Determine the class we're in
|
|
Declaration* klass = localClassFromCodeContext(context);
|
|
if(klass) {
|
|
if (klass->internalContext() == dec->context())
|
|
type = LocalClassMemberType; //Using Member of the local class
|
|
else if (dec->context()->type() == DUContext::Class && klass->internalContext() && klass->internalContext()->imports(dec->context()))
|
|
type = InheritedClassMemberType; //Using Member of an inherited class
|
|
}
|
|
}
|
|
|
|
if (type == LocalVariableType) {
|
|
if (dec->kind() == Declaration::Type || dec->type<KDevelop::FunctionType>() || dec->type<KDevelop::EnumeratorType>()) {
|
|
if (dec->isForwardDeclaration())
|
|
type = ForwardDeclarationType;
|
|
else if (dec->type<KDevelop::FunctionType>())
|
|
type = FunctionType;
|
|
else if(dec->type<StructureType>())
|
|
type = ClassType;
|
|
else if(dec->type<KDevelop::TypeAliasType>())
|
|
type = TypeAliasType;
|
|
else if(dec->type<EnumerationType>())
|
|
type = EnumType;
|
|
else if(dec->type<KDevelop::EnumeratorType>())
|
|
type = EnumeratorType;
|
|
}
|
|
}
|
|
|
|
if (type == LocalVariableType) {
|
|
|
|
switch (dec->context()->type()) {
|
|
case DUContext::Namespace:
|
|
type = NamespaceVariableType;
|
|
break;
|
|
case DUContext::Class:
|
|
type = MemberVariableType;
|
|
break;
|
|
case DUContext::Function:
|
|
type = FunctionVariableType;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
bool CodeHighlightingInstance::useRainbowColor(Declaration* dec) const
|
|
{
|
|
return dec->context()->type() == DUContext::Function || (dec->context()->type() == DUContext::Other && dec->context()->owner());
|
|
}
|
|
|
|
void CodeHighlightingInstance::highlightDeclaration(Declaration * declaration, const QColor &color)
|
|
{
|
|
HighlightedRange h;
|
|
h.range = declaration->range();
|
|
h.attribute = m_highlighting->attributeForType(typeForDeclaration(declaration, 0), DeclarationContext, color);
|
|
m_highlight.push_back(h);
|
|
}
|
|
|
|
void CodeHighlightingInstance::highlightUse(DUContext* context, int index, const QColor &color)
|
|
{
|
|
Types type = ErrorVariableType;
|
|
Declaration* decl = context->topContext()->usedDeclarationForIndex(context->uses()[index].m_declarationIndex);
|
|
|
|
type = typeForDeclaration(decl, context);
|
|
|
|
if(type != ErrorVariableType || ICore::self()->languageController()->completionSettings()->highlightSemanticProblems())
|
|
{
|
|
HighlightedRange h;
|
|
h.range = context->uses()[index].m_range;
|
|
h.attribute = m_highlighting->attributeForType(type, ReferenceContext, color);
|
|
m_highlight.push_back(h);
|
|
}
|
|
}
|
|
|
|
void CodeHighlightingInstance::highlightUses(DUContext* context)
|
|
{
|
|
for(int a = 0; a < context->usesCount(); ++a)
|
|
highlightUse(context, a, QColor(QColor::Invalid));
|
|
}
|
|
|
|
|
|
void CodeHighlighting::clearHighlightingForDocument(IndexedString document)
|
|
{
|
|
VERIFY_FOREGROUND_LOCKED
|
|
QMutexLocker lock(&m_dataMutex);
|
|
DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(document);
|
|
if(m_highlights.contains(tracker))
|
|
{
|
|
disconnect(tracker, SIGNAL(destroyed(QObject*)), this, SLOT(trackerDestroyed(QObject*)));
|
|
qDeleteAll(m_highlights[tracker]->m_highlightedRanges);
|
|
delete m_highlights[tracker];
|
|
m_highlights.remove(tracker);
|
|
}
|
|
}
|
|
|
|
void CodeHighlighting::applyHighlighting(void* _highlighting)
|
|
{
|
|
CodeHighlighting::DocumentHighlighting* highlighting = static_cast<CodeHighlighting::DocumentHighlighting*>(_highlighting);
|
|
|
|
VERIFY_FOREGROUND_LOCKED
|
|
QMutexLocker lock(&m_dataMutex);
|
|
DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(highlighting->m_document);
|
|
|
|
if(!tracker)
|
|
{
|
|
kDebug() << "no document found for the planned highlighting of" << highlighting->m_document.str();
|
|
delete highlighting;
|
|
return;
|
|
}
|
|
|
|
QVector< MovingRange* > oldHighlightedRanges;
|
|
|
|
if(m_highlights.contains(tracker))
|
|
{
|
|
oldHighlightedRanges = m_highlights[tracker]->m_highlightedRanges;
|
|
delete m_highlights[tracker];
|
|
}else{
|
|
// we newly add this tracker, so add the connection
|
|
connect(tracker, SIGNAL(destroyed(QObject*)), SLOT(trackerDestroyed(QObject*)));
|
|
connect(tracker->document(), SIGNAL(aboutToInvalidateMovingInterfaceContent(KTextEditor::Document*)),
|
|
this, SLOT(aboutToInvalidateMovingInterfaceContent(KTextEditor::Document*)));
|
|
connect(tracker->document(), SIGNAL(aboutToRemoveText(KTextEditor::Range)),
|
|
this, SLOT(aboutToRemoveText(KTextEditor::Range)));
|
|
}
|
|
|
|
m_highlights[tracker] = highlighting;
|
|
|
|
// Now create MovingRanges (match old ones with the incoming ranges)
|
|
|
|
KTextEditor::Range tempRange;
|
|
|
|
QVector<MovingRange*>::iterator movingIt = oldHighlightedRanges.begin();
|
|
QVector<HighlightedRange>::iterator rangeIt = highlighting->m_waiting.begin();
|
|
|
|
while(rangeIt != highlighting->m_waiting.end())
|
|
{
|
|
// Translate the range into the current revision
|
|
SimpleRange transformedRange = tracker->transformToCurrentRevision(rangeIt->range, highlighting->m_waitingRevision);
|
|
|
|
while(movingIt != oldHighlightedRanges.end() &&
|
|
((*movingIt)->start().line() < transformedRange.start.line ||
|
|
((*movingIt)->start().line() == transformedRange.start.line && (*movingIt)->start().column() < transformedRange.start.column)))
|
|
{
|
|
delete *movingIt; // Skip ranges that are in front of the current matched range
|
|
++movingIt;
|
|
}
|
|
|
|
tempRange.start().setPosition(transformedRange.start.line, transformedRange.start.column);
|
|
tempRange.end().setPosition(transformedRange.end.line, transformedRange.end.column);
|
|
|
|
if(movingIt == oldHighlightedRanges.end() ||
|
|
transformedRange.start.line != (*movingIt)->start().line() ||
|
|
transformedRange.start.column != (*movingIt)->start().column() ||
|
|
transformedRange.end.line != (*movingIt)->end().line() ||
|
|
transformedRange.end.column != (*movingIt)->end().column())
|
|
{
|
|
Q_ASSERT(rangeIt->attribute);
|
|
// The moving range is behind or unequal, create a new range
|
|
highlighting->m_highlightedRanges.push_back(tracker->documentMovingInterface()->newMovingRange(tempRange));
|
|
highlighting->m_highlightedRanges.back()->setAttribute(rangeIt->attribute);
|
|
highlighting->m_highlightedRanges.back()->setZDepth(highlightingZDepth);
|
|
}
|
|
else
|
|
{
|
|
// Update the existing moving range
|
|
(*movingIt)->setAttribute(rangeIt->attribute);
|
|
(*movingIt)->setRange(tempRange);
|
|
highlighting->m_highlightedRanges.push_back(*movingIt);
|
|
++movingIt;
|
|
}
|
|
++rangeIt;
|
|
}
|
|
|
|
for(; movingIt != oldHighlightedRanges.end(); ++movingIt)
|
|
delete *movingIt; // Delete unmatched moving ranges behind
|
|
}
|
|
|
|
void CodeHighlighting::trackerDestroyed(QObject* object)
|
|
{
|
|
// Called when a document is destroyed
|
|
VERIFY_FOREGROUND_LOCKED
|
|
QMutexLocker lock(&m_dataMutex);
|
|
DocumentChangeTracker* tracker = static_cast<DocumentChangeTracker*>(object);
|
|
Q_ASSERT(m_highlights.contains(tracker));
|
|
delete m_highlights[tracker]; // No need to care about the individual ranges, as the document is being destroyed
|
|
m_highlights.remove(tracker);
|
|
}
|
|
|
|
void CodeHighlighting::aboutToInvalidateMovingInterfaceContent(Document* doc)
|
|
{
|
|
clearHighlightingForDocument(IndexedString(doc->url()));
|
|
}
|
|
|
|
void CodeHighlighting::aboutToRemoveText( const KTextEditor::Range& range )
|
|
{
|
|
if (range.onSingleLine()) // don't try to optimize this
|
|
return;
|
|
|
|
VERIFY_FOREGROUND_LOCKED
|
|
QMutexLocker lock(&m_dataMutex);
|
|
Q_ASSERT(dynamic_cast<KTextEditor::Document*>(sender()));
|
|
KTextEditor::Document* doc = static_cast<KTextEditor::Document*>(sender());
|
|
|
|
DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()
|
|
->trackerForUrl(IndexedString(doc->url()));
|
|
if(m_highlights.contains(tracker))
|
|
{
|
|
QVector<MovingRange*>& ranges = m_highlights.value(tracker)->m_highlightedRanges;
|
|
QVector<MovingRange*>::iterator it = ranges.begin();
|
|
while(it != ranges.end()) {
|
|
if (range.contains((*it)->toRange())) {
|
|
delete (*it);
|
|
it = ranges.erase(it);
|
|
} else {
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#include "moc_codehighlighting.cpp"
|
|
|
|
// kate: space-indent on; indent-width 2; replace-trailing-space-save on; show-tabs on; tab-indents on; tab-width 2;
|