Begin implementation of context scopes.

Instead of context data being a single object, it now supports the same
scoping rules as keys.
Now the #index tag is always available.
This commit is contained in:
Travis Parks 2013-08-16 23:35:46 -04:00
parent eb7e909f95
commit f136dd61a5
19 changed files with 279 additions and 111 deletions

View File

@ -1093,7 +1093,7 @@ Item Number: foo<br />
return new TagParameter[] { new TagParameter("param") { IsRequired = false, DefaultValue = 123 } }; return new TagParameter[] { new TagParameter("param") { IsRequired = false, DefaultValue = 123 } };
} }
public override void GetText(TextWriter writer, Dictionary<string, object> arguments, object contextData) public override void GetText(TextWriter writer, Dictionary<string, object> arguments, Scope contextScope)
{ {
writer.Write(arguments["param"]); writer.Write(arguments["param"]);
} }
@ -1168,5 +1168,23 @@ Your order total was: $7.50";
} }
#endregion #endregion
#region Context Variables
/// <summary>
/// We will use the index variable to determine whether or not to print out a line.
/// </summary>
[TestMethod]
public void TestCompile_CanUseContextVariablesToMakeDecisions()
{
FormatCompiler compiler = new FormatCompiler();
const string format = @"{{#each this}}{{#if @index}}{{#index}}{{/if}}{{/each}}";
Generator generator = compiler.Compile(format);
string actual = generator.Render(new int[] { 1, 1, 1, 1, });
string expected = "123";
Assert.AreEqual(expected, actual, "The numbers were not valid.");
}
#endregion
} }
} }

View File

@ -50,9 +50,10 @@ namespace Mustache
/// <summary> /// <summary>
/// Substitutes the key placeholders with their respective values. /// Substitutes the key placeholders with their respective values.
/// </summary> /// </summary>
/// <param name="scope">The current lexical scope.</param> /// <param name="keyScope">The key/value pairs in the current lexical scope.</param>
/// <param name="contextScope">The key/value pairs in current context.</param>
/// <returns>A dictionary associating the parameter name to the associated value.</returns> /// <returns>A dictionary associating the parameter name to the associated value.</returns>
public Dictionary<string, object> GetArguments(KeyScope scope) public Dictionary<string, object> GetArguments(Scope keyScope, Scope contextScope)
{ {
Dictionary<string, object> arguments = new Dictionary<string,object>(); Dictionary<string, object> arguments = new Dictionary<string,object>();
foreach (KeyValuePair<TagParameter, string> pair in _argumentLookup) foreach (KeyValuePair<TagParameter, string> pair in _argumentLookup)
@ -62,13 +63,27 @@ namespace Mustache
{ {
value = pair.Key.DefaultValue; value = pair.Key.DefaultValue;
} }
else if (pair.Value.StartsWith("@"))
{
value = contextScope.Find(pair.Value.Substring(1));
}
else else
{ {
value = scope.Find(pair.Value); value = keyScope.Find(pair.Value);
} }
arguments.Add(pair.Key.Name, value); arguments.Add(pair.Key.Name, value);
} }
return arguments; return arguments;
} }
public Dictionary<string, object> GetArguments()
{
Dictionary<string, object> arguments = new Dictionary<string, object>();
foreach (KeyValuePair<TagParameter, string> pair in _argumentLookup)
{
arguments.Add(pair.Key.Name, pair.Value);
}
return arguments;
}
} }
} }

View File

