cppfindreferences.cpp 25.6 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
Roberto Raggi's avatar
Roberto Raggi committed
2
**
3
** Copyright (C) 2014 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
** 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
Eike Ziller's avatar
Eike Ziller committed
13 14
** conditions see http://www.qt.io/licensing.  For further information
** 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 25 26
**
** 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
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 318 319 320 321 322 323 324 325 326
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)
327
{
328
    Overview overview;
329
    Core::SearchResult *search = Core::SearchResultWindow::instance()->startNewSearch(tr("C++ Usages:"),
330
                                                QString(),
331
                                                overview.prettyName(context.fullyQualifiedName(symbol)),
332 333
                                                replace ? Core::SearchResultWindow::SearchAndReplace
                                                        : Core::SearchResultWindow::SearchOnly,
334
                                                Core::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 350
void CppFindReferences::renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context,
                                     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
void CppFindReferences::findAll_helper(Core::SearchResult *search, CPlusPlus::Symbol *symbol,
360
                                       const CPlusPlus::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
    Core::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<Core::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
        Core::SearchResultWindow::instance()->hide();
Roberto Raggi's avatar
Roberto Raggi committed
390 391 392
    }
}

393 394
void CppFindReferences::searchAgain()
{
395
    Core::SearchResult *search = qobject_cast<Core::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 409 410 411
}

namespace {
class SymbolFinder : public SymbolVisitor
{
public:
412
    SymbolFinder(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 448
CPlusPlus::Symbol *CppFindReferences::findSymbol(const CppFindReferencesParameters &parameters,
                                      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
    SymbolFinder 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
    Core::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
    Core::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
    Core::SearchResult *search = qobject_cast<Core::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
    Core::SearchResult *search = qobject_cast<Core::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 Core::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 621
    const Utils::FileName sourceFile = Utils::FileName::fromString(macro.fileName());
    Utils::FileNameList files(sourceFile);
622
    files += snapshot.filesDependingOn(sourceFile);
Christian Kamm's avatar
Christian Kamm committed
623 624 625
    files.removeDuplicates();

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

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

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

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

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

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

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

    // add the macro definition itself
    {
668 669
        const QByteArray &source = getSource(Utils::FileName::fromString(macro.fileName()),
                                             workingCopy);
670 671 672 673 674
        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
675 676 677
    }

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

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

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

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