codehighlighting.cpp 23.5 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
    DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(url);
    if (tracker) {
        QMutexLocker lock(&m_dataMutex);
157
158
        const auto highlightingIt = m_highlights.constFind(tracker);
        return highlightingIt != m_highlights.constEnd() && !(*highlightingIt)->m_highlightedRanges.isEmpty();
159
160
    }
    return false;
161
162
}

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

167
    IndexedString url;
168

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

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

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

180
    DUChainReadLocker lock;
181

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

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

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

193
    CodeHighlightingInstance* instance = createInstance();
194

195
    lock.unlock();
196

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

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

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

207
    delete instance;
208
209
}

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

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

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

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

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

230
231
232
233
234
235
236
237
    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.
238
239
240
241
242
243
        const auto functionColorsIt = m_functionColorsForDeclarations.constFind(imported.context(top));
        if (functionColorsIt != m_functionColorsForDeclarations.constEnd())
            colorsForDeclarations = *functionColorsIt;
        const auto functionDeclarationsIt = m_functionDeclarationsForColors.constFind(imported.context(top));
        if (functionDeclarationsIt != m_functionDeclarationsForColors.constEnd())
            declarationsForColors = *functionDeclarationsIt;
244
245
    }

246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
    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;
263

264
265
        highlightDeclaration(dec, ColorCache::self()->generatedColor(colorNum));
    }
266

267
268
269
270
271
272
273
274
275
276
277
278
    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()) {
279
            // Use primary color
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
            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));
            }
        }
301
    }
302

303
304
305
    for (int a = 0; a < context->usesCount(); ++a) {
        Declaration* decl = context->topContext()->usedDeclarationForIndex(context->uses()[a].m_declarationIndex);
        QColor color(QColor::Invalid);
306
307
308
        const auto colorsIt = colorsForDeclarations.constFind(decl);
        if (colorsIt != colorsForDeclarations.constEnd())
            color = ColorCache::self()->generatedColor(*colorsIt);
309
310
        highlightUse(context, a, color);
    }
311

312
313
314
315
    if (context->type() == DUContext::Other || context->type() == DUContext::Function) {
        m_functionColorsForDeclarations[IndexedDUContext(context)] = colorsForDeclarations;
        m_functionDeclarationsForColors[IndexedDUContext(context)] = declarationsForColors;
    }
316

317
    const QVector<DUContext*> children = context->childContexts();
318

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

321
322
323
    for (DUContext* child : children) {
        highlightDUChain(child,  colorsForDeclarations, declarationsForColors);
    }
324
325
326
327
}

KTextEditor::Attribute::Ptr CodeHighlighting::attributeForDepth(int depth) const
{
328
329
330
331
332
333
334
335
336
337
    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];
338
339
}

340
KDevelop::Declaration* CodeHighlightingInstance::localClassFromCodeContext(KDevelop::DUContext* context) const
341
{
342
343
    if (!context)
        return nullptr;
344

345
346
347
    const auto classIt = m_contextClasses.constFind(context);
    if (classIt != m_contextClasses.constEnd())
        return *classIt;
348

349
    DUContext* startContext = context;
350

351
352
353
354
355
356
357
    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();
358
    }
359

360
361
    ///Step 1: Find the function-declaration for the function we are in
    Declaration* functionDeclaration = nullptr;
362

363
    if (auto* def = dynamic_cast<FunctionDefinition*>(context->owner())) {
364
365
366
        const auto classIt = m_contextClasses.constFind(context);
        if (classIt != m_contextClasses.constEnd())
            return *classIt;
367

368
369
        functionDeclaration = def->declaration(startContext->topContext());
    }
370

371
372
    if (!functionDeclaration && context->owner())
        functionDeclaration = context->owner();
373

374
375
376
377
378
    if (!functionDeclaration) {
        if (m_useClassCache)
            m_contextClasses[context] = nullptr;
        return nullptr;
    }
379

380
    Declaration* decl  = functionDeclaration->context()->owner();
381

382
383
    if (m_useClassCache)
        m_contextClasses[context] = decl;
384

385
    return decl;
386
387
}

388
CodeHighlightingInstance::Types CodeHighlightingInstance::typeForDeclaration(Declaration* dec, DUContext* context) const
389
{
390
391
392
393
394
395
396
    /**
     * 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.
     *
     * */
397
398
399
400
401

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

402
403
404
405
406
407
408
409
410
    if (!dec)
        return ErrorVariableType;

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

    if (dec->kind() == Declaration::Macro) {
        return MacroType;
411
    }
412
413
414
415
416
417
418
419
420
421
422

    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
        }
423
424
    }

425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
    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;
        }
    }
442

443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
    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;
        }
460
461
    }

462
    return type;
463
464
}

