cppfindreferences.cpp 25.7 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>
Roberto Raggi's avatar
Roberto Raggi committed
68

69 70
#include <functional>

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

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

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

Roberto Raggi's avatar
Roberto Raggi committed
88
namespace {
89

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

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

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

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

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

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

142
            FindUsages process(unpreprocessedSource.toUtf8(), doc, snapshot);
143
            process(symbol);
144

145 146 147
            usages = process.usages();
        }

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

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

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

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

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

Roberto Raggi's avatar
Roberto Raggi committed
170 171
} // end of anonymous namespace

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

CppFindReferences::~CppFindReferences()
{
}

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

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

    return references;
}

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

202 203
    const Snapshot snapshot = context.snapshot();

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

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

            Control *control = doc->control();

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

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

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

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

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

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

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

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

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

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

312 313 314 315 316
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
317
    search->restart();
318
    if (!findSymbol(&parameters, snapshot)) {
Eike Ziller's avatar
Eike Ziller committed
319
        search->finishSearch(false);
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 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
        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
480
void CppFindReferences::displayResults(int first, int last)
Roberto Raggi's avatar
Roberto Raggi committed
481
{
482 483 484 485 486
    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();
487 488
        return;
    }
Roberto Raggi's avatar
Roberto Raggi committed
489
    for (int index = first; index != last; ++index) {
490 491 492 493 494 495
        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
496
    }
Roberto Raggi's avatar
Roberto Raggi committed
497 498 499 500
}

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

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

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

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

Christian Kamm's avatar
Christian Kamm committed
540 541 542 543 544

namespace {

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

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

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

        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(),
580
                                    use.begin() - lineStart, useMacro.name().length()));
Christian Kamm's avatar
Christian Kamm committed
581 582 583
            }
        }

584 585
        if (future->isPaused())
            future->waitForResume();
Christian Kamm's avatar
Christian Kamm committed
586 587 588 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
        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,
620
                                 const CppModelManagerInterface::WorkingCopy workingCopy,
621 622 623
                                 const Snapshot snapshot,
                                 CppFindReferences *findRefs,
                                 const Macro macro)
Christian Kamm's avatar
Christian Kamm committed
624
{
625 626 627
    // ensure the dependency table is updated
    DependencyTable dependencies = findRefs->updateDependencyTable(snapshot);

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

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

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

void CppFindReferences::findMacroUses(const Macro &macro)
646 647 648 649 650
{
    findMacroUses(macro, QString(), false);
}

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

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

666
    connect(search, SIGNAL(activated(Find::SearchResultItem)),
Christian Kamm's avatar
Christian Kamm committed
667
            this, SLOT(openEditor(Find::SearchResultItem)));
668
    connect(search, SIGNAL(cancelled()), this, SLOT(cancel()));
669
    connect(search, SIGNAL(paused(bool)), this, SLOT(setPaused(bool)));
Christian Kamm's avatar
Christian Kamm committed
670 671

    const Snapshot snapshot = _modelManager->snapshot();
672
    const CppModelManagerInterface::WorkingCopy workingCopy = _modelManager->workingCopy();
Christian Kamm's avatar
Christian Kamm committed
673 674 675 676 677

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

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

hjk's avatar
hjk committed
691
    Core::ProgressManager *progressManager = Core::ICore::progressManager();
692
    Core::FutureProgress *progress = progressManager->addTask(result, tr("Searching"),
Christian Kamm's avatar
Christian Kamm committed
693
                                                              CppTools::Constants::TASK_SEARCH);
694
    connect(progress, SIGNAL(clicked()), search, SLOT(popup()));
Christian Kamm's avatar
Christian Kamm committed
695 696
}

697 698 699 700 701 702
void CppFindReferences::renameMacroUses(const Macro &macro, const QString &replacement)
{
    const QString textToReplace = replacement.isEmpty() ? macro.name() : replacement;
    findMacroUses(macro, textToReplace, true);
}

703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727
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;
}
728 729 730 731 732 733 734 735

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
736
    watcher->setFuture(future);
737
}