groue / GRMustache

Flexible and production-ready Mustache templates for MacOS Cocoa and iOS
http://mustache.github.com/
MIT License
1.44k stars 190 forks source link

Indentation of multi-line values in text templates #45

Open KosmicTask opened 11 years ago

KosmicTask commented 11 years ago

Hi Gwendal

What is the best approach for trying to preserve newline formatting and indentation when processing text (as opposed to HTML) templates?

As a practical example consider the following text template that generates a C file to echo its inputs:

#include <stdio.h>

int main(int argc, char *argv[])
{

{{ task-input-variables }}
{{ task-input-result }}    
    return 0;
}

The minimal template variable dict for this is:

{
  "task-input-result" : "\t\/\/ Return inputs as task result\n\tprintf(\"%s\\n\", taskInput1);\n\tprintf(\"%s\\n\", taskInput2);\n\t",
  "task-input-variables" : "\t\/\/ Task input variables \n\tchar *taskInput1 = argv[1];\n\tchar *taskInput2 = argv[2];\n"
}

The output is :

#include <stdio.h>

int main(int argc, char *argv[])
{

    // Task input variables 
    char *taskInput1 = argv[1];
    char *taskInput2 = argv[2];

    // Return inputs as task result
    printf("%s\n", taskInput1);
    printf("%s\n", taskInput2);

    return 0;
}

So the above works. However the issues are that I have pre-empt indentation and new line creation. So I have to embed tabs in my variables (otherwise only the first line of say {{ task-input-variables }} will be indented) which is less than desirable. What this boils down to is that I would like to be able to format my template as below while maintaining the final rendering shown above.

#include <stdio.h>

int main(int argc, char *argv[])
{

    {{ task-input-variables }}

    {{ task-input-result }}   

    return 0;
}

Any thoughts?

groue commented 11 years ago

GRMustache is a bad Mustache citizen: it does not provide the white-space processing that is documented by the Mustache spec. My opinion on it that it was half-thought, painful to implement, for very little benefit since the main produced language was HTML, a language that is white-space insensitive unless it gets ultra-sensitive.

My opinion was that GRMustache had better avoiding all white-space management, with three benefits: 1. users who don't care about white space won't be bothered, 2. those who need precise white-space management will have a tool that obey to the letter and will not fuck their output, 3. I can focus on more interesting features in the library :-)

And now you come. And it looks like your issue is not exactly handled by the Mustache spec. Another reason for me to think it is half-thought.

That said, thank you for the interesing use case. Please give me some time to think about it.

KosmicTask commented 11 years ago

Thanks for the reply. I know that I am bending Mustache in this regard.

I would imagine that whitespace handling etc for text and HTML content may have to be quite different. As the CONTENT_TYPE pragma is a GRMustache extension you are free to implement text whitespace handling as you see fit.

groue commented 11 years ago

Yep, I'm free to do whatever I want - especially solve my users' problems. Mustache looks like it will remain a very bare spec forever - nothing that ties me very strongly. GRMustache has already evolved so much, just to make sure the template engine isn't painful to use. I'll try to figure out a sensible solution for you.

groue commented 11 years ago

I've rewritten the issue title - it now exactly describes my interpretation of your story. Let me know if you believe I missed something.

KosmicTask commented 11 years ago

Title is much better thanks.

groue commented 11 years ago

@thelucid had a neat idea that at first sight looks flawless to me (thanks!).

It would require:

Please tell me if this looks doable for you without polluting too much your code base. I'm planning to take some rest in the next couple of days. I hope you're not too much impatient :)

KosmicTask commented 11 years ago

I am not impatient at all.

I am implementing templates in my 20 or so languages so it's only now that I am really using GRMustache in earnest for text based templating.

I will get back with thoughts soon.

KosmicTask commented 11 years ago

I need to spend some time getting to grips with the GRMustache rendering API but the approach suggested by @theLucid (if I understand it correctly) seems sensible. Representing the strings as template instances would be simple to implement in my code I think.

That would hopefully resolve the indentation issue. I think that I can resolve the extraneous newline issue with the following approach. The template below generates the task-input-variables key referred to above. By explicitly identifying the last item in a collection with a last key I can output an eol (End Of Line - which renders as \n) item where appropriate. This gives a simple rule: to render a multiline value that occupies minimal vertical space code the template itself in one line.

{{#task-inputs}}{{#first}}{{tab}}// {{task-input-variables-message}}{{eol}}{{/}}{{tab}}{{name}}{{^last}}{{eol}}{{/}}{{^}}{{tab}}// {{task-inputs-undefined-message}} {{/}}

I use tab , tab2 etc to insert \t characters at present to force the indenting. If partial template indention was respected in a future GRMustache release then I could merely render these as NULL and all would well.

So now I can hopefully write templates that work more or less as I require in the knowledge that they may work much better in the future.

Thanks for your interest.

KosmicTask commented 11 years ago

Hi Gwendal

Just wondering how you are getting on. If I can be of any help with this issue let me know.

Hope you are well.

groue commented 11 years ago

Hi Jonathan. I'm sorry I've been very busy last weeks.

The more I've been looking at our goal, the more I convinced myself I need to implement spec-compliance regarding white space. Plus extra bits for your use cases that I consider quite well-founded.

My initial attemps were lousy, I must admit. Closer to random programming than anything else, mostly supported by existing tests, not by any clear trend of thought. I've been looking at the way other implementations do - so far I've not been rejoiced by what I've seen. I'm not rejoiced by the inconsistencies of the spec, neither.

A motivation crisis, that's what I'm in.

KosmicTask commented 11 years ago

HI Gwendal

In my own case I find that motivational issues arise when I don't have a really clear view of what I am trying to do. In this situation I would ask myself:

  1. Do I really want to spend time doing this? Striving towards something as relatively abstract as delivering spec compliance can be underwhelming. If that's really the case then move on to something else. However, it may still be possible to get a handle on things. #46 has been more less cracked from an external point of view (regardless of anything that may arise as a result of approaching #45).
  2. What are the exact problems I have with the spec and the other implementations with regard to whitespace? If we can pin those down then perhaps we can plot a path out of the maze. Quite often I find myself looking at the surface of a problem unable to penetrate into the substance of it. I normally feel more contented when I have really comprehended the true nature of the problem regardless of its implications for the amount of work that needs to be done. The actual implementation isn't usually a problem once the actual requirements have been truly identified.

Sometimes, not always, I find that any elegant solution can be wrangled from what lies before me (this is more likely to be the case if the existent code has been thoughtfully constructed - which is the case here).

So, insight or insanity! If you want we can try and kick #45 around until the ball bounces out of the maze.