hyperturtle / Stache

Trimmed mustache logic-less templates for python
MIT License
13 stars 8 forks source link

Extra line feeds #2

Open SmithSamuelM opened 11 years ago

SmithSamuelM commented 11 years ago

the following template fragment renders differently in Stache than in others such as pystache, mustache

{{#scripts}}
    <script src="{{name}}"></script>
{{/scripts}}

In pystache each script element is on a separate line in Stache there is an extra line feed between each script element line

SmithSamuelM commented 11 years ago

I think the difference is that other mustache parsers treat a {{# }} strip the trailing linefeed when its on a line by itself so one can nest blocks to make the templates easy to read.

For example this produces what I want

  {{#scripts}}<script src="{{name}}"></script>
  {{/scripts}}
SmithSamuelM commented 11 years ago

To be more specific

  >>> data = { "scripts": [ {"name": "A"}, {"name": "B"}, {"name": "C"}]}
  >>> fp = open("template.html", "ru")
  >>> mold = fp.read()

  >>> print mold
     <html>
       <head>

       </head>
       <body>
          {{#scripts}}
             <script src="{{name}}"></script>
           {{/scripts}}
       </body>
      </html>
>>> import pystache
>>> renderer = pystache.Renderer()
>>> content = renderer.render(mold,data)
>>> print content
   <html>
     <head>

     </head>
     <body>
         <script src="A"></script>
         <script src="B"></script>
         <script src="C"></script>
     </body>
   </html>
SmithSamuelM commented 11 years ago

But in Stache

>>> import stache
>>> content = stache.render(mold,data)
>>> print content
<!DOCTYPE html>
<html>
  <head>

  </head>
  <body>

    <script src="A"></script>

    <script src="B"></script>

    <script src="C"></script>

  </body>
</html>
SmithSamuelM commented 11 years ago

I also verified this with Mustache python so it would be nice if Stache were consistent with the other implementations. I really like the fact that stache is just one file so I can easily include it as a dependency and I also like the additional features. The {{.}} especially

clach04 commented 2 years ago

I keep forgetting to dig into this, I have some notes/workarounds that are Stache specific:

# based on https://github.com/hyperturtle/Stache/issues/2

import stache

data = {"scripts": [ {"name": "A"}, {"name": "B"}, {"name": "C"}]}

template_str1 = """<html>
<head>

</head>
<body>
  {{#scripts}}
     <script src="{{name}}"></script>
   {{/scripts}}
</body>
</html>
"""

template_str2 = """<html>
<head>

</head>
<body>
{{#scripts}}
  <script src="{{name}}"></script>{{/scripts}}

</body>
</html>
"""

content = stache.render(template_str1, data)
print(content)
content = stache.render(template_str2, data)
print(content)

Output:

<html>
<head>

</head>
<body>

     <script src="A"></script>

     <script src="B"></script>

     <script src="C"></script>

</body>
</html>

Versus:

<html>
<head>

</head>
<body>

  <script src="A"></script>
  <script src="B"></script>
  <script src="C"></script>

</body>
</html>
clach04 commented 2 years ago

https://github.com/SmithSamuelM/staching fork has a fix for this, changing the above test case import to:

import staching as stache

results in:

<html>
<head>

</head>
<body>
     <script src="A"></script>
     <script src="B"></script>
     <script src="C"></script>
</body>
</html>

Versus:

<html>
<head>

</head>
<body>
  <script src="A"></script>  <script src="B"></script>  <script src="C"></script>

</body>
</html>
clach04 commented 2 years ago

Fix appears to be:

diff --git a/__init__.py b/__init__.py
index 55ff995..2f92574 100644
--- a/__init__.py
+++ b/__init__.py
@@ -239,6 +239,61 @@ class Stache(object):
             delim_tag      = delim_tag.split(' ', 1) if delim_tag else None
             delim_tag      = delim_tag if delim_tag and len(delim_tag) == 2 else None

+            # fix for https://github.com/hyperturtle/Stache/issues/2 from https://github.com/SmithSamuelM/staching/commit/f2c591ec69cc922c6ffec67e0d66f8047f2f2bf3
+            if  (   open_tag or invert_tag or comment_tag or
+                    partial_tag or push_tag or bool_tag or
+                    booltern_tag or unescape_tag or delim_tag): # not a variable
+                inline = False
+                if rest: # strip trailing whitespace and linefeed if present
+                    front, sep, back = rest.partition("\n") # partition at linefeed
+                    if sep:
+                        if not front.strip(): # only whitespace before linefeed
+                            rest = back # removed whitespace and linefeed
+                            #if _debug: print( "open rest strip front: \n%s" %  rest)
+                        else: #inline
+                            inline = True
+                            #if _debug: print( "open inline:")
+                if not inline and pre: #strip trailing whitespace after linefeed if present
+                    front, sep, back = pre.rpartition("\n")
+                    if sep:
+                        if not back.strip(): # only whitespace after linefeed
+                            pre = ''.join((front, sep)) # restore linefeed
+                            #if _debug: print( "open pre strip back: \n%s" % pre)
+                    else:
+                        pre = back.rstrip() #no linefeed so rstrip
+                        #if _debug: print( "open pre rstrip back: \n%s" % pre)
+
+            elif close_tag:
+                inline = True # section is inline
+                follow = False # followed by inline
+                post = ''
+
+                if rest: # see if inline follows
+                    front, sep, back = rest.partition("\n")
+                    if front.strip(): # not empty before linefeed so inline follows
+                        follow = True # inline follows
+                        #if _debug: print( "close follow:")
+
+                if pre: #strip trailing whitespace after prev linefeed if present
+                    front, sep, back = pre.rpartition("\n")
+                    if sep and not back.strip(): # only whitespace after linefeed
+                        inline = False
+                        #if _debug: print() "close not inline:" )
+                        if follow:
+                            post = back # save spacing for following inline
+                        pre = ''.join((front, sep)) # restore upto linefeed
+                        #if _debug: print( "close pre strip back: \n%s" % pre)
+
+                if not inline and rest: # strip trailing whitespace and linefeed if present
+                    if follow: # restore saved spacing
+                        rest = post + rest
+                        #print( "close follow rest: \n%s" %  rest)
+                    front, sep, back = rest.partition("\n") # partition at linefeed
+                    if sep:
+                        if not front.strip(): # only whitespace before linefeed
+                            rest = back # remove trailing whitespace and linefeed
+                            #if _debug: print( "close rest strip front: \n%s" %  rest)
+
             if push_tag:
                 pre = pre.rstrip()
                 rest = rest.lstrip()