~ubuntu-branches/ubuntu/trusty/kdevplatform/trusty-proposed

« back to all changes in this revision

Viewing changes to language/highlighting/codehighlighting.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Bhargav Mangipudi
  • Date: 2010-12-16 19:31:23 UTC
  • mfrom: (0.3.10 upstream)
  • Revision ID: james.westby@ubuntu.com-20101216193123-xe2keh5754zwsn1t
Tags: 1.1.80-0ubuntu1
* New upstream release
  - kdevplatform2-libs is now kdevplatform3-libs due to ABI changes
  - Update kdevplatform3-libs.install to include l10n files
  - Update kdevplatform-dev.install
* Removed localization packages

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
2
 * This file is part of KDevelop
3
3
 *
4
 
 * Copyright 2007-2008 David Nolden <david.nolden.kdevelop@art-master.de>
 
4
 * Copyright 2007-2010 David Nolden <david.nolden.kdevelop@art-master.de>
5
5
 * Copyright 2006 Hamish Rodda <rodda@kde.org>
6
6
 * Copyright 2009 Milian Wolff <mail@milianw.de>
7
7
 *
23
23
 
24
24
#include "codehighlighting.h"
25
25
 
26
 
#include <KTextEditor/SmartRange>
27
 
#include <KTextEditor/SmartInterface>
28
26
#include <KTextEditor/Document>
29
27
 
30
28
#include "../../interfaces/icore.h"
31
29
#include "../../interfaces/ilanguagecontroller.h"
32
30
#include "../../interfaces/icompletionsettings.h"
 
31
#include "../../interfaces/foregroundlock.h"
33
32
 
34
33
#include "../duchain/declaration.h"
35
34
#include "../duchain/types/functiontype.h"
42
41
 
43
42
#include "colorcache.h"
44
43
#include "configurablecolors.h"
 
44
#include <duchain/parsingenvironment.h>
 
45
#include <backgroundparser/backgroundparser.h>
 
46
#include <ktexteditor/movinginterface.h>
 
47
#include <duchain/dumpchain.h>
 
48
#include <backgroundparser/urlparselock.h>
45
49
 
46
50
using namespace KTextEditor;
47
51
 
48
 
#define LOCK_SMART(range) KTextEditor::SmartInterface* iface = dynamic_cast<KTextEditor::SmartInterface*>(range->document()); QMutexLocker lock(iface ? iface->smartMutex() : 0);
 
52
static const float highlightingZDepth = -500;
49
53
 
50
54
#define ifDebug(x)
51
55
 
52
56
namespace KDevelop {
53
57
 
 
58
///@todo Don't highlighting everything, only what is visible on-demand
54
59
 
55
60
CodeHighlighting::CodeHighlighting( QObject * parent )
56
 
