Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve TextSpan and For statements #668

Merged
merged 4 commits into from
May 30, 2024
Merged
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
6 changes: 3 additions & 3 deletions Fluid/Ast/ForStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ namespace Fluid.Ast
{
public sealed class ForStatement : TagStatement
{
private readonly bool _isContinueOffset;
private readonly string _continueOffsetLiteral;

public ForStatement(
Expand All @@ -25,7 +24,7 @@ public ForStatement(
Reversed = reversed;
Else = elseStatement;

_isContinueOffset = Offset is MemberExpression l && l.Segments.Count == 1 && ((IdentifierSegment)l.Segments[0]).Identifier == "continue";
OffsetIsContinue = Offset is MemberExpression l && l.Segments.Count == 1 && ((IdentifierSegment)l.Segments[0]).Identifier == "continue";
_continueOffsetLiteral = source is MemberExpression m ? "for_continue_" + ((IdentifierSegment)m.Segments[0]).Identifier : null;
}

Expand All @@ -36,6 +35,7 @@ public ForStatement(
public Expression Offset { get; }
public bool Reversed { get; }
public ElseStatement Else { get; }
public bool OffsetIsContinue { get; }

public override async ValueTask<Completion> WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
{
Expand All @@ -55,7 +55,7 @@ public override async ValueTask<Completion> WriteToAsync(TextWriter writer, Text
var startIndex = 0;
if (Offset is not null)
{
if (_isContinueOffset)
if (OffsetIsContinue)
{
startIndex = (int)context.GetValue(_continueOffsetLiteral).ToNumberValue();
}
Expand Down
182 changes: 96 additions & 86 deletions Fluid/Ast/TextSpanStatement.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
using Parlot;
using Fluid.Utils;
using Parlot;
using System.Text.Encodings.Web;
using Fluid.Utils;

namespace Fluid.Ast
{
public sealed class TextSpanStatement : Statement
{
private bool _isStripped;
private bool _isEmpty;
private bool _isBufferPrepared;
private readonly object _synLock = new();
private TextSpan _text;
private string _buffer;
internal string _preparedBuffer;
sebastienros marked this conversation as resolved.
Show resolved Hide resolved

public TextSpanStatement(in TextSpan text)
{
Expand All @@ -32,115 +31,128 @@ public TextSpanStatement(string text)

public ref readonly TextSpan Text => ref _text;

public override ValueTask<Completion> WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
public string Buffer => _preparedBuffer;

public void PrepareBuffer(TemplateOptions options)
{
if (!_isStripped)
if (_isBufferPrepared)
{
return;
}

// Prevent two threads from stripping the same statement in case WriteToAsync is called concurrently
lock (_synLock)
{
// Prevent two threads from strsipping the same statement in case WriteToAsync is called concurrently
//
lock (_synLock)
if (!_isBufferPrepared)
{
if (!_isStripped)
var trimming = options.Trimming;
StripLeft |=
(PreviousIsTag && (trimming & TrimmingFlags.TagRight) != 0) ||
(PreviousIsOutput && (trimming & TrimmingFlags.OutputRight) != 0)
;

StripRight |=
(NextIsTag && (trimming & TrimmingFlags.TagLeft) != 0) ||
(NextIsOutput && (trimming & TrimmingFlags.OutputLeft) != 0)
;

var span = _text.Buffer;
var start = 0;
var end = _text.Length - 1;

// Does this text need to have its left part trimmed?
if (StripLeft)
{
var trimming = context.Options.Trimming;
StripLeft |=
(PreviousIsTag && (trimming & TrimmingFlags.TagRight) != 0) ||
(PreviousIsOutput && (trimming & TrimmingFlags.OutputRight) != 0)
;

StripRight |=
(NextIsTag && (trimming & TrimmingFlags.TagLeft) != 0) ||
(NextIsOutput && (trimming & TrimmingFlags.OutputLeft) != 0)
;

var span = _text.Buffer;
var start = 0;
var end = _text.Length - 1;

// Does this text need to have its left part trimmed?
if (StripLeft)
var firstNewLine = -1;

for (var i = start; i <= end; i++)
{
var firstNewLine = -1;
var c = span[_text.Offset + i];

for (var i = start; i <= end; i++)
if (Character.IsWhiteSpaceOrNewLine(c))
{
var c = span[_text.Offset + i];
start++;

if (Character.IsWhiteSpaceOrNewLine(c))
if (firstNewLine == -1 && (c == '\n'))
{
start++;

if (firstNewLine == -1 && (c == '\n'))
{
firstNewLine = start;
}
}
else
{
break;
firstNewLine = start;
}
}

if (!context.Options.Greedy)
else
{
if (firstNewLine != -1)
{
start = firstNewLine;
}
break;
}
}

// Does this text need to have its right part trimmed?
if (StripRight)
if (!options.Greedy)
{
var lastNewLine = -1;

for (var i = end; i >= start; i--)
if (firstNewLine != -1)
{
var c = span[_text.Offset + i];
start = firstNewLine;
}
}
}

if (Character.IsWhiteSpaceOrNewLine(c))
{
if (lastNewLine == -1 && c == '\n')
{
lastNewLine = end;
}
// Does this text need to have its right part trimmed?
if (StripRight)
{
var lastNewLine = -1;

end--;
}
else
{
break;
}
}
for (var i = end; i >= start; i--)
{
var c = span[_text.Offset + i];

if (!context.Options.Greedy)
if (Character.IsWhiteSpaceOrNewLine(c))
{
if (lastNewLine != -1)
if (lastNewLine == -1 && c == '\n')
{
end = lastNewLine;
lastNewLine = end;
}

end--;
}
else
{
break;
}
}
if (end - start + 1 == 0)
{
_isEmpty = true;
}
else if (start != 0 || end != _text.Length - 1)
{
var offset = _text.Offset;
var buffer = _text.Buffer;

_text = new TextSpan(buffer, offset + start, end - start + 1);
if (!options.Greedy)
{
if (lastNewLine != -1)
{
end = lastNewLine;
}
}
}
if (end - start + 1 == 0)
{
_text = "";
}
else if (start != 0 || end != _text.Length - 1)
{
var offset = _text.Offset;
var buffer = _text.Buffer;

_buffer = _text.ToString();
_isStripped = true;
_text = new TextSpan(buffer, offset + start, end - start + 1);
}

_preparedBuffer = _text.ToString();
_isBufferPrepared = true;
}
}
}

protected internal override Statement Accept(AstVisitor visitor) => visitor.VisitTextSpanStatement(this);

if (_isEmpty)
public override ValueTask<Completion> WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
{
if (!_isBufferPrepared)
{
PrepareBuffer(context.Options);
}

if (_preparedBuffer == "")
{
return new ValueTask<Completion>(Completion.Normal);
}
Expand All @@ -157,15 +169,13 @@ static async ValueTask<Completion> Awaited(Task task)
return Completion.Normal;
}

var task = writer.WriteAsync(_buffer);
var task = writer.WriteAsync(_preparedBuffer);
if (!task.IsCompletedSuccessfully())
{
return Awaited(task);
}

return new ValueTask<Completion>(Completion.Normal);
}

protected internal override Statement Accept(AstVisitor visitor) => visitor.VisitTextSpanStatement(this);
}
}
}
1 change: 1 addition & 0 deletions Fluid/Values/FluidValues.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ public enum FluidValues
Object = 7,
String = 8,
DateTime = 9,
Function = 10,
}
}
2 changes: 1 addition & 1 deletion Fluid/Values/FunctionValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public FunctionValue(Func<FunctionArguments, TemplateContext, FluidValue> action
_action = (args, c) => new ValueTask<FluidValue>(action(args, c));
}

public override FluidValues Type => FluidValues.Object;
public override FluidValues Type => FluidValues.Function;

public override ValueTask<FluidValue> InvokeAsync(FunctionArguments arguments, TemplateContext context)
{
Expand Down