[分享] pp-html.el 在emacs中快速生成复杂html。

[分享] 使用elisp很方便地打印html 继续讨论:

基于之前的代码,增加了类似Django的模版标签,组织成了package放在了github上。第一次写package,可能有些不成熟的地方,欢迎指正。(顺手给个star呗 :laughing:

目前支持 include, if, each, extend(继承) and block 这些逻辑标签。基本的思路是:当列表的第一个元素为标签对象时,正常解析标签(之前的帖子解释的很详细);当第一个元素为逻辑标签时,按照特定的逻辑解析。

这里给一个简单的例子,其余的README里面写的很详细。

(setq comment nil)
(setq comment-div1 '((a :class "if-test" "if-test-content")))
(setq comment-div2 '((a :class "else-test" "else-test-content")))
(setq front ‘((span :class "test" "test-content")))

(pp-html
 `(div :class "post-info"
       (p "「"
	     (:include front)
	     (:if ,comment
	         (:include comment-div1)
	         (:include comment-div2))
	     (ul
	       (:for fruit
	         ("apple" "peach" "orange" "grape")
	         (li :class "fruit" fruit)))
	     (span "分类")
	     (span "字数")
	     (span :id "id" :class "class"
		   (span :class "post-meta-item-text" "阅读 ")
		   (span :class "leancloud-visitors-count" "...")
		   " 次")
	  "」")))

结果:

<div class="post-info">
  <p>
    「
    <span class="test">test-content</span>
    <a class="if-test">if-test-content</a>
    <ul>
      <li class="fruit">apple</li>
      <li class="fruit">peach</li>
      <li class="fruit">orange</li>
      <li class="fruit">grape</li>
    </ul>
    <span>分类</span>
    <span>字数</span>
    <span id="id" class="class">
      <span class="post-meta-item-text">阅读 </span>
      <span class="leancloud-visitors-count">...</span>
      次
    </span>
    」
  </p>
</div>

另外,pp-html-test 可以在view-buffer里预览生成的html code。

后续计划:

融合bootstrap等css框架,更加方便控制html布局。

2 个赞

其它生成 XML 的包:

由于 XML、HTML 跟 Sexp 的结构相似,scheme、racket、clojure、common lisp 好像有不少现有用 sexp 表示 xml/html 的方法,或者用来模版,或许值得参考。不过大家也不会用 Emacs 来开发 Web 应用,估计没什么机会用上。另外,能生成专门的 HTML5 应该是个加分项,比如:

;; HTML5
<img>
;; XML
<img />
1 个赞

其实 HTML 5 也是建议用 <img /> 这种形式的啊。

说 HTML5 和 XML 差别也有,比如 boolean attribute,不知道帖主的包支不支持

是吗?我其实也不了解,只是直觉上没必要加的就不加。

还是加上好,更严谨一点

在 LISP 中用纯 list 结构(或者叫 s-exp)来表达 XML / HTML 有点好处是可以直接使用 LISP 里的各种方法,尤其指 quasiquote, unquote, unquote-splice。利用这些完全可以实现插入、条件插入等功能,比如你上面的例子就可以写成:

(pp-html
 `(div :class "post-info"
       (p "「"
          ,@front
          ,@(if comment 
                comment-div1
                comment-div2)
           (ul
             ,@(mapcar (lambda (item) `(li :class "fruit" ,item))
                 '("apple" "peach" "orange" "grape")))
          ...
          "」")))

这样就不需要 :include, :if, :each 这种东西了,同样很简单。

2 个赞

谢谢回复,看了下那两个package,第二个竟然和我的实现效果一模一样😅,,,,

不过它的代码好像比我简单,可以学习一下。

boolean attribute 是什么?

谢谢回复,我知道S表达式中可以写些复杂的逻辑(但不知道怎么写😅),用:include :each … 这样的关键字封装主要为了写法尽可能统一,也便于看出html的结构。不过用各种quote封装,我的实现就可以更简单了,而且可以有更复杂的逻辑。

还有个继承的例子,不知道用quote的方式怎么实现?


 (setq base-html
	  '(body
	     (h1 :id "logo" "戈楷旎")
	     (p :id "description" "happy hacking emacs")
	     (div :id "content"
		   (:block main (p "this is default content")))
	     (div :id "postamble"
		   (:block end (p "this is default postamble")))))

 (pp-html
  `(:extend ,base-html
	       ;; (:block main (p "this is the extend content"))
	       (:block end (p "this is the extend postamble"))))

结果:

 <body>
   <h1 id="logo">戈楷旎</h1>
   <p id="description">happy hacking emacs</p>
   <div id="content">
	 <p>this is default content</p>
   </div>
   <div id="postamble">
	 <p>this is the extend postamble</p>
   </div>
 </body>

不太好找常见的例子,boolean attribute 就是指这个属性的值只能为 boolean,于是就省写它的值,通过这个属性存不存在来表示 true 和 false,比如 <Button disabled /> 这样,写了就表示 “disabled”,不写就是 “enabled”。

似乎不太合适用 quasiquote 来实现,但是直接对 list 操作还是很容易的:

(pp-html
  (append base-html 
          '((:block end (p "this is the extend postamble")))))

当然写 quasiquote 也不是不可以:

(pp-html
  `(body ;; 甚至可以用 ,(car base-html)
     ,@(cdr base-html)
     (:block end (p "this is the extend postamble"))))
1 个赞

这个不对吧,继承的意思是在extend中替换base中相同block的内容,没有则使用base中的默认内容。 这需要匹配名称相同的block,然后修改block内容,继承其余默认内容。 append不是用来连接的吗?

现在也支持这种boolean attribute了,将属性的值设为nil就可以。 例子:

(pp-html '(button :class "btn" :disable nil))

结果:

<button class="btn" disable></button>