cppfindreferences.cpp 11.8 KB
Newer Older
Roberto Raggi's avatar
Roberto Raggi committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
con's avatar
con committed
26
** contact the sales department at http://qt.nokia.com/contact.
Roberto Raggi's avatar
Roberto Raggi committed
27 28 29 30
**
**************************************************************************/

#include "cppfindreferences.h"
31
#include "cppmodelmanagerinterface.h"
Roberto Raggi's avatar
Roberto Raggi committed
32 33 34 35 36 37 38
#include "cpptoolsconstants.h"

#include <texteditor/basetexteditor.h>
#include <find/searchresultwindow.h>
#include <extensionsystem/pluginmanager.h>
#include <utils/filesearch.h>
#include <coreplugin/progressmanager/progressmanager.h>
39
#include <coreplugin/editormanager/editormanager.h>
Roberto Raggi's avatar
Roberto Raggi committed
40 41 42 43 44 45 46 47
#include <coreplugin/icore.h>

#include <ASTVisitor.h>
#include <AST.h>
#include <Control.h>
#include <Literals.h>
#include <TranslationUnit.h>
#include <Symbols.h>
48 49
#include <Names.h>
#include <Scope.h>
Roberto Raggi's avatar
Roberto Raggi committed
50 51

#include <cplusplus/CppDocument.h>
52
#include <cplusplus/CppBindings.h>
53
#include <cplusplus/Overview.h>
Roberto Raggi's avatar
Roberto Raggi committed
54 55 56

#include <QtCore/QTime>
#include <QtCore/QtConcurrentRun>
57
#include <QtCore/QtConcurrentMap>
Roberto Raggi's avatar
Roberto Raggi committed
58
#include <QtCore/QDir>
59
#include <QtGui/QApplication>
Roberto Raggi's avatar
Roberto Raggi committed
60 61
#include <qtconcurrent/runextensions.h>

62 63
#include <functional>

Roberto Raggi's avatar
Roberto Raggi committed
64 65 66
using namespace CppTools::Internal;
using namespace CPlusPlus;

67
CppFindReferences::CppFindReferences(CppTools::CppModelManagerInterface *modelManager)
68 69
    : QObject(modelManager),
      _modelManager(modelManager),
Roberto Raggi's avatar
Roberto Raggi committed
70 71 72
      _resultWindow(ExtensionSystem::PluginManager::instance()->getObject<Find::SearchResultWindow>())
{
    m_watcher.setPendingResultsLimit(1);
Roberto Raggi's avatar
Roberto Raggi committed
73
    connect(&m_watcher, SIGNAL(resultsReadyAt(int,int)), this, SLOT(displayResults(int,int)));
Roberto Raggi's avatar
Roberto Raggi committed
74 75 76 77 78 79 80
    connect(&m_watcher, SIGNAL(finished()), this, SLOT(searchFinished()));
}

CppFindReferences::~CppFindReferences()
{
}

81 82 83 84 85 86
QList<int> CppFindReferences::references(Symbol *symbol,
                                         Document::Ptr doc,
                                         const Snapshot& snapshot) const
{
    QList<int> references;

87
    FindUsages findUsages(doc, snapshot);
Roberto Raggi's avatar
Roberto Raggi committed
88
    findUsages.setGlobalNamespaceBinding(bind(doc, snapshot));
Roberto Raggi's avatar
Roberto Raggi committed
89
    findUsages(symbol);
Roberto Raggi's avatar
Roberto Raggi committed
90
    references = findUsages.references();
91 92 93 94

    return references;
}

95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
class MyProcess: public std::unary_function<QString, QList<Usage> >
{
    const QMap<QString, QString> wl;
    const Snapshot snapshot;
    Symbol *symbol;

public:
    MyProcess(const QMap<QString, QString> wl,
              const Snapshot snapshot,
              Symbol *symbol)
        : wl(wl), snapshot(snapshot), symbol(symbol)
    { }

    QList<Usage> operator()(const QString &fileName)
    {
        QList<Usage> usages;
        const Identifier *symbolId = symbol->identifier();

        if (Document::Ptr previousDoc = snapshot.value(fileName)) {
            Control *control = previousDoc->control();
            if (! control->findIdentifier(symbolId->chars(), symbolId->size()))
                return usages; // skip this document, it's not using symbolId.
        }

        QByteArray source;

        if (wl.contains(fileName))
            source = snapshot.preprocessedCode(wl.value(fileName), fileName);
        else {
            QFile file(fileName);
            if (! file.open(QFile::ReadOnly))
                return usages;

            const QString contents = QTextStream(&file).readAll(); // ### FIXME
            source = snapshot.preprocessedCode(contents, fileName);
        }

        Document::Ptr doc = snapshot.documentFromSource(source, fileName);
        doc->tokenize();

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

            FindUsages process(doc, snapshot);
            process.setGlobalNamespaceBinding(bind(doc, snapshot));

            process(symbol);
            usages = process.usages();
        }

