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
}