forked from qt-creator/qt-creator
CMakePM: Allow files to be renamed in project view
This includes both with source files explicitly specified or resulted from a file(GLOB|GLOB_RECOURSE) call. Fixes: QTCREATORBUG-27538 Change-Id: I5ee113af168bdb8cd0a96e8ab2ae603c0607fb0b Reviewed-by: hjk <hjk@qt.io> Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
@@ -201,7 +201,8 @@ void CMakeBuildSystem::triggerParsing()
|
|||||||
bool CMakeBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const
|
bool CMakeBuildSystem::supportsAction(Node *context, ProjectAction action, const Node *node) const
|
||||||
{
|
{
|
||||||
if (dynamic_cast<CMakeTargetNode *>(context))
|
if (dynamic_cast<CMakeTargetNode *>(context))
|
||||||
return action == ProjectAction::AddNewFile || action == ProjectAction::AddExistingFile;
|
return action == ProjectAction::AddNewFile || action == ProjectAction::AddExistingFile
|
||||||
|
|| action == ProjectAction::Rename;
|
||||||
|
|
||||||
return BuildSystem::supportsAction(context, action, node);
|
return BuildSystem::supportsAction(context, action, node);
|
||||||
}
|
}
|
||||||
@@ -366,6 +367,150 @@ bool CMakeBuildSystem::addFiles(Node *context, const FilePaths &filePaths, FileP
|
|||||||
return BuildSystem::addFiles(context, filePaths, notAdded);
|
return BuildSystem::addFiles(context, filePaths, notAdded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CMakeBuildSystem::canRenameFile(Node *context,
|
||||||
|
const FilePath &oldFilePath,
|
||||||
|
const FilePath &newFilePath)
|
||||||
|
{
|
||||||
|
// "canRenameFile" will cause an actual rename after the function call.
|
||||||
|
// This will make the a sequence like
|
||||||
|
// canonicalPath().relativePathFrom(projDir).cleanPath().toString()
|
||||||
|
// to fail if the file doesn't exist on disk
|
||||||
|
// therefore cache the results for the subsequent "renameFile" call
|
||||||
|
// where oldFilePath has already been renamed as newFilePath.
|
||||||
|
|
||||||
|
if (auto n = dynamic_cast<CMakeTargetNode *>(context)) {
|
||||||
|
const FilePath projDir = n->filePath().canonicalPath();
|
||||||
|
const QString oldRelPathName
|
||||||
|
= oldFilePath.canonicalPath().relativePathFrom(projDir).cleanPath().toString();
|
||||||
|
|
||||||
|
const QString targetName = n->buildKey();
|
||||||
|
auto target = Utils::findOrDefault(buildTargets(),
|
||||||
|
[targetName](const CMakeBuildTarget &target) {
|
||||||
|
return target.title == targetName;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (target.backtrace.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const FilePath targetCMakeFile = target.backtrace.last().path;
|
||||||
|
|
||||||
|
// Have a fresh look at the CMake file, not relying on a cached value
|
||||||
|
expected_str<QByteArray> fileContent = targetCMakeFile.fileContents();
|
||||||
|
cmListFile cmakeListFile;
|
||||||
|
std::string errorString;
|
||||||
|
if (fileContent) {
|
||||||
|
fileContent = fileContent->replace("\r\n", "\n");
|
||||||
|
if (!cmakeListFile.ParseString(fileContent->toStdString(),
|
||||||
|
targetCMakeFile.fileName().toStdString(),
|
||||||
|
errorString))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int targetDefinitionLine = target.backtrace.last().line;
|
||||||
|
|
||||||
|
auto function = std::find_if(cmakeListFile.Functions.begin(),
|
||||||
|
cmakeListFile.Functions.end(),
|
||||||
|
[targetDefinitionLine](const auto &func) {
|
||||||
|
return func.Line() == targetDefinitionLine;
|
||||||
|
});
|
||||||
|
|
||||||
|
const std::string target_name = targetName.toStdString();
|
||||||
|
auto targetSourcesFunc
|
||||||
|
= std::find_if(cmakeListFile.Functions.begin(),
|
||||||
|
cmakeListFile.Functions.end(),
|
||||||
|
[target_name = targetName.toStdString()](const auto &func) {
|
||||||
|
return func.LowerCaseName() == "target_sources"
|
||||||
|
&& func.Arguments().front().Value == target_name;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const auto &func : {function, targetSourcesFunc}) {
|
||||||
|
if (func == cmakeListFile.Functions.end())
|
||||||
|
continue;
|
||||||
|
auto filePathArgument
|
||||||
|
= Utils::findOrDefault(func->Arguments(),
|
||||||
|
[fileName = oldRelPathName.toStdString()](const auto &arg) {
|
||||||
|
return arg.Delim != cmListFileArgument::Comment
|
||||||
|
&& arg.Value == fileName;
|
||||||
|
});
|
||||||
|
|
||||||
|
const QString key
|
||||||
|
= QStringList{projDir.path(), targetName, oldFilePath.path(), newFilePath.path()}
|
||||||
|
.join(";");
|
||||||
|
|
||||||
|
if (!filePathArgument.Value.empty()) {
|
||||||
|
m_filesToBeRenamed.insert(key, {filePathArgument, targetCMakeFile, oldRelPathName});
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
const auto globFunctions = std::get<0>(
|
||||||
|
Utils::partition(cmakeListFile.Functions, [](const auto &f) {
|
||||||
|
return f.LowerCaseName() == "file" && f.Arguments().size() > 2
|
||||||
|
&& (f.Arguments().front().Value == "GLOB"
|
||||||
|
|| f.Arguments().front().Value == "GLOB_RECURSE");
|
||||||
|
}));
|
||||||
|
|
||||||
|
const auto globVariables
|
||||||
|
= Utils::transform<QSet>(globFunctions, [](const auto &func) {
|
||||||
|
return std::string("${") + func.Arguments()[1].Value + "}";
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto haveGlobbing
|
||||||
|
= Utils::anyOf(func->Arguments(), [globVariables](const auto &arg) {
|
||||||
|
return globVariables.contains(arg.Value)
|
||||||
|
&& arg.Delim != cmListFileArgument::Comment;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (haveGlobbing) {
|
||||||
|
m_filesToBeRenamed
|
||||||
|
.insert(key, {filePathArgument, targetCMakeFile, oldRelPathName, true});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CMakeBuildSystem::renameFile(Node *context,
|
||||||
|
const FilePath &oldFilePath,
|
||||||
|
const FilePath &newFilePath)
|
||||||
|
{
|
||||||
|
if (auto n = dynamic_cast<CMakeTargetNode *>(context)) {
|
||||||
|
const FilePath projDir = n->filePath().canonicalPath();
|
||||||
|
const QString newRelPathName
|
||||||
|
= newFilePath.canonicalPath().relativePathFrom(projDir).cleanPath().toString();
|
||||||
|
|
||||||
|
const QString targetName = n->buildKey();
|
||||||
|
const QString key
|
||||||
|
= QStringList{projDir.path(), targetName, oldFilePath.path(), newFilePath.path()}.join(
|
||||||
|
";");
|
||||||
|
|
||||||
|
auto fileToRename = m_filesToBeRenamed.take(key);
|
||||||
|
if (!fileToRename.cmakeFile.exists())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BaseTextEditor *editor = qobject_cast<BaseTextEditor *>(
|
||||||
|
Core::EditorManager::openEditorAt({fileToRename.cmakeFile,
|
||||||
|
static_cast<int>(fileToRename.argumentPosition.Line),
|
||||||
|
static_cast<int>(fileToRename.argumentPosition.Column
|
||||||
|
- 1)},
|
||||||
|
Constants::CMAKE_EDITOR_ID,
|
||||||
|
Core::EditorManager::DoNotMakeVisible));
|
||||||
|
if (!editor)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!fileToRename.fromGlobbing)
|
||||||
|
editor->replace(fileToRename.oldRelativeFileName.length(), newRelPathName);
|
||||||
|
|
||||||
|
editor->editorWidget()->autoIndent();
|
||||||
|
if (!Core::DocumentManager::saveDocument(editor->document()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
FilePaths CMakeBuildSystem::filesGeneratedFrom(const FilePath &sourceFile) const
|
FilePaths CMakeBuildSystem::filesGeneratedFrom(const FilePath &sourceFile) const
|
||||||
{
|
{
|
||||||
FilePath project = projectDirectory();
|
FilePath project = projectDirectory();
|
||||||
|
|||||||
@@ -48,6 +48,13 @@ public:
|
|||||||
bool addFiles(ProjectExplorer::Node *context,
|
bool addFiles(ProjectExplorer::Node *context,
|
||||||
const Utils::FilePaths &filePaths, Utils::FilePaths *) final;
|
const Utils::FilePaths &filePaths, Utils::FilePaths *) final;
|
||||||
|
|
||||||
|
bool canRenameFile(ProjectExplorer::Node *context,
|
||||||
|
const Utils::FilePath &oldFilePath,
|
||||||
|
const Utils::FilePath &newFilePath) final;
|
||||||
|
bool renameFile(ProjectExplorer::Node *context,
|
||||||
|
const Utils::FilePath &oldFilePath,
|
||||||
|
const Utils::FilePath &newFilePath) final;
|
||||||
|
|
||||||
Utils::FilePaths filesGeneratedFrom(const Utils::FilePath &sourceFile) const final;
|
Utils::FilePaths filesGeneratedFrom(const Utils::FilePath &sourceFile) const final;
|
||||||
QString name() const final { return QLatin1String("cmake"); }
|
QString name() const final { return QLatin1String("cmake"); }
|
||||||
|
|
||||||
@@ -201,6 +208,15 @@ private:
|
|||||||
QList<CMakeBuildTarget> m_buildTargets;
|
QList<CMakeBuildTarget> m_buildTargets;
|
||||||
QSet<CMakeFileInfo> m_cmakeFiles;
|
QSet<CMakeFileInfo> m_cmakeFiles;
|
||||||
|
|
||||||
|
struct FileToBeRenamed
|
||||||
|
{
|
||||||
|
cmListFileArgument argumentPosition;
|
||||||
|
Utils::FilePath cmakeFile;
|
||||||
|
QString oldRelativeFileName;
|
||||||
|
bool fromGlobbing = false;
|
||||||
|
};
|
||||||
|
QHash<QString, FileToBeRenamed> m_filesToBeRenamed;
|
||||||
|
|
||||||
// Parsing state:
|
// Parsing state:
|
||||||
BuildDirParameters m_parameters;
|
BuildDirParameters m_parameters;
|
||||||
int m_reparseParameters = REPARSE_DEFAULT;
|
int m_reparseParameters = REPARSE_DEFAULT;
|
||||||
|
|||||||
@@ -30,3 +30,8 @@ my_add_executable(hello-my-widgets
|
|||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(hello-my-widgets PRIVATE Qt6::Widgets)
|
target_link_libraries(hello-my-widgets PRIVATE Qt6::Widgets)
|
||||||
|
|
||||||
|
file(GLOB SOURCE_FILES CONFIGURE_DEPENDS *.cpp *.h *.ui)
|
||||||
|
|
||||||
|
add_executable(hello-widgets-glob ${SOURCE_FILES})
|
||||||
|
target_link_libraries(hello-widgets-glob PRIVATE Qt6::Widgets)
|
||||||
|
|||||||
Reference in New Issue
Block a user