        return usages;
    }
};

class MyReduce: public std::binary_function<QList<Usage> &, QList<Usage>, void>
{
    QFutureInterface<Usage> *future;

public:
    MyReduce(QFutureInterface<Usage> *future): future(future) {}

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

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

166
static void find_helper(QFutureInterface<Usage> &future,
167
                        const QMap<QString, QString> wl,
168 169
                        Snapshot snapshot,
                        Symbol *symbol)
Roberto Raggi's avatar
Roberto Raggi committed
170 171 172
{
    QTime tm;
    tm.start();
173

Roberto Raggi's avatar
Roberto Raggi committed
174
    const Identifier *symbolId = symbol->identifier();
175 176
    Q_ASSERT(symbolId != 0);

Roberto Raggi's avatar
Cleanup  
Roberto Raggi committed
177 178
    const QString sourceFile = QString::fromUtf8(symbol->fileName(), symbol->fileNameLength());
    QStringList files(sourceFile);
179 180 181 182 183 184 185 186 187 188 189 190

    if (symbol->isClass() || symbol->isForwardClassDeclaration()) {
        foreach (const Document::Ptr &doc, snapshot) {
            if (doc->fileName() == sourceFile)
                continue;

            Control *control = doc->control();

            if (control->findIdentifier(symbolId->chars(), symbolId->size()))
                files.append(doc->fileName());
        }
    } else {
191
        files += snapshot.filesDependingOn(sourceFile);
192
    }
193
    files.removeDuplicates();
194
    //qDebug() << "done in:" << tm.elapsed() << "number of files to parse:" << files.size();
Roberto Raggi's avatar
Roberto Raggi committed
195 196 197

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

198 199
    MyProcess process(wl, snapshot, symbol);
    MyReduce reduce(&future);
200

201
    QtConcurrent::blockingMappedReduced<QList<Usage> > (files, process, reduce);
202

Roberto Raggi's avatar
Roberto Raggi committed
203 204 205
    future.setProgressValue(files.size());
}

206 207
void CppFindReferences::findUsages(Symbol *symbol)
{
Roberto Raggi's avatar
Roberto Raggi committed
208 209 210 211 212
    Find::SearchResult *search = _resultWindow->startNewSearch(Find::SearchResultWindow::SearchOnly);

    connect(search, SIGNAL(activated(Find::SearchResultItem)),
            this, SLOT(openEditor(Find::SearchResultItem)));

213 214 215 216
    findAll_helper(symbol);
}

void CppFindReferences::renameUsages(Symbol *symbol)
Roberto Raggi's avatar
Roberto Raggi committed
217
{
Roberto Raggi's avatar
Roberto Raggi committed
218
    if (const Identifier *id = symbol->identifier()) {
219
        const QString textToReplace = QString::fromUtf8(id->chars(), id->size());
Roberto Raggi's avatar
Roberto Raggi committed
220

221 222
        Find::SearchResult *search = _resultWindow->startNewSearch(Find::SearchResultWindow::SearchAndReplace);
        _resultWindow->setTextToReplace(textToReplace);
223

224 225
        connect(search, SIGNAL(activated(Find::SearchResultItem)),
                this, SLOT(openEditor(Find::SearchResultItem)));
Roberto Raggi's avatar
Roberto Raggi committed
226

227 228 229 230 231
        connect(search, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>)),
                SLOT(onReplaceButtonClicked(QString,QList<Find::SearchResultItem>)));

        findAll_helper(symbol);
    }
232 233 234 235
}

void CppFindReferences::findAll_helper(Symbol *symbol)
{
236 237 238
    if (! (symbol && symbol->identifier()))
        return;

Roberto Raggi's avatar
Roberto Raggi committed
239 240
    _resultWindow->popup(true);

241
    const Snapshot snapshot = _modelManager->snapshot();
242
    const QMap<QString, QString> wl = _modelManager->workingCopy();
243

Roberto Raggi's avatar
Roberto Raggi committed
244
    Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager();
Roberto Raggi's avatar
Roberto Raggi committed
245 246 247 248

    QFuture<Usage> result;

    result = QtConcurrent::run(&find_helper, wl, snapshot, symbol);
Roberto Raggi's avatar
Roberto Raggi committed
249 250 251
    m_watcher.setFuture(result);

    Core::FutureProgress *progress = progressManager->addTask(result, tr("Searching..."),
252
                                                              CppTools::Constants::TASK_SEARCH);
Roberto Raggi's avatar
Roberto Raggi committed
253 254 255 256

    connect(progress, SIGNAL(clicked()), _resultWindow, SLOT(popup()));
}