@ -59,10 +59,10 @@ namespace Mustache
} }
} }
void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData) void IGenerator.GetText(Scope keyScope, TextWriter writer, Scope contextScope)
{ {
Dictionary<string, object> arguments = _arguments.GetArguments(scope); Dictionary<string, object> arguments = _arguments.GetArguments(keyScope, contextScope);
IEnumerable<NestedContext> contexts = _definition.GetChildContext(writer, scope, arguments); IEnumerable<NestedContext> contexts = _definition.GetChildContext(writer, keyScope, arguments, contextScope);
List<IGenerator> generators; List<IGenerator> generators;
if (_definition.ShouldGeneratePrimaryGroup(arguments)) if (_definition.ShouldGeneratePrimaryGroup(arguments))
{ {
@ -80,7 +80,7 @@ namespace Mustache
{ {
foreach (IGenerator generator in generators) foreach (IGenerator generator in generators)
{ {
generator.GetText(context.KeyScope ?? scope, context.Writer ?? writer, context.Data); generator.GetText(context.KeyScope ?? keyScope, context.Writer ?? writer, context.ContextScope);
if (context.WriterNeedsConsidated) if (context.WriterNeedsConsidated)
{ {
writer.Write(_definition.ConsolidateWriter(context.Writer ?? writer, arguments)); writer.Write(_definition.ConsolidateWriter(context.Writer ?? writer, arguments));

View File

@ -43,10 +43,14 @@ namespace Mustache
/// Gets the context 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="writer">The text writer passed</param>
/// <param name="scope">The current scope.</param> /// <param name="keyScope">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 override IEnumerable<NestedContext> GetChildContext(TextWriter writer, KeyScope scope, Dictionary<string, object> arguments) public override IEnumerable<NestedContext> GetChildContext(
TextWriter writer,
Scope keyScope,
Dictionary<string, object> arguments,
Scope contextScope)
{ {
object value = arguments[collectionParameter]; object value = arguments[collectionParameter];
IEnumerable enumerable = value as IEnumerable; IEnumerable enumerable = value as IEnumerable;
@ -57,7 +61,14 @@ namespace Mustache
int index = 0; int index = 0;
foreach (object item in enumerable) foreach (object item in enumerable)
{ {
yield return new NestedContext() { KeyScope = scope.CreateChildScope(item), Writer = writer, Data = index }; NestedContext childContext = new NestedContext()
{
KeyScope = keyScope.CreateChildScope(item),
Writer = writer,
ContextScope = contextScope.CreateChildScope(),
};
childContext.ContextScope.Set("index", index);
yield return childContext;
++index; ++index;
} }
} }

View File

@ -39,6 +39,8 @@ namespace Mustache
_tagLookup.Add(withDefinition.Name, withDefinition); _tagLookup.Add(withDefinition.Name, withDefinition);
NewlineTagDefinition newlineDefinition = new NewlineTagDefinition(); NewlineTagDefinition newlineDefinition = new NewlineTagDefinition();
_tagLookup.Add(newlineDefinition.Name, newlineDefinition); _tagLookup.Add(newlineDefinition.Name, newlineDefinition);
SetTagDefinition setDefinition = new SetTagDefinition();
_tagLookup.Add(setDefinition.Name, setDefinition);
} }
/// <summary> /// <summary>
@ -150,9 +152,11 @@ namespace Mustache
foreach (TagParameter parameter in definition.Parameters) foreach (TagParameter parameter in definition.Parameters)
{ {
regexBuilder.Append(@"(\s+?"); regexBuilder.Append(@"(\s+?");
regexBuilder.Append(@"(?<argument>"); regexBuilder.Append(@"(?<argument>(");
regexBuilder.Append(RegexHelper.CompoundKey); regexBuilder.Append(RegexHelper.CompoundKey);
regexBuilder.Append(@"))"); regexBuilder.Append("|@");
regexBuilder.Append(RegexHelper.Key);
regexBuilder.Append(@")))");
if (!parameter.IsRequired) if (!parameter.IsRequired)
{ {
regexBuilder.Append("?"); regexBuilder.Append("?");

View File

@ -70,7 +70,7 @@ namespace Mustache
private string render(IFormatProvider provider, object source) private string render(IFormatProvider provider, object source)
{ {
KeyScope scope = new KeyScope(source); Scope scope = new Scope(source);
foreach (EventHandler<KeyFoundEventArgs> handler in _foundHandlers) foreach (EventHandler<KeyFoundEventArgs> handler in _foundHandlers)
{ {
scope.KeyFound += handler; scope.KeyFound += handler;
@ -80,7 +80,8 @@ namespace Mustache
scope.KeyNotFound += handler; scope.KeyNotFound += handler;
} }
StringWriter writer = new StringWriter(provider); StringWriter writer = new StringWriter(provider);
_generator.GetText(scope, writer, null); Scope contextScope = new Scope(new Dictionary<string, object>());
_generator.GetText(scope, writer, contextScope);
return writer.ToString(); return writer.ToString();
} }
} }

View File

@ -11,10 +11,10 @@ namespace Mustache
/// <summary> /// <summary>
/// Generates the text when applying the format plan. /// Generates the text when applying the format plan.
/// </summary> /// </summary>
/// <param name="scope">The current lexical scope of the keys.</param> /// <param name="keyScope">The current lexical scope of the keys.</param>
/// <param name="writer">The text writer to send all text to.</param> /// <param name="writer">The text writer to send all text to.</param>
/// <param name="contextData">The data associated to the context.</param> /// <param name="contextScope">The data associated to the context.</param>
/// <returns>The generated text.</returns> /// <returns>The generated text.</returns>
void GetText(KeyScope scope, TextWriter writer, object contextData); void GetText(Scope keyScope, TextWriter writer, Scope contextScope);
} }
} }

View File

@ -13,28 +13,23 @@ namespace Mustache
/// Initializes a new instance of an IndexTagDefinition. /// Initializes a new instance of an IndexTagDefinition.
/// </summary> /// </summary>
public IndexTagDefinition() public IndexTagDefinition()
: base("index") : base("index", true)
{ {
} }
/// <summary>
/// Gets whether the tag only exists within the scope of its parent.
/// </summary>
protected override bool GetIsContextSensitive()
{
return true;
}
/// <summary> /// <summary>
/// Gets the text to output. /// Gets the text to output.
/// </summary> /// </summary>
/// <param name="writer">The writer to write the output to.</param> /// <param name="writer">The writer to write the output to.</param>
/// <param name="arguments">The arguments passed to the tag.</param> /// <param name="arguments">The arguments passed to the tag.</param>
/// <param name="contextData">Extra data passed along with the context.</param> /// <param name="contextScope">Extra data passed along with the context.</param>
public override void GetText(TextWriter writer, Dictionary<string, object> arguments, object contextData) public override void GetText(TextWriter writer, Dictionary<string, object> arguments, Scope contextScope)
{
object index;
if (contextScope.TryFind("index", out index))
{ {
int index = (int)contextData;
writer.Write(index); writer.Write(index);
} }
} }
} }
}

View File

@ -5,7 +5,7 @@ using System.IO;
namespace Mustache namespace Mustache
{ {
/// <summary> /// <summary>
/// Generates the text for a tag that only exists on a single line. /// Generates the text for a tag that is replaced with its generated text.
/// </summary> /// </summary>
internal sealed class InlineGenerator : IGenerator internal sealed class InlineGenerator : IGenerator
{ {
@ -23,10 +23,18 @@ namespace Mustache
_arguments = arguments; _arguments = arguments;
} }
void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData) void IGenerator.GetText(Scope scope, TextWriter writer, Scope context)
{ {
Dictionary<string, object> arguments = _arguments.GetArguments(scope); Dictionary<string, object> arguments;
_definition.GetText(writer, arguments, contextData); if (_definition.IsSetter)
{
arguments = _arguments.GetArguments();
}
else
{
arguments = _arguments.GetArguments(scope, context);
}
_definition.GetText(writer, arguments, context);
} }
} }
} }

