https://github.com/arielnetworks/cl-markup.git
git clone 'https://github.com/arielnetworks/cl-markup.git'
(ql:quickload :cl-markup)
(html
(:body
(:p :id "title" "aiueo")))
;=> "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"
; \"http://www.w3.org/TR/html4/loose.dtd\">
; <html><body><p id=\"title\">aiueo</p></body></html>"
Generally CL-MARKUP generates efficient codes which mainly consists of series of `write-string's as much as possible. See how following two examples are expanded by macro expansion.
As you can see, the codes are a bit more complicated than that of CL-WHO because CL-MARKUP alters the destination of output in run-time.
Example A:
;; Example A
(let ((*output-stream* t))
(loop for (link . title) in '(("http://zappa.com/" . "Frank Zappa")
("http://marcusmiller.com/" . "Marcus Miller")
("http://www.milesdavis.com/" . "Miles Davis"))
do (markup (:a :href link
(:b title))
(:br))))
;; Example A: generated by CL-MARKUP
(let ((*output-stream* t))
(loop for (link . title) in '(("http://zappa.com/" . "Frank Zappa")
("http://marcusmiller.com/" . "Marcus Miller")
("http://www.milesdavis.com/" . "Miles Davis"))
do (if *output-stream*
(progn (write-string "<a href=\"" *output-stream*)
(write-string (escape-string (cl-markup::ensure-string link))
*output-stream*)
(write-string "\"><b>" *output-stream*)
(write-string (escape-string (cl-markup::ensure-string title))
*output-stream*)
(write-string "</b></a><br />" *output-stream*))
(with-output-to-string (#:G0)
(write-string "<a href=\"" #:G0)
(write-string (escape-string (cl-markup::ensure-string link)) #:G0)
(write-string "\"><b>" #:G0)
(write-string (escape-string (cl-markup::ensure-string title)) #:G0)
(write-string "</b></a><br />" #:G0)))))
Example B:
;; Example B
(markup
(:table :border 0 :cellpadding 4
(loop for i below 25 by 5
collect
(markup
(:tr :align "right"
(loop for j from i below (+ i 5)
collect
(markup
(:td :bgcolor
(if (oddp j)
"pink"
"green")
(format nil "~@R" (1+ j))))))))))
;; Example B: generated by CL-MARKUP
(if *output-stream*
(progn (write-string "<table border=\"0\" cellpadding=\"4\">"
*output-stream*)
(write-string (let ((#:G0
(loop for i below 25 by 5
collect (markup
(:tr
:align
"right"
(loop for j
from
i
below
(+ i 5)
collect (markup
(:td
:bgcolor
(if
(oddp j)
"pink"
"green")
(format
nil
"~@r"
(1+ j))))))))))
(if (consp #:G0)
(with-output-to-string (#:G1)
(dolist (#:G2 #:G0)
(write-string #:G2 #:G1)))
#:G0))
*output-stream*)
(write-string "</table>" *output-stream*))
(with-output-to-string (#:G0)
(write-string "<table border=\"0\" cellpadding=\"4\">"
#:G0)
(write-string (let
((#:G0
(loop for i below 25 by 5
collect (markup
(:tr
:align
"right"
(loop for j
from
i
below
(+ i 5)
collect (markup
(:td
:bgcolor
(if
(oddp j)
"pink"
"green")
(format
nil
"~@r"
(1+
j))))))))))
(if
(consp #:G0)
(with-output-to-string
(#:G1)
(dolist
(#:G2 #:G0)
(write-string #:G2 #:G1)))
#:G0))
#:G0)
(write-string "</table>" #:G0)))
markup
is the simplest way to generate HTML.
(markup (:p "あいうえお"))
;=> "<p>あいうえお</p>"
By default, CL-MARKUP follows XHTML valid styling.
(markup (:br))
;=> "<br />"
You can configure the style by setting *markup-language*
.
(eval-when (:compile-toplevel :load-toplevel :execute)
(setf *markup-language* :html))
Don't forget to wrap setf
with eval-when
since it is used in compile-time in order to expand
markup
. This also means you are NOT allowed to write
codes like this:
;; THIS IS A WRONG EXAMPLE!!
(let ((*markup-language* :html))
(markup (:br)))
;=> "<br />"
In case you really want to delay the decision until run-time, use
markup*
, a functional version of markup
.
;; This is a correct one.
;; But I don't recommend this for performance.
(let ((*markup-language* :xhtml))
(markup* '(:br)))
;=> "<br>"
Other macros such as html
, xhtml
,
html5
, and xml
output DOCTYPE before
markup
.
(html (:p "あいうえお") (:br))
;=> "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\"><html><p>あいうえお</p><br></html>"
(html5 (:p "あいうえお") (:br))
;=> "<!DOCTYPE html><html><p>あいうえお</p><br></html>"
(xhtml (:p "あいうえお") (:br))
;=> "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html><p>あいうえお</p><br /></html>"
(xml (:p "あいうえお") (:br))
;=> "<?xml version=\"1.0\" encoding=\"UTF-8\"?><p>あいうえお</p><br />"
Embedded strings are escaped automatically.
(markup (:p "Tiffany & Co."))
;=> "<p>Tiffany & Co.</p>"
If you don't want this behavior, set *auto-escape*
nil
or use raw
for temporal suppression.
(let ((*auto-escape* nil))
(markup (:p "Tiffany & Co.")))
;=> "<p>Tiffany & Co.</p>"
(markup (:p (raw "Tiffany & Co.")))
;=> "<p>Tiffany & Co.</p>"
Also, when you want to ensure a certain code to be escaped (maybe
inside raw
) use esc
, which has the similar
syntax as that of raw
.
Markup
macros returns html as a string. This behavior can
be customized by modifying *output-stream*
which is
defaulted to *standard-output*
.
;; Default behavior
(let (*output-stream*)
(markup (:p "hoge"))
;=> "<p>hoge</p>"
;; Output to *standard-output* directly
(let ((*output-stream* t))
(markup (:p "hoge")))
;;=> <p>hoge</p>
;=> "<p>hoge</p>"
You can embed Lisp code in the body of each tag.
(markup (:ul (loop for item in '(1 2 3) collect (markup (:li item)))))
For more readability, CL-MARKUP provides a reader macro #M
which can be enabled by (enable-markup-syntax)
.
(enable-markup-syntax)
#M(:ul (loop for item in '(1 2 3) collect #M(:li item))))
Copyright (c) 2011 Eitarow Fukamachi.
Contributors:
m7d - HTML5 support, April 21, 2012.
Licensed under the LLGPL License.