Skip to content

Commit 0b50a46

Browse files
authored
Merge pull request #206 from sempare/whitespace
Simpler whitespace support #147
2 parents 29c830f + a5d2b25 commit 0b50a46

17 files changed

+1469
-1004
lines changed

demo/SempareTemplatePlayground/Sempare.Template.PlaygroundForm.pas

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,13 @@ TFormTemplateEnginePlayground = class(TForm)
136136
procedure butExtractVarsClick(Sender: TObject);
137137
private
138138
{ Private declarations }
139+
FLastContent: string;
139140
FEncoding: TEncoding;
140141
FContext: ITemplateContext;
141142
FTemplate: ITemplate;
142143
FFilename: string;
143-
Finit: boolean;
144+
FInit: boolean;
145+
FForce: boolean;
144146
procedure Process;
145147
procedure GridPropsToContext;
146148
procedure WriteTmpHtml;
@@ -248,21 +250,29 @@ procedure TFormTemplateEnginePlayground.butSaveClick(Sender: TObject);
248250
procedure TFormTemplateEnginePlayground.cbConvertTabsToSpacesClick(Sender: TObject);
249251
begin
250252
SetOption(cbConvertTabsToSpaces.Checked, eoConvertTabsToSpaces);
253+
FForce := true;
254+
Eval;
251255
end;
252256

253257
procedure TFormTemplateEnginePlayground.cbEvalEarlyClick(Sender: TObject);
254258
begin
255259
SetOption(cbEvalEarly.Checked, eoEvalEarly);
260+
FForce := true;
261+
Eval;
256262
end;
257263

258264
procedure TFormTemplateEnginePlayground.cbEvalVarsEarlyClick(Sender: TObject);
259265
begin
260266
SetOption(cbEvalVarsEarly.Checked, eoEvalVarsEarly);
267+
FForce := true;
268+
Eval;
261269
end;
262270

263271
procedure TFormTemplateEnginePlayground.cbFlattenTemplateClick(Sender: TObject);
264272
begin
265273
SetOption(cbFlattenTemplate.Checked, eoFlattenTemplate);
274+
FForce := true;
275+
Eval;
266276
end;
267277

268278
function DefaultEncoder(const AValue: string): string;
@@ -276,6 +286,7 @@ procedure TFormTemplateEnginePlayground.cbHtmlClick(Sender: TObject);
276286
FContext.UseHtmlVariableEncoder
277287
else
278288
FContext.VariableEncoder := DefaultEncoder;
289+
FForce := true;
279290
Eval;
280291
end;
281292

@@ -284,26 +295,36 @@ procedure TFormTemplateEnginePlayground.cbOptimiseTemplateClick(Sender: TObject)
284295
SetOption(cbOptimiseTemplate.Checked, eoOptimiseTemplate);
285296
if cbOptimiseTemplate.Checked then
286297
cbFlattenTemplate.Checked := true;
298+
FForce := true;
299+
Eval;
287300
end;
288301

289302
procedure TFormTemplateEnginePlayground.cbRaiseErrorWhenVariableNotFoundClick(Sender: TObject);
290303
begin
291304
SetOption(cbRaiseErrorWhenVariableNotFound.Checked, eoRaiseErrorWhenVariableNotFound);
305+
FForce := true;
306+
Eval;
292307
end;
293308

294309
procedure TFormTemplateEnginePlayground.cbStripRecurringNewlinesClick(Sender: TObject);
295310
begin
296311
SetOption(cbStripRecurringNewlines.Checked, eoStripRecurringNewlines);
312+
FForce := true;
313+
Eval;
297314
end;
298315

299316
procedure TFormTemplateEnginePlayground.cbStripRecurringSpacesClick(Sender: TObject);
300317
begin
301318
SetOption(cbStripRecurringSpaces.Checked, eoStripRecurringSpaces);
319+
FForce := true;
320+
Eval;
302321
end;
303322

304323
procedure TFormTemplateEnginePlayground.cbTrimLinesClick(Sender: TObject);
305324
begin
306325
SetOption(cbTrimLines.Checked, eoTrimLines);
326+
FForce := true;
327+
Eval;
307328
end;
308329

309330
procedure TFormTemplateEnginePlayground.cbUseCustomScriptTagsClick(Sender: TObject);
@@ -320,12 +341,15 @@ procedure TFormTemplateEnginePlayground.cbUseHtmlBRClick(Sender: TObject);
320341
FContext.NewLine := '<br>'#13#10
321342
else
322343
FContext.NewLine := #13#10;
344+
FForce := true;
345+
Eval;
323346
end;
324347

