From 931ee3a2ae105d278da53d8129d4de38e8bff7e9 Mon Sep 17 00:00:00 2001
From: Chase Fleming <1666730+chasefleming@users.noreply.github.com>
Date: Thu, 12 Oct 2023 07:41:30 -0700
Subject: [PATCH 1/4] Move void elements map
---
elem.go | 59 +++++++++++++++++++++++++++++----------------------------
1 file changed, 30 insertions(+), 29 deletions(-)
diff --git a/elem.go b/elem.go
index ebd55de..e1d71a8 100644
--- a/elem.go
+++ b/elem.go
@@ -4,6 +4,28 @@ import (
"sort"
)
+// List of HTML5 void elements. Void elements, also known as self-closing or empty elements,
+// are elements that don't have a closing tag because they can't contain any content.
+// For example, the tag cannot wrap text or other tags, it stands alone, so it doesn't have a closing tag.
+var voidElements = map[string]bool{
+ "area": true,
+ "base": true,
+ "br": true,
+ "col": true,
+ "command": true,
+ "embed": true,
+ "hr": true,
+ "img": true,
+ "input": true,
+ "keygen": true,
+ "link": true,
+ "meta": true,
+ "param": true,
+ "source": true,
+ "track": true,
+ "wbr": true,
+}
+
type Attrs map[string]string
type Element struct {
@@ -12,36 +34,7 @@ type Element struct {
Children []interface{} // Can be either string (for text) or another Element
}
-func NewElement(tag string, attrs Attrs, children ...interface{}) *Element {
- return &Element{
- Tag: tag,
- Attrs: attrs,
- Children: children,
- }
-}
-
func (e *Element) Render() string {
- // List of HTML5 void elements. Void elements, also known as self-closing or empty elements,
- // are elements that don't have a closing tag because they can't contain any content.
- // For example, the tag cannot wrap text or other tags, it stands alone, so it doesn't have a closing tag.
- var voidElements = map[string]bool{
- "area": true,
- "base": true,
- "br": true,
- "col": true,
- "command": true,
- "embed": true,
- "hr": true,
- "img": true,
- "input": true,
- "keygen": true,
- "link": true,
- "meta": true,
- "param": true,
- "source": true,
- "track": true,
- "wbr": true,
- }
// Sort the keys for consistent order
keys := make([]string, 0, len(e.Attrs))
@@ -72,3 +65,11 @@ func (e *Element) Render() string {
return `<` + e.Tag + props + `>` + content + `` + e.Tag + `>`
}
+
+func NewElement(tag string, attrs Attrs, children ...interface{}) *Element {
+ return &Element{
+ Tag: tag,
+ Attrs: attrs,
+ Children: children,
+ }
+}
From 73a2e5c0b725f6ac852ed867615248472b1ec174 Mon Sep 17 00:00:00 2001
From: Chase Fleming <1666730+chasefleming@users.noreply.github.com>
Date: Thu, 12 Oct 2023 07:54:25 -0700
Subject: [PATCH 2/4] Replace the map value type for space efficiency
---
elem.go | 41 ++++++++++++++++++++++-------------------
1 file changed, 22 insertions(+), 19 deletions(-)
diff --git a/elem.go b/elem.go
index e1d71a8..f6fa113 100644
--- a/elem.go
+++ b/elem.go
@@ -7,23 +7,23 @@ import (
// List of HTML5 void elements. Void elements, also known as self-closing or empty elements,
// are elements that don't have a closing tag because they can't contain any content.
// For example, the tag cannot wrap text or other tags, it stands alone, so it doesn't have a closing tag.
-var voidElements = map[string]bool{
- "area": true,
- "base": true,
- "br": true,
- "col": true,
- "command": true,
- "embed": true,
- "hr": true,
- "img": true,
- "input": true,
- "keygen": true,
- "link": true,
- "meta": true,
- "param": true,
- "source": true,
- "track": true,
- "wbr": true,
+var voidElements = map[string]struct{}{
+ "area": {},
+ "base": {},
+ "br": {},
+ "col": {},
+ "command": {},
+ "embed": {},
+ "hr": {},
+ "img": {},
+ "input": {},
+ "keygen": {},
+ "link": {},
+ "meta": {},
+ "param": {},
+ "source": {},
+ "track": {},
+ "wbr": {},
}
type Attrs map[string]string
@@ -59,8 +59,11 @@ func (e *Element) Render() string {
}
// Check if the tag is a void element and doesn't have any content
- if content == "" && voidElements[e.Tag] {
- return `<` + e.Tag + props + `>` // No closing tag for void elements
+ if content == "" {
+ _, exists := voidElements[e.Tag]
+ if exists {
+ return `<` + e.Tag + props + `>` // No closing tag for void elements
+ }
}
return `<` + e.Tag + props + `>` + content + `` + e.Tag + `>`
From 81b497a5562b9fd7efd3d8b808cadcf496573ab5 Mon Sep 17 00:00:00 2001
From: Chase Fleming <1666730+chasefleming@users.noreply.github.com>
Date: Fri, 13 Oct 2023 08:39:50 -0700
Subject: [PATCH 3/4] Change to string builder
---
elem.go | 42 +++++++++++++++++++++++++++++-------------
1 file changed, 29 insertions(+), 13 deletions(-)
diff --git a/elem.go b/elem.go
index f6fa113..13d1b53 100644
--- a/elem.go
+++ b/elem.go
@@ -2,6 +2,7 @@ package elem
import (
"sort"
+ "strings"
)
// List of HTML5 void elements. Void elements, also known as self-closing or empty elements,
@@ -35,6 +36,11 @@ type Element struct {
}
func (e *Element) Render() string {
+ var builder strings.Builder
+
+ // Start with opening tag
+ builder.WriteString("<")
+ builder.WriteString(e.Tag)
// Sort the keys for consistent order
keys := make([]string, 0, len(e.Attrs))
@@ -43,30 +49,40 @@ func (e *Element) Render() string {
}
sort.Strings(keys)
- props := ""
+ // Append the attributes to the builder
for _, k := range keys {
- props += ` ` + k + `="` + e.Attrs[k] + `"`
+ builder.WriteString(` `)
+ builder.WriteString(k)
+ builder.WriteString(`="`)
+ builder.WriteString(e.Attrs[k])
+ builder.WriteString(`"`)
+ }
+
+ // If it's a void element, close it and return
+ if _, exists := voidElements[e.Tag]; exists {
+ builder.WriteString(`>`)
+ return builder.String()
}
- content := ""
+ // Close opening tag
+ builder.WriteString(`>`)
+
+ // Build the content (either child text or nested elements)
for _, child := range e.Children {
switch c := child.(type) {
case string:
- content += c
+ builder.WriteString(c)
case *Element:
- content += c.Render()
+ builder.WriteString(c.Render())
}
}
- // Check if the tag is a void element and doesn't have any content
- if content == "" {
- _, exists := voidElements[e.Tag]
- if exists {
- return `<` + e.Tag + props + `>` // No closing tag for void elements
- }
- }
+ // Append closing tag
+ builder.WriteString(``)
+ builder.WriteString(e.Tag)
+ builder.WriteString(`>`)
- return `<` + e.Tag + props + `>` + content + `` + e.Tag + `>`
+ return builder.String()
}
func NewElement(tag string, attrs Attrs, children ...interface{}) *Element {
From b5ab6deaa00b75cc9d077bfb27d19ab584d1f822 Mon Sep 17 00:00:00 2001
From: Chase Fleming <1666730+chasefleming@users.noreply.github.com>
Date: Fri, 13 Oct 2023 08:46:37 -0700
Subject: [PATCH 4/4] Pass the builder in RenderTo
---
elem.go | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/elem.go b/elem.go
index 13d1b53..b42249b 100644
--- a/elem.go
+++ b/elem.go
@@ -35,9 +35,7 @@ type Element struct {
Children []interface{} // Can be either string (for text) or another Element
}
-func (e *Element) Render() string {
- var builder strings.Builder
-
+func (e *Element) RenderTo(builder *strings.Builder) {
// Start with opening tag
builder.WriteString("<")
builder.WriteString(e.Tag)
@@ -61,7 +59,7 @@ func (e *Element) Render() string {
// If it's a void element, close it and return
if _, exists := voidElements[e.Tag]; exists {
builder.WriteString(`>`)
- return builder.String()
+ return
}
// Close opening tag
@@ -73,7 +71,7 @@ func (e *Element) Render() string {
case string:
builder.WriteString(c)
case *Element:
- builder.WriteString(c.Render())
+ c.RenderTo(builder)
}
}
@@ -81,7 +79,11 @@ func (e *Element) Render() string {
builder.WriteString(``)
builder.WriteString(e.Tag)
builder.WriteString(`>`)
+}
+func (e *Element) Render() string {
+ var builder strings.Builder
+ e.RenderTo(&builder)
return builder.String()
}