cppfindreferences.cpp 25.3 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
Roberto Raggi's avatar
Roberto Raggi committed
2
**
Eike Ziller's avatar
Eike Ziller committed
3 4
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
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
** 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
Eike Ziller's avatar
Eike Ziller committed
12 13
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
Eike Ziller's avatar
Eike Ziller committed
14
** use the contact form at http://www.qt.io/contact-us.
Roberto Raggi's avatar
Roberto Raggi committed
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18 19 20 21 22 23
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
hjk's avatar
hjk committed
24
**
Eike Ziller's avatar
Eike Ziller committed
25 26
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
con's avatar
con committed
27 28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
29
****************************************************************************/
Roberto Raggi's avatar
Roberto Raggi committed
30 31

#include "cppfindreferences.h"
32

Roberto Raggi's avatar
Roberto Raggi committed
33
#include "cpptoolsconstants.h"
34
#include "cppmodelmanager.h"
35
#include "cppworkingcopy.h"
Roberto Raggi's avatar
Roberto Raggi committed
36

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

43
#include <utils/algorithm.h>
44
#include <utils/qtcassert.h>
45 46
#include <utils/runextensions.h>
#include <utils/textfileformat.h>
Roberto Raggi's avatar
Roberto Raggi committed
47

48
#include <cplusplus/Overview.h>
49 50
#include <QtConcurrentMap>
#include <QDir>
Roberto Raggi's avatar
Roberto Raggi committed
51

52 53
#include <functional>

54
using namespace Core;
Roberto Raggi's avatar
Roberto Raggi committed
55
using namespace CppTools::Internal;
56
using namespace CppTools;
Roberto Raggi's avatar
Roberto Raggi committed
57 58
using namespace CPlusPlus;

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

74
        return fileContents.toUtf8();
Christian Kamm's avatar
Christian Kamm committed
75 76 77
    }
}

78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
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;
101
        temp.append(pretty.prettyType(symbol->type()).toUtf8());
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
        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;
145 146
        Scope::iterator it = scope->memberBegin();
        while (it != scope->memberEnd() && *it != symbol) {
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
            Symbol *val = *it;
            ++it;
            if (val->identifier() || typeId(val) != uid)
                continue;
            ++count;
        }
        uid.append(QString::number(count).toLocal8Bit());
    }
    return uid;
}

static QList<QByteArray> fullIdForSymbol(Symbol *symbol)
{
    QList<QByteArray> uid;
    Symbol *current = symbol;
    do {
        uid.prepend(idForSymbol(current));
        current = current->enclosingScope();
    } while (current);
    return uid;
}

