cppfindreferences.cpp 25.4 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
Roberto Raggi's avatar
Roberto Raggi committed
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
Roberto Raggi's avatar
Roberto Raggi committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
Roberto Raggi's avatar
Roberto Raggi committed
7
**
hjk's avatar
hjk committed
8
9
10
11
12
13
14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
Roberto Raggi's avatar
Roberto Raggi committed
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
18
19
20
21
22
23
24
25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
Roberto Raggi's avatar
Roberto Raggi committed
29
30

#include "cppfindreferences.h"
31

Roberto Raggi's avatar
Roberto Raggi committed
32
#include "cpptoolsconstants.h"
33
#include "cppmodelmanagerinterface.h"
Roberto Raggi's avatar
Roberto Raggi committed
34

35
#include <coreplugin/editormanager/editormanager.h>
36
37
38
39
40
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/futureprogress.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <texteditor/basefilefind.h>

41
#include <utils/qtcassert.h>
42
43
#include <utils/runextensions.h>
#include <utils/textfileformat.h>
Roberto Raggi's avatar
Roberto Raggi committed
44

45
#include <cplusplus/Overview.h>
46
47
#include <QtConcurrentMap>
#include <QDir>
Roberto Raggi's avatar
Roberto Raggi committed
48

49
50
#include <functional>

51
using namespace Core;
Roberto Raggi's avatar
Roberto Raggi committed
52
using namespace CppTools::Internal;
53
using namespace CppTools;
Roberto Raggi's avatar
Roberto Raggi committed
54
55
using namespace CPlusPlus;

Christian Kamm's avatar
Christian Kamm committed
56
static QString getSource(const QString &fileName,
57
                         const CppModelManagerInterface::WorkingCopy &workingCopy)
Christian Kamm's avatar
Christian Kamm committed
58
59
60
61
{
    if (workingCopy.contains(fileName)) {
        return workingCopy.source(fileName);
    } else {
62
63
64
        QString fileContents;
        Utils::TextFileFormat format;
        QString error;
65
        QTextCodec *defaultCodec = EditorManager::defaultTextCodec();
66
67
68
69
        Utils::TextFileFormat::ReadResult result = Utils::TextFileFormat::readFile(
                    fileName, defaultCodec, &fileContents, &format, &error);
        if (result != Utils::TextFileFormat::ReadSuccess)
            qWarning() << "Could not read " << fileName << ". Error: " << error;
Christian Kamm's avatar
Christian Kamm committed
70

71
        return fileContents;
Christian Kamm's avatar
Christian Kamm committed
72
73
74
    }
}

