~ubuntu-branches/ubuntu/quantal/kdevplatform/quantal-proposed

« back to all changes in this revision

Viewing changes to language/backgroundparser/documentchangetracker.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 2008 Hamish Rodda <rodda@kde.org>
 
4
* Copyright 2010 David Nolden <david.nolden.kdevelop@art-master.de>
5
5
*
6
6
* This program is free software; you can redistribute it and/or modify
7
7
* it under the terms of the GNU Library General Public License as
25
25
#include <QMutexLocker>
26
26
 
27
27
#include <kdebug.h>
28
 
 
29
28
#include <ktexteditor/document.h>
30
 
#include <ktexteditor/smartinterface.h>
 
29
#include <ktexteditor/movinginterface.h>
 
30
 
 
31
#include <interfaces/foregroundlock.h>
 
32
#include <editor/modificationrevisionset.h>
 
33
#include <duchain/indexedstring.h>
 
34
#include <interfaces/icore.h>
 
35
#include <interfaces/ilanguagecontroller.h>
 
36
#include "backgroundparser.h"
 
37
#include <QApplication>
 
38
 
 
39
// Can be used to disable the 'clever' updating logic that ignores whitespace-only changes and such.
 
40
// #define ALWAYS_UPDATE
31
41
 
32
42
using namespace KTextEditor;
33
43
 
 
44
/**
 
45
 * @todo Track the exact changes to the document, and then:
 
46
 * Dont reparse if:
 
47
 *  - Comment added/changed
 
48
 *  - Newlines added/changed (ready)
 
49
 * Complete the document for validation:
 
50
 *  - Incomplete for-loops
 
51
 *  - ...
 
52
 * Only reparse after a statement was completed (either with statement-completion or manually), or after the cursor was switched away
 
53
 * Incremental parsing:
 
54
 *  - All changes within a local function (or function parameter context): Update only the context (and all its importers)
 
55
 * 
 
56
 * @todo: Prevent recursive updates after insignificant changes 
 
57
 *             (whitespace changes, or changes that don't affect publically visible stuff, eg. local incremental changes)
 
58
 *             -> Maybe alter the file-modification caches directly
 
59
 * */
 
60
 
 
61
namespace {
 
62
    QRegExp whiteSpaceRegExp("\\s");
 
63
};
 
64
 
