cppfindreferences.cpp 25.1 KB
Newer Older
Roberto Raggi's avatar
Roberto Raggi committed
1 2 3 4
/**************************************************************************
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
Roberto Raggi's avatar
Roberto Raggi committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
Roberto Raggi's avatar
Roberto Raggi committed
8 9 10 11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** 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.
Roberto Raggi's avatar
Roberto Raggi committed
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
Roberto Raggi's avatar
Roberto Raggi committed
30 31 32 33 34 35 36
**
**************************************************************************/

#include "cppfindreferences.h"
#include "cpptoolsconstants.h"

#include <texteditor/basetexteditor.h>
37
#include <texteditor/basefilefind.h>
Roberto Raggi's avatar
Roberto Raggi committed
38 39 40
#include <find/searchresultwindow.h>
#include <extensionsystem/pluginmanager.h>
#include <utils/filesearch.h>
41
#include <utils/fileutils.h>
42
#include <utils/qtcassert.h>
Roberto Raggi's avatar
Roberto Raggi committed
43
#include <coreplugin/progressmanager/progressmanager.h>
44
#include <coreplugin/progressmanager/futureprogress.h>
45
#include <coreplugin/editormanager/editormanager.h>
Roberto Raggi's avatar
Roberto Raggi committed
46
#include <coreplugin/icore.h>
47
#include <coreplugin/infobar.h>
Roberto Raggi's avatar
Roberto Raggi committed
48 49 50 51 52 53 54

#include <ASTVisitor.h>
#include <AST.h>
#include <Control.h>
#include <Literals.h>
#include <TranslationUnit.h>
#include <Symbols.h>
55 56
#include <Names.h>
#include <Scope.h>
Roberto Raggi's avatar
Roberto Raggi committed
57

58
#include <cpptools/ModelManagerInterface.h>
Roberto Raggi's avatar
Roberto Raggi committed
59
#include <cplusplus/CppDocument.h>
60
#include <cplusplus/Overview.h>
61
#include <cplusplus/FindUsages.h>
Roberto Raggi's avatar
Roberto Raggi committed
62

63 64 65 66 67 68
#include <QTime>
#include <QTimer>
#include <QtConcurrentRun>
#include <QtConcurrentMap>
#include <QDir>
#include <QApplication>
69
#include <utils/runextensions.h>
Roberto Raggi's avatar
Roberto Raggi committed
70

71 72
#include <functional>

Roberto Raggi's avatar
Roberto Raggi committed
73 74 75
using namespace CppTools::Internal;
using namespace CPlusPlus;

Christian Kamm's avatar
Christian Kamm committed
76
static QString getSource(const QString &fileName,
77
                         const CppModelManagerInterface::WorkingCopy &workingCopy)
Christian Kamm's avatar
Christian Kamm committed
78 79 80 81
{
    if (workingCopy.contains(fileName)) {
        return workingCopy.source(fileName);
    } else {
82
        Utils::FileReader reader;
83
        if (!reader.fetch(fileName, QFile::Text)) // ### FIXME error reporting
Christian Kamm's avatar
Christian Kamm committed
84 85
            return QString();

86
        return QString::fromLocal8Bit(reader.data()); // ### FIXME encoding
Christian Kamm's avatar
Christian Kamm committed
87 88 89
    }
}