Roberto Raggi's avatar
Roberto Raggi committed
75
namespace {
76

Roberto Raggi's avatar
Roberto Raggi committed
77
class ProcessFile: public std::unary_function<QString, QList<Usage> >
78
{
79
    const CppModelManagerInterface::WorkingCopy workingCopy;
80
    const Snapshot snapshot;
Erik Verbruggen's avatar
Erik Verbruggen committed
81
    Document::Ptr symbolDocument;
82
    Symbol *symbol;
83
    QFutureInterface<Usage> *future;
84
85

public:
86
    ProcessFile(const CppModelManagerInterface::WorkingCopy &workingCopy,
87
                const Snapshot snapshot,
Erik Verbruggen's avatar
Erik Verbruggen committed
88
                Document::Ptr symbolDocument,
89
90
91
92
93
94
95
                Symbol *symbol,
                QFutureInterface<Usage> *future)
        : workingCopy(workingCopy),
          snapshot(snapshot),
          symbolDocument(symbolDocument),
          symbol(symbol),
          future(future)
96
97
98
99
100
    { }

    QList<Usage> operator()(const QString &fileName)
    {
        QList<Usage> usages;
101
102
        if (future->isPaused())
            future->waitForResume();
103
104
        if (future->isCanceled())
            return usages;
105
106
        const Identifier *symbolId = symbol->identifier();

107
        if (Document::Ptr previousDoc = snapshot.document(fileName)) {
108
            Control *control = previousDoc->control();
109
            if (!control->findIdentifier(symbolId->chars(), symbolId->size()))
110
111
                return usages; // skip this document, it's not using symbolId.
        }
112
        Document::Ptr doc;
113
        const QString unpreprocessedSource = getSource(fileName, workingCopy);
114

115
        if (symbolDocument && fileName == symbolDocument->fileName()) {
116
            doc = symbolDocument;
117
        } else {
118
            doc = snapshot.preprocessedDocument(unpreprocessedSource, fileName);
119
120
            doc->tokenize();
        }
121
122
123

        Control *control = doc->control();
        if (control->findIdentifier(symbolId->chars(), symbolId->size()) != 0) {
124
125
            if (doc != symbolDocument)
                doc->check();
126

127
            FindUsages process(unpreprocessedSource.toUtf8(), doc, snapshot);
128
            process(symbol);
129

130
131
132
            usages = process.usages();
        }

133
134
        if (future->isPaused())
            future->waitForResume();
135
136
137
138
        return usages;
    }
};

Roberto Raggi's avatar
Roberto Raggi committed
139
class UpdateUI: public std::binary_function<QList<Usage> &, QList<Usage>, void>
140
141
142
143
{
    QFutureInterface<Usage> *future;

public:
Roberto Raggi's avatar
Roberto Raggi committed
144
    UpdateUI(QFutureInterface<Usage> *future): future(future) {}
145

146
    void operator()(QList<Usage> &, const QList<Usage> &usages)
147
148
149
150
151
152
153
154
    {
        foreach (const Usage &u, usages)
            future->reportResult(u);

        future->setProgressValue(future->progressValue() + 1);
    }
};

Roberto Raggi's avatar
Roberto Raggi committed
155
156
} // end of anonymous namespace

157
CppFindReferences::CppFindReferences(CppModelManagerInterface *modelManager)
Roberto Raggi's avatar
Roberto Raggi committed
158
    : QObject(modelManager),
159
      _modelManager(modelManager)
Roberto Raggi's avatar
Roberto Raggi committed
160
161
162
163
164
165
166
{
}

CppFindReferences::~CppFindReferences()
{
}

167
QList<int> CppFindReferences::references(Symbol *symbol, const LookupContext &context) const
Roberto Raggi's avatar
Roberto Raggi committed
168
169
170
{
    QList<int> references;

171
    FindUsages findUsages(context);
Roberto Raggi's avatar
Roberto Raggi committed
172
173
174
175
176
177
    findUsages(symbol);
    references = findUsages.references();

    return references;
}

178
static void find_helper(QFutureInterface<Usage> &future,
179
                        const CppModelManagerInterface::WorkingCopy workingCopy,
180
                        const LookupContext context,
181
                        CppFindReferences *findRefs,
182
                        Symbol *symbol)
