Commit 050b4dd2 authored by Ivan Donchevskii's avatar Ivan Donchevskii

Clang: implement findUsages with existing index

Functionality is limited to the abilities of
current index which is not updated and is
generated only at project open.
Search box temporarily doesn't allow to "Search again".

Change-Id: Id1047f27ad0aafc901f06aa51ad38ceab95eaebb
Reviewed-by: Marco Bubke's avatarMarco Bubke <marco.bubke@qt.io>
Reviewed-by: Nikolai Kosjar's avatarNikolai Kosjar <nikolai.kosjar@qt.io>
parent 263cdc03
...@@ -12,7 +12,8 @@ HEADERS += \ ...@@ -12,7 +12,8 @@ HEADERS += \
$$PWD/clangqueryhighlightmarker.h \ $$PWD/clangqueryhighlightmarker.h \
$$PWD/clangqueryexamplehighlighter.h \ $$PWD/clangqueryexamplehighlighter.h \
$$PWD/clangqueryhighlighter.h \ $$PWD/clangqueryhighlighter.h \
$$PWD/refactoringprojectupdater.h $$PWD/refactoringprojectupdater.h \
$$PWD/symbolqueryinterface.h
SOURCES += \ SOURCES += \
$$PWD/refactoringengine.cpp \ $$PWD/refactoringengine.cpp \
......
...@@ -24,8 +24,13 @@ ...@@ -24,8 +24,13 @@
****************************************************************************/ ****************************************************************************/
#include "clangrefactoringplugin.h" #include "clangrefactoringplugin.h"
#include "symbolquery.h"
#include "sqlitereadstatement.h"
#include "sqlitedatabase.h"
#include "querysqlitestatementfactory.h"
#include <clangpchmanager/qtcreatorprojectupdater.h> #include <clangpchmanager/qtcreatorprojectupdater.h>
#include <clangsupport/refactoringdatabaseinitializer.h>
#include <cpptools/cppmodelmanager.h> #include <cpptools/cppmodelmanager.h>
...@@ -41,6 +46,7 @@ ...@@ -41,6 +46,7 @@
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <QDir> #include <QDir>
#include <QApplication>
namespace ClangRefactoring { namespace ClangRefactoring {
...@@ -59,21 +65,23 @@ std::unique_ptr<ClangRefactoringPluginData> ClangRefactoringPlugin::d; ...@@ -59,21 +65,23 @@ std::unique_ptr<ClangRefactoringPluginData> ClangRefactoringPlugin::d;
class ClangRefactoringPluginData class ClangRefactoringPluginData
{ {
using ProjectUpdater = ClangPchManager::QtCreatorProjectUpdater<ClangPchManager::ProjectUpdater>;
public: public:
using QuerySqliteStatementFactory = QuerySqliteStatementFactory<Sqlite::Database,
Sqlite::ReadStatement>;
Sqlite::Database database{Utils::PathString{QDir::tempPath() + "/symbol.db"}}; Sqlite::Database database{Utils::PathString{QDir::tempPath() + "/symbol.db"}};
ClangBackEnd::RefactoringDatabaseInitializer<Sqlite::Database> databaseInitializer{database}; ClangBackEnd::RefactoringDatabaseInitializer<Sqlite::Database> databaseInitializer{database};
ClangBackEnd::FilePathCaching filePathCache{database}; ClangBackEnd::FilePathCaching filePathCache{database};
RefactoringClient refactoringClient; RefactoringClient refactoringClient;
ClangBackEnd::RefactoringConnectionClient connectionClient{&refactoringClient}; ClangBackEnd::RefactoringConnectionClient connectionClient{&refactoringClient};
RefactoringEngine engine{connectionClient.serverProxy(), refactoringClient, filePathCache}; QuerySqliteStatementFactory statementFactory{database};
SymbolQuery<QuerySqliteStatementFactory> symbolQuery{statementFactory};
RefactoringEngine engine{connectionClient.serverProxy(), refactoringClient, filePathCache, symbolQuery};
QtCreatorSearch qtCreatorSearch{*Core::SearchResultWindow::instance()}; QtCreatorSearch qtCreatorSearch{*Core::SearchResultWindow::instance()};
QtCreatorClangQueryFindFilter qtCreatorfindFilter{connectionClient.serverProxy(), QtCreatorClangQueryFindFilter qtCreatorfindFilter{connectionClient.serverProxy(),
qtCreatorSearch, qtCreatorSearch,
refactoringClient}; refactoringClient};
ProjectUpdater projectUpdate{connectionClient.serverProxy()};
}; };
ClangRefactoringPlugin::ClangRefactoringPlugin() ClangRefactoringPlugin::ClangRefactoringPlugin()
......
...@@ -24,20 +24,22 @@ ...@@ -24,20 +24,22 @@
****************************************************************************/ ****************************************************************************/
#include "refactoringengine.h" #include "refactoringengine.h"
#include "projectpartutilities.h" #include "projectpartutilities.h"
#include <refactoringserverinterface.h> #include <refactoringserverinterface.h>
#include <requestsourcelocationforrenamingmessage.h> #include <requestsourcelocationforrenamingmessage.h>
#include <cpptools/compileroptionsbuilder.h> #include <cpptools/compileroptionsbuilder.h>
#include <cpptools/cpptoolsreuse.h> #include <cpptools/cpptoolsreuse.h>
#include <texteditor/textdocument.h> #include <clangsupport/filepathcachinginterface.h>
#include <utils/textutils.h>
#include <QTextCursor> #include <QTextCursor>
#include <QTextDocument> #include <QTextDocument>
#include <QTextBlock>
#include <QDir>
#include <algorithm> #include <algorithm>
...@@ -47,13 +49,17 @@ using ClangBackEnd::RequestSourceLocationsForRenamingMessage; ...@@ -47,13 +49,17 @@ using ClangBackEnd::RequestSourceLocationsForRenamingMessage;
RefactoringEngine::RefactoringEngine(ClangBackEnd::RefactoringServerInterface &server, RefactoringEngine::RefactoringEngine(ClangBackEnd::RefactoringServerInterface &server,
ClangBackEnd::RefactoringClientInterface &client, ClangBackEnd::RefactoringClientInterface &client,
ClangBackEnd::FilePathCachingInterface &filePathCache) ClangBackEnd::FilePathCachingInterface &filePathCache,
SymbolQueryInterface &symbolQuery)
: m_server(server), : m_server(server),
m_client(client), m_client(client),
m_filePathCache(filePathCache) m_filePathCache(filePathCache),
m_symbolQuery(symbolQuery)
{ {
} }
RefactoringEngine::~RefactoringEngine() = default;
void RefactoringEngine::startLocalRenaming(const CppTools::CursorInEditor &data, void RefactoringEngine::startLocalRenaming(const CppTools::CursorInEditor &data,
CppTools::ProjectPart *projectPart, CppTools::ProjectPart *projectPart,
RenameCallback &&renameSymbolsCallback) RenameCallback &&renameSymbolsCallback)
...@@ -89,6 +95,35 @@ void RefactoringEngine::startGlobalRenaming(const CppTools::CursorInEditor &) ...@@ -89,6 +95,35 @@ void RefactoringEngine::startGlobalRenaming(const CppTools::CursorInEditor &)
// TODO: implement // TODO: implement
} }
CppTools::Usages RefactoringEngine::locationsAt(const CppTools::CursorInEditor &data) const
{
int line = 0, column = 0;
QTextCursor cursor = Utils::Text::wordStartCursor(data.cursor());
Utils::Text::convertPosition(cursor.document(), cursor.position(), &line, &column);
const QByteArray filePath = data.filePath().toString().toLatin1();
const ClangBackEnd::FilePathId filePathId = m_filePathCache.filePathId(filePath.constData());
ClangRefactoring::SourceLocations usages = m_symbolQuery.locationsAt(filePathId, line,
column + 1);
CppTools::Usages result;
result.reserve(usages.size());
for (const auto &location : usages) {
const Utils::SmallStringView path = m_filePathCache.filePath(location.filePathId).path();
result.push_back({path, location.line, location.column});
}
return result;
}
void RefactoringEngine::findUsages(const CppTools::CursorInEditor &data,
CppTools::UsagesCallback &&showUsagesCallback) const
{
int line = 0, column = 0;
QTextCursor cursor = Utils::Text::wordStartCursor(data.cursor());
Utils::Text::convertPosition(cursor.document(), cursor.position(), &line, &column);
showUsagesCallback(locationsAt(data));
}
bool RefactoringEngine::isRefactoringEngineAvailable() const bool RefactoringEngine::isRefactoringEngineAvailable() const
{ {
return m_server.isAvailable(); return m_server.isAvailable();
......
...@@ -25,7 +25,9 @@ ...@@ -25,7 +25,9 @@
#pragma once #pragma once
#include <filepathcachingfwd.h> #include "symbolqueryinterface.h"
#include <clangsupport/filepathcachingfwd.h>
#include <cpptools/refactoringengineinterface.h> #include <cpptools/refactoringengineinterface.h>
...@@ -41,25 +43,33 @@ class RefactoringEngine : public CppTools::RefactoringEngineInterface ...@@ -41,25 +43,33 @@ class RefactoringEngine : public CppTools::RefactoringEngineInterface
public: public:
RefactoringEngine(ClangBackEnd::RefactoringServerInterface &m_server, RefactoringEngine(ClangBackEnd::RefactoringServerInterface &m_server,
ClangBackEnd::RefactoringClientInterface &m_client, ClangBackEnd::RefactoringClientInterface &m_client,
ClangBackEnd::FilePathCachingInterface &filePathCache); ClangBackEnd::FilePathCachingInterface &filePathCache,
SymbolQueryInterface &symbolQuery);
~RefactoringEngine() override;
void startLocalRenaming(const CppTools::CursorInEditor &data, void startLocalRenaming(const CppTools::CursorInEditor &data,
CppTools::ProjectPart *projectPart, CppTools::ProjectPart *projectPart,
RenameCallback &&renameSymbolsCallback) override; RenameCallback &&renameSymbolsCallback) override;
void startGlobalRenaming(const CppTools::CursorInEditor &data) override; void startGlobalRenaming(const CppTools::CursorInEditor &data) override;
void findUsages(const CppTools::CursorInEditor &data,
CppTools::UsagesCallback &&showUsagesCallback) const override;
bool isRefactoringEngineAvailable() const override; bool isRefactoringEngineAvailable() const override;
void setRefactoringEngineAvailable(bool isAvailable); void setRefactoringEngineAvailable(bool isAvailable);
ClangBackEnd::FilePathCachingInterface &filePathCache() const ClangBackEnd::FilePathCachingInterface &filePathCache() const
{ {
return m_filePathCache; return m_filePathCache;
} }
private: private:
CppTools::Usages locationsAt(const CppTools::CursorInEditor &data) const;
ClangBackEnd::RefactoringServerInterface &m_server; ClangBackEnd::RefactoringServerInterface &m_server;
ClangBackEnd::RefactoringClientInterface &m_client; ClangBackEnd::RefactoringClientInterface &m_client;
ClangBackEnd::FilePathCachingInterface &m_filePathCache; ClangBackEnd::FilePathCachingInterface &m_filePathCache;
SymbolQueryInterface &m_symbolQuery;
}; };
} // namespace ClangRefactoring } // namespace ClangRefactoring
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
#pragma once #pragma once
#include <utils/smallstring.h> #include "symbolqueryinterface.h"
#include <filepathid.h> #include <filepathid.h>
#include <sourcelocations.h> #include <sourcelocations.h>
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
namespace ClangRefactoring { namespace ClangRefactoring {
template <typename StatementFactory> template <typename StatementFactory>
class SymbolQuery class SymbolQuery final : public SymbolQueryInterface
{ {
using ReadStatement = typename StatementFactory::ReadStatementType; using ReadStatement = typename StatementFactory::ReadStatementType;
...@@ -46,7 +46,7 @@ public: ...@@ -46,7 +46,7 @@ public:
: m_statementFactory(statementFactory) : m_statementFactory(statementFactory)
{} {}
SourceLocations locationsAt(ClangBackEnd::FilePathId filePathId, int line, int utf8Column) SourceLocations locationsAt(ClangBackEnd::FilePathId filePathId, int line, int utf8Column) override
{ {
ReadStatement &locationsStatement = m_statementFactory.selectLocationsForSymbolLocation; ReadStatement &locationsStatement = m_statementFactory.selectLocationsForSymbolLocation;
......
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "sourcelocations.h"
namespace ClangRefactoring {
class SymbolQueryInterface
{
public:
virtual SourceLocations locationsAt(ClangBackEnd::FilePathId filePathId, int line, int utf8Column) = 0;
};
} // namespace ClangRefactoring
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/editormanager/documentmodel.h> #include <coreplugin/editormanager/documentmodel.h>
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/find/searchresultwindow.h>
#include <coreplugin/infobar.h> #include <coreplugin/infobar.h>
#include <cpptools/cppcanonicalsymbol.h> #include <cpptools/cppcanonicalsymbol.h>
...@@ -344,23 +345,73 @@ void CppEditorWidget::onShowInfoBarAction(const Id &id, bool show) ...@@ -344,23 +345,73 @@ void CppEditorWidget::onShowInfoBarAction(const Id &id, bool show)
action->setVisible(show); action->setVisible(show);
} }
void CppEditorWidget::findUsages() static QString getDocumentLine(const QTextDocument &document, int line)
{ {
if (!d->m_modelManager) return document.findBlockByNumber(line - 1).text();
return; }
SemanticInfo info = d->m_lastSemanticInfo; static QString getFileLine(const QString &path, int line)
info.snapshot = CppModelManager::instance()->snapshot(); {
info.snapshot.insert(info.doc); const IDocument *document = DocumentModel::documentForFilePath(path);
const TextDocument *textDocument = qobject_cast<const TextDocument *>(document);
if (textDocument)
return getDocumentLine(*textDocument->document(), line);
const QTextCodec *defaultCodec = Core::EditorManager::defaultTextCodec();
QString contents;
Utils::TextFileFormat format;
QString error;
if (Utils::TextFileFormat::readFile(path, defaultCodec, &contents, &format, &error)
!= Utils::TextFileFormat::ReadSuccess) {
qWarning() << "Error reading file " << path << " : " << error;
return QString();
}
if (const Macro *macro = CppTools::findCanonicalMacro(textCursor(), info.doc)) { const QTextDocument tmpDocument{contents};
d->m_modelManager->findMacroUsages(*macro); return getDocumentLine(tmpDocument, line);
} else { }
CanonicalSymbol cs(info.doc, info.snapshot);
Symbol *canonicalSymbol = cs(textCursor()); static void findRenameCallback(QTextCursor cursor,
if (canonicalSymbol) const CppTools::Usages &usages,
d->m_modelManager->findUsages(canonicalSymbol, cs.context()); bool rename = false)
{
cursor = Utils::Text::wordStartCursor(cursor);
cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
QString text = cursor.selectedText();
SearchResultWindow::SearchMode mode = SearchResultWindow::SearchOnly;
if (rename)
mode = SearchResultWindow::SearchAndReplace;
SearchResult *search = SearchResultWindow::instance()->startNewSearch(
QObject::tr("C++ Usages:"),
QString(),
text,
mode,
SearchResultWindow::PreserveCaseDisabled,
QLatin1String("CppEditor"));
for (const CppTools::Usage &usage : usages) {
const QString lineStr = getFileLine(usage.path, usage.line);
if (lineStr.isEmpty())
continue;
Search::TextRange range{Search::TextPosition(usage.line, usage.column - 1),
Search::TextPosition(usage.line, usage.column + text.length() - 1)};
search->addResult(usage.path, lineStr, range);
} }
search->finishSearch(false);
QObject::connect(search, &SearchResult::activated,
[](const Core::SearchResultItem& item) {
Core::EditorManager::openEditorAtSearchResult(item);
});
search->popup();
}
void CppEditorWidget::findUsages()
{
refactoringEngine().findUsages(CppTools::CursorInEditor{textCursor(),
textDocument()->filePath(),
this},
[this](const CppTools::Usages &usages) {
findRenameCallback(textCursor(), usages);
});
} }
void CppEditorWidget::renameUsagesInternal(const QString &replacement) void CppEditorWidget::renameUsagesInternal(const QString &replacement)
......
...@@ -59,7 +59,6 @@ public: ...@@ -59,7 +59,6 @@ public:
CppEditorDocument *cppEditorDocument() const; CppEditorDocument *cppEditorDocument() const;
CppTools::CppEditorOutline *outline() const; CppTools::CppEditorOutline *outline() const;
CppTools::SemanticInfo semanticInfo() const;
bool isSemanticInfoValidExceptLocalUses() const; bool isSemanticInfoValidExceptLocalUses() const;
bool isSemanticInfoValid() const; bool isSemanticInfoValid() const;
...@@ -88,6 +87,7 @@ public: ...@@ -88,6 +87,7 @@ public:
static void updateWidgetHighlighting(QWidget *widget, bool highlight); static void updateWidgetHighlighting(QWidget *widget, bool highlight);
static bool isWidgetHighlighted(QWidget *widget); static bool isWidgetHighlighted(QWidget *widget);
CppTools::SemanticInfo semanticInfo() const override;
void updateSemanticInfo() override; void updateSemanticInfo() override;
void invokeTextEditorWidgetAssist(TextEditor::AssistKind assistKind, void invokeTextEditorWidgetAssist(TextEditor::AssistKind assistKind,
TextEditor::IAssistProvider *provider) override; TextEditor::IAssistProvider *provider) override;
......
...@@ -35,6 +35,8 @@ namespace TextEditor { class IAssistProvider; } ...@@ -35,6 +35,8 @@ namespace TextEditor { class IAssistProvider; }
namespace CppTools { namespace CppTools {
class SemanticInfo;
class CPPTOOLS_EXPORT CppEditorWidgetInterface class CPPTOOLS_EXPORT CppEditorWidgetInterface
{ {
public: public:
...@@ -44,6 +46,7 @@ public: ...@@ -44,6 +46,7 @@ public:
} }
virtual void showPreProcessorWidget() = 0; virtual void showPreProcessorWidget() = 0;
virtual SemanticInfo semanticInfo() const = 0;
virtual void updateSemanticInfo() = 0; virtual void updateSemanticInfo() = 0;
virtual void renameUsagesInternal(const QString &replacement) = 0; virtual void renameUsagesInternal(const QString &replacement) = 0;
......
...@@ -300,6 +300,13 @@ void CppModelManager::startGlobalRenaming(const CursorInEditor &data) ...@@ -300,6 +300,13 @@ void CppModelManager::startGlobalRenaming(const CursorInEditor &data)
engine->startGlobalRenaming(data); engine->startGlobalRenaming(data);
} }
void CppModelManager::findUsages(const CppTools::CursorInEditor &data,
UsagesCallback &&showUsagesCallback) const
{
RefactoringEngineInterface *engine = getRefactoringEngine(instance()->d->m_refactoringEngines);
engine->findUsages(data, std::move(showUsagesCallback));
}
void CppModelManager::addRefactoringEngine(RefactoringEngineType type, void CppModelManager::addRefactoringEngine(RefactoringEngineType type,
RefactoringEngineInterface *refactoringEngine) RefactoringEngineInterface *refactoringEngine)
{ {
......
...@@ -152,6 +152,8 @@ public: ...@@ -152,6 +152,8 @@ public:
CppTools::ProjectPart *projectPart, CppTools::ProjectPart *projectPart,
RenameCallback &&renameSymbolsCallback) final; RenameCallback &&renameSymbolsCallback) final;
void startGlobalRenaming(const CursorInEditor &data) final; void startGlobalRenaming(const CursorInEditor &data) final;
void findUsages(const CppTools::CursorInEditor &data,
UsagesCallback &&showUsagesCallback) const final;
void renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context, void renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context,
const QString &replacement = QString()); const QString &replacement = QString());
......
...@@ -23,15 +23,20 @@ ...@@ -23,15 +23,20 @@
** **
****************************************************************************/ ****************************************************************************/
#include "cppcanonicalsymbol.h"
#include "cppmodelmanager.h"
#include "cpprefactoringengine.h" #include "cpprefactoringengine.h"
#include "texteditor/texteditor.h" #include "cppsemanticinfo.h"
#include "cpptoolsreuse.h"
#include "utils/qtcassert.h" #include <texteditor/texteditor.h>
#include <utils/qtcassert.h>
namespace CppTools { namespace CppTools {
void CppRefactoringEngine::startLocalRenaming(const CppTools::CursorInEditor &data, void CppRefactoringEngine::startLocalRenaming(const CursorInEditor &data,
CppTools::ProjectPart *, ProjectPart *,
RenameCallback &&renameSymbolsCallback) RenameCallback &&renameSymbolsCallback)
{ {
CppEditorWidgetInterface *editorWidget = data.editorWidget(); CppEditorWidgetInterface *editorWidget = data.editorWidget();
...@@ -45,11 +50,35 @@ void CppRefactoringEngine::startLocalRenaming(const CppTools::CursorInEditor &da ...@@ -45,11 +50,35 @@ void CppRefactoringEngine::startLocalRenaming(const CppTools::CursorInEditor &da
data.cursor().document()->revision()); data.cursor().document()->revision());
} }
void CppRefactoringEngine::startGlobalRenaming(const CppTools::CursorInEditor &data) void CppRefactoringEngine::startGlobalRenaming(const CursorInEditor &data)
{ {
CppEditorWidgetInterface *editorWidget = data.editorWidget(); CppEditorWidgetInterface *editorWidget = data.editorWidget();
QTC_ASSERT(editorWidget, return;); QTC_ASSERT(editorWidget, return;);
editorWidget->renameUsages(); editorWidget->renameUsages();
} }
void CppRefactoringEngine::findUsages(const CursorInEditor &data,
UsagesCallback &&) const
{
CppModelManager *modelManager = CppModelManager::instance();
if (!modelManager)
return;
CppEditorWidgetInterface *editorWidget = data.editorWidget();
QTC_ASSERT(editorWidget, return;);
SemanticInfo info = editorWidget->semanticInfo();
info.snapshot = modelManager->snapshot();
info.snapshot.insert(info.doc);
const QTextCursor &cursor = data.cursor();
if (const CPlusPlus::Macro *macro = findCanonicalMacro(cursor, info.doc)) {
modelManager->findMacroUsages(*macro);
} else {
CanonicalSymbol cs(info.doc, info.snapshot);
CPlusPlus::Symbol *canonicalSymbol = cs(cursor);
if (canonicalSymbol)
modelManager->findUsages(canonicalSymbol, cs.context());
}
}
} // namespace CppEditor } // namespace CppEditor
...@@ -32,10 +32,12 @@ namespace CppTools { ...@@ -32,10 +32,12 @@ namespace CppTools {
class CPPTOOLS_EXPORT CppRefactoringEngine : public RefactoringEngineInterface class CPPTOOLS_EXPORT CppRefactoringEngine : public RefactoringEngineInterface