Antaris / RazorEngine

Open source templating engine based on Microsoft's Razor parsing engine
http://antaris.github.io/RazorEngine
Other
2.14k stars 577 forks source link

Indentation for included template #392

Open egdd opened 8 years ago

egdd commented 8 years ago

Dear all,

I am making good progress with this great tool! As a requirement for my text templates I really have to get an idea of how to provide indentation. I want to generate text (no HTML) where indentation is important based on the calling parent template.

What I can create with the engine right now is this

Joe
Kim
Ken
Lela
Luke

What I want to create is this:

Joe
    Kim
    Ken
    ----Lela
    ----Luke

The models for that would be:

public class Person
{
    public Person(string name)
    {
        Name = name;
        Children = new List<Person>();
    }

    public string Name { get; set; }

    IList<Person> Children { get; set; }
}

The instances would be

Person grandFather = new Person("Joe");

Person sonOne = new Person("Kim");
Person sonTwo = new Person("Ken");

Person granddaughter = new Person("Lela");
Person grandson = new Person("Luke");

sonTwo.Children.Add(granddaughter);
sonTwo.Children.Add(grandson);

grandFather.Children.Add(sonOne);
grandFather.Children.Add(sonTwo);

The templates would be something like this:

Start.txt:

@Include("APerson.txt", Model)
@foreach(var person in Model.Children)
{
    @IncludeWithIndentation("    ", "Person.txt", person)
}

Person.txt:

@Include("APerson.txt", Model)
@foreach(var child in Model.Children)
{
    @IncludeWithIndentation("----", "Person.txt", child)
}

APerson.txt:

@Model.Name

Basically, I want to indent the children - that means basically to prefix all the lines of the called template with a string specified by the calling template. The children could have children themselves as well, so they should indent their children as well (kind of recursively). So the parent should specify the indentation for the children and add his indentation to the children's output text.

How to solve that?

First I tried to use RawStringFactory - however, no luck. Just literals end up here.

My current idea on how to solve that: I included a custom method with this signature in my MyCustomTemplate(T) as described in the article Extending the template Syntax.:

public virtual IEncodeString IncludeWithIndentation(string indentation, string, templateName, object model))
{
    TemplateWriter templateWriter = Include(name, model, modelType);
    //how to pass "indentation" to templateWriter?
    return templateWriter;
}

The first parameter should contain the identation, e.g. spaces or tabs or whatever. The first part works, the indentation gets passed to the method. That is fine. However, how to pass the indentation to TemplateWriter when I am in method IncludeWithIndentation?

Any advice on that one? Or any other, more elegant way of doing this (e.g. in a Template itself if the method call for RazorEngine's built in Include-method is placed four spaces/tabs from the line start, all the children's content should be indented four spaces/tabs)?

Greetings

egdd

matthid commented 8 years ago

Indentation is a difficult topic as there is no "correct" way to do it. Using the Indentation of the @ doesn't lead to a properly formatted html document:

<body>
@if (someCondition) {
    foreach(var person in Model.Children)
    {
        @Include(...)
    }
}
<body>

To implement the IncludeWithIndentation you would change the TemplateWriter class (or add another class TemplateWriterWithIndentation) and replace the given delegate with another one writing the additional indent here. Use is here.

It would look like this:

writerDelegate = textWriter => writer(new IndentedTextWriter(textWriter));