diff options
author | Christian Kandeler <christian.kandeler@qt.io> | 2024-03-14 10:24:46 +0100 |
---|---|---|
committer | Christian Kandeler <christian.kandeler@qt.io> | 2024-04-09 08:18:30 +0000 |
commit | 42edb0dd61a05702370a02d3efcbd5ba32e05e6b (patch) | |
tree | b7bc939e01bb51180e8ccbabb3415841d3cc93d0 /src/plugins/cppeditor | |
parent | dd876dc4054b06638567afbdc3a849d56e99669d (diff) |
CppEditor: Make renameIncludes() also work for moved files
Task-number: QTCREATORBUG-26545
Change-Id: I0bfe203af8f091562cdd91411dbe502fc5a76956
Reviewed-by: David Schulz <david.schulz@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Diffstat (limited to 'src/plugins/cppeditor')
-rw-r--r-- | src/plugins/cppeditor/cppmodelmanager.cpp | 29 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppmodelmanager_test.cpp | 99 | ||||
-rw-r--r-- | src/plugins/cppeditor/cppmodelmanager_test.h | 1 |
3 files changed, 87 insertions, 42 deletions
diff --git a/src/plugins/cppeditor/cppmodelmanager.cpp b/src/plugins/cppeditor/cppmodelmanager.cpp index 5ea954add4..d5d6842e00 100644 --- a/src/plugins/cppeditor/cppmodelmanager.cpp +++ b/src/plugins/cppeditor/cppmodelmanager.cpp @@ -1878,16 +1878,15 @@ void CppModelManager::renameIncludes(const QList<std::pair<FilePath, FilePath>> if (oldFilePath.isEmpty() || newFilePath.isEmpty()) continue; - // We just want to handle renamings so return when the file was actually moved. - if (oldFilePath.absolutePath() != newFilePath.absolutePath()) - continue; - const TextEditor::PlainRefactoringFileFactory changes; QString oldFileName = oldFilePath.fileName(); QString newFileName = newFilePath.fileName(); const bool isUiFile = oldFilePath.suffix() == "ui" && newFilePath.suffix() == "ui"; + const bool moved = oldFilePath.absolutePath() != newFilePath.absolutePath(); if (isUiFile) { + if (moved) + return; // This is out of scope. oldFileName = "ui_" + oldFilePath.baseName() + ".h"; newFileName = "ui_" + newFilePath.baseName() + ".h"; } @@ -1925,12 +1924,26 @@ void CppModelManager::renameIncludes(const QList<std::pair<FilePath, FilePath>> TextEditor::RefactoringFilePtr file = changes.file(includingFileNew); const QTextBlock &block = file->document()->findBlockByNumber(loc.second - 1); - const int replaceStart = block.text().indexOf(oldFileName); - if (replaceStart > -1) { + const FilePath relPathOld = FilePath::fromString(FilePath::calcRelativePath( + oldFilePath.toString(), includingFileOld.parentDir().toString())); + const FilePath relPathNew = FilePath::fromString(FilePath::calcRelativePath( + newFilePath.toString(), includingFileNew.parentDir().toString())); + int replaceStart = block.text().indexOf(relPathOld.toString()); + QString oldString; + QString newString; + if (isUiFile || replaceStart == -1) { + replaceStart = block.text().indexOf(oldFileName); + oldString = oldFileName; + newString = newFileName; + } else { + oldString = relPathOld.toString(); + newString = relPathNew.toString(); + } + if (replaceStart > -1 && oldString != newString) { ChangeSet changeSet; changeSet.replace(block.position() + replaceStart, - block.position() + replaceStart + oldFileName.length(), - newFileName); + block.position() + replaceStart + oldString.length(), + newString); file->setChangeSet(changeSet); file->apply(); } diff --git a/src/plugins/cppeditor/cppmodelmanager_test.cpp b/src/plugins/cppeditor/cppmodelmanager_test.cpp index a896563a61..4ac39de737 100644 --- a/src/plugins/cppeditor/cppmodelmanager_test.cpp +++ b/src/plugins/cppeditor/cppmodelmanager_test.cpp @@ -17,6 +17,7 @@ #include <cplusplus/LookupContext.h> +#include <projectexplorer/kitmanager.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectmanager.h> #include <projectexplorer/projectnodes.h> @@ -965,50 +966,80 @@ void ModelManagerTest::testUpdateEditorsAfterProjectUpdate() QCOMPARE(documentBProjectPart->topLevelProject, pi->projectFilePath()); } -void ModelManagerTest::testRenameIncludes() +void ModelManagerTest::testRenameIncludes_data() { - struct ModelManagerGCHelper { - ~ModelManagerGCHelper() { CppModelManager::GC(); } - } GCHelper; - Q_UNUSED(GCHelper) // do not warn about being unused + QTest::addColumn<QString>("oldRelPath"); + QTest::addColumn<QString>("newRelPath"); + QTest::addColumn<bool>("successExpected"); + + QTest::addRow("rename in place 1") + << "subdir1/header1.h" << "subdir1/header1_renamed.h" << true; + QTest::addRow("rename in place 2") + << "subdir2/header2.h" << "subdir2/header2_renamed.h" << true; + QTest::addRow("rename in place 3") << "header.h" << "header_renamed.h" << true; + QTest::addRow("move up") << "subdir1/header1.h" << "header1_moved.h" << true; + QTest::addRow("move up (breaks build)") << "subdir2/header2.h" << "header2_moved.h" << false; + QTest::addRow("move down") << "header.h" << "subdir1/header_moved.h" << true; + QTest::addRow("move across") << "subdir1/header1.h" << "subdir2/header1_moved.h" << true; + QTest::addRow("move across (breaks build)") + << "subdir2/header2.h" << "subdir1/header2_moved.h" << false; +} +void ModelManagerTest::testRenameIncludes() +{ + // Set up project. TemporaryDir tmpDir; QVERIFY(tmpDir.isValid()); - - const QDir workingDir(tmpDir.path()); - const QStringList fileNames = {"foo.h", "foo.cpp", "main.cpp"}; - const FilePath oldHeader = FilePath::fromString(workingDir.filePath("foo.h")); - const FilePath newHeader = FilePath::fromString(workingDir.filePath("bar.h")); - const MyTestDataDir testDir(_("testdata_project1")); - - // Copy test files to a temporary directory - QSet<FilePath> sourceFiles; - for (const QString &fileName : std::as_const(fileNames)) { - const QString &file = workingDir.filePath(fileName); - QVERIFY(QFile::copy(testDir.file(fileName), file)); - // Saving source file names for the model manager update, - // so we can update just the relevant files. - if (ProjectFile::classify(file) == ProjectFile::CXXSource) - sourceFiles.insert(FilePath::fromString(file)); - } - - // Update the c++ model manager and check for the old includes - CppModelManager::updateSourceFiles(sourceFiles).waitForFinished(); - QCoreApplication::processEvents(); + const MyTestDataDir sourceDir("testdata_renameheaders"); + const FilePath srcFilePath = FilePath::fromString(sourceDir.path()); + const FilePath projectDir = tmpDir.filePath().pathAppended(srcFilePath.fileName()); + const auto copyResult = srcFilePath.copyRecursively(projectDir); + if (!copyResult) + qDebug() << copyResult.error(); + QVERIFY(copyResult); + Kit * const kit = Utils::findOr(KitManager::kits(), nullptr, [](const Kit *k) { + return k->isValid() && !k->hasWarning() && k->value("QtSupport.QtInformation").isValid(); + }); + if (!kit) + QSKIP("The test requires at least one valid kit with a valid Qt"); + const FilePath projectFile = projectDir.pathAppended(projectDir.fileName() + ".pro"); + ProjectOpenerAndCloser projectMgr; + QVERIFY(projectMgr.open(projectFile, true, kit)); + + // Verify initial code model state. + const auto makeAbs = [&](const QStringList &relPaths) { + return Utils::transform<QSet<FilePath>>(relPaths, [&](const QString &relPath) { + return projectDir.pathAppended(relPath); + }); + }; + const QSet<FilePath> allSources = makeAbs({"main.cpp", "subdir1/file1.cpp", "subdir2/file2.cpp"}); + const QSet<FilePath> allHeaders = makeAbs({"header.h", "subdir1/header1.h", "subdir2/header2.h"}); CPlusPlus::Snapshot snapshot = CppModelManager::snapshot(); - for (const FilePath &sourceFile : std::as_const(sourceFiles)) { - QCOMPARE(snapshot.allIncludesForDocument(sourceFile), QSet<FilePath>{oldHeader}); + for (const FilePath &srcFile : allSources) { + QCOMPARE(snapshot.allIncludesForDocument(srcFile), allHeaders); } - // Renaming the header + // Rename the header. + QFETCH(QString, oldRelPath); + QFETCH(QString, newRelPath); + QFETCH(bool, successExpected); + const FilePath oldHeader = projectDir.pathAppended(oldRelPath); + const FilePath newHeader = projectDir.pathAppended(newRelPath); QVERIFY(ProjectExplorerPlugin::renameFile(oldHeader, newHeader)); - // Update the c++ model manager again and check for the new includes - CppModelManager::updateSourceFiles(sourceFiles).waitForFinished(); - QCoreApplication::processEvents(); + // Verify new code model state. + QVERIFY(::CppEditor::Tests::waitForSignalOrTimeout( + CppModelManager::instance(), &CppModelManager::sourceFilesRefreshed, 10000)); + QSet<FilePath> incompleteNewHeadersSet = allHeaders; + incompleteNewHeadersSet.remove(oldHeader); + QSet<FilePath> completeNewHeadersSet = incompleteNewHeadersSet; + completeNewHeadersSet << newHeader; + snapshot = CppModelManager::snapshot(); - for (const FilePath &sourceFile : std::as_const(sourceFiles)) { - QCOMPARE(snapshot.allIncludesForDocument(sourceFile), QSet<FilePath>{newHeader}); + for (const FilePath &srcFile : allSources) { + const QSet<FilePath> &expectedHeaders = srcFile.fileName() == "main.cpp" && !successExpected + ? incompleteNewHeadersSet : completeNewHeadersSet; + QCOMPARE(snapshot.allIncludesForDocument(srcFile), expectedHeaders); } } diff --git a/src/plugins/cppeditor/cppmodelmanager_test.h b/src/plugins/cppeditor/cppmodelmanager_test.h index 26cbefd1c8..14e9c617de 100644 --- a/src/plugins/cppeditor/cppmodelmanager_test.h +++ b/src/plugins/cppeditor/cppmodelmanager_test.h @@ -28,6 +28,7 @@ private slots: void testDefinesPerEditor(); void testUpdateEditorsAfterProjectUpdate(); void testPrecompiledHeaders(); + void testRenameIncludes_data(); void testRenameIncludes(); void testRenameIncludesInEditor(); void testDocumentsAndRevisions(); |