mirror of
https://github.com/hyprwm/hyprlang.git
synced 2025-12-20 03:50:02 +01:00
config: try variables before handlers if possible
This commit is contained in:
parent
4dafa28d4f
commit
995db114b8
4 changed files with 82 additions and 62 deletions
|
|
@ -442,17 +442,17 @@ namespace Hyprlang {
|
|||
}
|
||||
|
||||
private:
|
||||
bool m_bCommenced = false;
|
||||
bool m_bCommenced = false;
|
||||
|
||||
CConfigImpl* impl;
|
||||
CConfigImpl* impl;
|
||||
|
||||
CParseResult parseLine(std::string line, bool dynamic = false);
|
||||
CParseResult configSetValueSafe(const std::string& command, const std::string& value);
|
||||
CParseResult parseVariable(const std::string& lhs, const std::string& rhs, bool dynamic = false);
|
||||
void clearState();
|
||||
void applyDefaultsToCat(SSpecialCategory& cat);
|
||||
void retrieveKeysForCat(const char* category, const char*** out, size_t* len);
|
||||
CParseResult parseRawStream(const std::string& stream);
|
||||
CParseResult parseLine(std::string line, bool dynamic = false);
|
||||
std::pair<bool, CParseResult> configSetValueSafe(const std::string& command, const std::string& value);
|
||||
CParseResult parseVariable(const std::string& lhs, const std::string& rhs, bool dynamic = false);
|
||||
void clearState();
|
||||
void applyDefaultsToCat(SSpecialCategory& cat);
|
||||
void retrieveKeysForCat(const char* category, const char*** out, size_t* len);
|
||||
CParseResult parseRawStream(const std::string& stream);
|
||||
};
|
||||
|
||||
/*!
|
||||
|
|
|
|||
100
src/config.cpp
100
src/config.cpp
|
|
@ -279,7 +279,8 @@ static std::expected<int64_t, std::string> configStringToInt(const std::string&
|
|||
return 0;
|
||||
}
|
||||
|
||||
CParseResult CConfig::configSetValueSafe(const std::string& command, const std::string& value) {
|
||||
// found, result
|
||||
std::pair<bool, CParseResult> CConfig::configSetValueSafe(const std::string& command, const std::string& value) {
|
||||
CParseResult result;
|
||||
|
||||
std::string valueName;
|
||||
|
|
@ -358,7 +359,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
|
|||
if (VALUEIT != sc->values.end())
|
||||
found = true;
|
||||
else if (sc->descriptor->dontErrorOnMissing)
|
||||
return result; // will return a success, cuz we want to ignore missing
|
||||
return {true, result}; // will return a success, cuz we want to ignore missing
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
@ -407,7 +408,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
|
|||
} else {
|
||||
if (VALUEIT == PCAT->values.end() || VALUEIT->first != sc->key) {
|
||||
result.setError(std::format("special category's first value must be the key. Key for <{}> is <{}>", PCAT->name, PCAT->key));
|
||||
return result;
|
||||
return {true, result};
|
||||
}
|
||||
impl->currentSpecialKey = value;
|
||||
}
|
||||
|
|
@ -418,7 +419,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
|
|||
|
||||
if (!found) {
|
||||
result.setError(std::format("config option <{}> does not exist.", valueName));
|
||||
return result;
|
||||
return {false, result};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -428,7 +429,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
|
|||
const auto INT = configStringToInt(value);
|
||||
if (!INT.has_value()) {
|
||||
result.setError(INT.error());
|
||||
return result;
|
||||
return {true, result};
|
||||
}
|
||||
|
||||
VALUEIT->second.setFrom(INT.value());
|
||||
|
|
@ -440,7 +441,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
|
|||
VALUEIT->second.setFrom(std::stof(value));
|
||||
} catch (std::exception& e) {
|
||||
result.setError(std::format("failed parsing a float: {}", e.what()));
|
||||
return result;
|
||||
return {true, result};
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -458,7 +459,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
|
|||
VALUEIT->second.setFrom(SVector2D{.x = std::stof(LHS), .y = std::stof(RHS)});
|
||||
} catch (std::exception& e) {
|
||||
result.setError(std::format("failed parsing a vec2: {}", e.what()));
|
||||
return result;
|
||||
return {true, result};
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -473,19 +474,19 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
|
|||
|
||||
if (RESULT.error) {
|
||||
result.setError(RESULT.getError());
|
||||
return result;
|
||||
return {true, result};
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
result.setError("internal error: invalid value found (no type?)");
|
||||
return result;
|
||||
return {true, result};
|
||||
}
|
||||
}
|
||||
|
||||
VALUEIT->second.m_bSetByUser = true;
|
||||
|
||||
return result;
|
||||
return {true, result};
|
||||
}
|
||||
|
||||
CParseResult CConfig::parseVariable(const std::string& lhs, const std::string& rhs, bool dynamic) {
|
||||
|
|
@ -791,45 +792,50 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
|||
|
||||
bool found = false;
|
||||
|
||||
for (auto& h : impl->handlers) {
|
||||
// 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 = HANDLERNAME.find(':', idx)) != std::string::npos && impl->categories.size() > depth) {
|
||||
auto actual = HANDLERNAME.substr(idx, colon - idx);
|
||||
|
||||
if (actual != impl->categories[depth])
|
||||
break;
|
||||
|
||||
idx = colon + 1;
|
||||
++depth;
|
||||
}
|
||||
|
||||
if (depth != impl->categories.size() || HANDLERNAME.substr(idx) != LHS)
|
||||
continue;
|
||||
}
|
||||
|
||||
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());
|
||||
found = true;
|
||||
if (!impl->configOptions.verifyOnly) {
|
||||
auto [f, rv] = configSetValueSafe(LHS, RHS);
|
||||
found = f;
|
||||
ret = std::move(rv);
|
||||
}
|
||||
|
||||
if (!found && !impl->configOptions.verifyOnly)
|
||||
ret = configSetValueSafe(LHS, RHS);
|
||||
if (!found) {
|
||||
for (auto& h : impl->handlers) {
|
||||
// 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 = HANDLERNAME.find(':', idx)) != std::string::npos && impl->categories.size() > depth) {
|
||||
auto actual = HANDLERNAME.substr(idx, colon - idx);
|
||||
|
||||
if (actual != impl->categories[depth])
|
||||
break;
|
||||
|
||||
idx = colon + 1;
|
||||
++depth;
|
||||
}
|
||||
|
||||
if (depth != impl->categories.size() || HANDLERNAME.substr(idx) != LHS)
|
||||
continue;
|
||||
}
|
||||
|
||||
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());
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret.error)
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ specialGeneric {
|
|||
|
||||
specialAnonymous {
|
||||
value = 2
|
||||
testHandlerDontOverride = true
|
||||
}
|
||||
|
||||
specialAnonymous {
|
||||
|
|
|
|||
|
|
@ -23,12 +23,13 @@ namespace Colors {
|
|||
}
|
||||
|
||||
// globals for testing
|
||||
bool barrelRoll = false;
|
||||
std::string flagsFound = "";
|
||||
Hyprlang::CConfig* pConfig = nullptr;
|
||||
std::string currentPath = "";
|
||||
std::string ignoreKeyword = "";
|
||||
std::string useKeyword = "";
|
||||
bool barrelRoll = false;
|
||||
std::string flagsFound = "";
|
||||
Hyprlang::CConfig* pConfig = nullptr;
|
||||
std::string currentPath = "";
|
||||
std::string ignoreKeyword = "";
|
||||
std::string useKeyword = "";
|
||||
bool testHandlerDontOverrideValue = false;
|
||||
static std::vector<std::string> categoryKeywordActualValues;
|
||||
|
||||
static Hyprlang::CParseResult handleDoABarrelRoll(const char* COMMAND, const char* VALUE) {
|
||||
|
|
@ -74,6 +75,14 @@ static Hyprlang::CParseResult handleSource(const char* COMMAND, const char* VALU
|
|||
return pConfig->parseFile(PATH.c_str());
|
||||
}
|
||||
|
||||
static Hyprlang::CParseResult handleTestHandlerDontOverride(const char* COMMAND, const char* VALUE) {
|
||||
testHandlerDontOverrideValue = true;
|
||||
|
||||
Hyprlang::CParseResult result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static Hyprlang::CParseResult handleCustomValueSet(const char* VALUE, void** data) {
|
||||
if (!*data)
|
||||
*data = calloc(1, sizeof(int64_t));
|
||||
|
|
@ -147,12 +156,14 @@ int main(int argc, char** argv, char** envp) {
|
|||
config.registerHandler(&handleTestUseKeyword, ":testUseKeyword", {.allowFlags = false});
|
||||
config.registerHandler(&handleNoop, "testCategory:testUseKeyword", {.allowFlags = false});
|
||||
config.registerHandler(&handleCategoryKeyword, "testCategory:categoryKeyword", {.allowFlags = false});
|
||||
config.registerHandler(&handleTestHandlerDontOverride, "testHandlerDontOverride", {.allowFlags = false});
|
||||
|
||||
config.addSpecialCategory("special", {.key = "key"});
|
||||
config.addSpecialConfigValue("special", "value", (Hyprlang::INT)0);
|
||||
|
||||
config.addSpecialCategory("specialAnonymous", {.key = nullptr, .ignoreMissing = false, .anonymousKeyBased = true});
|
||||
config.addSpecialConfigValue("specialAnonymous", "value", (Hyprlang::INT)0);
|
||||
config.addSpecialConfigValue("specialAnonymous", "testHandlerDontOverride", (Hyprlang::INT)0);
|
||||
|
||||
config.addSpecialCategory("specialAnonymousNested", {.key = nullptr, .ignoreMissing = false, .anonymousKeyBased = true});
|
||||
config.addSpecialConfigValue("specialAnonymousNested", "nested:value1", (Hyprlang::INT)0);
|
||||
|
|
@ -239,6 +250,7 @@ int main(int argc, char** argv, char** envp) {
|
|||
std::cout << " → Testing handlers\n";
|
||||
EXPECT(barrelRoll, true);
|
||||
EXPECT(flagsFound, std::string{"abc"});
|
||||
EXPECT(testHandlerDontOverrideValue, false);
|
||||
|
||||
EXPECT(categoryKeywordActualValues.at(0), "we are having fun");
|
||||
EXPECT(categoryKeywordActualValues.at(1), "so much fun");
|
||||
|
|
@ -315,6 +327,7 @@ int main(int argc, char** argv, char** envp) {
|
|||
EXPECT(config.listKeysForSpecialCategory("specialAnonymous").size(), 2);
|
||||
const auto KEYS = config.listKeysForSpecialCategory("specialAnonymous");
|
||||
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialAnonymous", "value", KEYS[0].c_str())), 2);
|
||||
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialAnonymous", "testHandlerDontOverride", KEYS[0].c_str())), 1);
|
||||
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialAnonymous", "value", KEYS[1].c_str())), 3);
|
||||
|
||||
// test anonymous nested
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue