Tuhinga markup language

https://travis-ci.org/babab/tuhinga.svg?branch=master

Tuhinga is a minimalistic markup language that translates to XML/HTML. It can help you reduce typing and quicken the editing process of HTML pages. It may remind you a little of HAML since it shares a few common concepts, but it has different goals. HAML is essentially a template language, while tuhinga is a tiny markup language primarily aimed to be just a precursor for XML/HTML documents.

The only true goal of Tuhinga is conciseness. And this is also where it stands out against other solutions. It is the prettiest. This means it may never support XML and HTML for the full 100%, since that would cause the need for a much more expansive syntax. Use it if you think HAML is a good idea, but not beatiful enough. Otherwise I’d advise to just use HAML, since it is much more mature and has numerous implementations.

The implementation of Tuhinga is written in Python. Supported Python versions are 2.7 and 3.2 and later.

A tuhinga example document

An example of a HTML5 (*.tuh) document:

; Comments start with ;

html5
  head
    meta-charset utf-8
    meta :name=viewport device-width, initial-scale=1.0
    title Page title
  body
    #main.container
      h1.page-header Page title
      .row
        .col-lg-12
          p Paragraph line 1
            :: line 2
            :: line 3
            small line 4
          p
            :: line 1
            :: line 2
    #footer.container
      p.muted Copyright & 2015 Me

After converting to HTML:

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="device-width, initial-scale=1.0">
    <title>Page title</title>
  </head>
  <body>
    <div id="main" class="container">
      <h1 class="page-header">Page title</h1>
      <div class="row">
        <div class="col-lg-12">
          <p>
            Paragraph line 1
            line 2
            line 3
            <small>line 4</small>
          </p>
          <p>
            line 1
            line 2
          </p>
        </div>
      </div>
    </div>
    <div id="footer" class="container">
      <p class="muted">Copyright &amp; 2015 Me</p>
    </div>
  </body>
</html>

The tuhinga equivalent of any HTML output uses roughly 33% less characters, and not a single angle bracket.

Using Tuhinga to write XML / HTML5

The handling of certain symbols like js, meta, input and other void elements and the $content that follows is done by applying a set of rules, called a mapping. All other elements are treated with the default handling of the Lexer (currently there is only a XML/HTML Lexer).

What follows are the rules that are applied with the default mapping. You can alter how these mappings work though. This means you can easily add your own symbols.

Special symbols

  • html5: sets doctype; is expanded to <!doctype html><html> ... </html>
  • css: expanded to <link rel=”stylesheet” {href=”$content“}>
  • input: expanded to <input {value=”$content“}>
  • input-*: expanded to <input type=”*” {value=”$content“}>
  • js: an alternative for writing script-src
  • link: expanded to <link {href=”$content“}>
  • meta: expanded to <meta {content=”$content“}>
  • meta-charset: expanded to <meta charset=”$content“>
  • script-src: expanded to <script {src=”$content“}></script>

Recognised as void elements (elements that do not close)

area, base, br, col, embed, hr, img, keygen, param, source, track, input (mapped content), link (mapped content), meta (mapped content), wbr

Convert tuhinga templates with the python module

Tuhinga is distributed as a single module and can be downloaded and used directly. If you install Tuhinga into your system or virtualenv, you can use the more convenient tuh executable script. If you use the module, simply replace tuh with ./tuhinga.py in the instructions below.

Converting a document is simple:

$ tuh somedocument.tuh > somedocument.html

You can also read from stdinput:

$ cat somedocument.tuh | tuh > somedocument.html  # passing a file
$ tuh > somedocument.html # typing a doc directly in the terminal

The Tuhinga module itself has no external dependencies. The Tuhinga webREPL is distributed independently and requires bottle.

Download and install

Tuhinga itself has no external dependencies. If you have pip installed, you can just:

# pip install tuhinga

To work with the current development version, do something like this:

$ git clone git://bitbucket.org/babab/tuhinga.git
# cd tuhinga
# python setup.py install

Convert tuhinga templates with the instant webREPL

Use the webREPL as an easy way to fiddle around with writing tuhinga documents or use it as a serious tool to quickly write up your pages. It will give instant feedback of the output after each keystroke.

The webREPL is written using the bottle Python micro-framework, which is not a dependency of tuhinga itself. Therefore, you must be sure to have bottle installed if you wish to use it.

Install bottle (in a virtualenv)

$ pip install bottle

Run the webREPL

$ ./tuhinga_webrepl.py

Now you can visit http://localhost:8080/ and play around.

Syntax file for Vim

If you use Vim for your editing, you can install the syntax file to have pretty syntax highlighting for Tuturu (*.tuh) documents. It’s my first go at writing a syntax.vim file and it currently has some small bugs, which should probably be resolved soon.

http://i.imgur.com/uqpEpjN.png

Install the tuh.vim syntax file into your .vim folder:

$ mkdir -p ~/.vim/syntax
$ cp tuh.vim ~/.vim/syntax

And use it in your Vim buffer with :set filetype=tuh

License

Copyright (c) 2014-2015 Benjamin Althues <benjamin@babab.nl>

Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

Change Log

All notable changes to tuhinga will be documented here. The project adheres to Semantic Versioning.