Roberto Raggi's avatar
Roberto Raggi committed
183
{
Roberto Raggi's avatar
Roberto Raggi committed
184
    const Identifier *symbolId = symbol->identifier();
185
    QTC_ASSERT(symbolId != 0, return);
186

187
188
    const Snapshot snapshot = context.snapshot();

Roberto Raggi's avatar
Cleanup    
Roberto Raggi committed
189
190
    const QString sourceFile = QString::fromUtf8(symbol->fileName(), symbol->fileNameLength());
    QStringList files(sourceFile);
191

192
193
194
195
196
    if (symbol->isClass()
        || symbol->isForwardClassDeclaration()
        || (symbol->enclosingScope()
            && !symbol->isStatic()
            && symbol->enclosingScope()->isNamespace())) {
197
        foreach (const Document::Ptr &doc, context.snapshot()) {
198
199
200
201
202
203
204
205
206
            if (doc->fileName() == sourceFile)
                continue;

            Control *control = doc->control();

            if (control->findIdentifier(symbolId->chars(), symbolId->size()))
                files.append(doc->fileName());
        }
    } else {
207
        DependencyTable dependencyTable = findRefs->updateDependencyTable(snapshot);
208
        files += dependencyTable.filesDependingOn(sourceFile);
209
    }
210
    files.removeDuplicates();
Roberto Raggi's avatar
Roberto Raggi committed
211
212
213

    future.setProgressRange(0, files.size());

214
    ProcessFile process(workingCopy, snapshot, context.thisDocument(), symbol, &future);
Roberto Raggi's avatar
Roberto Raggi committed
215
    UpdateUI reduce(&future);
216
217
218
    // This thread waits for blockingMappedReduced to finish, so reduce the pool's used thread count
    // so the blockingMappedReduced can use one more thread, and increase it again afterwards.
    QThreadPool::globalInstance()->releaseThread();
219
    QtConcurrent::blockingMappedReduced<QList<Usage> > (files, process, reduce);
220
    QThreadPool::globalInstance()->reserveThread();
Roberto Raggi's avatar
Roberto Raggi committed
221
222
223
    future.setProgressValue(files.size());
}

224
225
226
227
228
229
230
231
232
233
void CppFindReferences::findUsages(CPlusPlus::Symbol *symbol,
                                   const CPlusPlus::LookupContext &context)
{
    findUsages(symbol, context, QString(), false);
}

void CppFindReferences::findUsages(CPlusPlus::Symbol *symbol,
                                   const CPlusPlus::LookupContext &context,
                                   const QString &replacement,
                                   bool replace)
234
{
235
    Overview overview;
236
    Find::SearchResult *search = Find::SearchResultWindow::instance()->startNewSearch(tr("C++ Usages:"),
237
                                                QString(),
238
                                                overview.prettyName(context.fullyQualifiedName(symbol)),
239
240
241
242
                                                replace ? Find::SearchResultWindow::SearchAndReplace
                                                        : Find::SearchResultWindow::SearchOnly,
                                                QLatin1String("CppEditor"));
    search->setTextToReplace(replacement);
243
244
    connect(search, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>,bool)),
            SLOT(onReplaceButtonClicked(QString,QList<Find::SearchResultItem>,bool)));
245
    connect(search, SIGNAL(paused(bool)), this, SLOT(setPaused(bool)));
246
247
248
249
250
251
252
    search->setSearchAgainSupported(true);
    connect(search, SIGNAL(searchAgainRequested()), this, SLOT(searchAgain()));
    CppFindReferencesParameters parameters;
    parameters.context = context;
    parameters.symbol = symbol;
    search->setUserData(qVariantFromValue(parameters));
    findAll_helper(search);
253
254
}

255
256
void CppFindReferences::renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context,
                                     const QString &replacement)
Roberto Raggi's avatar
Roberto Raggi committed
257
{
Roberto Raggi's avatar
Roberto Raggi committed
258
    if (const Identifier *id = symbol->identifier()) {
259
260
        const QString textToReplace = replacement.isEmpty()
                ? QString::fromUtf8(id->chars(), id->size()) : replacement;
261
        findUsages(symbol, context, textToReplace, true);
262
    }
263
264
}

265
void CppFindReferences::findAll_helper(Find::SearchResult *search)
266
{
267
268
    CppFindReferencesParameters parameters = search->userData().value<CppFindReferencesParameters>();
    if (!(parameters.symbol && parameters.symbol->identifier())) {
Eike Ziller's avatar
Eike Ziller committed
269
        search->finishSearch(false);
270
        return;
271
272
273
274
    }
    connect(search, SIGNAL(cancelled()), this, SLOT(cancel()));
    connect(search, SIGNAL(activated(Find::SearchResultItem)),
            this, SLOT(openEditor(Find::SearchResultItem)));
275

276
    Find::SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus);
277
    const CppModelManagerInterface::WorkingCopy workingCopy = _modelManager->workingCopy();