  : QObject(parent), m_localColorization(true), m_globalColorization(true)
 
61
  : QObject(parent), m_localColorization(true), m_globalColorization(true), m_dataMutex(QMutex::Recursive)
57
62
{
 
63
  qRegisterMetaType<KDevelop::IndexedString>("KDevelop::IndexedString");
 
64
 
58
65
  adaptToColorChanges();
59
66
 
60
67
  connect(ColorCache::self(), SIGNAL(colorsGotChanged()),
63
70
 
64
71
CodeHighlighting::~CodeHighlighting( )
65
72
{
 
73
  qDeleteAll(m_highlights.values());
66
74
}
67
75
 
68
76
void CodeHighlighting::adaptToColorChanges()
82
90
KTextEditor::Attribute::Ptr CodeHighlighting::attributeForType( Types type, Contexts context, const QColor &color ) const
83
91
{
84
92
  QMutexLocker lock(&m_dataMutex);
85
 
  ///@todo Clear cache when the highlighting has changed
86
93
  KTextEditor::Attribute::Ptr a;
87
94
  switch (context) {
88
95
    case DefinitionContext:
127
134
  return a;
128
135
}
129
136
 
130
 
 
131
 
bool CodeHighlighting::isCodeHighlight(Attribute::Ptr attr) const
132
 
{
133
 
  ///@todo Just create separate smart-ranges in the context-browser, that will solve this mess
134
 
  ///@todo Do this properly, by statically building a set of attributes, and testing whether the given attribute is in that set
135
 
  ///Right now we just try to keep the highlighting of the context-browser alive to prevent flashing
136
 
  if(!attr || attr->underlineStyle() != KTextEditor::Attribute::NoUnderline)
137
 
    return true;
138
 
  return !attr->hasProperty(QTextFormat::BackgroundBrush);
139
 
}
140
 
 
141
 
void CodeHighlightingInstance::outputRange( KTextEditor::SmartRange * range ) const
142
 
{
143
 
  ifDebug(kDebug() << range << QString(range->depth(), ' ') << *range << "attr" << range->attribute();)
144
 
  Q_ASSERT(range->start() <= range->end());
145
 
  foreach (SmartRange* child, range->childRanges())
146
 
    outputRange(child);
147
 
}
148
 
 
149
137
ColorMap emptyColorMap() {
150
138
 ColorMap ret(ColorCache::self()->validColorCount()+1, 0);
151
139
 return ret;
156
144
  return new CodeHighlightingInstance(this);
157
145
}
158
146
 
159
 
void CodeHighlighting::highlightDUChain(TopDUContext* context) const
160
 
{
161
 
  kDebug() << "highlighting du chain" << context->url().toUrl();
162
 
 
163
 
  DUChainReadLocker lock(DUChain::lock());
 
147
bool CodeHighlighting::hasHighlighting(IndexedString url) const
 
148
{
 
149
  DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(url);
 
150
  if(tracker)
 
151
  {
 
152
    QMutexLocker lock(&m_dataMutex);
 
153
    return m_highlights.contains(tracker) && !m_highlights[tracker]->m_highlightedRanges.isEmpty();
 
154
  }
 
155
  return false;
 
156
}
 
157
 
 
158
void CodeHighlighting::highlightDUChain(ReferencedTopDUContext context)
 
159
{
 
160
  IndexedString url;
 
161
 
 
162
  {
 
163
    DUChainReadLocker lock;
 
164
    url = context->url();
 
165
  }
 
166
 
 
167
  // This prevents the background-parser from updating the top-context while we're working with it
 
168
  UrlParseLock urlLock(context->url());
 
169
 
 
170
  DUChainReadLocker lock;
 
171
 
 
172
  qint64 revision = context->parsingEnvironmentFile()->modificationRevision().revision;
 
173
 
 
174
  kDebug() << "highlighting du chain" << url.toUrl();
164
175
 
165
176
  if ( !m_localColorization && !m_globalColorization ) {
166
177
    kDebug() << "highlighting disabled";
167
 
    deleteHighlighting(context);
 
178
    QMetaObject::invokeMethod(this, "clearHighlightingForDocument", Qt::QueuedConnection, Q_ARG(KDevelop::IndexedString, url));
168
179
    return;
169
180
  }
170
181
 
171
182
  CodeHighlightingInstance* instance = createInstance();
172
183
 
173
 
  instance->highlightDUChain(context);
 
184
  lock.unlock();
 
185
 
 
186
  instance->highlightDUChain(context.data());
 
187
 
 
188
  DocumentHighlighting* highlighting = new DocumentHighlighting;
 
189
  highlighting->m_document = url;
 
190
  highlighting->m_waitingRevision = revision;
 
191
  highlighting->m_waiting = instance->m_highlight;
 
192
  qSort(highlighting->m_waiting.begin(), highlighting->m_waiting.end());
 
193
 
 
194
  QMetaObject::invokeMethod(this, "applyHighlighting", Qt::QueuedConnection, Q_ARG(void*, highlighting));
174
195
 
175
196
  delete instance;
176
197
}
177
198
 
178
 
void CodeHighlighting::deleteHighlighting(KDevelop::DUContext* context) const {
179
 
  if (!context->smartRange())
180
 
    return;
181
 
 
182
 
  {
183
 
    LOCK_SMART(context->smartRange());
184
 
 
185
 
    foreach (Declaration* dec, context->localDeclarations())
186
 
      if(dec->smartRange() && isCodeHighlight(dec->smartRange()->attribute()))
187
 
        dec->smartRange()->setAttribute(KTextEditor::Attribute::Ptr());
188
 
 
189
 
    for(int a = 0; a < context->usesCount(); ++a)
190
 
      if(context->useSmartRange(a) && isCodeHighlight(context->useSmartRange(a)->attribute()))
191
 
        context->useSmartRange(a)->setAttribute(KTextEditor::Attribute::Ptr());
192
 
  }
193
 
 
194
 
  foreach (DUContext* child, context->childContexts())
195
 
    deleteHighlighting(child);
196
 
}
197
 
 
198
 
void CodeHighlightingInstance::highlightDUChain(DUContext* context) const
 
199
void CodeHighlightingInstance::highlightDUChain(TopDUContext* context)
199
200
{
200
201
  m_contextClasses.clear();
201
202
  m_useClassCache = true;
202
203
 
203
204
  //Highlight
204
 
  highlightDUChainSimple(static_cast<DUContext*>(context));
 
205
  highlightDUChain(context, QHash<Declaration*, uint>(), emptyColorMap());
205
206
 
206
207
  m_functionColorsForDeclarations.clear();
207
208
  m_functionDeclarationsForColors.clear();
210
211
  m_contextClasses.clear();
211
212
}
212
213
 
213
 
void CodeHighlightingInstance::highlightDUChainSimple(DUContext* context) const
214
 
{
215
 
  if (!context->smartRange()) {
216
 
    kDebug() << "not a smart range! highlighting aborted";
217
 
    return;
218
 
  }
219
 
 
220
 
  ///TODO: 4.1 make this overloadable, e.g. in PHP we also want local colorization in global context
221
 
  bool isInFunction = context->type() == DUContext::Function || (context->type() == DUContext::Other && context->owner());
222
 
 
223
 
  if( isInFunction && m_highlighting->m_localColorization ) {
224
 
    highlightDUChain(context, QHash<Declaration*, uint>(), emptyColorMap());
225
 
    return;
226
 
  }
227
 
 
228
 
 
229
 
  foreach (Declaration* dec, context->localDeclarations()) {
230
 
    highlightDeclaration(dec, QColor(QColor::Invalid));
231
 
  }
232
 
 
233
 
  highlightUses(context);
234
 
 
235
 
  foreach (DUContext* child, context->childContexts()) {
236
 
    highlightDUChainSimple(child);
237
 
  }
238
 
}
239
 
 
240
 
void CodeHighlightingInstance::highlightDUChain(DUContext* context, QHash<Declaration*, uint> colorsForDeclarations, ColorMap declarationsForColors) const
241
 
{
242
 
  if (!context->smartRange())
243
 
    return;
 
214
void CodeHighlightingInstance::highlightDUChain(DUContext* context, QHash<Declaration*, uint> colorsForDeclarations, ColorMap declarationsForColors)
 
215
{
 
216
  DUChainReadLocker lock;
244
217
 
245
218
  TopDUContext* top = context->topContext();
246
219
 
258
231
  QList<Declaration*> takeFreeColors;
259
232
 
260
233
  foreach (Declaration* dec, context->localDeclarations()) {
 
234
    if (!useRainbowColor(dec)) {
 
235
      highlightDeclaration(dec, QColor(QColor::Invalid));
 
236
      continue;
 
237
    }
261
238
    //Initially pick a color using the hash, so the chances are good that the same identifier gets the same color always.
262
239
    uint colorNum = dec->identifier().hash() % ColorCache::self()->validColorCount();
263
240
 
300
277
    highlightUse(context, a, color);
301
278
  }
302
279
 
303
 
  foreach (DUContext* child, context->childContexts()) {
304
 
    highlightDUChain(child,  colorsForDeclarations, declarationsForColors );
305
 
  }
306
280
  if(context->type() == DUContext::Other || context->type() == DUContext::Function) {
307
281
    m_functionColorsForDeclarations[IndexedDUContext(context)] = colorsForDeclarations;
308
282
    m_functionDeclarationsForColors[IndexedDUContext(context)] = declarationsForColors;
309
283
  }
 
284
 
 
285
  QVector< DUContext* > children = context->childContexts();
 
286
 
 
287
  lock.unlock(); // Periodically release the lock, so that the UI won't be blocked too much
 
288
 
 
289
  foreach (DUContext* child, children)
 
290
    highlightDUChain(child,  colorsForDeclarations, declarationsForColors );
310
291
}
311
292
 
312
293
KTextEditor::Attribute::Ptr CodeHighlighting::attributeForDepth(int depth) const
436
417
  return type;
437
418
}
438
419
 
439
 
void CodeHighlightingInstance::highlightDeclaration(Declaration * declaration, const QColor &color) const
440
 
{
441
 
  if (SmartRange* range = declaration->smartRange()) {
442
 
    LOCK_SMART(range);
443
 
 
444
 
    if(!m_highlighting->isCodeHighlight(range->attribute()))
445
 
      return;
446
 
 
447
 
    range->setAttribute(m_highlighting->attributeForType(typeForDeclaration(declaration, 0), DeclarationContext, color));
448
 
  }
449
 
}
450
 
 
451
 
void CodeHighlightingInstance::highlightUse(DUContext* context, int index, const QColor &color) const
452
 
{
453
 
  if (SmartRange* range = context->useSmartRange(index)) {
454
 
 
455
 
    Types type = ErrorVariableType;
456
 
    Declaration* decl = context->topContext()->usedDeclarationForIndex(context->uses()[index].m_declarationIndex);
457
 
 
458
 
    type = typeForDeclaration(decl, context);
459
 
 
460
 
    LOCK_SMART(range);
461
 
 
462
 
    if(!m_highlighting->isCodeHighlight(range->attribute())) {
463
 
      return;
464
 
    }
465
 
 
466
 
    if(type != ErrorVariableType || ICore::self()->languageController()->completionSettings()->highlightSemanticProblems())
467
 
      range->setAttribute(m_highlighting->attributeForType(type, ReferenceContext, color));
 
420
bool CodeHighlightingInstance::useRainbowColor(Declaration* dec) const
 
421
{
 
422
  return dec->context()->type() == DUContext::Function || (dec->context()->type() == DUContext::Other && dec->context()->owner());
 
423
}
 
424
 
 
425
void CodeHighlightingInstance::highlightDeclaration(Declaration * declaration, const QColor &color)
 
426
{
 
427
  HighlightedRange h;
 
428
  h.range = declaration->range();
 
429
  h.attribute = m_highlighting->attributeForType(typeForDeclaration(declaration, 0), DeclarationContext, color);
 
430
  m_highlight.push_back(h);
 
431
}
 
432
 
 
433
void CodeHighlightingInstance::highlightUse(DUContext* context, int index, const QColor &color)
 
434
{
 
435
  Types type = ErrorVariableType;
 
436
  Declaration* decl = context->topContext()->usedDeclarationForIndex(context->uses()[index].m_declarationIndex);
 
437
 
 
438
  type = typeForDeclaration(decl, context);
 
439
 
 
440
  if(type != ErrorVariableType || ICore::self()->languageController()->completionSettings()->highlightSemanticProblems())
 
441
  {
 
442
    HighlightedRange h;
 
443
    h.range = context->uses()[index].m_range;
 
444
    h.attribute = m_highlighting->attributeForType(type, ReferenceContext, color);
 
445
    m_highlight.push_back(h);
 
446
  }
 
447
}
 
448
 
 
449
void CodeHighlightingInstance::highlightUses(DUContext* context)
 
450
{
 
451
  for(int a = 0; a < context->usesCount(); ++a)
 
452
    highlightUse(context, a, QColor(QColor::Invalid));
 
453
}
 
454
 
 
455
 
 
456
void CodeHighlighting::clearHighlightingForDocument(IndexedString document)
 
457
{
 
458
  VERIFY_FOREGROUND_LOCKED
 
459
  QMutexLocker lock(&m_dataMutex);
 
460
  DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(document);
 
461
  if(m_highlights.contains(tracker))
 
462
  {
 
463
    disconnect(tracker, SIGNAL(destroyed(QObject*)), this, SLOT(trackerDestroyed(QObject*)));
 
464
    qDeleteAll(m_highlights[tracker]->m_highlightedRanges);
 
465
    delete m_highlights[tracker];
 
466
    m_highlights.remove(tracker);
 
467
  }
 
468
}
 
469
 
 
470
void CodeHighlighting::applyHighlighting(void* _highlighting)
 
471
{
 
472
  CodeHighlighting::DocumentHighlighting* highlighting = static_cast<CodeHighlighting::DocumentHighlighting*>(_highlighting);
 
473
 
 
474
  VERIFY_FOREGROUND_LOCKED
 
475
  QMutexLocker lock(&m_dataMutex);
 
476
  DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()->trackerForUrl(highlighting->m_document);
 
477
 
 
478
  if(!tracker)
 
479
  {
 
480
    kDebug() << "no document found for the planned highlighting of" << highlighting->m_document.str();
 
481
    delete highlighting;
 
482
    return;
 
483
  }
 
484
 
 
485
  QVector< MovingRange* > oldHighlightedRanges;
 
486
 
 
487
  if(m_highlights.contains(tracker))
 
488
  {
 
489
    oldHighlightedRanges = m_highlights[tracker]->m_highlightedRanges;
 
490
    delete m_highlights[tracker];
 
491
  }else{
 
492
    // we newly add this tracker, so add the connection
 
493
    connect(tracker, SIGNAL(destroyed(QObject*)), SLOT(trackerDestroyed(QObject*)));
 
494
    connect(tracker->document(), SIGNAL(aboutToInvalidateMovingInterfaceContent(KTextEditor::Document*)),
 
495
            this, SLOT(aboutToInvalidateMovingInterfaceContent(KTextEditor::Document*)));
 
496
    connect(tracker->document(), SIGNAL(aboutToRemoveText(KTextEditor::Range)),
 
497
            this, SLOT(aboutToRemoveText(KTextEditor::Range)));
 
498
  }
 
499
 
 
500
  m_highlights[tracker] = highlighting;
 
501
 
 
502
  // Now create MovingRanges (match old ones with the incoming ranges)
 
503
 
 
504
  KTextEditor::Range tempRange;
 
505
 
 
506
  QVector<MovingRange*>::iterator movingIt = oldHighlightedRanges.begin();
 
507
  QVector<HighlightedRange>::iterator rangeIt = highlighting->m_waiting.begin();
 
508
 
 
509
  while(rangeIt != highlighting->m_waiting.end())
 
510
  {
 
511
    // Translate the range into the current revision
 
512
    SimpleRange transformedRange = tracker->transformToCurrentRevision(rangeIt->range, highlighting->m_waitingRevision);
 
513
 
 
514
    while(movingIt != oldHighlightedRanges.end() &&
 
515
      ((*movingIt)->start().line() < transformedRange.start.line ||
 
516
      ((*movingIt)->start().line() == transformedRange.start.line && (*movingIt)->start().column() < transformedRange.start.column)))
 
517
    {
 
518
      delete *movingIt; // Skip ranges that are in front of the current matched range
 
519
      ++movingIt;
 
520
    }
 
521
 
 
522
    tempRange.start().setPosition(transformedRange.start.line, transformedRange.start.column);
 
523
    tempRange.end().setPosition(transformedRange.end.line, transformedRange.end.column);
 
524
 
 
525
    if(movingIt == oldHighlightedRanges.end() ||
 
526
      transformedRange.start.line != (*movingIt)->start().line() ||
 
527
      transformedRange.start.column != (*movingIt)->start().column() ||
 
528
      transformedRange.end.line != (*movingIt)->end().line() ||
 
529
      transformedRange.end.column != (*movingIt)->end().column())
 
530
    {
 
531
      Q_ASSERT(rangeIt->attribute);
 
532
      // The moving range is behind or unequal, create a new range
 
533
      highlighting->m_highlightedRanges.push_back(tracker->documentMovingInterface()->newMovingRange(tempRange));
 
534
      highlighting->m_highlightedRanges.back()->setAttribute(rangeIt->attribute);
 
535
      highlighting->m_highlightedRanges.back()->setZDepth(highlightingZDepth);
 
536
    }
468
537
    else
469
 
      range->setAttribute(KTextEditor::Attribute::Ptr());
 
538
    {
 
539
      // Update the existing moving range
 
540
      (*movingIt)->setAttribute(rangeIt->attribute);
 
541
      (*movingIt)->setRange(tempRange);
 
542
      highlighting->m_highlightedRanges.push_back(*movingIt);
 
543
      ++movingIt;
 
544
    }
 
545
    ++rangeIt;
470
546
  }
471
 
}
472
 
 
473
 
void CodeHighlightingInstance::highlightUses(DUContext* context) const
474
 
{
475
 
  for(int a = 0; a < context->usesCount(); ++a) {
476
 
    highlightUse(context, a, QColor(QColor::Invalid));
 
547
 
 
548
  for(; movingIt != oldHighlightedRanges.end(); ++movingIt)
 
549
    delete *movingIt; // Delete unmatched moving ranges behind
 
550
}
 
551
 
 
552
void CodeHighlighting::trackerDestroyed(QObject* object)
 
553
{
 
554
  // Called when a document is destroyed
 
555
  VERIFY_FOREGROUND_LOCKED
 
556
  QMutexLocker lock(&m_dataMutex);
 
557
  DocumentChangeTracker* tracker = static_cast<DocumentChangeTracker*>(object);
 
558
  Q_ASSERT(m_highlights.contains(tracker));
 
559
  delete m_highlights[tracker]; // No need to care about the individual ranges, as the document is being destroyed
 
560
  m_highlights.remove(tracker);
 
561
}
 
562
 
 
563
void CodeHighlighting::aboutToInvalidateMovingInterfaceContent(Document* doc)
 
564
{
 
565
  clearHighlightingForDocument(IndexedString(doc->url()));
 
566
}
 
567
 
 
568
void CodeHighlighting::aboutToRemoveText( const KTextEditor::Range& range )
 
569
{
 
570
  if (range.onSingleLine()) // don't try to optimize this
 
571
    return;
 
572
 
 
573
  VERIFY_FOREGROUND_LOCKED
 
574
  QMutexLocker lock(&m_dataMutex);
 
575
  Q_ASSERT(dynamic_cast<KTextEditor::Document*>(sender()));
 
576
  KTextEditor::Document* doc = static_cast<KTextEditor::Document*>(sender());
 
577
 
 
578
  DocumentChangeTracker* tracker = ICore::self()->languageController()->backgroundParser()
 
579
                                      ->trackerForUrl(IndexedString(doc->url()));
 
580
  if(m_highlights.contains(tracker))
 
581
  {
 
582
    QVector<MovingRange*>& ranges = m_highlights.value(tracker)->m_highlightedRanges;
 
583
    QVector<MovingRange*>::iterator it = ranges.begin();
 
584
    while(it != ranges.end()) {
 
585
      if (range.contains((*it)->toRange())) {
 
586
        delete (*it);
 
587
        it = ranges.erase(it);
 
588
      } else {
 
589
        ++it;
 
590
      }
 
591
    }
477
592
  }
478
593
}
479
594