cppfindreferences.cpp 12 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;

Roberto Raggi's avatar
Roberto Raggi committed
67
namespace {
68

Roberto Raggi's avatar
Roberto Raggi committed
69
class ProcessFile: public std::unary_function<QString, QList<Usage> >
70
{
71
    const CppTools::CppModelManagerInterface::WorkingCopy workingCopy;
72
73
74
75
    const Snapshot snapshot;
    Symbol *symbol;

public:
76
77
78
79
    ProcessFile(const CppTools::CppModelManagerInterface::WorkingCopy &workingCopy,
                const Snapshot snapshot,
                Symbol *symbol)
        : workingCopy(workingCopy), snapshot(snapshot), symbol(symbol)
80
81
82
83
84
85
86
    { }

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

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

        QByteArray source;

95
96
        if (workingCopy.contains(fileName))
            source = snapshot.preprocessedCode(workingCopy.source(fileName), fileName);
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
        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;
    }
};

Roberto Raggi's avatar
Roberto Raggi committed
124
class UpdateUI: public std::binary_function<QList<Usage> &, QList<Usage>, void>
125
126
127
128
{
    QFutureInterface<Usage> *future;

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

131
    void operator()(QList<Usage> &, const QList<Usage> &usages)
132
133
134
135
136
137
138
139
    {
        foreach (const Usage &u, usages)
            future->reportResult(u);

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

Roberto Raggi's avatar
Roberto Raggi committed
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
} // end of anonymous namespace

CppFindReferences::CppFindReferences(CppTools::CppModelManagerInterface *modelManager)
    : QObject(modelManager),
      _modelManager(modelManager),
      _resultWindow(ExtensionSystem::PluginManager::instance()->getObject<Find::SearchResultWindow>())
{
    m_watcher.setPendingResultsLimit(1);
    connect(&m_watcher, SIGNAL(resultsReadyAt(int,int)), this, SLOT(displayResults(int,int)));
    connect(&m_watcher, SIGNAL(finished()), this, SLOT(searchFinished()));
}

CppFindReferences::~CppFindReferences()
{
}

QList<int> CppFindReferences::references(Symbol *symbol,
                                         Document::Ptr doc,
                                         const Snapshot& snapshot) const
{
    QList<int> references;

    FindUsages findUsages(doc, snapshot);
    findUsages.setGlobalNamespaceBinding(bind(doc, snapshot));
    findUsages(symbol);
    references = findUsages.references();

    return references;
}

170
static void find_helper(QFutureInterface<Usage> &future,
171
                        const CppTools::CppModelManagerInterface::WorkingCopy workingCopy,
172
173
                        Snapshot snapshot,
                        Symbol *symbol)
Roberto Raggi's avatar
Roberto Raggi committed
174
175
176
{
    QTime tm;
    tm.start();
177

Roberto Raggi's avatar
Roberto Raggi committed
178
    const Identifier *symbolId = symbol->identifier();
179
180
    Q_ASSERT(symbolId != 0);

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

    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 {
195
        files += snapshot.filesDependingOn(sourceFile);
196
    }
197
    files.removeDuplicates();
198
    //qDebug() << "done in:" << tm.elapsed() << "number of files to parse:" << files.size();
Roberto Raggi's avatar
Roberto Raggi committed
199
200
201

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

202
    ProcessFile process(workingCopy, snapshot, symbol);
Roberto Raggi's avatar
Roberto Raggi committed
203
    UpdateUI reduce(&future);
204

205
    QtConcurrent::blockingMappedReduced<QList<Usage> > (files, process, reduce);
206

Roberto Raggi's avatar
Roberto Raggi committed
207
208
209
    future.setProgressValue(files.size());
}

210
211
void CppFindReferences::findUsages(Symbol *symbol)
{
Roberto Raggi's avatar
Roberto Raggi committed
212
213
214
215
216
    Find::SearchResult *search = _resultWindow->startNewSearch(Find::SearchResultWindow::SearchOnly);

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

217
218
219
220
    findAll_helper(symbol);
}

void CppFindReferences::renameUsages(Symbol *symbol)
Roberto Raggi's avatar
Roberto Raggi committed
221
{
Roberto Raggi's avatar
Roberto Raggi committed
222
    if (const Identifier *id = symbol->identifier()) {
223
        const QString textToReplace = QString::fromUtf8(id->chars(), id->size());
Roberto Raggi's avatar
Roberto Raggi committed
224

225
226
        Find::SearchResult *search = _resultWindow->startNewSearch(Find::SearchResultWindow::SearchAndReplace);
        _resultWindow->setTextToReplace(textToReplace);
227

228
229
        connect(search, SIGNAL(activated(Find::SearchResultItem)),
                this, SLOT(openEditor(Find::SearchResultItem)));
Roberto Raggi's avatar
Roberto Raggi committed
230

231
232
233
234
235
        connect(search, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>)),
                SLOT(onReplaceButtonClicked(QString,QList<Find::SearchResultItem>)));

        findAll_helper(symbol);
    }
