Skip to content

Commit d9c269e

Browse files
committed
Added He.List
Now you have an easy way to generate multiple top-level tags. Usefull for generating partials.
1 parent 1b31135 commit d9c269e

File tree

9 files changed

+121
-32
lines changed

9 files changed

+121
-32
lines changed

src/Cubist.Helium.Tests/Cubist.Helium.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<Nullable>enable</Nullable>
77

88
<IsPackable>false</IsPackable>
9+
<LangVersion>11</LangVersion>
910
</PropertyGroup>
1011

1112
<ItemGroup>

src/Cubist.Helium.Tests/HeTests.cs

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,71 @@ public HeTests(ITestOutputHelper output)
1313
_output = output;
1414
}
1515

16+
[Fact]
17+
public void CanConstructList()
18+
{
19+
var list = List(P("one"), P("two"), P("three"));
20+
var html = list.ToString();
21+
_output.WriteLine(html);
22+
Assert.Equal("<p>one</p><p>two</p><p>three</p>", html);
23+
}
24+
25+
[Fact]
26+
public void CanConstructListPrettyPrinted()
27+
{
28+
var list = List(P("one"), P("two"), P("three"));
29+
var html = list.PrettyPrint();
30+
_output.WriteLine(html);
31+
Assert.Equal("""
32+
<p>one</p>
33+
<p>two</p>
34+
<p>three</p>
35+
36+
""", html);
37+
}
38+
[Fact]
39+
public void CanConstructListInDivPrettyPrinted()
40+
{
41+
var list = Div(List(P("one"), P("two"), P("three")));
42+
var html = list.PrettyPrint();
43+
_output.WriteLine(html);
44+
Assert.Equal("""
45+
<div>
46+
<p>one</p>
47+
<p>two</p>
48+
<p>three</p>
49+
</div>
50+
51+
""", html);
52+
}
53+
54+
[Fact]
55+
public void CanConstructListInSpanPrettyPrinted()
56+
{
57+
var list = Span(List(Span("one"), Span("two"), Span("three")));
58+
var html = list.PrettyPrint();
59+
_output.WriteLine(html);
60+
Assert.Equal("""
61+
<span><span>one</span><span>two</span><span>three</span></span>
62+
""", html);
63+
}
64+
65+
[Fact]
66+
public void ListIgnoresAttributes()
67+
{
68+
var list = List(("id", "test"));
69+
Assert.Equal(string.Empty, list.ToString());
70+
Assert.Equal(string.Empty, list.PrettyPrint());
71+
}
72+
73+
[Fact]
74+
public void ListIgnoresAttributesWithChildTag()
75+
{
76+
var list = List(("id", "test"), P("test"));
77+
Assert.Equal("<p>test</p>", list.ToString());
78+
Assert.Equal("<p>test</p>\r\n", list.PrettyPrint());
79+
}
80+
1681
[Fact]
1782
public void CanConstructMetaElement()
1883
{
@@ -21,7 +86,6 @@ public void CanConstructMetaElement()
2186
var html = meta.ToString();
2287
_output.WriteLine(html);
2388
Assert.Equal("<meta name=\"keywords\" content=\"test, library\">", html);
24-
2589
}
2690

2791
[Fact]

src/Cubist.Helium/Cubist.Helium.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<EnablePackageValidation>true</EnablePackageValidation>
2525

2626
<DebugType>portable</DebugType>
27+
<LangVersion>11</LangVersion>
2728
</PropertyGroup>
2829

2930
<PropertyGroup>

src/Cubist.Helium/He.Factory.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,4 +575,7 @@ public static He Time(DateTime datetime, params object[] content) =>
575575

576576
/// <inheritdoc cref="Tags.Wbr"/>
577577
public static He Wbr() => new(Tags.Wbr);
578+
579+
/// <summary> A container for nodes, only prints the contained nodes, ignores any added attributes. </summary>
580+
public static He List(params object[] content) => new(Tag.Empty) { content };
578581
}

src/Cubist.Helium/He.cs

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,10 @@ public He CAttr(bool condition, string name, object? value = null)
7979
return this;
8080
}
8181

82-
/// <summary> returns this element's attributes </summary>
82+
/// <summary> Returns the number of attributes on this element. </summary>
83+
internal int AttrCount => _attrs?.Count ?? 0;
84+
85+
/// <summary> Returns this element's attributes </summary>
8386
public IEnumerable<(string, object?)> Attrs()
8487
{
8588
if (_attrs == null) yield break;
@@ -90,27 +93,15 @@ public He CAttr(bool condition, string name, object? value = null)
9093
/// <inheritdoc cref="Node.WriteTo"/>
9194
public override void WriteTo(TextWriter w)
9295
{
93-
if (_attrs == null || _attrs.Count == 0)
94-
{
95-
Tag.WriteStart(w);
96-
}
97-
else
98-
{
99-
Tag.WriteStartBegin(w);
100-
foreach (var attr in _attrs)
101-
attr.WriteTo(w);
102-
103-
Tag.WriteStartEnd(w);
104-
}
96+
this.WriteStartTag(w);
10597

10698
if (_nodes != null)
10799
{
108100
foreach (var node in _nodes)
109101
node.WriteTo(w);
110102
}
111103

112-
if (!Tag.IsVoid())
113-
Tag.WriteClose(w);
104+
this.WriteCloseTag(w);
114105
}
115106

116107
/// <inheritdoc cref="Node.PrettyPrintTo"/>>
@@ -121,23 +112,24 @@ public override void PrettyPrintTo(IndentWriter w)
121112
this.WriteStartTag(w);
122113

123114
var allChildrenInline = this.All(n => n.IsInline());
124-
using (w.Indent())
125-
{
126-
if (indent && !allChildrenInline)
127-
w.WriteLine();
128115

129-
foreach (var child in this)
130-
child.PrettyPrintTo(w);
116+
bool isEmptyTag = Tag == Tag.Empty;
117+
var indentBlock = isEmptyTag ? IndentWriter.IndentBlock.Empty : w.Indent();
131118

132-
if (indent && !allChildrenInline && this.Count > 0 && this.Last().IsInline())
133-
w.WriteLine();
134-
}
119+
if (indent && !allChildrenInline && !isEmptyTag)
120+
w.WriteLine();
121+
122+
foreach (var child in this)
123+
child.PrettyPrintTo(w);
124+
125+
if (indent && !allChildrenInline && this.Count > 0 && this.Last().IsInline())
126+
w.WriteLine();
135127

128+
indentBlock.Dispose();
136129

137-
if (!Tag.IsVoid())
138-
this.WriteCloseTag(w);
130+
this.WriteCloseTag(w);
139131

140-
if (indent)
132+
if (indent && !isEmptyTag)
141133
w.WriteLine();
142134
}
143135

