mirror of
https://github.com/hyprwm/hyprlang.git
synced 2024-11-17 02:25:59 +01:00
feat: implement multiline support
This commit is contained in:
parent
ec6938c662
commit
e15cfdcd97
4 changed files with 116 additions and 9 deletions
|
@ -3,6 +3,7 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <format>
|
#include <format>
|
||||||
|
#include <iostream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <expected>
|
#include <expected>
|
||||||
|
@ -473,8 +474,9 @@ void CConfigImpl::parseComment(const std::string& comment) {
|
||||||
|
|
||||||
CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
||||||
CParseResult result;
|
CParseResult result;
|
||||||
|
bool shouldPreverseLeadingWhitespace = impl->multiline.delimiter == '\\';
|
||||||
|
|
||||||
line = trim(line);
|
line = shouldPreverseLeadingWhitespace ? line.substr(0, line.find_last_not_of(MULTILINE_SPACE_CHARSET) + 1) : trim(line);
|
||||||
|
|
||||||
auto commentPos = line.find('#');
|
auto commentPos = line.find('#');
|
||||||
|
|
||||||
|
@ -496,6 +498,8 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
||||||
|
|
||||||
if (!escaped) {
|
if (!escaped) {
|
||||||
line = line.substr(0, commentPos);
|
line = line.substr(0, commentPos);
|
||||||
|
// there might be trailing whitespaces after the comment that weren't previous trimmed
|
||||||
|
line = line.substr(0, line.find_last_not_of(MULTILINE_SPACE_CHARSET) + 1);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
line = line.substr(0, commentPos + 1) + line.substr(commentPos + 2);
|
line = line.substr(0, commentPos + 1) + line.substr(commentPos + 2);
|
||||||
|
@ -503,27 +507,29 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
line = trim(line);
|
if (line.empty()) {
|
||||||
|
if (impl->multiline.active)
|
||||||
|
result.setError("Found empty line while parsing multiline value");
|
||||||
|
|
||||||
if (line.empty())
|
|
||||||
return result;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
auto equalsPos = line.find('=');
|
auto equalsPos = line.find('=');
|
||||||
|
|
||||||
if (equalsPos == std::string::npos && !line.ends_with("{") && line != "}") {
|
if (equalsPos == std::string::npos && !line.ends_with("{") && line != "}" && !impl->multiline.active) {
|
||||||
// invalid line
|
// invalid line
|
||||||
result.setError("Invalid config line");
|
result.setError("Invalid config line");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (equalsPos != std::string::npos) {
|
if (equalsPos != std::string::npos || impl->multiline.active) {
|
||||||
// set value or call handler
|
// set value or call handler
|
||||||
CParseResult ret;
|
CParseResult ret;
|
||||||
auto LHS = trim(line.substr(0, equalsPos));
|
auto LHS = impl->multiline.active ? impl->multiline.lhs : trim(line.substr(0, equalsPos));
|
||||||
auto RHS = trim(line.substr(equalsPos + 1));
|
auto RHS = impl->multiline.active ? line : trim(line.substr(equalsPos + 1));
|
||||||
|
|
||||||
if (LHS.empty()) {
|
if (LHS.empty()) {
|
||||||
result.setError("Empty lhs.");
|
result.setError("Empty lhs");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,6 +568,34 @@ CParseResult CConfig::parseLine(std::string line, bool dynamic) {
|
||||||
if (ISVARIABLE)
|
if (ISVARIABLE)
|
||||||
return parseVariable(LHS, RHS, dynamic);
|
return parseVariable(LHS, RHS, dynamic);
|
||||||
|
|
||||||
|
auto lastChar = RHS[RHS.size() - 1];
|
||||||
|
bool isMultilineContinuation = lastChar == '\\' || lastChar == '>';
|
||||||
|
|
||||||
|
if (isMultilineContinuation && impl->multiline.active && impl->multiline.delimiter != lastChar) {
|
||||||
|
result.setError("Multiline continuation character mismatch. Make sure you are not mixing \\ and >");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (impl->multiline.buffer.size() > 0 && impl->multiline.delimiter == '>')
|
||||||
|
impl->multiline.buffer += " ";
|
||||||
|
|
||||||
|
impl->multiline.active = isMultilineContinuation;
|
||||||
|
|
||||||
|
if (isMultilineContinuation) {
|
||||||
|
impl->multiline.lhs = LHS;
|
||||||
|
impl->multiline.delimiter = lastChar;
|
||||||
|
RHS.erase(RHS.size() - 1);
|
||||||
|
impl->multiline.buffer += RHS.substr(0, RHS.find_last_not_of(MULTILINE_SPACE_CHARSET) + 1);
|
||||||
|
|
||||||
|
return CParseResult{};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!impl->multiline.buffer.empty()) {
|
||||||
|
RHS = impl->multiline.buffer + RHS;
|
||||||
|
impl->multiline.buffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (auto& h : impl->handlers) {
|
for (auto& h : impl->handlers) {
|
||||||
if (!h.options.allowFlags && h.name != LHS)
|
if (!h.options.allowFlags && h.name != LHS)
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
static const char* MULTILINE_SPACE_CHARSET = " \t";
|
||||||
|
|
||||||
struct SHandler {
|
struct SHandler {
|
||||||
std::string name = "";
|
std::string name = "";
|
||||||
Hyprlang::SHandlerOptions options;
|
Hyprlang::SHandlerOptions options;
|
||||||
|
@ -33,6 +35,13 @@ enum eDataType {
|
||||||
CONFIGDATATYPE_CUSTOM,
|
CONFIGDATATYPE_CUSTOM,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SMultiLine {
|
||||||
|
std::string buffer;
|
||||||
|
char delimiter = 0;
|
||||||
|
bool active = false;
|
||||||
|
std::string lhs;
|
||||||
|
};
|
||||||
|
|
||||||
// CUSTOM is stored as STR!!
|
// CUSTOM is stored as STR!!
|
||||||
struct SConfigDefaultValue {
|
struct SConfigDefaultValue {
|
||||||
std::any data;
|
std::any data;
|
||||||
|
@ -83,6 +92,8 @@ class CConfigImpl {
|
||||||
std::vector<std::string> categories;
|
std::vector<std::string> categories;
|
||||||
std::string currentSpecialKey = "";
|
std::string currentSpecialKey = "";
|
||||||
SSpecialCategory* currentSpecialCategory = nullptr; // if applicable
|
SSpecialCategory* currentSpecialCategory = nullptr; // if applicable
|
||||||
|
bool isSpecialCategory = false;
|
||||||
|
SMultiLine multiline;
|
||||||
|
|
||||||
std::string parseError = "";
|
std::string parseError = "";
|
||||||
|
|
||||||
|
@ -93,4 +104,4 @@ class CConfigImpl {
|
||||||
struct {
|
struct {
|
||||||
bool noError = false;
|
bool noError = false;
|
||||||
} currentFlags;
|
} currentFlags;
|
||||||
};
|
};
|
||||||
|
|
|
@ -79,6 +79,51 @@ flagsStuff {
|
||||||
value = 2
|
value = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# '\' POSIX shell-like multiline syntax
|
||||||
|
# Leading spaces are preserved while trailing spaces and linebreaks are discarded
|
||||||
|
multilineSimple = I use C++ because \
|
||||||
|
I hate Java
|
||||||
|
|
||||||
|
multilineTrim = I use Javascript because \ # Spaces should be trimmed before and after the delimiter
|
||||||
|
I hate Python # here also.
|
||||||
|
|
||||||
|
multilineVar = $SPECIALVAL1 \
|
||||||
|
$SPECIALVAL1 \
|
||||||
|
$SPECIALVAL1 \
|
||||||
|
$SPECIALVAL1
|
||||||
|
|
||||||
|
$NAME = multiline
|
||||||
|
|
||||||
|
multilineBreakWord = Hello $NAME, how are you to\
|
||||||
|
day?
|
||||||
|
|
||||||
|
multilineMultiBreakWord = oui \
|
||||||
|
oui \
|
||||||
|
b \
|
||||||
|
a \
|
||||||
|
g \
|
||||||
|
u \
|
||||||
|
e \
|
||||||
|
t \
|
||||||
|
t \
|
||||||
|
e
|
||||||
|
|
||||||
|
# Another syntax for multiline.
|
||||||
|
# Ignores leading and trailing whitespaces. Linebreaks are turned into spaces.
|
||||||
|
|
||||||
|
multilineCategory {
|
||||||
|
indentedMultiline = Hello >
|
||||||
|
world >
|
||||||
|
this is another syntax for >
|
||||||
|
multiline that trims all spaces
|
||||||
|
|
||||||
|
multilineUneven = Hello >
|
||||||
|
world >
|
||||||
|
this is another syntax for >
|
||||||
|
multiline that trims all spaces
|
||||||
|
}
|
||||||
|
|
||||||
testCategory:testValueHex = 0xFFfFaAbB
|
testCategory:testValueHex = 0xFFfFaAbB
|
||||||
|
|
||||||
$RECURSIVE1 = a
|
$RECURSIVE1 = a
|
||||||
|
|
|
@ -103,6 +103,14 @@ int main(int argc, char** argv, char** envp) {
|
||||||
config.addConfigValue("myColors:green", (Hyprlang::INT)0);
|
config.addConfigValue("myColors:green", (Hyprlang::INT)0);
|
||||||
config.addConfigValue("myColors:random", (Hyprlang::INT)0);
|
config.addConfigValue("myColors:random", (Hyprlang::INT)0);
|
||||||
config.addConfigValue("customType", {Hyprlang::CConfigCustomValueType{&handleCustomValueSet, &handleCustomValueDestroy, "def"}});
|
config.addConfigValue("customType", {Hyprlang::CConfigCustomValueType{&handleCustomValueSet, &handleCustomValueDestroy, "def"}});
|
||||||
|
config.addConfigValue("multilineSimple", (Hyprlang::STRING) "");
|
||||||
|
config.addConfigValue("multilineTrim", (Hyprlang::STRING) "");
|
||||||
|
config.addConfigValue("multilineVar", (Hyprlang::STRING) "");
|
||||||
|
config.addConfigValue("multilineTrim", (Hyprlang::STRING) "");
|
||||||
|
config.addConfigValue("multilineBreakWord", (Hyprlang::STRING) "");
|
||||||
|
config.addConfigValue("multilineMultiBreakWord", (Hyprlang::STRING) "");
|
||||||
|
config.addConfigValue("multilineCategory:indentedMultiline", (Hyprlang::STRING) "");
|
||||||
|
config.addConfigValue("multilineCategory:multilineUneven", (Hyprlang::STRING) "");
|
||||||
|
|
||||||
config.registerHandler(&handleDoABarrelRoll, "doABarrelRoll", {false});
|
config.registerHandler(&handleDoABarrelRoll, "doABarrelRoll", {false});
|
||||||
config.registerHandler(&handleFlagsTest, "flags", {true});
|
config.registerHandler(&handleFlagsTest, "flags", {true});
|
||||||
|
@ -149,6 +157,15 @@ int main(int argc, char** argv, char** envp) {
|
||||||
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testCategory:testColor2")), (Hyprlang::INT)0xFF000000);
|
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testCategory:testColor2")), (Hyprlang::INT)0xFF000000);
|
||||||
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testCategory:testColor3")), (Hyprlang::INT)0x22ffeeff);
|
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testCategory:testColor3")), (Hyprlang::INT)0x22ffeeff);
|
||||||
EXPECT(std::any_cast<const char*>(config.getConfigValue("testStringColon")), std::string{"ee:ee:ee"});
|
EXPECT(std::any_cast<const char*>(config.getConfigValue("testStringColon")), std::string{"ee:ee:ee"});
|
||||||
|
EXPECT(std::any_cast<const char*>(config.getConfigValue("multilineSimple")), std::string{"I use C++ because I hate Java"});
|
||||||
|
EXPECT(std::any_cast<const char*>(config.getConfigValue("multilineTrim")), std::string{"I use Javascript because I hate Python"});
|
||||||
|
EXPECT(std::any_cast<const char*>(config.getConfigValue("multilineVar")), std::string{"1 1 1 1"});
|
||||||
|
EXPECT(std::any_cast<const char*>(config.getConfigValue("multilineBreakWord")), std::string{"Hello multiline, how are you today?"});
|
||||||
|
EXPECT(std::any_cast<const char*>(config.getConfigValue("multilineMultiBreakWord")), std::string{"oui oui baguette"});
|
||||||
|
EXPECT(std::any_cast<const char*>(config.getConfigValue("multilineCategory:indentedMultiline")),
|
||||||
|
std::string{"Hello world this is another syntax for multiline that trims all spaces"});
|
||||||
|
EXPECT(std::any_cast<const char*>(config.getConfigValue("multilineCategory:multilineUneven")),
|
||||||
|
std::string{"Hello world this is another syntax for multiline that trims all spaces"});
|
||||||
|
|
||||||
// test static values
|
// test static values
|
||||||
std::cout << " → Testing static values\n";
|
std::cout << " → Testing static values\n";
|
||||||
|
|
Loading…
Reference in a new issue