home
readme
diff
tree
note
docs
0commit 350e7d88bb2feb9db00c6e032cc6623f215b7adf
1Author: Mason Wright <mason@lakefox.net>
2Date: Thu Jul 3 14:48:07 2025 -0600
3
4 ClassList
5
6diff --git a/Makefile b/Makefile
7index 31db22a..149b1df 100644
8--- a/Makefile
9+++ b/Makefile
10@@ -25,6 +25,9 @@ build/parser.o: src/parser.cc include/parser.h | build
11 run_tests: build/html_parser_test
12 build/html_parser_test
13
14+build/html_node_test: tests/html_node.cc build/grim.o build/catch.o build/parser.o | build
15+ ${CC} ${CCFLAGS} -Iinclude/ tests/html_node.cc build/grim.o build/catch.o build/parser.o -o build/html_node_test
16+
17 build/css_selector_test: tests/css_selector.cc build/grim.o build/catch.o build/parser.o | build
18 ${CC} ${CCFLAGS} -Iinclude/ tests/css_selector.cc build/grim.o build/catch.o build/parser.o -o build/css_selector_test
19
20diff --git a/src/grim.cc b/src/grim.cc
21index f674f6f..1dcde70 100755
22--- a/src/grim.cc
23+++ b/src/grim.cc
24@@ -4,36 +4,148 @@
25 #include <algorithm>
26 #include <stdexcept>
27 #include <cctype>
28+#include <string_view>
29
30-std::string ClassList::value() const {
31- if (classes.empty()) {
32- return "";
33- }
34- std::string collection = classes[0];
35- for (size_t i = 1; i < classes.size(); ++i) {
36- collection += " " + classes[i];
37- }
38- return collection;
39+// Constructor
40+
41+void Node::ClassList::createIndex() {
42+ int index = 0;
43+ // Add a space to the back to help trigger the adding of the
44+ // last item
45+ std::string classes = self->Attributes["class"]+" ";
46+ // Update the stored length
47+ len = classes.length();
48+ indexes.clear();
49+
50+ bool waitForFirstChar = true;
51+
52+ for (size_t i = 0; i < len; i++) {
53+ if (waitForFirstChar && classes[i] != ' ') {
54+ index = i;
55+ waitForFirstChar = false;
56+ } else if (!waitForFirstChar) {
57+ if (classes[i] == ' ') {
58+ indexes.push_back({index, i-index});
59+ waitForFirstChar = true;
60+ }
61+ }
62+ }
63+}
64+
65+
66+bool Node::ClassList::checkIndex() {
67+ std::string classes = self->Attributes["class"];
68+ if (classes.length() != len) {
69+ return false;
70+ } else {
71+ // Skip the first one because there is a space
72+ // also we don't care if the values are the same
73+ // just that the array indexing is the same. If the
74+ // value is the same size and position it doesn't matter
75+ for (size_t i = 1; i < indexes.size(); i++) {
76+ // See if the index position is a space
77+ // if not return false
78+ if (classes[indexes[i-1].first] != ' ') {
79+ return false;
80+ }
81+ }
82+ return true;
83+ }
84 }
85
86-std::vector<std::string> ClassList::values() {
87- return classes;
88+size_t Node::ClassList::length() {
89+ if (!checkIndex()) {
90+ createIndex();
91+ }
92+ return indexes.size();
93 }
94
95-void ClassList::add(std::string value) {
96- classes.push_back(value);
97+
98+std::string Node::ClassList::item(size_t key) {
99+ if (!checkIndex()) {
100+ createIndex();
101+ }
102+ if (key < indexes.size()) {
103+ std::pair<int, int> pair = indexes[key];
104+ return self->Attributes["class"].substr(pair.first, pair.second);
105+ } else {
106+ return "";
107+ }
108 }
109
110-void ClassList::remove(std::string value) {
111- auto it_prev = std::find(classes.begin(), classes.end(), value);
112- if (it_prev != classes.end()) {
113- *it_prev = classes.back();
114- classes.pop_back();
115- }
116+void Node::ClassList::add(std::string value) {
117+ if (!checkIndex()) {
118+ createIndex();
119+ }
120+
121+ if (self->Attributes["class"].length() == 0) {
122+ self->Attributes["class"] += value;
123+ } else {
124+ self->Attributes["class"] += " "+value;
125+ }
126+ // len because the index of the added space will be the length of
127+ // the prevous string
128+ int vl = value.length();
129+ indexes.push_back({len, vl});
130+ len += vl+1;
131 }
132
133-// Constructor
134-Node::Node() : parent(nullptr) {}
135+void Node::ClassList::remove(std::string value) {
136+ if (!checkIndex()) {
137+ createIndex();
138+ }
139+
140+ int newLen = value.length();
141+ size_t cLen = indexes.size();
142+ std::string classes = self->Attributes["class"];
143+
144+ for (size_t i = 0; i < cLen; i++) {
145+ std::pair<int, int> pair = indexes[i];
146+ if (newLen == pair.second) {
147+ if (classes.substr(pair.first, pair.second) == value) {
148+ // Splice out the value to be removed
149+ // !ISSUE: This will keep the surounding spaces and cause the size to grow when
150+ // + classes are added and removed
151+ self->Attributes["class"] = classes.substr(0, pair.first)+classes.substr(pair.first+pair.second);
152+ }
153+ }
154+ }
155+}
156+
157+bool Node::ClassList::contains(std::string value) {
158+ if (!checkIndex()) {
159+ createIndex();
160+ }
161+
162+ int newLen = value.length();
163+ size_t cLen = indexes.size();
164+ std::string classes = self->Attributes["class"];
165+ bool found = false;
166+
167+ for (size_t i = 0; i < cLen; i++) {
168+ std::pair<int, int> pair = indexes[i];
169+ if (newLen == pair.second) {
170+ if (classes.substr(pair.first, pair.second) == value) {
171+ // Same logic as remove but turns found true then breaks
172+ found = true;
173+ break;
174+ }
175+ }
176+ }
177+ return found;
178+}
179+
180+void Node::ClassList::toggle(std::string value) {
181+ if (contains(value)) {
182+ remove(value);
183+ } else {
184+ add(value);
185+ }
186+}
187+
188+std::string Node::ClassList::value() const {
189+ return self->Attributes["class"];
190+}
191
192 std::string Node::getTagName() const { return TagName; }
193 void Node::setTagName(const std::string& name) { TagName = name; }
194@@ -350,10 +462,10 @@ class StyleHandler {
195 }
196 }
197
198- auto classes = node->classList.values();
199+ auto classLen = node->classList.length();
200
201- for (auto c : classes) {
202- parts.push_back("."+c);
203+ for (size_t i = 0; i < classLen; i++) {
204+ parts.push_back("."+node->classList.item(i));
205 }
206
207 return parts;
208@@ -457,7 +569,13 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
209 // returns if matched is false
210 bool matched = true;
211
212- std::vector<std::string> nodeClasses = node->classList.values();
213+ std::vector<std::string> nodeClasses;
214+ size_t classLen = node->classList.length();
215+ for (size_t i = 0; i < classLen; i++) {
216+ nodeClasses.push_back("."+node->classList.item(i));
217+ }
218+
219+
220 std::unordered_map<std::string, std::string> attributes = node->getAttributes();
221 std::vector<std::string> attrs;
222
223@@ -469,26 +587,41 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
224 attrs.push_back("["+a.first+"]");
225 }
226 }
227-
228+
229+ // Find the index of the child relative to it's parent
230+ const auto& children = node->parent->children;
231+ size_t childIndex = 0;
232+ if (children.size() > 1) {
233+ for (size_t i = 0; i < children.size(); i++) {
234+ Node* child_ptr = children[i].get();
235+
236+ if (child_ptr == node) {
237+ childIndex = i;
238+ }
239+ }
240+ }
241+
242+
243 size_t i = 0;
244 for (auto p : parts) {
245- i++;
246 if (i == 0 && p[0] != '#' && p[0] != '.' && p[0] != '[') {
247 matched = node->getTagName() == p;
248 } else if (p[0] == '#') {
249- matched = node->getId() == p;
250+ matched = "#"+node->getId() == p;
251 } else if (p[0] == '.') {
252- // Remove the period
253- std::string clipped = p.substr(1);
254 bool found = false;
255 for (auto c : nodeClasses) {
256- if (clipped == c) {
257+ if (p == c) {
258 found = true;
259 break;
260 }
261 }
262 matched = found;
263 } else if (p[0] == '[') {
264+ // This covers all attributes and non relative pseudo-classes
265+ // as parseSelectorParts converts any attribute
266+ // without a ending ) to [name]:
267+ // See tests/css_selector.cc for examples
268 bool found = false;
269 for (auto a : attrs) {
270 if (p == a) {
271diff --git a/tests/css_selector.cc b/tests/css_selector.cc
272index ab00d29..d2f8ea9 100644
273--- a/tests/css_selector.cc
274+++ b/tests/css_selector.cc
275@@ -3,7 +3,6 @@
276 #include "../include/parser.h"
277 #include <string>
278 #include <fstream>
279-#include <iostream>
280
281 void checkSelectorParts(std::string selector, std::vector<std::vector<std::string>> expected) {
282 INFO("=========================");