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/qtcassert.h>
44 45
#include <utils/runextensions.h>
#include <utils/textfileformat.h>
Roberto Raggi's avatar
Roberto Raggi committed
46

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

51 52
#include <functional>

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

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

73
        return fileContents.toUtf8();
Christian Kamm's avatar
Christian Kamm committed
74 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
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;
100
        temp.append(pretty.prettyType(symbol->type()).toUtf8());
101 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
        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;
144 145
        Scope::iterator it = scope->memberBegin();
        while (it != scope->memberEnd() && *it != symbol) {
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
            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
168
namespace {
169

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

CppFindReferences::~CppFindReferences()
{
}

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

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

    return references;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

namespace {
408
class UidSymbolFinder : public SymbolVisitor
409 410
{
public:
411
    UidSymbolFinder(const QList<QByteArray> &uid) : m_uid(uid), m_index(0), m_result(0) { }
412 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
    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:
440
    QList<QByteArray> m_uid;
441 442 443 444 445
    int m_index;
    Symbol *m_result;
};
}

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

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

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

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

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

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

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

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

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

namespace {

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

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

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

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

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

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

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

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

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

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

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

} // end of anonymous namespace

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

    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
}