

Warning

This section may contain outdated information.

# Leaf

Welcome to Leaf. Leaf's goal is to be a simple templating language that can make generating views easier. There are plenty of great templating languages, so use what's best for you -- maybe that's Leaf! The goals of Leaf are:

- Small set of strictly enforced rules
- Consistency
- Parser first mentality
- Extensibility

## Syntax

### Structure

Leaf Tags are made up of 4 Elements: - Token: `#` is the Token - Name: A `string` that identifies the tag - Parameter List: `()` May accept 0 or more arguments - Body (optional): `{}` Must be separated from the Parameter List by a space

There can be many different usages of these 4 elements depending on the Tag's implementation. Let's look at a few examples of how Leaf's built-in Tags might be used:

- `#()`
- `#(variable)`
- `#import("template")`
- `#export("link") { <a href="#()"></a> }`
- `#index(friends, "0")`
- `#loop(friends, "friend") { <li>#(friend.name)</li> }`
- `#raw() { <a href="#raw">Anything goes!@#$%^&*</a> }`

### Using the `#` token in HTML

The `#` token cannot be escaped. Use the `#()` or `#raw() {}` Tag to output a `#` in a Leaf Template. `#()` => `#`

### Raw HTML

All Leaf output is escaped by default. Use the `#raw() {}` Tag for unescaped output. `#raw() { <a href="#link">Link</a> }` => `<a href="#link">Link</a>`

> IMPORTANT! Make sure you are not using the `#raw() {}` Tag with user input.

### Chaining

The double token: `##` indicates a chain. It can be applied to any standard Tag. If the previous Tag fails, the chained Tag will be given an opportunity to run.

    #if(hasFriends) ##embed("getFriends")

### Leaf's built-in Tags

#### Token: `#()`

    #() #()hashtags #()FTW => # #Hashtags #FTW

#### Raw: `#raw() {}`

    #raw() {
        Do whatever w/ #'s here, this code won't be rendered as leaf document and is not escaped.
        It's a great place for things like Javascript or large HTML sections.
    }

#### Equal: `#equal(lhs, rhs) {}`

    #equal(leaf, leaf) { Leaf == Leaf } => Leaf == Leaf
    #equal(leaf, mustache) { Leaf == Mustache } =>

#### Variable: `#(variable)`

    Hello, #(name)!

#### Loop: `#loop(array, "item") {}`

    #loop(friends, "friend") {
      #(offset). #(friend.name)
    }

The body of a `#loop` can access an `index` variable corresponding to the index of the array. There is also an `offset` variable which is the index plus 1.

#### Index: `#index(array, _ index: String)`

    Hello, #index(friends, "0")!
    Hello, #index(friends, "best")!

Note: array indexes are always strings.

#### If - Else: `#if(bool) ##else() { this }`

    #if(entering) {
      Hello, there!
    } ##if(leaving) {
      Goodbye!
    } ##else() {
      I've been here the whole time.
    }

Note that `#if` requires a boolean variable. If you need to do a comparison then `#equal` is more appropriate. You can chain `#equal` in the same way as `#if`:

    #equal(state, "0") {
      <span class="green">Normal</span>
    } ##equal(state, "1") {
      <span class="orange">Warning</span>
    } ##else() {
      <span class="red">Alert</span>
    }

#### Import: `#import("template")`

#### Export: `#export("template") { Leaf/HTML }`

#### Extend: `#extend("template")`

#### Embed: `#embed("template")`

> When using these Layout Tags, omit the template file's .leaf extension.

    /// base.leaf
    <!DOCTYPE html>
    #import("html")

    /// html.leaf
    #extend("base")

    #export("html") { <html>#embed("body")</html> }

    /// body.leaf
    <body></body>

Leaf renders `html.leaf` as:

    <!DOCTYPE html>
    <html><body></body></html>

### Custom Tags

Look at the existing tags for advanced scenarios, let's look at a basic example by creating `Index` together. This tag will take two arguments, an array, and an index to access.

    class Index: BasicTag {
      let name = "index"

      func run(arguments: [Argument]) throws -> Node? {
        guard
          arguments.count == 2,
          let array = arguments[0].value?.nodeArray,
          let index = arguments[1].value?.int,
          index < array.count
        else { return nil }
            return array[index]
        }
    }

We can now register this Tag in our `main.swift` file with:

    if let leaf = drop.view as? LeafRenderer {
        leaf.stem.register(Index())
    }

And use it just like we did above.

> Note: Use of non-alphanumeric characters in Tag Names is strongly discouraged and may be disallowed in future versions of Leaf.

## Syntax Highlighting

### Atom

language-leaf by ButkiewiczP

### Xcode

It is not currently possible to implement Leaf Syntax Highlighting in Xcode, however, using Xcode's HTML Syntax Coloring can help a bit. Select one or more Leaf files and then choose Editor > Syntax Coloring > HTML. Your selected Leaf files will now use Xcode's HTML Syntax Coloring. Unfortunately the usefulness of this is limited because this association will be removed when `vapor xcode` is run.

There appears to be a way to make Xcode file associations persist but that requires a bit more kung-fu.

### VS Code

html-leaf by FranciscoAmado

### CLion & AppCode

Some preliminary work has been done to implement a Leaf Plugin for CLion & AppCode but lack of skill and interest in Java has slowed progress! If you have IntelliJ SDK experience and want to help with this, message Tom Holland on Vapor Slack