325348
procedure TFormTemplateEnginePlayground.cmbCustomScriptTagsChange(Sender: TObject);
326349
begin
327350
cbUseCustomScriptTags.Checked := true;
328351
SetScriptTags(cmbCustomScriptTags.ItemIndex);
352+
FForce := true;
329353
Eval;
330354
end;
331355

@@ -336,7 +360,13 @@ procedure TFormTemplateEnginePlayground.Eval;
336360
try
337361
if FFilename <> '' then
338362
butSave.Enabled := true;
339-
FTemplate := Template.Parse(FContext, memoTemplate.Lines.Text);
363+
364+
if FForce or (FLastContent <> memoTemplate.Lines.Text) then
365+
begin
366+
FTemplate := Template.Parse(FContext, memoTemplate.Lines.Text);
367+
FLastContent := memoTemplate.Lines.Text;
368+
end;
369+
340370
Process;
341371
// this is a hack so that app does not throw an exception
342372
// during shutdown. it seems that the webbrowser must be visible
@@ -366,15 +396,22 @@ procedure TFormTemplateEnginePlayground.cbSetEncodingClick(Sender: TObject);
366396
end
367397
else
368398
FEncoding := TEncoding.UTF8WithoutBOM;
399+
FForce := true;
369400
Eval;
370401
end;
371402

372403
procedure TFormTemplateEnginePlayground.cbShowWhitespaceClick(Sender: TObject);
373404
begin
374405
if cbShowWhitespace.Checked then
375-
FContext.WhitespaceChar := #183
406+
begin
407+
if cbHtml.Checked then
408+
FContext.WhitespaceChar := '&bull;'
409+
else
410+
FContext.WhitespaceChar := #183
411+
end
376412
else
377413
FContext.WhitespaceChar := ' ';
414+
FForce := true;
378415
Eval;
379416
end;
380417

@@ -396,7 +433,7 @@ procedure TFormTemplateEnginePlayground.FormCreate(Sender: TObject);
396433
WebBrowser1.Enabled := true;
397434
tsGithubHelp.TabVisible := false;
398435
wbHelp.Enabled := false; // Doesn't work on github at this stage
399-
// wbHelp.Navigate('https://github.com/sempare/sempare-delphi-template-engine#Introduction');
436+
// wbHelp.Navigate('https://github.com/sempare/sempare-delphi-template-engine#Introduction');
400437
{$IF defined(RELEASE)}
401438
FContext.MaxRunTimeMs := 5000;
402439
{$ENDIF}
@@ -453,7 +490,7 @@ procedure TFormTemplateEnginePlayground.FormCreate(Sender: TObject);
453490
' ' + #13#10 + //
454491
' If you like this project, please consider supporting enhancements via a commercial license which also entitles you to priority support.<p> ' + #13#10;
455492

456-
Finit := true;
493+
FInit := true;
457494
end;
458495

459496
procedure TFormTemplateEnginePlayground.GridPropsToContext;
@@ -519,7 +556,7 @@ procedure TFormTemplateEnginePlayground.Process;
519556
end;
520557

521558
begin
522-
if not Finit then
559+
if not FInit then
523560
exit;
524561
GridPropsToContext;
525562
LPrettyOk := false;

src/Sempare.Template.AST.pas

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,7 @@ ETemplate = class(Exception);
5656

5757
TStripAction = ( //
5858
saWhitespace, //
59-
saNL, //
60-
saKeepOneSpace //
59+
saNL //
6160
);
6261

6362
TStripActionSet = set of TStripAction;
@@ -150,10 +149,8 @@ ETemplate = class(Exception);
150149
vsBlock, //
151150

152151
vsNewLine, //
153-
vsWhiteSpace, //
152+
vsWhiteSpace //
154153

155-
vsSingleton, //
156-
vsValidate //
157154
);
158155

159156
TTemplateSymbolSet = set of TTemplateSymbol;
@@ -201,9 +198,13 @@ ETemplate = class(Exception);
201198
['{8C539211-ED84-4963-B894-C569C2F7B2FE}']
202199
end;
203200

201+
TParserOption = (poAllowEnd, poAllowElse, poAllowElIf, poHasElse, poInLoop, poStripNL, poStripWS, poStripRecurringNL);
202+
TParserOptions = set of TParserOption;
203+
204204
IStmt = interface(ITemplateVisitorHost)
205205
['{6D37028E-A0C0-41F1-8A59-EDC0C9ADD9C7}']
206206
function Flatten: TArray<IStmt>;
207+
procedure OptimiseTemplate(const AOptions: TParserOptions; const ANewLine: string);
207208
function GetHasEnd: boolean;
208209
property HasEnd: boolean read GetHasEnd;
209210
end;
@@ -214,22 +215,19 @@ ETemplate = class(Exception);
214215
property Stmt: IStmt read GetStmt;
215216
end;
216217

