views:

312

answers:

4

I'm looking for a solution that allows me to write native Emacs Lisp code and at compile time turns it into HTML, like Franz's htmlgen:

(html
 ((:div class "post")
  (:h1 "Title")
  (:p "Hello, World!")))

Of course I can write my own macros, but I'm interested if there are any projects around this problem.

A: 

This is not quite what you're looking for, but there's a 20 minute video where a guy creates a simple website using UCW, the UnCommon Web application framework. It's all done in Emacs using lisp...

Here is a link to the transcript (all the code (~25 lines) is available at the end of the transcript).

Trey Jackson
Thanks, but that's really not what I'm looking for. I've got an existing Elisp framework that has some HTML generating facilities—currently with hard-coded HTML strings I'd like to replace.
Török Gábor
+2  A: 

This could be a starting point: http://www.emacswiki.org/emacs/HtmlLite

js
Thanks, this is useful, too. My only problem with it that it pollutes the namespace with many functions instead of using a macro.
Török Gábor
+1  A: 

Meanwhile, I found some code that contains something similar I want. Now I can write:

(views-with-html
 ((body)
  (h1 "Title")
  ((p (class . "entry")) "Hello, World!")))

The implementation has a few limitations (e.g. hard-coded element list), but it seems to be a good starting point.

Török Gábor
+1  A: 

As you found out, xmlgen generates XML from a list structure. What I did find disappointing with the `xmlgen package that the format it supports is not quite the inverse of Emacs' xml parser.

I did add this to my copy of xmlgen:

;; this creates a routine to be the inverse of what xml-parse does
;;;###autoload
(defun xml-gen (form &optional in-elm level)
  "Convert a sexp to xml:
  '(p :class \"big\")) => \"<p class=\\\"big\\\" />\""
  (let ((level (or level 0)))
    (cond
     ((numberp form) (number-to-string form))
     ((stringp form) form)
     ((listp form)
      (destructuring-bind (xml attrs) (xml-gen-extract-plist form)
        (let ((el (car xml)))
          (unless (symbolp el)
            (error "Element must be a symbol (got '%S')." el))
          (setq el (symbol-name el))
          (concat "<" el (xml-gen-attr-to-string attrs)
                  (if (> (length xml) 1)
                      (concat ">" (mapconcat
                                   (lambda (s) (xml-gen s el (1+ level)))
                                   (cdr xml)
                                   "")
                              "</" el ">")
                    "/>"))))))))

(defun xml-gen-attr-to-string (plist)
  (reduce 'concat (mapcar (lambda (p) (concat " " (symbol-name (car p)) "=\"" (cdr p) "\"")) plist)))

(defun xml-gen-extract-plist (list)
  (list (cons (car list) (let ((kids (xml-node-children list)))
                           (if (= 1 (length kids))
                               kids
                             (remove-if-not 'listp kids))))
        (xml-node-attributes list)))

Note: the interface for this is xml-gen (not xmlgen which is the original parsing).

With this interface, the following holds:

(string-equal (xml-gen (car (xml-parse-region <some-region-of-xml>)))
              <some-region-of-xml>)

and

(equal (car (xml-parse-region (insert (xml-gen <some-xml-form>))))
       <some-xml-form>)

The new xml-gen does not strive to preserve the whitespace around that the xml-parse-region routine generates.

Trey Jackson