Roberto Raggi's avatar
Roberto Raggi committed
278
    QFuture<Usage> result;
279
280
    result = QtConcurrent::run(&find_helper, workingCopy,
                               parameters.context, this, parameters.symbol);
281
282
    createWatcher(result, search);

283
    FutureProgress *progress = ProgressManager::addTask(result, tr("Searching"),
284
                                                              QLatin1String(CppTools::Constants::TASK_SEARCH));
Roberto Raggi's avatar
Roberto Raggi committed
285

286
    connect(progress, SIGNAL(clicked()), search, SLOT(popup()));
Roberto Raggi's avatar
Roberto Raggi committed
287
288
}

Roberto Raggi's avatar
Roberto Raggi committed
289
void CppFindReferences::onReplaceButtonClicked(const QString &text,
290
291
                                               const QList<Find::SearchResultItem> &items,
                                               bool preserveCase)
Roberto Raggi's avatar
Roberto Raggi committed
292
{
293
    const QStringList fileNames = TextEditor::BaseFileFind::replaceAll(text, items, preserveCase);
294
295
    if (!fileNames.isEmpty()) {
        _modelManager->updateSourceFiles(fileNames);
296
        Find::SearchResultWindow::instance()->hide();
Roberto Raggi's avatar
Roberto Raggi committed
297
298
299
    }
}

300
301
302
303
304
void CppFindReferences::searchAgain()
{
    Find::SearchResult *search = qobject_cast<Find::SearchResult *>(sender());
    CppFindReferencesParameters parameters = search->userData().value<CppFindReferencesParameters>();
    Snapshot snapshot = CppModelManagerInterface::instance()->snapshot();
Eike Ziller's avatar
Eike Ziller committed
305
    search->restart();
306
    if (!findSymbol(&parameters, snapshot)) {
Eike Ziller's avatar
Eike Ziller committed
307
        search->finishSearch(false);
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
        return;
    }
    search->setUserData(qVariantFromValue(parameters));
    findAll_helper(search);
}

static QByteArray typeId(Symbol *symbol)
{
    if (symbol->asEnum()) {
        return QByteArray("e");
    } else if (symbol->asFunction()) {
        return QByteArray("f");
    } else if (symbol->asNamespace()) {
        return QByteArray("n");
    } else if (symbol->asTemplate()) {
        return QByteArray("t");
    } else if (symbol->asNamespaceAlias()) {
        return QByteArray("na");
    } else if (symbol->asClass()) {
        return QByteArray("c");
    } else if (symbol->asBlock()) {
        return QByteArray("b");
    } else if (symbol->asUsingNamespaceDirective()) {
        return QByteArray("u");
    } else if (symbol->asUsingDeclaration()) {
        return QByteArray("ud");
    } else if (symbol->asDeclaration()) {
        QByteArray temp("d,");
        Overview pretty;
        temp.append(pretty.prettyType(symbol->type()).toLatin1());
        return temp;
    } else if (symbol->asArgument()) {
        return QByteArray("a");
    } else if (symbol->asTypenameArgument()) {
        return QByteArray("ta");
    } else if (symbol->asBaseClass()) {
        return QByteArray("bc");
    } else if (symbol->asForwardClassDeclaration()) {
        return QByteArray("fcd");
    } else if (symbol->asQtPropertyDeclaration()) {
        return QByteArray("qpd");
    } else if (symbol->asQtEnum()) {
        return QByteArray("qe");
    } else if (symbol->asObjCBaseClass()) {
        return QByteArray("ocbc");
    } else if (symbol->asObjCBaseProtocol()) {
        return QByteArray("ocbp");
    } else if (symbol->asObjCClass()) {
        return QByteArray("occ");
    } else if (symbol->asObjCForwardClassDeclaration()) {
        return QByteArray("ocfd");
    } else if (symbol->asObjCProtocol()) {
        return QByteArray("ocp");
    } else if (symbol->asObjCForwardProtocolDeclaration()) {
        return QByteArray("ocfpd");
    } else if (symbol->asObjCMethod()) {
        return QByteArray("ocm");
    } else if (symbol->asObjCPropertyDeclaration()) {
        return QByteArray("ocpd");
    }
    return QByteArray("unknown");
}

