Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

css: Add support for shorthand property padding #33

Merged
merged 1 commit into from Sep 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
63 changes: 60 additions & 3 deletions css/parser.h
Expand Up @@ -73,7 +73,8 @@ class Parser final : util::BaseParser {
skip_whitespace();

while (peek() != '}') {
rule.declarations.insert(parse_declaration());
auto [name, value] = parse_declaration();
add_declaration(rule.declarations, name, value);
skip_whitespace();
}

Expand All @@ -82,13 +83,69 @@ class Parser final : util::BaseParser {
return rule;
}

std::pair<std::string, std::string> parse_declaration() {
std::pair<std::string_view, std::string_view> parse_declaration() {
auto name = consume_while([](char c) { return c != ':'; });
consume_char(); // :
skip_whitespace();
auto value = consume_while([](char c) { return c != ';' && c != '}'; });
skip_if_neq('}'); // ;
return {std::string{name}, std::string{value}};
return {name, value};
}

void add_declaration(std::map<std::string, std::string>& declarations,
std::string_view name,
std::string_view value) {
if (name == "padding") {
expand_padding(declarations, value);
} else {
declarations.insert_or_assign(std::string{name}, std::string{value});
}
}

void expand_padding(std::map<std::string, std::string>& declarations, std::string_view value) {
std::string_view top = "", bottom = "", left = "", right = "";
auto values = split(value, ' ');
switch (values.size()) {
case 1:
top = bottom = left = right = values[0];
break;
case 2:
top = bottom = values[0];
left = right = values[1];
break;
case 3:
top = values[0];
left = right = values[1];
bottom = values[2];
break;
case 4:
top = values[0];
right = values[1];
bottom = values[2];
left = values[3];
break;
default:
break;
}
declarations.insert_or_assign("padding-top", std::string{top});
declarations.insert_or_assign("padding-bottom", std::string{bottom});
declarations.insert_or_assign("padding-left", std::string{left});
declarations.insert_or_assign("padding-right", std::string{right});
}

std::vector<std::string_view> split(std::string_view str, char delimiter) {
std::vector<std::string_view> result;
std::size_t pos = 0, loc = 0;
while ((loc = str.find(delimiter, pos)) != std::string_view::npos) {
if (auto substr = str.substr(pos, loc-pos); !substr.empty()) {
result.push_back(substr);
}
pos = loc+1;
}
if (pos < str.size()) {
result.push_back(str.substr(pos, str.size()-pos));
}
return result;
}
};

Expand Down
80 changes: 80 additions & 0 deletions css/parser_test.cpp
Expand Up @@ -134,5 +134,85 @@ int main() {
expect(a.media_query.empty());
});

etest::test("parser: shorthand padding, one value", [] {
auto rules = css::parse("p { padding: 10px; }"sv);
require(rules.size() == 1);

auto body = rules[0];
expect(body.declarations.size() == 4);
expect(body.declarations.at("padding-top"s) == "10px"s);
expect(body.declarations.at("padding-bottom"s) == "10px"s);
expect(body.declarations.at("padding-left"s) == "10px"s);
expect(body.declarations.at("padding-right"s) == "10px"s);
});

etest::test("parser: shorthand padding, two values", [] {
auto rules = css::parse("p { padding: 12em 36em; }"sv);
require(rules.size() == 1);

auto body = rules[0];
expect(body.declarations.size() == 4);
expect(body.declarations.at("padding-top"s) == "12em"s);
expect(body.declarations.at("padding-bottom"s) == "12em"s);
expect(body.declarations.at("padding-left"s) == "36em"s);
expect(body.declarations.at("padding-right"s) == "36em"s);
});

etest::test("parser: shorthand padding, three values", [] {
auto rules = css::parse("p { padding: 12px 36px 52px; }"sv);
require(rules.size() == 1);

auto body = rules[0];
expect(body.declarations.size() == 4);
expect(body.declarations.at("padding-top"s) == "12px"s);
expect(body.declarations.at("padding-bottom"s) == "52px"s);
expect(body.declarations.at("padding-left"s) == "36px"s);
expect(body.declarations.at("padding-right"s) == "36px"s);
});

etest::test("parser: shorthand padding, four values", [] {
auto rules = css::parse("p { padding: 12px 36px 52px 2px; }"sv);
require(rules.size() == 1);

auto body = rules[0];
expect(body.declarations.size() == 4);
expect(body.declarations.at("padding-top"s) == "12px"s);
expect(body.declarations.at("padding-right"s) == "36px"s);
expect(body.declarations.at("padding-bottom"s) == "52px"s);
expect(body.declarations.at("padding-left"s) == "2px"s);
});

etest::test("parser: shorthand padding overridden", [] {
auto rules = css::parse("p {\n"
"padding: 10px;\n"
"padding-top: 15px;\n"
"padding-left: 25px;\n"
"}\n"sv);
require(rules.size() == 1);

auto body = rules[0];
expect(body.declarations.size() == 4);
expect(body.declarations.at("padding-top"s) == "15px"s);
expect(body.declarations.at("padding-bottom"s) == "10px"s);
expect(body.declarations.at("padding-left"s) == "25px"s);
expect(body.declarations.at("padding-right"s) == "10px"s);
});

etest::test("parser: override padding with shorthand", [] {
auto rules = css::parse("p {\n"
"padding-bottom: 5px;\n"
"padding-left: 25px;\n"
"padding: 12px 40px;\n"
"}\n"sv);
require(rules.size() == 1);

auto body = rules[0];
expect(body.declarations.size() == 4);
expect(body.declarations.at("padding-top"s) == "12px"s);
expect(body.declarations.at("padding-bottom"s) == "12px"s);
expect(body.declarations.at("padding-left"s) == "40px"s);
expect(body.declarations.at("padding-right"s) == "40px"s);
});

return etest::run_all_tests();
}