Optimized text generation.
The way the code was implemented before, each block of text was generating a string which was then being added to a StringBuilder. This only improved performance within a block itself. Needing to then copy the results of that builder into the parent tag's builder was wasteful. Now, a single TextWriter is used for all tags. If a block needs to be processed after-the-fact, the tag can indicate that it wants to provide a new text writer and that it wants to consolidate the text.
This commit is contained in:
parent
83b2a8a3d9
commit
790f856b44
|
@ -2,6 +2,7 @@
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace mustache.test
|
namespace mustache.test
|
||||||
{
|
{
|
||||||
|
@ -901,19 +902,14 @@ Last";
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool GetIsContextSensitive()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override IEnumerable<TagParameter> GetParameters()
|
protected override IEnumerable<TagParameter> GetParameters()
|
||||||
{
|
{
|
||||||
return new TagParameter[] { new TagParameter("param") { IsRequired = false, DefaultValue = 123 } };
|
return new TagParameter[] { new TagParameter("param") { IsRequired = false, DefaultValue = 123 } };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string GetText(IFormatProvider provider, Dictionary<string, object> arguments)
|
public override void GetText(TextWriter writer, Dictionary<string, object> arguments)
|
||||||
{
|
{
|
||||||
return arguments["param"].ToString();
|
writer.Write(arguments["param"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.0.1.0")]
|
[assembly: AssemblyVersion("0.0.2.0")]
|
||||||
[assembly: AssemblyFileVersion("0.0.1.0")]
|
[assembly: AssemblyFileVersion("0.0.2.0")]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.IO;
|
||||||
|
|
||||||
namespace mustache
|
namespace mustache
|
||||||
{
|
{
|
||||||
|
@ -72,11 +72,10 @@ namespace mustache
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string IGenerator.GetText(IFormatProvider provider, KeyScope scope)
|
void IGenerator.GetText(KeyScope scope, TextWriter writer)
|
||||||
{
|
{
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
Dictionary<string, object> arguments = _arguments.GetArguments(scope);
|
Dictionary<string, object> arguments = _arguments.GetArguments(scope);
|
||||||
IEnumerable<KeyScope> scopes = _definition.GetChildScopes(scope, arguments);
|
IEnumerable<NestedContext> contexts = _definition.GetChildContext(writer, scope, arguments);
|
||||||
LinkedList<IGenerator> generators;
|
LinkedList<IGenerator> generators;
|
||||||
if (_definition.ShouldGeneratePrimaryGroup(arguments))
|
if (_definition.ShouldGeneratePrimaryGroup(arguments))
|
||||||
{
|
{
|
||||||
|
@ -90,16 +89,17 @@ namespace mustache
|
||||||
generators.AddLast(_subGenerator);
|
generators.AddLast(_subGenerator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach (KeyScope childScope in scopes)
|
foreach (NestedContext context in contexts)
|
||||||
{
|
{
|
||||||
foreach (IGenerator generator in generators)
|
foreach (IGenerator generator in generators)
|
||||||
{
|
{
|
||||||
builder.Append(generator.GetText(provider, childScope));
|
generator.GetText(context.KeyScope, context.Writer);
|
||||||
}
|
if (context.WriterNeedsConsidated)
|
||||||
}
|
{
|
||||||
string innerText = builder.ToString();
|
writer.Write(_definition.ConsolidateWriter(context.Writer, arguments));
|
||||||
string outerText = _definition.Decorate(provider, innerText, arguments);
|
}
|
||||||
return outerText;
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace mustache
|
namespace mustache
|
||||||
{
|
{
|
||||||
|
@ -38,12 +39,13 @@ namespace mustache
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the scopes for each of the items found in the argument.
|
/// Gets the context to use when building the inner text of the tag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="writer">The text writer passed</param>
|
||||||
/// <param name="scope">The current scope.</param>
|
/// <param name="scope">The current scope.</param>
|
||||||
/// <param name="arguments">The arguments passed to the tag.</param>
|
/// <param name="arguments">The arguments passed to the tag.</param>
|
||||||
/// <returns>The scopes for each of the items found in the argument.</returns>
|
/// <returns>The scope to use when building the inner text of the tag.</returns>
|
||||||
public override IEnumerable<KeyScope> GetChildScopes(KeyScope scope, Dictionary<string, object> arguments)
|
public override IEnumerable<NestedContext> GetChildContext(TextWriter writer, KeyScope scope, Dictionary<string, object> arguments)
|
||||||
{
|
{
|
||||||
object value = arguments[collectionParameter];
|
object value = arguments[collectionParameter];
|
||||||
IEnumerable enumerable = value as IEnumerable;
|
IEnumerable enumerable = value as IEnumerable;
|
||||||
|
@ -53,7 +55,7 @@ namespace mustache
|
||||||
}
|
}
|
||||||
foreach (object item in enumerable)
|
foreach (object item in enumerable)
|
||||||
{
|
{
|
||||||
yield return scope.CreateChildScope(item);
|
yield return new NestedContext() { KeyScope = scope.CreateChildScope(item), Writer = writer };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace mustache
|
namespace mustache
|
||||||
{
|
{
|
||||||
|
@ -47,7 +48,9 @@ namespace mustache
|
||||||
private string render(IFormatProvider provider, object source)
|
private string render(IFormatProvider provider, object source)
|
||||||
{
|
{
|
||||||
KeyScope scope = new KeyScope(source);
|
KeyScope scope = new KeyScope(source);
|
||||||
return _generator.GetText(provider, scope);
|
StringWriter writer = new StringWriter(provider);
|
||||||
|
_generator.GetText(scope, writer);
|
||||||
|
return writer.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace mustache
|
namespace mustache
|
||||||
{
|
{
|
||||||
|
@ -10,9 +11,9 @@ namespace mustache
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates the text when applying the format plan.
|
/// Generates the text when applying the format plan.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="provider">The format provider to use when formatting the keys.</param>
|
|
||||||
/// <param name="scope">The current lexical scope of the keys.</param>
|
/// <param name="scope">The current lexical scope of the keys.</param>
|
||||||
|
/// <param name="writer">The text writer to send all text to.</param>
|
||||||
/// <returns>The generated text.</returns>
|
/// <returns>The generated text.</returns>
|
||||||
string GetText(IFormatProvider provider, KeyScope scope);
|
void GetText(KeyScope scope, TextWriter writer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace mustache
|
namespace mustache
|
||||||
{
|
{
|
||||||
|
@ -22,10 +23,10 @@ namespace mustache
|
||||||
_arguments = arguments;
|
_arguments = arguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
string IGenerator.GetText(IFormatProvider provider, KeyScope scope)
|
void IGenerator.GetText(KeyScope scope, TextWriter writer)
|
||||||
{
|
{
|
||||||
Dictionary<string, object> arguments = _arguments.GetArguments(scope);
|
Dictionary<string, object> arguments = _arguments.GetArguments(scope);
|
||||||
return _definition.Decorate(provider, String.Empty, arguments);
|
_definition.GetText(writer, arguments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace mustache
|
namespace mustache
|
||||||
{
|
{
|
||||||
|
@ -35,25 +36,5 @@ namespace mustache
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generates the text for the tag.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="provider">The format provider to use.</param>
|
|
||||||
/// <param name="innerText">The text to decorate. This will always be an empty string.</param>
|
|
||||||
/// <param name="arguments">The arguments passed to the tag.</param>
|
|
||||||
/// <returns>The generated text.</returns>
|
|
||||||
public sealed override string Decorate(IFormatProvider provider, string innerText, Dictionary<string, object> arguments)
|
|
||||||
{
|
|
||||||
return GetText(provider, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the text of the inline tag.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="provider">The format provider to use.</param>
|
|
||||||
/// <param name="arguments">The arguments passed to the tag.</param>
|
|
||||||
/// <returns>The generated text.</returns>
|
|
||||||
protected abstract string GetText(IFormatProvider provider, Dictionary<string, object> arguments);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace mustache
|
namespace mustache
|
||||||
|
@ -41,10 +42,10 @@ namespace mustache
|
||||||
return formatBuilder.ToString();
|
return formatBuilder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
string IGenerator.GetText(IFormatProvider provider, KeyScope scope)
|
void IGenerator.GetText(KeyScope scope, TextWriter writer)
|
||||||
{
|
{
|
||||||
object value = scope.Find(_key);
|
object value = scope.Find(_key);
|
||||||
return String.Format(provider, _format, value);
|
writer.Write(_format, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace mustache
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Holds the objects to use when processing a child context of another tag.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class NestedContext
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of a NestedContext.
|
||||||
|
/// </summary>
|
||||||
|
public NestedContext()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the writer to use when generating the child context.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Setting the writer to null will indicate that the tag's writer should be used.</remarks>
|
||||||
|
public TextWriter Writer
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether the text sent to the returned writer needs to be added
|
||||||
|
/// to the parent tag's writer. This should be false if the parent writer is
|
||||||
|
/// being returned or is being wrapped.
|
||||||
|
/// </summary>
|
||||||
|
public bool WriterNeedsConsidated
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the scope to use when generating the child context.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Setting the scope to null will indicate that the current scope should be used.</remarks>
|
||||||
|
public KeyScope KeyScope
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.0.1.0")]
|
[assembly: AssemblyVersion("0.0.2.0")]
|
||||||
[assembly: AssemblyFileVersion("0.0.1.0")]
|
[assembly: AssemblyFileVersion("0.0.2.0")]
|
||||||
[assembly: InternalsVisibleTo("mustache-sharp.test")]
|
[assembly: InternalsVisibleTo("mustache-sharp.test")]
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace mustache
|
namespace mustache
|
||||||
{
|
{
|
||||||
|
@ -45,9 +46,9 @@ namespace mustache
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string IGenerator.GetText(IFormatProvider provider, KeyScope scope)
|
void IGenerator.GetText(KeyScope scope, TextWriter writer)
|
||||||
{
|
{
|
||||||
return Value;
|
writer.Write(Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using mustache.Properties;
|
using mustache.Properties;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace mustache
|
namespace mustache
|
||||||
{
|
{
|
||||||
|
@ -130,26 +130,35 @@ namespace mustache
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the scope to use when building the inner text of the tag.
|
/// Gets the context to use when building the inner text of the tag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="writer">The text writer passed</param>
|
||||||
/// <param name="scope">The current scope.</param>
|
/// <param name="scope">The current scope.</param>
|
||||||
/// <param name="arguments">The arguments passed to the tag.</param>
|
/// <param name="arguments">The arguments passed to the tag.</param>
|
||||||
/// <returns>The scope to use when building the inner text of the tag.</returns>
|
/// <returns>The scope to use when building the inner text of the tag.</returns>
|
||||||
public virtual IEnumerable<KeyScope> GetChildScopes(KeyScope scope, Dictionary<string, object> arguments)
|
public virtual IEnumerable<NestedContext> GetChildContext(TextWriter writer, KeyScope scope, Dictionary<string, object> arguments)
|
||||||
{
|
{
|
||||||
yield return scope;
|
yield return new NestedContext() { KeyScope = scope, Writer = writer };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Applies additional formatting to the inner text of the tag.
|
/// Applies additional formatting to the inner text of the tag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="provider">The format provider to use.</param>
|
/// <param name="writer">The text writer to write to.</param>
|
||||||
/// <param name="innerText">The inner text of the tag.</param>
|
|
||||||
/// <param name="arguments">The arguments passed to the tag.</param>
|
/// <param name="arguments">The arguments passed to the tag.</param>
|
||||||
/// <returns>The decorated inner text.</returns>
|
public virtual void GetText(TextWriter writer, Dictionary<string, object> arguments)
|
||||||
public virtual string Decorate(IFormatProvider provider, string innerText, Dictionary<string, object> arguments)
|
|
||||||
{
|
{
|
||||||
return innerText;
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Consolidates the text in the given writer to a string, using the given arguments as necessary.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="writer">The writer containing the text to consolidate.</param>
|
||||||
|
/// <param name="arguments">The arguments passed to the tag.</param>
|
||||||
|
/// <returns>The consolidated string.</returns>
|
||||||
|
public virtual string ConsolidateWriter(TextWriter writer, Dictionary<string, object> arguments)
|
||||||
|
{
|
||||||
|
return writer.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace mustache
|
namespace mustache
|
||||||
{
|
{
|
||||||
|
@ -36,15 +37,16 @@ namespace mustache
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the scopes to use for generating the tag's content.
|
/// Gets the context to use when building the inner text of the tag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="writer">The text writer passed</param>
|
||||||
/// <param name="scope">The current scope.</param>
|
/// <param name="scope">The current scope.</param>
|
||||||
/// <param name="arguments">The arguments that were passed to the tag.</param>
|
/// <param name="arguments">The arguments passed to the tag.</param>
|
||||||
/// <returns>The scopes to use for generating the tag's content.</returns>
|
/// <returns>The scope to use when building the inner text of the tag.</returns>
|
||||||
public override IEnumerable<KeyScope> GetChildScopes(KeyScope scope, Dictionary<string, object> arguments)
|
public override IEnumerable<NestedContext> GetChildContext(TextWriter writer, KeyScope scope, Dictionary<string, object> arguments)
|
||||||
{
|
{
|
||||||
object context = arguments[contextParameter];
|
object context = arguments[contextParameter];
|
||||||
yield return scope.CreateChildScope(context);
|
yield return new NestedContext() { KeyScope = scope.CreateChildScope(context), Writer = writer };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
<Compile Include="InlineGenerator.cs" />
|
<Compile Include="InlineGenerator.cs" />
|
||||||
<Compile Include="KeyGenerator.cs" />
|
<Compile Include="KeyGenerator.cs" />
|
||||||
<Compile Include="MasterTagDefinition.cs" />
|
<Compile Include="MasterTagDefinition.cs" />
|
||||||
|
<Compile Include="NestedContext.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Properties\Resources.Designer.cs">
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
|
|
Loading…
Reference in New Issue