From 5df0174fd09de4ac5475233d65ffc703e89b82eb Mon Sep 17 00:00:00 2001 From: Vaxry Date: Sun, 7 Jul 2024 21:42:53 +0200 Subject: [PATCH] core: properly handle unscoped keywords for users: prefix your keyword with : to make it only global scope --- src/config.cpp | 22 +++++++++++++++------- tests/config/config.conf | 4 ++++ tests/parse/main.cpp | 31 +++++++++++++++++++++++++++---- 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index c3add4c..c25efe0 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -565,15 +565,20 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) { bool found = false; for (auto& h : impl->handlers) { - if (!h.options.allowFlags) { - // we want to handle potentially nested keywords and ensure - // we only call the handler if they are scoped correctly. + // we want to handle potentially nested keywords and ensure + // we only call the handler if they are scoped correctly, + // unless the keyword is not scoped itself + + const bool UNSCOPED = !h.name.contains(":"); + const auto HANDLERNAME = !h.name.empty() && h.name.at(0) == ':' ? h.name.substr(1) : h.name; + + if (!h.options.allowFlags && !UNSCOPED) { size_t colon = 0; size_t idx = 0; size_t depth = 0; - while ((colon = h.name.find(":", idx)) != std::string::npos && impl->categories.size() > depth) { - auto actual = h.name.substr(idx, colon - idx); + while ((colon = HANDLERNAME.find(":", idx)) != std::string::npos && impl->categories.size() > depth) { + auto actual = HANDLERNAME.substr(idx, colon - idx); if (actual != impl->categories[depth]) break; @@ -582,11 +587,14 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) { ++depth; } - if (depth != impl->categories.size() || h.name.substr(idx) != LHS) + if (depth != impl->categories.size() || HANDLERNAME.substr(idx) != LHS) continue; } - if (h.options.allowFlags && (!LHS.starts_with(h.name) || LHS.contains(':') /* avoid cases where a category is called the same as a handler */)) + if (UNSCOPED && HANDLERNAME != LHS && !h.options.allowFlags) + continue; + + if (h.options.allowFlags && (!LHS.starts_with(HANDLERNAME) || LHS.contains(':') /* avoid cases where a category is called the same as a handler */)) continue; ret = h.func(LHS.c_str(), RHS.c_str()); diff --git a/tests/config/config.conf b/tests/config/config.conf index d94094a..c8ea739 100644 --- a/tests/config/config.conf +++ b/tests/config/config.conf @@ -29,6 +29,7 @@ errorVariable = true # hyprlang noerror false categoryKeyword = oops, this one shouldn't call the handler, not fun +testUseKeyword = yes testCategory { testValueInt = 123456 @@ -38,6 +39,9 @@ testCategory { testColor2 = rgba(0, 0, 0, 1.0) testColor3 = rgba(ffeeff22) + testIgnoreKeyword = aaa + testUseKeyword = no + nested1 { testValueNest = 1 nested2 { diff --git a/tests/parse/main.cpp b/tests/parse/main.cpp index bbc84b4..0fdb916 100644 --- a/tests/parse/main.cpp +++ b/tests/parse/main.cpp @@ -23,10 +23,12 @@ namespace Colors { } // globals for testing -bool barrelRoll = false; -std::string flagsFound = ""; -Hyprlang::CConfig* pConfig = nullptr; -std::string currentPath = ""; +bool barrelRoll = false; +std::string flagsFound = ""; +Hyprlang::CConfig* pConfig = nullptr; +std::string currentPath = ""; +std::string ignoreKeyword = ""; +std::string useKeyword = ""; static std::vector categoryKeywordActualValues; static Hyprlang::CParseResult handleDoABarrelRoll(const char* COMMAND, const char* VALUE) { @@ -51,6 +53,22 @@ static Hyprlang::CParseResult handleCategoryKeyword(const char* COMMAND, const c return Hyprlang::CParseResult(); } +static Hyprlang::CParseResult handleTestIgnoreKeyword(const char* COMMAND, const char* VALUE) { + ignoreKeyword = VALUE; + + return Hyprlang::CParseResult(); +} + +static Hyprlang::CParseResult handleTestUseKeyword(const char* COMMAND, const char* VALUE) { + useKeyword = VALUE; + + return Hyprlang::CParseResult(); +} + +static Hyprlang::CParseResult handleNoop(const char* COMMAND, const char* VALUE) { + return Hyprlang::CParseResult(); +} + static Hyprlang::CParseResult handleSource(const char* COMMAND, const char* VALUE) { std::string PATH = std::filesystem::canonical(currentPath + "/" + VALUE); return pConfig->parseFile(PATH.c_str()); @@ -116,6 +134,9 @@ int main(int argc, char** argv, char** envp) { config.registerHandler(&handleDoABarrelRoll, "doABarrelRoll", {false}); config.registerHandler(&handleFlagsTest, "flags", {true}); config.registerHandler(&handleSource, "source", {false}); + config.registerHandler(&handleTestIgnoreKeyword, "testIgnoreKeyword", {false}); + config.registerHandler(&handleTestUseKeyword, ":testUseKeyword", {false}); + config.registerHandler(&handleNoop, "testCategory:testUseKeyword", {false}); config.registerHandler(&handleCategoryKeyword, "testCategory:categoryKeyword", {false}); config.addSpecialCategory("special", {"key"}); @@ -161,6 +182,8 @@ int main(int argc, char** argv, char** envp) { EXPECT(std::any_cast(config.getConfigValue("testStringColon")), std::string{"ee:ee:ee"}); EXPECT(std::any_cast(config.getConfigValue("categoryKeyword")), std::string{"oops, this one shouldn't call the handler, not fun"}); EXPECT(std::any_cast(config.getConfigValue("testCategory:nested1:categoryKeyword")), std::string{"this one should not either"}); + EXPECT(ignoreKeyword, "aaa"); + EXPECT(useKeyword, "yes"); // test static values std::cout << " → Testing static values\n";