Roberto Raggi's avatar
Roberto Raggi committed
169
namespace {
170

Roberto Raggi's avatar
Roberto Raggi committed
171
class ProcessFile: public std::unary_function<QString, QList<Usage> >
172
{
173
    const WorkingCopy workingCopy;
174
    const Snapshot snapshot;
Erik Verbruggen's avatar
Erik Verbruggen committed
175
    Document::Ptr symbolDocument;
176
    Symbol *symbol;
177
    QFutureInterface<Usage> *future;
178 179

public:
180
    ProcessFile(const WorkingCopy &workingCopy,
181
                const Snapshot snapshot,
Erik Verbruggen's avatar
Erik Verbruggen committed
182
                Document::Ptr symbolDocument,
183 184 185 186 187 188 189
                Symbol *symbol,
                QFutureInterface<Usage> *future)
        : workingCopy(workingCopy),
          snapshot(snapshot),
          symbolDocument(symbolDocument),
          symbol(symbol),
          future(future)
190 191
    { }

192
    QList<Usage> operator()(const Utils::FileName &fileName)
193 194
    {
        QList<Usage> usages;
195 196
        if (future->isPaused())
            future->waitForResume();
197 198
        if (future->isCanceled())
            return usages;
199 200
        const Identifier *symbolId = symbol->identifier();

201
        if (Document::Ptr previousDoc = snapshot.document(fileName)) {
202
            Control *control = previousDoc->control();
203
            if (!control->findIdentifier(symbolId->chars(), symbolId->size()))
204 205
                return usages; // skip this document, it's not using symbolId.
        }
206
        Document::Ptr doc;
207
        const QByteArray unpreprocessedSource = getSource(fileName, workingCopy);
208

209
        if (symbolDocument && fileName == Utils::FileName::fromString(symbolDocument->fileName())) {
210
            doc = symbolDocument;
211
        } else {
212
            doc = snapshot.preprocessedDocument(unpreprocessedSource, fileName);
213 214
            doc->tokenize();
        }
215 216 217

        Control *control = doc->control();
        if (control->findIdentifier(symbolId->chars(), symbolId->size()) != 0) {
218 219
            if (doc != symbolDocument)
                doc->check();
220

221
            FindUsages process(unpreprocessedSource, doc, snapshot);
222
            process(symbol);
223

224 225 226
            usages = process.usages();
        }

227 228
        if (future->isPaused())
            future->waitForResume();
229 230 231 232
        return usages;
    }
};

Roberto Raggi's avatar
Roberto Raggi committed
233
class UpdateUI: public std::binary_function<QList<Usage> &, QList<Usage>, void>
234 235 236 237
{
    QFutureInterface<Usage> *future;

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

240
    void operator()(QList<Usage> &, const QList<Usage> &usages)
241 242 243 244 245 246 247 248
    {
        foreach (const Usage &u, usages)
            future->reportResult(u);

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

Roberto Raggi's avatar
Roberto Raggi committed
249 250
} // end of anonymous namespace

251
CppFindReferences::CppFindReferences(CppModelManager *modelManager)
Roberto Raggi's avatar
Roberto Raggi committed
252
    : QObject(modelManager),
Erik Verbruggen's avatar
Erik Verbruggen committed
253
      m_modelManager(modelManager)
Roberto Raggi's avatar
Roberto Raggi committed
254 255 256 257 258 259 260
{
}

CppFindReferences::~CppFindReferences()
{
}

261
QList<int> CppFindReferences::references(Symbol *symbol, const LookupContext &context) const
Roberto Raggi's avatar
Roberto Raggi committed
262 263 264
{
    QList<int> references;

265
    FindUsages findUsages(context);
Roberto Raggi's avatar
Roberto Raggi committed
266 267 268 269 270 271
    findUsages(symbol);
    references = findUsages.references();

    return references;
}

272
static void find_helper(QFutureInterface<Usage> &future,
273
                        const WorkingCopy workingCopy,
274
                        const LookupContext context,
275
                        Symbol *symbol)
Roberto Raggi's avatar
Roberto Raggi committed
276
{
Roberto Raggi's avatar
Roberto Raggi committed
277
    const Identifier *symbolId = symbol->identifier();
278
    QTC_ASSERT(symbolId != 0, return);
279

280 281
    const Snapshot snapshot = context.snapshot();

282 283
    const Utils::FileName sourceFile = Utils::FileName::fromUtf8(symbol->fileName(),
                                                                 symbol->fileNameLength());
284
    Utils::FileNameList files {sourceFile};
285

286 287 288 289 290
    if (symbol->isClass()
        || symbol->isForwardClassDeclaration()
        || (symbol->enclosingScope()
            && !symbol->isStatic()
            && symbol->enclosingScope()->isNamespace())) {
291 292 293
        const Snapshot snapshotFromContext = context.snapshot();
        for (auto i = snapshotFromContext.begin(), ei = snapshotFromContext.end(); i != ei; ++i) {
            if (i.key() == sourceFile)
294 295
                continue;

296
            const Control *control = i.value()->control();
297 298

            if (control->findIdentifier(symbolId->chars(), symbolId->size()))
299
                files.append(i.key());
300 301
        }
    } else {
302
        files += snapshot.filesDependingOn(sourceFile);
303
    }
304
    files = Utils::filteredUnique(files);
Roberto Raggi's avatar
Roberto Raggi committed
305 306 307

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

308
    ProcessFile process(workingCopy, snapshot, context.thisDocument(), symbol, &future);
Roberto Raggi's avatar
Roberto Raggi committed
309
    UpdateUI reduce(&future);
310 311 312
    // 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();
313
    QtConcurrent::blockingMappedReduced<QList<Usage> > (files, process, reduce);
314
    QThreadPool::globalInstance()->reserveThread();
Roberto Raggi's avatar
Roberto Raggi committed
315 316 317
    future.setProgressValue(files.size());
}

318
void CppFindReferences::findUsages(Symbol *symbol, const LookupContext &context)
319 320 321 322
{
    findUsages(symbol, context, QString(), false);
}

323 324
void CppFindReferences::findUsages(Symbol *symbol,
                                   const LookupContext &context,
325 326
                                   const QString &replacement,
                                   bool replace)
327
{
328
    Overview overview;
329
    SearchResult *search = SearchResultWindow::instance()->startNewSearch(tr("C++ Usages:"),
330
                                                QString(),
331
                                                overview.prettyName(context.fullyQualifiedName(symbol)),
332 333 334
                                                replace ? SearchResultWindow::SearchAndReplace
                                                        : SearchResultWindow::SearchOnly,
                                                SearchResultWindow::PreserveCaseDisabled,
335 336
                                                QLatin1String("CppEditor"));
    search->setTextToReplace(replacement);
337 338
    connect(search, SIGNAL(replaceButtonClicked(QString,QList<Core::SearchResultItem>,bool)),
            SLOT(onReplaceButtonClicked(QString,QList<Core::SearchResultItem>,bool)));
339
    connect(search, SIGNAL(paused(bool)), this, SLOT(setPaused(bool)));
340 341 342
    search->setSearchAgainSupported(true);
    connect(search, SIGNAL(searchAgainRequested()), this, SLOT(searchAgain()));
    CppFindReferencesParameters parameters;
343 344
    parameters.symbolId = fullIdForSymbol(symbol);
    parameters.symbolFileName = QByteArray(symbol->fileName());
345
    search->setUserData(qVariantFromValue(parameters));
346
    findAll_helper(search, symbol, context);
347 348
}

349
void CppFindReferences::renameUsages(Symbol *symbol, const LookupContext &context,
350
                                     const QString &replacement)
Roberto Raggi's avatar
Roberto Raggi committed
351
{
Roberto Raggi's avatar
Roberto Raggi committed
352
    if (const Identifier *id = symbol->identifier()) {
353 354
        const QString textToReplace = replacement.isEmpty()
                ? QString::fromUtf8(id->chars(), id->size()) : replacement;
355
        findUsages(symbol, context, textToReplace, true);
356
    }
357 358
}

359 360
void CppFindReferences::findAll_helper(SearchResult *search, Symbol *symbol,
                                       const LookupContext &context)
361
{
362
    if (!(symbol && symbol->identifier())) {
Eike Ziller's avatar
Eike Ziller committed
363
        search->finishSearch(false);
364
        return;
365 366
    }
    connect(search, SIGNAL(cancelled()), this, SLOT(cancel()));
367 368
    connect(search, SIGNAL(activated(Core::SearchResultItem)),
            this, SLOT(openEditor(Core::SearchResultItem)));
369

370
    SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus);
371
    const WorkingCopy workingCopy = m_modelManager->workingCopy();
Roberto Raggi's avatar
Roberto Raggi committed
372
    QFuture<Usage> result;
373
    result = QtConcurrent::run(&find_helper, workingCopy, context, symbol);
374 375
    createWatcher(result, search);

376
    FutureProgress *progress = ProgressManager::addTask(result, tr("Searching for Usages"),
377
                                                              CppTools::Constants::TASK_SEARCH);
Roberto Raggi's avatar
Roberto Raggi committed
378

379
    connect(progress, SIGNAL(clicked()), search, SLOT(popup()));
Roberto Raggi's avatar
Roberto Raggi committed
380 381
}

Roberto Raggi's avatar
Roberto Raggi committed
382
void CppFindReferences::onReplaceButtonClicked(const QString &text,
383
                                               const QList<SearchResultItem> &items,
384
                                               bool preserveCase)
Roberto Raggi's avatar
Roberto Raggi committed
385
{
386
    const QStringList fileNames = TextEditor::BaseFileFind::replaceAll(text, items, preserveCase);
387
    if (!fileNames.isEmpty()) {
388
        m_modelManager->updateSourceFiles(fileNames.toSet());
389
        SearchResultWindow::instance()->hide();
Roberto Raggi's avatar
Roberto Raggi committed
390 391 392
    }
}

393 394
void CppFindReferences::searchAgain()
{
395
    SearchResult *search = qobject_cast<SearchResult *>(sender());
396
    CppFindReferencesParameters parameters = search->userData().value<CppFindReferencesParameters>();
397
    Snapshot snapshot = CppModelManager::instance()->snapshot();
Eike Ziller's avatar
Eike Ziller committed
398
    search->restart();
399 400 401
    LookupContext context;
    Symbol *symbol = findSymbol(parameters, snapshot, &context);
    if (!symbol) {
Eike Ziller's avatar
Eike Ziller committed
402
        search->finishSearch(false);
403 404
        return;
    }
405
    findAll_helper(search, symbol, context);
406 407 408
}

namespace {
409
class UidSymbolFinder : public SymbolVisitor
410 411
{
public:
412
    UidSymbolFinder(const QList<QByteArray> &uid) : m_uid(uid), m_index(0), m_result(0) { }
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
    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:
441
    QList<QByteArray> m_uid;
442 443 444 445 446
    int m_index;
    Symbol *m_result;
};
}

447
Symbol *CppFindReferences::findSymbol(const CppFindReferencesParameters &parameters,
448
                                      const Snapshot &snapshot, LookupContext *context)
449
{
450 451
    QTC_ASSERT(context, return 0);
    QString symbolFile = QLatin1String(parameters.symbolFileName);
452
    if (!snapshot.contains(symbolFile))
453
        return 0;
454 455 456

    Document::Ptr newSymbolDocument = snapshot.document(symbolFile);
    // document is not parsed and has no bindings yet, do it
457 458
    QByteArray source = getSource(Utils::FileName::fromString(newSymbolDocument->fileName()),
                                  m_modelManager->workingCopy());
459
    Document::Ptr doc =
460
            snapshot.preprocessedDocument(source, newSymbolDocument->fileName());
461 462 463
    doc->check();

    // find matching symbol in new document and return the new parameters
464
    UidSymbolFinder finder(parameters.symbolId);
465 466
    finder.accept(doc->globalNamespace());
    if (finder.result()) {
467 468
        *context = LookupContext(doc, snapshot);
        return finder.result();
469
    }
470
    return 0;
471 472
}

Roberto Raggi's avatar
Roberto Raggi committed
473
void CppFindReferences::displayResults(int first, int last)
Roberto Raggi's avatar
Roberto Raggi committed
474
{
475
    QFutureWatcher<Usage> *watcher = static_cast<QFutureWatcher<Usage> *>(sender());
476
    SearchResult *search = m_watchers.value(watcher);
477 478 479
    if (!search) {
        // search was deleted while it was running
        watcher->cancel();
480 481
        return;
    }
Roberto Raggi's avatar
Roberto Raggi committed
482
    for (int index = first; index != last; ++index) {
483 484 485 486 487 488
        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
489
    }
Roberto Raggi's avatar
Roberto Raggi committed
490 491 492 493
}

void CppFindReferences::searchFinished()
{
494
    QFutureWatcher<Usage> *watcher = static_cast<QFutureWatcher<Usage> *>(sender());
495
    SearchResult *search = m_watchers.value(watcher);
496
    if (search)
Eike Ziller's avatar
Eike Ziller committed
497
        search->finishSearch(watcher->isCanceled());
498
    m_watchers.remove(watcher);
Erik Verbruggen's avatar
Erik Verbruggen committed
499
    watcher->deleteLater();
Roberto Raggi's avatar
Roberto Raggi committed
500 501
}

502 503
void CppFindReferences::cancel()
{
504
    SearchResult *search = qobject_cast<SearchResult *>(sender());
505 506 507 508
    QTC_ASSERT(search, return);
    QFutureWatcher<Usage> *watcher = m_watchers.key(search);
    QTC_ASSERT(watcher, return);
    watcher->cancel();
509 510
}

511 512
void CppFindReferences::setPaused(bool paused)
{
513
    SearchResult *search = qobject_cast<SearchResult *>(sender());
514 515 516 517 518 519 520
    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);
}

