* Patch by Dokkie8844, better whitespace handling: fix issue #39557

This commit is contained in:
Michaël Van Canneyt 2022-02-17 14:10:06 +01:00
parent cec8d84ae7
commit b3e079c5da

View File

@ -202,11 +202,8 @@ Type
Procedure DoParse(aParent : TMustacheElement; Const aTemplate, aStart, aStop : TMustacheString); virtual;
// Called to get the template of a partial. The template is parsed, and the result added to the partials list.
Function GetPartial(const aName : TMustacheString) : TMustacheString; virtual;
// Auxuliary functions for the peculiar whitespace handling of Mustache specs...
function EndsOnWhiteSpace(aElement: TMustacheElement): Boolean; virtual;
function GetEndingWhiteSpace(aElement: TMustacheElement): TMustacheString; virtual;
// Extract new start/stop tag markers
procedure ExtractStartStop(const aName: TMustacheString; out aStart, aStop: TMustacheString); virtual;
procedure TrimEndingWhiteSpace(aElement: TMustacheElement); virtual;
Public
// Create a new parser.
Constructor Create(aTemplate : TMustacheString = '';aStart: TMustacheString='';aStop: TMustacheString = ''); virtual;
@ -961,55 +958,6 @@ begin
DoParse(aParent,FTemplate,StartTag, StopTag);
end;
function TMustacheParser.EndsOnWhiteSpace(aElement: TMustacheElement): Boolean;
Var
I : Integer;
S : TMustacheString;
begin
// if on standalone line, the entire line must be removed, see specs comments.standalone
Result:=(aElement.ElementType=metText);
s:=aElement.Data;
I:=Length(S);
While Result and (I>0) do
begin
if S[i] in [#13,#10] then
Break;
Result:=(S[I]=' ');
Dec(i);
end;
Result:=Result and ((I>0) or (aElement.Position=1));
end;
function TMustacheParser.GetEndingWhiteSpace(aElement: TMustacheElement): TMustacheString;
Var
S : TMustacheString;
I : Integer;
begin
s:=aElement.Data;
I:=Length(S);
While (I>0) and (S[I]=' ') do
Dec(i);
Result:=Copy(S,I+1);
end;
procedure TMustacheParser.TrimEndingWhiteSpace(aElement: TMustacheElement);
Var
I : Integer;
S : TMustacheString;
begin
s:=aElement.Data;
I:=Length(S);
While (I>0) and (S[I]=' ') do
Dec(i);
aElement.Data:=Copy(S,1,I);
end;
Function TMustacheParser.CreateDefault(aParent : TMustacheElement; aPosition : Integer;Const aName : String) : TMustacheElement;
begin
@ -1025,42 +973,74 @@ Var
aLen,clStop, lStart,lStop, NewPos, Current, Total : Integer;
aName,cStart,cStop,R : TMustacheString;
C: TMustacheChar;
IsWhiteSpace : Boolean;
Partial,WhiteSpaceEl : TMustacheELement;
Function CheckWhiteSpace : Boolean;
IsStandalone: Boolean;
PartialPrefix: TMustacheString;
Partial : TMustacheELement;
// check if the current tag is a tag that can occur standalone on a line
// if so, the whole line will be removed as per the specs
function IsStandaloneTag: Boolean;
begin
WhiteSpaceEl:=Nil;
With CurrParent do
begin
Result:=(ChildCount=0) or EndsOnWhiteSpace(Children[ChildCount-1]);
if Result and (ChildCount>0) then
WhiteSpaceEl:=Children[ChildCount-1];
end;
Result := (NewPos + lStart <= Total) and
(aTemplate[NewPos + lStart] in ['=','#','!','^','>','/']);
end;
Procedure FinishWhiteSpace(Full : Boolean = true);
Var
I : Integer;
function IsPartialTag: Boolean;
begin
I:=NewPos;
While IsWhiteSpace and (I+clStop<=Total) do
begin
C:=aTemplate[I+clStop];
if (C in [#13,#10]) then
Break;
isWhiteSpace:=aTemplate[I+clStop]=' ';
I:=I+1;
end;
if isWhiteSpace then
begin
While (I<=Total) and (aTemplate[I+clStop] in [#13,#10]) do
Inc(I);
NewPos:=I;
if Assigned(WhiteSpaceEl) and full then
TrimEndingWhiteSpace(WhiteSpaceEl);
end;
Result := (NewPos + lStart <= Total) and (aTemplate[NewPos + lStart] = '>');
end;
// check if the current tag occurs standalone on a line
function CheckStandalone: Boolean;
function LeftOnlyWhiteSpace: Boolean;
var
I: Integer;
begin
I := NewPos - 1;
while (I >= 1) and (aTemplate[I] = ' ') do
Dec(I);
Result := (I < 1) or (aTemplate[I] = #10);
end;
function RightOnlyWhiteSpace: Boolean;
var
I: Integer;
begin
I := Pos(cStop, aTemplate, NewPos + lStart);
if I = 0 then
Exit(False);
Inc(I, lStop);
while (I <= Total) and (aTemplate[I] = ' ') do
Inc(I);
Result := (I > Total) or (aTemplate[I] = #10) or
(Copy(aTemplate, I, 2) = #13#10);
end;
begin
Result := IsStandaloneTag and LeftOnlyWhiteSpace and RightOnlyWhiteSpace;
end;
function WhiteSpaceRightPos(const S: TMustacheString): Integer;
var
I: Integer;
begin
I := Length(S);
while (I > 0) and (S[I] = ' ') do
Dec(I);
Result := I + 1;
end;
procedure SkipRestOfLine;
var
EndOfLine: Integer;
begin
EndOfLine := Pos(#10, aTemplate, Current);
if EndOfLine = 0 then
Current := Total + 1
else
Current := EndOfLine + 1;
end;
begin
@ -1073,15 +1053,24 @@ begin
Total:=Length(aTemplate);
While (Current<=Total) do
begin
C:=Template[Current];
PartialPrefix := '';
NewPos:=Pos(cStart,aTemplate,Current);
IsStandalone := CheckStandalone;
if NewPos=0 then
NewPos:=Total+1;
// Stash what we have till now.
if NewPos>Current then
begin
R:=Copy(aTemplate,Current,NewPos-Current);
CreateElement(metText,currParent,Current).SetData(R);
if IsStandalone then
if IsPartialTag then
// keep R intact; copy trailing whitespace to PartialPrefix
PartialPrefix := Copy(R, WhiteSpaceRightPos(R))
else
// remove trailing whitespace from R
R := Copy(R, 1, WhiteSpaceRightPos(R) - 1);
if R <> '' then
CreateElement(metText,currParent,Current).SetData(R);
Current:=NewPos;
end;
if Current<Total then
@ -1100,14 +1089,9 @@ begin
case C of
'=' :
begin
IsWhiteSpace:=CheckWhiteSpace;
if IsWhiteSpace then
FinishWhiteSpace;
ExtractStartStop(aName,cStart,cStop);
lStart:=Length(cStart);
lStop:=Length(cStop);
//R:=Copy(aTemplate,newPos+clStop);
//Writeln(R);
end;
'{' :
begin
@ -1122,31 +1106,21 @@ begin
end;
'#' :
begin
IsWhiteSpace:=CheckWhiteSpace;
CurrParent:=CreateElement(metSection,currParent,Current);
CurrParent.SetData(aName);
if IsWhiteSpace then
FinishWhiteSpace;
end;
'!' :
begin
IsWhiteSpace:=CheckWhiteSpace;
CreateElement(metComment,currParent,Current).SetData(aName);
if IsWhiteSpace then
FinishWhiteSpace;
end;
'^' :
begin
IsWhiteSpace:=CheckWhiteSpace;
CurrParent:=CreateElement(metInvertedSection,currParent,Current);
CurrParent.SetData(aName);
if IsWhiteSpace then
FinishWhiteSpace;
end;
'>' :
begin
// Find or create compiled partial;
IsWhiteSpace:=CheckWhiteSpace;
aName:=Trim(aName);
if not Assigned(Partials) then
Raise EMustache.Create(SErrNoPartials);
@ -1162,28 +1136,24 @@ begin
begin
AddChild(Partial);
Data:=aName;
if isWhitespace and assigned(WhiteSpaceEl) then
Prefix:=GetEndingWhiteSpace(WhiteSpaceEl);
Prefix := PartialPrefix;
end;
if IsWhiteSpace then
FinishWhiteSpace(False);
end;
'/' :
begin
IsWhiteSpace:=CheckWhiteSpace;
if Not (CurrParent.ElementType in [metSection,metInvertedSection]) then
Raise EMustache.CreateFmt(SErrNoSectionToClose,[aName,Current])
else if (CurrParent.Data<>Trim(aName)) then
Raise EMustache.CreateFmt(SErrSectionClose,[currParent.Data,CurrParent.Position,aName,Current])
else
currParent:=currParent.Parent;
if IsWhiteSpace then
FinishWhiteSpace;
end
else
CreateDefault(CurrParent,Current,aName);
end;
Current:=NewPos+clStop;
if IsStandalone then
SkipRestOfLine;
end;
end;
if CurrParent<>aParent then