codehighlighting.cpp 22.9 KB
Newer Older
1
2
3
/*
 * This file is part of KDevelop
 *
David nolden's avatar
David nolden committed
4
 * Copyright 2007-2010 David Nolden <david.nolden.kdevelop@art-master.de>
5
 * Copyright 2006 Hamish Rodda <rodda@kde.org>
6
 * Copyright 2009 Milian Wolff <mail@milianw.de>
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 *
 * 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"

26
27
28
#include "../../interfaces/icore.h"
#include "../../interfaces/ilanguagecontroller.h"
#include "../../interfaces/icompletionsettings.h"
Kevin Funk's avatar
Kevin Funk committed
29
#include "../../util/foregroundlock.h"
30
#include <debug.h>
31

32
33
34
35
36
37
38
39
#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"
40
41
42

#include "colorcache.h"
#include "configurablecolors.h"
David nolden's avatar
David nolden committed
43
44
#include <duchain/parsingenvironment.h>
#include <backgroundparser/backgroundparser.h>
45
#include <backgroundparser/urlparselock.h>
46

47
48
49
#include <KTextEditor/Document>
#include <KTextEditor/MovingInterface>

50
51
using namespace KTextEditor;

52
53
static const float highlightingZDepth = -500;

54
55
#define ifDebug(x)

56
namespace KDevelop {
David nolden's avatar
David nolden committed
57
///@todo Don't highlighting everything, only what is visible on-demand
58

59
60
61
62
63
CodeHighlighting::CodeHighlighting(QObject* parent)
    : QObject(parent)
    , m_localColorization(true)
    , m_globalColorization(true)
    , m_dataMutex(QMutex::Recursive)
64
{
65
    qRegisterMetaType<KDevelop::IndexedString>("KDevelop::IndexedString");
David nolden's avatar
David nolden committed
66

67
    adaptToColorChanges();
68

69
70
    connect(ColorCache::self(), &ColorCache::colorsGotChanged,
            this, &CodeHighlighting::adaptToColorChanges);
71
72
}

73
CodeHighlighting::~CodeHighlighting()
74
{
75
    qDeleteAll(m_highlights);
76
77
}

78
79
void CodeHighlighting::adaptToColorChanges()
{
80
81
82
83
84
85
86
87
88
89
    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();
90
91
}

92
KTextEditor::Attribute::Ptr CodeHighlighting::attributeForType(Types type, Contexts context, const QColor& color) const
93
{
94
95
96
    QMutexLocker lock(&m_dataMutex);
    KTextEditor::Attribute::Ptr a;
    switch (context) {
97
    case DefinitionContext:
98
99
        a = m_definitionAttributes[type];
        break;
100
101

    case DeclarationContext:
102
103
        a = m_declarationAttributes[type];
        break;
104
105

    case ReferenceContext:
106
107
108
        a = m_referenceAttributes[type];
        break;
    }
109

110
111
112
    if (!a || color.isValid()) {
        a = KTextEditor::Attribute::Ptr(new KTextEditor::Attribute(*ColorCache::self()->defaultColors()->attribute(
                                                                       type)));
113

114
115
116
117
118
        if (context == DefinitionContext || context == DeclarationContext) {
            if (ICore::self()->languageController()->completionSettings()->boldDeclarations()) {
                a->setFontBold();
            }
        }
119

120
121
        if (color.isValid()) {
            a->setForeground(color);
122
//       a->setBackground(QColor(mix(0xffffff-color, backgroundColor(), 255-backgroundTinting)));
123
124
125
126
127
128
129
130
131
132
133
134
135
        } 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;
            }
        }
136
137
    }

138
    return a;
139
140
}

141
142
143
144
ColorMap emptyColorMap()
{
    ColorMap ret(ColorCache::self()->validColorCount() + 1, nullptr);
    return ret;
145
146
}

147
148
CodeHighlightingInstance* CodeHighlighting::createInstance() const
{
149
    return new CodeHighlightingInstance(this);
150
151
}

152
153
bool CodeHighlighting::hasHighlighting(IndexedString url) const
{
154
155
156
157
158
159
    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;
160
161
}

162
void CodeHighlighting::highlightDUChain(ReferencedTopDUContext context)
163
{
164
    ENSURE_CHAIN_NOT_LOCKED
165

166
    IndexedString url;
167

168
169
170
171
    {
        DUChainReadLocker lock;
        if (!context)
            return;
172

173
174
        url = context->url();
    }
David nolden's avatar
David nolden committed
175

176
177
    // This prevents the background-parser from updating the top-context while we're working with it
    UrlParseLock urlLock(context->url());
178

179
    DUChainReadLocker lock;
180

181
    qint64 revision = context->parsingEnvironmentFile()->modificationRevision().revision;
David nolden's avatar
David nolden committed
182

183
    qCDebug(LANGUAGE) << "highlighting du chain" << url.toUrl();
184

185
186
187
188
189
190
    if (!m_localColorization && !m_globalColorization) {
        qCDebug(LANGUAGE) << "highlighting disabled";
        QMetaObject::invokeMethod(this, "clearHighlightingForDocument", Qt::QueuedConnection,
                                  Q_ARG(KDevelop::IndexedString, url));
        return;
    }
191

192
    CodeHighlightingInstance* instance = createInstance();
193

194
    lock.unlock();
195

196
    instance->highlightDUChain(context.data());
197

198
    auto* highlighting = new DocumentHighlighting;
199
200
201
202
    highlighting->m_document = url;
    highlighting->m_waitingRevision = revision;
    highlighting->m_waiting = instance->m_highlight;
    std::sort(highlighting->m_waiting.begin(), highlighting->m_waiting.end());
203

204
    QMetaObject::invokeMethod(this, "applyHighlighting", Qt::QueuedConnection, Q_ARG(void*, highlighting));
205

206
    delete instance;
207
208
}

209
void CodeHighlightingInstance::highlightDUChain(TopDUContext* context)
210
{
211
212
    m_contextClasses.clear();
    m_useClassCache = true;
213

214
215
    //Highlight
    highlightDUChain(context, QHash<Declaration*, uint>(), emptyColorMap());
216

217
218
    m_functionColorsForDeclarations.clear();
    m_functionDeclarationsForColors.clear();
219

220
221
    m_useClassCache = false;
    m_contextClasses.clear();
222
223
}

224
225
void CodeHighlightingInstance::highlightDUChain(DUContext* context, QHash<Declaration*, uint> colorsForDeclarations,
                                                ColorMap declarationsForColors)
226
{
227
    DUChainReadLocker lock;
228

229
230
231
232
233
234
235
236
237
238
239
240
    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)];
241
242
    }

243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
    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()->primaryColorCount();

        if (declarationsForColors[colorNum]) {
            takeFreeColors << dec; //Use one of the colors that stays free
            continue;
        }

        colorsForDeclarations[dec] = colorNum;
        declarationsForColors[colorNum] = dec;
260

261
262
        highlightDeclaration(dec, ColorCache::self()->generatedColor(colorNum));
    }
263

264
265
266
267
268
269
270
271
272
273
274
275
    foreach (Declaration* dec, takeFreeColors) {
        uint colorNum = dec->identifier().hash() % ColorCache::self()->primaryColorCount();
        uint oldColorNum = colorNum;
        while (declarationsForColors[colorNum]) {
            colorNum = (colorNum + 1) % ColorCache::self()->primaryColorCount();
            if (colorNum == oldColorNum) {
                colorNum = ColorCache::self()->primaryColorCount();
                break;
            }
        }

        if (colorNum < ColorCache::self()->primaryColorCount()) {
276
            // Use primary color
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
            colorsForDeclarations[dec] = colorNum;
            declarationsForColors[colorNum] = dec;
            highlightDeclaration(dec, ColorCache::self()->generatedColor(colorNum));
        } else {
            // Try to use supplementary color
            colorNum = ColorCache::self()->primaryColorCount();
            while (declarationsForColors[colorNum]) {
                colorNum++;
                if (colorNum == ColorCache::self()->validColorCount()) {
                    //If no color could be found, use default color
                    highlightDeclaration(dec, QColor(QColor::Invalid));
                    break;
                }
            }
            if (colorNum < ColorCache::self()->validColorCount()) {
                // Use supplementary color
                colorsForDeclarations[dec] = colorNum;
                declarationsForColors[colorNum] = dec;
                highlightDeclaration(dec, ColorCache::self()->generatedColor(colorNum));
            }
        }
298
    }
299

300
301
302
303
304
305
306
    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);
    }
307

308
309
310
311
    if (context->type() == DUContext::Other || context->type() == DUContext::Function) {
        m_functionColorsForDeclarations[IndexedDUContext(context)] = colorsForDeclarations;
        m_functionDeclarationsForColors[IndexedDUContext(context)] = declarationsForColors;
    }
312

313
    const QVector<DUContext*> children = context->childContexts();
314

315
    lock.unlock(); // Periodically release the lock, so that the UI won't be blocked too much
316

317
318
319
    for (DUContext* child : children) {
        highlightDUChain(child,  colorsForDeclarations, declarationsForColors);
    }
320
321
322
323
}

KTextEditor::Attribute::Ptr CodeHighlighting::attributeForDepth(int depth) const
{
324
325
326
327
328
329
330
331
332
333
    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];
334
335
}

336
KDevelop::Declaration* CodeHighlightingInstance::localClassFromCodeContext(KDevelop::DUContext* context) const
337
{
338
339
    if (!context)
        return nullptr;
340

341
342
    if (m_contextClasses.contains(context))
        return m_contextClasses[context];
343

344
    DUContext* startContext = context;
345

346
347
348
349
350
351
352
    while (context->type() == DUContext::Other) {
        //Move context to the top context of type "Other". This is needed because every compound-statement creates a new sub-context.
        auto parent = context->parentContext();
        if (!parent || (parent->type() != DUContext::Other && parent->type() != DUContext::Function)) {
            break;
        }
        context = context->parentContext();
353
    }
354

355
356
    ///Step 1: Find the function-declaration for the function we are in
    Declaration* functionDeclaration = nullptr;
357

358
    if (auto* def = dynamic_cast<FunctionDefinition*>(context->owner())) {
359
360
        if (m_contextClasses.contains(context))
            return m_contextClasses[context];
361

362
363
        functionDeclaration = def->declaration(startContext->topContext());
    }
364

365
366
    if (!functionDeclaration && context->owner())
        functionDeclaration = context->owner();
367

368
369
370
371
372
    if (!functionDeclaration) {
        if (m_useClassCache)
            m_contextClasses[context] = nullptr;
        return nullptr;
    }
373

374
    Declaration* decl  = functionDeclaration->context()->owner();
375

376
377
    if (m_useClassCache)
        m_contextClasses[context] = decl;
378

379
    return decl;
380
381
}

382
CodeHighlightingInstance::Types CodeHighlightingInstance::typeForDeclaration(Declaration* dec, DUContext* context) const
383
{
384
385
386
387
388
389
390
    /**
     * 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.
     *
     * */
