在漂亮打印的Clojure表单周围包装HTML标签



Clojure的漂亮打印机(Clojure .pprint)接受如下未格式化的代码:

(defn fib ([n] (fib n 1 0)) ([n a b] (if (= n 0) a (fib (dec n) (+ a b) a))))

让它看起来很漂亮,像这样

(defn fib
([n] (fib n 1 0))
([n a b]
(if (= n 0)
a
(fib (dec n) (+ a b) a))))

我想把一些资源在网页上,所以我希望它是漂亮的印刷。但我还想把每个表单包装在一组<跨度比;标记具有唯一的ID,以便我可以用javascript操作表示。也就是说,我想把>

(foo bar baz)

<span id="001">(<span id="002">foo</span> <span id="003">bar</span> <span id="004">baz</span>)</span>

但是我仍然希望生成的表单像漂亮的打印机那样缩进,以便实际显示的代码看起来正确。

这个漂亮的打印机的一些文档提到它可以使用自定义分派函数,但是我找不到任何关于它们做什么或如何定义它们的信息。有可能用这样的野兽做我想做的事吗?如果是这样的话,有人能给我提供一些关于如何做到这一点的信息吗?

有很多方法可以打印出漂亮的XML,如下所示:https://nakkaya.com/2010/03/27/pretty-printing-xml-with-clojure/

那个人使用

(defn ppxml [xml]
(let [in (javax.xml.transform.stream.StreamSource.
(java.io.StringReader. xml))
writer (java.io.StringWriter.)
out (javax.xml.transform.stream.StreamResult. writer)
transformer (.newTransformer 
(javax.xml.transform.TransformerFactory/newInstance))]
(.setOutputProperty transformer 
javax.xml.transform.OutputKeys/INDENT "yes")
(.setOutputProperty transformer 
"{http://xml.apache.org/xslt}indent-amount" "2")
(.setOutputProperty transformer 
javax.xml.transform.OutputKeys/METHOD "xml")
(.transform transformer in out)
(-> out .getWriter .toString)))

所以如果你把你的HTMl字符串(不完全是XML的子集),你会得到:

(ppxml "<root><child>aaa</child><child/></root>")

输出:

<?xml version="1.0" encoding="UTF-8"?>
<root>
<child>aaa</child>
<child/>
</root>

在Clojure中,使用Compojure,您可以用非常简洁的语法构建HTML/XML标记。你也可以使用它们:

(ppxml (html
[:html
[:head
[:title "Hello World"]]
[:body "Hello World!"]]))

输出:

<html> 
<head> 
<title>Hello World</title> 
</head> 
<body>Hello World!</body> 
</html>

你也可以在这里看到建议:合成HTML格式

这是可能的,但我认为这比你想象的要多得多。您将使用源代码pprint/dispatch并修改已经在这里的函数。

你肯定需要with- print-dispatch。这个函数使用给定的分派函数来执行body:

(with-pprint-dispatch code-dispatch
(pprint '(foo bar baz)))
(foo bar baz)
=> nil

查找函数code-dispatch并查看它的定义:

(defmulti 
code-dispatch
"The pretty print dispatch function for pretty printing Clojure code."
{:added "1.2" :arglists '[[object]]} 
class)
(use-method code-dispatch clojure.lang.ISeq pprint-code-list)
(use-method code-dispatch clojure.lang.Symbol pprint-code-symbol)
;; The following are all exact copies of simple-dispatch
(use-method code-dispatch clojure.lang.IPersistentVector pprint-vector)
(use-method code-dispatch clojure.lang.IPersistentMap pprint-map)
(use-method code-dispatch clojure.lang.IPersistentSet pprint-set)
(use-method code-dispatch clojure.lang.PersistentQueue pprint-pqueue)
(use-method code-dispatch clojure.lang.IDeref pprint-ideref)
(use-method code-dispatch nil pr)
(use-method code-dispatch :default pprint-simple-default)

可以看到,每个集合类型都有一个特殊的函数。我选择了list和vector分派函数是这样的

(defmulti
my-code-dispatch
class)
(use-method my-code-dispatch clojure.lang.ISeq my-pprint-code-list)
(use-method my-code-dispatch clojure.lang.IPersistentVector my-pprint-vector)
(use-method my-code-dispatch :default my-pprint-simple-default)

现在,寻找pprint-code-list,pprint-vectorpprint-simple-default。其中两个使用pprint-logical-block和关键字:prefix:suffix-这是您插入额外字符串的地方(函数的其余部分将相同)。不要忘记为span编号定义一些计数器:

(in-ns 'clojure.pprint)
(def id (atom 0))
(defn- my-pprint-vector [avec]
(pprint-meta avec)
(pprint-logical-block :prefix (format "<span id="%03d">[" (swap! id inc))
:suffix "]</span>"
...)
(defn- my-pprint-simple-default [obj]
(cond
(.isArray (class obj)) (pprint-array obj)
(and *print-suppress-namespaces* (symbol? obj)) (print (name obj))
:else (cl-format true "<span id="~3,'0d">~s</span>"
(swap! id inc)
obj)))
(defn- my-pprint-simple-code-list [alis]
(pprint-logical-block :prefix (format "<span id="%03d">(" (swap! id inc))
:suffix ")</span>"
...)
(defn- my-pprint-code-list [alis]
(if-not (pprint-reader-macro alis)
(if-let [special-form (*code-table* (first alis))]
(special-form alis)
(my-pprint-simple-code-list alis))))

设置好之后,我调用:

(with-pprint-dispatch my-code-dispatch
(pprint '(foo bar baz)))
<span id="001">(<span id="002">foo</span>
<span id="003">bar</span>
<span id="004">baz</span>)</span>
=> nil

也可以打印成字符串:

(with-out-str (with-pprint-dispatch my-code-dispatch
(pprint '(foo bar baz))))
=>
"<span id="001">(<span id="002">foo</span>r
<span id="003">bar</span>r
<span id="004">baz</span>)</span>r
"

我必须再次提到,为了打印一些真正的代码,您必须修改所有数据类型的所有函数。这是可能的吗?是的。这些努力值得吗?我很怀疑。

最新更新