Force newlines to be explicit

Since there weren't consistent rules for when to include newlines, I
decided to make an explicit tag (similar to HTML's <br /> tag). This can
have a dramatic impact on your existing code (unless it is just HTML).
This commit is contained in:
Travis Parks 2013-07-23 08:44:48 -04:00
parent 65b79062d6
commit e2fd882ef1
9 changed files with 91 additions and 238 deletions

View File

@ -60,11 +60,14 @@ namespace Mustache.Test
public void TestCompile_OutputNewLineBlank_PrintsBothLines() public void TestCompile_OutputNewLineBlank_PrintsBothLines()
{ {
FormatCompiler compiler = new FormatCompiler(); FormatCompiler compiler = new FormatCompiler();
const string format = @"Hello const string format = @"Hello{{#newline}}
";
const string expected = @"Hello
"; ";
Generator generator = compiler.Compile(format); Generator generator = compiler.Compile(format);
string result = generator.Render(null); string result = generator.Render(null);
Assert.AreEqual(format, result, "The wrong text was generated."); Assert.AreEqual(expected, result, "The wrong text was generated.");
} }
#endregion #endregion
@ -268,7 +271,7 @@ namespace Mustache.Test
public void TestCompile_OutputNewLineOutput_PrintsBothLines() public void TestCompile_OutputNewLineOutput_PrintsBothLines()
{ {
FormatCompiler compiler = new FormatCompiler(); FormatCompiler compiler = new FormatCompiler();
const string format = @"{{this}} const string format = @"{{this}}{{#newline}}
After"; After";
Generator generator = compiler.Compile(format); Generator generator = compiler.Compile(format);
string result = generator.Render("Content"); string result = generator.Render("Content");
@ -285,7 +288,7 @@ After";
public void TestCompile_EmptyNewLineKey_PrintsBothLines() public void TestCompile_EmptyNewLineKey_PrintsBothLines()
{ {
FormatCompiler compiler = new FormatCompiler(); FormatCompiler compiler = new FormatCompiler();
const string format = @" const string format = @"{{#newline}}
{{this}}"; {{this}}";
Generator generator = compiler.Compile(format); Generator generator = compiler.Compile(format);
string result = generator.Render("Content"); string result = generator.Render("Content");
@ -318,7 +321,7 @@ Content";
public void TestCompile_KeyKey_PrintsBothLines() public void TestCompile_KeyKey_PrintsBothLines()
{ {
FormatCompiler compiler = new FormatCompiler(); FormatCompiler compiler = new FormatCompiler();
const string format = @"{{this}} const string format = @"{{this}}{{#newline}}
{{this}}"; {{this}}";
Generator generator = compiler.Compile(format); Generator generator = compiler.Compile(format);
string result = generator.Render("Content"); string result = generator.Render("Content");
@ -432,7 +435,7 @@ Content";
const string format = "{{#! comment }} {{#! comment }}"; const string format = "{{#! comment }} {{#! comment }}";
Generator generator = compiler.Compile(format); Generator generator = compiler.Compile(format);
string result = generator.Render(new object()); string result = generator.Render(new object());
Assert.AreEqual(String.Empty, result, "The wrong text was generated."); Assert.AreEqual(" ", result, "The wrong text was generated.");
} }
/// <summary> /// <summary>
@ -465,12 +468,12 @@ Content";
/// If a comment makes up the entire format string, the nothing should be printed out. /// If a comment makes up the entire format string, the nothing should be printed out.
/// </summary> /// </summary>
[TestMethod] [TestMethod]
public void TestCompile_CommentAloneOnlyLine__PrintsEmpty() public void TestCompile_CommentAloneOnlyLine_PrintsSurroundingSpace()
{ {
FormatCompiler compiler = new FormatCompiler(); FormatCompiler compiler = new FormatCompiler();
Generator generator = compiler.Compile(" {{#! comment }} "); Generator generator = compiler.Compile(" {{#! comment }} ");
string result = generator.Render(null); string result = generator.Render(null);
Assert.AreEqual(String.Empty, result, "The wrong text was generated."); Assert.AreEqual(" ", result, "The wrong text was generated.");
} }
/// <summary> /// <summary>
@ -486,8 +489,7 @@ Content";
After"; After";
Generator generator = compiler.Compile(format); Generator generator = compiler.Compile(format);
string result = generator.Render(new object()); string result = generator.Render(new object());
const string expected = @"Before const string expected = @"Before After";
After";
Assert.AreEqual(expected, result, "The wrong text was generated."); Assert.AreEqual(expected, result, "The wrong text was generated.");
} }
@ -504,8 +506,7 @@ After";
After"; After";
Generator generator = compiler.Compile(format); Generator generator = compiler.Compile(format);
string result = generator.Render(new object()); string result = generator.Render(new object());
const string expected = @"Before const string expected = @"Before After";
After";
Assert.AreEqual(expected, result, "The wrong text was generated."); Assert.AreEqual(expected, result, "The wrong text was generated.");
} }
@ -520,14 +521,13 @@ After";
const string format = @"Before const string format = @"Before
{{#! This is a comment }} {{#! This is a comment }}
{{#! This is another comment }} {{#! This is another comment }}
{{#newline}}
{{#! This is the final comment }} {{#! This is the final comment }}
After"; After";
Generator generator = compiler.Compile(format); Generator generator = compiler.Compile(format);
string result = generator.Render(new object()); string result = generator.Render(new object());
const string expected = @"Before const string expected = @"Before
After";
After";
Assert.AreEqual(expected, result, "The wrong text was generated."); Assert.AreEqual(expected, result, "The wrong text was generated.");
} }
@ -543,9 +543,7 @@ After";
After"; After";
Generator generator = compiler.Compile(format); Generator generator = compiler.Compile(format);
string result = generator.Render(new object()); string result = generator.Render(new object());
const string expected = @"Before const string expected = @"Before ExtraAfter";
Extra
After";
Assert.AreEqual(expected, result, "The wrong text was generated."); Assert.AreEqual(expected, result, "The wrong text was generated.");
} }
@ -561,7 +559,7 @@ After";
"; ";
Generator generator = compiler.Compile(format); Generator generator = compiler.Compile(format);
string result = generator.Render(null); string result = generator.Render(null);
Assert.AreEqual(" ", result, "The wrong text was generated."); Assert.AreEqual(" ", result, "The wrong text was generated.");
} }
/// <summary> /// <summary>
@ -576,7 +574,7 @@ After";
After"; After";
Generator generator = compiler.Compile(format); Generator generator = compiler.Compile(format);
string result = generator.Render(null); string result = generator.Render(null);
Assert.AreEqual("After", result, "The wrong text was generated."); Assert.AreEqual(" After", result, "The wrong text was generated.");
} }
/// <summary> /// <summary>
@ -618,8 +616,8 @@ First
public void TestCompile_ContentNewLineCommentNewLineContentNewLineComment_PrintsContent() public void TestCompile_ContentNewLineCommentNewLineContentNewLineComment_PrintsContent()
{ {
FormatCompiler compiler = new FormatCompiler(); FormatCompiler compiler = new FormatCompiler();
const string format = @"First const string format = @"First{{#newline}}
{{#! comment }} {{#! comment }}
Middle Middle
{{#! comment }}"; {{#! comment }}";
Generator generator = compiler.Compile(format); Generator generator = compiler.Compile(format);
@ -795,6 +793,7 @@ Content{{/if}}";
const string format = @"{{#if this}} const string format = @"{{#if this}}
First First
{{/if}}{{#if this}} {{/if}}{{#if this}}
{{#newline}}
Last Last
{{/if}}"; {{/if}}";
Generator generator = parser.Compile(format); Generator generator = parser.Compile(format);
@ -832,6 +831,7 @@ Content
const string format = @"{{#if this}} const string format = @"{{#if this}}
{{/if}} {{/if}}
First First
{{#newline}}
{{#if this}} {{#if this}}
{{/if}} {{/if}}
Last"; Last";
@ -1005,10 +1005,11 @@ Last";
} }
/// <summary> /// <summary>
/// /// A bug was found where the index tag was trying to read the arguments for the next tag.
/// This was caused by the index tag chewing up more of the input than it was supposed to.
/// </summary> /// </summary>
[TestMethod] [TestMethod]
public void TestCompile_Each_LoopOverCollectionTwice() public void TestCompile_Each_ContextAfterIndexTag()
{ {
List<TestObject> objects = new List<TestObject>(); List<TestObject> objects = new List<TestObject>();
objects.Add(new TestObject { Name = "name1", Val = "val1" }); objects.Add(new TestObject { Name = "name1", Val = "val1" });
@ -1016,11 +1017,10 @@ Last";
objects.Add(new TestObject { Name = "name3", Val = "val3" }); objects.Add(new TestObject { Name = "name3", Val = "val3" });
const string template = @"{{#each this}} const string template = @"{{#each this}}
Item Number: {{#index}}<br /> Item Number: {{#index}}<br />{{#newline}}
{{/each}} {{/each}}
{{#each this}} {{#each this}}
Item Number: foo<br /> Item Number: foo<br />{{#newline}}
{{/each}}"; {{/each}}";
FormatCompiler compiler = new FormatCompiler(); FormatCompiler compiler = new FormatCompiler();
@ -1111,15 +1111,17 @@ Item Number: foo<br />
{ {
FormatCompiler compiler = new FormatCompiler(); FormatCompiler compiler = new FormatCompiler();
const string format = @"Hello {{Customer.FirstName}}: const string format = @"Hello {{Customer.FirstName}}:
{{#newline}}
{{#newline}}
{{#with Order}} {{#with Order}}
{{#if LineItems}} {{#if LineItems}}
Below are your order details: Below are your order details:
{{#newline}}
{{#newline}}
{{#each LineItems}} {{#each LineItems}}
{{Name}}: {{UnitPrice:C}} x {{Quantity}} {{Name}}: {{UnitPrice:C}} x {{Quantity}}{{#newline}}
{{/each}} {{/each}}
{{#newline}}
Your order total was: {{Total:C}} Your order total was: {{Total:C}}
{{/if}} {{/if}}
{{/with}}"; {{/with}}";

View File

@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// //
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
[assembly: AssemblyVersion("0.1.3.0")] [assembly: AssemblyVersion("0.2.0.0")]
[assembly: AssemblyFileVersion("0.1.3.0")] [assembly: AssemblyFileVersion("0.2.0.0")]

View File

@ -11,7 +11,7 @@ namespace Mustache
{ {
private readonly TagDefinition _definition; private readonly TagDefinition _definition;
private readonly ArgumentCollection _arguments; private readonly ArgumentCollection _arguments;
private readonly LinkedList<IGenerator> _primaryGenerators; private readonly List<IGenerator> _primaryGenerators;
private IGenerator _subGenerator; private IGenerator _subGenerator;
/// <summary> /// <summary>
@ -23,7 +23,7 @@ namespace Mustache
{ {
_definition = definition; _definition = definition;
_arguments = arguments; _arguments = arguments;
_primaryGenerators = new LinkedList<IGenerator>(); _primaryGenerators = new List<IGenerator>();
} }
/// <summary> /// <summary>
@ -47,19 +47,6 @@ namespace Mustache
addGenerator(generator, isSubGenerator); addGenerator(generator, isSubGenerator);
} }
/// <summary>
/// Creates a StaticGenerator from the given value and adds it.
/// </summary>
/// <param name="generators">The static generators to add.</param>
public void AddStaticGenerators(IEnumerable<StaticGenerator> generators)
{
foreach (StaticGenerator generator in generators)
{
LinkedListNode<IGenerator> node = _primaryGenerators.AddLast(generator);
generator.Node = node;
}
}
private void addGenerator(IGenerator generator, bool isSubGenerator) private void addGenerator(IGenerator generator, bool isSubGenerator)
{ {
if (isSubGenerator) if (isSubGenerator)
@ -68,7 +55,7 @@ namespace Mustache
} }
else else
{ {
_primaryGenerators.AddLast(generator); _primaryGenerators.Add(generator);
} }
} }
@ -76,17 +63,17 @@ namespace Mustache
{ {
Dictionary<string, object> arguments = _arguments.GetArguments(scope); Dictionary<string, object> arguments = _arguments.GetArguments(scope);
IEnumerable<NestedContext> contexts = _definition.GetChildContext(writer, scope, arguments); IEnumerable<NestedContext> contexts = _definition.GetChildContext(writer, scope, arguments);
LinkedList<IGenerator> generators; List<IGenerator> generators;
if (_definition.ShouldGeneratePrimaryGroup(arguments)) if (_definition.ShouldGeneratePrimaryGroup(arguments))
{ {
generators = _primaryGenerators; generators = _primaryGenerators;
} }
else else
{ {
generators = new LinkedList<IGenerator>(); generators = new List<IGenerator>();
if (_subGenerator != null) if (_subGenerator != null)
{ {
generators.AddLast(_subGenerator); generators.Add(_subGenerator);
} }
} }
foreach (NestedContext context in contexts) foreach (NestedContext context in contexts)

View File

@ -37,6 +37,8 @@ namespace Mustache
_tagLookup.Add(indexDefinition.Name, indexDefinition); _tagLookup.Add(indexDefinition.Name, indexDefinition);
WithTagDefinition withDefinition = new WithTagDefinition(); WithTagDefinition withDefinition = new WithTagDefinition();
_tagLookup.Add(withDefinition.Name, withDefinition); _tagLookup.Add(withDefinition.Name, withDefinition);
NewlineTagDefinition newlineDefinition = new NewlineTagDefinition();
_tagLookup.Add(newlineDefinition.Name, newlineDefinition);
} }
/// <summary> /// <summary>
@ -75,12 +77,10 @@ namespace Mustache
throw new ArgumentNullException("format"); throw new ArgumentNullException("format");
} }
CompoundGenerator generator = new CompoundGenerator(_masterDefinition, new ArgumentCollection()); CompoundGenerator generator = new CompoundGenerator(_masterDefinition, new ArgumentCollection());
Trimmer trimmer = new Trimmer();
List<Context> context = new List<Context>() { new Context(_masterDefinition.Name, new ContextParameter[0]) }; List<Context> context = new List<Context>() { new Context(_masterDefinition.Name, new ContextParameter[0]) };
int formatIndex = buildCompoundGenerator(_masterDefinition, context, generator, trimmer, format, 0); int formatIndex = buildCompoundGenerator(_masterDefinition, context, generator, format, 0);
string trailing = format.Substring(formatIndex); string trailing = format.Substring(formatIndex);
generator.AddStaticGenerators(trimmer.RecordText(trailing, false, false)); generator.AddGenerator(new StaticGenerator(trailing));
trimmer.Trim();
return new Generator(generator); return new Generator(generator);
} }
@ -171,7 +171,6 @@ namespace Mustache
TagDefinition tagDefinition, TagDefinition tagDefinition,
List<Context> context, List<Context> context,
CompoundGenerator generator, CompoundGenerator generator,
Trimmer trimmer,
string format, int formatIndex) string format, int formatIndex)
{ {
while (true) while (true)
@ -192,7 +191,7 @@ namespace Mustache
if (match.Groups["key"].Success) if (match.Groups["key"].Success)
{ {
generator.AddStaticGenerators(trimmer.RecordText(leading, true, true)); generator.AddGenerator(new StaticGenerator(leading));
formatIndex = match.Index + match.Length; formatIndex = match.Index + match.Length;
string key = match.Groups["key"].Value; string key = match.Groups["key"].Value;
string alignment = match.Groups["alignment"].Value; string alignment = match.Groups["alignment"].Value;
@ -217,7 +216,7 @@ namespace Mustache
} }
if (nextDefinition.HasContent) if (nextDefinition.HasContent)
{ {
generator.AddStaticGenerators(trimmer.RecordText(leading, true, false)); generator.AddGenerator(new StaticGenerator(leading));
ArgumentCollection arguments = getArguments(nextDefinition, match); ArgumentCollection arguments = getArguments(nextDefinition, match);
CompoundGenerator compoundGenerator = new CompoundGenerator(nextDefinition, arguments); CompoundGenerator compoundGenerator = new CompoundGenerator(nextDefinition, arguments);
IEnumerable<TagParameter> contextParameters = nextDefinition.GetChildContextParameters(); IEnumerable<TagParameter> contextParameters = nextDefinition.GetChildContextParameters();
@ -227,7 +226,7 @@ namespace Mustache
ContextParameter[] parameters = contextParameters.Select(p => new ContextParameter(p.Name, arguments.GetKey(p))).ToArray(); ContextParameter[] parameters = contextParameters.Select(p => new ContextParameter(p.Name, arguments.GetKey(p))).ToArray();
context.Add(new Context(nextDefinition.Name, parameters)); context.Add(new Context(nextDefinition.Name, parameters));
} }
formatIndex = buildCompoundGenerator(nextDefinition, context, compoundGenerator, trimmer, format, formatIndex); formatIndex = buildCompoundGenerator(nextDefinition, context, compoundGenerator, format, formatIndex);
generator.AddGenerator(nextDefinition, compoundGenerator); generator.AddGenerator(nextDefinition, compoundGenerator);
if (hasContext) if (hasContext)
{ {
@ -236,7 +235,7 @@ namespace Mustache
} }
else else
{ {
generator.AddStaticGenerators(trimmer.RecordText(leading, true, true)); generator.AddGenerator(new StaticGenerator(leading));
ArgumentCollection arguments = getArguments(nextDefinition, match); ArgumentCollection arguments = getArguments(nextDefinition, match);
InlineGenerator inlineGenerator = new InlineGenerator(nextDefinition, arguments); InlineGenerator inlineGenerator = new InlineGenerator(nextDefinition, arguments);
generator.AddGenerator(inlineGenerator); generator.AddGenerator(inlineGenerator);
@ -244,7 +243,7 @@ namespace Mustache
} }
else if (match.Groups["close"].Success) else if (match.Groups["close"].Success)
{ {
generator.AddStaticGenerators(trimmer.RecordText(leading, true, false)); generator.AddGenerator(new StaticGenerator(leading));
string tagName = match.Groups["name"].Value; string tagName = match.Groups["name"].Value;
TagDefinition nextDefinition = _tagLookup[tagName]; TagDefinition nextDefinition = _tagLookup[tagName];
formatIndex = match.Index; formatIndex = match.Index;
@ -256,7 +255,7 @@ namespace Mustache
} }
else if (match.Groups["comment"].Success) else if (match.Groups["comment"].Success)
{ {
generator.AddStaticGenerators(trimmer.RecordText(leading, true, false)); generator.AddGenerator(new StaticGenerator(leading));
formatIndex = match.Index + match.Length; formatIndex = match.Index + match.Length;
} }
else if (match.Groups["unknown"].Success) else if (match.Groups["unknown"].Success)

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace Mustache
{
/// <summary>
/// Defines a tag that outputs a newline.
/// </summary>
internal sealed class NewlineTagDefinition : InlineTagDefinition
{
/// <summary>
/// Initializes a new instance of an NewlineTagDefinition.
/// </summary>
public NewlineTagDefinition()
: base("newline")
{
}
/// <summary>
/// Gets the text to output.
/// </summary>
/// <param name="writer">The writer to write the output to.</param>
/// <param name="arguments">The arguments passed to the tag.</param>
/// <param name="contextData">Extra data passed along with the context.</param>
public override void GetText(TextWriter writer, Dictionary<string, object> arguments, object contextData)
{
writer.Write(Environment.NewLine);
}
}
}

View File

@ -34,6 +34,6 @@ using System.Runtime.CompilerServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.1.3.0")] [assembly: AssemblyVersion("0.2.0.0")]
[assembly: AssemblyFileVersion("0.1.3.0")] [assembly: AssemblyFileVersion("0.2.0.0")]
[assembly: InternalsVisibleTo("mustache-sharp.test")] [assembly: InternalsVisibleTo("mustache-sharp.test")]

View File

@ -9,20 +9,14 @@ namespace Mustache
/// </summary> /// </summary>
internal sealed class StaticGenerator : IGenerator internal sealed class StaticGenerator : IGenerator
{ {
private readonly string value;
/// <summary> /// <summary>
/// Initializes a new instance of a StaticGenerator. /// Initializes a new instance of a StaticGenerator.
/// </summary> /// </summary>
public StaticGenerator() public StaticGenerator(string value)
{ {
} this.value = value.Replace(Environment.NewLine, String.Empty);
/// <summary>
/// Gets or sets the linked list node containing the current generator.
/// </summary>
public LinkedListNode<IGenerator> Node
{
get;
set;
} }
/// <summary> /// <summary>
@ -30,20 +24,7 @@ namespace Mustache
/// </summary> /// </summary>
public string Value public string Value
{ {
get; get { return value; }
set;
}
/// <summary>
/// Removes the static text from the final output.
/// </summary>
public void Prune()
{
if (Node != null)
{
Node.List.Remove(Node);
Node = null;
}
} }
void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData) void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData)

View File

@ -1,147 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Mustache
{
/// <summary>
/// Removes unnecessary lines from the final output.
/// </summary>
internal sealed class Trimmer
{
private readonly LinkedList<LineDetails> _lines;
private LinkedListNode<LineDetails> _currentLine;
/// <summary>
/// Initializes a new instance of a Trimmer.
/// </summary>
public Trimmer()
{
_lines = new LinkedList<LineDetails>();
_currentLine = _lines.AddLast(new LineDetails());
}
/// <summary>
/// Updates the state of the trimmer, indicating that the given text was encountered before an inline tag.
/// </summary>
/// <param name="value">The text at the end of the format string.</param>
/// <param name="generator">The generator created for the inline tag.</param>
/// <returns>A static generator containing the passed text.</returns>
public IEnumerable<StaticGenerator> RecordText(string value, bool isTag, bool isOutput)
{
int newLineIndex = value.IndexOf(Environment.NewLine);
if (newLineIndex == -1)
{
StaticGenerator generator = new StaticGenerator() { Value = value };
_currentLine.Value.Generators.Add(generator);
_currentLine.Value.HasTag |= isTag;
_currentLine.Value.HasOutput |= !String.IsNullOrWhiteSpace(value);
yield return generator;
}
else
{
string[] lines = value.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
// get the trailing generator
string trailing = lines[0];
StaticGenerator trailingGenerator = new StaticGenerator() { Value = trailing };
_currentLine.Value.Generators.Add(trailingGenerator);
_currentLine.Value.HasOutput |= !String.IsNullOrWhiteSpace(trailing);
yield return trailingGenerator;
// get the middle generators
for (int lineIndex = 1; lineIndex < lines.Length - 1; ++lineIndex)
{
string middle = lines[lineIndex];
StaticGenerator middleGenerator = new StaticGenerator() { Value = middle };
LineDetails middleDetails = new LineDetails() { HasTag = false };
_currentLine = _lines.AddLast(middleDetails);
_currentLine.Value.Generators.Add(middleGenerator);
_currentLine.Value.HasOutput = true;
yield return middleGenerator;
}
// get the leading generator
string leading = lines[lines.Length - 1];
StaticGenerator leadingGenerator = new StaticGenerator() { Value = leading };
LineDetails details = new LineDetails() { HasTag = isTag };
_currentLine = _lines.AddLast(details);
_currentLine.Value.Generators.Add(leadingGenerator);
_currentLine.Value.HasOutput = !String.IsNullOrWhiteSpace(leading);
yield return leadingGenerator;
}
if (isOutput)
{
_currentLine.Value.HasOutput = true;
}
}
public void Trim()
{
removeBlankLines();
separateLines();
removeEmptyGenerators();
}
private void removeBlankLines()
{
LinkedListNode<LineDetails> current = _lines.First;
while (current != null)
{
LineDetails details = current.Value;
LinkedListNode<LineDetails> temp = current;
current = current.Next;
if (details.HasTag && !details.HasOutput)
{
foreach (StaticGenerator generator in temp.Value.Generators)
{
generator.Prune();
}
temp.List.Remove(temp);
}
}
}
private void separateLines()
{
LinkedListNode<LineDetails> current = _lines.First;
while (current != _lines.Last)
{
List<StaticGenerator> generators = current.Value.Generators;
StaticGenerator lastGenerator = generators[generators.Count - 1];
lastGenerator.Value += Environment.NewLine;
current = current.Next;
}
}
private void removeEmptyGenerators()
{
LinkedListNode<LineDetails> current = _lines.First;
while (current != null)
{
foreach (StaticGenerator generator in current.Value.Generators)
{
if (generator.Value.Length == 0)
{
generator.Prune();
}
}
current = current.Next;
}
}
private sealed class LineDetails
{
public LineDetails()
{
Generators = new List<StaticGenerator>();
}
public bool HasTag { get; set; }
public List<StaticGenerator> Generators { get; set; }
public bool HasOutput { get; set; }
}
}
}

View File

@ -40,6 +40,7 @@
<Compile Include="ContentTagDefinition.cs" /> <Compile Include="ContentTagDefinition.cs" />
<Compile Include="Context.cs" /> <Compile Include="Context.cs" />
<Compile Include="ContextParameter.cs" /> <Compile Include="ContextParameter.cs" />
<Compile Include="NewlineTagDefinition.cs" />
<Compile Include="IndexTagDefinition.cs" /> <Compile Include="IndexTagDefinition.cs" />
<Compile Include="KeyFoundEventArgs.cs" /> <Compile Include="KeyFoundEventArgs.cs" />
<Compile Include="InlineTagDefinition.cs" /> <Compile Include="InlineTagDefinition.cs" />
@ -68,7 +69,6 @@
<Compile Include="TagDefinition.cs" /> <Compile Include="TagDefinition.cs" />
<Compile Include="TagParameter.cs" /> <Compile Include="TagParameter.cs" />
<Compile Include="KeyScope.cs" /> <Compile Include="KeyScope.cs" />
<Compile Include="Trimmer.cs" />
<Compile Include="WithGenerator.cs" /> <Compile Include="WithGenerator.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>