cppfindreferences.cpp 22.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
26
27
28
29
30
31
32
33
34
35
36
37
38
/**************************************************************************
**
** 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
** contact the sales department at http://www.qtsoftware.com/contact.
**
**************************************************************************/

#include "cppfindreferences.h"
#include "cppmodelmanager.h"
#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
52
53

#include <cplusplus/CppDocument.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/ResolveExpression.h>
54
#include <cplusplus/Overview.h>
Roberto Raggi's avatar
Roberto Raggi committed
55
#include <cplusplus/TypeOfExpression.h>
56
#include <cplusplus/FastPreprocessor.h>
Roberto Raggi's avatar
Roberto Raggi committed
57
58
59
60

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

using namespace CppTools::Internal;
using namespace CPlusPlus;

namespace {

struct Process: protected ASTVisitor
{
public:
72
    Process(Document::Ptr doc, const Snapshot &snapshot,
73
            QFutureInterface<Utils::FileSearchResult> *future)
Roberto Raggi's avatar
Roberto Raggi committed
74
75
76
77
            : ASTVisitor(doc->control()),
              _future(future),
              _doc(doc),
              _snapshot(snapshot),
78
79
80
81
82
              _source(_doc->source()),
              _sem(doc->control())
    {
        _snapshot.insert(_doc);
    }
Roberto Raggi's avatar
Roberto Raggi committed
83

84
    QList<int> operator()(Symbol *symbol, Identifier *id, AST *ast)
Roberto Raggi's avatar
Roberto Raggi committed
85
    {
86
        _references.clear();
87
        _declSymbol = symbol;
Roberto Raggi's avatar
Roberto Raggi committed
88
89
90
        _id = id;
        _exprDoc = Document::create("<references>");
        accept(ast);
91
        return _references;
Roberto Raggi's avatar
Roberto Raggi committed
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
    }

protected:
    using ASTVisitor::visit;

    QString matchingLine(const Token &tk) const
    {
        const char *beg = _source.constData();
        const char *cp = beg + tk.offset;
        for (; cp != beg - 1; --cp) {
            if (*cp == '\n')
                break;
        }

        ++cp;

        const char *lineEnd = cp + 1;
        for (; *lineEnd; ++lineEnd) {
            if (*lineEnd == '\n')
                break;
        }

        const QString matchingLine = QString::fromUtf8(cp, lineEnd - cp);
        return matchingLine;

    }

119
120
121
122
123
124
125
126
    void reportResult(unsigned tokenIndex, const QList<Symbol *> &candidates)
    {
        const bool isStrongResult = checkCandidates(candidates);

        if (isStrongResult)
            reportResult(tokenIndex);
    }

Roberto Raggi's avatar
Roberto Raggi committed
127
128
129
130
131
132
133
134
135
136
137
    void reportResult(unsigned tokenIndex)
    {
        const Token &tk = tokenAt(tokenIndex);
        const QString lineText = matchingLine(tk);

        unsigned line, col;
        getTokenStartPosition(tokenIndex, &line, &col);

        if (col)
            --col;  // adjust the column position.

138
        const int len = tk.f.length;
Roberto Raggi's avatar
Roberto Raggi committed
139

140
        if (_future)
141
            _future->reportResult(Utils::FileSearchResult(QDir::toNativeSeparators(_doc->fileName()),
142
                                                          line, lineText, col, len));
143
144

        _references.append(tokenIndex);
Roberto Raggi's avatar
Roberto Raggi committed
145
146
    }

147
    bool checkCandidates(const QList<Symbol *> &candidates) const
Roberto Raggi's avatar
Roberto Raggi committed
148
    {
Roberto Raggi's avatar
Roberto Raggi committed
149
150
151
152
153
154
155
156
157
158
159
        if (Symbol *canonicalSymbol = LookupContext::canonicalSymbol(candidates)) {
#if 0
            qDebug() << "*** canonical symbol:" << canonicalSymbol->fileName()
                    << canonicalSymbol->line() << canonicalSymbol->column()
                    << "candidates:" << candidates.size();
#endif

            return isDeclSymbol(canonicalSymbol);
        }

        return false;
Roberto Raggi's avatar
Roberto Raggi committed
160
161
    }

162
    bool isDeclSymbol(Symbol *symbol) const
Roberto Raggi's avatar
Roberto Raggi committed
163
    {
164
165
        if (! symbol)
            return false;
Roberto Raggi's avatar
Roberto Raggi committed
166

167
        else if (symbol == _declSymbol) {
168
169
            return true;

170
        } else if (symbol->line() == _declSymbol->line() && symbol->column() == _declSymbol->column()) {
171
172
            if (! qstrcmp(symbol->fileName(), _declSymbol->fileName()))
                return true;
173
174
175
176
177
178
179
180

        } else if (symbol->isForwardClassDeclaration() && (_declSymbol->isClass() ||
                                                           _declSymbol->isForwardClassDeclaration())) {
            return true;

        } else if (_declSymbol->isForwardClassDeclaration() && (symbol->isClass() ||
                                                                symbol->isForwardClassDeclaration())) {
            return true;
181
182
183
        }

        return false;
Roberto Raggi's avatar
Roberto Raggi committed
184
185
    }

Roberto Raggi's avatar
Roberto Raggi committed
186
187
188
    LookupContext _previousContext;