View File

@ -42,7 +42,7 @@ namespace Mustache
return formatBuilder.ToString(); return formatBuilder.ToString();
} }
void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData) void IGenerator.GetText(Scope scope, TextWriter writer, Scope context)
{ {
object value = scope.Find(_key); object value = scope.Find(_key);
writer.Write(_format, value); writer.Write(_format, value);

View File

@ -19,36 +19,24 @@ namespace Mustache
/// Gets or sets the writer to use when generating the child context. /// Gets or sets the writer to use when generating the child context.
/// </summary> /// </summary>
/// <remarks>Setting the writer to null will indicate that the tag's writer should be used.</remarks> /// <remarks>Setting the writer to null will indicate that the tag's writer should be used.</remarks>
public TextWriter Writer public TextWriter Writer { get; set; }
{
get;
set;
}
/// <summary> /// <summary>
/// Gets or sets whether the text sent to the returned writer needs to be added /// 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 /// to the parent tag's writer. This should be false if the parent writer is
/// being returned or is being wrapped. /// being returned or is being wrapped.
/// </summary> /// </summary>
public bool WriterNeedsConsidated public bool WriterNeedsConsidated { get; set; }
{
get;
set;
}
/// <summary> /// <summary>
/// Gets or sets the scope to use when generating the child context. /// Gets or sets the key scope to use when generating the child context.
/// </summary> /// </summary>
/// <remarks>Setting the scope to null will indicate that the current scope should be used.</remarks> /// <remarks>Setting the scope to null will indicate that the current scope should be used.</remarks>
public KeyScope KeyScope public Scope KeyScope { get; set; }
{
get;
set;
}
/// <summary> /// <summary>
/// Gets or sets data associated with the context. /// Gets or sets data associated with the context.
/// </summary> /// </summary>
public object Data { get; set; } public Scope ContextScope { get; set; }
} }
} }