static QByteArray idForSymbol(Symbol *symbol)
{
    QByteArray uid(typeId(symbol));
    if (const Identifier *id = symbol->identifier()) {
        uid.append("|");
        uid.append(QByteArray(id->chars(), id->size()));
    } else if (Scope *scope = symbol->enclosingScope()) {
        // add the index of this symbol within its enclosing scope
        // (counting symbols without identifier of the same type)
        int count = 0;
        Scope::iterator it = scope->firstMember();
        while (it != scope->lastMember() && *it != symbol) {
            Symbol *val = *it;
            ++it;
            if (val->identifier() || typeId(val) != uid)
                continue;
            ++count;
        }
        uid.append(QString::number(count).toLocal8Bit());
    }
    return uid;
}

namespace {
class SymbolFinder : public SymbolVisitor
{
public:
398
    SymbolFinder(const QList<QByteArray> &uid) : m_uid(uid), m_index(0), m_result(0) { }
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
    Symbol *result() const { return m_result; }

    bool preVisit(Symbol *symbol)
    {
        if (m_result)
            return false;
        int index = m_index;
        if (symbol->asScope())
            ++m_index;
        if (index >= m_uid.size())
            return false;
        if (idForSymbol(symbol) != m_uid.at(index))
            return false;
        if (index == m_uid.size() - 1) {
            // symbol found
            m_result = symbol;
            return false;
        }
        return true;
    }

    void postVisit(Symbol *symbol)
    {
        if (symbol->asScope())
            --m_index;
    }

private:
427
    QList<QByteArray> m_uid;
428
429
430
431
432
433
434
435
436
    int m_index;
    Symbol *m_result;
};
}

bool CppFindReferences::findSymbol(CppFindReferencesParameters *parameters,
                                      const Snapshot &snapshot)
{
    QString symbolFile = QLatin1String(parameters->symbol->fileName());
437
    if (!snapshot.contains(symbolFile))
438
439
440
441
442
443
        return false;

    Document::Ptr newSymbolDocument = snapshot.document(symbolFile);
    // document is not parsed and has no bindings yet, do it
    QString source = getSource(newSymbolDocument->fileName(), _modelManager->workingCopy());
    Document::Ptr doc =
444
            snapshot.preprocessedDocument(source, newSymbolDocument->fileName());
445
446
447
    doc->check();

    // construct id of old symbol
448
    QList<QByteArray> uid;
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
    Symbol *current = parameters->symbol;
    do {
        uid.prepend(idForSymbol(current));
        current = current->enclosingScope();
    } while (current);
    // find matching symbol in new document and return the new parameters
    SymbolFinder finder(uid);
    finder.accept(doc->globalNamespace());
    if (finder.result()) {
        parameters->symbol = finder.result();
        parameters->context = LookupContext(doc, snapshot);
        return true;
    }
    return false;
}

Roberto Raggi's avatar
Roberto Raggi committed
465
void CppFindReferences::displayResults(int first, int last)
Roberto Raggi's avatar
Roberto Raggi committed
466
{
467
468
469
470
471
    QFutureWatcher<Usage> *watcher = static_cast<QFutureWatcher<Usage> *>(sender());
    Find::SearchResult *search = m_watchers.value(watcher);
    if (!search) {
        // search was deleted while it was running
        watcher->cancel();
472
473
        return;
    }
Roberto Raggi's avatar
Roberto Raggi committed
474
    for (int index = first; index != last; ++index) {
475
476
477
478
479
480
        Usage result = watcher->future().resultAt(index);
        search->addResult(result.path,
                          result.line,
                          result.lineText,
                          result.col,
                          result.len);
Roberto Raggi's avatar
Roberto Raggi committed
481
    }
Roberto Raggi's avatar
Roberto Raggi committed
482
483
484
485
}

