commit b862381d54d9e9f7e7c211dbdbf768f3f48728ce
parent 07de6e47c5e479c5deadf225c4b8d62d0dc076ae
Author: Demonstrandum <moi@knutsen.co>
Date:   Mon, 22 Jun 2020 05:55:25 +0100
Properly documentise HTML with missing tags.
Diffstat:
3 files changed, 85 insertions(+), 13 deletions(-)
diff --git a/src/assemble/html.rs b/src/assemble/html.rs
@@ -1,6 +1,6 @@
 //! Assembles an expanded tree into valid HTML.
 use super::Documentise;
-use crate::parse::parser::{ParseNode, ParseTree};
+use crate::parse::parser::{self, ParseNode, ParseTree};
 
 use std::fmt::{self, Display};
 
@@ -24,16 +24,17 @@ pub const DEFAULT : &str =
 
 impl Documentise for HTMLFormatter {
     fn document(&self) -> String {
-        // Check if <!DOCTYPE html> exists.
         let mut doc = String::new();
         if self.tree.is_empty() {
             return String::from(DEFAULT);
         }
-        let mut current_node = &self.tree[0];
-        let mut has_declaration = false;
+        let stripped = parser::strip(&self.tree);
+        let mut current_node = stripped.get(0);
 
-        if let ParseNode::List(list) = ¤t_node {
-            if let Some(ParseNode::Symbol(declaration)) = &list.get(0) {
+        // Check if <!DOCTYPE html> exists.
+        let mut has_declaration = false;
+        if let Some(ParseNode::List(list)) = current_node.as_ref() {
+            if let Some(ParseNode::Symbol(declaration)) = list.get(0) {
                 if declaration.value.to_lowercase() == "!doctype" {
                     has_declaration = true;
                 }
@@ -41,17 +42,70 @@ impl Documentise for HTMLFormatter {
         }
 
         if has_declaration {
-            current_node = &self.tree[1];
+            current_node = stripped.get(1);
         } else {
             doc += "<!DOCTYPE html>\n"
         }
+
         // Check if <html></html> root object exists.
-        // Check if head exits, if not, make an empty one.
-        // Check if body exists, if not, make it, and put everything
-        // in there.
+        let mut html_tag = false;
+        if let Some(ParseNode::List(list)) = current_node.as_ref() {
+            if let Some(ParseNode::Symbol(root_tag)) = &list.get(0) {
+                if root_tag.value.to_lowercase() == "html" {
+                    html_tag = true;
+                }
+            }
+        }
+
+        if !html_tag {
+            doc += "<html>\n";
+        }
 
+        // Check if <head></head> exists.
+        let mut head_tag = false;
+        if let Some(ParseNode::List(list)) = current_node.as_ref() {
+            if let Some(ParseNode::List(head_list)) = &list.get(1) {
+                if let Some(ParseNode::Symbol(head)) = &head_list.get(0) {
+                    if head.value.to_lowercase() == "head" {
+                        head_tag = true;
+                    }
+                }
+            }
+        }
+
+        if !head_tag {
+            doc += "<head></head>\n";
+        }
+
+        // Check if body exists, if not, make it, and populate it.
+        let mut body_tag = false;
+        if let Some(ParseNode::List(list)) = current_node.as_ref() {
+            if let Some(ParseNode::List(body_list)) = &list.get(2) {
+                if let Some(ParseNode::Symbol(body)) = &body_list.get(0) {
+                    if body.value.to_lowercase() == "body" {
+                        body_tag = true;
+                    }
+                }
+            }
+        }
+
+        if !body_tag {
+            doc += "<body>\n";
+        }
+
+        // Populate.
+        doc += "<!-- Generated from symbolic-expressions \
+                     into HTML. -->\n";
         doc += &self.to_string();
 
+        // Cloes all new tags.
+        if !body_tag {
+            doc += "</body>\n";
+        }
+        if !html_tag {
+            doc += "</html>";
+        }
+
         doc
     }
 }
diff --git a/src/parse/parser.rs b/src/parse/parser.rs
@@ -157,6 +157,23 @@ pub fn parse_stream(tokens: tokens::TokenStream)
     Ok(tree)
 }
 
+/// Strip any pure whitespace nodes from the tree.
+pub fn strip(tree : &ParseTree) -> ParseTree {
+    let mut stripped = tree.to_owned();
+    stripped.retain(|branch| {
+        match branch {
+            ParseNode::String(node) => !node.value.trim().is_empty(),
+            _ => true
+        }
+    });
+    for branch in stripped.iter_mut() {
+        if let ParseNode::List(ref mut list) = branch {
+            *list = strip(list);
+        }
+    }
+    stripped
+}
+
 /// Pretty printing for parse nodes.
 #[cfg(feature="debug")]
 impl fmt::Display for ParseNode {
diff --git a/test.html b/test.html
@@ -1,8 +1,9 @@
+<!-- Generated from symbolic-expressions into HTML. -->
 <!DOCTYPE html>
-<html> <head> <title>Example HTML Document</title></head>
-<body> <p id="hello">Hello, World!</p>
+<html><head><title>Example HTML Document</title></head>
+<body><p id="hello">Hello, World!</p>
 <p>something something text...</p>
-<h1> A (big) Header!</h1>
+<h1>A (big) Header!</h1>
 <p>Yet some more <span style="color: red">text</span> <3</p>
 <p>Hello<span style="color: green">World</span>!</p>
 <img alt="Cat" src="https://static.insider.com/image/5d24d6b921a861093e71fef3.jpg" width="300"></img></body></html>