View File

@ -22,8 +22,8 @@ namespace Mustache
/// </summary> /// </summary>
/// <param name="writer">The writer to write the output to.</param> /// <param name="writer">The writer to write the output to.</param>
/// <param name="arguments">The arguments passed to the tag.</param> /// <param name="arguments">The arguments passed to the tag.</param>
/// <param name="contextData">Extra data passed along with the context.</param> /// <param name="context">Extra data passed along with the context.</param>
public override void GetText(TextWriter writer, Dictionary<string, object> arguments, object contextData) public override void GetText(TextWriter writer, Dictionary<string, object> arguments, Scope context)
{ {
writer.Write(Environment.NewLine); writer.Write(Environment.NewLine);
} }

View File

@ -8,7 +8,7 @@ namespace Mustache
/// </summary> /// </summary>
public static class RegexHelper public static class RegexHelper
{ {
private const string Key = @"[_\w][_\w\d]*"; internal const string Key = @"[_\w][_\w\d]*";
internal const string CompoundKey = Key + @"(\." + Key + ")*"; internal const string CompoundKey = Key + @"(\." + Key + ")*";
/// <summary> /// <summary>

View File

@ -9,16 +9,16 @@ namespace Mustache
/// <summary> /// <summary>
/// Represents a scope of keys. /// Represents a scope of keys.
/// </summary> /// </summary>
public sealed class KeyScope public sealed class Scope
{ {
private readonly object _source; private readonly object _source;
private readonly KeyScope _parent; private readonly Scope _parent;
/// <summary> /// <summary>
/// Initializes a new instance of a KeyScope. /// Initializes a new instance of a KeyScope.
/// </summary> /// </summary>
/// <param name="source">The object to search for keys in.</param> /// <param name="source">The object to search for keys in.</param>
internal KeyScope(object source) internal Scope(object source)
: this(source, null) : this(source, null)
{ {
} }
@ -28,7 +28,7 @@ namespace Mustache
/// </summary> /// </summary>
/// <param name="source">The object to search for keys in.</param> /// <param name="source">The object to search for keys in.</param>
/// <param name="parent">The parent scope to search in if the value is not found.</param> /// <param name="parent">The parent scope to search in if the value is not found.</param>
internal KeyScope(object source, KeyScope parent) internal Scope(object source, Scope parent)
{ {
_parent = parent; _parent = parent;
_source = source; _source = source;
@ -44,14 +44,23 @@ namespace Mustache
/// </summary> /// </summary>
public event EventHandler<KeyNotFoundEventArgs> KeyNotFound; public event EventHandler<KeyNotFoundEventArgs> KeyNotFound;
/// <summary>
/// Creates a child scope that searches for keys in a default dictionary of key/value pairs.
/// </summary>
/// <returns>The new child scope.</returns>
public Scope CreateChildScope()
{
return CreateChildScope(new Dictionary<string, object>());
}
/// <summary> /// <summary>
/// Creates a child scope that searches for keys in the given object. /// Creates a child scope that searches for keys in the given object.
/// </summary> /// </summary>
/// <param name="source">The object to search for keys in.</param> /// <param name="source">The object to search for keys in.</param>
/// <returns>The new child scope.</returns> /// <returns>The new child scope.</returns>
public KeyScope CreateChildScope(object source) public Scope CreateChildScope(object source)
{ {
KeyScope scope = new KeyScope(source, this); Scope scope = new Scope(source, this);
scope.KeyFound = KeyFound; scope.KeyFound = KeyFound;
scope.KeyNotFound = KeyNotFound; scope.KeyNotFound = KeyNotFound;
return scope; return scope;
@ -65,58 +74,45 @@ namespace Mustache
/// <exception cref="System.Collections.Generic.KeyNotFoundException">A key with the given name could not be found.</exception> /// <exception cref="System.Collections.Generic.KeyNotFoundException">A key with the given name could not be found.</exception>
internal object Find(string name) internal object Find(string name)
{ {
string[] names = name.Split('.'); string member = null;
string member = names[0]; object value = null;
object nextLevel = _source; if (tryFind(name, ref member, ref value))
if (member != "this")
{ {
nextLevel = find(name, member); onKeyFound(name, ref value);
return value;
} }
for (int index = 1; index < names.Length; ++index) if (onKeyNotFound(name, member, ref value))
{ {
IDictionary<string, object> context = toLookup(nextLevel); return value;
member = names[index]; }
if (!context.TryGetValue(member, out nextLevel)) string message = String.Format(CultureInfo.CurrentCulture, Resources.KeyNotFound, member);
throw new KeyNotFoundException(message);
}
private void onKeyFound(string name, ref object value)
{ {
nextLevel = handleKeyNotFound(name, member);
}
}
if (KeyFound != null) if (KeyFound != null)
{ {
KeyFoundEventArgs args = new KeyFoundEventArgs(name, nextLevel); KeyFoundEventArgs args = new KeyFoundEventArgs(name, value);
KeyFound(this, args); KeyFound(this, args);
nextLevel = args.Substitute; value = args.Substitute;
} }
return nextLevel;
} }
private object find(string fullName, string memberName) private bool onKeyNotFound(string name, string member, ref object value)
{ {
IDictionary<string, object> lookup = toLookup(_source); if (KeyNotFound == null)
if (lookup.ContainsKey(memberName))
{ {
return lookup[memberName]; return false;
} }
if (_parent == null) KeyNotFoundEventArgs args = new KeyNotFoundEventArgs(name, member);
{
return handleKeyNotFound(fullName, memberName);
}
return _parent.find(fullName, memberName);
}
private object handleKeyNotFound(string fullName, string memberName)
{
KeyNotFoundEventArgs args = new KeyNotFoundEventArgs(fullName, memberName);
if (KeyNotFound != null)
{
KeyNotFound(this, args); KeyNotFound(this, args);
} if (!args.Handled)
if (args.Handled)
{ {
return args.Substitute; return false;
} }
string message = String.Format(CultureInfo.CurrentCulture, Resources.KeyNotFound, memberName); value = args.Substitute;
throw new KeyNotFoundException(message); return true;
} }
private static IDictionary<string, object> toLookup(object value) private static IDictionary<string, object> toLookup(object value)
@ -128,5 +124,59 @@ namespace Mustache
} }
return lookup; return lookup;
} }
internal void Set(string key, object value)
{
IDictionary<string, object> lookup = toLookup(_source);
lookup[key] = value;
}
public bool TryFind(string name, out object value)
{
string member = null;
value = null;
return tryFind(name, ref member, ref value);
}
private bool tryFind(string name, ref string member, ref object value)
{
string[] names = name.Split('.');
member = names[0];
value = _source;
if (member != "this")
{
if (!tryFindFirst(member, ref value))
{
return false;
}
}
for (int index = 1; index < names.Length; ++index)
{
IDictionary<string, object> context = toLookup(value);
member = names[index];
if (!context.TryGetValue(member, out value))
{
value = null;
return false;
}
}
return true;
}
private bool tryFindFirst(string member, ref object value)
{
IDictionary<string, object> lookup = toLookup(_source);
if (lookup.ContainsKey(member))
{
value = lookup[member];
return true;
}
if (_parent == null)
{
value = null;
return false;
}
return _parent.tryFindFirst(member, ref value);
}
} }
} }

