hemmet is a CLI-tool, that expands text snippets to markup blocks in Haskell/Elm/HTML/CSS/Bash. The template language is similar to Emmet/ZenCoding (has a subset of their features) and has an optional BEM flavour :) Also hemmet can generate file trees (useful for project scaffolding).
$ echo "#root>h1.red+p.article" | hemmet dom html
<div id="root">
<h1 class="red"></h1>
<p class="article"></p>
</div>
In addition to the CLI-tool there is a TUI-app hemmeti which one can use to write templates having a live preview.
That's how the HTML generation looks like:
And this is a file tree scaffolding:
$ hemmet INPUT OUTPUT -e EXPRESSION
or
$ echo "EXPRESSION" | hemmet INPUT OUTPUT
See hemmet --help
for full options list.
hemmeti
uses the same options but runs interactively.
dom
works with DOM-templates,bem
works with BEM-templates,ftree
works with file tree templates.
Hemmet expands Emmet-like templates and produces these formats (outputs)
html
, just HTML
echo "#root>h1.red+p.article" | hemmet dom html
<div id="root">
<h1 class="red"></h1>
<p class="article"></p>
</div>
css
, styles for all classes in the template
echo "#root>h1.red+p.article" | hemmet dom css
.article {
}
.red {
}
elm
, an Elm.Html markup
echo "#root>h1.red+p.article" | hemmet dom css
div [ id "root" ]
[ h1 [ class "red" ] []
, p [ class "article" ] []
]
lucid
, the Lucid HTML eDSL
echo "#root>h1.red+p.article" | hemmet dom css
div_ [id_ "root"] $ do
h1_ [class_ "red"]
p_ [class_ "article"]
p+ul>(li+li+ul>li+li)+p
<p></p>
<ul>
<li></li>
<li></li>
<ul>
<li></li>
<li></li>
</ul>
</ul>
<p></p>
Tag name prepends the id or classes if any. If no tag was defined the div
will be used.
Just #id
, one at time.
Just .class.another
, simple that.
Hemmet expands BEM-templates with structure checking and produce outputs:
react-flux
— eDSL for react-flux Haskell library
$ echo ":foo>.bar" | hemmet bem react-flux
divc_ "foo" $ do
divc_ "foo__bar" $ pure ()
html
$ echo ":foo>.bar" | hemmet bem html
<div class="foo">
<div class="foo__bar"></div>
</div>
css
$ echo ":foo>.bar" | hemmet bem css
.foo {
}
.foo__bar {
}
Tags are the same.
form:form>.submit:button>img.icon:icon+.label:label
<form class="form">
<div class="button form__submit">
<img class="icon button__icon"></img>
<div class="label button__label"></div>
</div>
</form>
form:login-form>button.submit-button:button~small~disabled
<form class="login-form">
<button class="button button_small button_disabled login-form__submit-button"></button>
</form>
:foo$bar~baz
divc_ ("foo foo_baz" <> bar) $ pure ()
Note: at the moment variables are available only for the react-flux
output!
<:foo>.bar+.baz
(note leading <
)
<div class="foo__bar"></div>
<div class="foo__baz"></div>
The ftree
templates can be transformed to:
tree
, the pseudographical file tree representation.
$ echo "docs/{todo.txt to_read.txt}" | hemmet ftree tree
.
└── docs/
├── to_read.txt
└── todo.txt
bash
script, that constructs a real tree!
$ echo "docs/{todo.txt to_read.txt}" | hemmet ftree bash
#!/bin/bash
cat <<PREVIEW_END
This file tree will be created:
.
└── docs/
├── to_read.txt
└── todo.txt
PREVIEW_END
read -p "Press any key to continue..." -n1 -s
set -euf -o pipefail
mkdir "docs" && pushd "docs"
touch "to_read.txt"
touch "todo.txt"
popd
You can even make a shell script that will call the TUI and then execute the result of generation automatically.
With |hs|
prefix you can scaffold Haskell projects:
$ echo "|hs|app/main src/*lib/{data-types utils} !foo-bar" | hemmet ftree tree
.
├── App/
│ └── Main.hs
├── Src/
│ ├── Lib/
│ │ ├── DataTypes.hs
│ │ └── Utils.hs
│ └── Lib.hs
└── foo-bar
Note that
- files get
.hs
extension, *
beforelib
means "also create a.hs
module for this folder",!
before any name means "don't touch this item"- "kebab-case" morphs to "CamelCase"
With |py|
prefix you can scaffold Python projects:
$ echo "|py|src/*package/{core str-utils} !foo-bar" | hemmet ftree tree
.
├── foo-bar
└── src/
└── package/
├── __init__.py
├── core.py
└── str_utils.py
Note that
- files get
.py
extension, *
beforepackage
means "also create an__init__.py
module",!
before any name means "don't touch this item"- "kebab-case" morphs to "snake_case"
- put a
hemmet
binary somewhere in$PATH
- add to your
.emacs
(defun hemmet-expand-region ()
(interactive)
(let ((f (lambda (b e)
(shell-command-on-region
b e "hemmet dom html" t t "*hemmet error*" t))))
(if (region-active-p)
(funcall f (region-beginning) (region-end))
(funcall f (line-beginning-position) (line-end-position)))
))
;; bind using a function from "bind-key" package
(bind-key "C-c C-j" 'hemmet-expand-region html-mode-map)
;; or just use built-in function
(define-key haskell-mode-map (kbd "C-c C-j") 'hemmet-expand-region)