src/Cubist.Helium/IndentWriter.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,18 @@ private void WriteIndent()
9595
/// <summary> Represent a indentation level </summary>
9696
public struct IndentBlock : IDisposable
9797
{
98+
99+
/// <summary> An empty <see cref="IndentBlock"/> value</summary>
100+
public static IndentBlock Empty { get; } = new();
101+
98102
private IndentWriter? _w;
99103

104+
/// <summary> Creates an empty <see cref="IndentBlock"/> value</summary>
105+
public IndentBlock()
106+
{
107+
_w = null;
108+
}
109+
100110
/// <summary> Creates a <see cref="IndentBlock"/> value</summary>
101111
public IndentBlock(IndentWriter w)
102112
{

src/Cubist.Helium/NodeExtensions.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,22 @@ public static void Apply(this Node start, Func<Node, bool> match, Action<Node> a
4949
/// <summary> Writes the node start tag </summary>
5050
public static void WriteStartTag(this He he, TextWriter w)
5151
{
52+
if (he.Tag == Tag.Empty) return;
53+
5254
he.Tag.WriteStartBegin(w);
53-
foreach (var attr in he.Attrs())
54-
attr.WriteTo(w);
55+
if (he.AttrCount > 0)
56+
{
57+
foreach (var attr in he.Attrs())
58+
attr.WriteTo(w);
59+
}
5560
he.Tag.WriteStartEnd(w);
5661
}
5762

5863
/// <summary> Writes the node closing tag if it needs one.</summary>
5964
public static void WriteCloseTag(this He he, TextWriter w)
6065
{
66+
if (he.Tag == Tag.Empty) return;
67+
6168
he.Tag.WriteClose(w);
6269
}
6370
}

src/Cubist.Helium/PrettyPrintExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public static string PrettyPrint(this Node n)
1818
public static bool IsInline(this Node n)
1919
=> n is CData ||
2020
(n is Text t && !t.Value.Contains('\n')) ||
21-
(n is He he && he.Tag.IsInline() &&
21+
(n is He he && (he.Tag.IsInline() || he.Tag==Tag.Empty) &&
2222
he.All(child => child.IsInline()));
2323

2424
/// <summary>

src/Cubist.Helium/Tag.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,28 @@ namespace Cubist.Helium;
55
/// <summary> Represents a html tag name with some extra information. </summary>
66
public readonly record struct Tag(string Value, TagOptions Options)
77
{
8+
/// <summary> The empty tag that will not render </summary>
9+
public static Tag Empty => new(string.Empty, TagOptions.None);
10+
811
/// <summary> implicit conversion from a string to a tag, adds TagOptions </summary>
912
public static implicit operator Tag(string s) => new(s, GetOption(s));
1013

1114
/// <summary> Writes the start tag. <c>&lt;{tag}&gt;</c> </summary>
1215
public void WriteStart(TextWriter w)
1316
{
17+
if (string.IsNullOrEmpty(Value))
18+
return;
19+
1420
WriteStartBegin(w);
1521
WriteStartEnd(w);
1622
}
1723

1824
/// <summary> Writes the start tag begin <c>&lt;{tag}</c> so that attributes can be written. </summary>
1925
public void WriteStartBegin(TextWriter w)
2026
{
27+
if (string.IsNullOrEmpty(Value))
28+
return;
29+
2130
w.Write('<');
2231
WriteTag(w);
2332
}
@@ -33,12 +42,14 @@ private void WriteTag(TextWriter w)
3342
/// <summary> Writes the start tag's closing <c>&gt;</c> character. </summary>
3443
public void WriteStartEnd(TextWriter w)
3544
{
45+
if (string.IsNullOrEmpty(Value)) return;
3646
w.Write(">");
3747
}
3848

3949
/// <summary> Writes the close tag <c>&lt;/{tag}&gt;</c>. </summary>
4050
public void WriteClose(TextWriter w)
4151
{
52+
if (string.IsNullOrEmpty(Value)) return;
4253
if (this.IsVoid()) return;
4354
w.Write("</");
4455
WriteTag(w);
@@ -78,7 +89,7 @@ public void WriteClose(TextWriter w)
7889
{
7990
"h1","h2","h3","h4","h5","h6"
8091
};
81-
92+
8293
/// <summary> See https://html.spec.whatwg.org/multipage/dom.html#phrasing-content </summary>
8394
private static readonly IReadOnlySet<string> _phrasingContent = new HashSet<string>
8495
{

0 commit comments

Comments
 (0)