void CppFindReferences::searchFinished()
{
486
487
488
    QFutureWatcher<Usage> *watcher = static_cast<QFutureWatcher<Usage> *>(sender());
    Find::SearchResult *search = m_watchers.value(watcher);
    if (search)
Eike Ziller's avatar
Eike Ziller committed
489
        search->finishSearch(watcher->isCanceled());
490
    m_watchers.remove(watcher);
Roberto Raggi's avatar
Roberto Raggi committed
491
492
}

493
494
void CppFindReferences::cancel()
{
495
496
497
498
499
    Find::SearchResult *search = qobject_cast<Find::SearchResult *>(sender());
    QTC_ASSERT(search, return);
    QFutureWatcher<Usage> *watcher = m_watchers.key(search);
    QTC_ASSERT(watcher, return);
    watcher->cancel();
500
501
}

502
503
504
505
506
507
508
509
510
511
void CppFindReferences::setPaused(bool paused)
{
    Find::SearchResult *search = qobject_cast<Find::SearchResult *>(sender());
    QTC_ASSERT(search, return);
    QFutureWatcher<Usage> *watcher = m_watchers.key(search);
    QTC_ASSERT(watcher, return);
    if (!paused || watcher->isRunning()) // guard against pausing when the search is finished
        watcher->setPaused(paused);
}

512
void CppFindReferences::openEditor(const Find::SearchResultItem &item)
Roberto Raggi's avatar
Roberto Raggi committed
513
{
514
    if (item.path.size() > 0) {
515
        EditorManager::openEditorAt(QDir::fromNativeSeparators(item.path.first()),
Eike Ziller's avatar
Eike Ziller committed
516
                                              item.lineNumber, item.textMarkPos);
517
    } else {
518
        EditorManager::openEditor(QDir::fromNativeSeparators(item.text));
519
    }
Roberto Raggi's avatar
Roberto Raggi committed
520
521
}

Christian Kamm's avatar
Christian Kamm committed
522
523
524
525
526

namespace {

class FindMacroUsesInFile: public std::unary_function<QString, QList<Usage> >
{
527
    const CppModelManagerInterface::WorkingCopy workingCopy;
Christian Kamm's avatar
Christian Kamm committed
528
529
    const Snapshot snapshot;
    const Macro &macro;
530
    QFutureInterface<Usage> *future;
Christian Kamm's avatar
Christian Kamm committed
531
532

public:
533
    FindMacroUsesInFile(const CppModelManagerInterface::WorkingCopy &workingCopy,
Christian Kamm's avatar
Christian Kamm committed
534
                        const Snapshot snapshot,
535
536
537
                        const Macro &macro,
                        QFutureInterface<Usage> *future)
        : workingCopy(workingCopy), snapshot(snapshot), macro(macro), future(future)
Christian Kamm's avatar
Christian Kamm committed
538
539
540
541
542
    { }

