【问题标题】:FreeMarker: keep identation when using macrosFreeMarker:使用宏时保持标识
【发布时间】:2013-03-17 12:50:55
【问题描述】:

我正在使用 FreeMarker 模板引擎从 Web 服务的抽象描述中生成一些 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;
    }
?>

一种解决方案是将前导空格作为参数提交给宏,但这只会使模板更加不可读。有没有更好的解决方案?

【问题讨论】:

    标签: java code-generation freemarker


    【解决方案1】:

    对于那些希望在导入的宏前加上一些空格缩进的人,这里有一个执行工作的类:

    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>
    

    【讨论】:

      【解决方案2】:

      今天,可以使用&lt;#nt&gt;whitespace documentation 对此表示如下:

      可以使用 nt 指令(用于无修剪)禁用单行的空白剥离。

      根据V2.3 changelog,在以前的版本中,除了&lt;#include&gt; 和自定义指令(如&lt;@macroname&gt;)之外,只包含FTL 标记的行会被修剪。但在 V2.3 中,他们将此行为更改为始终修剪此类线条。因此,在使用宏时,您可以将&lt;#nt&gt; 放在行中以防止修剪,从而保持缩进。

      <#macro test>
      ...<#t>
      </#macro>
      
      Example:
         - <@test /><#nt>
      

      给出结果:

      Example:
         - ...
      

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

      编辑:

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

      例子:

      <#macro test>
      ...
      wow
      </#macro>
      
      Example:
         - <@test><#nt>
      

      将导致:

      Example:
         - ...
      wow
      

      【讨论】:

        【解决方案3】:

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

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

        <@indent spaces=4>
           ...
        </@indent>
        

        使用它会使模板更复杂,但它仍然不像在每一行中插入缩进那样嘈杂。

        【讨论】:

        • 你建议如何将 分成几行?
        • indent 指令应该通过TemplateDirectiveModel 实现,而不是通过#macro。如果您查看TemplateDirectiveModel API,您将了解如何操作。
        • 是吗? :) 然后你也可以做&lt;#local capturedOutput&gt;&lt;#nested&gt;&lt;/#local&gt; 然后在它上面使用正则表达式(通过?replace)来达到同样的效果。
        • 我在想你可能要去的地方。我喜欢 RAD(纯 FTL 快速简单)。 TemplateDirectiveModel 最终更干净,但我的偏好是避免将逻辑移动到 Java 类中,直到 1)函数的要求已经彻底建立,2)在 FTL 中实现逻辑真的难以理解(在 Java 中会更清晰) .
        【解决方案4】:

        似乎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>
        

        【讨论】:

        • 感谢您的回复! “使模板更难阅读”,我的意思几乎是你所想的,它使宏更复杂,更不自解释。看来提交缩进级别是解决问题最合理的方式。
        • 您可以随时在宏上添加注释标题以帮助阐明它的作用。
        • 你的代码sn-p中有一个类型,应该是:...&lt;#local spc&gt;${""?left_pad(indent * 4)}&lt;/#local&gt;...
        • @klingt.net 谢谢!固定。
        猜你喜欢
        • 1970-01-01
        • 2013-12-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-02-15
        相关资源
        最近更新 更多