Roberto Raggi's avatar
Roberto Raggi committed
90
namespace {
91

Roberto Raggi's avatar
Roberto Raggi committed
92
class ProcessFile: public std::unary_function<QString, QList<Usage> >
93
{
94
    const CppModelManagerInterface::WorkingCopy workingCopy;
95
    const Snapshot snapshot;
Erik Verbruggen's avatar
Erik Verbruggen committed
96
    Document::Ptr symbolDocument;
97
    Symbol *symbol;
98
    QFutureInterface<Usage> *future;
99 100

public:
101
    ProcessFile(const CppModelManagerInterface::WorkingCopy &workingCopy,
102
                const Snapshot snapshot,
Erik Verbruggen's avatar
Erik Verbruggen committed
103
                Document::Ptr symbolDocument,
104 105 106 107 108 109 110
                Symbol *symbol,
                QFutureInterface<Usage> *future)
        : workingCopy(workingCopy),
          snapshot(snapshot),
          symbolDocument(symbolDocument),
          symbol(symbol),
          future(future)
111 112 113 114 115
    { }

    QList<Usage> operator()(const QString &fileName)
    {
        QList<Usage> usages;
116 117
        if (future->isPaused())
            future->waitForResume();
118 119
        if (future->isCanceled())
            return usages;
120 121
        const Identifier *symbolId = symbol->identifier();

122
        if (Document::Ptr previousDoc = snapshot.document(fileName)) {
123 124 125 126
            Control *control = previousDoc->control();
            if (! control->findIdentifier(symbolId->chars(), symbolId->size()))
                return usages; // skip this document, it's not using symbolId.
        }
127 128
        Document::Ptr doc;
        QByteArray source;
129
        const QString unpreprocessedSource = getSource(fileName, workingCopy);
130

131
        if (symbolDocument && fileName == symbolDocument->fileName()) {
132
            doc = symbolDocument;
133
        } else {
134
            source = snapshot.preprocessedCode(unpreprocessedSource, fileName);
135 136 137
            doc = snapshot.documentFromSource(source, fileName);
            doc->tokenize();
        }
138 139 140

        Control *control = doc->control();
        if (control->findIdentifier(symbolId->chars(), symbolId->size()) != 0) {
141 142
            if (doc != symbolDocument)
                doc->check();
143

144
            FindUsages process(unpreprocessedSource.toUtf8(), doc, snapshot);
145
            process(symbol);
146

147 148 149
            usages = process.usages();
        }

150 151
        if (future->isPaused())
            future->waitForResume();
152 153 154 155
        return usages;
    }
};

Roberto Raggi's avatar
Roberto Raggi committed
156
class UpdateUI: public std::binary_function<QList<Usage> &, QList<Usage>, void>
157 158 159 160
{
    QFutureInterface<Usage> *future;

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

163
    void operator()(QList<Usage> &, const QList<Usage> &usages)
164 165 166 167 168 169 170 171
    {
        foreach (const Usage &u, usages)
            future->reportResult(u);

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

Roberto Raggi's avatar
Roberto Raggi committed
172 173
} // end of anonymous namespace

174
CppFindReferences::CppFindReferences(CppModelManagerInterface *modelManager)
Roberto Raggi's avatar
Roberto Raggi committed
175
    : QObject(modelManager),
176
      _modelManager(modelManager)
Roberto Raggi's avatar
Roberto Raggi committed
177 178 179 180 181 182 183
{
}

CppFindReferences::~CppFindReferences()
{
}

184
QList<int> CppFindReferences::references(Symbol *symbol, const LookupContext &context) const
Roberto Raggi's avatar
Roberto Raggi committed
185 186 187
{
    QList<int> references;

188
    FindUsages findUsages(context);
Roberto Raggi's avatar
Roberto Raggi committed
189 190 191 192 193 194
    findUsages(symbol);
    references = findUsages.references();

    return references;
}

195
static void find_helper(QFutureInterface<Usage> &future,
196
                        const CppModelManagerInterface::WorkingCopy workingCopy,
197
                        const LookupContext context,
198
                        CppFindReferences *findRefs,
199
                        Symbol *symbol)
Roberto Raggi's avatar
Roberto Raggi committed
200
{
Roberto Raggi's avatar
Roberto Raggi committed
201
    const Identifier *symbolId = symbol->identifier();
202
    QTC_ASSERT(symbolId != 0, return);
203

204 205
    const Snapshot snapshot = context.snapshot();

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

209 210
    if (symbol->isClass() || symbol->isForwardClassDeclaration() || (symbol->enclosingScope() && ! symbol->isStatic() &&
                                                                     symbol->enclosingScope()->isNamespace())) {
211
        foreach (const Document::Ptr &doc, context.snapshot()) {
212 213 214 215 216 217 218 219 220
            if (doc->fileName() == sourceFile)
                continue;

            Control *control = doc->control();

            if (control->findIdentifier(symbolId->chars(), symbolId->size()))
                files.append(doc->fileName());
        }
    } else {
221
        DependencyTable dependencyTable = findRefs->updateDependencyTable(snapshot);
222
        files += dependencyTable.filesDependingOn(sourceFile);
223
    }
224
    files.removeDuplicates();
Roberto Raggi's avatar
Roberto Raggi committed
225 226 227

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

228
    ProcessFile process(workingCopy, snapshot, context.thisDocument(), symbol, &future);
Roberto Raggi's avatar
Roberto Raggi committed
229
    UpdateUI reduce(&future);
230 231 232
    // 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();
233
    QtConcurrent::blockingMappedReduced<QList<Usage> > (files, process, reduce);
234
    QThreadPool::globalInstance()->reserveThread();
Roberto Raggi's avatar
Roberto Raggi committed
235 236 237
    future.setProgressValue(files.size());
}

238 239 240 241 242 243 244 245 246 247
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)
248
{
249
    Overview overview;
250
    Find::SearchResult *search = Find::SearchResultWindow::instance()->startNewSearch(tr("C++ Usages:"),
251 252
                                                QString(),
                                                overview(context.fullyQualifiedName(symbol)),
253 254 255 256 257 258
                                                replace ? Find::SearchResultWindow::SearchAndReplace
                                                        : Find::SearchResultWindow::SearchOnly,
                                                QLatin1String("CppEditor"));
    search->setTextToReplace(replacement);
    connect(search, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>)),
            SLOT(onReplaceButtonClicked(QString,QList<Find::SearchResultItem>)));
