Emacs Muse 是一個可以在 Emacs 中寫 Wiki 文檔的插件,通過 Emacs Muse,我們可以很容易地編寫 Wiki 文檔,並生成各種格式的文件。本文介紹了如何擴展使用 Emacs Muse —— 一個 Emacs 編輯器插件來生成精美的測試結果報告。
前言
Emacs 是一個開放源代碼的編輯器。由於它使用效率高,可擴展性強,自20世紀70年代誕生以來就一直經久不衰,受到廣大開發人員的熱烈追捧。關於 Emacs 的各種介紹和使用技巧教程屢見不鮮。而本文所介紹的 Emacs Muse 是 Emacs 的一個擴展插件,它的前身就是 Emacs Wiki。由於 Emacs Wiki Mode 原作者 Michael Olson 需要重新架構代碼,才另外創立了一個新的 Emacs Muse 項目。通過該插件,我們可以在 Emacs 中寫 Wiki 文檔,生成各種格式,包括網頁,pdf ,DocBook ,LaTex 等等,並可以直接發布到網路中。
我們在工作過程中,往往需要製作一些結果報告,例如測試結果報告。在一份測試報告中,需要統計測試結果,並對結果進行一定的分析和總結。如果我們想製作一份精美的報告,包括能對不同的結果著色,自動統計結果,對結果分析能有按照不同的格式來突出顯示,這樣的工作完全可以通過 Emacs Muse 來實現。而且 Emacs Muse 可能按照 Wiki 網頁的要求來生成 HTML 格式的網頁,發布到網路中供人瀏覽。
![]() ![]() |
Emacs Muse 的安裝與配置
從 Emacs Muse 的官方網站上(參考資源)可以下載最新的 Muse 安裝包,解壓后修改Makefile.defs 文件來設置 Emacs 的安裝路徑,如清單1所示,該路徑是 Mac OS X 平台下 Emacs 的設置,其他平台也可以類似修改。
#設置EMACS的路徑 EMACS = emacs SITEFLAG = --no-site-file #設置Muse安裝路徑 DESTDIR = PREFIX = /Applications/Emacs.app/Contents/Resources ELISPDIR = $(DESTDIR)$(PREFIX)/site-lisp/muse INFODIR = $(DESTDIR)$(PREFIX)/info |
安裝路徑設置完成後,用 GNU Make 編譯安裝 Muse ,只需要運行 make install 命令即可。
安裝完成後,我們需要修改 Emacs 的配置文件 .emacs 來載入 Muse 包,添加如清單2所示的代碼到 .Emacs 文件中。
(require 'muse-mode) (require 'muse-html) ;添加html格式的支持 (require 'muse-latex) ; 添加latex格式的支持 (require 'muse-texinfo) ; 添加texinfo格式的支持 (require 'muse-docbook) ; 添加docbook格式的支持 (require 'muse-wiki nil t) (require 'muse-project) ; 添加wiki project的支持 ;設置編碼方式為utf-8 (setq muse-html-meta-content-type (concat "text/html; charset=utf-8")) ;新建一個wiki工程 (setq muse-project-alist '(("MyWiki" ("~/Documents/wiki" :default "index") (:base "html" :path "~/Document/wiki/publish")))) |
這樣就完成了配置,在這個配置文件里創建了我們第一個 wiki 工程 MyWiki。
![]() ![]() |
Emacs Muse的基本操作
配置完成以後重新啟動 Emacs,就已經載入了 Muse。這時所有的以 muse 為擴展名的文件都是 Emacs Muse 的源文件,我們只需要用一些簡單的標記來編寫 muse 文件,然後用 Emacs 來生成各種格式的輸出,下面我們就以生成一個簡單的 Wiki 網頁為例介紹下 Emacs Muse 的基本操作。
在 Emacs 中按快捷鍵 Ctrl+x Ctrl+f ,創建文件 ~/Documents/wiki/FirstPage.muse ,輸入如清單3所示的內容:
#title 第一個Wiki頁面 * 一級標題需要一個星號開頭 ** 二級標題需要兩個星號開頭 *** 三級標題需要三個星號開頭 段落需要兩個以上空行 居中一行文字需要以6個以上的空白開頭 ---- 示一個橫線只需要輸入4個以上的“-”標記 這是 *著重* 的文字,這是 **進一步著重** 的文字,這是 ***更進一步的著重*** 的文字 這是 _下劃線_ 文字,這是 =等寬verbatim and monospace= 的文字 *** 列表: - 無序列表需要以空格和“-”開頭 1. 有序列表需要以空格和數字序號開頭 字典 :: 名詞定義需要以“::”分隔名詞和所定義文字 - 列表也可以嵌套 1. 列表嵌套深度按照開頭空格的多少來控制 2. 可以繼續嵌套不同類型的列表 - 比如這樣 *** 表格: 表格標題 || 用“||”分割 表格內容 | 用“|”分割 表格結尾 ||| 用“|||”分割 |
在 FirstPage.muse 文件輸入過程中,Emacs 的 Muse 模式會根據用戶的輸入,生成不同的顯示預覽,如圖1所示,這樣大大方便了我們寫 wiki 的效率,在 Emacs 中實現了所見即所得的用戶體驗。
輸入完成後,按快捷鍵 Ctrl+c Ctrl+p ,該快捷鍵用來在當前工程目錄下生成 wiki 網頁。生成成功后, wiki 網頁位於 ~/Documents/wiki/publish/FirstPage.html 。按快捷鍵 Ctrl+c Ctrl+v 來預覽該網頁,如圖2所示:
這樣我們就生成了一份 wiki 網頁。在 firstPage.muse 文件中,我們用各種標記符號(*, -, =)來表示網頁中的各種元素(標題,加粗,列表),這樣就非常簡單地生成了一張 wiki 網頁,它可以發布到網站中直接供人閱讀,非常適用於記錄技術文檔和筆記。
更為重要的是,Emacs Muse 不僅可以由 wiki 記號生成 HTML 格式,還可以生成 Latex, texinfo, docbook 等等多種格式,這也意味著我們完全可以用 Emacs Muse 來製作各種格式的文檔。Emacs Muse 對多種文檔格式的支持非常豐富,這在它的官方網站上可以找到完整的支持文檔列表。
![]() ![]() |
自定義單元測試報告的樣式和自動分析結果
上面內容簡單介紹了 Emacs Muse 的基本用法和基本快捷鍵。由於 Emacs 天生的易於擴展的特點,Emacs Muse 也提供了大量的函數介面來擴展它的功能。擴展 Emacs 所用的 Elisp 語言簡潔優美,非常容易學習和編程。
Muse 默認生成的 HTML 格式是非常普通的網頁樣式,我們可以通過自定義其樣式來美化其輸出。我們可以根據項目,工作和學習的具體需要,來定義不同需求的格式,使其成為一個多樣式的網頁生成工具。下面本文就介紹如何自定義一種簡單的單元測試報告的格式,該格式基於 HTML 的輸出樣式,但自定義了表單樣式,同時根據測試結果來生成統計數據。
將 Muse 擴展代碼放在 emacs-muse.el 文件中,在 .emacs 文件最後一樣加入代碼來包括這個文件,如清單4所示,把載入 muse 包的代碼以及自定義樣式的代碼放入到 emacs-muse.el 文件,這樣可以方便編寫調試,而且還能方便在命令行調用。
;; Emacs Muse (load-file "~/emacs-muse.el") |
自定義單元測試樣式
Muse 提供了函數 muse-define-style 來自定義格式,還有函數 muse-derive-style 來繼承已定義的格式。本文所需輸出的測試結果是一種自定義的 HTML 格式,所以只需要函數 muse-derive-style 來繼承自 HTML 格式即可,代碼如清單5所示,這裡我們定義了一個名為 UT 的新格式,它繼承自 HTML 格式。
(muse-derive-style "ut" "html" :header 'ut-html-header :style-sheet 'ut-style-sheet) |
在該格式中,HTML 文件頭由函數 ut-html-header 定義,該函數代碼如清單6所示:
(setq ut-html-header "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"> <html> <head> <title><lisp> (concat (muse-publishing-directive \"title\") (let ((author (muse-publishing-directive \"author\"))) (if (not (string= author (user-full-name))) (concat \" (by \" author \")\")))) </lisp></title> <meta name=\"generator\" content=\"muse.el\"> <meta http-equiv=\"<lisp>muse-html-meta-http-equiv</lisp>\" content=\"<lisp>muse-html-meta-content-type</lisp>\"> <lisp> (let ((maintainer (muse-style-element :maintainer))) (when maintainer (concat \"<link rev=\\\"made\\\" href=\\\"\" maintainer \"\\\">\"))) </lisp> <lisp>(muse-style-element :style-sheet muse-publishing-current-style) </lisp> </head> <body> <h1><lisp>(muse-publishing-directive \"title\")</lisp></h1> <p><big><em><lisp>(let ((author (muse-publishing-directive \"author\"))) (if (not (string= author (user-full-name))) (concat \"by \" author )))</lisp></em></big></p> <p><big><em><lisp>(let ((date (muse-publishing-directive \"date\"))) (concat \"Last Modified: \" date ))</lisp></em></big></p> <h2><lisp>(let ((package (muse-publishing-directive \"package\"))) (concat \"Test script Package &mdash\; \" package )) </lisp> </h2> <table class=\"muse-table\" border=\"2\" cellpadding=\"5\"> <tbody><tr><td><span class=\"pass\">PASS</span></td> <td>Passed all test</td></tr><tr><td><span class=\"fail\">FAIL</span></td> <td>Failed for new issue</td></tr><tr><td><span class=\"attempt\">ATTEMPTED</span></td> <td>Tried but there is known issue</td></tr> <tr><td><span class=\"nattempt\">NOT ATTEMPTED</span></td> <td>Not tried</td></tr></tbody></table> <!-- Page published by Emacs Muse begins here -->\n") |
清單6中,定義了 UT 樣式中所輸出的網頁文件的開頭部分代碼。作為一個簡單的單元測試結果報告樣式,在該網頁的開始部分,說明了報告的標題,測試人員名稱,單元測試的代碼包。定義了四種測試結果: PASS, FAIL, ATTEMPTED, NOT ATTEMPTED ,分別表示通過,失敗,有已知錯誤但已運行了單元測試,未測試。這段報告的開頭部分所對應的輸出網頁位置如圖4 所示。
為了表示不同樣式元素,我們需要有個自定義的樣式表嵌入到輸出網頁中,清單5 的代碼中,樣式表格式由函數 ut-style-sheet 定義,該函數定義如清單7所示。
(setq ut-style-sheet "<style type=\"text/css\"> body { FONT: 14px/1.4 'Trebuchet MS',Verdana, Arial, Helvetica, sans-serif; background:#fff; width: 60em; margin: 0 auto 0; padding: 2em 0 6em 0; text-align: left; } a { font-family: Verdana; text-decoration:none; font-weight:bold; color:#c00; } a:hover { background:#000000; color:#FFFFFF; } h1 a { color:#666;} h2 a { color:#666;} h3 a { color:#666;} h4 a { color:#666;} h1 { font-size: 40px; color:#666; border-bottom: 5px solid #000; padding: 2px; margin: 0px; margin-bottom: 8px; } h2 { color:#666; font-size: 22px; padding: 2px; margin-top: 15px; border-bottom: 2px solid #000000; } h3 { color:#666; font-size: 18px; padding: 2px; margin-top: 5px; } h4 { color:#666; font-size: 18px; padding: 2px; margin-top: 5px; } img { float: right; margin: 10px; border-style:solid; border-width:2px; } #im { clear: right;} pre { border: #777777 1px solid; padding: 0.5em; margin-left: 1em; margin-right: 2em; white-space: pre; background-color: #e6e6e6; color: black; } .pass { color:Green; } .fail { color:Red; font-weight: bold; } .attempt { color:Maroon; font-weight: bold; } .nattempt { color:Silver; } .verse { white-space: pre; margin-left: 1em; } dt { font-weight: bold;} li { margin-bottom: 0.9ex;} blockquote { margin-left: 2em; color: #4444ff; } td { font-size: 13px;} th { background: #d6d6d6; font-size: 14px; } </style>") |
在清單7所示的樣式表中,我們定義了網頁報告中的各種元素(標題,章節,表格等)的格式。而且還專門針對於單元測試結果報告,定義了四種樣式: .pass, .fail, .attempt, .nattempt 。定義了它們的顏色和字體,讀者可以根據自己的需要,修改它們的樣式。
這樣我們做好了一份測試報告的準備工作,定義了不同測試結果的 html 樣式,定義了報告開始部分的格式。下面將介紹如何生成報告的主體部分。
自定義單元測試報告內容格式
在 Muse 中我們可以使用一種格式類似於 XML 的標籤來表示特定的內容,例如如果想輸入一段包括了 Muse 的關鍵字字元的文本,而又不想被 Muse 所解釋這些關鍵字字元,則可以在 Muse 文件中輸入如清單8所示的文本:
<verbatim> 這是 *著重* 的文字,這是 **進一步著重** 的文字,這是 ***更進一步的著重*** 的文字 </verbatim> |
輸出則如圖3所示。
Muse 提供了多種標籤可以在 Muse 文件中使用,包括 lisp(動態嵌入 lisp 代碼的運行結果),python (動態嵌入 python 代碼的運算結果), src (對標籤內的代碼文本進行著色)等等。通過 Muse 函數 muse-publish-markup-tags ,我們可以自定義標籤。本測試報告自定義了 result 標籤,通過在該標籤範圍內輸入測試結果, 那麼Muse 在生成 HTML 文件時,會根據我們的擴展代碼解釋 result 標籤內的內容,生成我們所期望的格式。result 標籤的定義代碼如清單9所示。
(defvar muse-pass-tag '("pass" t nil nil muse-ut-pass-tag)) (defun muse-ut-pass-tag (beg end) (delete-region beg end) (goto-char beg) (muse-insert-markup "<span class=\"pass\">PASS</span>")) (defvar muse-fail-tag '("fail" t nil nil muse-ut-fail-tag)) (defun muse-ut-fail-tag (beg end) (delete-region beg end) (goto-char beg) (muse-insert-markup "<span class=\"fail\">FAIL</span>")) (defvar muse-attempt-tag '("attempt" t nil nil muse-ut-attempt-tag)) (defun muse-ut-attempt-tag (beg end) (delete-region beg end) (goto-char beg) (muse-insert-markup "<span class=\"attempt\">ATTEMPTED</span>")) (defvar muse-nattempt-tag '("nattempt" t nil nil muse-ut-nattempt-tag)) (defun muse-ut-nattempt-tag (beg end) (delete-region beg end) (goto-char beg) (muse-insert-markup "<span class=\"nattempt\">NOT ATTEMPTED</span>")) (defvar muse-result-tag '("result" t t nil muse-ut-result-tag)) (defun muse-ut-result-tag (beg end attrs) "Insert unit test result <result> ... </result>." (muse-publish-markup-attribute beg end attrs nil (save-excursion (save-restriction (setq passed 0) (setq failed 0) (setq attempted 0) (setq nattempted 0) (goto-char (point-min)) (while (re-search-forward "<pass/>" nil t) (setq passed (1+ passed))) (goto-char (point-min)) (while (re-search-forward "<fail/>" nil t) (setq failed (1+ failed))) (goto-char (point-min)) (while (re-search-forward "<attempt/>" nil t) (setq attempted (1+ attempted))) (goto-char (point-min)) (while (re-search-forward "<nattempt/>" nil t) (setq nattempted (1+ nattempted))) (goto-char (point-min)) (setq count (+ passed failed attempted nattempted)) (goto-char (point-min)) (muse-insert-markup (concat "<h3>Result:</h3><p><strong><em>" "Totally " (number-to-string count) " cases," (number-to-string passed) " passed, " (number-to-string failed) " failed, " (number-to-string attempted) " attempted, " (number-to-string nattempted) " not attempted." "</em></strong></p>")) )))) (add-to-list 'muse-publish-markup-tags muse-result-tag) (add-to-list 'muse-publish-markup-tags muse-pass-tag) (add-to-list 'muse-publish-markup-tags muse-fail-tag) (add-to-list 'muse-publish-markup-tags muse-attempt-tag) (add-to-list 'muse-publish-markup-tags muse-nattempt-tag) |
在清單9中,首先我們定義了四個單元測試結果標籤,這四個標籤分別代表了四種單元測試結果,並對每種結果應用對用的樣式表樣式,進行著色和設置字體。然後定義了 result 標籤,該標籤統計在該標籤內容中所包含的每種測試結果標籤的個數。最後一句代碼將 result 標籤和四種單元測試結果標籤發布到 muse 的標籤列表中。
解釋 result 標籤的代碼位於 muse-ut-result-tag 函數中,它首先定義了4個變數用於存儲每種測試結果的次數,然後不斷遍歷標籤中的文本,統計測試結果標籤的個數來獲知測試結果。最後,該函數輸出統計結果,將其列在報告中的 Result 小節的開始處,如圖4 所示。
生成第一份單元測試結果報告
上面做好了所有的準備工作,保存好 emacs-muse.el 文件。重啟 Emacs 或者運行命令 M-x eval-buffer ,我們自定義的單元測試報告格式就生效了。
現在新建一個 Muse 工程,如清單10所示:
;添加一個wiki工程 (setq muse-project-alist '(("MyWiki" ("~/Documents/wiki" :default "index") (:base "html" :path "~/Document/wiki/publish")) ("MyUTReport" ("~/Documents/wiki2" :default "index") (:base "ut" :path "~/Document/wiki2/publish")))) |
新建 muse 源文件 ~/Documents/wiki2/firstReport.muse ,輸入如清單11所示的文本。
#author sky #package org.example <result> Script Name || Result || Description Script_a || <fail/> || resaon...reason...reason...reason... Scrpit_b | <pass/> | Script_c | <nattempt/> | why...why...why...why...why...why...why... Script_d | <attempt/> | result...result...result...result </result> * Analysis 1. Below methods need refactor: <src lang="c"> 1. code ... 2. code2... </src> |
按快捷鍵 Ctrl+c Ctrl+p 發布工程,發布成功后,按快捷鍵 Ctrl+c Ctrl+v 來預覽結果,結果圖4 所示。
![]() ![]() |
同其他工具的結合
通過 Emacs Muse ,我們可以通過簡單的標記來生成精美的測試結果報告。生成報告所需要的muse文件既可以是手動輸入,也可以是由自動化測試工具來自動生成。通過 Emacs 的 batch 模式,我們可以從命令行運行 Emacs ,讓它以命令行的形式執行 emacs-muse.el 文件中的 Elisp 代碼,生成 HTML 格式的網頁報告。這一切只需要執行如下的命令:
skys-imac:~ sky$ emacs –q -batch -l ~/emacs-muse.el –f muse-project-batch-publish MyWiki |
最後的 MyWiki 是 muse 工程的名字。這樣,通過自動化的測試工具來生成 muse 文件,再從命令行運行生成 HTML 報告。這樣就將 Emacs Muse 同其他工具完美結合在一起。而且可以根據自己需求來自定義樣式,將藝術和技術完美地結合到了一起,達到了很好的效果。
![]() ![]() |
結束語
Emacs Muse 擴展性強,並在發布 Wiki 方面有著獨到的強大功能。它支持格式多,支持源代碼標記,可嵌套列表等等。有興趣的讀者可以直接訪問附錄中 Muse 的官方網站 http://mwolson.org/ ,這個網站完全使用 Emacs Muse 生成併發布到網路中的,並且所有的 Muse 文件都是開放代碼,可以直接下載學習的。正是由於它的強大擴展性,也使得我們可以自定義樣式來生成符合我們要求的文檔,這也正是 Emacs 編輯器的迷人之處。
(責任編輯:A6)
[火星人 ] 用 Emacs Muse 來製作測試結果報告已經有717次圍觀