0.1.1 - 2013-05-28

Added

  • Package as (source and) wheel distribution to speed up installations
  • This CHANGELOG

0.1.0 - 2013-05-21

Added

  • Initial release

Developer documentation / API Reference

Shortcut functions

tuhinga.file(filelocation, input_indent=2, output_indent=2)[source]

Shortcut for parsing, lexing and mapping a document from file

tuhinga.string(string, input_indent=2, output_indent=2)[source]

Shortcut for parsing, lexing and mapping a document from a string

tuhinga.stdin(input_indent=2, output_indent=2)[source]

Shortcut for parsing, lexing and mapping from stdin/fileinput

Default settings

tuhinga.DEFAULT_INPUT_INDENT = 2

The standard value of tuhinga’s indentation is 2 spaces

tuhinga.DEFAULT_OUTPUT_INDENT = 2

The output can be set as a negative value to create condensed one liners

Parser and Lexer objects

class tuhinga.Parser(input_indent=2)[source]

Parse a tuhinga doc and create nodes to be processed with a lexer

close()[source]

Close all open nodes

file(filename)[source]

Parse a complete tuhinga document by filename

fileinput()[source]

Parse stdin or files with the fileinput module

parseLine(line)[source]

Parse a single line of tuhinga markup

Make sure to run close() after the last call to parseLine.

string(string)[source]

Parse a complete tuhinga document as string

class tuhinga.LexerXML(parser, output_indent=2)[source]

Lexical compilation of parsed nodes to XML markup

Default mapper

tuhinga.mapper = {'html5': {'input-tel': {'c': 'value', 'e': 'input', 'h': 'type="tel"', 'v': True}, 'input-text': {'c': 'value', 'e': 'input', 'h': 'type="text"', 'v': True}, 'base': {'v': True}, 'input-checkbox': {'c': 'value', 'e': 'input', 'h': 'type="checkbox"', 'v': True}, 'input-reset': {'c': 'value', 'e': 'input', 'h': 'type="reset"', 'v': True}, 'hr': {'v': True}, 'input-number': {'c': 'value', 'e': 'input', 'h': 'type="number"', 'v': True}, 'input': {'c': 'value', 'v': True}, 'input-search': {'c': 'value', 'e': 'input', 'h': 'type="search"', 'v': True}, 'input-button': {'c': 'value', 'e': 'input', 'h': 'type="button"', 'v': True}, 'input-url': {'c': 'value', 'e': 'input', 'h': 'type="url"', 'v': True}, 'col': {'v': True}, 'param': {'v': True}, 'track': {'v': True}, 'js': {'c': 'src', 'e': 'script', 'h': 'type="text/javascript"'}, 'input-datetime': {'c': 'value', 'e': 'input', 'h': 'type="datetime"', 'v': True}, 'input-month': {'c': 'value', 'e': 'input', 'h': 'type="month"', 'v': True}, 'input-hidden': {'c': 'value', 'e': 'input', 'h': 'type="hidden"', 'v': True}, 'input-image': {'c': 'value', 'e': 'input', 'h': 'type="image"', 'v': True}, 'wbr': {'v': True}, 'area': {'v': True}, 'keygen': {'v': True}, 'input-file': {'c': 'value', 'e': 'input', 'h': 'type="file"', 'v': True}, 'link': {'c': 'href', 'v': True}, 'script-src': {'c': 'src', 'e': 'script'}, 'input-time': {'c': 'value', 'e': 'input', 'h': 'type="time"', 'v': True}, 'source': {'v': True}, 'input-datetime-local': {'c': 'value', 'e': 'input', 'h': 'type="datetime-local"', 'v': True}, 'input-submit': {'c': 'value', 'e': 'input', 'h': 'type="submit"', 'v': True}, 'embed': {'v': True}, 'img': {'v': True}, 'input-radio': {'c': 'value', 'e': 'input', 'h': 'type="radio"', 'v': True}, 'input-email': {'c': 'value', 'e': 'input', 'h': 'type="email"', 'v': True}, 'input-password': {'c': 'value', 'e': 'input', 'h': 'type="password"', 'v': True}, 'meta': {'c': 'content', 'v': True}, 'input-date': {'c': 'value', 'e': 'input', 'h': 'type="date"', 'v': True}, 'input-color': {'c': 'value', 'e': 'input', 'h': 'type="color"', 'v': True}, 'css': {'c': 'href', 'e': 'link', 'h': 'rel="stylesheet"', 'v': True}, 'input-range': {'c': 'value', 'e': 'input', 'h': 'type="range"', 'v': True}, 'meta-charset': {'c': 'charset', 'e': 'meta', 'v': True}, 'br': {'v': True}, 'input-week': {'c': 'value', 'e': 'input', 'h': 'type="week"', 'v': True}}}

Mapping of contents to arguments / list of void elements

Possible keys:
  • ‘v’: True if void element like <meta>. Default = false
  • ‘e’: HTML element. Default = <name_of_dict_key>
  • ‘c’: Content mapping, see below. Default = ‘>’
  • ‘h’: Extra html arguments. Default = false
Possible value of content:
  • ‘>’: print contents after start tag (default)
  • ‘-‘: strip any contents
  • ‘some-string’: map any contents to an html argument