259
    connect(search, SIGNAL(paused(bool)), this, SLOT(setPaused(bool)));
260 261 262 263 264 265 266
    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);
267 268
}

269 270
void CppFindReferences::renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context,
                                     const QString &replacement)
Roberto Raggi's avatar
Roberto Raggi committed
271
{
Roberto Raggi's avatar
Roberto Raggi committed
272
    if (const Identifier *id = symbol->identifier()) {
273 274
        const QString textToReplace = replacement.isEmpty()
                ? QString::fromUtf8(id->chars(), id->size()) : replacement;
275
        findUsages(symbol, context, textToReplace, true);
276
    }
277 278
}

279
void CppFindReferences::findAll_helper(Find::SearchResult *search)
280
{
281 282
    CppFindReferencesParameters parameters = search->userData().value<CppFindReferencesParameters>();
    if (!(parameters.symbol && parameters.symbol->identifier())) {
Eike Ziller's avatar
Eike Ziller committed
283
        search->finishSearch(false);
284
        return;
285 286 287 288
    }
    connect(search, SIGNAL(cancelled()), this, SLOT(cancel()));
    connect(search, SIGNAL(activated(Find::SearchResultItem)),
            this, SLOT(openEditor(Find::SearchResultItem)));
289

290
    Find::SearchResultWindow::instance()->popup(true);
291
    const CppModelManagerInterface::WorkingCopy workingCopy = _modelManager->workingCopy();
Roberto Raggi's avatar
Roberto Raggi committed
292
    QFuture<Usage> result;
293 294
    result = QtConcurrent::run(&find_helper, workingCopy,
                               parameters.context, this, parameters.symbol);
295 296
    createWatcher(result, search);

hjk's avatar
hjk committed
297
    Core::ProgressManager *progressManager = Core::ICore::progressManager();
298
    Core::FutureProgress *progress = progressManager->addTask(result, tr("Searching"),
299
                                                              CppTools::Constants::TASK_SEARCH);
Roberto Raggi's avatar
Roberto Raggi committed
300

301
    connect(progress, SIGNAL(clicked()), search, SLOT(popup()));
Roberto Raggi's avatar
Roberto Raggi committed
302 303
}

Roberto Raggi's avatar
Roberto Raggi committed
304 305 306
void CppFindReferences::onReplaceButtonClicked(const QString &text,
                                               const QList<Find::SearchResultItem> &items)
{
307 308 309
    const QStringList fileNames = TextEditor::BaseFileFind::replaceAll(text, items);
    if (!fileNames.isEmpty()) {
        _modelManager->updateSourceFiles(fileNames);
310
        Find::SearchResultWindow::instance()->hide();
Roberto Raggi's avatar
Roberto Raggi committed
311 312 313
    }
}

314 315 316 317 318
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
319
    search->restart();
320
    if (!findSymbol(&parameters, snapshot)) {
Eike Ziller's avatar
Eike Ziller committed
321
        search->finishSearch(false);
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 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 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
        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:
    SymbolFinder(const QStringList &uid) : m_uid(uid), m_index(0), m_result(0) { }
    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:
    QStringList m_uid;
    int m_index;
    Symbol *m_result;
};
}

