cppfindreferences.cpp 24 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 <cplusplus/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

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

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
    }
}

90
namespace {
91

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->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 148 149 150 151
            usages = process.usages();
        }

        return usages;
    }
};

152
class UpdateUI: public std::binary_function<QList<Usage> &, QList<Usage>, void>
153 154 155 156
{
    QFutureInterface<Usage> *future;

public:
157
    UpdateUI(QFutureInterface<Usage> *future): future(future) {}
158

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

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

168 169
} // end of anonymous namespace

170
CppFindReferences::CppFindReferences(CppModelManagerInterface *modelManager)
171
    : QObject(modelManager),
172
      _modelManager(modelManager)
173 174 175 176 177 178 179
{
}

CppFindReferences::~CppFindReferences()
{
}

180
QList<int> CppFindReferences::references(Symbol *symbol, const LookupContext &context) const
181 182 183
{
    QList<int> references;

184
    FindUsages findUsages(context);
185 186 187 188 189 190
    findUsages(symbol);
    references = findUsages.references();

    return references;
}

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

200 201
    const Snapshot snapshot = context.snapshot();

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

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

            Control *control = doc->control();

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

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

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

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

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

274
void CppFindReferences::findAll_helper(Find::SearchResult *search)
275
{
276 277
    CppFindReferencesParameters parameters = search->userData().value<CppFindReferencesParameters>();
    if (!(parameters.symbol && parameters.symbol->identifier())) {
278
        search->finishSearch();
279
        return;
280 281 282 283
    }
    connect(search, SIGNAL(cancelled()), this, SLOT(cancel()));
    connect(search, SIGNAL(activated(Find::SearchResultItem)),
            this, SLOT(openEditor(Find::SearchResultItem)));
284

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

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

296
    connect(progress, SIGNAL(clicked()), Find::SearchResultWindow::instance(), SLOT(popup()));
Roberto Raggi's avatar
Roberto Raggi committed
297 298
}

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

309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 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
void CppFindReferences::searchAgain()
{
    Find::SearchResult *search = qobject_cast<Find::SearchResult *>(sender());
    CppFindReferencesParameters parameters = search->userData().value<CppFindReferencesParameters>();
    Snapshot snapshot = CppModelManagerInterface::instance()->snapshot();
    search->reset();
    if (!findSymbol(&parameters, snapshot)) {
        search->finishSearch();
        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
477
void CppFindReferences::displayResults(int first, int last)
Roberto Raggi's avatar
Roberto Raggi committed
478
{
479 480 481 482 483
    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();
484 485
        return;
    }
Roberto Raggi's avatar
Roberto Raggi committed
486
    for (int index = first; index != last; ++index) {
487 488 489 490 491 492
        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
493
    }
Roberto Raggi's avatar
Roberto Raggi committed
494 495 496 497
}

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

505 506
void CppFindReferences::cancel()
{
507 508 509 510 511
    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();
512 513
}

514
void CppFindReferences::openEditor(const Find::SearchResultItem &item)
Roberto Raggi's avatar
Roberto Raggi committed
515
{
516
    if (item.path.size() > 0) {
517
        TextEditor::BaseTextEditorWidget::openEditorAt(item.path.first(), item.lineNumber, item.textMarkPos,
hjk's avatar
hjk committed
518
                                                 Core::Id(),
519
                                                 Core::EditorManager::ModeSwitch);
520
    } else {
hjk's avatar
hjk committed
521
        Core::EditorManager::instance()->openEditor(item.text, Core::Id(), Core::EditorManager::ModeSwitch);
522
    }
Roberto Raggi's avatar
Roberto Raggi committed
523 524
}

Christian Kamm's avatar
Christian Kamm committed
525 526 527 528 529

namespace {

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

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

    QList<Usage> operator()(const QString &fileName)
    {
        QList<Usage> usages;
546 547
        if (future->isCanceled())
            return usages;
Christian Kamm's avatar
Christian Kamm committed
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600

        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(),
                                    use.begin() - lineStart, use.length()));
            }
        }

        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,
601
                                 const CppModelManagerInterface::WorkingCopy workingCopy,
602 603 604
                                 const Snapshot snapshot,
                                 CppFindReferences *findRefs,
                                 const Macro macro)
Christian Kamm's avatar
Christian Kamm committed
605
{
606 607 608
    // ensure the dependency table is updated
    DependencyTable dependencies = findRefs->updateDependencyTable(snapshot);

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

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

616
    FindMacroUsesInFile process(workingCopy, snapshot, macro, &future);
Christian Kamm's avatar
Christian Kamm committed
617
    UpdateUI reduce(&future);
618 619 620
    // 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
621
    QtConcurrent::blockingMappedReduced<QList<Usage> > (files, process, reduce);
622
    QThreadPool::globalInstance()->reserveThread();
Christian Kamm's avatar
Christian Kamm committed
623 624 625 626 627
    future.setProgressValue(files.size());
}

void CppFindReferences::findMacroUses(const Macro &macro)
{
628
    Find::SearchResult *search = Find::SearchResultWindow::instance()->startNewSearch(
629 630 631 632
                tr("C++ Macro Usages:"),
                QString(),
                macro.name(),
                Find::SearchResultWindow::SearchOnly);
Christian Kamm's avatar
Christian Kamm committed
633

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

636
    connect(search, SIGNAL(activated(Find::SearchResultItem)),
Christian Kamm's avatar
Christian Kamm committed
637
            this, SLOT(openEditor(Find::SearchResultItem)));
638
    connect(search, SIGNAL(cancelled()), this, SLOT(cancel()));
Christian Kamm's avatar
Christian Kamm committed
639 640

    const Snapshot snapshot = _modelManager->snapshot();
641
    const CppModelManagerInterface::WorkingCopy workingCopy = _modelManager->workingCopy();
Christian Kamm's avatar
Christian Kamm committed
642 643 644 645 646

    // add the macro definition itself
    {
        // ### FIXME: Encoding?
        const QByteArray &source = getSource(macro.fileName(), workingCopy).toLatin1();
647 648
        search->addResult(macro.fileName(), macro.line(),
                          source.mid(macro.offset(), macro.length()), 0, macro.length());
Christian Kamm's avatar
Christian Kamm committed
649 650 651
    }

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

hjk's avatar
hjk committed
655
    Core::ProgressManager *progressManager = Core::ICore::progressManager();
656
    Core::FutureProgress *progress = progressManager->addTask(result, tr("Searching"),
Christian Kamm's avatar
Christian Kamm committed
657
                                                              CppTools::Constants::TASK_SEARCH);
658
    connect(progress, SIGNAL(clicked()), Find::SearchResultWindow::instance(), SLOT(popup()));
Christian Kamm's avatar
Christian Kamm committed
659 660
}

661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
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;
}
686 687 688 689 690 691 692 693

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
694
    watcher->setFuture(future);
695
}