public override async ValueTask <Completion> WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context) { List <FluidValue> list = null; if (Member != null) { var member = await Member.EvaluateAsync(context); list = member.ToList(); } else if (Range != null) { int start = Convert.ToInt32((await Range.From.EvaluateAsync(context)).ToNumberValue()); int end = Convert.ToInt32((await Range.To.EvaluateAsync(context)).ToNumberValue()); // Cache range if (_rangeElements == null || _rangeStart != start || _rangeEnd != end) { _rangeElements = new List <FluidValue>(end - start); for (var i = start; i <= end; i++) { _rangeElements.Add(NumberValue.Create(i)); } list = _rangeElements; _rangeStart = start; _rangeEnd = end; } else { list = _rangeElements; } } if (list is null || list.Count == 0) { if (Else != null) { await Else.WriteToAsync(writer, encoder, context); } return(Completion.Normal); } // Apply options var startIndex = 0; if (Offset is not null) { var offset = (int)(await Offset.EvaluateAsync(context)).ToNumberValue(); startIndex = offset; } var count = Math.Max(0, list.Count - startIndex); if (Limit is not null) { var limit = (int)(await Limit.EvaluateAsync(context)).ToNumberValue(); count = Math.Min(count, limit); } if (count == 0) { if (Else != null) { await Else.WriteToAsync(writer, encoder, context); } return(Completion.Normal); } if (Reversed) { list.Reverse(startIndex, count); } try { var forloop = new ForLoopValue(); var length = forloop.Length = startIndex + count; context.SetValue("forloop", forloop); for (var i = startIndex; i < length; i++) { context.IncrementSteps(); var item = list[i]; context.SetValue(Identifier, item); // Set helper variables forloop.Index = i + 1; forloop.Index0 = i; forloop.RIndex = length - i - 1; forloop.RIndex0 = length - i; forloop.First = i == 0; forloop.Last = i == length - 1; Completion completion = Completion.Normal; for (var index = 0; index < _statements.Count; index++) { var statement = _statements[index]; completion = await statement.WriteToAsync(writer, encoder, context); // Restore the forloop property after every statement in case it replaced it, // for instance if it contains a nested for loop context.SetValue("forloop", forloop); if (completion != Completion.Normal) { // Stop processing the block statements break; } } if (completion == Completion.Continue) { // Go to next iteration continue; } if (completion == Completion.Break) { // Leave the loop break; } } } finally { context.LocalScope.Delete("forloop"); } return(Completion.Normal); }
public override async ValueTask <Completion> WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context) { var source = (await Source.EvaluateAsync(context)).Enumerate(context).ToList(); if (source.Count == 0) { if (Else != null) { await Else.WriteToAsync(writer, encoder, context); } return(Completion.Normal); } // Apply options var startIndex = 0; if (Offset is not null) { if (_isContinueOffset) { startIndex = (int)context.GetValue(_continueOffsetLiteral).ToNumberValue(); } else { var offset = (int)(await Offset.EvaluateAsync(context)).ToNumberValue(); startIndex = offset; } } var count = Math.Max(0, source.Count - startIndex); if (Limit is not null) { var limit = (int)(await Limit.EvaluateAsync(context)).ToNumberValue(); // Limit can be negative if (limit >= 0) { count = Math.Min(count, limit); } else { count = Math.Max(0, count + limit); } } if (count == 0) { if (Else != null) { await Else.WriteToAsync(writer, encoder, context); } return(Completion.Normal); } if (Reversed) { source.Reverse(startIndex, count); } context.EnterForLoopScope(); try { var forloop = new ForLoopValue(); var length = forloop.Length = startIndex + count; context.LocalScope._properties["forloop"] = forloop; for (var i = startIndex; i < length; i++) { context.IncrementSteps(); var item = source[i]; context.LocalScope._properties[Identifier] = item; // Set helper variables forloop.Index = i + 1; forloop.Index0 = i; forloop.RIndex = length - i - 1; forloop.RIndex0 = length - i; forloop.First = i == 0; forloop.Last = i == length - 1; if (_continueOffsetLiteral != null) { context.SetValue(_continueOffsetLiteral, forloop.Index); } Completion completion = Completion.Normal; for (var index = 0; index < _statements.Count; index++) { var statement = _statements[index]; completion = await statement.WriteToAsync(writer, encoder, context); //// Restore the forloop property after every statement in case it replaced it, //// for instance if it contains a nested for loop //context.LocalScope._properties["forloop"] = forloop; if (completion != Completion.Normal) { // Stop processing the block statements break; } } if (completion == Completion.Continue) { // Go to next iteration continue; } if (completion == Completion.Break) { // Leave the loop break; } } } finally { context.ReleaseScope(); } return(Completion.Normal); }
public override async ValueTask <Completion> WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context) { IEnumerable <FluidValue> elements = Array.Empty <FluidValue>(); if (Member != null) { var member = await Member.EvaluateAsync(context); elements = member.Enumerate(); } else if (Range != null) { int start = Convert.ToInt32((await Range.From.EvaluateAsync(context)).ToNumberValue()); int end = Convert.ToInt32((await Range.To.EvaluateAsync(context)).ToNumberValue()); // Cache range if (_rangeElements == null || _rangeStart != start || _rangeEnd != end) { _rangeElements = new List <FluidValue>(); for (var i = start; i <= end; i++) { _rangeElements.Add(NumberValue.Create(i)); } elements = _rangeElements; _rangeStart = start; _rangeEnd = end; } else { elements = _rangeElements; } } if (!elements.Any()) { return(Completion.Normal); } // Apply options if (Offset != null) { var offset = (int)(await Offset.EvaluateAsync(context)).ToNumberValue(); elements = elements.Skip(offset); } if (Limit != null) { var limit = (int)(await Limit.EvaluateAsync(context)).ToNumberValue(); elements = elements.Take(limit); } if (Reversed) { elements = elements.Reverse(); } var list = elements.ToList(); if (!list.Any()) { return(Completion.Normal); } // Selz: We need to keep the original value for the identifier so that // after the for statement it will not be override var originalValue = context.GetValue(Identifier); try { var forloop = new ForLoopValue(); var length = forloop.Length = list.Count; context.SetValue("forloop", forloop); for (var i = 0; i < length; i++) { context.IncrementSteps(); var item = list[i]; context.SetValue(Identifier, item); // Set helper variables forloop.Index = i + 1; forloop.Index0 = i; forloop.RIndex = length - i - 1; forloop.RIndex0 = length - i; forloop.First = i == 0; forloop.Last = i == length - 1; Completion completion = Completion.Normal; for (var index = 0; index < Statements.Count; index++) { var statement = Statements[index]; completion = await statement.WriteToAsync(writer, encoder, context); // Restore the forloop property after every statement in case it replaced it, // for instance if it contains a nested for loop context.SetValue("forloop", forloop); if (completion != Completion.Normal) { // Stop processing the block statements break; } } if (completion == Completion.Continue) { // Go to next iteration continue; } if (completion == Completion.Break) { // Leave the loop break; } } } finally { context.LocalScope.Delete("forloop"); context.SetValue(Identifier, originalValue); } return(Completion.Normal); }
public override async ValueTask <Completion> WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context) { context.IncrementSteps(); var relativePath = (await Path.EvaluateAsync(context)).ToStringValue(); if (!relativePath.EndsWith(ViewExtension, StringComparison.OrdinalIgnoreCase)) { relativePath += ViewExtension; } if (_cachedTemplate == null || !string.Equals(_cachedTemplate.Name, System.IO.Path.GetFileNameWithoutExtension(relativePath), StringComparison.Ordinal)) { await _semaphore.WaitAsync(); try { if (_cachedTemplate == null || !string.Equals(_cachedTemplate.Name, System.IO.Path.GetFileNameWithoutExtension(relativePath), StringComparison.Ordinal)) { var fileProvider = context.Options.FileProvider; var fileInfo = fileProvider.GetFileInfo(relativePath); if (fileInfo == null || !fileInfo.Exists) { throw new FileNotFoundException(relativePath); } var content = ""; using (var stream = fileInfo.CreateReadStream()) using (var streamReader = new StreamReader(stream)) { content = await streamReader.ReadToEndAsync(); } if (!_parser.TryParse(content, out var template, out var errors)) { throw new ParseException(errors); } var identifier = System.IO.Path.GetFileNameWithoutExtension(relativePath); _cachedTemplate = new CachedTemplate(template, identifier); } } finally { _semaphore.Release(); } } try { context.EnterChildScope(); if (With != null) { var with = await With.EvaluateAsync(context); context.SetValue(Alias ?? _cachedTemplate.Name, with); await _cachedTemplate.Template.RenderAsync(writer, encoder, context); } else if (AssignStatements != null) { var length = AssignStatements.Count; for (var i = 0; i < length; i++) { await AssignStatements[i].WriteToAsync(writer, encoder, context); } await _cachedTemplate.Template.RenderAsync(writer, encoder, context); } else if (For != null) { try { var forloop = new ForLoopValue(); var list = (await For.EvaluateAsync(context)).Enumerate(context).ToList(); var length = forloop.Length = list.Count; context.SetValue("forloop", forloop); for (var i = 0; i < length; i++) { context.IncrementSteps(); var item = list[i]; context.SetValue(Alias ?? _cachedTemplate.Name, item); // Set helper variables forloop.Index = i + 1; forloop.Index0 = i; forloop.RIndex = length - i - 1; forloop.RIndex0 = length - i; forloop.First = i == 0; forloop.Last = i == length - 1; await _cachedTemplate.Template.RenderAsync(writer, encoder, context); // Restore the forloop property after every statement in case it replaced it, // for instance if it contains a nested for loop context.SetValue("forloop", forloop); } } finally { context.LocalScope.Delete("forloop"); } } else { // no with, for or assignments, e.g. {% include 'products' %} await _cachedTemplate.Template.RenderAsync(writer, encoder, context); } } finally { context.ReleaseScope(); } return(Completion.Normal); }