GithubHelp home page GithubHelp logo

slang's Introduction

Slang Build Status

Lightweight, terse, templating language for Crystal.

Installation

Add this to your application's shard.yml:

dependencies:
  slang:
    github: jeromegn/slang

Usage

Preferred: use Kilt

Kilt is included as a dependency for this project. It should help integrating non-ECR template engines.

Add this to your application's shard.yml:

dependencies:
  kilt:
    github: jeromegn/kilt
require "kilt/slang"

Kilt.render("path/to/file.slang") #=> <compiled template>

Example with Kemal (includes Kilt):

require "kilt/slang"

get "/" do
  Kilt.render "path/to/file.slang"
end

Without Kilt

String.build do |str|
  Slang.embed("path/to/file.slang", "str")
end

Syntax

doctype html
html
  head
    meta name="viewport" content="width=device-width,initial-scale=1.0"
    title This is a title
    css:
      h1 {color: red;}
      p {color: green;}
    style h2 {color: blue;}
  body
    /! Visible multi-line comment
      span this is wrapped in a comment
    /[if IE]
      p Dat browser is old.
    / Invisible multi-line comment
      span this is wrapped in a comment
    h1 This is a slang file
    h2 This is blue
    input type="checkbox" checked=false
    input type="checkbox" checked=true
    input type="checkbox" checked="checked"
    span#some-id.classname
      #hello.world.world2
        - some_var = "hello world haha"
        span
          span data-some-var=some_var two-attr="fun" and a #{p("hello")}
          span
            span.deep_nested
              p
                | text inside of <p>
              = Process.pid
              | text node
              ' other text node
        span.alongside pid=Process.pid
          custom-tag#with-id pid="#{Process.pid}"
            - ["ah", "oh"].each do |s|
              span = s
    / This is an invisible comment
    - if true == true
      #amazing-div some-attr="hello"
    - else
      #not-so-amazing-div some-attr="goodbye"
    /! This is a visible comment
    script var num1 = 8*4;

    javascript:
      var num2 = 8*3;
      alert("8 * 3 + 8 * 4 = " + (num1 + num2));

Given the context:

some_var = "hello"
strings = ["ah", "oh"]

Compiles to HTML:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>This is a title</title>
    <style>
      h1 {color: red;}
      p {color: green;}
    </style>
    <style>h2 {color: blue;}</style>
  </head>
  <body>
    <!--Visible multi-line comment
      <span>this is wrapped in a comment</span>
    -->
    <!--[if IE]>
      <p>Dat browser is old.</p>
    <![endif]-->
    <h1>This is a slang file</h1>
    <h2>This is blue</h2>
    <input type="checkbox"/>
    <input type="checkbox" checked/>
    <input type="checkbox" checked="checked"/>
    <span id="some-id" class="classname">
      <div id="hello" class="world world2">
        <span>
          <span data-some-var="hello world haha" two-attr="fun">and a hello</span>
          <span>
            <span class="deep_nested">
              <p>
                text inside of &lt;p&gt;
              </p>
              #{Process.pid}
              text node
              other text node
            </span>
          </span>
        </span>
        <span class="alongside" pid="#{Process.pid}">
          <custom-tag id="with-id" pid="#{Process.pid}">
            <span>ah</span>
            <span>oh</span>
          </custom-tag>
        </span>
      </div>
    </span>
    <div id="amazing-div" some-attr="hello"></div>
    <!--This is a visible comment-->
    <script>var num1 = 8*4;</script>
    <script>
      var num2 = 8*3;
      alert("8 * 3 + 8 * 4 = " + (num1 + num2));
    </script>
  </body>
</html>

Difference between single and double equals in Slang

  • = inserts HTML with escaped characters
  • == inserts HTML without escaping. It is needed when you have already rendered HTML and you need to insert it to your layout directly.

TODO

  • Fix known limitations
  • More tests
  • Website
  • Documentation

