@@ -172,6 +172,7 @@ def safe_markdown(value: Any) -> str:
172
172
r"{% comment ('.*?' |\".*?\" )?%}[\s\S]*?{% endcomment %}"
173
173
)
174
174
_PRECOMPILED_TOKEN_PATTERN = re .compile (r"{{ .+? }}|{% .+? %}" )
175
+ _PRECOMPILED_LSTRIP_BLOCK_PATTERN = re .compile (r"\n( )+$" )
175
176
176
177
177
178
def _find_next_extends (template : str ):
@@ -340,10 +341,16 @@ def _find_next_token(template: str):
340
341
return _PRECOMPILED_TOKEN_PATTERN .search (template )
341
342
342
343
344
+ def _token_is_on_own_line (text_before_token : str ):
345
+ return _PRECOMPILED_LSTRIP_BLOCK_PATTERN .search (text_before_token ) is not None
346
+
347
+
343
348
def _create_template_function ( # pylint: disable=,too-many-locals,too-many-branches,too-many-statements
344
349
template : str ,
345
350
language : str = Language .HTML ,
346
351
* ,
352
+ trim_blocks : bool = False ,
353
+ lstrip_blocks : bool = False ,
347
354
function_name : str = "_" ,
348
355
context_name : str = "context" ,
349
356
dry_run : bool = False ,
@@ -361,19 +368,31 @@ def _create_template_function( # pylint: disable=,too-many-locals,too-many-bran
361
368
# Keep track of the template state
362
369
forloop_iterables : "list[str]" = []
363
370
autoescape_modes : "list[bool]" = ["default_on" ]
371
+ last_token_was_block = False
364
372
365
373
# Resolve tokens
366
374
while (token_match := _find_next_token (template )) is not None :
367
375
token = token_match .group (0 )
368
376
369
377
# Add the text before the token
370
378
if text_before_token := template [: token_match .start ()]:
371
- function_string += (
372
- indent * indentation_level + f"yield { repr (text_before_token )} \n "
373
- )
379
+ if lstrip_blocks and token .startswith (r"{% " ):
380
+ if _token_is_on_own_line (text_before_token ):
381
+ text_before_token = text_before_token .rstrip (" " )
382
+
383
+ if trim_blocks :
384
+ if last_token_was_block and text_before_token .startswith ("\n " ):
385
+ text_before_token = text_before_token [1 :]
386
+
387
+ if text_before_token :
388
+ function_string += (
389
+ indent * indentation_level + f"yield { repr (text_before_token )} \n "
390
+ )
374
391
375
392
# Token is an expression
376
393
if token .startswith (r"{{ " ):
394
+ last_token_was_block = False
395
+
377
396
autoescape = autoescape_modes [- 1 ] in ("on" , "default_on" )
378
397
379
398
# Expression should be escaped with language-specific function
@@ -390,6 +409,8 @@ def _create_template_function( # pylint: disable=,too-many-locals,too-many-bran
390
409
391
410
# Token is a statement
392
411
elif token .startswith (r"{% " ):
412
+ last_token_was_block = True
413
+
393
414
# Token is a some sort of if statement
394
415
if token .startswith (r"{% if " ):
395
416
function_string += indent * indentation_level + f"{ token [3 :- 3 ]} :\n "
@@ -457,8 +478,15 @@ def _create_template_function( # pylint: disable=,too-many-locals,too-many-bran
457
478
template = template [token_match .end () :]
458
479
459
480
# Add the text after the last token (if any)
460
- if template :
461
- function_string += indent * indentation_level + f"yield { repr (template )} \n "
481
+ text_after_last_token = template
482
+
483
+ if text_after_last_token :
484
+ if trim_blocks and text_after_last_token .startswith ("\n " ):
485
+ text_after_last_token = text_after_last_token [1 :]
486
+
487
+ function_string += (
488
+ indent * indentation_level + f"yield { repr (text_after_last_token )} \n "
489
+ )
462
490
463
491
# If dry run, return the template function string
464
492
if dry_run :
0 commit comments