forked from qt-creator/qt-creator
ClangCodeModel: Implement global renaming via clangd
Note that we do not use the LSP rename functionality. We do "manual" renaming the same way as in the built-in code model, but based on the references found by clangd. Change-Id: Ifa5597efe5c89c8f9204a4f5323bc755544696cf Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -32,7 +32,12 @@
|
|||||||
#include <cpptools/cppfindreferences.h>
|
#include <cpptools/cppfindreferences.h>
|
||||||
#include <cpptools/cpptoolsreuse.h>
|
#include <cpptools/cpptoolsreuse.h>
|
||||||
#include <languageclient/languageclientinterface.h>
|
#include <languageclient/languageclientinterface.h>
|
||||||
|
#include <projectexplorer/projecttree.h>
|
||||||
|
#include <projectexplorer/session.h>
|
||||||
|
#include <texteditor/basefilefind.h>
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
|
||||||
|
#include <QCheckBox>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
@@ -42,6 +47,7 @@ using namespace CPlusPlus;
|
|||||||
using namespace Core;
|
using namespace Core;
|
||||||
using namespace LanguageClient;
|
using namespace LanguageClient;
|
||||||
using namespace LanguageServerProtocol;
|
using namespace LanguageServerProtocol;
|
||||||
|
using namespace ProjectExplorer;
|
||||||
|
|
||||||
namespace ClangCodeModel {
|
namespace ClangCodeModel {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
@@ -296,15 +302,20 @@ public:
|
|||||||
QString fileContent;
|
QString fileContent;
|
||||||
AstNode ast;
|
AstNode ast;
|
||||||
};
|
};
|
||||||
|
class ReplacementData {
|
||||||
|
public:
|
||||||
|
QString oldSymbolName;
|
||||||
|
QString newSymbolName;
|
||||||
|
QSet<Utils::FilePath> fileRenameCandidates;
|
||||||
|
};
|
||||||
class ReferencesData {
|
class ReferencesData {
|
||||||
public:
|
public:
|
||||||
void setCanceled() { search->setUserData(true); }
|
|
||||||
bool isCanceled() const { return search && search->userData().toBool(); }
|
|
||||||
|
|
||||||
QMap<DocumentUri, ReferencesFileData> fileData;
|
QMap<DocumentUri, ReferencesFileData> fileData;
|
||||||
QList<MessageId> pendingAstRequests;
|
QList<MessageId> pendingAstRequests;
|
||||||
QPointer<SearchResult> search;
|
QPointer<SearchResult> search;
|
||||||
|
Utils::optional<ReplacementData> replacementData;
|
||||||
quint64 key;
|
quint64 key;
|
||||||
|
bool canceled = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ClangdClient::Private
|
class ClangdClient::Private
|
||||||
@@ -313,9 +324,14 @@ public:
|
|||||||
Private(ClangdClient *q) : q(q) {}
|
Private(ClangdClient *q) : q(q) {}
|
||||||
|
|
||||||
void handleFindUsagesResult(quint64 key, const QList<Location> &locations);
|
void handleFindUsagesResult(quint64 key, const QList<Location> &locations);
|
||||||
void addSearchResultsForFile(const ReferencesData &refData, const Utils::FilePath &file,
|
static void handleRenameRequest(const SearchResult *search,
|
||||||
|
const ReplacementData &replacementData,
|
||||||
|
const QString &newSymbolName,
|
||||||
|
const QList<Core::SearchResultItem> &checkedItems,
|
||||||
|
bool preserveCase);
|
||||||
|
void addSearchResultsForFile(ReferencesData &refData, const Utils::FilePath &file,
|
||||||
const ReferencesFileData &fileData);
|
const ReferencesFileData &fileData);
|
||||||
void reportAllSearchResultsAndFinish(const ReferencesData &data);
|
void reportAllSearchResultsAndFinish(ReferencesData &data);
|
||||||
void finishSearch(const ReferencesData &refData, bool canceled);
|
void finishSearch(const ReferencesData &refData, bool canceled);
|
||||||
|
|
||||||
ClangdClient * const q;
|
ClangdClient * const q;
|
||||||
@@ -326,7 +342,7 @@ public:
|
|||||||
bool isTesting = false;
|
bool isTesting = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
ClangdClient::ClangdClient(ProjectExplorer::Project *project, const Utils::FilePath &jsonDbDir)
|
ClangdClient::ClangdClient(Project *project, const Utils::FilePath &jsonDbDir)
|
||||||
: Client(clientInterface(jsonDbDir)), d(new Private(this))
|
: Client(clientInterface(jsonDbDir)), d(new Private(this))
|
||||||
{
|
{
|
||||||
setName(tr("clangd"));
|
setName(tr("clangd"));
|
||||||
@@ -355,7 +371,7 @@ ClangdClient::ClangdClient(ProjectExplorer::Project *project, const Utils::FileP
|
|||||||
|
|
||||||
// Report all search results found so far.
|
// Report all search results found so far.
|
||||||
for (quint64 key : d->runningFindUsages.keys())
|
for (quint64 key : d->runningFindUsages.keys())
|
||||||
d->reportAllSearchResultsAndFinish(d->runningFindUsages.value(key));
|
d->reportAllSearchResultsAndFinish(d->runningFindUsages[key]);
|
||||||
QTC_CHECK(d->runningFindUsages.isEmpty());
|
QTC_CHECK(d->runningFindUsages.isEmpty());
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -388,13 +404,9 @@ void ClangdClient::closeExtraFile(const Utils::FilePath &filePath)
|
|||||||
TextDocumentIdentifier{DocumentUri::fromFilePath(filePath)})));
|
TextDocumentIdentifier{DocumentUri::fromFilePath(filePath)})));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangdClient::findUsages(TextEditor::TextDocument *document, const QTextCursor &cursor)
|
void ClangdClient::findUsages(TextEditor::TextDocument *document, const QTextCursor &cursor,
|
||||||
|
const Utils::optional<QString> &replacement)
|
||||||
{
|
{
|
||||||
if (versionNumber() < QVersionNumber(13)) {
|
|
||||||
symbolSupport().findUsages(document, cursor);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QTextCursor termCursor(cursor);
|
QTextCursor termCursor(cursor);
|
||||||
termCursor.select(QTextCursor::WordUnderCursor);
|
termCursor.select(QTextCursor::WordUnderCursor);
|
||||||
const QString searchTerm = termCursor.selectedText(); // TODO: This will be wrong for e.g. operators. Use a Symbol info request to get the real symbol string.
|
const QString searchTerm = termCursor.selectedText(); // TODO: This will be wrong for e.g. operators. Use a Symbol info request to get the real symbol string.
|
||||||
@@ -402,19 +414,42 @@ void ClangdClient::findUsages(TextEditor::TextDocument *document, const QTextCur
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
ReferencesData refData;
|
ReferencesData refData;
|
||||||
|
refData.key = d->nextFindUsagesKey++;
|
||||||
|
if (replacement) {
|
||||||
|
ReplacementData replacementData;
|
||||||
|
replacementData.oldSymbolName = searchTerm;
|
||||||
|
replacementData.newSymbolName = *replacement;
|
||||||
|
if (replacementData.newSymbolName.isEmpty())
|
||||||
|
replacementData.newSymbolName = replacementData.oldSymbolName;
|
||||||
|
refData.replacementData = replacementData;
|
||||||
|
}
|
||||||
refData.search = SearchResultWindow::instance()->startNewSearch(
|
refData.search = SearchResultWindow::instance()->startNewSearch(
|
||||||
tr("C++ Usages:"),
|
tr("C++ Usages:"),
|
||||||
{},
|
{},
|
||||||
searchTerm,
|
searchTerm,
|
||||||
SearchResultWindow::SearchOnly,
|
replacement ? SearchResultWindow::SearchAndReplace : SearchResultWindow::SearchOnly,
|
||||||
SearchResultWindow::PreserveCaseDisabled,
|
SearchResultWindow::PreserveCaseDisabled,
|
||||||
"CppEditor");
|
"CppEditor");
|
||||||
refData.search->setFilter(new CppTools::CppSearchResultFilter);
|
refData.search->setFilter(new CppTools::CppSearchResultFilter);
|
||||||
|
if (refData.replacementData) {
|
||||||
|
refData.search->setTextToReplace(refData.replacementData->newSymbolName);
|
||||||
|
const auto renameFilesCheckBox = new QCheckBox;
|
||||||
|
renameFilesCheckBox->setVisible(false);
|
||||||
|
refData.search->setAdditionalReplaceWidget(renameFilesCheckBox);
|
||||||
|
const auto renameHandler =
|
||||||
|
[search = refData.search](const QString &newSymbolName,
|
||||||
|
const QList<SearchResultItem> &checkedItems,
|
||||||
|
bool preserveCase) {
|
||||||
|
const auto replacementData = search->userData().value<ReplacementData>();
|
||||||
|
Private::handleRenameRequest(search, replacementData, newSymbolName, checkedItems,
|
||||||
|
preserveCase);
|
||||||
|
};
|
||||||
|
connect(refData.search, &SearchResult::replaceButtonClicked, renameHandler);
|
||||||
|
}
|
||||||
connect(refData.search, &SearchResult::activated, [](const SearchResultItem& item) {
|
connect(refData.search, &SearchResult::activated, [](const SearchResultItem& item) {
|
||||||
Core::EditorManager::openEditorAtSearchResult(item);
|
Core::EditorManager::openEditorAtSearchResult(item);
|
||||||
});
|
});
|
||||||
SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus);
|
SearchResultWindow::instance()->popup(IOutputPane::ModeSwitch | IOutputPane::WithFocus);
|
||||||
refData.key = d->nextFindUsagesKey++;
|
|
||||||
d->runningFindUsages.insert(refData.key, refData);
|
d->runningFindUsages.insert(refData.key, refData);
|
||||||
|
|
||||||
const Utils::optional<MessageId> requestId = symbolSupport().findUsages(
|
const Utils::optional<MessageId> requestId = symbolSupport().findUsages(
|
||||||
@@ -431,7 +466,7 @@ void ClangdClient::findUsages(TextEditor::TextDocument *document, const QTextCur
|
|||||||
if (refData == d->runningFindUsages.end())
|
if (refData == d->runningFindUsages.end())
|
||||||
return;
|
return;
|
||||||
cancelRequest(*requestId);
|
cancelRequest(*requestId);
|
||||||
refData->setCanceled();
|
refData->canceled = true;
|
||||||
refData->search->disconnect(this);
|
refData->search->disconnect(this);
|
||||||
d->finishSearch(*refData, true);
|
d->finishSearch(*refData, true);
|
||||||
});
|
});
|
||||||
@@ -462,7 +497,7 @@ void ClangdClient::Private::handleFindUsagesResult(quint64 key, const QList<Loca
|
|||||||
const auto refData = runningFindUsages.find(key);
|
const auto refData = runningFindUsages.find(key);
|
||||||
if (refData == runningFindUsages.end())
|
if (refData == runningFindUsages.end())
|
||||||
return;
|
return;
|
||||||
if (!refData->search || refData->isCanceled()) {
|
if (!refData->search || refData->canceled) {
|
||||||
finishSearch(*refData, true);
|
finishSearch(*refData, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -478,7 +513,7 @@ void ClangdClient::Private::handleFindUsagesResult(quint64 key, const QList<Loca
|
|||||||
const auto refData = runningFindUsages.find(key);
|
const auto refData = runningFindUsages.find(key);
|
||||||
if (refData == runningFindUsages.end())
|
if (refData == runningFindUsages.end())
|
||||||
return;
|
return;
|
||||||
refData->setCanceled();
|
refData->canceled = true;
|
||||||
refData->search->disconnect(q);
|
refData->search->disconnect(q);
|
||||||
for (const MessageId &id : qAsConst(refData->pendingAstRequests))
|
for (const MessageId &id : qAsConst(refData->pendingAstRequests))
|
||||||
q->cancelRequest(id);
|
q->cancelRequest(id);
|
||||||
@@ -499,7 +534,8 @@ void ClangdClient::Private::handleFindUsagesResult(quint64 key, const QList<Loca
|
|||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(clangdLog) << "document count is" << refData->fileData.size();
|
qCDebug(clangdLog) << "document count is" << refData->fileData.size();
|
||||||
if (refData->fileData.size() > 15) { // TODO: If we need to keep this, make it configurable.
|
if (refData->replacementData || q->versionNumber() < QVersionNumber(13)
|
||||||
|
|| refData->fileData.size() > 15) { // TODO: If we need to keep this, make it configurable.
|
||||||
qCDebug(clangdLog) << "skipping AST retrieval";
|
qCDebug(clangdLog) << "skipping AST retrieval";
|
||||||
reportAllSearchResultsAndFinish(*refData);
|
reportAllSearchResultsAndFinish(*refData);
|
||||||
return;
|
return;
|
||||||
@@ -520,7 +556,7 @@ void ClangdClient::Private::handleFindUsagesResult(quint64 key, const QList<Loca
|
|||||||
const auto refData = runningFindUsages.find(key);
|
const auto refData = runningFindUsages.find(key);
|
||||||
if (refData == runningFindUsages.end())
|
if (refData == runningFindUsages.end())
|
||||||
return;
|
return;
|
||||||
if (!refData->search || refData->isCanceled())
|
if (!refData->search || refData->canceled)
|
||||||
return;
|
return;
|
||||||
ReferencesFileData &data = refData->fileData[loc];
|
ReferencesFileData &data = refData->fileData[loc];
|
||||||
const auto result = response.result();
|
const auto result = response.result();
|
||||||
@@ -545,7 +581,33 @@ void ClangdClient::Private::handleFindUsagesResult(quint64 key, const QList<Loca
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangdClient::Private::addSearchResultsForFile(const ReferencesData &refData,
|
void ClangdClient::Private::handleRenameRequest(const SearchResult *search,
|
||||||
|
const ReplacementData &replacementData,
|
||||||
|
const QString &newSymbolName,
|
||||||
|
const QList<SearchResultItem> &checkedItems,
|
||||||
|
bool preserveCase)
|
||||||
|
{
|
||||||
|
const QStringList fileNames = TextEditor::BaseFileFind::replaceAll(newSymbolName, checkedItems,
|
||||||
|
preserveCase);
|
||||||
|
if (!fileNames.isEmpty())
|
||||||
|
SearchResultWindow::instance()->hide();
|
||||||
|
|
||||||
|
const auto renameFilesCheckBox = qobject_cast<QCheckBox *>(search->additionalReplaceWidget());
|
||||||
|
QTC_ASSERT(renameFilesCheckBox, return);
|
||||||
|
if (!renameFilesCheckBox->isChecked())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QVector<Node *> fileNodes;
|
||||||
|
for (const Utils::FilePath &file : replacementData.fileRenameCandidates) {
|
||||||
|
Node * const node = ProjectTree::nodeForFile(file);
|
||||||
|
if (node)
|
||||||
|
fileNodes << node;
|
||||||
|
}
|
||||||
|
if (!fileNodes.isEmpty())
|
||||||
|
CppTools::renameFilesForSymbol(replacementData.oldSymbolName, newSymbolName, fileNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClangdClient::Private::addSearchResultsForFile(ReferencesData &refData,
|
||||||
const Utils::FilePath &file,
|
const Utils::FilePath &file,
|
||||||
const ReferencesFileData &fileData)
|
const ReferencesFileData &fileData)
|
||||||
{
|
{
|
||||||
@@ -563,6 +625,15 @@ void ClangdClient::Private::addSearchResultsForFile(const ReferencesData &refDat
|
|||||||
item.setMainRange(SymbolSupport::convertRange(range));
|
item.setMainRange(SymbolSupport::convertRange(range));
|
||||||
item.setUseTextEditorFont(true);
|
item.setUseTextEditorFont(true);
|
||||||
item.setLineText(rangeWithText.second);
|
item.setLineText(rangeWithText.second);
|
||||||
|
if (refData.search->supportsReplace()) {
|
||||||
|
const bool fileInSession = SessionManager::projectForFile(file);
|
||||||
|
item.setSelectForReplacement(fileInSession);
|
||||||
|
if (fileInSession && file.toFileInfo().baseName().compare(
|
||||||
|
refData.replacementData->oldSymbolName,
|
||||||
|
Qt::CaseInsensitive) == 0) {
|
||||||
|
refData.replacementData->fileRenameCandidates << file; // TODO: We want to do this only for types. Use SymbolInformation once we have it.
|
||||||
|
}
|
||||||
|
}
|
||||||
items << item;
|
items << item;
|
||||||
}
|
}
|
||||||
if (isTesting)
|
if (isTesting)
|
||||||
@@ -571,11 +642,11 @@ void ClangdClient::Private::addSearchResultsForFile(const ReferencesData &refDat
|
|||||||
refData.search->addResults(items, SearchResult::AddOrdered);
|
refData.search->addResults(items, SearchResult::AddOrdered);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangdClient::Private::reportAllSearchResultsAndFinish(const ReferencesData &refData)
|
void ClangdClient::Private::reportAllSearchResultsAndFinish(ReferencesData &refData)
|
||||||
{
|
{
|
||||||
for (auto it = refData.fileData.begin(); it != refData.fileData.end(); ++it)
|
for (auto it = refData.fileData.begin(); it != refData.fileData.end(); ++it)
|
||||||
addSearchResultsForFile(refData, it.key().toFilePath(), it.value());
|
addSearchResultsForFile(refData, it.key().toFilePath(), it.value());
|
||||||
finishSearch(refData, refData.isCanceled());
|
finishSearch(refData, refData.canceled);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangdClient::Private::finishSearch(const ReferencesData &refData, bool canceled)
|
void ClangdClient::Private::finishSearch(const ReferencesData &refData, bool canceled)
|
||||||
@@ -585,9 +656,23 @@ void ClangdClient::Private::finishSearch(const ReferencesData &refData, bool can
|
|||||||
} else if (refData.search) {
|
} else if (refData.search) {
|
||||||
refData.search->finishSearch(canceled);
|
refData.search->finishSearch(canceled);
|
||||||
refData.search->disconnect(q);
|
refData.search->disconnect(q);
|
||||||
|
if (refData.replacementData) {
|
||||||
|
const auto renameCheckBox = qobject_cast<QCheckBox *>(
|
||||||
|
refData.search->additionalReplaceWidget());
|
||||||
|
QTC_CHECK(renameCheckBox);
|
||||||
|
const QSet<Utils::FilePath> files = refData.replacementData->fileRenameCandidates;
|
||||||
|
renameCheckBox->setText(tr("Re&name %n files", nullptr, files.size()));
|
||||||
|
const QStringList filesForUser = Utils::transform<QStringList>(files,
|
||||||
|
[](const Utils::FilePath &fp) { return fp.toUserOutput(); });
|
||||||
|
renameCheckBox->setToolTip(tr("Files:\n%1").arg(filesForUser.join('\n')));
|
||||||
|
renameCheckBox->setVisible(true);
|
||||||
|
refData.search->setUserData(QVariant::fromValue(*refData.replacementData));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
runningFindUsages.remove(refData.key);
|
runningFindUsages.remove(refData.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace ClangCodeModel
|
} // namespace ClangCodeModel
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(ClangCodeModel::Internal::ReplacementData)
|
||||||
|
@@ -26,6 +26,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <languageclient/client.h>
|
#include <languageclient/client.h>
|
||||||
|
#include <utils/optional.h>
|
||||||
|
|
||||||
#include <QVersionNumber>
|
#include <QVersionNumber>
|
||||||
|
|
||||||
@@ -49,7 +50,8 @@ public:
|
|||||||
void openExtraFile(const Utils::FilePath &filePath, const QString &content = {});
|
void openExtraFile(const Utils::FilePath &filePath, const QString &content = {});
|
||||||
void closeExtraFile(const Utils::FilePath &filePath);
|
void closeExtraFile(const Utils::FilePath &filePath);
|
||||||
|
|
||||||
void findUsages(TextEditor::TextDocument *document, const QTextCursor &cursor);
|
void findUsages(TextEditor::TextDocument *document, const QTextCursor &cursor,
|
||||||
|
const Utils::optional<QString> &replacement);
|
||||||
|
|
||||||
void enableTesting();
|
void enableTesting();
|
||||||
|
|
||||||
|
@@ -86,6 +86,23 @@ void RefactoringEngine::startLocalRenaming(const CppTools::CursorInEditor &data,
|
|||||||
m_watcher->setFuture(cursorFuture);
|
m_watcher->setFuture(cursorFuture);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RefactoringEngine::globalRename(const CppTools::CursorInEditor &cursor,
|
||||||
|
CppTools::UsagesCallback &&callback,
|
||||||
|
const QString &replacement)
|
||||||
|
{
|
||||||
|
ProjectExplorer::Project * const project
|
||||||
|
= ProjectExplorer::SessionManager::projectForFile(cursor.filePath());
|
||||||
|
ClangdClient * const client = ClangModelManagerSupport::instance()->clientForProject(project);
|
||||||
|
if (!client || !client->isFullyIndexed()) {
|
||||||
|
CppTools::CppModelManager::builtinRefactoringEngine()
|
||||||
|
->globalRename(cursor, std::move(callback), replacement);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QTC_ASSERT(client->documentOpen(cursor.textDocument()),
|
||||||
|
client->openDocument(cursor.textDocument()));
|
||||||
|
client->findUsages(cursor.textDocument(), cursor.cursor(), replacement);
|
||||||
|
}
|
||||||
|
|
||||||
void RefactoringEngine::findUsages(const CppTools::CursorInEditor &cursor,
|
void RefactoringEngine::findUsages(const CppTools::CursorInEditor &cursor,
|
||||||
CppTools::UsagesCallback &&callback) const
|
CppTools::UsagesCallback &&callback) const
|
||||||
{
|
{
|
||||||
@@ -99,7 +116,7 @@ void RefactoringEngine::findUsages(const CppTools::CursorInEditor &cursor,
|
|||||||
}
|
}
|
||||||
QTC_ASSERT(client->documentOpen(cursor.textDocument()),
|
QTC_ASSERT(client->documentOpen(cursor.textDocument()),
|
||||||
client->openDocument(cursor.textDocument()));
|
client->openDocument(cursor.textDocument()));
|
||||||
client->findUsages(cursor.textDocument(), cursor.cursor());
|
client->findUsages(cursor.textDocument(), cursor.cursor(), {});
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
@@ -44,8 +44,8 @@ public:
|
|||||||
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 globalRename(const CppTools::CursorInEditor &, CppTools::UsagesCallback &&,
|
void globalRename(const CppTools::CursorInEditor &cursor, CppTools::UsagesCallback &&callback,
|
||||||
const QString &) override {}
|
const QString &replacement) override;
|
||||||
void findUsages(const CppTools::CursorInEditor &cursor,
|
void findUsages(const CppTools::CursorInEditor &cursor,
|
||||||
CppTools::UsagesCallback &&callback) const override;
|
CppTools::UsagesCallback &&callback) const override;
|
||||||
void globalFollowSymbol(const CppTools::CursorInEditor &,
|
void globalFollowSymbol(const CppTools::CursorInEditor &,
|
||||||
|
@@ -168,7 +168,7 @@ void ClangdTests::testFindReferences()
|
|||||||
QTextCursor cursor((doc)->document()); \
|
QTextCursor cursor((doc)->document()); \
|
||||||
cursor.setPosition((pos)); \
|
cursor.setPosition((pos)); \
|
||||||
searchResults.clear(); \
|
searchResults.clear(); \
|
||||||
client->findUsages((doc), cursor); \
|
client->findUsages((doc), cursor, {}); \
|
||||||
QVERIFY(waitForSignalOrTimeout(client, &ClangdClient::findUsagesDone)); \
|
QVERIFY(waitForSignalOrTimeout(client, &ClangdClient::findUsagesDone)); \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
|
@@ -491,7 +491,8 @@ void CppEditorWidget::renameUsages(const QString &replacement, QTextCursor curso
|
|||||||
{
|
{
|
||||||
if (cursor.isNull())
|
if (cursor.isNull())
|
||||||
cursor = textCursor();
|
cursor = textCursor();
|
||||||
CppTools::CursorInEditor cursorInEditor{cursor, textDocument()->filePath(), this};
|
CppTools::CursorInEditor cursorInEditor{cursor, textDocument()->filePath(), this,
|
||||||
|
textDocument()};
|
||||||
QPointer<CppEditorWidget> cppEditorWidget = this;
|
QPointer<CppEditorWidget> cppEditorWidget = this;
|
||||||
d->m_modelManager->globalRename(cursorInEditor,
|
d->m_modelManager->globalRename(cursorInEditor,
|
||||||
[=](const CppTools::Usages &usages) {
|
[=](const CppTools::Usages &usages) {
|
||||||
|
@@ -59,6 +59,8 @@ using namespace ProjectExplorer;
|
|||||||
|
|
||||||
namespace CppTools {
|
namespace CppTools {
|
||||||
|
|
||||||
|
namespace { static bool isAllLowerCase(const QString &text) { return text.toLower() == text; } }
|
||||||
|
|
||||||
SearchResultColor::Style colorStyleForUsageType(CPlusPlus::Usage::Type type)
|
SearchResultColor::Style colorStyleForUsageType(CPlusPlus::Usage::Type type)
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -75,6 +77,51 @@ SearchResultColor::Style colorStyleForUsageType(CPlusPlus::Usage::Type type)
|
|||||||
return SearchResultColor::Style::Default; // For dumb compilers.
|
return SearchResultColor::Style::Default; // For dumb compilers.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void renameFilesForSymbol(const QString &oldSymbolName, const QString &newSymbolName,
|
||||||
|
const QVector<Node *> &files)
|
||||||
|
{
|
||||||
|
Internal::CppFileSettings settings;
|
||||||
|
settings.fromSettings(Core::ICore::settings());
|
||||||
|
|
||||||
|
const QStringList newPaths =
|
||||||
|
Utils::transform<QList>(files,
|
||||||
|
[&oldSymbolName, newSymbolName, &settings](const Node *node) -> QString {
|
||||||
|
const QFileInfo fi = node->filePath().toFileInfo();
|
||||||
|
const QString oldBaseName = fi.baseName();
|
||||||
|
QString newBaseName = newSymbolName;
|
||||||
|
|
||||||
|
// 1) new symbol lowercase: new base name lowercase
|
||||||
|
if (isAllLowerCase(newSymbolName)) {
|
||||||
|
newBaseName = newSymbolName;
|
||||||
|
|
||||||
|
// 2) old base name mixed case: new base name is verbatim symbol name
|
||||||
|
} else if (!isAllLowerCase(oldBaseName)) {
|
||||||
|
newBaseName = newSymbolName;
|
||||||
|
|
||||||
|
// 3) old base name lowercase, old symbol mixed case: new base name lowercase
|
||||||
|
} else if (!isAllLowerCase(oldSymbolName)) {
|
||||||
|
newBaseName = newSymbolName.toLower();
|
||||||
|
|
||||||
|
// 4) old base name lowercase, old symbol lowercase, new symbol mixed case:
|
||||||
|
// use the preferences setting for new base name case
|
||||||
|
} else if (settings.lowerCaseFiles) {
|
||||||
|
newBaseName = newSymbolName.toLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newBaseName == oldBaseName)
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
return fi.absolutePath() + "/" + newBaseName + '.' + fi.completeSuffix();
|
||||||
|
});
|
||||||
|
|
||||||
|
for (int i = 0; i < files.size(); ++i) {
|
||||||
|
if (!newPaths.at(i).isEmpty()) {
|
||||||
|
Node *node = files.at(i);
|
||||||
|
ProjectExplorerPlugin::renameFile(node, newPaths.at(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QWidget *CppSearchResultFilter::createWidget()
|
QWidget *CppSearchResultFilter::createWidget()
|
||||||
{
|
{
|
||||||
const auto widget = new QWidget;
|
const auto widget = new QWidget;
|
||||||
@@ -469,11 +516,6 @@ void CppFindReferences::findAll_helper(SearchResult *search, CPlusPlus::Symbol *
|
|||||||
connect(progress, &FutureProgress::clicked, search, &SearchResult::popup);
|
connect(progress, &FutureProgress::clicked, search, &SearchResult::popup);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isAllLowerCase(const QString &text)
|
|
||||||
{
|
|
||||||
return text.toLower() == text;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CppFindReferences::onReplaceButtonClicked(const QString &text,
|
void CppFindReferences::onReplaceButtonClicked(const QString &text,
|
||||||
const QList<SearchResultItem> &items,
|
const QList<SearchResultItem> &items,
|
||||||
bool preserveCase)
|
bool preserveCase)
|
||||||
@@ -495,48 +537,7 @@ void CppFindReferences::onReplaceButtonClicked(const QString &text,
|
|||||||
if (!renameFilesCheckBox || !renameFilesCheckBox->isChecked())
|
if (!renameFilesCheckBox || !renameFilesCheckBox->isChecked())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CppFileSettings settings;
|
renameFilesForSymbol(parameters.prettySymbolName, text, parameters.filesToRename);
|
||||||
settings.fromSettings(Core::ICore::settings());
|
|
||||||
|
|
||||||
const QStringList newPaths =
|
|
||||||
Utils::transform<QList>(parameters.filesToRename,
|
|
||||||
[¶meters, text, &settings](const Node *node) -> QString {
|
|
||||||
const QFileInfo fi = node->filePath().toFileInfo();
|
|
||||||
const QString oldSymbolName = parameters.prettySymbolName;
|
|
||||||
const QString oldBaseName = fi.baseName();
|
|
||||||
const QString newSymbolName = text;
|
|
||||||
QString newBaseName = newSymbolName;
|
|
||||||
|
|
||||||
// 1) new symbol lowercase: new base name lowercase
|
|
||||||
if (isAllLowerCase(newSymbolName)) {
|
|
||||||
newBaseName = newSymbolName;
|
|
||||||
|
|
||||||
// 2) old base name mixed case: new base name is verbatim symbol name
|
|
||||||
} else if (!isAllLowerCase(oldBaseName)) {
|
|
||||||
newBaseName = newSymbolName;
|
|
||||||
|
|
||||||
// 3) old base name lowercase, old symbol mixed case: new base name lowercase
|
|
||||||
} else if (!isAllLowerCase(oldSymbolName)) {
|
|
||||||
newBaseName = newSymbolName.toLower();
|
|
||||||
|
|
||||||
// 4) old base name lowercase, old symbol lowercase, new symbol mixed case:
|
|
||||||
// use the preferences setting for new base name case
|
|
||||||
} else if (settings.lowerCaseFiles) {
|
|
||||||
newBaseName = newSymbolName.toLower();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newBaseName == oldBaseName)
|
|
||||||
return QString();
|
|
||||||
|
|
||||||
return fi.absolutePath() + "/" + newBaseName + '.' + fi.completeSuffix();
|
|
||||||
});
|
|
||||||
|
|
||||||
for (int i = 0; i < parameters.filesToRename.size(); ++i) {
|
|
||||||
if (!newPaths.at(i).isEmpty()) {
|
|
||||||
Node *node = parameters.filesToRename.at(i);
|
|
||||||
ProjectExplorerPlugin::renameFile(node, newPaths.at(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CppFindReferences::searchAgain()
|
void CppFindReferences::searchAgain()
|
||||||
|
@@ -49,6 +49,9 @@ namespace CppTools {
|
|||||||
class CppModelManager;
|
class CppModelManager;
|
||||||
|
|
||||||
Core::SearchResultColor::Style CPPTOOLS_EXPORT colorStyleForUsageType(CPlusPlus::Usage::Type type);
|
Core::SearchResultColor::Style CPPTOOLS_EXPORT colorStyleForUsageType(CPlusPlus::Usage::Type type);
|
||||||
|
void CPPTOOLS_EXPORT renameFilesForSymbol(const QString &oldSymbolName,
|
||||||
|
const QString &newSymbolName,
|
||||||
|
const QVector<ProjectExplorer::Node *> &files);
|
||||||
|
|
||||||
class CPPTOOLS_EXPORT CppSearchResultFilter : public Core::SearchResultFilter
|
class CPPTOOLS_EXPORT CppSearchResultFilter : public Core::SearchResultFilter
|
||||||
{
|
{
|
||||||
|
@@ -323,7 +323,7 @@ void CppModelManager::startLocalRenaming(const CursorInEditor &data,
|
|||||||
void CppModelManager::globalRename(const CursorInEditor &data, UsagesCallback &&renameCallback,
|
void CppModelManager::globalRename(const CursorInEditor &data, UsagesCallback &&renameCallback,
|
||||||
const QString &replacement)
|
const QString &replacement)
|
||||||
{
|
{
|
||||||
RefactoringEngineInterface *engine = getRefactoringEngine(d->m_refactoringEngines);
|
RefactoringEngineInterface *engine = getRefactoringEngine(d->m_refactoringEngines, false);
|
||||||
QTC_ASSERT(engine, return;);
|
QTC_ASSERT(engine, return;);
|
||||||
engine->globalRename(data, std::move(renameCallback), replacement);
|
engine->globalRename(data, std::move(renameCallback), replacement);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user