    LookupContext currentContext(AST *ast)
Roberto Raggi's avatar
Roberto Raggi committed
189
    {
190
191
192
        unsigned line, column;
        getTokenStartPosition(ast->firstToken(), &line, &column);
        Symbol *lastVisibleSymbol = _doc->findSymbolAt(line, column);
Roberto Raggi's avatar
Roberto Raggi committed
193
194
195
196

        if (lastVisibleSymbol && lastVisibleSymbol == _previousContext.symbol())
            return _previousContext;

Roberto Raggi's avatar
Roberto Raggi committed
197
        LookupContext ctx(lastVisibleSymbol, _exprDoc, _doc, _snapshot);
Roberto Raggi's avatar
Roberto Raggi committed
198
        _previousContext = ctx;
Roberto Raggi's avatar
Roberto Raggi committed
199
        return ctx;
Roberto Raggi's avatar
Roberto Raggi committed
200
201
    }

Roberto Raggi's avatar
Roberto Raggi committed
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
    void ensureNameIsValid(NameAST *ast)
    {
        if (ast && ! ast->name)
            ast->name = _sem.check(ast, /*scope = */ 0);
    }

    virtual bool visit(MemInitializerAST *ast)
    {
        if (ast->name && ast->name->asSimpleName() != 0) {
            ensureNameIsValid(ast->name);

            SimpleNameAST *simple = ast->name->asSimpleName();
            if (identifier(simple->identifier_token) == _id) {
                LookupContext context = currentContext(ast);
                const QList<Symbol *> candidates = context.resolve(simple->name);
217
                reportResult(simple->identifier_token, candidates);
Roberto Raggi's avatar
Roberto Raggi committed
218
219
220
221
222
223
            }
        }
        accept(ast->expression);
        return false;
    }

Roberto Raggi's avatar
Roberto Raggi committed
224
225
226
227
228
229
    virtual bool visit(PostfixExpressionAST *ast)
    {
        _postfixExpressionStack.append(ast);
        return true;
    }

Roberto Raggi's avatar
Roberto Raggi committed
230
    virtual void endVisit(PostfixExpressionAST *)
Roberto Raggi's avatar
Roberto Raggi committed
231
232
233
234
235
236
    {
        _postfixExpressionStack.removeLast();
    }