bool CppFindReferences::findSymbol(CppFindReferencesParameters *parameters,
                                      const Snapshot &snapshot)
{
    QString symbolFile = QLatin1String(parameters->symbol->fileName());
    if (!snapshot.contains(symbolFile)) {
        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());
    const QByteArray &preprocessedCode =
            snapshot.preprocessedCode(source, newSymbolDocument->fileName());
    Document::Ptr doc =
            snapshot.documentFromSource(preprocessedCode, newSymbolDocument->fileName());
    doc->check();

    // construct id of old symbol
    QStringList uid;
    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
482
void CppFindReferences::displayResults(int first, int last)
Roberto Raggi's avatar
Roberto Raggi committed
483
{
484 485 486 487 488
    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();
489 490
        return;
    }
Roberto Raggi's avatar
Roberto Raggi committed
491
    for (int index = first; index != last; ++index) {
492 493 494 495 496 497
        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
498
    }
Roberto Raggi's avatar
Roberto Raggi committed
499 500 501 502
}

void CppFindReferences::searchFinished()
{
503 504 505
    QFutureWatcher<Usage> *watcher = static_cast<QFutureWatcher<Usage> *>(sender());
    Find::SearchResult *search = m_watchers.value(watcher);
    if (search)
Eike Ziller's avatar
Eike Ziller committed
506
        search->finishSearch(watcher->isCanceled());
507
    m_watchers.remove(watcher);
Roberto Raggi's avatar
Roberto Raggi committed
508 509
}

510 511
void CppFindReferences::cancel()
{
512 513 514 515 516
    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();
517 518
}

519 520 521 522 523 524 525 526 527 528
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);
}

529
void CppFindReferences::openEditor(const Find::SearchResultItem &item)
Roberto Raggi's avatar
Roberto Raggi committed
530
{
531
    if (item.path.size() > 0) {
532 533 534 535
        TextEditor::BaseTextEditorWidget::openEditorAt(QDir::fromNativeSeparators(item.path.first()),
                                                       item.lineNumber, item.textMarkPos,
                                                       Core::Id(),
                                                       Core::EditorManager::ModeSwitch);
536
    } else {
hjk's avatar
hjk committed
537 538
        Core::EditorManager::openEditor(QDir::fromNativeSeparators(item.text),
                                        Core::Id(), Core::EditorManager::ModeSwitch);
539
    }
Roberto Raggi's avatar
Roberto Raggi committed
540 541
}

Christian Kamm's avatar
Christian Kamm committed
542 543 544 545 546

namespace {

class FindMacroUsesInFile: public std::unary_function<QString, QList<Usage> >
{
547
    const CppModelManagerInterface::WorkingCopy workingCopy;
Christian Kamm's avatar
Christian Kamm committed
548 549
    const Snapshot snapshot;
    const Macro &macro;
550
    QFutureInterface<Usage> *future;
Christian Kamm's avatar
Christian Kamm committed
551 552

public:
553
    FindMacroUsesInFile(const CppModelManagerInterface::WorkingCopy &workingCopy,
Christian Kamm's avatar
Christian Kamm committed
554
                        const Snapshot snapshot,
555 556 557
                        const Macro &macro,
                        QFutureInterface<Usage> *future)
        : workingCopy(workingCopy), snapshot(snapshot), macro(macro), future(future)
Christian Kamm's avatar
Christian Kamm committed
558 559 560 561 562
    { }

    QList<Usage> operator()(const QString &fileName)
    {
        QList<Usage> usages;
563 564
        if (future->isPaused())
            future->waitForResume();
565 566
        if (future->isCanceled())
            return usages;
Christian Kamm's avatar
Christian Kamm committed
567 568 569 570 571 572 573 574 575 576 577 578 579 580 581

        const Document::Ptr &doc = snapshot.document(fileName);
        QByteArray source;

        foreach (const Document::MacroUse &use, doc->macroUses()) {
            const Macro &useMacro = use.macro();
            if (useMacro.line() == macro.line()
                && useMacro.fileName() == macro.fileName())
                {
                if (source.isEmpty())
                    source = getSource(fileName, workingCopy).toLatin1(); // ### FIXME: Encoding?

                unsigned lineStart;
                const QString &lineSource = matchingLine(use.begin(), source, &lineStart);
                usages.append(Usage(fileName, lineSource, use.beginLine(),
582
                                    use.begin() - lineStart, useMacro.name().length()));
Christian Kamm's avatar
Christian Kamm committed
583 584 585
            }
        }

586 587
        if (future->isPaused())
            future->waitForResume();
Christian Kamm's avatar
Christian Kamm committed
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
        return usages;
    }