    QList<Usage> operator()(const QString &fileName)
    {
        QList<Usage> usages;
543
544
545
        Document::Ptr doc = snapshot.document(fileName);
        QString source;

546
restart_search:
547
548
        if (future->isPaused())
            future->waitForResume();
549
550
        if (future->isCanceled())
            return usages;
Christian Kamm's avatar
Christian Kamm committed
551

552
        usages.clear();
Christian Kamm's avatar
Christian Kamm committed
553
554
        foreach (const Document::MacroUse &use, doc->macroUses()) {
            const Macro &useMacro = use.macro();
555
556

            if (useMacro.fileName() == macro.fileName()) { // Check if this is a match, but possibly against an outdated document.
Andre Hartmann's avatar
Andre Hartmann committed
557
558
559
                if (source.isEmpty())
                    source = getSource(fileName, workingCopy);

560
561
562
                if (macro.fileRevision() > useMacro.fileRevision()) {
                    // yes, it is outdated, so re-preprocess and start from scratch for this file.
                    doc = snapshot.preprocessedDocument(source, fileName);
563
564
                    usages.clear();
                    goto restart_search;
565
566
                }

Andre Hartmann's avatar
Andre Hartmann committed
567
568
569
570
571
572
                if (macro.name() == useMacro.name()) {
                    unsigned lineStart;
                    const QString &lineSource = matchingLine(use.begin(), source, &lineStart);
                    usages.append(Usage(fileName, lineSource, use.beginLine(),
                                        use.begin() - lineStart, useMacro.name().length()));
                }
Christian Kamm's avatar
Christian Kamm committed
573
574
575
            }
        }

576
577
        if (future->isPaused())
            future->waitForResume();
Christian Kamm's avatar
Christian Kamm committed
578
579
580
        return usages;
    }

581
    static QString matchingLine(unsigned position, const QString &source,
Christian Kamm's avatar
Christian Kamm committed
582
583
                                unsigned *lineStart = 0)
    {
584
585
586
587
        int lineBegin = source.lastIndexOf(QLatin1Char('\n'), position) + 1;
        int lineEnd = source.indexOf(QLatin1Char('\n'), position);
        if (lineEnd == -1)
            lineEnd = source.length();
Christian Kamm's avatar
Christian Kamm committed
588
589

        if (lineStart)
590
            *lineStart = lineBegin;
Christian Kamm's avatar
Christian Kamm committed
591

592
        const QString matchingLine = source.mid(lineBegin, lineEnd - lineBegin);
Christian Kamm's avatar
Christian Kamm committed
593
594
595
596
597
598
599
        return matchingLine;
    }
};

} // end of anonymous namespace

static void findMacroUses_helper(QFutureInterface<Usage> &future,
600
                                 const CppModelManagerInterface::WorkingCopy workingCopy,
601
602
603
                                 const Snapshot snapshot,
                                 CppFindReferences *findRefs,
                                 const Macro macro)
Christian Kamm's avatar
Christian Kamm committed
604
{
605
606
607
    // ensure the dependency table is updated
    DependencyTable dependencies = findRefs->updateDependencyTable(snapshot);

Christian Kamm's avatar
Christian Kamm committed
608
609
    const QString& sourceFile = macro.fileName();
    QStringList files(sourceFile);
610
    files += dependencies.filesDependingOn(sourceFile);
Christian Kamm's avatar
Christian Kamm committed
611
612
613
    files.removeDuplicates();

    future.setProgressRange(0, files.size());
614
    FindMacroUsesInFile process(workingCopy, snapshot, macro, &future);
Christian Kamm's avatar
Christian Kamm committed
615
    UpdateUI reduce(&future);
616
617
618
    // This thread waits for blockingMappedReduced to finish, so reduce the pool's used thread count
    // so the blockingMappedReduced can use one more thread, and increase it again afterwards.
    QThreadPool::globalInstance()->releaseThread();
Christian Kamm's avatar
Christian Kamm committed
619
    QtConcurrent::blockingMappedReduced<QList<Usage> > (files, process, reduce);
620
    QThreadPool::globalInstance()->reserveThread();
Christian Kamm's avatar
Christian Kamm committed
621
622
623
624
    future.setProgressValue(files.size());
}

void CppFindReferences::findMacroUses(const Macro &macro)
625
626
627
628
629
{
    findMacroUses(macro, QString(), false);
}