391
392
393
394
395

//   if(ClassMemberDeclaration* classMember = dynamic_cast<ClassMemberDeclaration*>(dec))
//     if(!Cpp::isAccessible(context, classMember))
//       return ErrorVariableType;

396
397
398
399
400
401
402
403
404
    if (!dec)
        return ErrorVariableType;

    Types type = LocalVariableType;
    if (dec->kind() == Declaration::Namespace)
        return NamespaceType;

    if (dec->kind() == Declaration::Macro) {
        return MacroType;
405
    }
406
407
408
409
410
411
412
413
414
415
416

    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 (klass->internalContext() && klass->internalContext()->imports(dec->context()))
                type = InheritedClassMemberType; //Using Member of an inherited class
        }
417
418
    }

419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
    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;
        }
    }
436

437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
    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;
        case DUContext::Global:
            type = GlobalVariableType;
            break;
        default:
            break;
        }
454
455
    }

456
    return type;
457
458
}

459
460
bool CodeHighlightingInstance::useRainbowColor(Declaration* dec) const
{
461
462
    return dec->context()->type() == DUContext::Function ||
           (dec->context()->type() == DUContext::Other && dec->context()->owner());
463
464
}

465
void CodeHighlightingInstance::highlightDeclaration(Declaration* declaration, const QColor& color)
David nolden's avatar
David nolden committed
466
{
467
468
469
470
    HighlightedRange h;
    h.range = declaration->range();
    h.attribute = m_highlighting->attributeForType(typeForDeclaration(declaration, nullptr), DeclarationContext, color);
    m_highlight.push_back(h);
David nolden's avatar
David nolden committed
471
472
}