521
void CppFindReferences::openEditor(const SearchResultItem &item)
Roberto Raggi's avatar
Roberto Raggi committed
522
{
523
    if (item.path.size() > 0) {
524
        EditorManager::openEditorAt(QDir::fromNativeSeparators(item.path.first()),
Eike Ziller's avatar
Eike Ziller committed
525
                                              item.lineNumber, item.textMarkPos);
526
    } else {
527
        EditorManager::openEditor(QDir::fromNativeSeparators(item.text));
528
    }
Roberto Raggi's avatar
Roberto Raggi committed
529 530
}

Christian Kamm's avatar
Christian Kamm committed
531 532 533 534 535

namespace {

class FindMacroUsesInFile: public std::unary_function<QString, QList<Usage> >
{
536
    const WorkingCopy workingCopy;
Christian Kamm's avatar
Christian Kamm committed
537 538
    const Snapshot snapshot;
    const Macro &macro;
539
    QFutureInterface<Usage> *future;
Christian Kamm's avatar
Christian Kamm committed
540 541

public:
542
    FindMacroUsesInFile(const WorkingCopy &workingCopy,
Christian Kamm's avatar
Christian Kamm committed
543
                        const Snapshot snapshot,
544 545 546
                        const Macro &macro,
                        QFutureInterface<Usage> *future)
        : workingCopy(workingCopy), snapshot(snapshot), macro(macro), future(future)
Christian Kamm's avatar
Christian Kamm committed
547 548
    { }

549
    QList<Usage> operator()(const Utils::FileName &fileName)
Christian Kamm's avatar
Christian Kamm committed
550 551
    {
        QList<Usage> usages;
552
        Document::Ptr doc = snapshot.document(fileName);
553
        QByteArray source;
554

555
restart_search:
556 557
        if (future->isPaused())
            future->waitForResume();
558 559
        if (future->isCanceled())
            return usages;
Christian Kamm's avatar
Christian Kamm committed
560

561
        usages.clear();
Christian Kamm's avatar
Christian Kamm committed
562 563
        foreach (const Document::MacroUse &use, doc->macroUses()) {
            const Macro &useMacro = use.macro();
564 565

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

569 570 571
                if (macro.fileRevision() > useMacro.fileRevision()) {
                    // yes, it is outdated, so re-preprocess and start from scratch for this file.
                    doc = snapshot.preprocessedDocument(source, fileName);
572 573
                    usages.clear();
                    goto restart_search;
574 575
                }

Andre Hartmann's avatar
Andre Hartmann committed
576
                if (macro.name() == useMacro.name()) {
577 578
                    unsigned column;
                    const QString &lineSource = matchingLine(use.bytesBegin(), source, &column);
579
                    usages.append(Usage(fileName.toString(), lineSource, use.beginLine(), column,
580
                                        useMacro.nameToQString().size()));
Andre Hartmann's avatar
Andre Hartmann committed
581
                }
Christian Kamm's avatar
Christian Kamm committed
582 583 584
            }
        }

585 586
        if (future->isPaused())
            future->waitForResume();
Christian Kamm's avatar
Christian Kamm committed
587 588 589
        return usages;
    }

590 591
    static QString matchingLine(unsigned bytesOffsetOfUseStart, const QByteArray &utf8Source,
                                unsigned *columnOfUseStart = 0)
Christian Kamm's avatar
Christian Kamm committed
592
    {
593 594
        int lineBegin = utf8Source.lastIndexOf('\n', bytesOffsetOfUseStart) + 1;
        int lineEnd = utf8Source.indexOf('\n', bytesOffsetOfUseStart);
595
        if (lineEnd == -1)
596 597 598 599 600 601 602 603 604 605 606
            lineEnd = utf8Source.length();

        if (columnOfUseStart) {
            *columnOfUseStart = 0;
            const char *startOfUse = utf8Source.constData() + bytesOffsetOfUseStart;
            QTC_ASSERT(startOfUse < utf8Source.constData() + lineEnd, return QString());
            const char *currentSourceByte = utf8Source.constData() + lineBegin;
            unsigned char yychar = *currentSourceByte;
            while (currentSourceByte != startOfUse)
                Lexer::yyinp_utf8(currentSourceByte, yychar, *columnOfUseStart);
        }
Christian Kamm's avatar
Christian Kamm committed
607

608 609
        const QByteArray matchingLine = utf8Source.mid(lineBegin, lineEnd - lineBegin);
        return QString::fromUtf8(matchingLine, matchingLine.size());
Christian Kamm's avatar
Christian Kamm committed
610 611 612 613 614 615
    }
};

} // end of anonymous namespace

