!!! 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") { }` - `#index(friends, "0")` - `#loop(friends, "friend") {
  • #(friend.name)
  • }` - `#raw() { Anything goes!@#$%^&* }` ### 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() { Link }` => `Link` > 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") { Normal } ##equal(state, "1") { Warning } ##else() { Alert } ``` #### 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 #import("html") /// html.leaf #extend("base") #export("html") { #embed("body") } /// body.leaf ``` Leaf renders `html.leaf` as: ``` ``` ### 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. ```swift 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: ```swift if let leaf = drop.view as? LeafRenderer { leaf.stem.register(Index()) } ``` And use it just like we did [above](#index). > 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](https://atom.io/packages/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](http://stackoverflow.com/questions/9050035/how-to-make-xcode-recognize-a-custom-file-extension-as-objective-c-for-syntax-hi) but that requires a bit more kung-fu. ### VS Code [html-leaf](https://marketplace.visualstudio.com/items?itemName=Francisco.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](http://vapor.team)