From d7a0ab3b38e8e2289d5135b2b8c0cd050910c240 Mon Sep 17 00:00:00 2001 From: Travis Parks Date: Sat, 20 Jul 2013 12:06:38 -0400 Subject: [PATCH] Add support for index in each tags It was requested that the current index could be accessed in the each tag. --- README.md | 10 +++++ mustache-sharp.test/FormatCompilerTester.cs | 16 +++++++- .../Properties/AssemblyInfo.cs | 4 +- mustache-sharp/CompoundGenerator.cs | 4 +- mustache-sharp/EachTagDefinition.cs | 6 ++- mustache-sharp/FormatCompiler.cs | 2 + mustache-sharp/Generator.cs | 2 +- mustache-sharp/IGenerator.cs | 3 +- mustache-sharp/IndexTagDefinition.cs | 40 +++++++++++++++++++ mustache-sharp/InlineGenerator.cs | 4 +- mustache-sharp/KeyGenerator.cs | 2 +- mustache-sharp/NestedContext.cs | 5 +++ mustache-sharp/Properties/AssemblyInfo.cs | 4 +- mustache-sharp/StaticGenerator.cs | 2 +- mustache-sharp/TagDefinition.cs | 3 +- mustache-sharp/mustache-sharp.csproj | 1 + 16 files changed, 92 insertions(+), 16 deletions(-) create mode 100644 mustache-sharp/IndexTagDefinition.cs diff --git a/README.md b/README.md index 78667a7..b4f59e0 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,16 @@ If you need to print out a block of text for each item in a collection, use the {{/each}} Within the context of the **each** block, the scope changes to the current item. So, in the example above, `Name` would refer to a property in the `Customer` class. + +Additionally, you can access the current index into the collection being enumerated using the **index** tag. + + + +This will build an HTML list, building a list of items with `Description` and `Value` properties. Additionally, the `index` tag is used to create a CSS class with increasing numbers. ## The 'with' tag Within a block of text, you may refer to a same top-level placeholder over and over. You can cut down the amount of text by using the **with** tag. diff --git a/mustache-sharp.test/FormatCompilerTester.cs b/mustache-sharp.test/FormatCompilerTester.cs index 30cec2a..e035525 100644 --- a/mustache-sharp.test/FormatCompilerTester.cs +++ b/mustache-sharp.test/FormatCompilerTester.cs @@ -990,6 +990,20 @@ Last"; Assert.AreEqual("Before123After", result, "The wrong text was generated."); } + /// + /// We can use the index tag to get the current iteration. + /// + [TestMethod] + public void TestCompile_Each_Index_PrintsIndexOfItem() + { + FormatCompiler parser = new FormatCompiler(); + const string format = ""; + Generator generator = parser.Compile(format); + string result = generator.Render(new int[] { 1, 2, 3 }); + const string expected = @""; + Assert.AreEqual(expected, result, "The wrong text was generated."); + } + #endregion #region With @@ -1038,7 +1052,7 @@ Last"; return new TagParameter[] { new TagParameter("param") { IsRequired = false, DefaultValue = 123 } }; } - public override void GetText(TextWriter writer, Dictionary arguments) + public override void GetText(TextWriter writer, Dictionary arguments, object contextData) { writer.Write(arguments["param"]); } diff --git a/mustache-sharp.test/Properties/AssemblyInfo.cs b/mustache-sharp.test/Properties/AssemblyInfo.cs index 0fdb5f2..7ccaa92 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.1.0")] -[assembly: AssemblyFileVersion("0.1.1.0")] +[assembly: AssemblyVersion("0.1.2.0")] +[assembly: AssemblyFileVersion("0.1.2.0")] diff --git a/mustache-sharp/CompoundGenerator.cs b/mustache-sharp/CompoundGenerator.cs index aa7227f..6e8934c 100644 --- a/mustache-sharp/CompoundGenerator.cs +++ b/mustache-sharp/CompoundGenerator.cs @@ -72,7 +72,7 @@ namespace Mustache } } - void IGenerator.GetText(KeyScope scope, TextWriter writer) + void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData) { Dictionary arguments = _arguments.GetArguments(scope); IEnumerable contexts = _definition.GetChildContext(writer, scope, arguments); @@ -93,7 +93,7 @@ namespace Mustache { foreach (IGenerator generator in generators) { - generator.GetText(context.KeyScope ?? scope, context.Writer ?? writer); + generator.GetText(context.KeyScope ?? scope, context.Writer ?? writer, context.Data); if (context.WriterNeedsConsidated) { writer.Write(_definition.ConsolidateWriter(context.Writer ?? writer, arguments)); diff --git a/mustache-sharp/EachTagDefinition.cs b/mustache-sharp/EachTagDefinition.cs index 54ca2b6..30c8e2f 100644 --- a/mustache-sharp/EachTagDefinition.cs +++ b/mustache-sharp/EachTagDefinition.cs @@ -54,9 +54,11 @@ namespace Mustache { yield break; } + int index = 0; foreach (object item in enumerable) { - yield return new NestedContext() { KeyScope = scope.CreateChildScope(item), Writer = writer }; + yield return new NestedContext() { KeyScope = scope.CreateChildScope(item), Writer = writer, Data = index }; + ++index; } } @@ -66,7 +68,7 @@ namespace Mustache /// The name of the tags that are in scope. protected override IEnumerable GetChildTags() { - return new string[] { }; + return new string[] { "index" }; } /// diff --git a/mustache-sharp/FormatCompiler.cs b/mustache-sharp/FormatCompiler.cs index 040e738..27a6738 100644 --- a/mustache-sharp/FormatCompiler.cs +++ b/mustache-sharp/FormatCompiler.cs @@ -33,6 +33,8 @@ namespace Mustache _tagLookup.Add(elseDefinition.Name, elseDefinition); EachTagDefinition eachDefinition = new EachTagDefinition(); _tagLookup.Add(eachDefinition.Name, eachDefinition); + IndexTagDefinition indexDefinition = new IndexTagDefinition(); + _tagLookup.Add(indexDefinition.Name, indexDefinition); WithTagDefinition withDefinition = new WithTagDefinition(); _tagLookup.Add(withDefinition.Name, withDefinition); } diff --git a/mustache-sharp/Generator.cs b/mustache-sharp/Generator.cs index f0b51b8..0553e12 100644 --- a/mustache-sharp/Generator.cs +++ b/mustache-sharp/Generator.cs @@ -80,7 +80,7 @@ namespace Mustache scope.KeyNotFound += handler; } StringWriter writer = new StringWriter(provider); - _generator.GetText(scope, writer); + _generator.GetText(scope, writer, null); return writer.ToString(); } } diff --git a/mustache-sharp/IGenerator.cs b/mustache-sharp/IGenerator.cs index 5f16d18..4ddb7ce 100644 --- a/mustache-sharp/IGenerator.cs +++ b/mustache-sharp/IGenerator.cs @@ -13,7 +13,8 @@ namespace Mustache /// /// The current lexical scope of the keys. /// The text writer to send all text to. + /// The data associated to the context. /// The generated text. - void GetText(KeyScope scope, TextWriter writer); + void GetText(KeyScope scope, TextWriter writer, object contextData); } } diff --git a/mustache-sharp/IndexTagDefinition.cs b/mustache-sharp/IndexTagDefinition.cs new file mode 100644 index 0000000..86d1759 --- /dev/null +++ b/mustache-sharp/IndexTagDefinition.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Mustache +{ + /// + /// Defines a tag that outputs the current index within an each loop. + /// + internal sealed class IndexTagDefinition : InlineTagDefinition + { + /// + /// Initializes a new instance of an IndexTagDefinition. + /// + public IndexTagDefinition() + : base("index") + { + } + + /// + /// Gets whether the tag only exists within the scope of its parent. + /// + protected override bool GetIsContextSensitive() + { + return true; + } + + /// + /// 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) + { + int index = (int)contextData; + writer.Write(index); + } + } +} diff --git a/mustache-sharp/InlineGenerator.cs b/mustache-sharp/InlineGenerator.cs index c1fb2c7..cf07ea7 100644 --- a/mustache-sharp/InlineGenerator.cs +++ b/mustache-sharp/InlineGenerator.cs @@ -23,10 +23,10 @@ namespace Mustache _arguments = arguments; } - void IGenerator.GetText(KeyScope scope, TextWriter writer) + void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData) { Dictionary arguments = _arguments.GetArguments(scope); - _definition.GetText(writer, arguments); + _definition.GetText(writer, arguments, contextData); } } } diff --git a/mustache-sharp/KeyGenerator.cs b/mustache-sharp/KeyGenerator.cs index 6dbe146..599ff2a 100644 --- a/mustache-sharp/KeyGenerator.cs +++ b/mustache-sharp/KeyGenerator.cs @@ -42,7 +42,7 @@ namespace Mustache return formatBuilder.ToString(); } - void IGenerator.GetText(KeyScope scope, TextWriter writer) + void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData) { object value = scope.Find(_key); writer.Write(_format, value); diff --git a/mustache-sharp/NestedContext.cs b/mustache-sharp/NestedContext.cs index 090706a..1a0cacb 100644 --- a/mustache-sharp/NestedContext.cs +++ b/mustache-sharp/NestedContext.cs @@ -45,5 +45,10 @@ namespace Mustache get; set; } + + /// + /// Gets or sets data associated with the context. + /// + public object Data { get; set; } } } diff --git a/mustache-sharp/Properties/AssemblyInfo.cs b/mustache-sharp/Properties/AssemblyInfo.cs index f37dd73..f08da59 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.1.0")] -[assembly: AssemblyFileVersion("0.1.1.0")] +[assembly: AssemblyVersion("0.1.2.0")] +[assembly: AssemblyFileVersion("0.1.2.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 7ed6b4d..f03fd4a 100644 --- a/mustache-sharp/StaticGenerator.cs +++ b/mustache-sharp/StaticGenerator.cs @@ -46,7 +46,7 @@ namespace Mustache } } - void IGenerator.GetText(KeyScope scope, TextWriter writer) + void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData) { writer.Write(Value); } diff --git a/mustache-sharp/TagDefinition.cs b/mustache-sharp/TagDefinition.cs index 2d3ce02..dec179f 100644 --- a/mustache-sharp/TagDefinition.cs +++ b/mustache-sharp/TagDefinition.cs @@ -152,7 +152,8 @@ namespace Mustache /// /// The text writer to write to. /// The arguments passed to the tag. - public virtual void GetText(TextWriter writer, Dictionary arguments) + /// The data associated to the context. + public virtual void GetText(TextWriter writer, Dictionary arguments, object contextData) { } diff --git a/mustache-sharp/mustache-sharp.csproj b/mustache-sharp/mustache-sharp.csproj index d7ecc04..0d5d7b4 100644 --- a/mustache-sharp/mustache-sharp.csproj +++ b/mustache-sharp/mustache-sharp.csproj @@ -40,6 +40,7 @@ +