diff --git a/mustache-sharp.test/FormatCompilerTester.cs b/mustache-sharp.test/FormatCompilerTester.cs
index f11dd1f..694bb66 100644
--- a/mustache-sharp.test/FormatCompilerTester.cs
+++ b/mustache-sharp.test/FormatCompilerTester.cs
@@ -60,11 +60,14 @@ namespace Mustache.Test
public void TestCompile_OutputNewLineBlank_PrintsBothLines()
{
FormatCompiler compiler = new FormatCompiler();
- const string format = @"Hello
+ const string format = @"Hello{{#newline}}
+ ";
+
+ const string expected = @"Hello
";
Generator generator = compiler.Compile(format);
string result = generator.Render(null);
- Assert.AreEqual(format, result, "The wrong text was generated.");
+ Assert.AreEqual(expected, result, "The wrong text was generated.");
}
#endregion
@@ -268,7 +271,7 @@ namespace Mustache.Test
public void TestCompile_OutputNewLineOutput_PrintsBothLines()
{
FormatCompiler compiler = new FormatCompiler();
- const string format = @"{{this}}
+ const string format = @"{{this}}{{#newline}}
After";
Generator generator = compiler.Compile(format);
string result = generator.Render("Content");
@@ -285,7 +288,7 @@ After";
public void TestCompile_EmptyNewLineKey_PrintsBothLines()
{
FormatCompiler compiler = new FormatCompiler();
- const string format = @"
+ const string format = @"{{#newline}}
{{this}}";
Generator generator = compiler.Compile(format);
string result = generator.Render("Content");
@@ -318,7 +321,7 @@ Content";
public void TestCompile_KeyKey_PrintsBothLines()
{
FormatCompiler compiler = new FormatCompiler();
- const string format = @"{{this}}
+ const string format = @"{{this}}{{#newline}}
{{this}}";
Generator generator = compiler.Compile(format);
string result = generator.Render("Content");
@@ -432,7 +435,7 @@ Content";
const string format = "{{#! comment }} {{#! comment }}";
Generator generator = compiler.Compile(format);
string result = generator.Render(new object());
- Assert.AreEqual(String.Empty, result, "The wrong text was generated.");
+ Assert.AreEqual(" ", result, "The wrong text was generated.");
}
///
@@ -465,12 +468,12 @@ Content";
/// If a comment makes up the entire format string, the nothing should be printed out.
///
[TestMethod]
- public void TestCompile_CommentAloneOnlyLine__PrintsEmpty()
+ public void TestCompile_CommentAloneOnlyLine_PrintsSurroundingSpace()
{
FormatCompiler compiler = new FormatCompiler();
Generator generator = compiler.Compile(" {{#! comment }} ");
string result = generator.Render(null);
- Assert.AreEqual(String.Empty, result, "The wrong text was generated.");
+ Assert.AreEqual(" ", result, "The wrong text was generated.");
}
///
@@ -486,8 +489,7 @@ Content";
After";
Generator generator = compiler.Compile(format);
string result = generator.Render(new object());
- const string expected = @"Before
-After";
+ const string expected = @"Before After";
Assert.AreEqual(expected, result, "The wrong text was generated.");
}
@@ -504,8 +506,7 @@ After";
After";
Generator generator = compiler.Compile(format);
string result = generator.Render(new object());
- const string expected = @"Before
-After";
+ const string expected = @"Before After";
Assert.AreEqual(expected, result, "The wrong text was generated.");
}
@@ -520,14 +521,13 @@ After";
const string format = @"Before
{{#! This is a comment }}
{{#! This is another comment }}
-
+ {{#newline}}
{{#! This is the final comment }}
After";
Generator generator = compiler.Compile(format);
string result = generator.Render(new object());
- const string expected = @"Before
-
-After";
+ const string expected = @"Before
+ After";
Assert.AreEqual(expected, result, "The wrong text was generated.");
}
@@ -543,9 +543,7 @@ After";
After";
Generator generator = compiler.Compile(format);
string result = generator.Render(new object());
- const string expected = @"Before
- Extra
-After";
+ const string expected = @"Before ExtraAfter";
Assert.AreEqual(expected, result, "The wrong text was generated.");
}
@@ -561,7 +559,7 @@ After";
";
Generator generator = compiler.Compile(format);
string result = generator.Render(null);
- Assert.AreEqual(" ", result, "The wrong text was generated.");
+ Assert.AreEqual(" ", result, "The wrong text was generated.");
}
///
@@ -576,7 +574,7 @@ After";
After";
Generator generator = compiler.Compile(format);
string result = generator.Render(null);
- Assert.AreEqual("After", result, "The wrong text was generated.");
+ Assert.AreEqual(" After", result, "The wrong text was generated.");
}
///
@@ -618,8 +616,8 @@ First
public void TestCompile_ContentNewLineCommentNewLineContentNewLineComment_PrintsContent()
{
FormatCompiler compiler = new FormatCompiler();
- const string format = @"First
- {{#! comment }}
+ const string format = @"First{{#newline}}
+{{#! comment }}
Middle
{{#! comment }}";
Generator generator = compiler.Compile(format);
@@ -795,6 +793,7 @@ Content{{/if}}";
const string format = @"{{#if this}}
First
{{/if}}{{#if this}}
+{{#newline}}
Last
{{/if}}";
Generator generator = parser.Compile(format);
@@ -832,6 +831,7 @@ Content
const string format = @"{{#if this}}
{{/if}}
First
+{{#newline}}
{{#if this}}
{{/if}}
Last";
@@ -1005,10 +1005,11 @@ Last";
}
///
- ///
+ /// 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.
///
[TestMethod]
- public void TestCompile_Each_LoopOverCollectionTwice()
+ public void TestCompile_Each_ContextAfterIndexTag()
{
List objects = new List();
objects.Add(new TestObject { Name = "name1", Val = "val1" });
@@ -1016,11 +1017,10 @@ Last";
objects.Add(new TestObject { Name = "name3", Val = "val3" });
const string template = @"{{#each this}}
-Item Number: {{#index}}
+Item Number: {{#index}}
{{#newline}}
{{/each}}
{{#each this}}
-Item Number: foo
-
+Item Number: foo
{{#newline}}
{{/each}}";
FormatCompiler compiler = new FormatCompiler();
@@ -1111,15 +1111,17 @@ Item Number: foo
{
FormatCompiler compiler = new FormatCompiler();
const string format = @"Hello {{Customer.FirstName}}:
-
+{{#newline}}
+{{#newline}}
{{#with Order}}
{{#if LineItems}}
Below are your order details:
-
+{{#newline}}
+{{#newline}}
{{#each LineItems}}
- {{Name}}: {{UnitPrice:C}} x {{Quantity}}
+ {{Name}}: {{UnitPrice:C}} x {{Quantity}}{{#newline}}
{{/each}}
-
+{{#newline}}
Your order total was: {{Total:C}}
{{/if}}
{{/with}}";
diff --git a/mustache-sharp.test/Properties/AssemblyInfo.cs b/mustache-sharp.test/Properties/AssemblyInfo.cs
index f03d8b1..972b946 100644
--- a/mustache-sharp.test/Properties/AssemblyInfo.cs
+++ b/mustache-sharp.test/Properties/AssemblyInfo.cs
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
-[assembly: AssemblyVersion("0.1.3.0")]
-[assembly: AssemblyFileVersion("0.1.3.0")]
+[assembly: AssemblyVersion("0.2.0.0")]
+[assembly: AssemblyFileVersion("0.2.0.0")]
diff --git a/mustache-sharp/CompoundGenerator.cs b/mustache-sharp/CompoundGenerator.cs
index 6e8934c..8cad303 100644
--- a/mustache-sharp/CompoundGenerator.cs
+++ b/mustache-sharp/CompoundGenerator.cs
@@ -11,7 +11,7 @@ namespace Mustache
{
private readonly TagDefinition _definition;
private readonly ArgumentCollection _arguments;
- private readonly LinkedList _primaryGenerators;
+ private readonly List _primaryGenerators;
private IGenerator _subGenerator;
///
@@ -23,7 +23,7 @@ namespace Mustache
{
_definition = definition;
_arguments = arguments;
- _primaryGenerators = new LinkedList();
+ _primaryGenerators = new List();
}
///
@@ -47,19 +47,6 @@ namespace Mustache
addGenerator(generator, isSubGenerator);
}
- ///
- /// Creates a StaticGenerator from the given value and adds it.
- ///
- /// The static generators to add.
- public void AddStaticGenerators(IEnumerable generators)
- {
- foreach (StaticGenerator generator in generators)
- {
- LinkedListNode node = _primaryGenerators.AddLast(generator);
- generator.Node = node;
- }
- }
-
private void addGenerator(IGenerator generator, bool isSubGenerator)
{
if (isSubGenerator)
@@ -68,7 +55,7 @@ namespace Mustache
}
else
{
- _primaryGenerators.AddLast(generator);
+ _primaryGenerators.Add(generator);
}
}
@@ -76,17 +63,17 @@ namespace Mustache
{
Dictionary arguments = _arguments.GetArguments(scope);
IEnumerable contexts = _definition.GetChildContext(writer, scope, arguments);
- LinkedList generators;
+ List generators;
if (_definition.ShouldGeneratePrimaryGroup(arguments))
{
generators = _primaryGenerators;
}
else
{
- generators = new LinkedList();
+ generators = new List();
if (_subGenerator != null)
{
- generators.AddLast(_subGenerator);
+ generators.Add(_subGenerator);
}
}
foreach (NestedContext context in contexts)
diff --git a/mustache-sharp/FormatCompiler.cs b/mustache-sharp/FormatCompiler.cs
index 856e8cf..5593fbd 100644
--- a/mustache-sharp/FormatCompiler.cs
+++ b/mustache-sharp/FormatCompiler.cs
@@ -37,6 +37,8 @@ namespace Mustache
_tagLookup.Add(indexDefinition.Name, indexDefinition);
WithTagDefinition withDefinition = new WithTagDefinition();
_tagLookup.Add(withDefinition.Name, withDefinition);
+ NewlineTagDefinition newlineDefinition = new NewlineTagDefinition();
+ _tagLookup.Add(newlineDefinition.Name, newlineDefinition);
}
///
@@ -75,12 +77,10 @@ namespace Mustache
throw new ArgumentNullException("format");
}
CompoundGenerator generator = new CompoundGenerator(_masterDefinition, new ArgumentCollection());
- Trimmer trimmer = new Trimmer();
List context = new List() { 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);
- generator.AddStaticGenerators(trimmer.RecordText(trailing, false, false));
- trimmer.Trim();
+ generator.AddGenerator(new StaticGenerator(trailing));
return new Generator(generator);
}
@@ -171,7 +171,6 @@ namespace Mustache
TagDefinition tagDefinition,
List context,
CompoundGenerator generator,
- Trimmer trimmer,
string format, int formatIndex)
{
while (true)
@@ -192,7 +191,7 @@ namespace Mustache
if (match.Groups["key"].Success)
{
- generator.AddStaticGenerators(trimmer.RecordText(leading, true, true));
+ generator.AddGenerator(new StaticGenerator(leading));
formatIndex = match.Index + match.Length;
string key = match.Groups["key"].Value;
string alignment = match.Groups["alignment"].Value;
@@ -217,7 +216,7 @@ namespace Mustache
}
if (nextDefinition.HasContent)
{
- generator.AddStaticGenerators(trimmer.RecordText(leading, true, false));
+ generator.AddGenerator(new StaticGenerator(leading));
ArgumentCollection arguments = getArguments(nextDefinition, match);
CompoundGenerator compoundGenerator = new CompoundGenerator(nextDefinition, arguments);
IEnumerable contextParameters = nextDefinition.GetChildContextParameters();
@@ -227,7 +226,7 @@ namespace Mustache
ContextParameter[] parameters = contextParameters.Select(p => new ContextParameter(p.Name, arguments.GetKey(p))).ToArray();
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);
if (hasContext)
{
@@ -236,7 +235,7 @@ namespace Mustache
}
else
{
- generator.AddStaticGenerators(trimmer.RecordText(leading, true, true));
+ generator.AddGenerator(new StaticGenerator(leading));
ArgumentCollection arguments = getArguments(nextDefinition, match);
InlineGenerator inlineGenerator = new InlineGenerator(nextDefinition, arguments);
generator.AddGenerator(inlineGenerator);
@@ -244,7 +243,7 @@ namespace Mustache
}
else if (match.Groups["close"].Success)
{
- generator.AddStaticGenerators(trimmer.RecordText(leading, true, false));
+ generator.AddGenerator(new StaticGenerator(leading));
string tagName = match.Groups["name"].Value;
TagDefinition nextDefinition = _tagLookup[tagName];
formatIndex = match.Index;
@@ -256,7 +255,7 @@ namespace Mustache
}
else if (match.Groups["comment"].Success)
{
- generator.AddStaticGenerators(trimmer.RecordText(leading, true, false));
+ generator.AddGenerator(new StaticGenerator(leading));
formatIndex = match.Index + match.Length;
}
else if (match.Groups["unknown"].Success)
diff --git a/mustache-sharp/NewlineTagDefinition.cs b/mustache-sharp/NewlineTagDefinition.cs
new file mode 100644
index 0000000..e814fbd
--- /dev/null
+++ b/mustache-sharp/NewlineTagDefinition.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Mustache
+{
+ ///
+ /// Defines a tag that outputs a newline.
+ ///
+ internal sealed class NewlineTagDefinition : InlineTagDefinition
+ {
+ ///
+ /// Initializes a new instance of an NewlineTagDefinition.
+ ///
+ public NewlineTagDefinition()
+ : base("newline")
+ {
+ }
+
+ ///
+ /// Gets the text to output.
+ ///
+ /// The writer to write the output to.
+ /// The arguments passed to the tag.
+ /// Extra data passed along with the context.
+ public override void GetText(TextWriter writer, Dictionary arguments, object contextData)
+ {
+ writer.Write(Environment.NewLine);
+ }
+ }
+}
diff --git a/mustache-sharp/Properties/AssemblyInfo.cs b/mustache-sharp/Properties/AssemblyInfo.cs
index 07e6b38..abef423 100644
--- a/mustache-sharp/Properties/AssemblyInfo.cs
+++ b/mustache-sharp/Properties/AssemblyInfo.cs
@@ -34,6 +34,6 @@ using System.Runtime.CompilerServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("0.1.3.0")]
-[assembly: AssemblyFileVersion("0.1.3.0")]
+[assembly: AssemblyVersion("0.2.0.0")]
+[assembly: AssemblyFileVersion("0.2.0.0")]
[assembly: InternalsVisibleTo("mustache-sharp.test")]
\ No newline at end of file
diff --git a/mustache-sharp/StaticGenerator.cs b/mustache-sharp/StaticGenerator.cs
index f03fd4a..8ac8996 100644
--- a/mustache-sharp/StaticGenerator.cs
+++ b/mustache-sharp/StaticGenerator.cs
@@ -9,20 +9,14 @@ namespace Mustache
///
internal sealed class StaticGenerator : IGenerator
{
+ private readonly string value;
+
///
/// Initializes a new instance of a StaticGenerator.
///
- public StaticGenerator()
+ public StaticGenerator(string value)
{
- }
-
- ///
- /// Gets or sets the linked list node containing the current generator.
- ///
- public LinkedListNode Node
- {
- get;
- set;
+ this.value = value.Replace(Environment.NewLine, String.Empty);
}
///
@@ -30,20 +24,7 @@ namespace Mustache
///
public string Value
{
- get;
- set;
- }
-
- ///
- /// Removes the static text from the final output.
- ///
- public void Prune()
- {
- if (Node != null)
- {
- Node.List.Remove(Node);
- Node = null;
- }
+ get { return value; }
}
void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData)
diff --git a/mustache-sharp/Trimmer.cs b/mustache-sharp/Trimmer.cs
deleted file mode 100644
index b6179fe..0000000
--- a/mustache-sharp/Trimmer.cs
+++ /dev/null
@@ -1,147 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Mustache
-{
- ///
- /// Removes unnecessary lines from the final output.
- ///
- internal sealed class Trimmer
- {
- private readonly LinkedList _lines;
- private LinkedListNode _currentLine;
-
- ///
- /// Initializes a new instance of a Trimmer.
- ///
- public Trimmer()
- {
- _lines = new LinkedList();
- _currentLine = _lines.AddLast(new LineDetails());
- }
-
- ///
- /// Updates the state of the trimmer, indicating that the given text was encountered before an inline tag.
- ///
- /// The text at the end of the format string.
- /// The generator created for the inline tag.
- /// A static generator containing the passed text.
- public IEnumerable 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 current = _lines.First;
- while (current != null)
- {
- LineDetails details = current.Value;
- LinkedListNode 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 current = _lines.First;
- while (current != _lines.Last)
- {
- List generators = current.Value.Generators;
- StaticGenerator lastGenerator = generators[generators.Count - 1];
- lastGenerator.Value += Environment.NewLine;
- current = current.Next;
- }
- }
-
- private void removeEmptyGenerators()
- {
- LinkedListNode 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();
- }
-
- public bool HasTag { get; set; }
-
- public List Generators { get; set; }
-
- public bool HasOutput { get; set; }
- }
- }
-}
diff --git a/mustache-sharp/mustache-sharp.csproj b/mustache-sharp/mustache-sharp.csproj
index 0d5d7b4..55d74ea 100644
--- a/mustache-sharp/mustache-sharp.csproj
+++ b/mustache-sharp/mustache-sharp.csproj
@@ -40,6 +40,7 @@
+
@@ -68,7 +69,6 @@
-