236
237
238
239
}

void CppFindReferences::findAll_helper(Symbol *symbol)
{
240
241
242
    if (! (symbol && symbol->identifier()))
        return;

Roberto Raggi's avatar
Roberto Raggi committed
243
244
    _resultWindow->popup(true);

245
    const Snapshot snapshot = _modelManager->snapshot();
246
    const CppTools::CppModelManagerInterface::WorkingCopy workingCopy = _modelManager->workingCopy();
247

Roberto Raggi's avatar
Roberto Raggi committed
248
    Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager();
Roberto Raggi's avatar
Roberto Raggi committed
249
250
251

    QFuture<Usage> result;

252
    result = QtConcurrent::run(&find_helper, workingCopy, snapshot, symbol);
Roberto Raggi's avatar
Roberto Raggi committed
253
254
255
    m_watcher.setFuture(result);

    Core::FutureProgress *progress = progressManager->addTask(result, tr("Searching..."),
256
                                                              CppTools::Constants::TASK_SEARCH);
Roberto Raggi's avatar
Roberto Raggi committed
257
258
259
260

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

261
262
263
264
265
266
267
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));
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282

        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);
283
284
285
286
287
288
289
290
291
        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
292
293
294
void CppFindReferences::onReplaceButtonClicked(const QString &text,
                                               const QList<Find::SearchResultItem> &items)
{
295
296
    Core::EditorManager::instance()->hideEditorInfoBar(QLatin1String("CppEditor.Rename"));

Roberto Raggi's avatar
Roberto Raggi committed
297
298
299
300
301
302
303
304
    if (text.isEmpty())
        return;

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

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

305
306
    Core::EditorManager *editorManager = Core::EditorManager::instance();

Roberto Raggi's avatar
Roberto Raggi committed
307
308
309
310
311
    QHashIterator<QString, QList<Find::SearchResultItem> > it(changes);
    while (it.hasNext()) {
        it.next();

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

314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
        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
329

330
331
            if (file.open(QFile::ReadOnly)) {
                QTextStream stream(&file);
Roberto Raggi's avatar
Roberto Raggi committed
332
                // ### set the encoding
333
334
335
336
337
338
339
340
341
342
343
344
345
346
                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
347
348
349
350
351
352
            }
        }
    }

    const QStringList fileNames = changes.keys();
    _modelManager->updateSourceFiles(fileNames);
353
    _resultWindow->hide();
Roberto Raggi's avatar
Roberto Raggi committed
354
355
}

Roberto Raggi's avatar
Roberto Raggi committed
356
void CppFindReferences::displayResults(int first, int last)
Roberto Raggi's avatar
Roberto Raggi committed
357
{
Roberto Raggi's avatar
Roberto Raggi committed
358
359
360
361
362
363
364
365
    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
366
367
368
369
}

void CppFindReferences::searchFinished()
{
370
    _resultWindow->finishSearch();
Roberto Raggi's avatar
Roberto Raggi committed
371
372
373
    emit changed();
}

374
void CppFindReferences::openEditor(const Find::SearchResultItem &item)
Roberto Raggi's avatar
Roberto Raggi committed
375
{
376
    TextEditor::BaseTextEditor::openEditorAt(item.fileName, item.lineNumber, item.searchTermStart);
Roberto Raggi's avatar
Roberto Raggi committed
377
378
}