34
65
namespace KDevelop
35
66
{
36
67
 
37
 
class DocumentChangeTrackerPrivate
38
 
{
39
 
public:
40
 
    DocumentChangeTrackerPrivate()
41
 
        : contentsRetrieved(false)
42
 
        , contentsRetrievedMutex(new QMutex)
43
 
    {
44
 
    }
45
 
 
46
 
    ~DocumentChangeTrackerPrivate()
47
 
    {
48
 
        delete contentsRetrievedMutex;
49
 
    }
50
 
 
51
 
    bool contentsRetrieved;
52
 
 
53
 
    QMutex* contentsRetrievedMutex;
54
 
    QList<SmartRange*> changedRanges;
55
 
};
56
 
 
57
 
DocumentChangeTracker::DocumentChangeTracker()
58
 
    : d(new DocumentChangeTrackerPrivate)
59
 
{
 
68
DocumentChangeTracker::DocumentChangeTracker( KTextEditor::Document* document )
 
69
    : m_needUpdate(false), m_changedRange(0), m_document(document), m_moving(0)
 
70
{
 
71
    m_url = IndexedString(document->url());
 
72
    Q_ASSERT(document->url().isValid());
 
73
    Q_ASSERT(document);
 
74
    connect(document, SIGNAL(textInserted(KTextEditor::Document*,KTextEditor::Range)), SLOT(textInserted(KTextEditor::Document*,KTextEditor::Range)));
 
75
    connect(document, SIGNAL(textRemoved(KTextEditor::Document*,KTextEditor::Range, QString)), SLOT(textRemoved(KTextEditor::Document*,KTextEditor::Range, QString)));
 
76
    connect(document, SIGNAL(textChanged(KTextEditor::Document*,KTextEditor::Range,QString, KTextEditor::Range)), SLOT(textChanged(KTextEditor::Document*,KTextEditor::Range,QString, KTextEditor::Range)));
 
77
    connect(document, SIGNAL(destroyed(QObject*)), SLOT(documentDestroyed(QObject*)));
 
78
    
 
79
    m_moving = dynamic_cast<KTextEditor::MovingInterface*>(document);
 
80
    Q_ASSERT(m_moving);
 
81
    m_changedRange = m_moving->newMovingRange(KTextEditor::Range(), KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight);
 
82
    
 
83
    connect(m_document, SIGNAL(aboutToInvalidateMovingInterfaceContent (KTextEditor::Document*)), this, SLOT(aboutToInvalidateMovingInterfaceContent (KTextEditor::Document*)));
 
84
    
 
85
    ModificationRevision::setEditorRevisionForFile(m_url, m_moving->revision());
 
86
    
 
87
    reset();
 
88
}
 
89
 
 
90
QList< QPair< SimpleRange, QString > > DocumentChangeTracker::completions() const
 
91
{
 
92
    VERIFY_FOREGROUND_LOCKED
 
93
    
 
94
    QList< QPair< SimpleRange , QString > > ret;
 
95
    return ret;
 
96
}
 
97
 
 
98
Range DocumentChangeTracker::changedRange() const
 
99
{
 
100
    VERIFY_FOREGROUND_LOCKED
 
101
    
 
102
    return m_changedRange->toRange();
 
103
}
 
104
 
 
105
void DocumentChangeTracker::reset()
 
106
{
 
107
    VERIFY_FOREGROUND_LOCKED
 
108
    
 
109
    // We don't reset the insertion here, as it may continue
 
110
    m_needUpdate = false;
 
111
    m_changedRange->setRange(KTextEditor::Range::invalid());
 
112
    
 
113
    m_revisionAtLastReset = acquireRevision(m_moving->revision());
 
114
    Q_ASSERT(m_revisionAtLastReset);
 
115
    m_textAtLastReset = m_document->text();
 
116
}
 
117
 
 
118
RevisionReference DocumentChangeTracker::currentRevision()
 
119
{
 
120
    VERIFY_FOREGROUND_LOCKED
 
121
    
 
122
    return acquireRevision(m_moving->revision());
 
123
}
 
124
 
 
125
RevisionReference DocumentChangeTracker::revisionAtLastReset() const
 
126
{
 
127
    VERIFY_FOREGROUND_LOCKED
 
128
    
 
129
    return m_revisionAtLastReset;
 
130
}
 
131
 
 
132
QString DocumentChangeTracker::textAtLastReset() const
 
133
{
 
134
    VERIFY_FOREGROUND_LOCKED
 
135
    
 
136
    return m_textAtLastReset;
 
137
}
 
138
 
 
139
bool DocumentChangeTracker::needUpdate() const
 
140
{
 
141
    VERIFY_FOREGROUND_LOCKED
 
142
    
 
143
    return m_needUpdate;
 
144
}
 
145
 
 
146
bool DocumentChangeTracker::checkMergeTokens(const KTextEditor::Range& range, QString oldText, QString newText)
 
147
{
 
148
    ///@todo Improve this so that it notices when we wrapped in/out of a line-comment
 
149
    ///@todo Improve this so that it really checks whether some merge-able tokens have been moved together
 
150
    if(m_document->documentRange().contains(range))
 
151
    {
 
152
        if(range.start().column() == 0 || m_document->text(KTextEditor::Range(range.start().line(), range.start().column()-1, range.start().line(), range.start().column()))[0].isSpace())
 
153
            return true;
 
154
        if(range.end().column() >= m_document->lineLength(range.end().line()) || m_document->text(KTextEditor::Range(range.end().line(), range.end().column(), range.end().line(), range.end().column()+1))[0].isSpace())
 
155
            return true;
 
156
    }
 
157
    return false;
 
158
}
 
159
 
 
160
void DocumentChangeTracker::textChanged( Document* document, Range /*oldRange*/, QString oldText, Range newRange )
 
161
{
 
162
    m_currentCleanedInsertion.clear();
 
163
 
 
164
    QString newText = document->text(newRange);
 
165
 
 
166
    QString oldTextWithoutWhitespace = oldText;
 
167
    oldTextWithoutWhitespace.remove(whiteSpaceRegExp);
 
168
    
 
169
    QString newTextWithoutWhitespace = newText;
 
170
    newTextWithoutWhitespace.remove(whiteSpaceRegExp);
 
171
    
 
172
    if(oldTextWithoutWhitespace.isEmpty() && newTextWithoutWhitespace.isEmpty() && checkMergeTokens(newRange, oldText, newText))
 
173
    {
 
174
        // Only whitespace was changed, no update is required
 
175
    }else{
 
176
        m_needUpdate = true;
 
177
    }
 
178
    
 
179
    #ifdef ALWAYS_UPDATE
 
180
    m_needUpdate = true;
 
181
    #endif
 
182
    
 
183
    m_currentCleanedInsertion.clear();
 
184
    m_lastInsertionPosition = KTextEditor::Cursor::invalid();
 
185
    
 
186
    updateChangedRange(newRange);
 
187
}
 
188
 
 
189
void DocumentChangeTracker::updateChangedRange( Range changed )
 
190
{
 
191
    if(m_changedRange->toRange() == KTextEditor::Range::invalid())
 
192
        m_changedRange->setRange(changed);
 
193
    else
 
194
        m_changedRange->setRange(changed.encompass(m_changedRange->toRange()));
 
195
    
 
196
//     Q_ASSERT(m_moving->revision() != m_revisionAtLastReset->revision()); // May happen after reload
 
197
 
 
198
    // When reloading, textRemoved is called with an invalid m_document->url(). For that reason, we use m_url instead.
 
199
        
 
200
    ModificationRevision::setEditorRevisionForFile(m_url, m_moving->revision());
 
201
    
 
202
    if(needUpdate())
 
203
        ICore::self()->languageController()->backgroundParser()->addDocument(m_url.toUrl(), TopDUContext::AllDeclarationsContextsAndUses);
 
204
}
 
205
 
 
206
void DocumentChangeTracker::textInserted( Document* document, Range range )
 
207
{
 
208
    QString text = document->text(range);
 
209
    QString textWithoutWhitespace = text;
 
210
    textWithoutWhitespace.remove(whiteSpaceRegExp);
 
211
    
 
212
    if(textWithoutWhitespace.isEmpty() && checkMergeTokens(range, "", text))
 
213
    {
 
214
        // Only whitespace was changed, no update is required
 
215
    }else{
 
216
        m_needUpdate = true; // If we've inserted something else than whitespace, an update is required
 
217
    }
 
218
    
 
219
    #ifdef ALWAYS_UPDATE
 
220
    m_needUpdate = true;
 
221
    #endif
 
222
    
 
223
    if(m_lastInsertionPosition == KTextEditor::Cursor::invalid() || m_lastInsertionPosition == range.start())
 
224
    {
 
225
        m_currentCleanedInsertion.append(text);
 
226
        m_lastInsertionPosition = range.end();
 
227
    }
 
228
 
 
229
    updateChangedRange(range);
 
230
}
 
231
 
 
232
void DocumentChangeTracker::textRemoved( Document* document, Range oldRange, QString oldText )
 
233
{
 
234
    QString text = oldText;
 
235
    
 
236
    QString textWithoutWhitespace = text;
 
237
    textWithoutWhitespace.remove(whiteSpaceRegExp);
 
238
    
 
239
    if(textWithoutWhitespace.isEmpty() && checkMergeTokens(Range(oldRange.start(), oldRange.start()), oldText, ""))
 
240
    {
 
241
        // Only whitespace was changed, no update is required
 
242
    }else{
 
243
        m_needUpdate = true; // If we've inserted something else than whitespace, an update is required
 
244
    }
 
245
    
 
246
    #ifdef ALWAYS_UPDATE
 
247
    m_needUpdate = true;
 
248
    #endif
 
249
    
 
250
    m_currentCleanedInsertion.clear();
 
251
    m_lastInsertionPosition = KTextEditor::Cursor::invalid();
 
252
    
 
253
    updateChangedRange(oldRange);
 
254
}
 
255
 
 
256
void DocumentChangeTracker::documentDestroyed( QObject* )
 
257
{
 
258
    m_document = 0;
 
259
    m_moving = 0;
 
260
    m_changedRange = 0;
60
261
}
61
262
 
62
263
DocumentChangeTracker::~DocumentChangeTracker()
63
264
{
64
 
    foreach (SmartRange* range, d->changedRanges)
65
 
        range->removeWatcher(this);
66
 
 
67
 
    delete d;
68
 
}
69
 
 
70
 
QList<SmartRange*> DocumentChangeTracker::changedRanges() const
71
 
{
72
 
    return d->changedRanges;
73
 
}
74
 
 
75
 
bool DocumentChangeTracker::addChangedRange(SmartRange* changed)
76
 
{
77
 
    QMutexLocker l(d->contentsRetrievedMutex);
78
 
    if (d->contentsRetrieved)
 
265
    Q_ASSERT(m_document);
 
266
    ModificationRevision::clearEditorRevisionForFile(KDevelop::IndexedString(m_document->url()));
 
267
}
 
268
 
 
269
Document* DocumentChangeTracker::document() const
 
270
{
 
271
    return m_document;
 
272
}
 
273
 
 
274
MovingInterface* DocumentChangeTracker::documentMovingInterface() const
 
275
{
 
276
    return m_moving;
 
277
}
 
278
 
 
279
void DocumentChangeTracker::aboutToInvalidateMovingInterfaceContent ( Document* )
 
280
{
 
281
    // Release all revisions! They must not be used any more.
 
282
    kDebug() << "clearing all revisions";
 
283
    m_revisionLocks.clear();
 
284
    m_revisionAtLastReset = RevisionReference();
 
285
    ModificationRevision::setEditorRevisionForFile(m_url, 0);
 
286
}
 
287
 
 
288
KDevelop::RangeInRevision DocumentChangeTracker::transformBetweenRevisions(KDevelop::RangeInRevision range, qint64 fromRevision, qint64 toRevision) const
 
289
{
 
290
    VERIFY_FOREGROUND_LOCKED
 
291
    
 
292
    if((fromRevision == -1 || holdingRevision(fromRevision)) && (toRevision == -1 || holdingRevision(toRevision)))
 
293
    {
 
294
        m_moving->transformCursor(range.start.line, range.start.column, KTextEditor::MovingCursor::MoveOnInsert, fromRevision, toRevision);
 
295
        m_moving->transformCursor(range.end.line, range.end.column, KTextEditor::MovingCursor::StayOnInsert, fromRevision, toRevision);
 
296
    }
 
297
    
 
298
    return range;
 
299
}
 
300
 
 
301
KDevelop::CursorInRevision DocumentChangeTracker::transformBetweenRevisions(KDevelop::CursorInRevision cursor, qint64 fromRevision, qint64 toRevision, KTextEditor::MovingCursor::InsertBehavior behavior) const
 
302
{
 
303
    VERIFY_FOREGROUND_LOCKED
 
304
    
 
305
    if((fromRevision == -1 || holdingRevision(fromRevision)) && (toRevision == -1 || holdingRevision(toRevision)))
 
306
    {
 
307
        m_moving->transformCursor(cursor.line, cursor.column, behavior, fromRevision, toRevision);
 
308
    }
 
309
    
 
310
    return cursor;
 
311
}
 
312
 
 
313
RangeInRevision DocumentChangeTracker::transformToRevision(SimpleRange range, qint64 toRevision) const
 
314
{
 
315
    return transformBetweenRevisions(RangeInRevision::castFromSimpleRange(range), -1, toRevision);
 
316
}
 
317
 
 
318
CursorInRevision DocumentChangeTracker::transformToRevision(SimpleCursor cursor, qint64 toRevision, MovingCursor::InsertBehavior behavior) const
 
319
{
 
320
    return transformBetweenRevisions(CursorInRevision::castFromSimpleCursor(cursor), -1, toRevision, behavior);
 
321
}
 
322
 
 
323
SimpleRange DocumentChangeTracker::transformToCurrentRevision(RangeInRevision range, qint64 fromRevision) const
 
324
{
 
325
    return transformBetweenRevisions(range, fromRevision, -1).castToSimpleRange();
 
326
}
 
327
 
 
328
SimpleCursor DocumentChangeTracker::transformToCurrentRevision(CursorInRevision cursor, qint64 fromRevision, MovingCursor::InsertBehavior behavior) const
 
329
{
 
330
    return transformBetweenRevisions(cursor, fromRevision, -1, behavior).castToSimpleCursor();
 
331
}
 
332
 
 
333
RevisionLockerAndClearerPrivate::RevisionLockerAndClearerPrivate(DocumentChangeTracker* tracker, qint64 revision) : m_tracker(tracker), m_revision(revision)
 
334
{
 
335
    VERIFY_FOREGROUND_LOCKED
 
336
    
 
337
    moveToThread(QApplication::instance()->thread());
 
338
    
 
339
    // Lock the revision
 
340
    m_tracker->lockRevision(revision);
 
341
}
 
342
 
 
343
RevisionLockerAndClearerPrivate::~RevisionLockerAndClearerPrivate() {
 
344
    if (m_tracker)
 
345
        m_tracker->unlockRevision(m_revision);
 
346
}
 
347
 
 
348
RevisionLockerAndClearer::~RevisionLockerAndClearer()
 
349
{
 
350
    m_p->deleteLater(); // Will be deleted in the foreground thread, as the object was re-owned to the foreground
 
351
}
 
352
 
 
353
RevisionReference DocumentChangeTracker::acquireRevision(qint64 revision)
 
354
{
 
355
    VERIFY_FOREGROUND_LOCKED
 
356
    
 
357
    if(!holdingRevision(revision) && revision != m_moving->revision())
 
358
        return RevisionReference();
 
359
    
 
360
    RevisionReference ret(new RevisionLockerAndClearer);
 
361
    ret->m_p = new RevisionLockerAndClearerPrivate(this, revision);
 
362
    return ret;
 
363
}
 
364
 
 
365
bool DocumentChangeTracker::holdingRevision(qint64 revision) const
 
366
{
 
367
    VERIFY_FOREGROUND_LOCKED
 
368
    
 
369
    return m_revisionLocks.contains(revision);
 
370
}
 
371
 
 
372
void DocumentChangeTracker::lockRevision(qint64 revision)
 
373
{
 
374
    VERIFY_FOREGROUND_LOCKED
 
375
    
 
376
    QMap< qint64, int >::iterator it = m_revisionLocks.find(revision);
 
377
    if(it != m_revisionLocks.end())
 
378
        ++(*it);
 
379
    else
 
380
    {
 
381
        m_revisionLocks.insert(revision, 1);
 
382
        m_moving->lockRevision(revision);
 
383
    }
 
384
}
 
385
 
 
386
void DocumentChangeTracker::unlockRevision(qint64 revision)
 
387
{
 
388
    VERIFY_FOREGROUND_LOCKED
 
389
    
 
390
    QMap< qint64, int >::iterator it = m_revisionLocks.find(revision);
 
391
    if(it == m_revisionLocks.end())
 
392
    {
 
393
        kDebug() << "cannot unlock revision" << revision << ", probably the revisions have been cleared";
 
394
        return;
 
395
    }
 
396
    --(*it);
 
397
    
 
398
    if(*it == 0)
 
399
    {
 
400
        m_moving->unlockRevision(revision);        
 
401
        m_revisionLocks.erase(it);
 
402
    }
 
403
}
 
404
 
 
405
qint64 RevisionLockerAndClearer::revision() const {
 
406
    return m_p->revision();
 
407
}
 
408
RangeInRevision RevisionLockerAndClearer::transformToRevision(const KDevelop::RangeInRevision& range, const KDevelop::RevisionLockerAndClearer::Ptr& to)
 
409
{
 
410
    VERIFY_FOREGROUND_LOCKED
 
411
    
 
412
    if(!m_p->m_tracker || !valid() || (to && !to->valid()))
 
413
        return range;
 
414
    
 
415
    qint64 fromRevision = revision();
 
416
    qint64 toRevision = -1;
 
417
    
 
418
    if(to)
 
419
        toRevision = to->revision();
 
420
    
 
421
    return m_p->m_tracker->transformBetweenRevisions(range, fromRevision, toRevision);
 
422
}
 
423
 
 
424
CursorInRevision RevisionLockerAndClearer::transformToRevision(const KDevelop::CursorInRevision& cursor, const KDevelop::RevisionLockerAndClearer::Ptr& to, MovingCursor::InsertBehavior behavior)
 
425
{
 
426
    VERIFY_FOREGROUND_LOCKED
 
427
    
 
428
    if(!m_p->m_tracker || !valid() || (to && !to->valid()))
 
429
        return cursor;
 
430
    
 
431
    qint64 fromRevision = revision();
 
432
    qint64 toRevision = -1;
 
433
    
 
434
    if(to)
 
435
        toRevision = to->revision();
 
436
    
 
437
    return m_p->m_tracker->transformBetweenRevisions(cursor, fromRevision, toRevision, behavior);
 
438
}
 
439
 
 
440
RangeInRevision RevisionLockerAndClearer::transformFromRevision(const KDevelop::RangeInRevision& range, const KDevelop::RevisionLockerAndClearer::Ptr& from)
 
441
{
 
442
    VERIFY_FOREGROUND_LOCKED
 
443
    
 
444
    if(!m_p->m_tracker || !valid())
 
445
        return range;
 
446
    
 
447
    qint64 toRevision = revision();
 
448
    qint64 fromRevision = -1;
 
449
    
 
450
    if(from)
 
451
        fromRevision = from->revision();
 
452
    
 
453
    return m_p->m_tracker->transformBetweenRevisions(range, fromRevision, toRevision);
 
454
}
 
455
 
 
456
CursorInRevision RevisionLockerAndClearer::transformFromRevision(const KDevelop::CursorInRevision& cursor, const KDevelop::RevisionLockerAndClearer::Ptr& from, MovingCursor::InsertBehavior behavior)
 
457
{
 
458
    VERIFY_FOREGROUND_LOCKED
 
459
    
 
460
    if(!m_p->m_tracker)
 
461
        return cursor;
 
462
    
 
463
    qint64 toRevision = revision();
 
464
    qint64 fromRevision = -1;
 
465
    
 
466
    if(from)
 
467
        fromRevision = from->revision();
 
468
    
 
469
    return m_p->m_tracker->transformBetweenRevisions(cursor, fromRevision, toRevision, behavior);
 
470
}
 
471
 
 
472
 
 
473
SimpleRange RevisionLockerAndClearer::transformToCurrentRevision(const KDevelop::RangeInRevision& range)
 
474
{
 
475
    return transformToRevision(range, KDevelop::RevisionLockerAndClearer::Ptr()).castToSimpleRange();
 
476
}
 
477
 
 
478
SimpleCursor RevisionLockerAndClearer::transformToCurrentRevision(const KDevelop::CursorInRevision& cursor, MovingCursor::InsertBehavior behavior)
 
479
{
 
480
    return transformToRevision(cursor, KDevelop::RevisionLockerAndClearer::Ptr(), behavior).castToSimpleCursor();
 
481
}
 
482
 
 
483
RangeInRevision RevisionLockerAndClearer::transformFromCurrentRevision(const KDevelop::SimpleRange& range)
 
484
{
 
485
    return transformFromRevision(RangeInRevision::castFromSimpleRange(range), RevisionReference());
 
486
}
 
487
 
 
488
CursorInRevision RevisionLockerAndClearer::transformFromCurrentRevision(const KDevelop::SimpleCursor& cursor, MovingCursor::InsertBehavior behavior)
 
489
{
 
490
    return transformFromRevision(CursorInRevision::castFromSimpleCursor(cursor), RevisionReference(), behavior);
 
491
}
 
492
 
 
493
bool RevisionLockerAndClearer::valid() const
 
494
{
 
495
    VERIFY_FOREGROUND_LOCKED
 
496
    
 
497
    if(!m_p->m_tracker)
79
498
        return false;
80
 
 
81
 
    addChangedRangeInternal(changed);
82
 
 
83
 
    return true;
84
 
}
85
 
 
86
 
void DocumentChangeTracker::addChangedRangeInternal(SmartRange* changed)
87
 
{
88
 
    QMutableListIterator<SmartRange*> it = d->changedRanges;
89
 
    bool foundOverlap = false;
90
 
    while (it.hasNext()) {
91
 
        if (it.next() == changed) {
92
 
            foundOverlap = true;
93
 
            break;
94
 
        }
95
 
 
96
 
        if (it.value()->overlaps(*changed)) {
97
 
            int rangeDepth = it.value()->depth();
98
 
            int changedDepth = changed->depth();
99
 
 
100
 
            if (changedDepth < rangeDepth) {
101
 
                // We have a new parent range
102
 
                it.value()->removeWatcher(this);
103
 
                
104
 
                if (!foundOverlap) {
105
 
                    // Replace current range
106
 
                    it.value() = changed;
107
 
                    changed->addWatcher(this);
108
 
                    foundOverlap = true;
109
 
                    
110
 
                } else {
111
 
                    // Remove now child range
112
 
                    it.remove();
113
 
                }
114
 
 
115
 
            } else {
116
 
                // We're contained by the range we found
117
 
                // Nothing more to do
118
 
                foundOverlap = true;
119
 
                break;
120
 
            }
121
 
        }
122
 
    }
123
 
 
124
 
    if (!foundOverlap) {
125
 
        d->changedRanges.append(changed);
126
 
        changed->addWatcher(this);
127
 
    }
128
 
}
129
 
 
130
 
void DocumentChangeTracker::finaliseChangedRanges()
131
 
{
132
 
    QMutexLocker l(d->contentsRetrievedMutex);
133
 
    d->contentsRetrieved = true;
134
 
}
135
 
 
136
 
QMutex* DocumentChangeTracker::changeMutex() const
137
 
{
138
 
    return d->contentsRetrievedMutex;
139
 
}
140
 
 
141
 
void DocumentChangeTracker::setChangedRanges(const QList<SmartRange*>& changedRanges)
142
 
{
143
 
    QMutexLocker l(d->contentsRetrievedMutex);
144
 
    Q_ASSERT(!d->contentsRetrieved);
145
 
    Q_ASSERT(d->changedRanges.isEmpty());
146
 
    
147
 
    d->changedRanges = changedRanges;
148
 
 
149
 
    foreach (SmartRange* range, d->changedRanges)
150
 
        range->addWatcher(this);
151
 
}
152
 
 
153
 
bool DocumentChangeTracker::rangeChangesFinalised() const
154
 
{
155
 
    return d->contentsRetrieved;
156
 
}
157
 
 
158
 
void DocumentChangeTracker::rangeDeleted(SmartRange *range)
159
 
{
160
 
    QMutexLocker l(d->contentsRetrievedMutex);
161
 
 
162
 
    int index = d->changedRanges.indexOf(range);
163
 
    Q_ASSERT(index != -1);
164
 
    d->changedRanges.removeAt(index);
165
 
 
166
 
    if (range->parentRange())
167
 
        addChangedRangeInternal(range->parentRange());
168
 
    else
169
 
        kWarning() << "Top range deleted?";
 
499
    
 
500
    if(revision() == -1)
 
501
        return true; // The 'current' revision is always valid
 
502
    
 
503
    return m_p->m_tracker->holdingRevision(revision());
 
504
}
 
505
 
 
506
RevisionReference DocumentChangeTracker::diskRevision() const
 
507
{
 
508
    ///@todo Track which revision was last saved to disk
 
509
    return RevisionReference();
170
510
}
171
511
 
172
512
}