465
466
bool CodeHighlightingInstance::useRainbowColor(Declaration* dec) const
{
467
468
    return dec->context()->type() == DUContext::Function ||
           (dec->context()->type() == DUContext::Other && dec->context()->owner());
469
470
}

471
void CodeHighlightingInstance::highlightDeclaration(Declaration* declaration, const QColor& color)
David nolden's avatar
David nolden committed
472
{
473
474
475
476
    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
477
478
}

479
void CodeHighlightingInstance::highlightUse(DUContext* context, int index, const QColor& color)
480
{
481
482
    Types type = ErrorVariableType;
    Declaration* decl = context->topContext()->usedDeclarationForIndex(context->uses()[index].m_declarationIndex);
483

484
    type = typeForDeclaration(decl, context);
485

486
487
488
489
490
491
492
    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);
    }
493
494
}

David nolden's avatar
David nolden committed
495
void CodeHighlightingInstance::highlightUses(DUContext* context)
496
{
497
498
    for (int a = 0; a < context->usesCount(); ++a)
        highlightUse(context, a, QColor(QColor::Invalid));
David nolden's avatar
David nolden committed
499
}
500

501
void CodeHighlighting::clearHighlightingForDocument(const IndexedString& document)
David nolden's avatar
David nolden committed
502
{
503
504
505
    VERIFY_FOREGROUND_LOCKED
    QMutexLocker lock(&m_dataMutex);
    DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(document);
506
507
    auto highlightingIt = m_highlights.find(tracker);
    if (highlightingIt != m_highlights.end()) {
508
        disconnect(tracker, &DocumentChangeTracker::destroyed, this, &CodeHighlighting::trackerDestroyed);
509
510
511
512
        auto& highlighting = *highlightingIt;
        qDeleteAll(highlighting->m_highlightedRanges);
        delete highlighting;
        m_highlights.erase(highlightingIt);
513
    }
David nolden's avatar
David nolden committed
514
515
}

516
void CodeHighlighting::applyHighlighting(void* _highlighting)
David nolden's avatar
David nolden committed
517
{
518
    auto* highlighting =
519
520
521
522
523
524
525
526
527
528
529
        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;
530
531
    }

532
533
534
535
536
537
    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
538

539
    QVector<MovingRange*> oldHighlightedRanges;
David nolden's avatar
David nolden committed
540

541
542
543
544
545
    const auto highlightingIt = m_highlights.find(tracker);
    if (highlightingIt != m_highlights.end()) {
        oldHighlightedRanges = (*highlightingIt)->m_highlightedRanges;
        delete *highlightingIt;
        *highlightingIt = highlighting;
546
547
548
549
550
551
552
553
    } 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);
554
        m_highlights.insert(tracker, highlighting);
David nolden's avatar
David nolden committed
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
589
590
591
592
593
594
595
596
597

    // 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
598
599
    }

600
601
    for (; movingIt != oldHighlightedRanges.end(); ++movingIt)
        delete *movingIt; // Delete unmatched moving ranges behind
602
603
}

David nolden's avatar
David nolden committed
604
void CodeHighlighting::trackerDestroyed(QObject* object)
605
{
606
607
608
    // Called when a document is destroyed
    VERIFY_FOREGROUND_LOCKED
    QMutexLocker lock(&m_dataMutex);
609
    auto* tracker = static_cast<DocumentChangeTracker*>(object);
610
611
612
    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);
613
614
}

615
616
void CodeHighlighting::aboutToInvalidateMovingInterfaceContent(Document* doc)
{
617
    clearHighlightingForDocument(IndexedString(doc->url()));
618
}
David nolden's avatar
David nolden committed
619

620
void CodeHighlighting::aboutToRemoveText(const KTextEditor::Range& range)
621
{
622
623
624
625
626
627
    if (range.onSingleLine()) // don't try to optimize this
        return;

    VERIFY_FOREGROUND_LOCKED
    QMutexLocker lock(&m_dataMutex);
    Q_ASSERT(dynamic_cast<KTextEditor::Document*>(sender()));
628
    auto* doc = static_cast<KTextEditor::Document*>(sender());
629
630
631

    DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()
                                     ->trackerForUrl(IndexedString(doc->url()));
632
633
634
    const auto highlightingIt = m_highlights.constFind(tracker);
    if (highlightingIt != m_highlights.constEnd()) {
        QVector<MovingRange*>& ranges = (*highlightingIt)->m_highlightedRanges;
635
636
637
638
639
640
641
642
643
        QVector<MovingRange*>::iterator it = ranges.begin();
        while (it != ranges.end()) {
            if (range.contains((*it)->toRange())) {
                delete (*it);
                it = ranges.erase(it);
            } else {
                ++it;
            }
        }
644
645
    }
}
646
}