    // ### FIXME: Pretty close to FindUsages::matchingLine.
    static QString matchingLine(unsigned position, const QByteArray &source,
                                unsigned *lineStart = 0)
    {
        const char *beg = source.constData();
        const char *start = beg + position;
        for (; start != beg - 1; --start) {
            if (*start == '\n')
                break;
        }

        ++start;

        const char *end = start + 1;
        for (; *end; ++end) {
            if (*end == '\n')
                break;
        }

        if (lineStart)
            *lineStart = start - beg;

        // ### FIXME: Encoding?
        const QString matchingLine = QString::fromUtf8(start, end - start);
        return matchingLine;
    }
};

} // end of anonymous namespace

static void findMacroUses_helper(QFutureInterface<Usage> &future,
622
                                 const CppModelManagerInterface::WorkingCopy workingCopy,
623 624 625
                                 const Snapshot snapshot,
                                 CppFindReferences *findRefs,
                                 const Macro macro)
Christian Kamm's avatar
Christian Kamm committed
626
{
627 628 629
    // ensure the dependency table is updated
    DependencyTable dependencies = findRefs->updateDependencyTable(snapshot);

Christian Kamm's avatar
Christian Kamm committed
630 631
    const QString& sourceFile = macro.fileName();
    QStringList files(sourceFile);
632
    files += dependencies.filesDependingOn(sourceFile);
Christian Kamm's avatar
Christian Kamm committed
633 634 635 636
    files.removeDuplicates();

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

637
    FindMacroUsesInFile process(workingCopy, snapshot, macro, &future);
Christian Kamm's avatar
Christian Kamm committed
638
    UpdateUI reduce(&future);
639 640 641
    // 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
642
    QtConcurrent::blockingMappedReduced<QList<Usage> > (files, process, reduce);
643
    QThreadPool::globalInstance()->reserveThread();
Christian Kamm's avatar
Christian Kamm committed
644 645 646 647 648
    future.setProgressValue(files.size());
}

void CppFindReferences::findMacroUses(const Macro &macro)
{
649
    Find::SearchResult *search = Find::SearchResultWindow::instance()->startNewSearch(
650 651 652 653
                tr("C++ Macro Usages:"),
                QString(),
                macro.name(),
                Find::SearchResultWindow::SearchOnly);
Christian Kamm's avatar
Christian Kamm committed
654

655
    Find::SearchResultWindow::instance()->popup(true);
Christian Kamm's avatar
Christian Kamm committed
656

657
    connect(search, SIGNAL(activated(Find::SearchResultItem)),
Christian Kamm's avatar
Christian Kamm committed
658
            this, SLOT(openEditor(Find::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 662

    const Snapshot snapshot = _modelManager->snapshot();
663
    const CppModelManagerInterface::WorkingCopy workingCopy = _modelManager->workingCopy();
Christian Kamm's avatar
Christian Kamm committed
664 665 666 667 668

    // add the macro definition itself
    {
        // ### FIXME: Encoding?
        const QByteArray &source = getSource(macro.fileName(), workingCopy).toLatin1();
669 670 671 672 673
        int lineBegin = source.lastIndexOf('\n', macro.offset()) + 1;
        int lineEnd = source.indexOf('\n', macro.offset());
        if (lineEnd == -1)
            lineEnd = source.length();
        const QByteArray line = source.mid(lineBegin, lineEnd - lineBegin);
674
        search->addResult(macro.fileName(), macro.line(), line,
675
                          line.indexOf(macro.name()), macro.name().length());
Christian Kamm's avatar
Christian Kamm committed
676 677 678
    }

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

hjk's avatar
hjk committed
682
    Core::ProgressManager *progressManager = Core::ICore::progressManager();
683
    Core::FutureProgress *progress = progressManager->addTask(result, tr("Searching"),
Christian Kamm's avatar
Christian Kamm committed
684
                                                              CppTools::Constants::TASK_SEARCH);
685
    connect(progress, SIGNAL(clicked()), search, SLOT(popup()));
Christian Kamm's avatar
Christian Kamm committed
686 687
}

688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
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;
}
713 714 715 716 717 718 719 720

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
721
    watcher->setFuture(future);
722
}