Make cl-template able to trim spaces
The contents of this site are written in gemtext[1] markup language and then published as both a Gemini capsule and a web site. I wrote a generator program in Common Lisp to build the capsule from a bunch of gemtext files, so I need a template library which works for output not only in HTML. I found cl-template[2] which is just such a library I need, as said in its document:
I like these design approaches. However, due to the lack of ability to handle unwanted spaces generated by the template, It's quite inconvenient to use for output formats which are space sensitive. Gemtext is spaces sensitive - blank lines are rendered verbatim by clients verbatim. For example, given the template:
Index page: <% if (@ page) %> <%= page-body (@ page) %> <% end %> <% loop for post in (@ children) do %> => /<%= page-url post %> <%= page-date post %> - <%= page-title post %> <% end %> Some other text.
Apply it produces something like:
Index page: # Page content title Page content line1 =>/post-2-2023-12-06.gmi 2023-12-06 - Post 2 =>/post-1-2023-12-05.gmi 2023-12-05 - Post 1 Some other text.
You can see a lot of unwanted blank lines. It's because all text between actions are copied verbatim when template is executed, as '(write-string text stream)' expressions. So every newline after the end delimiter ("%>") are included as is.
Go's text/template library takes care of this problem by providing options for user to trim out the leading and preceding spaces. You can find the details in "Text and Spaces"[3] section on its document. So I added a similar feature to cl-template:
- If the left delimiter (by default "<%" and "<%=") is followed immediately by a minus sign and white space, the last trailing white space is trimmed from the immediately preceding text.
- If the right delimiter (by default "%>") is preceded by white space and a minus sign, the first leading white space is trimmed from the immediately following text.
More details can be found in the Readme.org[4] of the modified version cl-template-trim. It's currently not on Quicklisp, as I think it's better to be merged into its upstream cl-template ultimately.
Now with the trim options on:
Index page: <% if (@ page) -%> <%= page-body (@ page) -%> <% end -%> <% loop for post in (@ children) do -%> => /<%= page-url post %> <%= page-date post %> - <%= page-title post %> <% end -%> Some other text
The output is much more as expected:
Index page: # Page content title Page content line1 =>/post-2-2023-12-06.gmi 2023-12-06 - Post 2 =>/post-1-2023-12-05.gmi 2023-12-05 - Post 1 Some other text.
Implementation
The implementation is straightforward:
- Define a function to parse the trim options user specified - it's quite easy thanks of the flexibility of the original implementation.
- Extend the item of expression stack from expression to '(expression trim-before? trim-end?)'
- Each time when we push an expression onto stack, if the expression has a 't' for trim-before?, and the top of expression stack is a '(write-string text stream)', alter it as '(write-string text-with-tailing-space-trimmed stream)'. Remember, expression stack is in reverse order.
- Each time when we push a '(write-string text stream)' onto stack, if the top of expression stack has a 't' for trim-end?, trim the leading space of text before push into the stack.
There is also a few fixes on tests and system definition to stop the complains from recent version ASDF.
--------
---------------------
Published on 2023-12-07
The content for this site is licensed under: