home
readme
diff
tree
note
docs
0commit e4e05418a640eaed08cd1ec7cd8644eb1dbcca50
1Author: Mason Wright <mason@lakefox.net>
2Date: Sun Jul 6 16:46:32 2025 -0600
3
4 Allowed parseSelectorParts to parse +p
5
6diff --git a/include/parser.h b/include/parser.h
7index 68f0246..155b798 100644
8--- a/include/parser.h
9+++ b/include/parser.h
10@@ -6,11 +6,12 @@
11 #include <istream>
12 #include <unordered_map>
13 #include <string>
14+#include <string_view>
15
16 class Node;
17
18 // Declare the parsing functions
19-std::unordered_map<std::string, std::string> parseAttributes(std::string token);
20+std::unordered_map<std::string, std::string> parseAttributes(std::string_view& token);
21 std::unique_ptr<Node> parseStream(std::istream& inputStream);
22
23 #endif
24diff --git a/src/grim.cc b/src/grim.cc
25index 2bf0c39..1bf4374 100755
26--- a/src/grim.cc
27+++ b/src/grim.cc
28@@ -340,6 +340,7 @@ struct Style {
29 // and comparing a node to a selector in testSelector. This is a higher level function
30 // that should be ran once perselector and the results saved somewhere
31 // selector: any CSS selector
32+// //!TODO: have parseSelectorParts return string_views
33 std::vector<std::vector<std::string>> parseSelectorParts(std::string_view selector) {
34 // need to account for selectors with parenthesis like: h1:(+ input:required)
35 // need to convert all single quotes to double quotes
36@@ -377,7 +378,7 @@ std::vector<std::vector<std::string>> parseSelectorParts(std::string_view select
37 }
38 }
39
40- if (nesting == 0 && !current.empty()) {
41+ if (nesting == 0 ) {
42 if (s == ':' || s == '[' || s == ',' || s == '#' || s == '.') {
43 // We convert any : selectors (like :checked) to [checked]
44 // because we store every thing as attributes on the Node
45@@ -396,32 +397,38 @@ std::vector<std::vector<std::string>> parseSelectorParts(std::string_view select
46 }
47 current = s;
48 } else
49- if (s == '>' || s == '+' || s == '~' || s == ' ') {
50- if (std::isspace(current.front()) || std::isspace(current.back())) {
51- trimSpace(current);
52- }
53-
54- if (current[0] == ':' && current.back() != ')') {
55- current[0] = '[';
56- current.push_back(']');
57+ if (s == '>' || s == '+' || s == '~' || s == ' ') {
58+ if (std::isspace(current.front()) || std::isspace(current.back())) {
59+ trimSpace(current);
60+ }
61+ if (!current.empty()) {
62+ if (current[0] == ':' && current.back() != ')') {
63+ current[0] = '[';
64+ current.push_back(']');
65+ }
66+
67+ buffer.push_back(std::move(current));
68+ buffer.push_back("");
69+ buffer.back() += s;
70+
71+ } else
72+ if (s != ' ') {
73+ buffer.push_back("");
74+ buffer.back() += s;
75+ }
76+ current.clear();
77+ } else if (s != '\0') {
78+ current.push_back(s);
79 }
80-
81- buffer.push_back(std::move(current));
82- buffer.push_back("");
83- buffer.back() += s;
84- current.erase();
85- } else if (s != '\0') {
86- current.push_back(s);
87- }
88 } else if (s != '\0') {
89 current.push_back(s);
90 }
91
92 if ((s == ',' && nesting == 0) || e == sl-1) {
93+ if (std::isspace(current.front()) || std::isspace(current.back())) {
94+ trimSpace(current);
95+ };
96 if (!current.empty() && current != ",") {
97- if (std::isspace(current.front()) || std::isspace(current.back())) {
98- trimSpace(current);
99- };
100 if (current[0] == ':' && current.back() != ')') {
101 current[0] = '[';
102 current.push_back(']');
103@@ -431,7 +438,7 @@ std::vector<std::vector<std::string>> parseSelectorParts(std::string_view select
104 }
105 parts.push_back(std::move(buffer));
106 buffer.clear();
107- current.erase();
108+ current.clear();
109 }
110 if (s == '(' || s == '[' || s == '{') {
111 nesting++;
112diff --git a/tests/css_selector.cc b/tests/css_selector.cc
113index 38fb7a0..ee88bda 100644
114--- a/tests/css_selector.cc
115+++ b/tests/css_selector.cc
116@@ -99,6 +99,9 @@ TEST_CASE( "CSS Selection", "[css]" ) {
117 return parseSelectorParts("h1, h1 ~p, :last-child");
118 };
119 }
120+ SECTION("parseSelectorParts can parse a relative selector without another reference") {
121+ checkSelectorParts("+ p", {{"+", "p"}});
122+ }
123 }
124
125 TEST_CASE("testSelector", "[CSS]") {
126@@ -219,6 +222,11 @@ TEST_CASE("testSelector", "[CSS]") {
127 return testSelector(input, parseSelectorParts(":not(:is(h1, html, div))"));
128 };
129 }
130+ SECTION("testSelector can match a :has() selector") {
131+ auto h1 = document->children[0]->children[1]->children[0].get();
132+ REQUIRE(testSelector(h1, parseSelectorParts("h1:has(+ p)")));
133+ REQUIRE_FALSE(testSelector(h1, parseSelectorParts("h1:has(+ p:required)")));
134+ }
135 // make for:
136 // pseudo elements
137 // *