CDSoft / pp

PP - Generic preprocessor (with pandoc in mind) - macros, literate programming, diagrams, scripts...
http://cdelord.fr/pp
GNU General Public License v3.0
252 stars 21 forks source link

Add !space macro #34

Closed tajmone closed 6 years ago

tajmone commented 6 years ago

Currently PP strips leading and trailing whitespaces from bracketed/braced parameters. This can result in some struggling with parameter-based conditional text in contexts where spaces can't be represented as HTML or Unicode entities (eg: inside html tags, or inside verbatim text).

I propose adding a built-in macro to emit blank space: !sp[ace].

Additionally, it could take an optional parameter for specifying multiple spaces (eg: !sp(8) to emit eight spaces). This could be handy in verbatim blocks.

Examples

Let's say I want to create a !PRE macro that encloses some text within <pre> tags, with an optional first parameter for specifying a class ....

!def(PRE)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<pre!ifne(\1)()( class="\1")>\2</pre>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

\PRE()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Some   verbatim   text.
  Spacing preserved.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

\PRE(someclass)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Some   verbatim   text.
  Spacing preserved.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

the problem here is that the leading space in ( class="\1") is actually lost in the final output (ie: "<preclass=""):

<preclass="someclass">Some verbatim text. Spacing preserved.
</pre>

So, the easiest workaround seems to be this:

!def(PRE)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<!ifeq(\1)()(pre)(pre class="\1")>\2</pre>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

... which works but isn't really elegant because of the repetition of "pre".

The !space macro would solve the problem in a more elegant way:

!def(PRE)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<pre!ifne(\1)()(!spclass="\1")>\2</pre>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

One could argue that in this example it was possible to just add a space after the "<pre" in the definition of the first example:

!def(PRE)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<pre !ifne(\1)()( class="\1")>\2</pre>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

... but this would produce a <pre > tag when no class parameter is specified, and might cause problems if the final HTML is to be parsed by some script that has strict expectations — and in other contexts spaces might be more critical.

CDSoft commented 6 years ago

There is a workaround due to the current implementation of the parser. Block separators can be in the middle of a line:

!def(PRE)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<pre!ifne(\1)()~~~ class="\1"~~~>\2</pre>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This feature is of course not documented ;-)

tajmone commented 6 years ago

That's fantastic! It also looks neat.

Does this work only for last argument, like with parameters? ie: it can only be used once for each macro, and only as last arg/param?

CDSoft commented 6 years ago

Yes, it works for the last argument only.

tajmone commented 6 years ago

I'v tried to create a !space macro myself, using the tildas syntax, but it seems impossible to produce a macro that emits just a space:

!def(sp)~~~   ~~~

!def(PRE)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<pre!ifne(\1)()(!sp class="\1")>\2</pre>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

... still produces "<preclass=""!

This looks like something that can only be achieved via a built-in macro.

The tildas solution is good, but since it only works in last parameter/argument it might leave uncovered some situations where leading or trailing whitespaces are needed.

CDSoft commented 6 years ago

!sp class="\1" produces spaces but they are lost when given as a parameter of ifne.

I'm not convinced that preserving spaces is useful. The output of pp is meant to be given to pandoc as markdown or restructuredtext. The only case where I found it useful is the case of code blocks (or more generally multiline arguments), that's why tildas have a specific behaviour. In other cases, the value of arguments are generally inserted within a line and spaces are around the argument, not in the argument itself.

Isn't this clearer:

!def(PRE)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
\ifdef(2) ( <pre class="\1">\2</pre>)
          ( <pre>\1</pre> )
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

!PRE(myclass)(some text)
!PRE(some text without any specific class)
tajmone commented 6 years ago

Yes, the example you provided is definitely clearer to read.

The output of pp is meant to be given to pandoc as markdown or restructuredtext.

I'm leveraging PP to produce also raw HTML, to circumvent markdown limitations (eg: GitHub Task Lists in pandoc have to be implemented via raw html) and give the user more control over styled HTML elements (from alert messages to multicolumn divs, and so on). So I ended up pushing the limits, so to speak, in testing how PP can expand markdown potentials (ie: in HTML conversion) by extending it with some markdown-like macros.

I also use PP in YAML headers and files, to allow dynamic YAML vars.

Surely, a !sp macro is definitely not strictly needed, but I realized that should the need arise there is no way around — ie: you can't create a user macro to fulfill this task. Also, I thought that if this macro was to accept a parameter to specify the number of spaces to produce, it could be used for smart indentantion of generated verbatim text (by using some PP symbol that keeps track of indentation).

But it was just an idea (that didn't disrupt backward comp) that I thought might be worth considering, and wanted to hear your opinion on it. But then, probably most PP users won't need it, and I agree that PP should be kept as simple as possible --- all the rest can be handled by external scripts, after all.

CDSoft commented 6 years ago

I'd rather keep pp simple. The first goal is markdown, other formats are better handled by Pandoc.