Smart Indentation Engine

View Source

CI License Version Hex Docs

A custom EEx engine that does its best to handle indentation in an intuitive way, so that you can enjoy both clean readable templates and well-formatted output.

Features

The engine ships with the following features:

  • Smart Indentation - Preserves proper indentation in template output.
  • Template Inclusion - Supports including (and re-indenting) partial templates.
  • Assigns Binding - Access values from the assigns binding using the @ syntax (e.g., @foo).
  • Flexible Spacing - Works with both spaces and tabs.

Overview

This engine extends the default EEx.SmartEngine by implementing the <%| tag to handles indentation in template blocks.

When you use it with control flow structures like if, for, case, etc., the the engine automatically aligns the output to match the indentation of the control statement.

Templates included with <%= include :template %> are also re-indented to match the surrounding context.

Usage

Installation

def deps do
  [
    {:smart_indentation_engine, "~> 0.1"}
  ]
end

Quick Example

Using the <%| tag for control flow:

Mix.install([
  {:smart_indentation_engine, "~> 0.1"}
])

"""
<ul>
  <%| for name <- ["world", "darkness my old friend"] do %>
    <li>hello <%= name %></li>
  <% end %>
</ul>
"""
|> EEx.eval_string([], engine: SmartIndentationEngine)

Will produce the following output:

<ul>
  <li>hello world</li>
  <li>hello darkness my old friend</li>
</ul>

The ~TT Sigil

By importing the SmartIndentationEngine.Template module you'll have access to the ~TT sigil that automatically uses the engine:

defmodule Template.BasicList do
  import SmartIndentationEngine.Template

  def render(_assigns \\ []) do
    ~TT"""
    <ul>
      <%| for name <- ["world", "darkness my old friend"] do %>
        <li>hello <%= name %></li>
      <% end %>
    </ul>
    """
  end
end

Template.BasicList.render()

Using @ for Assigns

defmodule Template.AssignedList do
  import SmartIndentationEngine.Template

  def render(assigns \\ []) do
    ~TT"""
    <ul>
      <%| for name <- @names do %>
        <li>hello <%= name %></li>
      <% end %>
    </ul>
    """
  end
end

Template.AssignedList.render(names: ["world", "darkness my old friend"])

Template Inclusion

You can also include other templates using the include function. Included templates will be reindented to match the surrounding context.

defmodule Template.IncludedList do
  import SmartIndentationEngine.Template

  def render(assigns \\ []) do
    ~TT"""
    <div>
      <h1>Nice to meet you, hope you guess my name!</h1>
      <%= include :list, names: @names %>
    </div>
    """
  end

  def list(assigns) do
    ~TT"""
    <ul>
      <%| for name <- @names do %>
        <li>hello <%= name %></li>
      <% end %>
    </ul>
    """
  end
end

Template.IncludedList.render(names: ["world", "darkness my old friend"])

This produces:

<div>
  <h1>Nice to meet you, hope you guess my name!</h1>
  <ul>
    <li>hello world</li>
    <li>hello darkness my old friend</li>
  </ul>
</div>

How It Works

When you use the <%| marker with control structures, the engine:

  1. Captures the indentation context of the control statement
  2. Remove the empty lines introduced by the control statement
  3. Processes the content inside the block (handles nested structures and re-indents included templates)
  4. Dedents the block output to match captured indentation

License

The package is available as open source under the terms of the MIT License.