cppfindreferences.cpp 26.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
**
Eike Ziller's avatar
Eike Ziller committed
7
** Contact: http://www.qt-project.org/
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.
**
Roberto Raggi's avatar
Roberto Raggi committed
28 29 30 31 32 33 34
**
**************************************************************************/

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

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

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

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

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

70 71
#include <functional>

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

Christian Kamm's avatar
Christian Kamm committed
75
static QString getSource(const QString &fileName,
76
                         const CppModelManagerInterface::WorkingCopy &workingCopy)
Christian Kamm's avatar
Christian Kamm committed
77 78 79 80
{
    if (workingCopy.contains(fileName)) {
        return workingCopy.source(fileName);
    } else {
81 82 83 84 85 86 87 88
        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
89

90
        return fileContents;
Christian Kamm's avatar
Christian Kamm committed
91 92 93
    }
}

Roberto Raggi's avatar
Roberto Raggi committed
94
namespace {
95

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

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

    QList<Usage> operator()(const QString &fileName)
    {
        QList<Usage> usages;
120 121
        if (future->isPaused())
            future->waitForResume();
122 123
        if (future->isCanceled())
            return usages;
124 125
        const Identifier *symbolId = symbol->identifier();

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

135
        if (symbolDocument && fileName == symbolDocument->fileName()) {
136
            doc = symbolDocument;
137
        } else {
138
            source = snapshot.preprocessedCode(unpreprocessedSource, fileName);
139 140 141
            doc = snapshot.documentFromSource(source, fileName);
            doc->tokenize();
        }
142 143 144

        Control *control = doc->control();
        if (control->findIdentifier(symbolId->chars(), symbolId->size()) != 0) {
145 146
            if (doc != symbolDocument)
                doc->check();
147

148
            FindUsages process(unpreprocessedSource.toUtf8(), doc, snapshot);
149
            process(symbol);
150

151 152 153
            usages = process.usages();
        }

154 155
        if (future->isPaused())
            future->waitForResume();
156 157 158 159
        return usages;
    }
};

Roberto Raggi's avatar
Roberto Raggi committed
160
class UpdateUI: public std::binary_function<QList<Usage> &, QList<Usage>, void>
161 162 163 164
{
    QFutureInterface<Usage> *future;

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

167
    void operator()(QList<Usage> &, const QList<Usage> &usages)
168 169 170 171 172 173 174 175
    {
        foreach (const Usage &u, usages)
            future->reportResult(u);

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

Roberto Raggi's avatar
Roberto Raggi committed
176 177
} // end of anonymous namespace

178
CppFindReferences::CppFindReferences(CppModelManagerInterface *modelManager)
Roberto Raggi's avatar
Roberto Raggi committed
179
    : QObject(modelManager),
180
      _modelManager(modelManager)
Roberto Raggi's avatar
Roberto Raggi committed
181 182 183 184 185 186 187
{
}

CppFindReferences::~CppFindReferences()
{
}

188
QList<int> CppFindReferences::references(Symbol *symbol, const LookupContext &context) const
Roberto Raggi's avatar
Roberto Raggi committed
189 190 191
{
    QList<int> references;

192
    FindUsages findUsages(context);
Roberto Raggi's avatar
Roberto Raggi committed
193 194 195 196 197 198
    findUsages(symbol);
    references = findUsages.references();

    return references;
}

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

208 209
    const Snapshot snapshot = context.snapshot();

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

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

            Control *control = doc->control();

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

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

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

242 243 244 245 246 247 248 249 250 251
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)
252
{
253
    Overview overview;
254
    Find::SearchResult *search = Find::SearchResultWindow::instance()->startNewSearch(tr("C++ Usages:"),
255 256
                                                QString(),
                                                overview(context.fullyQualifiedName(symbol)),
257 258 259 260 261 262
                                                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>)));
263
    connect(search, SIGNAL(paused(bool)), this, SLOT(setPaused(bool)));
264 265 266 267 268 269 270
    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);
271 272
}

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

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

294
    Find::SearchResultWindow::instance()->popup(Core::IOutputPane::ModeSwitch | Core::IOutputPane::WithFocus);
295
    const CppModelManagerInterface::WorkingCopy workingCopy = _modelManager->workingCopy();
Roberto Raggi's avatar
Roberto Raggi committed
296
    QFuture<Usage> result;
297 298
    result = QtConcurrent::run(&find_helper, workingCopy,
                               parameters.context, this, parameters.symbol);
299 300
    createWatcher(result, search);

hjk's avatar
hjk committed
301
    Core::ProgressManager *progressManager = Core::ICore::progressManager();
302
    Core::FutureProgress *progress = progressManager->addTask(result, tr("Searching"),
303
                                                              CppTools::Constants::TASK_SEARCH);
