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:
|
private:
|
||||||
bool m_bCommenced = false;
|
bool m_bCommenced = false;
|
||||||
|
|
||||||
CConfigImpl* impl;
|
CConfigImpl* impl;
|
||||||
|
|
||||||
CParseResult parseLine(std::string line, bool dynamic = false);
|
CParseResult parseLine(std::string line, bool dynamic = false);
|
||||||
CParseResult configSetValueSafe(const std::string& command, const std::string& value);
|
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);
|
CParseResult parseVariable(const std::string& lhs, const std::string& rhs, bool dynamic = false);
|
||||||
void clearState();
|
void clearState();
|
||||||
void applyDefaultsToCat(SSpecialCategory& cat);
|
void applyDefaultsToCat(SSpecialCategory& cat);
|
||||||
void retrieveKeysForCat(const char* category, const char*** out, size_t* len);
|
void retrieveKeysForCat(const char* category, const char*** out, size_t* len);
|
||||||
CParseResult parseRawStream(const std::string& stream);
|
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;
|
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;
|
CParseResult result;
|
||||||
|
|
||||||
std::string valueName;
|
std::string valueName;
|
||||||
|
|
@ -358,7 +359,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
|
||||||
if (VALUEIT != sc->values.end())
|
if (VALUEIT != sc->values.end())
|
||||||
found = true;
|
found = true;
|
||||||
else if (sc->descriptor->dontErrorOnMissing)
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -407,7 +408,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
|
||||||
} else {
|
} else {
|
||||||
if (VALUEIT == PCAT->values.end() || VALUEIT->first != sc->key) {
|
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));
|
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;
|
impl->currentSpecialKey = value;
|
||||||
}
|
}
|
||||||
|
|
@ -418,7 +419,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
result.setError(std::format("config option <{}> does not exist.", valueName));
|
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);
|
const auto INT = configStringToInt(value);
|
||||||
if (!INT.has_value()) {
|
if (!INT.has_value()) {
|
||||||
result.setError(INT.error());
|
result.setError(INT.error());
|
||||||
return result;
|
return {true, result};
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUEIT->second.setFrom(INT.value());
|
VALUEIT->second.setFrom(INT.value());
|
||||||
|
|
@ -440,7 +441,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
|
||||||
VALUEIT->second.setFrom(std::stof(value));
|
VALUEIT->second.setFrom(std::stof(value));
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
result.setError(std::format("failed parsing a float: {}", e.what()));
|
result.setError(std::format("failed parsing a float: {}", e.what()));
|
||||||
return result;
|
return {true, result};
|
||||||
}
|
}
|
||||||
break;
|
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)});
|
VALUEIT->second.setFrom(SVector2D{.x = std::stof(LHS), .y = std::stof(RHS)});
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
result.setError(std::format("failed parsing a vec2: {}", e.what()));
|
result.setError(std::format("failed parsing a vec2: {}", e.what()));
|
||||||
return result;
|
return {true, result};
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -473,19 +474,19 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
|
||||||
|
|
||||||
if (RESULT.error) {
|
if (RESULT.error) {
|
||||||
result.setError(RESULT.getError());
|
result.setError(RESULT.getError());
|
||||||
return result;
|
return {true, result};
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
result.setError("internal error: invalid value found (no type?)");
|
result.setError("internal error: invalid value found (no type?)");
|
||||||
return result;
|
return {true, result};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUEIT->second.m_bSetByUser = true;
|
VALUEIT->second.m_bSetByUser = true;
|
||||||
|
|
||||||
return result;
|
return {true, result};
|
||||||
}
|
}
|
||||||
|
|
||||||
CParseResult CConfig::parseVariable(const std::string& lhs, const std::string& rhs, bool dynamic) {
|
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;
|
bool found = false;
|
||||||
|
|
||||||
for (auto& h : impl->handlers) {
|
if (!impl->configOptions.verifyOnly) {
|
||||||
// we want to handle potentially nested keywords and ensure
|
auto [f, rv] = configSetValueSafe(LHS, RHS);
|
||||||
// we only call the handler if they are scoped correctly,
|
found = f;
|
||||||
// unless the keyword is not scoped itself
|
ret = std::move(rv);
|
||||||
|
|
||||||
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 (!found && !impl->configOptions.verifyOnly)
|
if (!found) {
|
||||||
ret = configSetValueSafe(LHS, RHS);
|
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)
|
if (ret.error)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,7 @@ specialGeneric {
|
||||||
|
|
||||||
specialAnonymous {
|
specialAnonymous {
|
||||||
value = 2
|
value = 2
|
||||||
|
testHandlerDontOverride = true
|
||||||
}
|
}
|
||||||
|
|
||||||
specialAnonymous {
|
specialAnonymous {
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,13 @@ namespace Colors {
|
||||||
}
|
}
|
||||||
|
|
||||||
// globals for testing
|
// globals for testing
|
||||||
bool barrelRoll = false;
|
bool barrelRoll = false;
|
||||||
std::string flagsFound = "";
|
std::string flagsFound = "";
|
||||||
Hyprlang::CConfig* pConfig = nullptr;
|
Hyprlang::CConfig* pConfig = nullptr;
|
||||||
std::string currentPath = "";
|
std::string currentPath = "";
|
||||||
std::string ignoreKeyword = "";
|
std::string ignoreKeyword = "";
|
||||||
std::string useKeyword = "";
|
std::string useKeyword = "";
|
||||||
|
bool testHandlerDontOverrideValue = false;
|
||||||
static std::vector<std::string> categoryKeywordActualValues;
|
static std::vector<std::string> categoryKeywordActualValues;
|
||||||
|
|
||||||
static Hyprlang::CParseResult handleDoABarrelRoll(const char* COMMAND, const char* VALUE) {
|
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());
|
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) {
|
static Hyprlang::CParseResult handleCustomValueSet(const char* VALUE, void** data) {
|
||||||
if (!*data)
|
if (!*data)
|
||||||
*data = calloc(1, sizeof(int64_t));
|
*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(&handleTestUseKeyword, ":testUseKeyword", {.allowFlags = false});
|
||||||
config.registerHandler(&handleNoop, "testCategory:testUseKeyword", {.allowFlags = false});
|
config.registerHandler(&handleNoop, "testCategory:testUseKeyword", {.allowFlags = false});
|
||||||
config.registerHandler(&handleCategoryKeyword, "testCategory:categoryKeyword", {.allowFlags = false});
|
config.registerHandler(&handleCategoryKeyword, "testCategory:categoryKeyword", {.allowFlags = false});
|
||||||
|
config.registerHandler(&handleTestHandlerDontOverride, "testHandlerDontOverride", {.allowFlags = false});
|
||||||
|
|
||||||
config.addSpecialCategory("special", {.key = "key"});
|
config.addSpecialCategory("special", {.key = "key"});
|
||||||
config.addSpecialConfigValue("special", "value", (Hyprlang::INT)0);
|
config.addSpecialConfigValue("special", "value", (Hyprlang::INT)0);
|
||||||
|
|
||||||
config.addSpecialCategory("specialAnonymous", {.key = nullptr, .ignoreMissing = false, .anonymousKeyBased = true});
|
config.addSpecialCategory("specialAnonymous", {.key = nullptr, .ignoreMissing = false, .anonymousKeyBased = true});
|
||||||
config.addSpecialConfigValue("specialAnonymous", "value", (Hyprlang::INT)0);
|
config.addSpecialConfigValue("specialAnonymous", "value", (Hyprlang::INT)0);
|
||||||
|
config.addSpecialConfigValue("specialAnonymous", "testHandlerDontOverride", (Hyprlang::INT)0);
|
||||||
|
|
||||||
config.addSpecialCategory("specialAnonymousNested", {.key = nullptr, .ignoreMissing = false, .anonymousKeyBased = true});
|
config.addSpecialCategory("specialAnonymousNested", {.key = nullptr, .ignoreMissing = false, .anonymousKeyBased = true});
|
||||||
config.addSpecialConfigValue("specialAnonymousNested", "nested:value1", (Hyprlang::INT)0);
|
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";
|
std::cout << " → Testing handlers\n";
|
||||||
EXPECT(barrelRoll, true);
|
EXPECT(barrelRoll, true);
|
||||||
EXPECT(flagsFound, std::string{"abc"});
|
EXPECT(flagsFound, std::string{"abc"});
|
||||||
|
EXPECT(testHandlerDontOverrideValue, false);
|
||||||
|
|
||||||
EXPECT(categoryKeywordActualValues.at(0), "we are having fun");
|
EXPECT(categoryKeywordActualValues.at(0), "we are having fun");
|
||||||
EXPECT(categoryKeywordActualValues.at(1), "so much 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);
|
EXPECT(config.listKeysForSpecialCategory("specialAnonymous").size(), 2);
|
||||||
const auto KEYS = config.listKeysForSpecialCategory("specialAnonymous");
|
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", "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);
|
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialAnonymous", "value", KEYS[1].c_str())), 3);
|
||||||
|
|
||||||
// test anonymous nested
|
// test anonymous nested
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue