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

#include "cppfindreferences.h"
31

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

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

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

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

49 50
#include <functional>

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

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

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

74
namespace {
75

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

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

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

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

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

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

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

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

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

138
class UpdateUI: public std::binary_function<QList<Usage> &, QList<Usage>, void>
139 140 141 142
{
    QFutureInterface<Usage> *future;

public:
143
    UpdateUI(QFutureInterface<Usage> *future): future(future) {}
144

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

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

154 155
} // end of anonymous namespace

156
CppFindReferences::CppFindReferences(CppModelManagerInterface *modelManager)
157
    : QObject(modelManager),
158
      _modelManager(modelManager)
159 160 161 162 163 164 165
{
}

CppFindReferences::~CppFindReferences()
{
}

166
QList<int> CppFindReferences::references(Symbol *symbol, const LookupContext &context) const
167 168 169
{
    QList<int> references;

170
    FindUsages findUsages(context);
171 172 173 174 175 176
    findUsages(symbol);
    references = findUsages.references();

    return references;
}

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

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

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

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

            Control *control = doc->control();

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

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

210
    ProcessFile process(workingCopy, snapshot, context.thisDocument(), symbol, &future);
211
    UpdateUI reduce(&future);
212 213 214
    // 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();
215
    QtConcurrent::blockingMappedReduced<QList<Usage> > (files, process, reduce);
216
    QThreadPool::globalInstance()->reserveThread();
Roberto Raggi's avatar
Roberto Raggi committed
217 218 219
    future.setProgressValue(files.size());
}

220 221 222 223 224 225 226 227 228 229
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)
230
{
231
    Overview overview;
232
    Find::SearchResult *search = Find::SearchResultWindow::instance()->startNewSearch(tr("C++ Usages:"),
233
                                                QString(),
234
                                                overview.prettyName(context.fullyQualifiedName(symbol)),
235 236 237 238
                                                replace ? Find::SearchResultWindow::SearchAndReplace
                                                        : Find::SearchResultWindow::SearchOnly,
                                                QLatin1String("CppEditor"));
    search->setTextToReplace(replacement);
239 240
    connect(search, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>,bool)),
            SLOT(onReplaceButtonClicked(QString,QList<Find::SearchResultItem>,bool)));
241
    connect(search, SIGNAL(paused(bool)), this, SLOT(setPaused(bool)));
242 243 244 245 246 247 248
    search->setSearchAgainSupported(true);
    connect(search, SIGNAL(searchAgainRequested()), this, SLOT(searchAgain()));
    CppFindReferencesParameters parameters;
    parameters.context = context;
    parameters.symbol = symbol;
    search->setUserData(qVariantFromValue(parameters));
    findAll_helper(search);
249 250
}

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

261
void CppFindReferences::findAll_helper(Find::SearchResult *search)
262
{
263 264
    CppFindReferencesParameters parameters = search->userData().value<CppFindReferencesParameters>();
    if (!(parameters.symbol && parameters.symbol->identifier())) {
265
        search->finishSearch(false);
266
        return;
267 268 269 270
    }
    connect(search, SIGNAL(cancelled()), this, SLOT(cancel()));
    connect(search, SIGNAL(activated(Find::SearchResultItem)),
            this, SLOT(openEditor(Find::SearchResultItem)));
271

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

hjk's avatar
hjk committed
279
    Core::ProgressManager *progressManager = Core::ICore::progressManager();
280
    Core::FutureProgress *progress = progressManager->addTask(result, tr("Searching"),
281
                                                              QLatin1String(CppTools::Constants::TASK_SEARCH));
Roberto Raggi's avatar
Roberto Raggi committed
282

283
    connect(progress, SIGNAL(clicked()), search, SLOT(popup()));
Roberto Raggi's avatar
Roberto Raggi committed
284 285
}

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

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

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

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

namespace {
class SymbolFinder : public SymbolVisitor
{
public:
395
    SymbolFinder(const QList<QByteArray> &uid) : m_uid(uid), m_index(0), m_result(0) { }
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
    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:
424
    QList<QByteArray> m_uid;
425 426 427 428 429 430 431 432 433
    int m_index;
    Symbol *m_result;
};
}

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

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

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

Roberto Raggi's avatar
Roberto Raggi committed
462
void CppFindReferences::displayResults(int first, int last)
Roberto Raggi's avatar
Roberto Raggi committed
463
{
464 465 466 467 468
    QFutureWatcher<Usage> *watcher = static_cast<QFutureWatcher<Usage> *>(sender());
    Find::SearchResult *search = m_watchers.value(watcher);
    if (!search) {
        // search was deleted while it was running
        watcher->cancel();
469 470
        return;
    }
Roberto Raggi's avatar
Roberto Raggi committed
471
    for (int index = first; index != last; ++index) {
472 473 474 475 476 477
        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
478
    }
Roberto Raggi's avatar
Roberto Raggi committed
479 480 481 482
}

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

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

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

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

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

namespace {

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

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

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

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

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

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

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

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

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

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

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

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

} // end of anonymous namespace

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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