static void findMacroUses_helper(QFutureInterface<Usage> &future,
616
                                 const WorkingCopy workingCopy,
617 618
                                 const Snapshot snapshot,
                                 const Macro macro)
Christian Kamm's avatar
Christian Kamm committed
619
{
620
    const Utils::FileName sourceFile = Utils::FileName::fromString(macro.fileName());
621 622
    Utils::FileNameList files {sourceFile};
    files = Utils::filteredUnique(files + snapshot.filesDependingOn(sourceFile));
Christian Kamm's avatar
Christian Kamm committed
623 624

    future.setProgressRange(0, files.size());
625
    FindMacroUsesInFile process(workingCopy, snapshot, macro, &future);
Christian Kamm's avatar
Christian Kamm committed
626
    UpdateUI reduce(&future);
627 628 629
    // 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
630
    QtConcurrent::blockingMappedReduced<QList<Usage> > (files, process, reduce);
631
    QThreadPool::globalInstance()->reserveThread();
Christian Kamm's avatar
Christian Kamm committed
632 633 634 635
    future.setProgressValue(files.size());
}

void CppFindReferences::findMacroUses(const Macro &macro)
636 637 638 639 640
{
    findMacroUses(macro, QString(), false);
}

void CppFindReferences::findMacroUses(const Macro &macro, const QString &replacement, bool replace)
Christian Kamm's avatar
Christian Kamm committed
641
{
642
    SearchResult *search = SearchResultWindow::instance()->startNewSearch(
643 644
                tr("C++ Macro Usages:"),
                QString(),
645
                macro.nameToQString(),
646 647 648
                replace ? SearchResultWindow::SearchAndReplace
                        : SearchResultWindow::SearchOnly,
                SearchResultWindow::PreserveCaseDisabled,
649 650 651
                QLatin1String("CppEditor"));

    search->setTextToReplace(replacement);
652 653
    connect(search, SIGNAL(replaceButtonClicked(QString,QList<Core::SearchResultItem>,bool)),
            SLOT(onReplaceButtonClicked(QString,QList<Core::SearchResultItem>,bool)));
Christian Kamm's avatar
Christian Kamm committed
654

655
    SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus);