View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace Mustache
{
/// <summary>
/// Defines a tag that declares a named value in the current context.
/// </summary>
internal sealed class SetTagDefinition : InlineTagDefinition
{
private const string nameParameter = "name";
private static readonly TagParameter name = new TagParameter(nameParameter) { IsRequired = true };
/// <summary>
/// Initializes a new instance of an SetTagDefinition.
/// </summary>
public SetTagDefinition()
: base("set", true)
{
}
protected override bool GetIsSetter()
{
return true;
}
protected override IEnumerable<TagParameter> GetParameters()
{
return new TagParameter[] { name };
}
/// <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="contextScope">Extra data passed along with the context.</param>
public override void GetText(TextWriter writer, Dictionary<string, object> arguments, Scope contextScope)
{
string name = (string)arguments[nameParameter];
// TODO - get the value for the variable
object value = null;
contextScope.Set(name, value);
}
}
}

View File

@ -27,7 +27,7 @@ namespace Mustache
get { return value; } get { return value; }
} }
void IGenerator.GetText(KeyScope scope, TextWriter writer, object contextData) void IGenerator.GetText(Scope scope, TextWriter writer, Scope context)
{ {
writer.Write(Value); writer.Write(Value);
} }

View File

@ -44,6 +44,16 @@ namespace Mustache
get { return _tagName; } get { return _tagName; }
} }
internal bool IsSetter
{
get { return GetIsSetter(); }
}
protected virtual bool GetIsSetter()
{
return false;
}
/// <summary> /// <summary>
/// Gets whether the tag is limited to the parent tag's context. /// Gets whether the tag is limited to the parent tag's context.
/// </summary> /// </summary>
@ -139,12 +149,22 @@ namespace Mustache
/// Gets the context 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="writer">The text writer passed</param>
/// <param name="scope">The current scope.</param> /// <param name="keyScope">The current key 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<NestedContext> GetChildContext(TextWriter writer, KeyScope scope, Dictionary<string, object> arguments) public virtual IEnumerable<NestedContext> GetChildContext(
TextWriter writer,
Scope keyScope,
Dictionary<string, object> arguments,
Scope contextScope)
{ {
yield return new NestedContext() { KeyScope = scope, Writer = writer }; NestedContext context = new NestedContext()
{
KeyScope = keyScope,
Writer = writer,
ContextScope = contextScope.CreateChildScope()
};
yield return context;
} }
/// <summary> /// <summary>
@ -152,8 +172,8 @@ namespace Mustache
/// </summary> /// </summary>
/// <param name="writer">The text writer to write to.</param> /// <param name="writer">The text writer to write to.</param>
/// <param name="arguments">The arguments passed to the tag.</param> /// <param name="arguments">The arguments passed to the tag.</param>
/// <param name="contextData">The data associated to the context.</param> /// <param name="context">The data associated to the context.</param>
public virtual void GetText(TextWriter writer, Dictionary<string, object> arguments, object contextData) public virtual void GetText(TextWriter writer, Dictionary<string, object> arguments, Scope context)
{ {
} }