217-
TParserOption = (poAllowEnd, poAllowElse, poAllowElIf, poHasElse, poInLoop, poStripNL, poStripWS);
218-
TParserOptions = set of TParserOption;
219-
220218
ITemplate = interface(ITemplateVisitorHost)
221219
['{93AAB971-5B4B-4959-93F2-6C7DAE15C91B}']
222220
function GetItem(const AOffset: integer): IStmt;
223221
function GetCount: integer;
224222
function GetLastItem: IStmt;
225223
procedure FlattenTemplate;
226-
procedure OptimiseTemplate(const AOptions: TParserOptions);
224+
procedure OptimiseTemplate(const AOptions: TParserOptions; const ANewLine: string);
227225
property Items[const AOffset: integer]: IStmt read GetItem;
228226
property Count: integer read GetCount;
229227
property LastItem: IStmt read GetLastItem;
230228
end;
231229

232-
TAddLocation = (alLast, alBeforeNL, alAfterNL);
230+
TAddLocation = (alLast, alFront);
233231

234232
ITemplateAdd = interface(ITemplate)
235233
['{64465D68-0E9D-479F-9EF3-A30E75967809}']
@@ -260,7 +258,7 @@ ETemplate = class(Exception);
260258
['{FB4CC3AB-BFEC-4189-B555-153DDA490D15}']
261259
end;
262260

263-
TStripDirection = (sdEnd, sdLeft, sdRight, sdBeforeNewLine, sdAfterNewLine);
261+
TStripDirection = (sdLeft, sdRight);
264262

265263
IStripStmt = interface(IStmt)
266264
['{3313745B-D635-4453-9808-660DC462E15C}']
@@ -369,6 +367,16 @@ ETemplate = class(Exception);
369367
property Container: ITemplate read GetContainer;
370368
end;
371369

370+
IIgnoreNLStmt = interface(IStmt)
371+
function GetContainer: ITemplate;
372+
property Container: ITemplate read GetContainer;
373+
end;
374+
375+
IIgnoreWSStmt = interface(IStmt)
376+
function GetContainer: ITemplate;
377+
property Container: ITemplate read GetContainer;
378+
end;
379+
372380
ILoopStmt = interface(IStmt)
373381
['{D6C26A41-3250-4EB9-A776-8952DE3931BD}']
374382
function GetOnBeginContainer: ITemplate;
@@ -580,7 +588,10 @@ ETemplate = class(Exception);
580588
procedure Visit(const AStmt: IBlockStmt); overload;
581589
procedure Visit(const AStmt: IExtendsStmt); overload;
582590
procedure Visit(const AStmt: ICompositeStmt); overload;
591+
procedure Visit(const AStmt: INoopStmt); overload;
583592
procedure Visit(const AStmt: IStripStmt); overload;
593+
procedure Visit(const AStmt: IIgnoreNLStmt); overload;
594+
procedure Visit(const AStmt: IIgnoreWSStmt); overload;
584595
end;
585596

586597
IEvaluationTemplateVisitor = interface(ITemplateVisitor)
@@ -614,6 +625,7 @@ TAbstractStmt = class abstract(TAbstractBase, IStmt)
614625
protected
615626
function Flatten: TArray<IStmt>; virtual;
616627
function GetHasEnd: boolean; virtual;
628+
procedure OptimiseTemplate(const AOptions: TParserOptions; const ANewLine: string); virtual;
617629
public
618630
constructor Create(const APosition: IPosition);
619631
end;
@@ -632,12 +644,11 @@ TMapExpr = class(TAbstractExpr, IMapExpr)
632644

633645
const
634646
StripDirectionStr: array [TStripDirection] of string = ( //
635-
'sdEnd', 'sdLeft', 'sdRight', 'sdBeforeNewLine', 'sdAfterNewLine');
647+
'sdLeft', 'sdRight' //
648+
);
636649

637650
StripActionStr: array [TStripAction] of string = ( //
638-
'saWhitespace', //
639-
'saNL', //
640-
'saKeepOneSpace' //
651+
'saWhitespace', 'saNL' //
641652
);
642653

643654
type
@@ -744,4 +755,9 @@ function TAbstractStmt.GetHasEnd: boolean;
744755
exit(false);
745756
end;
746757