Christian Kamm's avatar
Christian Kamm committed
656

657 658
    connect(search, SIGNAL(activated(Core::SearchResultItem)),
            this, SLOT(openEditor(Core::SearchResultItem)));
659
    connect(search, SIGNAL(cancelled()), this, SLOT(cancel()));
660
    connect(search, SIGNAL(paused(bool)), this, SLOT(setPaused(bool)));
Christian Kamm's avatar
Christian Kamm committed
661

Erik Verbruggen's avatar
Erik Verbruggen committed
662
    const Snapshot snapshot = m_modelManager->snapshot();
663
    const WorkingCopy workingCopy = m_modelManager->workingCopy();
Christian Kamm's avatar
Christian Kamm committed
664 665 666

    // add the macro definition itself
    {
667 668
        const QByteArray &source = getSource(Utils::FileName::fromString(macro.fileName()),
                                             workingCopy);
669 670 671 672 673
        unsigned column;
        const QString line = FindMacroUsesInFile::matchingLine(macro.bytesOffset(), source,
                                                               &column);
        search->addResult(macro.fileName(), macro.line(), line, column,
                          macro.nameToQString().length());
Christian Kamm's avatar
Christian Kamm committed
674 675 676
    }

    QFuture<Usage> result;
677
    result = QtConcurrent::run(&findMacroUses_helper, workingCopy, snapshot, macro);
678
    createWatcher(result, search);
Christian Kamm's avatar
Christian Kamm committed
679

680
    FutureProgress *progress = ProgressManager::addTask(result, tr("Searching for Usages"),
681
                                                              CppTools::Constants::TASK_SEARCH);
682
    connect(progress, SIGNAL(clicked()), search, SLOT(popup()));
Christian Kamm's avatar
Christian Kamm committed
683 684
}

685 686
void CppFindReferences::renameMacroUses(const Macro &macro, const QString &replacement)
{
687
    const QString textToReplace = replacement.isEmpty() ? macro.nameToQString() : replacement;
688 689 690
    findMacroUses(macro, textToReplace, true);
}

691
void CppFindReferences::createWatcher(const QFuture<Usage> &future, SearchResult *search)
692 693 694 695 696 697
{
    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
698
    watcher->setFuture(future);
699
}