/************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2010 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://qt.nokia.com/contact. ** **************************************************************************/ #include "cppfindreferences.h" #include "cppmodelmanagerinterface.h" #include "cpptoolsconstants.h" #include <texteditor/basetexteditor.h> #include <texteditor/basefilefind.h> #include <find/searchresultwindow.h> #include <extensionsystem/pluginmanager.h> #include <utils/filesearch.h> #include <coreplugin/progressmanager/progressmanager.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/icore.h> #include <ASTVisitor.h> #include <AST.h> #include <Control.h> #include <Literals.h> #include <TranslationUnit.h> #include <Symbols.h> #include <Names.h> #include <Scope.h> #include <cplusplus/CppDocument.h> #include <cplusplus/CppBindings.h> #include <cplusplus/Overview.h> #include <QtCore/QTime> #include <QtCore/QtConcurrentRun> #include <QtCore/QtConcurrentMap> #include <QtCore/QDir> #include <QtGui/QApplication> #include <qtconcurrent/runextensions.h> #include <functional> using namespace CppTools::Internal; using namespace CPlusPlus; static QString getSource(const QString &fileName, const CppTools::CppModelManagerInterface::WorkingCopy &workingCopy) { if (workingCopy.contains(fileName)) { return workingCopy.source(fileName); } else { QFile file(fileName); if (! file.open(QFile::ReadOnly)) return QString(); return QTextStream(&file).readAll(); // ### FIXME } } namespace { class ProcessFile: public std::unary_function<QString, QList<Usage> > { const CppTools::CppModelManagerInterface::WorkingCopy workingCopy; const Snapshot snapshot; Document::Ptr symbolDocument; Symbol *symbol; public: ProcessFile(const CppTools::CppModelManagerInterface::WorkingCopy &workingCopy, const Snapshot snapshot, Document::Ptr symbolDocument, Symbol *symbol) : workingCopy(workingCopy), snapshot(snapshot), symbolDocument(symbolDocument), symbol(symbol) { } QList<Usage> operator()(const QString &fileName) { QList<Usage> usages; const Identifier *symbolId = symbol->identifier(); if (Document::Ptr previousDoc = snapshot.document(fileName)) { Control *control = previousDoc->control(); if (! control->findIdentifier(symbolId->chars(), symbolId->size())) return usages; // skip this document, it's not using symbolId. } QByteArray source = snapshot.preprocessedCode( getSource(fileName, workingCopy), 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 UpdateUI: public std::binary_function<QList<Usage> &, QList<Usage>, void> { QFutureInterface<Usage> *future; public: UpdateUI(QFutureInterface<Usage> *future): future(future) {} void operator()(QList<Usage> &, const QList<Usage> &usages) { foreach (const Usage &u, usages) future->reportResult(u); future->setProgressValue(future->progressValue() + 1); } }; } // 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; } static void find_helper(QFutureInterface<Usage> &future, const CppTools::CppModelManagerInterface::WorkingCopy workingCopy, Snapshot snapshot, Document::Ptr symbolDocument, Symbol *symbol) { QTime tm; tm.start(); const Identifier *symbolId = symbol->identifier(); Q_ASSERT(symbolId != 0); const QString sourceFile = QString::fromUtf8(symbol->fileName(), symbol->fileNameLength()); QStringList files(sourceFile); 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.filesDependingOn(sourceFile); } files.removeDuplicates(); //qDebug() << "done in:" << tm.elapsed() << "number of files to parse:" << files.size(); future.setProgressRange(0, files.size()); ProcessFile process(workingCopy, snapshot, symbolDocument, symbol); UpdateUI reduce(&future); QtConcurrent::blockingMappedReduced<QList<Usage> > (files, process, reduce); future.setProgressValue(files.size()); } void CppFindReferences::findUsages(Document::Ptr symbolDocument, Symbol *symbol) { Find::SearchResult *search = _resultWindow->startNewSearch(Find::SearchResultWindow::SearchOnly); connect(search, SIGNAL(activated(Find::SearchResultItem)), this, SLOT(openEditor(Find::SearchResultItem))); findAll_helper(symbolDocument, symbol); } void CppFindReferences::renameUsages(Document::Ptr symbolDocument, Symbol *symbol) { if (const Identifier *id = symbol->identifier()) { const QString textToReplace = QString::fromUtf8(id->chars(), id->size()); Find::SearchResult *search = _resultWindow->startNewSearch(Find::SearchResultWindow::SearchAndReplace); _resultWindow->setTextToReplace(textToReplace); connect(search, SIGNAL(activated(Find::SearchResultItem)), this, SLOT(openEditor(Find::SearchResultItem))); connect(search, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>)), SLOT(onReplaceButtonClicked(QString,QList<Find::SearchResultItem>))); findAll_helper(symbolDocument, symbol); } } void CppFindReferences::findAll_helper(Document::Ptr symbolDocument, Symbol *symbol) { if (! (symbol && symbol->identifier())) return; _resultWindow->popup(true); const Snapshot snapshot = _modelManager->snapshot(); const CppTools::CppModelManagerInterface::WorkingCopy workingCopy = _modelManager->workingCopy(); Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager(); QFuture<Usage> result; result = QtConcurrent::run(&find_helper, workingCopy, snapshot, symbolDocument, symbol); m_watcher.setFuture(result); Core::FutureProgress *progress = progressManager->addTask(result, tr("Searching..."), CppTools::Constants::TASK_SEARCH); connect(progress, SIGNAL(clicked()), _resultWindow, SLOT(popup())); } void CppFindReferences::onReplaceButtonClicked(const QString &text, const QList<Find::SearchResultItem> &items) { Core::EditorManager::instance()->hideEditorInfoBar(QLatin1String("CppEditor.Rename")); const QStringList fileNames = TextEditor::BaseFileFind::replaceAll(text, items); if (!fileNames.isEmpty()) { _modelManager->updateSourceFiles(fileNames); _resultWindow->hide(); } } void CppFindReferences::displayResults(int first, int last) { 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); } } void CppFindReferences::searchFinished() { _resultWindow->finishSearch(); emit changed(); } void CppFindReferences::openEditor(const Find::SearchResultItem &item) { TextEditor::BaseTextEditor::openEditorAt(item.fileName, item.lineNumber, item.searchTermStart); } namespace { class FindMacroUsesInFile: public std::unary_function<QString, QList<Usage> > { const CppTools::CppModelManagerInterface::WorkingCopy workingCopy; const Snapshot snapshot; const Macro ¯o; public: FindMacroUsesInFile(const CppTools::CppModelManagerInterface::WorkingCopy &workingCopy, const Snapshot snapshot, const Macro ¯o) : workingCopy(workingCopy), snapshot(snapshot), macro(macro) { } QList<Usage> operator()(const QString &fileName) { QList<Usage> usages; 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, const CppTools::CppModelManagerInterface::WorkingCopy workingCopy, const Snapshot snapshot, const Macro macro) { const QString& sourceFile = macro.fileName(); QStringList files(sourceFile); files += snapshot.filesDependingOn(sourceFile); files.removeDuplicates(); future.setProgressRange(0, files.size()); FindMacroUsesInFile process(workingCopy, snapshot, macro); UpdateUI reduce(&future); QtConcurrent::blockingMappedReduced<QList<Usage> > (files, process, reduce); future.setProgressValue(files.size()); } void CppFindReferences::findMacroUses(const Macro ¯o) { Find::SearchResult *search = _resultWindow->startNewSearch(Find::SearchResultWindow::SearchOnly); _resultWindow->popup(true); connect(search, SIGNAL(activated(Find::SearchResultItem)), this, SLOT(openEditor(Find::SearchResultItem))); const Snapshot snapshot = _modelManager->snapshot(); const CppTools::CppModelManagerInterface::WorkingCopy workingCopy = _modelManager->workingCopy(); // add the macro definition itself { // ### FIXME: Encoding? const QByteArray &source = getSource(macro.fileName(), workingCopy).toLatin1(); _resultWindow->addResult(macro.fileName(), macro.line(), source.mid(macro.offset(), macro.length()), 0, macro.length()); } QFuture<Usage> result; result = QtConcurrent::run(&findMacroUses_helper, workingCopy, snapshot, macro); m_watcher.setFuture(result); Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager(); Core::FutureProgress *progress = progressManager->addTask(result, tr("Searching..."), CppTools::Constants::TASK_SEARCH); connect(progress, SIGNAL(clicked()), _resultWindow, SLOT(popup())); }