    virtual bool visit(MemberAccessAST *ast)
    {
Roberto Raggi's avatar
Roberto Raggi committed
237
238
239
240
        if (ast->member_name) {
            if (SimpleNameAST *simple = ast->member_name->asSimpleName()) {
                if (identifier(simple->identifier_token) == _id) {
                    Q_ASSERT(! _postfixExpressionStack.isEmpty());
Roberto Raggi's avatar
Roberto Raggi committed
241

Roberto Raggi's avatar
Roberto Raggi committed
242
243
                    checkExpression(_postfixExpressionStack.last()->firstToken(),
                                    simple->identifier_token);
Roberto Raggi's avatar
Roberto Raggi committed
244

Roberto Raggi's avatar
Roberto Raggi committed
245
246
247
248
                    return false;
                }
            }
        }
Roberto Raggi's avatar
Roberto Raggi committed
249

Roberto Raggi's avatar
Roberto Raggi committed
250
251
        return true;
    }
Roberto Raggi's avatar
Roberto Raggi committed
252

Roberto Raggi's avatar
Roberto Raggi committed
253
254
255
256
    void checkExpression(unsigned startToken, unsigned endToken)
    {
        const unsigned begin = tokenAt(startToken).begin();
        const unsigned end = tokenAt(endToken).end();
Roberto Raggi's avatar
Roberto Raggi committed
257

Roberto Raggi's avatar
Roberto Raggi committed
258
        const QString expression = _source.mid(begin, end - begin);
Roberto Raggi's avatar
Roberto Raggi committed
259
        // qDebug() << "*** check expression:" << expression;
Roberto Raggi's avatar
Roberto Raggi committed
260

Roberto Raggi's avatar
Roberto Raggi committed
261
262
        TypeOfExpression typeofExpression;
        typeofExpression.setSnapshot(_snapshot);
Roberto Raggi's avatar
Roberto Raggi committed
263

Roberto Raggi's avatar
Roberto Raggi committed
264
265
266
        unsigned line, column;
        getTokenStartPosition(startToken, &line, &column);
        Symbol *lastVisibleSymbol = _doc->findSymbolAt(line, column);
Roberto Raggi's avatar
Roberto Raggi committed
267

Roberto Raggi's avatar
Roberto Raggi committed
268
269
        const QList<TypeOfExpression::Result> results =
                typeofExpression(expression, _doc, lastVisibleSymbol,
270
                                 TypeOfExpression::Preprocess);
Roberto Raggi's avatar
Roberto Raggi committed
271

Roberto Raggi's avatar
Roberto Raggi committed
272
        QList<Symbol *> candidates;
Roberto Raggi's avatar
Roberto Raggi committed
273

Roberto Raggi's avatar
Roberto Raggi committed
274
275
276
        foreach (TypeOfExpression::Result r, results) {
            FullySpecifiedType ty = r.first;
            Symbol *lastVisibleSymbol = r.second;
Roberto Raggi's avatar
Roberto Raggi committed
277

Roberto Raggi's avatar
Roberto Raggi committed
278
            candidates.append(lastVisibleSymbol);
Roberto Raggi's avatar
Roberto Raggi committed
279
280
        }

281
        reportResult(endToken, candidates);
Roberto Raggi's avatar
Roberto Raggi committed
282
283
    }

Roberto Raggi's avatar
Roberto Raggi committed
284
285
    virtual bool visit(QualifiedNameAST *ast)
    {
Roberto Raggi's avatar
Roberto Raggi committed
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
        for (NestedNameSpecifierAST *nested_name_specifier = ast->nested_name_specifier;
             nested_name_specifier; nested_name_specifier = nested_name_specifier->next) {

            if (NameAST *class_or_namespace_name = nested_name_specifier->class_or_namespace_name) {
                SimpleNameAST *simple_name = class_or_namespace_name->asSimpleName();

                TemplateIdAST *template_id = 0;
                if (! simple_name) {
                    template_id = class_or_namespace_name->asTemplateId();

                    if (template_id) {
                        for (TemplateArgumentListAST *template_arguments = template_id->template_arguments;
                             template_arguments; template_arguments = template_arguments->next) {
                            accept(template_arguments->template_argument);
                        }
                    }
                }

                if (simple_name || template_id) {
                    const unsigned identifier_token = simple_name
                               ? simple_name->identifier_token
                               : template_id->identifier_token;

                    if (identifier(identifier_token) == _id)
                        checkExpression(ast->firstToken(), identifier_token);
                }
            }
        }

315
316
317
318
319
320
321
322
        if (NameAST *unqualified_name = ast->unqualified_name) {
            unsigned identifier_token = 0;

            if (SimpleNameAST *simple_name = unqualified_name->asSimpleName())
                identifier_token = simple_name->identifier_token;

            else if (DestructorNameAST *dtor_name = unqualified_name->asDestructorName())
                identifier_token = dtor_name->identifier_token;
Roberto Raggi's avatar
Roberto Raggi committed
323
324

            TemplateIdAST *template_id = 0;
325
326
            if (! identifier_token) {
                template_id = unqualified_name->asTemplateId();
Roberto Raggi's avatar
Roberto Raggi committed
327
328

                if (template_id) {
329
330
                    identifier_token = template_id->identifier_token;

Roberto Raggi's avatar
Roberto Raggi committed
331
332
333
334
335
336
337
                    for (TemplateArgumentListAST *template_arguments = template_id->template_arguments;
                         template_arguments; template_arguments = template_arguments->next) {
                        accept(template_arguments->template_argument);
                    }
                }
            }

338
339
            if (identifier_token && identifier(identifier_token) == _id)
                checkExpression(ast->firstToken(), identifier_token);
340
341
        }

Roberto Raggi's avatar
Roberto Raggi committed
342
343
344
345
346
347
348
        return false;
    }

