Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions Nustache.Core.Tests/Describe_Helpers.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System.Collections;
using NUnit.Framework;
using NUnit.Framework;
using System.Collections;

namespace Nustache.Core.Tests
{
Expand Down Expand Up @@ -72,7 +72,7 @@ public void It_passes_arguments_into_block_helpers()
}

[Test]
public void It_parses_quoted_arguments_as_literal_strings()
public void It_parses_double_quoted_arguments_as_literal_strings()
{
Helpers.Register("link", (ctx, args, opts, fn, inverse) => ctx.Write(string.Format("<a href=\"{0}\">{1}</a>", args[1], args[0])));

Expand All @@ -81,6 +81,16 @@ public void It_parses_quoted_arguments_as_literal_strings()
Assert.AreEqual("<a href=\"URL\">TEXT</a>", result);
}

[Test]
public void It_parses_single_quoted_arguments_as_literal_strings()
{
Helpers.Register("link", (ctx, args, opts, fn, inverse) => ctx.Write(string.Format("<a href=\"{0}\">{1}</a>", args[1], args[0])));

var result = Render.StringToString("{{link 'TEXT' 'URL'}}", new {});

Assert.AreEqual("<a href=\"URL\">TEXT</a>", result);
}

[Test]
public void It_parses_quoted_options_as_literal_strings()
{
Expand All @@ -91,6 +101,16 @@ public void It_parses_quoted_options_as_literal_strings()
Assert.AreEqual("<a href=\"URL\">TEXT</a>", result);
}

[Test]
public void It_parses_quoted_options_with_spaces_as_literal_strings()
{
Helpers.Register("link", (ctx, args, opts, fn, inverse) => ctx.Write(string.Format("<a href=\"{0}\">{1}</a>", opts["url"], opts["text"])));

var result = Render.StringToString("{{link text=\"ANCHOR TEXT\" url=\"URL\"}}", new { });

Assert.AreEqual("<a href=\"URL\">ANCHOR TEXT</a>", result);
}

[Test]
public void It_registers_each_by_default()
{
Expand Down
92 changes: 54 additions & 38 deletions Nustache.Core/Helpers.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.Collections;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace Nustache.Core
{
Expand Down Expand Up @@ -43,7 +43,7 @@ static Helpers()

public static void EachHelper(RenderContext context, IList<object> arguments, IDictionary<string, object> options, RenderBlock fn, RenderBlock inverse)
{
foreach (var item in (IEnumerable) arguments[0])
foreach (var item in (IEnumerable)arguments[0])
{
fn(item);
}
Expand Down Expand Up @@ -73,6 +73,18 @@ public static void WithHelper(RenderContext context, IList<object> arguments, ID
fn(arguments[0]);
}

private static IEnumerable<string> ExtractCaptureValues(MatchCollection matches, string groupName)
{
foreach (var match in matches)
{
var m = match as Match;
if (m.Groups[groupName].Captures.Count != 0)
{
yield return m.Value;
}
}
}

public static void Parse(RenderContext ctx, string path, out string name, out IList<object> arguments, out IDictionary<string, object> options)
{
name = path;
Expand All @@ -81,57 +93,59 @@ public static void Parse(RenderContext ctx, string path, out string name, out IL

if (path.Contains(" "))
{
var splits = path.Split();
name = splits[0];
ParseArguments(ctx, splits, out arguments);
ParseOptions(ctx, splits, out options);
// unescaped: (?<option>\S+=(?:["'][^"']+["']|\S+))|(?<argument>["'][^"']+["']|\S+)
const string pattern = "(?<option>\\S+=(?:[\"'][^\"']+[\"']|\\S+))|(?<argument>[\"'][^\"']+[\"']|\\S+)";
var tokens = Regex.Matches(path, pattern);
var argTokens = ExtractCaptureValues(tokens, "argument");
var optTokens = ExtractCaptureValues(tokens, "option");
name = ParseName(argTokens);
arguments = ParseArguments(ctx, argTokens);
options = ParseOptions(ctx, optTokens);
}
}

private static void ParseArguments(RenderContext ctx, string[] splits, out IList<object> arguments)
private static string ParseName(IEnumerable<string> tokens) {
var e = tokens.GetEnumerator();
e.MoveNext();
return e.Current;
}

private static IList<object> ParseArguments(RenderContext ctx, IEnumerable<string> tokens)
{
arguments = null;
var e = tokens.GetEnumerator();
e.MoveNext(); //Skip first argument as it's the helper name.

if (splits.Length > 1)
{
arguments = new List<object>(splits).GetRange(1, splits.Length - 1);
var arguments = new List<object>();

for (var i = 0; i < arguments.Count; i++)
while (e.MoveNext())
{
var value = e.Current;
if (value[0] == '"' || value[0] == '\'')
{
var arg = (string)arguments[i];

if (arg[0] == '"')
{
arguments[i] = arg.Substring(1, arg.Length - 2);
}
else
{
arguments[i] = ctx.GetValue(arg);
}
arguments.Add(value.Substring(1, value.Length - 2));
}
else
{
arguments.Add(ctx.GetValue(value));
}
}

return arguments;
}

private static void ParseOptions(RenderContext ctx, string[] splits, out IDictionary<string, object> options)
private static IDictionary<string, object> ParseOptions(RenderContext ctx, IEnumerable<string> tokens)
{
options = null;
var options = new Dictionary<string, object>();

for (var i = 0; i < splits.Length; i++)
foreach (var token in tokens)
{
var arg = splits[i];

if (arg.Contains("="))
if (token.Contains("="))
{
if (options == null)
{
options = new Dictionary<string, object>();
}

var splits2 = arg.Split(new[] { '=' }, 2);
var key = splits2[0];
var val = splits2[1];
var split = token.Split(new[] { '=' }, 2);
var key = split[0];
var val = split[1];

if (val[0] == '"')
if (val[0] == '"' || val[0] == '\'')
{
options[key] = val.Substring(1, val.Length - 2);
}
Expand All @@ -141,6 +155,8 @@ private static void ParseOptions(RenderContext ctx, string[] splits, out IDictio
}
}
}

return options;
}
}
}