Contributing

  1. Fork it ( https://github.com/jeromegn/slang/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Contributors

slang's People

Contributors

amadanmath avatar beno avatar crisward avatar elorest avatar janczer avatar jeromegn avatar kachick avatar kraw1er avatar lipanski avatar makenowjust avatar mamantoha avatar msa7 avatar nashimoto avatar renich avatar slayershadow avatar veelenga avatar wstein avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

slang's Issues

How to control spaces between tags

So this

    a href="#"
      span
      | Contacts

is translated to such html:

<a href="#">
      <span>
      </span>
      Contacts
    </a>

But I want it to be without space between span and text like that:

<a href="#">
      <span>
      </span>Contacts
    </a>

Is it possible?

Colons in attributes not working.

When linking SVG, it uses odd attributes with colons in.
The template engine seems to be closing the tag, then escaping these.

eg.

svg width="256" height="448" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 

converts to

<svg width="256" height="448" xmlns="http://www.w3.org/2000/svg">xmlns:xlink&#61;&quot;http://www.w3.org/1999/xlink&quot;

Escaping HTML entities

it looks like span &nbsp; renders the literal &nbsp; instead of .
I know that I can do span== "&nbsp;" to escape it, but it feels like HTML entities should be rendered as normal when not in any code block.

So something like this

span &bull; would render a span with the escaped โ€ข
span= "&bull;" would render the literal &bull;
span== "&bull;" would render the escaped โ€ข

Minify HTML output

It would be great if it was possible to minify HTML output to save bytes. Most of the time, there is no need to output whitespaces. Maybe specify it with some kind option/variable.

optional end keyword

Hi @jeromegn

I like slang a lot, however sometimes I would like to see an end keyword, for some reasons

end keyword give some kind of structure to logic code, I know slang is slim compatible, but Do you think would be possible to add optional - end line inside slang templates? I mean:

Before: (looks good for tags, but I miss end keyword on blocks and statements)

It feels more like Python ๐Ÿ

- if user = session["user"]
  p You are
    span.user
      = user
    a.btn.btn-danger.btn-xs href="/logout"
      | Logout
  div#messages
  == form do
    == text_field(name: "message", placeholder: "Your message here")
- else
  == form do
    == csrf_tag
    == text_field(name: "user", placeholder: "Your name here")

After (using end keyword) ๐ŸŽ‰

Now It feels more like ruby/crystal ๐Ÿ’Ž

- if user = session["user"]
  p You are
    span.user
      = user
    a.btn.btn-danger.btn-xs href="/logout"
      | Logout
  div#messages
  == form do
    == text_field(name: "message", placeholder: "Your message here")
  - end
- else
  == form do
    == csrf_tag
    == text_field(name: "user", placeholder: "Your name here")
  - end
- end

Currently my templates looks like:

- if user = session["user"]
  p You are
    span.user
      = user
    a.btn.btn-danger.btn-xs href="/logout"
      | Logout
  div#messages
  == form(class: "form-group", id: "form-message") do
    == text_field(name: "message", autofocus: "", required: "", class: "form-control", id: "message", placeholder: "Your message here")
  - :end
- else
  == form(class: "form-group") do
    == csrf_tag
    == text_field(name: "user", autofocus: "", required: "", class: "form-control", placeholder: "Your name here")
  - :end
- :end

Just for blocks and statements, WDYT? ๐Ÿค”

Allow unescaped interpolation

I've been using Slang for a while in my Kemal project, but I noticed a while ago that whenever I tried to include some other page in my layout, I would have to HTML.unescape the final result for it to render properly. Is it possible to have an extra sigil (instead of =) that allows for unescaped variable interpolation? Thanks!

Using slang with Kemal error

I'm using slang with Kemal, but running in to a weird issue of slang swallowing __FILE__ before kemal gets to it.

# layout.slang
== yield_content("some_key")

# page.slang
- content_for("some_key") do
  h1 This does not get rendered

In Kemal, the yield_content macro takes the value of __FILE__ and adds that as the first element in this Tuple. Then it compares that value to the current file calling it. With ECR, the value here would be {"some_key" => {"views/page.ecr", #<Proc(String)...>}, but in slang the value here is {"some_key" => {"expanded macro: macro_1234332", #<Proc(String)...>}.

The current fix for this is to use this helper variable that's set in Kemal as the second argument to the content_for macro. Maybe you have an idea about this though?

multiline javascript strings using backtick do not compile

reported also in kilt jeromegn/kilt#14

For instance, when using Vue inline:

script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"

#app

javascript:
  new Vue({
    el: '#app',
    template: `
      <div>
        something
      </div>
    `
  })

It seems like the run macro raises the exception: unterminated parenthesized expression

Multiline attributes inside an attribute wrapper

in Slim, you can take multiple lines to set attributes on a tag, if you use an attribute wrapper. For example, here's something I just tried:

script{
  defer crossorigin="anonymous"
  src="https://use.fontawesome.com/releases/v5.0.13/js/all.js"
  integrity="sha384-xymdQtn1n3lH2wcu0qhcdaOpQwyoarkgLVxC/wZ5q7h9gHtxICrpcaSUfygqZGOe"
}

But in Slang (testing with 1.7.1), the above syntax produces an error on compilation, since it thinks there's an unterminated curly brace. This should probably work!

Incorrect parse of attribute value causes error

Whenever the last attribute of a tag is not a string literal, it incorrectly includes the closing parenthesis as part of the attribute value. This causes the generated template code to have an extra closing parenthesis, and causes an "unexpected token: )" error:

a(href=Constants::TWITTER_URL) Contact Us
--------------------------------------------------------------------------------
Syntax error in expanded macro: macro_4472530688:191: unexpected token: )

__kilt_io__ << (Constants::TWITTER_URL)).to_s.gsub(/"/,"&quot;")

Adding a space after the attribute fixes the issue:

a(href=Constants::TWITTER_URL ) Contact Us

But it'd be nice to not have to have this extra space every time there's a dynamic value.

Can't find file "secure_random"

This is the error in lib/slang/src/slang/nodes/text.cr:1: while requiring "secure_random": can't find file 'secure_random'

Awesome templating language

(Edit: I'm using crystal version 0.24.1)

Syntax error when using a "=" in an attribute

<script> tags will sometimes have SRI checking with an integrity attribute like the latest minified version of jQuery. This attribute contains a sha256 hash which ends in a = causing slang to throw a syntax error

script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"

Or in short hand

h1 id="asdf=" Hello
1. __kilt_io__ << "<h1"
2. __kilt_io__ << " id"
3. __kilt_io__ << "=\""
4. ("asdf=" Hello).to_s __kilt_io__
5. __kilt_io__ << "\""
6. __kilt_io__ << ">"
7. __kilt_io__ << "</h1>"
8. __kilt_io__ << "
9. "
--------------------------------------------------------------------------------
Syntax error in expanded macro: macro_4631584368:4: unexpected token: Hello
("asdf=" Hello).to_s __kilt_io__

Maybe this is related to #6 ?

Backslash handling

javascript:
  let foo = "bar\nbaz";

currently erroneously renders as

  let foo = "bar
baz";

I believe the backslash in HTML (or JavaScript) should be passed literally, not interpreted as string escape by Crystal, except inside Crystal interopolation #{...}.

Embedded HTML with double quoted attributes breaks interpreter

This template:

            div
              <img src="/">

Causes this compile time error:

Syntax error in expanded macro: macro_4629572560:239: unexpected token: DELIMITER_START

__kilt_io__ << ("<img src="/">").to_s(__kilt_io__)
                           ^

The " symbol isn't being escaped, and it's causing an error in the string literal.

A quick workaround for this is to use single quotes.

slang 1.7.1
crystal 0.24.2

How to do ternary conditional statement?

Not really an issue, but I would like to know how to achieve this, is it even possible?

I was thinking:
span.tag.#{game_server.status == "online" ? "online" : "offline"} = game_server.status

but i don't even know...

Compile Time or Runtime?

Can the README file be updated to clarify whether the pug -> html translation occurs at which of the following:

  • compile time
  • application initiation
  • first render
  • each render

This would help people understand the performance difference between using this or plain ECR files.

Kilt dependency and Kemal

Not an issue yet :
I have been using slang for some time for its simple elegance. Like slim, it helps me avoid the embedded syntax structures. Kemal is removing kilt dependency. How is that going to affect slang going forward? Will I be tied to ecr again? Ugh!

Thank you for writing this shard in the first place. I have quite a few apps in production that depend on it.

Including layouts

Support for including partials, like

include my_partial

would be nice

Add HTML entity support to Slang

Hello,

First of all, thank you for spending the time to create Slang! I've used Haml in the past and personally enjoy how Slang differs from it (I hope that doesn't change anytime soon!).

As I was playing around with Slang, I noticed that the ampersand symbol (&) always gets converted to &amp;. This doesn't work so well when you want to use HTML entities like &copy; and &euro;.

Input: &euro;
Expected output: &euro;
Actual output: &amp;euro;

Would love to have some feedback on this!

Crystal 0.19.x compatibility

I'm seeing some errors when attempting to run the specs for the project with Crystal 0.19.

>    Error in ./src/slang.cr:3: while requiring "./slang/node"
>
>    require "./slang/node"
>    ^
>
>    in ./src/slang/node.cr:52: while requiring "./nodes/*"
>
>    require "./nodes/*"
>    ^
>
>    Syntax error in ./src/slang/nodes/element.cr:10: unexpected token: as
>
>            names = attributes.delete("class") as Set
>                                               ^
>

Looks like the new syntax is object.as(Klass).

Windows end of line can give issues

When a file is saved under Windows to WSL, it has the default CRLF encoding instead of the expacted LF.

Compiling this results in:

macro didn't expand to a valid program, it expanded to:

================================================================================
--------------------------------------------------------------------------------
   1. __slang__ << "<html"
   2. __slang__ << ">"
.....
--------------------------------------------------------------------------------
Syntax error in expanded macro: macro_140680034121248:65: expecting '\n' after '\r'

"_slang__ << "Multi-line comment

Might be interesting to also add Windows end of line file support, as Crystal is recently making a lot of process towards a Windows version.

Crystal blocks in slang

#form_helper.cr

def form_for
  String.build do |str|
  str << "<form>"
  str << yield
  str << "</form>
end

Theoretically this should work.

body
  == form_for do |f|
    input type="hidden" name="_csrf" value="EFnnkNC/rFDHQclQemvVVEzi1WVMMbItMUQBk6apbE"
    br
    input type="submit"

If whoever wrote the code evaling code in the first place could work on this that would be amazing. If not I'll take a stab at it in a couple weeks. I'm in the middle of writing form helpers for the Amber framework right now.

https://github.com/Amber-Crystal/amber

What about HTML Boolean Attributes?

You can write <input type="checkbox" checked="checked"> as <input type="checkbox" checked>.

In slim you can write input(type="checkbox" checked) but this doesn't work with slang. What's the preferred way to to this?

Checked attribute?

Some attributes need to be removed if set to false

input name="active" checked=false

should render to

<input name="active" />

This doesn't seem to happen. Any ideas / work arounds for this?

Quotes in interpolation in attribute string do not get parsed correctly

Example:

a href="/t/#{thread["id"]}" = thread["subject"]

expands to

context.response << "<a"
context.response << " href"
context.response << "=\""
("/t/#{thread[").to_s context.response
context.response << "\""
context.response << ">"
context.response << HTML.escape(("id\"]}\" = thread[\"subject\"]").to_s).to_s(context.response)
context.response << "</a>"

which does not compile.

Workaround is to use %{} strings in interpolations.

slang with strings?

Slang works perfectly with files ( embed ) but the program will also use strings as to embed the slang information into the compiled executable.

My attempt at bypassing Slang its file requirement, is to run Slang.process_string but the whole buffer and other symbols are also output.

There are ways around it by temporary creating a file, writing the string value and then letting slang read it but that is silly and will have performance issues.

If there a way to force slang to run processes with string, without the whole buffer output?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.