    virtual bool visit(SimpleNameAST *ast)
    {
        Identifier *id = identifier(ast->identifier_token);
        if (id == _id) {
349
350
            LookupContext context = currentContext(ast);
            const QList<Symbol *> candidates = context.resolve(ast->name);
351
            reportResult(ast->identifier_token, candidates);
352
353
354
        }

        return false;
Roberto Raggi's avatar
Roberto Raggi committed
355
356
    }

Roberto Raggi's avatar
Roberto Raggi committed
357
    virtual bool visit(DestructorNameAST *ast)
Roberto Raggi's avatar
Roberto Raggi committed
358
359
360
    {
        Identifier *id = identifier(ast->identifier_token);
        if (id == _id) {
361
362
            LookupContext context = currentContext(ast);
            const QList<Symbol *> candidates = context.resolve(ast->name);
363
            reportResult(ast->identifier_token, candidates);
Roberto Raggi's avatar
Roberto Raggi committed
364
365
366
367
368
369
370
        }

        return false;
    }

    virtual bool visit(TemplateIdAST *ast)
    {
371
        if (_id == identifier(ast->identifier_token)) {
372
373
            LookupContext context = currentContext(ast);
            const QList<Symbol *> candidates = context.resolve(ast->name);
374
            reportResult(ast->identifier_token, candidates);
375
        }
Roberto Raggi's avatar
Roberto Raggi committed
376

377
378
379
        for (TemplateArgumentListAST *template_arguments = ast->template_arguments;
             template_arguments; template_arguments = template_arguments->next) {
            accept(template_arguments->template_argument);
380
        }
Roberto Raggi's avatar
Roberto Raggi committed
381

382
        return false;
Roberto Raggi's avatar
Roberto Raggi committed
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
    virtual bool visit(ParameterDeclarationAST *ast)
    {
        for (SpecifierAST *spec = ast->type_specifier; spec; spec = spec->next)
            accept(spec);

        if (DeclaratorAST *declarator = ast->declarator) {
            for (SpecifierAST *attr = declarator->attributes; attr; attr = attr->next)
                accept(attr);

            for (PtrOperatorAST *ptr_op = declarator->ptr_operators; ptr_op; ptr_op = ptr_op->next)
                accept(ptr_op);

            // ### TODO: well, not exactly. We need to look at qualified-name-ids and nested-declarators.
            // accept(declarator->core_declarator);

            for (PostfixDeclaratorAST *fx_op = declarator->postfix_declarators; fx_op; fx_op = fx_op->next)
                accept(fx_op);

            for (SpecifierAST *spec = declarator->post_attributes; spec; spec = spec->next)
                accept(spec);

            accept(declarator->initializer);
        }

        accept(ast->expression);
        return false;
    }

Roberto Raggi's avatar
Roberto Raggi committed
413
private:
414
    QFutureInterface<Utils::FileSearchResult> *_future;
415
416
    Identifier *_id; // ### remove me
    Symbol *_declSymbol;
Roberto Raggi's avatar
Roberto Raggi committed
417
418
419
420
    Document::Ptr _doc;
    Snapshot _snapshot;
    QByteArray _source;
    Document::Ptr _exprDoc;
421
    Semantic _sem;
Roberto Raggi's avatar
Roberto Raggi committed
422
    QList<PostfixExpressionAST *> _postfixExpressionStack;
Roberto Raggi's avatar
Roberto Raggi committed
423
    QList<QualifiedNameAST *> _qualifiedNameStack;
424
    QList<int> _references;
Roberto Raggi's avatar
Roberto Raggi committed
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
};

} // end of anonymous namespace

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

CppFindReferences::~CppFindReferences()
{
}

442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
QList<int> CppFindReferences::references(Symbol *symbol,
                                         Document::Ptr doc,
                                         const Snapshot& snapshot) const
{
    Identifier *id = 0;
    if (Identifier *symbolId = symbol->identifier())
        id = doc->control()->findIdentifier(symbolId->chars(), symbolId->size());

    QList<int> references;

    if (! id)
        return references;

    TranslationUnit *translationUnit = doc->translationUnit();
    Q_ASSERT(translationUnit != 0);

    Process process(doc, snapshot, /*future = */ 0);
    references = process(symbol, id, translationUnit->ast());

    return references;
}

464
static void find_helper(QFutureInterface<Utils::FileSearchResult> &future,
465
                        const QMap<QString, QString> wl,
466
467
                        Snapshot snapshot,
                        Symbol *symbol)
Roberto Raggi's avatar
Roberto Raggi committed
468
469
470
{
    QTime tm;
    tm.start();
471

472
473
474
    Identifier *symbolId = symbol->identifier();
    Q_ASSERT(symbolId != 0);

Roberto Raggi's avatar
Cleanup    
Roberto Raggi committed
475
    const QString sourceFile = QString::fromUtf8(symbol->fileName(), symbol->fileNameLength());
476

Roberto Raggi's avatar
Cleanup    
Roberto Raggi committed
477
    QStringList files(sourceFile);
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492

    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 {
        files += snapshot.dependsOn(sourceFile);
    }

Roberto Raggi's avatar
Roberto Raggi committed
493
494
495
496
497
    qDebug() << "done in:" << tm.elapsed() << "number of files to parse:" << files.size();

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

    for (int i = 0; i < files.size(); ++i) {
498
499
500
501
502
503
        if (future.isPaused())
            future.waitForResume();

        if (future.isCanceled())
            break;

Roberto Raggi's avatar
Cleanup    
Roberto Raggi committed
504
505
        const QString &fileName = files.at(i);
        future.setProgressValueAndText(i, QFileInfo(fileName).fileName());
506

Roberto Raggi's avatar
Roberto Raggi committed
507
        if (Document::Ptr previousDoc = snapshot.value(fileName)) {
508
509
510
511
512
513
            Control *control = previousDoc->control();
            Identifier *id = control->findIdentifier(symbolId->chars(), symbolId->size());
            if (! id)
                continue; // skip this document, it's not using symbolId.
        }

514
515
516
517
518
519
520
521
522
523
524
525
526
527
        QByteArray source;

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

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

        Document::Ptr doc = snapshot.documentFromSource(source, fileName);
528
        doc->tokenize();
Roberto Raggi's avatar
Roberto Raggi committed
529
530

        Control *control = doc->control();
531
        if (Identifier *id = control->findIdentifier(symbolId->chars(), symbolId->size())) {
Roberto Raggi's avatar
Roberto Raggi committed
532
533
            QTime tm;
            tm.start();
534
            TranslationUnit *unit = doc->translationUnit();
535
536
            Control *control = doc->control();

Roberto Raggi's avatar
Roberto Raggi committed
537
            FastMacroResolver fastMacroResolver(unit, snapshot);
538
539
540
541
            control->setMacroResolver(&fastMacroResolver);
            doc->parse();
            control->setMacroResolver(0);

Roberto Raggi's avatar
Roberto Raggi committed
542
543
544
            //qDebug() << "***" << unit->fileName() << "parsed in:" << tm.elapsed();

            tm.start();
545
            doc->check();
Roberto Raggi's avatar
Roberto Raggi committed
546
547
548
            //qDebug() << "***" << unit->fileName() << "checked in:" << tm.elapsed();

            tm.start();
549

550
            Process process(doc, snapshot, &future);
551
            process(symbol, id, unit->ast());
Roberto Raggi's avatar
Roberto Raggi committed
552
553

            //qDebug() << "***" << unit->fileName() << "processed in:" << tm.elapsed();
554
        }
Roberto Raggi's avatar
Roberto Raggi committed
555
    }
556

Roberto Raggi's avatar
Roberto Raggi committed
557
558
559
    future.setProgressValue(files.size());
}

560
561
void CppFindReferences::findUsages(Symbol *symbol)
{
Roberto Raggi's avatar
Roberto Raggi committed
562
563
564
565
566
    Find::SearchResult *search = _resultWindow->startNewSearch(Find::SearchResultWindow::SearchOnly);

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

567
568
569
570
    findAll_helper(symbol);
}

void CppFindReferences::renameUsages(Symbol *symbol)
Roberto Raggi's avatar
Roberto Raggi committed
571
{
572
573
    if (Identifier *id = symbol->identifier()) {
        const QString textToReplace = QString::fromUtf8(id->chars(), id->size());
Roberto Raggi's avatar
Roberto Raggi committed
574

575
576
        Find::SearchResult *search = _resultWindow->startNewSearch(Find::SearchResultWindow::SearchAndReplace);
        _resultWindow->setTextToReplace(textToReplace);
577

578
579
        connect(search, SIGNAL(activated(Find::SearchResultItem)),
                this, SLOT(openEditor(Find::SearchResultItem)));
Roberto Raggi's avatar
Roberto Raggi committed
580

581
582
583
584
585
        connect(search, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>)),
                SLOT(onReplaceButtonClicked(QString,QList<Find::SearchResultItem>)));