Roberto Raggi's avatar
Roberto Raggi committed
304

305
    connect(progress, SIGNAL(clicked()), search, SLOT(popup()));
Roberto Raggi's avatar
Roberto Raggi committed
306 307
}

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

318 319 320 321 322
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
323
    search->restart();
324
    if (!findSymbol(&parameters, snapshot)) {
Eike Ziller's avatar
Eike Ziller committed
325
        search->finishSearch(false);
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 482 483 484 485
        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
486
void CppFindReferences::displayResults(int first, int last)
Roberto Raggi's avatar
Roberto Raggi committed
487
{
488 489 490 491 492
    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();
493 494
        return;
    }
Roberto Raggi's avatar
Roberto Raggi committed
495
    for (int index = first; index != last; ++index) {
496 497 498 499 500 501
        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
502
    }
Roberto Raggi's avatar
Roberto Raggi committed
503 504 505 506
}

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

514 515
void CppFindReferences::cancel()
{
516 517 518 519 520
    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();
521 522
}

523 524 525 526 527 528 529 530 531 532
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);
}

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

Christian Kamm's avatar
Christian Kamm committed
546 547 548 549 550

namespace {

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

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

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

        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(),
586
                                    use.begin() - lineStart, useMacro.name().length()));
Christian Kamm's avatar
Christian Kamm committed
587 588 589
            }
        }

590 591
        if (future->isPaused())
            future->waitForResume();
Christian Kamm's avatar
Christian Kamm committed
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 622 623 624 625
        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,
626
                                 const CppModelManagerInterface::WorkingCopy workingCopy,
627 628 629
                                 const Snapshot snapshot,
                                 CppFindReferences *findRefs,
                                 const Macro macro)
Christian Kamm's avatar
Christian Kamm committed
630
{
631 632 633
    // ensure the dependency table is updated
    DependencyTable dependencies = findRefs->updateDependencyTable(snapshot);

Christian Kamm's avatar
Christian Kamm committed
634 635
    const QString& sourceFile = macro.fileName();
    QStringList files(sourceFile);
636
    files += dependencies.filesDependingOn(sourceFile);
Christian Kamm's avatar
Christian Kamm committed
637 638 639 640
    files.removeDuplicates();

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

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

void CppFindReferences::findMacroUses(const Macro &macro)
652 653 654 655 656
{
    findMacroUses(macro, QString(), false);
}

void CppFindReferences::findMacroUses(const Macro &macro, const QString &replacement, bool replace)
Christian Kamm's avatar
Christian Kamm committed
657
{
658
    Find::SearchResult *search = Find::SearchResultWindow::instance()->startNewSearch(
659 660 661
                tr("C++ Macro Usages:"),
                QString(),
                macro.name(),
662 663 664 665 666 667 668
                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>)));
Christian Kamm's avatar
Christian Kamm committed
669

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

672
    connect(search, SIGNAL(activated(Find::SearchResultItem)),
Christian Kamm's avatar
Christian Kamm committed
673
            this, SLOT(openEditor(Find::SearchResultItem)));
674
    connect(search, SIGNAL(cancelled()), this, SLOT(cancel()));
675
    connect(search, SIGNAL(paused(bool)), this, SLOT(setPaused(bool)));
Christian Kamm's avatar
Christian Kamm committed
676 677

    const Snapshot snapshot = _modelManager->snapshot();
678
    const CppModelManagerInterface::WorkingCopy workingCopy = _modelManager->workingCopy();
Christian Kamm's avatar
Christian Kamm committed
679 680 681 682 683

    // add the macro definition itself
    {
        // ### FIXME: Encoding?
        const QByteArray &source = getSource(macro.fileName(), workingCopy).toLatin1();
684 685 686 687 688
        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);
689
        search->addResult(macro.fileName(), macro.line(), line,
690
                          line.indexOf(macro.name()), macro.name().length());
Christian Kamm's avatar
Christian Kamm committed
691 692 693
    }

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

hjk's avatar
hjk committed
697
    Core::ProgressManager *progressManager = Core::ICore::progressManager();
698
    Core::FutureProgress *progress = progressManager->addTask(result, tr("Searching"),
Christian Kamm's avatar
Christian Kamm committed
699
                                                              CppTools::Constants::TASK_SEARCH);
700
    connect(progress, SIGNAL(clicked()), search, SLOT(popup()));
Christian Kamm's avatar
Christian Kamm committed
701 702
}

703 704 705 706 707 708
void CppFindReferences::renameMacroUses(const Macro &macro, const QString &replacement)
{
    const QString textToReplace = replacement.isEmpty() ? macro.name() : replacement;
    findMacroUses(macro, textToReplace, true);
}

709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733
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;
}
734 735 736 737 738 739 740 741

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
742
    watcher->setFuture(future);
743
}