codehighlighting.cpp 23.6 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
    TopDUContext* top = context->topContext();

    //Merge the colors from the function arguments
233
234
    const auto importedParentContexts = context->importedParentContexts();
    for (const DUContext::Import& imported : importedParentContexts) {
235
236
237
238
        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.
239
240
241
242
243
244
        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;
245
246
    }

247
248
    QList<Declaration*> takeFreeColors;

249
250
    const auto localDeclarations = context->localDeclarations();
    for (Declaration* dec : localDeclarations) {
251
252
253
254
255
256
257
258
259
260
261
262
263
264
        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;
265

266
267
        highlightDeclaration(dec, ColorCache::self()->generatedColor(colorNum));
    }
268

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

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

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

319
    const QVector<DUContext*> children = context->childContexts();
320

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

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

KTextEditor::Attribute::Ptr CodeHighlighting::attributeForDepth(int depth) const
{
330
331
    while (depth >= m_depthAttributes.count()) {
        KTextEditor::Attribute::Ptr a(new KTextEditor::Attribute());
332
        a->setBackground(QColor(Qt::white).darker(100 + (m_depthAttributes.count() * 25)));
333
334
335
336
337
338
339
        a->setBackgroundFillWhitespace(true);
        if (depth % 2)
            a->setOutline(Qt::red);
        m_depthAttributes.append(a);
    }

    return m_depthAttributes[depth];
340
341
}

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

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

351
    DUContext* startContext = context;
352

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

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

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

370
371
        functionDeclaration = def->declaration(startContext->topContext());
    }
372

373
374
    if (!functionDeclaration && context->owner())
        functionDeclaration = context->owner();
375

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

382
    Declaration* decl  = functionDeclaration->context()->owner();
383

384
385
    if (m_useClassCache)
        m_contextClasses[context] = decl;
386

387
    return decl;
388
389
}

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

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

404
405
406
407
408
409
410
411
412
    if (!dec)
        return ErrorVariableType;

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

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

    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
        }
425
426
    }

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

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

464
    return type;
465
466
}

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

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

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

486
    type = typeForDeclaration(decl, context);
487

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

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

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

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

534
535
536
537
538
539
    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
540

541
    QVector<MovingRange*> oldHighlightedRanges;
David nolden's avatar
David nolden committed
542

543
544
545
546
547
    const auto highlightingIt = m_highlights.find(tracker);
    if (highlightingIt != m_highlights.end()) {
        oldHighlightedRanges = (*highlightingIt)->m_highlightedRanges;
        delete *highlightingIt;
        *highlightingIt = highlighting;
548
549
550
551
552
553
554
555
    } 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);
556
        m_highlights.insert(tracker, highlighting);
David nolden's avatar
David nolden committed
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
598
599

    // 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
600
601
    }

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

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

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

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

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

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