257 258 259 260 261 262 263
static void applyChanges(QTextDocument *doc, const QString &text, const QList<Find::SearchResultItem> &items)
{
    QList<QTextCursor> cursors;

    foreach (const Find::SearchResultItem &item, items) {
        const int blockNumber = item.lineNumber - 1;
        QTextCursor tc(doc->findBlockByNumber(blockNumber));
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278

        const int cursorPosition = tc.position() + item.searchTermStart;

        int cursorIndex = 0;
        for (; cursorIndex < cursors.size(); ++cursorIndex) {
            const QTextCursor &tc = cursors.at(cursorIndex);

            if (tc.position() == cursorPosition)
                break;
        }

        if (cursorIndex != cursors.size())
            continue; // skip this change.

        tc.setPosition(cursorPosition);
279 280 281 282 283 284 285 286 287
        tc.setPosition(tc.position() + item.searchTermLength,
                       QTextCursor::KeepAnchor);
        cursors.append(tc);
    }

    foreach (QTextCursor tc, cursors)
        tc.insertText(text);
}

Roberto Raggi's avatar
Roberto Raggi committed
288 289 290
void CppFindReferences::onReplaceButtonClicked(const QString &text,
                                               const QList<Find::SearchResultItem> &items)
{
291 292
    Core::EditorManager::instance()->hideEditorInfoBar(QLatin1String("CppEditor.Rename"));

Roberto Raggi's avatar
Roberto Raggi committed
293 294 295 296 297 298 299 300
    if (text.isEmpty())
        return;

    QHash<QString, QList<Find::SearchResultItem> > changes;

    foreach (const Find::SearchResultItem &item, items)
        changes[item.fileName].append(item);

301 302
    Core::EditorManager *editorManager = Core::EditorManager::instance();

Roberto Raggi's avatar
Roberto Raggi committed
303 304 305 306 307
    QHashIterator<QString, QList<Find::SearchResultItem> > it(changes);
    while (it.hasNext()) {
        it.next();

        const QString fileName = it.key();
308
        const QList<Find::SearchResultItem> items = it.value();
Roberto Raggi's avatar
Roberto Raggi committed
309

310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
        const QList<Core::IEditor *> editors = editorManager->editorsForFileName(fileName);
        TextEditor::BaseTextEditor *textEditor = 0;
        foreach (Core::IEditor *editor, editors) {
            textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor->widget());
            if (textEditor != 0)
                break;
        }

        if (textEditor != 0) {
            QTextCursor tc = textEditor->textCursor();
            tc.beginEditBlock();
            applyChanges(textEditor->document(), text, items);
            tc.endEditBlock();
        } else {
            QFile file(fileName);
Roberto Raggi's avatar
Roberto Raggi committed
325

326 327
            if (file.open(QFile::ReadOnly)) {
                QTextStream stream(&file);
Roberto Raggi's avatar
Roberto Raggi committed
328
                // ### set the encoding
329 330 331 332 333 334 335 336 337 338 339 340 341 342
                const QString plainText = stream.readAll();
                file.close();

                QTextDocument doc;
                doc.setPlainText(plainText);

                applyChanges(&doc, text, items);

                QFile newFile(fileName);
                if (newFile.open(QFile::WriteOnly)) {
                    QTextStream stream(&newFile);
                    // ### set the encoding
                    stream << doc.toPlainText();
                }
Roberto Raggi's avatar
Roberto Raggi committed
343 344 345 346 347 348
            }
        }
    }

    const QStringList fileNames = changes.keys();
    _modelManager->updateSourceFiles(fileNames);
349
    _resultWindow->hide();
Roberto Raggi's avatar
Roberto Raggi committed
350 351
}

Roberto Raggi's avatar
Roberto Raggi committed
352
void CppFindReferences::displayResults(int first, int last)
Roberto Raggi's avatar
Roberto Raggi committed
353
{
Roberto Raggi's avatar
Roberto Raggi committed
354 355 356 357 358 359 360 361
    for (int index = first; index != last; ++index) {
        Usage result = m_watcher.future().resultAt(index);
        _resultWindow->addResult(result.path,
                                 result.line,
                                 result.lineText,
                                 result.col,
                                 result.len);
    }
Roberto Raggi's avatar
Roberto Raggi committed
362 363 364 365
}

void CppFindReferences::searchFinished()
{
366
    _resultWindow->finishSearch();
Roberto Raggi's avatar
Roberto Raggi committed
367 368 369
    emit changed();
}

370
void CppFindReferences::openEditor(const Find::SearchResultItem &item)
Roberto Raggi's avatar
Roberto Raggi committed
371
{
372
    TextEditor::BaseTextEditor::openEditorAt(item.fileName, item.lineNumber, item.searchTermStart);
Roberto Raggi's avatar
Roberto Raggi committed
373 374
}