home
readme
diff
tree
note
docs
0commit 5c7ab985d8fc32ca0a47252da49377f12377d3e0
1Author: Mason Wright <mason@lakefox.net>
2Date: Fri Aug 8 17:36:16 2025 -0600
3
4 Adding support of parsing of at-rules
5
6 At rule & sheet type update
7
8diff --git a/.gitignore b/.gitignore
9old mode 100644
10new mode 100755
11diff --git a/Makefile b/Makefile
12old mode 100644
13new mode 100755
14index 0406c7f..29d253e
15--- a/Makefile
16+++ b/Makefile
17@@ -1,5 +1,5 @@
18 # --- Variables ---
19-CCFLAGS = -Wall -Wextra -std=c++23 -g
20+CCFLAGS = -Wall -Wextra -std=c++2b -g
21 CC = clang++
22
23 .PHONY: all clean run docs serve
24@@ -54,7 +54,6 @@ MD_FILES := $(shell find docs/content -name "*.md")
25 HTML_FILES := $(MD_FILES:docs/content/%.md=docs/dist/%.html)
26
27 docs: $(HTML_FILES)
28- cd ../build && ./tracker.sh
29
30 # See 080f729 note for the template command
31 docs/dist/%.html: docs/content/%.md docs/components/navbar.html
32diff --git a/docs/components/navbar.html b/docs/components/navbar.html
33old mode 100644
34new mode 100755
35index 666d5d0..e9e6756
36--- a/docs/components/navbar.html
37+++ b/docs/components/navbar.html
38@@ -8,8 +8,7 @@
39 <details open>
40 <summary>Links</summary>
41 <ul>
42- <li><a href="https://src.grimui.com">Source</a></li>
43- <li><a href="https://tracker.grimui.com">Trackers</a></li>
44+ <li><a href="sftp://share@grimui.com">Source</a></li>
45 </ul>
46 </details>
47 <details open>
48diff --git a/docs/content/features/supported-css-selectors.md b/docs/content/features/supported-css-selectors.md
49old mode 100644
50new mode 100755
51index cda043a..4dc2a6b
52--- a/docs/content/features/supported-css-selectors.md
53+++ b/docs/content/features/supported-css-selectors.md
54@@ -108,10 +108,16 @@
55 | @counter-style ||||||
56 | @font-face ||||||
57 | @font-feature-values ||||||
58+| @swash | / |||| Subset of @font-feature-values |
59+| @annotation | / |||| Subset of @font-feature-values |
60+| @ornaments | / |||| Subset of @font-feature-values |
61+| @stylistic | / |||| Subset of @font-feature-values |
62+| @styleset | / |||| Subset of @font-feature-values |
63+| @character-varient | / |||| Subset of @font-feature-values |
64 | @font-palette-values ||||||
65-| @import ||||| <string> and <url> support only |
66+| @import ||||| Parsed where the first argument after @import is the url and the last non parenthesized value if present is the layer name. Inbetween the url and the layer is the media queries. |
67 | @keyframes ||||||
68-| @layer ||||||
69+| @layer ||||| Currently setting the layer order via @layer l1, l2; is not supported |
70 | @media ||||||
71 | @namespace ||||||
72 | @page | / |||| Will not be supported, currently there are no plans to support printing |
73diff --git a/docs/content/index.md b/docs/content/index.md
74old mode 100644
75new mode 100755
76diff --git a/docs/template.html b/docs/template.html
77old mode 100644
78new mode 100755
79index ad2b1b2..a9db999
80--- a/docs/template.html
81+++ b/docs/template.html
82@@ -9,7 +9,7 @@ body {
83 margin-bottom: 100px;
84 }
85 table {
86- width: 100%;
87+ width: 100%%;
88 border-collapse: collapse;
89 }
90 th,
91@@ -27,7 +27,7 @@ th {
92 <main style="margin: 0 4rem 0 4rem">
93 <div style="display: flex; gap: 1rem">
94 %s
95- <div style="width: 100%">
96+ <div style="width: 100%%">
97 <section>%s</section>
98 </div>
99 </div>
100diff --git a/include/.grim.h.swp b/include/.grim.h.swp
101new file mode 100644
102index 0000000..6bd7da8
103Binary files /dev/null and b/include/.grim.h.swp differ
104diff --git a/include/catch_amalgamated.cpp b/include/catch_amalgamated.cpp
105old mode 100644
106new mode 100755
107diff --git a/include/catch_amalgamated.hpp b/include/catch_amalgamated.hpp
108old mode 100644
109new mode 100755
110diff --git a/include/grim.h b/include/grim.h
111old mode 100644
112new mode 100755
113index 93364ff..205ef5c
114--- a/include/grim.h
115+++ b/include/grim.h
116@@ -145,31 +145,40 @@ std::vector<std::vector<std::string>> parseSelectorParts(std::string_view);
117
118 bool testSelector(Node*, std::vector<std::vector<std::string>>);
119
120-struct Environment {
121- int width;
122- int height;
123-};
124-
125-struct Style {
126- std::unordered_map<std::string, std::string> properties;
127- std::vector<std::vector<std::string>> selector;
128- size_t index;
129-};
130-
131-enum SheetType {
132- LAYER,
133+enum class SheetType {
134 CONTAINER,
135 COUNTER,
136- FONT,
137+ FONTFACE,
138+ FONTFEATURE,
139+ SWASH,
140+ ANNOTATION,
141+ ORNAMENTS,
142+ STYLISTIC,
143+ STYLESET,
144+ CHARACTERVARIANT,
145+ FONTPALETTEVALUES,
146 IMPORT,
147+ KEYFRAMES,
148+ LAYER,
149 NAMESPACE,
150 PAGE,
151 PROPERTY,
152- STARTINGSTYLE
153+ ROOT,
154+ SHEET,
155+ MEDIA,
156+ STARTINGSTYLE,
157+ STYLE
158 };
159
160-class StyleSheet {
161- private:
162+// A sheet is a group of styles, layers can be nested for specifity and the order can be set via
163+// @layer order...;
164+// all style sheets are parsed into ROOT layers
165+// the non-layer styles will be placed inside of "styles" and they take priority over the layers
166+// all at rules are placed inside of "children"
167+// defining the order of the layers only effects the children with the type layer
168+// the rest are defined in the order they are parsed
169+class Sheet {
170+ public:
171 // basemap: Maps baseparts to a index of the styles vector pointing to a Style object
172 // because processing a CSS selector isn't trivial we want to make a short list of
173 // the styles that can possibly be a match so we aren't spending a lot of compute
174@@ -179,175 +188,165 @@ class StyleSheet {
175 // is a candidate and we run the full selector on it. That includes checking parents,children,
176 // or what ever else the selector needs to match.
177 std::unordered_map<std::string, std::vector<size_t>> basemap;
178- std::vector<Style> styles;
179
180- public:
181 std::string name;
182 std::string path;
183- query query;
184- SheetType type;
185+ std::vector<std::vector<std::string>> selector;
186+ std::unordered_map<std::string, std::string> properties;
187
188+ SheetType type;
189+ std::unordered_map<std::string, std::string> variables;
190
191- void add(std::string selector, std::unordered_map<std::string, std::string> properties);
192+ Sheet* parent;
193+ std::vector<Sheet> children;
194 };
195
196-struct AtRule {
197- std::string identifier;
198- std::string rule;
199- // How to capture the data inside a block?
200- // it could be anything that can be in a style sheet
201- // basically need to parse a @rule like a seperate stylesheet
202- // then merge when a condition is meant if its applicable
203- //
204- // if linked a shared document pointer then each should be able to
205- // report if applies. the main one should be the same as the rest
206-
207- // this will be a circular reference kinda
208- StyleSheet styleSheet;
209-};
210+class Window;
211
212 class StyleHandler {
213 private:
214-
215+
216 std::vector<std::string> parseNodeParts(Node* node);
217- /*
218- // At rules: should be querable, these will apply over the getStyles if they apply
219- // also overwrite the variables
220- std::unordered_map<std::string, Style> rules;
221
222- // Map of all of the CSS varibles and system generated variables like (width, height)
223 // to compute the @ rules.
224 std::unordered_map<std::string, std::string> variables;
225
226- Document* document;
227-
228-
229- struct WindowState {
230- // pull from the Document object
231- double aspectRatio;
232- };*/
233+ Window* window;
234
235 public:
236-
237-
238- std::unordered_map<std::string, std::string> getStyles(Node* node);
239+ StyleHandler(Window* w): window(w) {}
240+ // Rules from the master.css file are direct styles of this object
241+ // all sheets are parsed as children of the ROOT
242+ Sheet root;
243
244- Style item(int index);
245+ std::unordered_map<std::string, std::string> getStyles(Node* node);
246
247+ void parseStream(std::istream&);
248 // make a function that will return a list of attributes the will cause style changes if set
249 };
250
251 enum class AnyHover {
252- None,
253- Hover
254+ None,
255+ Hover
256 };
257
258 enum class AnyPointer {
259- None,
260- Fine,
261- Coarse
262+ None,
263+ Fine,
264+ Coarse
265 };
266
267 enum class Orientation {
268- Landscape,
269- Portrait
270+ Landscape,
271+ Portrait
272 };
273
274 enum class ColorScheme {
275- Light,
276- Dark
277+ Light,
278+ Dark
279 };
280
281 enum class Contrast {
282- NoPreference,
283- More,
284- Less
285+ NoPreference,
286+ More,
287+ Less
288 };
289
290 enum class ReducedMotion {
291- NoPreference,
292- Reduce
293+ NoPreference,
294+ Reduce
295 };
296
297 enum class ReducedTransparency {
298- NoPreference,
299- Reduce
300+ NoPreference,
301+ Reduce
302 };
303
304 enum class Update {
305- Fast,
306- Slow
307+ Fast,
308+ Slow
309 };
310
311 class Window {
312-private:
313- int width = 0;
314- int height = 0;
315- int resolution = 0;
316- AnyHover anyHover = AnyHover::Hover;
317- AnyPointer anyPointer = AnyPointer::Fine;
318- Orientation orientation = Orientation::Landscape;
319- ColorScheme colorScheme = ColorScheme::Light;
320- Contrast contrast = Contrast::NoPreference;
321- ReducedMotion reducedMotion = ReducedMotion::NoPreference;
322- ReducedTransparency reducedTransparency = ReducedTransparency::NoPreference;
323- Update update = Update::Fast;
324-
325-public:
326- void setWidth(int value) {
327- width = value;
328- }
329- void setHeight(int value) {
330- height = value;
331- }
332- void setResolution(int value) {
333- resolution = value;
334- }
335-
336- void setMouse(AnyPointer pointer) {
337- if (pointer == AnyPointer::None) {
338- anyHover = AnyHover::None;
339- } else {
340- anyHover = AnyHover::Hover;
341- }
342- anyPointer = pointer;
343- }
344-
345- void setOrientation(Orientation value) {
346- orientation = value;
347- }
348-
349- void setColorScheme(ColorScheme value) {
350- colorScheme = value;
351- }
352-
353- void setContrast(Contrast value) {
354- contrast = value;
355- }
356-
357- void setReducedMotion(ReducedMotion value) {
358- reducedMotion = value;
359- }
360-
361- void setReducedTransparency(ReducedTransparency value) {
362- reducedTransparency = value;
363- }
364-
365- void setUpdate(Update value) {
366- update = value;
367- }
368-
369- // Getters
370- int getWidth() const {
371- return width;
372- }
373- int getHeight() const {
374- return height;
375- }
376- double getAspectRatio() const {
377- if (height == 0) return 0.0;
378- return static_cast<double>(width) / height;
379- }
380+ private:
381+ int width = 0;
382+ int height = 0;
383+ int resolution = 0;
384+ AnyHover anyHover = AnyHover::Hover;
385+ AnyPointer anyPointer = AnyPointer::Fine;
386+ Orientation orientation = Orientation::Landscape;
387+ ColorScheme colorScheme = ColorScheme::Light;
388+ Contrast contrast = Contrast::NoPreference;
389+ ReducedMotion reducedMotion = ReducedMotion::NoPreference;
390+ ReducedTransparency reducedTransparency = ReducedTransparency::NoPreference;
391+ Update update = Update::Fast;
392+
393+ public:
394+
395+ StyleHandler CSS;
396+
397+ Window(): CSS(this) {}
398+ // +---------------------------------------------------+
399+ // | (hldfk) Window Properties |
400+ // +---------------------------------------------------+
401+
402+ void setWidth(int value) {
403+ width = value;
404+ }
405+ void setHeight(int value) {
406+ height = value;
407+ }
408+ void setResolution(int value) {
409+ resolution = value;
410+ }
411+
412+ void setMouse(AnyPointer pointer) {
413+ if (pointer == AnyPointer::None) {
414+ anyHover = AnyHover::None;
415+ } else {
416+ anyHover = AnyHover::Hover;
417+ }
418+ anyPointer = pointer;
419+ }
420+
421+ void setOrientation(Orientation value) {
422+ orientation = value;
423+ }
424+
425+ void setColorScheme(ColorScheme value) {
426+ colorScheme = value;
427+ }
428+
429+ void setContrast(Contrast value) {
430+ contrast = value;
431+ }
432+
433+ void setReducedMotion(ReducedMotion value) {
434+ reducedMotion = value;
435+ }
436+
437+ void setReducedTransparency(ReducedTransparency value) {
438+ reducedTransparency = value;
439+ }
440+
441+ void setUpdate(Update value) {
442+ update = value;
443+ }
444+
445+ // Getters
446+ int getWidth() const {
447+ return width;
448+ }
449+ int getHeight() const {
450+ return height;
451+ }
452+ double getAspectRatio() const {
453+ if (height == 0) return 0.0;
454+ return static_cast<double>(width) / height;
455+ }
456+
457+
458+
459 };
460
461 #endif // NODE_H
462diff --git a/include/parser.h b/include/parser.h
463old mode 100644
464new mode 100755
465diff --git a/src/adapter.cc b/src/adapter.cc
466old mode 100644
467new mode 100755
468diff --git a/src/events.cc b/src/events.cc
469old mode 100644
470new mode 100755
471diff --git a/src/grim.cc b/src/grim.cc
472index 2988bcf..75aa4d3 100755
473--- a/src/grim.cc
474+++ b/src/grim.cc
475@@ -15,6 +15,7 @@
476 // |(TOC) Table of contents |
477 // +------------------------------------------------------+
478 // |(p98ym) Utils |
479+// | (hldfk) Document Parsing |
480 // |(tigfn) Attribute Handling |
481 // |(f98y0) ClassList |
482 // |(lhujn) StyleList |
483@@ -62,6 +63,16 @@ void trimSpace(std::string& str) {
484 str.erase(0, first_non_space);
485 }
486 }
487+// +---------------------------------------------------+
488+// | (hldfk) Document Parsing |
489+// +---------------------------------------------------+
490+
491+// Node* CreateDocument(std::string path) {
492+ // Parse document
493+ // Pull links from head and get stylesheets
494+ // parse css (pass window to precalculate all of the values)
495+// }
496+
497
498 // +---------------------------------------------------+
499 // | (tigfn) Attribute Handling |
500@@ -222,8 +233,8 @@ void Node::ClassList::remove(std::string value) {
501 if (newLen == pair.second) {
502 if (classes.substr(pair.first, pair.second) == value) {
503 // Splice out the value to be removed
504- // !ISSUE: This will keep the surounding spaces and cause the size to grow when
505- // + classes are added and removed
506+ // ISSUE: This will keep the surounding spaces and cause the size to grow when
507+ // classes are added and removed
508 self->attributes["class"] = classes.substr(0, pair.first)+classes.substr(pair.first+pair.second);
509 }
510 }
511@@ -509,7 +520,7 @@ std::string Node::print(int indent) {
512 // parseSelectorParts deconstructs a selector into its indiviual parts
513 // so they can be store and used in things like finding styles for basemap
514 // and comparing a node to a selector in testSelector. This is a higher level function
515-// that should be ran once perselector and the results saved somewhere due to
516+// that should be ran once parselector and the results saved somewhere due to
517 // the high cost to run the function
518
519 std::vector<std::vector<std::string>> parseSelectorParts(std::string_view selector) {
520@@ -672,13 +683,21 @@ std::vector<std::string> StyleHandler::parseNodeParts(Node* node) {
521 return parts;
522 }
523
524-void StyleHandler::add(std::string selector, std::unordered_map<std::string, std::string> properties) {
525+/*
526+void Sheet::add(std::string selector, std::unordered_map<std::string, std::string> properties) {
527 // Type is 2d vector of selector parts
528
529 auto parts = parseSelectorParts(selector);
530+
531+ for (auto pr : parts) {
532+ for (auto p : pr) {
533+ std::cout << p << std::endl;
534+ }
535+ }
536+
537 size_t index = styles.size();
538
539- styles.push_back({properties,parts,index});
540+ styles.push_back({parts,properties});
541
542 // Get the last selector group from the parsed selector parts
543 // because that is the selector that applies to an element
544@@ -693,7 +712,9 @@ void StyleHandler::add(std::string selector, std::unordered_map<std::string, std
545 }
546 }
547 }
548-
549+*/
550+
551+/*
552 std::unordered_map<std::string, std::string> StyleHandler::getStyles(Node* node) {
553 // TODO: Will need to change getStyles to first consider if the style sheet applies
554 // then to check the selectors
555@@ -757,6 +778,7 @@ std::unordered_map<std::string, std::string> StyleHandler::getStyles(Node* node)
556 Style StyleHandler::item(int index) {
557 return styles[index];
558 }
559+*/
560
561 // Used for root node without a parent
562 const std::vector<std::unique_ptr<Node>> EMPTY_NODE_CHILDREN_VECTOR;
563diff --git a/src/parser.cc b/src/parser.cc
564old mode 100644
565new mode 100755
566index c85915a..f819dcb
567--- a/src/parser.cc
568+++ b/src/parser.cc
569@@ -310,7 +310,7 @@ std::unique_ptr<Node> parseHTML(std::istream& inputStream) {
570 }
571 }
572
573- // !TODO: Add a vector that stores inner text and when elements start to close out pop them but add all to their innerText
574+ // TODO: Add a vector that stores inner text and when elements start to close out pop them but add all to their innerText
575 // + Will need something simular for innerHTML
576 if (hasText) {
577 auto node = currentNode->createElement("text");
578@@ -335,88 +335,14 @@ std::unique_ptr<Node> parseHTML(std::istream& inputStream) {
579 return root;
580 }
581
582-struct SelectorAndProps {
583- std::string selector;
584- std::unordered_map<std::string, std::string> props;
585-};
586-
587-std::vector<std::string> genSelectorStrings(std::string prefix, const std::vector<SelectorAndProps>& stack) {
588- std::vector<std::string> selectors;
589-
590- std::string buffer;
591- bool inParens = false;
592- size_t len = stack[0].selector.length();
593- for (size_t i = 0; i < len; i++) {
594- char c = stack[0].selector[i];
595- if (c == '(') {
596- inParens = true;
597- }
598- if (c == ')') {
599- inParens = false;
600- }
601- if ((c == ',' && !inParens) || i == len-1) {
602- if (i == len-1 && c != ',') {
603- buffer.push_back(c);
604- }
605- std::vector<std::string> s;
606-
607- if (stack.size() > 1) {
608- std::vector<SelectorAndProps> temp(stack.begin()+1, stack.end());
609- size_t lastChar = buffer.length()-1;
610- for (size_t k = buffer.length()-1; k >= 0; k--) {
611- if (!std::isspace(buffer[k])) {
612- lastChar = k+1;
613- break;
614- }
615- }
616- s = genSelectorStrings(buffer.substr(0, lastChar), temp);
617- } else {
618- s.push_back(buffer);
619- }
620-
621- for (size_t j = 0; j < s.size(); j++) {
622- std::string temp;
623- bool hasAmp = false;
624- for (size_t k = 0; k < s[j].length(); k++) {
625- char c = s[j][k];
626- if (c == '&') {
627- hasAmp = true;
628- for (auto p : prefix) {
629- temp.push_back(p);
630- }
631- } else {
632- temp.push_back(c);
633- }
634- }
635- // Remove trailing space
636- size_t lastChar = temp.length() - 1;
637- for (size_t k = temp.length()-1; k >= 0; k--) {
638- if (!std::isspace(temp[k])) {
639- lastChar = k+1;
640- break;
641- }
642- }
643- if (!hasAmp) {
644- selectors.push_back(prefix+" "+temp.substr(0,lastChar));
645- } else {
646- selectors.push_back(temp.substr(0,lastChar));
647- }
648- }
649- buffer.clear();
650-
651- } else {
652- buffer.push_back(c);
653- }
654- }
655- return selectors;
656-}
657-
658-std::unique_ptr<StyleHandler> parseCSS(std::istream& inputStream) {
659+void StyleHandler::parseStream(std::istream& inputStream) {
660 // nowhitespace: value(can have whitespace); = property
661 // a selector name is anything before a { up to a ; or a }
662- std::unique_ptr<StyleHandler> handler = std::make_unique<StyleHandler>();
663- std::vector<SelectorAndProps> stack;
664- SelectorAndProps current;
665+ Sheet* root = &window->CSS.root;
666+
667+ Sheet* current = root;
668+ // how to handle @import
669+
670 std::string buffer;
671
672 bool incomment = false;
673@@ -439,58 +365,143 @@ std::unique_ptr<StyleHandler> parseCSS(std::istream& inputStream) {
674
675
676 if (ch == '{') {
677- // start of a group of properties
678- // need to parse the selector and add it to the stack
679- current.selector = buffer;
680- stack.push_back(current);
681- current = SelectorAndProps();
682+ // Parse the selector into its parts
683+ std::vector<std::vector<std::string>> parts = parseSelectorParts(buffer);
684+
685+ // Find who the parent is
686+ Sheet newSheet;
687+ newSheet.parent = current;
688+ newSheet.selector = parts;
689+ current->children.push_back(newSheet);
690+
691+ current = ¤t->children.back();
692+
693+ // here we need to take the selector (buffer)
694+ // and find out what type it is. Also need to manage the nesting
695+ if (parts[0][0][0] == '@') {
696+ if (parts[0][0] == "@container") {
697+ current->type = SheetType::CONTAINER;
698+ bool hasName = false;
699+ if (parts[0].size() >= 3) {
700+ // @container not (width < 400px)
701+ if (parts[0][2] != "not") {
702+ // @container (width > 400px) or|and (height > 400px)
703+ if (parts[0].size() >= 5) {
704+ if (parts[0][5] != "and" || parts[0][5] != "or") {
705+ current->name = parts[0][2];
706+ hasName = true;
707+ }
708+ }
709+ }
710+ }
711+ // If a name is present then remove the name and tag from the selector
712+ // else just remove the @container
713+ if (hasName) {
714+ current->selector[0] = std::vector<std::string>(current->selector[0].begin() + 2, current->selector[0].end());
715+ } else {
716+
717+ current->selector[0] = std::vector<std::string>(current->selector[0].begin() + 1, current->selector[0].end());
718+ }
719+ } else if (parts[0][0] == "@counter-style") {
720+ current->name = parts[0][2];
721+ current->type = SheetType::COUNTER;
722+ } else if (parts[0][0] == "@font-face") {
723+ current->type = SheetType::FONTFACE;
724+ } else if (parts[0][0] == "@font-feature") {
725+ current->type = SheetType::FONTFEATURE;
726+ } else if (parts[0][0] == "@swash") {
727+ current->type = SheetType::SWASH;
728+ } else if (parts[0][0] == "@annotation") {
729+ current->type = SheetType::ANNOTATION;
730+ } else if (parts[0][0] == "@ornaments") {
731+ current->type = SheetType::ORNAMENTS;
732+ } else if (parts[0][0] == "@stylistic") {
733+ current->type = SheetType::STYLISTIC;
734+ } else if (parts[0][0] == "@styleset") {
735+ current->type = SheetType::STYLESET;
736+ } else if (parts[0][0] == "@character-varient") {
737+ current->type = SheetType::CHARACTERVARIANT;
738+ } else if (parts[0][0] == "@font-palette-values") {
739+ current->type = SheetType::FONTPALETTEVALUES;
740+ current->name = parts[0][2];
741+ } else if (parts[0][0] == "@keyframes") {
742+ current->type = SheetType::KEYFRAMES;
743+ current->name = parts[0][2];
744+ } else if (parts[0][0] == "@layer") {
745+ current->type = SheetType::LAYER;
746+ } else if (parts[0][0] == "@media") {
747+ current->type = SheetType::MEDIA;
748+ } else if (parts[0][0] == "@property") {
749+ current->type = SheetType::PROPERTY;
750+ current->name = parts[0][2];
751+ } else if (parts[0][0] == "@starting-style") {
752+ current->type = SheetType::STARTINGSTYLE;
753+ }
754+ // ISSUE: need to remove the leading at-rule and other data
755+ } else {
756+ // Rework this, should make a Style not a Sheet
757+ current->type = SheetType::STYLE;
758+ }
759
760+
761 buffer.clear();
762 } else if (ch == '}') {
763- // compile the selector and add to the handler
764- // This handles the nested selectors
765- std::vector<std::string> selectors = genSelectorStrings("", stack);
766-
767- for (auto s : selectors) {
768- handler->add(s, current.props);
769+ if (current->parent != nullptr) {
770+ current = current->parent;
771 }
772
773- if (!stack.empty()) {
774- current = stack.back();
775- stack.pop_back();
776- } else {
777- current = SelectorAndProps();
778- }
779 buffer.clear();
780 } else if (ch == ';') {
781- // end of a style property
782- std::string key;
783- std::string value;
784- bool keyFound = false;
785- for (size_t i = 0; i < buffer.length(); i++) {
786- char c = buffer[i];
787- if (!keyFound && c == ':') {
788- keyFound = true;
789- } else if (!std::isspace(c) && !keyFound) {
790- key += c;
791- } else if (keyFound &&
792- !(std::isspace(c) && i < buffer.length()-1 && std::isspace(buffer[i+1]))) {
793- // if the key is found and the charactor isn't a ':'
794- // also (if c is a space and the next c will be a space)
795- // this is to auto trim duplicate spaces from the value
796- value += c;
797-
798+ if (buffer[0] == '@') {
799+ // Single line at-rule
800+ Sheet inlineRule;
801+ std::vector<std::vector<std::string>> parts = parseSelectorParts(buffer);
802+ inlineRule.selector = parts;
803+
804+ if (parts[0][0] == "@import") {
805+ inlineRule.path = parts[0][2];
806+
807+ if (parts[0].size() > 3) {
808+ // See if the last word ends in )
809+ // if not then its the name of the layer
810+ if (parts[0].back().back() != ')') {
811+ inlineRule.name = parts[0].back();
812+ }
813+ }
814+
815+ } else if (parts[0][0] == "@layer") {
816+ // need to add speficity
817+
818+ }
819+
820+ current->children.push_back(inlineRule);
821+
822+ } else {
823+ // end of a style property
824+ std::string key;
825+ std::string value;
826+ bool keyFound = false;
827+ for (size_t i = 0; i < buffer.length(); i++) {
828+ char c = buffer[i];
829+ if (!keyFound && c == ':') {
830+ keyFound = true;
831+ } else if (!std::isspace(c) && !keyFound) {
832+ key += c;
833+ } else if (keyFound && !(std::isspace(c) && i < buffer.length()-1 && std::isspace(buffer[i+1]))) {
834+ // if the key is found and the charactor isn't a ':'
835+ // also (if c is a space and the next c will be a space)
836+ // this is to auto trim duplicate spaces from the value
837+ value += c;
838+ }
839 }
840- }
841
842- current.props[key] = value;
843+ current->properties[key] = value;
844+ }
845 buffer.clear();
846 } else {
847 buffer.push_back(ch);
848 }
849 }
850-
851- return handler;
852 }
853
854 std::unordered_map<std::string, std::string> parseCSSInline(std::string inlineStyles) {
855diff --git a/tests/css_parser.cc b/tests/css_parser.cc
856old mode 100644
857new mode 100755
858index 8470cc1..66bea3c
859--- a/tests/css_parser.cc
860+++ b/tests/css_parser.cc
861@@ -47,9 +47,8 @@ void trimSpace2(std::string& str) {
862 str.erase(0, first_non_space);
863 }
864 }
865-
866-void compareStyle(std::string selector, std::unordered_map<std::string, std::string> props, size_t index, Style style) {
867- REQUIRE(style.index == index);
868+/*
869+void compareStyle(std::string selector, std::unordered_map<std::string, std::string> props, Style style) {
870 std::vector<std::vector<std::string>> parts = parseSelectorParts(selector);
871 INFO(selector);
872 INFO(printSelector( style.selector));
873@@ -73,7 +72,7 @@ void compareStyle(std::string selector, std::unordered_map<std::string, std::str
874 REQUIRE(f == s);
875 }
876 }
877-
878+*/
879 void compareInlineStyles(std::string style, std::unordered_map<std::string, std::string> targetProps) {
880 std::unordered_map<std::string, std::string> props = parseCSSInline(style);
881
882@@ -147,30 +146,32 @@ html#nested {
883 transition-duration: 0.5s;
884 }
885 }
886-
887-@media not all and (hover: hover) {
888- abbr::after {
889- content: " (" attr(title) ")";
890- }
891-}
892 )~~~";
893
894 std::istringstream inputFile(cssContent);
895
896- std::unique_ptr<StyleHandler> handler = parseCSS(inputFile);
897- SECTION("StyleHandler Benchmark") {
898+ Window window;
899+ window.CSS.parseStream(inputFile);
900+
901+ StyleHandler handler = window.CSS;
902+
903+ std::cout << handler.root.children.size() << std::endl;
904+
905+ /*SECTION("StyleHandler Benchmark") {
906 BENCHMARK("master.css") {
907 std::ifstream tempFile("./tests/master.css");
908 return parseCSS(tempFile);
909 };
910- }
911+ }*/
912
913+/*
914 SECTION("parseCSS can parse simple CSS rules") {
915 compareStyle("h1", {
916 {"color", "red"}
917- }, 0, handler->item(0));
918+ }, handler.root.styles[0]);
919 }
920-
921+*/
922+/*
923 SECTION("parseCSS can parse a CSS rule with a child combinator") {
924 compareStyle("h1 > #id", {
925 {"background", "purple"}
926@@ -234,8 +235,10 @@ html#nested {
927 {"font-size", "10px"}
928 }, 9, handler->item(9));
929 }
930+*/
931 }
932
933+/*
934 TEST_CASE("parseCSS can parse multiple selectors nested") {
935 std::string cssContent = R"~~~(
936 div, .class1 {
937@@ -267,6 +270,8 @@ div, .class1 {
938 }
939
940 }
941+*/
942+
943
944 TEST_CASE("parseCSSInline can parse inline styles") {
945 SECTION("parseCSSInline can parse a single property with a ending ;") {
946@@ -300,4 +305,29 @@ TEST_CASE("parseCSSInline can parse inline styles") {
947 });
948 }
949 }
950-// !TODO: Figure out how to handle @ rules
951+
952+/*
953+TEST_CASE("parseCSS-at-rules") {
954+ std::string cssContent = R"~~~(
955+@container (width > 400px) {
956+ h2 {
957+ font-size: 1.5em;
958+ }
959+}
960+
961+// With an optional <container-name>
962+@container tall (height > 30rem) {
963+ p {
964+ line-height: 1.6;
965+ }
966+}
967+)~~~";
968+
969+ std::istringstream inputFile(cssContent);
970+
971+ std::unique_ptr<StyleHandler> handler = parseCSS(inputFile);
972+ SECTION("parseCSS can parse @container") {
973+ REQUIRE(handler->item(1).name == "tall");
974+ }
975+}
976+*/
977diff --git a/tests/css_selector.cc b/tests/css_selector.cc
978old mode 100644
979new mode 100755
980diff --git a/tests/html_node.cc b/tests/html_node.cc
981old mode 100644
982new mode 100755
983diff --git a/tests/html_parser.cc b/tests/html_parser.cc
984old mode 100644
985new mode 100755
986diff --git a/tests/index.html b/tests/index.html
987old mode 100644
988new mode 100755
989diff --git a/tests/style.css b/tests/style.css
990old mode 100644
991new mode 100755