From 762b0518a26fbe4f0415902d2196d03b6e8c25c7 Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Fri, 29 Sep 2023 22:39:01 +0200 Subject: [PATCH] CMakePM: Handle include(CMakeFileWithoutSuffix) navigation Check that the function operating upon is "include" and then match the word under cursor with a file from the project. Change-Id: Ia0131f08515c56582a1fb02a59d6b2e41ac04288 Reviewed-by: Alessandro Portale Reviewed-by: --- .../cmakeprojectmanager/cmakebuildsystem.cpp | 13 ++++ .../cmakeprojectmanager/cmakebuildsystem.h | 2 + .../cmakeprojectmanager/cmakeeditor.cpp | 64 ++++++++++++++++--- 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp index a055d07149c..c0dbd23604b 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp @@ -1379,12 +1379,25 @@ void CMakeBuildSystem::setupCMakeSymbolsHash() } }; + // Prepare a hash with all .cmake files + m_dotCMakeFilesHash.clear(); + auto handleDotCMakeFiles = [&](const CMakeFileInfo &cmakeFile) { + if (cmakeFile.path.suffix() == "cmake") { + Utils::Link link; + link.targetFilePath = cmakeFile.path; + link.targetLine = 1; + link.targetColumn = 0; + m_dotCMakeFilesHash.insert(cmakeFile.path.completeBaseName(), link); + } + }; + for (const auto &cmakeFile : std::as_const(m_cmakeFiles)) { for (const auto &func : cmakeFile.cmakeListFile.Functions) { handleFunctionMacroOption(cmakeFile, func); handleImportedTargets(cmakeFile, func); handleProjectTargets(cmakeFile, func); handleFindPackageVariables(cmakeFile, func); + handleDotCMakeFiles(cmakeFile); } } diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h index d29ee777d41..ffe32f7e210 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h @@ -123,6 +123,7 @@ public: CMakeKeywords projectKeywords() const { return m_projectKeywords; } QStringList projectImportedTargets() const { return m_projectImportedTargets; } QStringList projectFindPackageVariables() const { return m_projectFindPackageVariables; } + const QHash &dotCMakeFilesHash() const { return m_dotCMakeFilesHash; } signals: void configurationCleared(); @@ -228,6 +229,7 @@ private: QList m_buildTargets; QSet m_cmakeFiles; QHash m_cmakeSymbolsHash; + QHash m_dotCMakeFilesHash; CMakeKeywords m_projectKeywords; QStringList m_projectImportedTargets; QStringList m_projectFindPackageVariables; diff --git a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp index 2a971048c92..0e77b2f1745 100644 --- a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp @@ -153,6 +153,11 @@ static bool isValidUrlChar(const QChar &c) return (c.isLetterOrNumber() || urlChars.contains(c)) && !c.isSpace(); } +static bool isValidIdentifierChar(const QChar &chr) +{ + return chr.isLetterOrNumber() || chr == '_' || chr == '-'; +} + QHash getLocalSymbolsHash(const QByteArray &content, const Utils::FilePath &filePath) { cmListFile cmakeListFile; @@ -266,6 +271,43 @@ void CMakeEditorWidget::findLinkAt(const QTextCursor &cursor, buffer.replace("${CMAKE_CURRENT_SOURCE_DIR}", dir.path()); buffer.replace("${CMAKE_CURRENT_LIST_DIR}", dir.path()); + // Lambdas to find the CMake function name + auto findFunctionStart = [cursor, this]() -> int { + int pos = cursor.position(); + QChar chr; + do { + chr = textDocument()->characterAt(--pos); + } while (pos > 0 && chr != '('); + + if (pos > 0 && chr == '(') { + // allow space between function name and ( + do { + chr = textDocument()->characterAt(--pos); + } while (pos > 0 && chr.isSpace()); + ++pos; + } + return pos; + }; + auto findFunctionEnd = [cursor, this]() -> int { + int pos = cursor.position(); + QChar chr; + do { + chr = textDocument()->characterAt(--pos); + } while (pos > 0 && chr != ')'); + return pos; + }; + auto findWordStart = [cursor, this](int pos) -> int { + // Find start position + QChar chr; + do { + chr = textDocument()->characterAt(--pos); + } while (pos > 0 && isValidIdentifierChar(chr)); + + return ++pos; + }; + const int funcStart = findFunctionStart(); + const int funcEnd = findFunctionEnd(); + if (auto project = ProjectTree::currentProject()) { buffer.replace("${CMAKE_SOURCE_DIR}", project->projectDirectory().path()); if (auto bs = ProjectTree::currentBuildSystem()) { @@ -295,20 +337,24 @@ void CMakeEditorWidget::findLinkAt(const QTextCursor &cursor, addTextStartEndToLink(link); return processLinkCallback(link); } + + // Handle include(CMakeFileWithoutSuffix) + QString functionName; + if (funcStart > funcEnd) { + int funcStartPos = findWordStart(funcStart); + functionName = textDocument()->textAt(funcStartPos, funcStart - funcStartPos); + if (functionName == "include" && cbs->dotCMakeFilesHash().contains(buffer)) { + link = cbs->dotCMakeFilesHash().value(buffer); + addTextStartEndToLink(link); + return processLinkCallback(link); + } + } } } // TODO: Resolve more variables // Resolve local variables and functions - auto findFunctionEnd = [cursor, this]() -> int { - int pos = cursor.position(); - QChar chr; - do { - chr = textDocument()->characterAt(--pos); - } while (pos > 0 && chr != ')'); - return pos; - }; - auto hash = getLocalSymbolsHash(textDocument()->textAt(0, findFunctionEnd() + 1).toUtf8(), + auto hash = getLocalSymbolsHash(textDocument()->textAt(0, funcEnd + 1).toUtf8(), textDocument()->filePath()); // Strip variable coating