758+
procedure TAbstractStmt.OptimiseTemplate(const AOptions: TParserOptions; const ANewLine: string);
759+
begin
760+
761+
end;
762+
747763
end.

src/Sempare.Template.Context.pas

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,8 @@ interface
169169
procedure SetPrettyPrintOutput(const APrettyPrintOutput: TPrettyPrintOutput);
170170
function GetPrettyPrintOutput: TPrettyPrintOutput;
171171

172-
function GetWhitespace: char;
173-
procedure SetWhiteSpace(const AWS: char);
172+
function GetWhitespace: string;
173+
procedure SetWhiteSpace(const AWS: string);
174174

175175
function GetVariableResolver: TTemplateVariableResolver;
176176
procedure SetVariableResolver(const AResolver: TTemplateVariableResolver);
@@ -181,7 +181,7 @@ interface
181181
property RttiContext: TGetRttiContext read GetRttiContext write SetRttiContext;
182182
property Functions: ITemplateFunctions read GetFunctions write SetFunctions;
183183
property NewLine: string read GetNewLine write SetNewLine;
184-
property WhitespaceChar: char read GetWhitespace write SetWhiteSpace;
184+
property WhitespaceChar: string read GetWhitespace write SetWhiteSpace;
185185
property TemplateResolver: TTemplateResolver read GetTemplateResolver write SetTemplateResolver;
186186
property TemplateResolverWithContext: TTemplateResolverWithContext read GetTemplateResolverWithContext write SetTemplateResolverWithContext;
187187
property MaxRunTimeMs: integer read GetMaxRunTimeMs write SetMaxRunTimeMs;
@@ -227,8 +227,6 @@ function CreateTemplateContext(const AOptions: TTemplateEvaluationOptions = []):
227227
GUTF8WithoutPreambleEncoding: TUTF8WithoutPreambleEncoding;
228228
GStreamWriterProvider: TStreamWriterProvider;
229229
GPrettyPrintOutput: TPrettyPrintOutput;
230-
GDefaultOpenStripWSTag: string = '<|';
231-
GDefaultCloseWSTag: string = '|>';
232230

233231
implementation
234232

@@ -286,7 +284,7 @@ TTemplateContext = class(TInterfacedObject, ITemplateContext, ITemplateContext
286284
FFormatSettings: TFormatSettings;
287285
FDebugFormat: string;
288286
FPrettyPrintOutput: TPrettyPrintOutput;
289-
FWhiteSpace: char;
287+
FWhiteSpace: string;
290288
FVariableResolver: TTemplateVariableResolver;
291289
FRttiContext: TGetRttiContext;
292290
public
@@ -376,8 +374,8 @@ TTemplateContext = class(TInterfacedObject, ITemplateContext, ITemplateContext
376374
function GetDebugErrorFormat: string;
377375
procedure SetDebugErrorFormat(const AFormat: string);
378376

379-
function GetWhitespace: char;
380-
procedure SetWhiteSpace(const AWS: char);
377+
function GetWhitespace: string;
378+
procedure SetWhiteSpace(const AWS: string);
381379

382380
function GetVariableResolver: TTemplateVariableResolver;
383381
procedure SetVariableResolver(const AResolver: TTemplateVariableResolver);
@@ -438,8 +436,6 @@ constructor TTemplateContext.Create(const AOptions: TTemplateEvaluationOptions);
438436
SetEncoding(GDefaultEncoding);
439437
FStartToken := GDefaultOpenTag;
440438
FEndToken := GDefaultCloseTag;
441-
FStartStripToken := GDefaultOpenStripWSTag;
442-
FEndStripToken := GDefaultCloseWSTag;
443439
FTemplates := TDictionary<string, ITemplate>.Create;
444440
FVariables := TTemplateVariables.Create;
445441
FFunctions := CreateTemplateFunctions(self);
@@ -579,7 +575,7 @@ function TTemplateContext.GetVariables: ITemplateVariables;
579575
exit(FVariables);
580576
end;
581577

582-
function TTemplateContext.GetWhitespace: char;
578+
function TTemplateContext.GetWhitespace: string;
583579
begin
584580
exit(FWhiteSpace);
585581
end;
@@ -738,7 +734,7 @@ procedure TTemplateContext.SetVariableResolver(const AResolver: TTemplateVariabl
738734
FVariableResolver := AResolver;
739735
end;
740736

741-
procedure TTemplateContext.SetWhiteSpace(const AWS: char);
737+
procedure TTemplateContext.SetWhiteSpace(const AWS: string);
742738
begin
743739
FWhiteSpace := AWS;
744740
end;

0 commit comments

Comments
 (0)