473
void CodeHighlightingInstance::highlightUse(DUContext* context, int index, const QColor& color)
474
{
475
476
    Types type = ErrorVariableType;
    Declaration* decl = context->topContext()->usedDeclarationForIndex(context->uses()[index].m_declarationIndex);
477

478
    type = typeForDeclaration(decl, context);
479

480
481
482
483
484
485
486
    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);
    }
487
488
}

David nolden's avatar
David nolden committed
489
void CodeHighlightingInstance::highlightUses(DUContext* context)
490
{
491
492
    for (int a = 0; a < context->usesCount(); ++a)
        highlightUse(context, a, QColor(QColor::Invalid));
David nolden's avatar
David nolden committed
493
}
494

495
void CodeHighlighting::clearHighlightingForDocument(const IndexedString& document)
David nolden's avatar
David nolden committed
496
{
497
498
499
500
501
502
503
504
505
    VERIFY_FOREGROUND_LOCKED
    QMutexLocker lock(&m_dataMutex);
    DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(document);
    if (m_highlights.contains(tracker)) {
        disconnect(tracker, &DocumentChangeTracker::destroyed, this, &CodeHighlighting::trackerDestroyed);
        qDeleteAll(m_highlights[tracker]->m_highlightedRanges);
        delete m_highlights[tracker];
        m_highlights.remove(tracker);
    }
David nolden's avatar
David nolden committed
506
507
}

