FreeMarker:使用宏时保持缩进

时间:2013-03-27 12:32:15

标签: java code-generation freemarker

我正在使用FreeMarker模板引擎从webservice的抽象描述中生成一些php类。我的问题是,当我在FreeMarker模板中调用一个宏时,宏在宏调用之前插入没有左手空格的文本。

exampleTemplate.ftl:

<?php
    class ${class.name} {
        <@docAsComment class.doc/>

        <#list class.fields as field>
        $${field.name};
        </#list>
        <#-- ... -->
    }
?>

<#macro docAsComment doc>
/*
<#if doc.title != "">
* ${doc.title}
</#if>
<#list doc.content as content>
<#if content != ""> * ${content}</#if>
</#list>
*/
</#macro>

这将生成如下内容:

<?php
    class foo {
/*
 * foo
 * bar foo, bla
 */          

    $a;
    $b;
    }
?>

一种解决方案是将前导空格作为参数提交给宏,但这使得模板更难以理解。有更好的解决方案吗?

4 个答案:

答案 0 :(得分:5)

似乎docAsComment始终在代码生成中以相同的缩进级别调用。你可以将缩进法加入宏中。

如果注释的缩进是可变的,则必须传入缩进级别。我不明白你的评论使模板难以阅读。它 使宏更复杂。

调用看起来像这样:

<@docAsComment class.doc 1/>

宏将改为这样:

<#macro docAsComment doc indent=1>
   <#local spc>${""?left_pad(indent * 4)}</#local>
${spc}/*
<#if doc.title != "">
${spc}* ${doc.title}
</#if>
<#list doc.content as content>
<#if content != "">${spc} * ${content}</#if>
</#list>
${spc}*/
</#macro>

还不错,真的。您可以通过缩进来使宏更容易阅读:

<#macro docAsComment doc indent=1>
    <#local spc>${""?left_pad(indent * 4)}</#local>
    ${spc}/*<#lt>
    <#if doc.title != "">
        ${spc}* ${doc.title}<#lt>
    </#if>
    <#list doc.content as content>
        <#if content != "">${spc} * ${content}</#if><#lt>
    </#list>
    ${spc}*/<#lt>
</#macro>

答案 1 :(得分:1)

这类问题的通用解决方案(动态缩进)是一个过滤器,(基本)理解您生成的语言(PHP)并重新缩进代码。您可以将该过滤器实现为包裹实际输出Writer的{​​{1}}。如果它看到Writer{}/*代币(我不确定)的位置,那就足够了。

另一个更易于实现的解决方案是通过实现*/来创建自定义FreeMarker指令,该指令通过简单地添加或删除作为参数给出的空间量来过滤其嵌套内容中生成的输出,在每一行的开头。然后你可以做类似的事情:

TemplateDirectiveModel

使用它会使模板变得更复杂,但是在每行中插入缩进时,它仍然不那么嘈杂。

答案 2 :(得分:1)

今天,可以使用<#nt>whitespace documentation说明了以下内容:

  

使用nt指令(对于No Trim),可以禁用单行的白色空间剥离。

根据V2.3 changelog,在以前的版本中,除了<#include>和自定义指令(如<@macroname>)之外,仅包含FTL标记的行被修剪。但在V2.3中,他们改变了这种行为,总是修剪这些线条。因此,在使用宏时,可以在行上放置<#nt>以防止修剪,从而保持缩进。

<#macro test>
...<#t>
</#macro>

Example:
   - <@test /><#nt>

给出了结果:

Example:
   - ...

你可以在宏中看到我定义<#t>,这是因为宏内部的新行不会被修剪,并且总是在你<@macro>的位置给出一个新行,所以在一个部分,我们修剪了白色空间,而在另一个部分,我们保留它!

修改

值得一提的是,出于某种原因,此仅适用于一行。如果宏中有多行,则只保留第一行的缩进。到目前为止,我没有找到解决方法,但我为此创建了an issue in the Freemarker JIRA

示例:

<#macro test>
...
wow
</#macro>

Example:
   - <@test><#nt>

将导致:

Example:
   - ...
wow

答案 3 :(得分:0)

对于那些希望为导入的宏添加一些空格缩进作为前缀的人,下面是一个执行工作的类:

public final static class IndentDirective
    implements TemplateDirectiveModel
{

  private static final String COUNT = "count";

  public void execute(Environment environment, Map parameters, TemplateModel[] templateModels,
      TemplateDirectiveBody body)
      throws TemplateException, IOException
  {
    Integer count = null;
    final Iterator iterator = parameters.entrySet().iterator();
    while (iterator.hasNext())
    {
      final Map.Entry entry = (Map.Entry) iterator.next();
      final String name = (String) entry.getKey();
      final TemplateModel value = (TemplateModel) entry.getValue();

      if (name.equals(COUNT) == true)
      {
        if (value instanceof TemplateNumberModel == false)
        {
          throw new TemplateModelException("The \"" + COUNT + "\" parameter " + "must be a number");
        }
        count = ((TemplateNumberModel) value).getAsNumber().intValue();
        if (count < 0)
        {
          throw new TemplateModelException("The \"" + COUNT + "\" parameter " + "cannot be negative");
        }
      }
      else
      {
        throw new TemplateModelException("Unsupported parameter '" + name + "'");
      }
    }
    if (count == null)
    {
      throw new TemplateModelException("The required \"" + COUNT + "\" parameter" + "is missing");
    }

    final String indentation = StringUtils.repeat(' ', count);
    final StringWriter writer = new StringWriter();
    body.render(writer);
    final String string = writer.toString();
    final String lineFeed = "\n";
    final boolean containsLineFeed = string.contains(lineFeed) == true;
    final String[] tokens = string.split(lineFeed);
    for (String token : tokens)
    {
      environment.getOut().write(indentation + token + (containsLineFeed == true ? lineFeed : ""));
    }
  }

}

您可以通过将configuration.setSharedVariable("indent", new IndentDirective());添加到FreeMarker配置中来对其进行集成,然后通过插入来在模板中使用它

<@indent count=4>
[whathever template code, including macro usage]
</@indent>