View File

@ -50,13 +50,23 @@ namespace Mustache
/// Gets the context 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="writer">The text writer passed</param>
/// <param name="scope">The current scope.</param> /// <param name="keyScope">The current key 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 override IEnumerable<NestedContext> GetChildContext(TextWriter writer, KeyScope scope, Dictionary<string, object> arguments) public override IEnumerable<NestedContext> GetChildContext(
TextWriter writer,
Scope keyScope,
Dictionary<string, object> arguments,
Scope contextScope)
{ {
object context = arguments[contextParameter]; object contextSource = arguments[contextParameter];
yield return new NestedContext() { KeyScope = scope.CreateChildScope(context), Writer = writer }; NestedContext context = new NestedContext()
{
KeyScope = keyScope.CreateChildScope(contextSource),
Writer = writer,
ContextScope = contextScope.CreateChildScope()
};
yield return context;
} }
} }
} }

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="SetTagDefinition.cs" />
<Compile Include="NewlineTagDefinition.cs" /> <Compile Include="NewlineTagDefinition.cs" />
<Compile Include="IndexTagDefinition.cs" /> <Compile Include="IndexTagDefinition.cs" />
<Compile Include="KeyFoundEventArgs.cs" /> <Compile Include="KeyFoundEventArgs.cs" />
@ -68,8 +69,8 @@
<Compile Include="StaticGenerator.cs" /> <Compile Include="StaticGenerator.cs" />
<Compile Include="TagDefinition.cs" /> <Compile Include="TagDefinition.cs" />
<Compile Include="TagParameter.cs" /> <Compile Include="TagParameter.cs" />
<Compile Include="KeyScope.cs" /> <Compile Include="Scope.cs" />
<Compile Include="WithGenerator.cs" /> <Compile Include="WithTagDefinition.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Properties\Resources.resx"> <EmbeddedResource Include="Properties\Resources.resx">