CMakeProjectManager: Add File System virtual folder

If a CMake project cannot be parsed by CMake, it is practically unusable in
Qt Creator. According to discussion in QTCREATORBUG-24677, a virtual
folder with the project's file system view is added to the project
manager as a convenience feature.

Fixes: QTCREATORBUG-24677
Change-Id: I48775bb89c704d3f7e5bb21ec6481bd5cc0f4b6c
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: Kai Koehne <kai.koehne@qt.io>
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
Dmitriy Purgin
2020-10-26 15:36:59 +01:00
parent 56eb7628c3
commit 53115259ba
8 changed files with 94 additions and 14 deletions

View File

@@ -517,11 +517,11 @@ void CMakeBuildSystem::clearCMakeCache()
Utils::FileUtils::removeRecursively(cmakeFiles); Utils::FileUtils::removeRecursively(cmakeFiles);
} }
std::unique_ptr<CMakeProjectNode> std::unique_ptr<CMakeProjectNode> CMakeBuildSystem::generateProjectTree(
CMakeBuildSystem::generateProjectTree(const QList<const FileNode *> &allFiles) const QList<const FileNode *> &allFiles, bool includeHeaderNodes)
{ {
QString errorMessage; QString errorMessage;
auto root = m_reader.generateProjectTree(allFiles, errorMessage); auto root = m_reader.generateProjectTree(allFiles, errorMessage, includeHeaderNodes);
checkAndReportError(errorMessage); checkAndReportError(errorMessage);
return root; return root;
} }
@@ -535,6 +535,8 @@ void CMakeBuildSystem::combineScanAndParse()
if (m_combinedScanAndParseResult) { if (m_combinedScanAndParseResult) {
updateProjectData(); updateProjectData();
m_currentGuard.markAsSuccess(); m_currentGuard.markAsSuccess();
} else {
updateFallbackProjectData();
} }
} }
@@ -590,7 +592,7 @@ void CMakeBuildSystem::updateProjectData()
Project *p = project(); Project *p = project();
{ {
auto newRoot = generateProjectTree(m_allFiles); auto newRoot = generateProjectTree(m_allFiles, true);
if (newRoot) { if (newRoot) {
setRootProjectNode(std::move(newRoot)); setRootProjectNode(std::move(newRoot));
CMakeConfigItem settingFileItem; CMakeConfigItem settingFileItem;
@@ -671,6 +673,18 @@ void CMakeBuildSystem::updateProjectData()
qCDebug(cmakeBuildSystemLog) << "All CMake project data up to date."; qCDebug(cmakeBuildSystemLog) << "All CMake project data up to date.";
} }
void CMakeBuildSystem::updateFallbackProjectData()
{
qCDebug(cmakeBuildSystemLog) << "Updating fallback CMake project data";
QTC_ASSERT(m_treeScanner.isFinished() && !m_reader.isParsing(), return );
auto newRoot = generateProjectTree(m_allFiles, false);
setRootProjectNode(std::move(newRoot));
qCDebug(cmakeBuildSystemLog) << "All fallback CMake project data up to date.";
}
void CMakeBuildSystem::handleParsingSucceeded() void CMakeBuildSystem::handleParsingSucceeded()
{ {
if (!cmakeBuildConfiguration()->isActive()) { if (!cmakeBuildConfiguration()->isActive()) {

View File

@@ -123,11 +123,11 @@ private:
void combineScanAndParse(); void combineScanAndParse();
std::unique_ptr<CMakeProjectNode> generateProjectTree( std::unique_ptr<CMakeProjectNode> generateProjectTree(
const QList<const ProjectExplorer::FileNode *> &allFiles); const QList<const ProjectExplorer::FileNode *> &allFiles, bool includeHeadersNode);
void checkAndReportError(QString &errorMessage); void checkAndReportError(QString &errorMessage);
void updateProjectData(); void updateProjectData();
void updateFallbackProjectData();
QList<ProjectExplorer::ExtraCompiler *> findExtraCompilers(); QList<ProjectExplorer::ExtraCompiler *> findExtraCompilers();
void updateQmlJSCodeModel(); void updateQmlJSCodeModel();

View File

@@ -692,5 +692,29 @@ FileApiQtcData extractData(FileApiData &input,
return result; return result;
} }
FileApiQtcData generateFallbackData(const FilePath &topCmakeFile,
const FilePath &sourceDirectory,
const FilePath &buildDirectory,
QString errorMessage)
{
FileApiQtcData result;
result.rootProjectNode.reset(new CMakeProjectNode{sourceDirectory});
result.rootProjectNode->setDisplayName(sourceDirectory.fileName());
result.errorMessage = errorMessage;
if (!topCmakeFile.isEmpty()) {
auto node = std::make_unique<FileNode>(topCmakeFile, FileType::Project);
node->setIsGenerated(false);
std::vector<std::unique_ptr<FileNode>> fileNodes;
fileNodes.emplace_back(std::move(node));
addCMakeLists(result.rootProjectNode.get(), std::move(fileNodes));
}
return result;
}
} // namespace Internal } // namespace Internal
} // namespace CMakeProjectManager } // namespace CMakeProjectManager

View File

@@ -31,6 +31,7 @@
#include <projectexplorer/rawprojectpart.h> #include <projectexplorer/rawprojectpart.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/optional.h>
#include <QList> #include <QList>
#include <QSet> #include <QSet>
@@ -58,6 +59,10 @@ public:
FileApiQtcData extractData(FileApiData &data, FileApiQtcData extractData(FileApiData &data,
const Utils::FilePath &sourceDirectory, const Utils::FilePath &sourceDirectory,
const Utils::FilePath &buildDirectory); const Utils::FilePath &buildDirectory);
FileApiQtcData generateFallbackData(const Utils::FilePath &topCmakeFile,
const Utils::FilePath &sourceDirectory,
const Utils::FilePath &buildDirectory,
QString errorMessage);
} // namespace Internal } // namespace Internal
} // namespace CMakeProjectManager } // namespace CMakeProjectManager

View File

@@ -193,11 +193,14 @@ CMakeConfig FileApiReader::takeParsedConfiguration(QString &errorMessage)
} }
std::unique_ptr<CMakeProjectNode> FileApiReader::generateProjectTree( std::unique_ptr<CMakeProjectNode> FileApiReader::generateProjectTree(
const QList<const FileNode *> &allFiles, QString &errorMessage) const QList<const FileNode *> &allFiles, QString &errorMessage, bool includeHeaderNodes)
{ {
Q_UNUSED(errorMessage) Q_UNUSED(errorMessage)
addHeaderNodes(m_rootProjectNode.get(), m_knownHeaders, allFiles); if (includeHeaderNodes) {
addHeaderNodes(m_rootProjectNode.get(), m_knownHeaders, allFiles);
}
addFileSystemNodes(m_rootProjectNode.get(), allFiles);
return std::move(m_rootProjectNode); return std::move(m_rootProjectNode);
} }
@@ -230,19 +233,23 @@ void FileApiReader::endState(const QFileInfo &replyFi)
const FilePath sourceDirectory = m_parameters.sourceDirectory; const FilePath sourceDirectory = m_parameters.sourceDirectory;
const FilePath buildDirectory = m_parameters.workDirectory; const FilePath buildDirectory = m_parameters.workDirectory;
const FilePath topCmakeFile = m_cmakeFiles.size() == 1 ? *m_cmakeFiles.begin() : FilePath{};
m_lastReplyTimestamp = replyFi.lastModified(); m_lastReplyTimestamp = replyFi.lastModified();
m_future = runAsync(ProjectExplorerPlugin::sharedThreadPool(), m_future = runAsync(ProjectExplorerPlugin::sharedThreadPool(),
[replyFi, sourceDirectory, buildDirectory]() { [replyFi, sourceDirectory, buildDirectory, topCmakeFile]() {
auto result = std::make_unique<FileApiQtcData>(); auto result = std::make_unique<FileApiQtcData>();
FileApiData data = FileApiParser::parseData(replyFi, FileApiData data = FileApiParser::parseData(replyFi, result->errorMessage);
result->errorMessage);
if (!result->errorMessage.isEmpty()) { if (!result->errorMessage.isEmpty()) {
qWarning() << result->errorMessage; qWarning() << result->errorMessage;
return result.release(); *result = generateFallbackData(topCmakeFile,
sourceDirectory,
buildDirectory,
result->errorMessage);
} else {
*result = extractData(data, sourceDirectory, buildDirectory);
} }
*result = extractData(data, sourceDirectory, buildDirectory);
if (!result->errorMessage.isEmpty()) { if (!result->errorMessage.isEmpty()) {
qWarning() << result->errorMessage; qWarning() << result->errorMessage;
} }

View File

@@ -67,7 +67,9 @@ public:
QList<CMakeBuildTarget> takeBuildTargets(QString &errorMessage); QList<CMakeBuildTarget> takeBuildTargets(QString &errorMessage);
CMakeConfig takeParsedConfiguration(QString &errorMessage); CMakeConfig takeParsedConfiguration(QString &errorMessage);
std::unique_ptr<CMakeProjectNode> generateProjectTree( std::unique_ptr<CMakeProjectNode> generateProjectTree(
const QList<const ProjectExplorer::FileNode *> &allFiles, QString &errorMessage); const QList<const ProjectExplorer::FileNode *> &allFiles,
QString &errorMessage,
bool includeHeaderNodes);
ProjectExplorer::RawProjectParts createRawProjectParts(QString &errorMessage); ProjectExplorer::RawProjectParts createRawProjectParts(QString &errorMessage);
signals: signals:

View File

@@ -208,5 +208,31 @@ void addHeaderNodes(ProjectNode *root,
root->addNode(std::move(headerNode)); root->addNode(std::move(headerNode));
} }
void addFileSystemNodes(ProjectNode *root, const QList<const FileNode *> &allFiles)
{
QTC_ASSERT(root, return );
static QIcon fileSystemNodeIcon = Core::FileIconProvider::directoryIcon(
ProjectExplorer::Constants::FILEOVERLAY_UNKNOWN);
auto fileSystemNode = std::make_unique<VirtualFolderNode>(root->filePath());
fileSystemNode->setPriority(Node::DefaultPriority - 6);
fileSystemNode->setDisplayName(
QCoreApplication::translate("CMakeProjectManager::Internal::ProjectTreeHelper",
"<File System>"));
fileSystemNode->setIcon(fileSystemNodeIcon);
for (const FileNode *fn : allFiles) {
if (!fn->filePath().isChildOf(root->filePath()))
continue;
std::unique_ptr<FileNode> node(fn->clone());
node->setEnabled(false);
fileSystemNode->addNestedNode(std::move(node));
}
if (!fileSystemNode->isEmpty())
root->addNode(std::move(fileSystemNode));
}
} // namespace Internal } // namespace Internal
} // namespace CMakeProjectManager } // namespace CMakeProjectManager

View File

@@ -68,5 +68,7 @@ void addHeaderNodes(ProjectExplorer::ProjectNode *root,
QSet<Utils::FilePath> &seenHeaders, QSet<Utils::FilePath> &seenHeaders,
const QList<const ProjectExplorer::FileNode *> &allFiles); const QList<const ProjectExplorer::FileNode *> &allFiles);
void addFileSystemNodes(ProjectExplorer::ProjectNode *root,
const QList<const ProjectExplorer::FileNode *> &allFiles);
} // namespace Internal } // namespace Internal
} // namespace CMakeProjectManager } // namespace CMakeProjectManager