508
void CodeHighlighting::applyHighlighting(void* _highlighting)
David nolden's avatar
David nolden committed
509
{
510
    auto* highlighting =
511
512
513
514
515
516
517
518
519
520
521
        static_cast<CodeHighlighting::DocumentHighlighting*>(_highlighting);

    VERIFY_FOREGROUND_LOCKED
    QMutexLocker lock(&m_dataMutex);
    DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(
        highlighting->m_document);

    if (!tracker) {
        qCDebug(LANGUAGE) << "no document found for the planned highlighting of" << highlighting->m_document.str();
        delete highlighting;
        return;
522
523
    }

524
525
526
527
528
529
    if (!tracker->holdingRevision(highlighting->m_waitingRevision)) {
        qCDebug(LANGUAGE) << "not holding revision" << highlighting->m_waitingRevision << "not applying highlighting;"
                          << "probably a new parse job has already updated the context";
        delete highlighting;
        return;
    }
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
530

531
    QVector<MovingRange*> oldHighlightedRanges;
David nolden's avatar
David nolden committed
532

533
534
535
536
537
538
539
540
541
542
543
    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
        // This can't use new style connect syntax since MovingInterface is not a 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)));
        connect(tracker, &DocumentChangeTracker::destroyed, this, &CodeHighlighting::trackerDestroyed);
David nolden's avatar
David nolden committed
544
    }
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588

    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
        KTextEditor::Range 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 = transformedRange;

        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;
David nolden's avatar
David nolden committed
589
590
    }

591
592
    for (; movingIt != oldHighlightedRanges.end(); ++movingIt)
        delete *movingIt; // Delete unmatched moving ranges behind
593
594
}

David nolden's avatar
David nolden committed
595
void CodeHighlighting::trackerDestroyed(QObject* object)
596
{
597
598
599
    // Called when a document is destroyed
    VERIFY_FOREGROUND_LOCKED
    QMutexLocker lock(&m_dataMutex);
600
    auto* tracker = static_cast<DocumentChangeTracker*>(object);
601
602
603
    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);
604
605
}

606
607
void CodeHighlighting::aboutToInvalidateMovingInterfaceContent(Document* doc)
{
608
    clearHighlightingForDocument(IndexedString(doc->url()));
609
}
David nolden's avatar
David nolden committed
610

611
void CodeHighlighting::aboutToRemoveText(const KTextEditor::Range& range)
612
{
613
614
615
616
617
618
    if (range.onSingleLine()) // don't try to optimize this
        return;

    VERIFY_FOREGROUND_LOCKED
    QMutexLocker lock(&m_dataMutex);
    Q_ASSERT(dynamic_cast<KTextEditor::Document*>(sender()));
619
    auto* doc = static_cast<KTextEditor::Document*>(sender());
620
621
622
623
624
625
626
627
628
629
630
631
632
633

    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;
            }
        }
634
635
    }
}
636
}