void CppFindReferences::findMacroUses(const Macro &macro, const QString &replacement, bool replace)
Christian Kamm's avatar
Christian Kamm committed
630
{
631
    Find::SearchResult *search = Find::SearchResultWindow::instance()->startNewSearch(
632
633
                tr("C++ Macro Usages:"),
                QString(),
634
                QString::fromUtf8(macro.name()),
635
636
637
638
639
                replace ? Find::SearchResultWindow::SearchAndReplace
                        : Find::SearchResultWindow::SearchOnly,
                QLatin1String("CppEditor"));

    search->setTextToReplace(replacement);
640
641
    connect(search, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>,bool)),
            SLOT(onReplaceButtonClicked(QString,QList<Find::SearchResultItem>,bool)));
Christian Kamm's avatar
Christian Kamm committed
642

643
    Find::SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus);
Christian Kamm's avatar
Christian Kamm committed
644

645
    connect(search, SIGNAL(activated(Find::SearchResultItem)),
Christian Kamm's avatar
Christian Kamm committed
646
            this, SLOT(openEditor(Find::SearchResultItem)));
647
    connect(search, SIGNAL(cancelled()), this, SLOT(cancel()));
648
    connect(search, SIGNAL(paused(bool)), this, SLOT(setPaused(bool)));
Christian Kamm's avatar
Christian Kamm committed
649
650

    const Snapshot snapshot = _modelManager->snapshot();
651
    const CppModelManagerInterface::WorkingCopy workingCopy = _modelManager->workingCopy();
Christian Kamm's avatar
Christian Kamm committed
652
653
654

    // add the macro definition itself
    {
655
656
657
        const QString &source = getSource(macro.fileName(), workingCopy);
        unsigned lineStart;
        const QString line = FindMacroUsesInFile::matchingLine(macro.offset(), source, &lineStart);
658
        search->addResult(macro.fileName(), macro.line(), line,
659
                          macro.offset() - lineStart, macro.name().length());
Christian Kamm's avatar
Christian Kamm committed
660
661
662
    }

    QFuture<Usage> result;
663
    result = QtConcurrent::run(&findMacroUses_helper, workingCopy, snapshot, this, macro);
664
    createWatcher(result, search);
Christian Kamm's avatar
Christian Kamm committed
665

666
    FutureProgress *progress = ProgressManager::addTask(result, tr("Searching"),
667
                                                              QLatin1String(CppTools::Constants::TASK_SEARCH));
668
    connect(progress, SIGNAL(clicked()), search, SLOT(popup()));
Christian Kamm's avatar
Christian Kamm committed
669
670
}

671
672
void CppFindReferences::renameMacroUses(const Macro &macro, const QString &replacement)
{
673
    const QString textToReplace = replacement.isEmpty() ? QString::fromUtf8(macro.name()) : replacement;
674
675
676
    findMacroUses(macro, textToReplace, true);
}

677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
DependencyTable CppFindReferences::updateDependencyTable(CPlusPlus::Snapshot snapshot)
{
    DependencyTable oldDeps = dependencyTable();
    if (oldDeps.isValidFor(snapshot))
        return oldDeps;

    DependencyTable newDeps;
    newDeps.build(snapshot);
    setDependencyTable(newDeps);
    return newDeps;
}

DependencyTable CppFindReferences::dependencyTable() const
{
    QMutexLocker locker(&m_depsLock);
    Q_UNUSED(locker);
    return m_deps;
}

void CppFindReferences::setDependencyTable(const CPlusPlus::DependencyTable &newTable)
{
    QMutexLocker locker(&m_depsLock);
    Q_UNUSED(locker);
    m_deps = newTable;
}
702
703
704
705
706
707
708
709

void CppFindReferences::createWatcher(const QFuture<Usage> &future, Find::SearchResult *search)
{
    QFutureWatcher<Usage> *watcher = new QFutureWatcher<Usage>();
    watcher->setPendingResultsLimit(1);
    connect(watcher, SIGNAL(resultsReadyAt(int,int)), this, SLOT(displayResults(int,int)));
    connect(watcher, SIGNAL(finished()), this, SLOT(searchFinished()));
    m_watchers.insert(watcher, search);
Eike Ziller's avatar
Eike Ziller committed
710
    watcher->setFuture(future);
711
}