        findAll_helper(symbol);
    }
586
587
588
589
}

void CppFindReferences::findAll_helper(Symbol *symbol)
{
Roberto Raggi's avatar
Roberto Raggi committed
590
591
    _resultWindow->popup(true);

592
593
594
    const Snapshot snapshot = _modelManager->snapshot();
    const QMap<QString, QString> wl = _modelManager->buildWorkingCopyList();

Roberto Raggi's avatar
Roberto Raggi committed
595
596
    Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager();

597
    QFuture<Utils::FileSearchResult> result = QtConcurrent::run(&find_helper, wl, snapshot, symbol);
Roberto Raggi's avatar
Roberto Raggi committed
598
599
600
    m_watcher.setFuture(result);

    Core::FutureProgress *progress = progressManager->addTask(result, tr("Searching..."),
601
                                                              CppTools::Constants::TASK_SEARCH,
Roberto Raggi's avatar
Roberto Raggi committed
602
603
604
605
606
                                                              Core::ProgressManager::CloseOnSuccess);

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

607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
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));
        tc.setPosition(tc.position() + item.searchTermStart);
        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
624
625
626
627
628
629
630
631
632
633
634
void CppFindReferences::onReplaceButtonClicked(const QString &text,
                                               const QList<Find::SearchResultItem> &items)
{
    if (text.isEmpty())
        return;

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

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

635
636
    Core::EditorManager *editorManager = Core::EditorManager::instance();

Roberto Raggi's avatar
Roberto Raggi committed
637
638
639
640
641
    QHashIterator<QString, QList<Find::SearchResultItem> > it(changes);
    while (it.hasNext()) {
        it.next();

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

644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
        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
659

660
661
            if (file.open(QFile::ReadOnly)) {
                QTextStream stream(&file);
Roberto Raggi's avatar
Roberto Raggi committed
662
                // ### set the encoding
663
664
665
666
667
668
669
670
671
672
673
674
675
676
                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
677
678
679
680
681
682
            }
        }
    }

    const QStringList fileNames = changes.keys();
    _modelManager->updateSourceFiles(fileNames);
683
    _resultWindow->hide();
Roberto Raggi's avatar
Roberto Raggi committed
684
685
}

Roberto Raggi's avatar
Roberto Raggi committed
686
687
void CppFindReferences::displayResult(int index)
{
688
    Utils::FileSearchResult result = m_watcher.future().resultAt(index);
689
690
691
692
693
    _resultWindow->addResult(result.fileName,
                             result.lineNumber,
                             result.matchingLine,
                             result.matchStart,
                             result.matchLength);
Roberto Raggi's avatar
Roberto Raggi committed
694
695
696
697
698
699
700
}

void CppFindReferences::searchFinished()
{
    emit changed();
}

701
void CppFindReferences::openEditor(const Find::SearchResultItem &item)
Roberto Raggi's avatar
Roberto Raggi committed
702
{
703
    TextEditor::BaseTextEditor::openEditorAt(item.fileName, item.lineNumber, item.searchTermStart);
Roberto Raggi's avatar
Roberto Raggi committed
704
705
}