揮一揮手,向CSDN狗屎代碼框說再見
揮一揮手,向CSDN狗屎代碼框說再見1
引子
憑心而論,CSDN是我見過的技術類部落格系統裡做的最渣的,渣在:
- 居然不支援Mozilla Firefox的粘帖、複製。
太有意思了,這是程式員的技術部落格網站嗎?居然不支援開源自訂的瀏覽器?和著這網站主辦者就覺得上這網站的都是那些愣頭愣腦,說什麼就聽什麼,
不願自己動手DIY外掛程式的呆木雞嗎?
- 代碼框就像茅坑的石頭,是又臭又硬。
支援的語言少吧,就算了,還偏偏花裡胡哨的弄成個條子色,
你要不用它預先提供的那套玩意兒吧,它就把你自己格式化好的代碼整的七零八落的,用它的吧,空行阿,
和文本的間距阿,根本就不讓你調整。網上完善的網頁代碼高亮系統多的很,比如 GeSHi
還是自由軟體,
可惜阿,這裡還是老樣子幾年雷打不動的。
- 根本不支援數學公式的格式化。
OK,再重新google下:CSDN 這個關鍵詞,這是頭條: CSDN.NET - 全球最大中文IT社區,為IT專業技術人員提供最全面的資訊 !!。。。。。。。
這是坑爹的吧!!!!!
之前,撰寫拙作《撰寫Haskell模組的自訂安裝配置》
的時候,Emacs Org-Mode
裡寫文章加用ditaa
畫1
本身用掉的時間,加在一起,不及最後發到CSDN上調教格式花去的時間。So, 如果還想寫點東西話,
必須有一個一勞永逸能解決格式化問題的方法,那能完美相容CSDN目前的狗屎指令碼的唯一方案,就只能片了。。。
那現在的問題也就轉換為,能用現有的工具,自動化的匯出需要格式的代碼,並自動再轉換成圖片,並且需要保證的是,
在匯出的各部分代碼之間, 縮排和格式必須一致。作為一個DIY愛好者,這個匯出方案還必須足夠Flexible。
1.1
現有的工具
毫無疑問,Emacs中Org-Mode
本身的匯出功能非常強悍,配合Babel以後更是如虎添翼2
,類似Office裡
儲存格的計算等等功能在Babel+Emacs面前,只是小兒科中的小兒科,Babel可以即時運行你插入在純文字中的代碼,
並將最後結果插入到你的文檔中,可以是圖片,也可以是資料表格,而且支援的語言種類非常多(幾乎所有主流的指令碼和數值計算軟體)。
咳,這些本來都是很美好,很美好的願望,在Org-Mode
只要直接匯出程式碼片段,
就可以產生很漂亮的代碼高亮的HTML文檔,BUT。。。。。
沒用的,凡人,在CSDN的渣系統前, 任何努力都是徒勞的。
在代碼框的歎息之牆前,流下你悔恨而絕望的淚水吧。。。。
——————By CSDN網站架構師
1.2
理想中的方案
恩,寫文章依舊還是用純文字,用Emacs的Org-mode,我們需要額外做的工作在於:
- 剖析.org文本,找到下一個標註的程式碼片段,取出放入Emacs的一個temp buffer中。
- 使用程式設計語言特異的文法剖析器,將代碼中的運算式分類並進行
格式註解。
- 調用TeX工具集,產生.dvi檔案後以dvipng工具將.dvi檔案產生png圖片。
- 登出掉原org檔案中的該段代碼,插入圖片連結,並重頭執行step 1,直到沒有標記輸出的程式碼片段。
- 匯出整個org檔案產生相應的HTML代碼。
2
幹活
要從頭做這個工作,內容不簡單,好在有現成的工具完成大部分內容,要處理的只是將各個工具串連起來的膠水代碼。
2.1
需要的額外工具
-
文法剖析
-
毫無疑問,最艱難的部分是程式設計語言自身的文法剖析器,不過這部分不用自己動手寫(真要寫就不是個小工程了),Haskell
的實現本身就是編程學術界關注的熱點,所以Haskell
專門有官方的詞法剖析模組,
haskell-src
&
haskell-src-ext
, 以及基於此上構建的專用於Haskell學術文章發表的
lhs2TeX
, 為了更方便的給關鍵詞上色,
lhs2TeX-hl
也是個好東東3
。
-
格式化輸出
-
想都不用想,那當然是————Word了!!!。。。。。。開玩笑, 根據《完全裝B指南》
的精神,作為一個懂點編程的,你怎麼能讓wxxd那種詞彙從你的嘴裡冒出來呢?不,是想都不能想,要排版格式化,當然是LaTeX
才是王道呀。
-
模板
-
沒錯,這個當然是必須的!!寫代碼就應該像M$ V$ $tudio一樣, 開啟編輯器,啪啪啪,ide自動填好一大堆代碼, 對於我這個只翻過一天《Emacs Lisp Reference Manual》
的門外漢來說,這是必須的!!好在這個模板很好找,只需要參考ob-haskell.el中的haskell lhs匯出函數(org-babel-haskell-export-to-lhs)做的大致工作,在此基礎上做加工即可。
2.2
Lisp Code
;;; haskell-export.el ---<br />;; Copyright (C) 2011 Free Software Foundation, Inc.<br />;;<br />;; Author: Boyun Tang <tangboyun@gmail.com><br />;; Maintainer: Boyun Tang <tangboyun@gmail.com><br />;; Created: 04 Mar 2011<br />;; Version: 0.01<br />;; Keywords<br />;; This program is free software; you can redistribute it and/or modify<br />;; it under the terms of the GNU General Public License as published by<br />;; the Free Software Foundation; either version 2, or (at your option)<br />;; any later version.<br />;;<br />;; This program is distributed in the hope that it will be useful,<br />;; but WITHOUT ANY WARRANTY; without even the implied warranty of<br />;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br />;; GNU General Public License for more details.<br />;;<br />;; You should have received a copy of the GNU General Public License<br />;; along with this program; if not, write to the Free Software<br />;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.<br />;;; Commentary:<br />;;<br />;; Put this file into your load-path and the following into your ~/.emacs:<br />;; (require 'haskell-export)<br />;;; Code:<br />(eval-when-compile<br /> (require 'cl)<br />)<br />(require 'ob-haskell)<br />(defun single-lhs-export (&optional arg)<br /> "Export to a .lhs file with all haskell code blocks escaped.<br /> .lhs file will be exported to a .tex file. This function will<br /> create two new files, base-name.lhs and base-name.tex and finally<br /> export result to base-name.png. Note that input should in standard<br /> Haskell literate programming format."<br /> (interactive "P")<br /> (let* (<br /> ;; 各種要用到的檔案名稱<br /> (base-name (file-name-sans-extension (buffer-file-name)))<br /> (tmp-file (make-temp-file "mytemp"))<br /> (tmp-org-file (concat tmp-file ".org"))<br /> (tmp-tex-file (concat tmp-file ".tex"))<br /> (lhs-file (concat base-name ".lhs"))<br /> (tex-file (concat base-name ".tex"))<br /> (fmt-file (concat base-name ".fmt"))<br /> (dvi-file (concat base-name ".dvi"))<br /> (png-file (concat base-name ".png"))<br /> ;; 各種要在shell中執行的工具軟體,及其命令參數<br /> (png-control (concat "-bg " "Transparent"<br /> "-D " "96"<br /> ))<br /> (lhs2TeX "~/.cabal/bin//lhs2TeX")<br /> (texi2dvi (concat "latex"<br /> " "<br /> "-o " dvi-file<br /> " "<br /> (file-truename tex-file)))<br /> (dvipng (concat "dvipng"<br /> " "<br /> png-control<br /> " "<br /> dvi-file))<br /> (lhs2tex (concat lhs2TeX " " lhs-file " > " tex-file))<br /> (lhs2tex-hl (concat "~/.cabal/bin/lhs2TeX-hl" " -o"<br /> " " fmt-file " " lhs-file ))<br /> (preserve-indentp org-src-preserve-indentation)<br /> (indentation 0))</p><p> (org-babel-mark-block) ; 用babel提供的介面,得到當前src block的所有代碼<br /> (setq contents (buffer-substring-no-properties (region-beginning) (region-end)))<br /> (with-temp-file tmp-org-file ; 建立一個新buffer,函數所有結果寫入tmp-org-file<br /> (insert ;; 下面是個必須做的小hacking,<br /> ;; 如果不增加個額外的title, 最後format出來的latex結果會有嵌套錯誤<br /> ;; 解決的辦法,目前只是先插入個額外section,之後再用Regex剔除該部分。<br /> (concat "/n* tagforHaskell/n/n#+begin_latex/n/n"<br /> contents<br /> "/n/n#+end_latex/n"<br /> )<br /> ))<br /> (save-excursion<br /> ;; 這個部分匯出中間的lhs檔案。<br /> (find-file tmp-org-file) (funcall 'org-export-as-latex nil)<br /> (kill-buffer)<br /> (delete-file tmp-org-file)<br /> (find-file tmp-tex-file)<br /> (goto-char (point-min)) (forward-line 2)<br /> ;; 下面是定義一個格式化的tex檔案的檔案頭,這部分應該拿出去另外做個變數的。<br /> ;; 放一起為了緊湊。<br /> (insert (concat "//usepackage{xcolor}/n"<br /> "//usepackage[top=0.2in, bottom=0.2in, left=0.1in, right=0.1in]{geometry}/n"<br /> "%include lhs2TeX.fmt/n"<br /> "%include " fmt-file "/n"<br /> "//definecolor{syntax}{RGB}{0, 0, 255}/n"<br /> "//definecolor{datatype}{RGB}{0,204,0}/n"<br /> "//definecolor{class}{RGB}{239,181,24}/n"<br /> "//definecolor{fieldname}{RGB}{0,0,162}/n"<br /> "//definecolor{prelude}{RGB}{64,80,117}/n"<br /> "//definecolor{numeral}{RGB}{0,0,153}/n"<br /> "//definecolor{function}{RGB}{0,0,0}/n"<br /> "//definecolor{infixoperator}{RGB}{215,106,175}/n"<br /> "//definecolor{constructor}{RGB}{229,120,0}/n"<br /> "//definecolor{keyword}{RGB}{255,0,0}/n"<br /> "//definecolor{special1}{RGB}{159,138,0}/n"<br /> "//definecolor{string}{RGB}{102, 51, 00}/n"<br /> "//definecolor{char}{RGB}{204, 153, 0}/n"<br /> "//definecolor{comment}{RGB}{164,164,164}/n"<br /> "%subst conid a = /"//Conid{//color{constructor}{/"" " a " "/"}}/"/n"<br /> "%subst char a = /"//color{char}//text{//tt ''/"" " a " "/"''}/"/n"<br /> "%subst string a = /"//color{string}//text{//tt //char34 /"" " a " "/"//char34}/"/n"<br /> "%subst numeral a = /"//color{numeral}{ /"" "a" "/" }/"/n"<br /> "//newcommand{//lhsCHclass}[1]{//color{class}{{#1}}}/n"<br /> "//newcommand{//lhsCHsyntax}[1]{//color{syntax}{{#1}}}/n"<br /> "//newcommand{//lhsCHfunction}[1]{//color{function}{{#1}}}/n"<br /> "//newcommand{//lhsCHinfixoperator}[1]{//color{infixoperator}{{#1}}}/n"<br /> "//newcommand{//lhsCHprelude}[1]{//color{prelude}{//mathbf{#1}}}/n"<br /> "//newcommand{//lhsCHkeyword}[1]{//color{keyword}{//textbf{#1}}}/n"<br /> "//newcommand{//lhsCHconstructor}[1]{//color{constructor}{//textbf{#1}}}/n"<br /> "//newcommand{//lhsCHlitNumber}[1]{//color{numeral}{{#1}}}/n"<br /> "//newcommand{//lhsCHtype}[1]{//color{datatype}{{#1}}}/n"<br /> "//newcommand{//lhsComment}[1]{//textit{//color{comment}{{#1}}}}/n"<br /> "//pagestyle{empty}/n"))<br /> ;; 下面這個函數是手動hack的,糾正產生的lhs檔案的一些嵌套問題。<br /> (delete-extra-info)<br /> (setq contents (buffer-string))<br /> (save-buffer) (kill-buffer))<br /> (delete-file tmp-tex-file)<br /> ;; save org exported latex to a .lhs file<br /> (with-temp-file lhs-file (insert contents))<br /> (message "running %s" lhs2tex-hl)<br /> (shell-command lhs2tex-hl)<br /> (message "running %s" lhs2tex)<br /> (fix-hl-bug fmt-file)<br /> (shell-command lhs2tex)<br /> (shell-command texi2dvi)<br /> (shell-command dvipng)<br /> ))<br />(defun fix-hl-bug (fmt)<br /> (find-file fmt)<br /> (goto-char (point-min))<br /> (replace-regexp "^%format //[//].*/n" "")<br /> (save-buffer))</p><p>(defun delete-extra-info ()<br /> ;; 刪掉不需要的title部分,我們不是要產生正規的文檔<br /> ;; 只需要程式碼片段<br /> (setq tag-reg "^////title//(.*/n//)*////label.*/n")<br /> (goto-char (point-min))<br /> (replace-regexp tag-reg "")<br /> (goto-char (point-min))<br /> ;; 解決babel自動匯出lhs檔案時,額外產生一個不需要的/begin{code}與/end{code}嵌套<br /> (replace-regexp "^////begin{document}/n+////begin{code}/n+//((.*/n)+//)////end{code}/n+////end{document}"<br /> "^////begin{document}/n/1////end{document}"))<br />(provide 'haskell-export)<br />;;; haskell-export.el ends here<br />
3
最後效果
#+begin_src haskell<br />You can insert any valid /LaTeX{} code HERE//<br />/lhsComment{<br />O(n). 'length' returns the length of a finite list as an 'Int'.//<br />It is an instance of the more general 'Data.List.genericLength', //<br />the result type of which may be any kind of number.}<br />/begin{code}<br />length :: [a] -> Int<br />length l = len l 0<br /> where<br /> len :: [a] -> Int -> Int<br /> len [] a = a<br /> len (_:xs) a = len xs (a + 1)<br />/end{code}<br />You can insert any valid /LaTeX{} code HERE//<br />/lhsComment{<br />applied to a predicate and a list, returns the list of those elements //<br />that satisfy the predicate; i.e., //<br />filter p xs = [ x $/vert$ x <- xs, p x]}<br />/begin{code}<br />filter :: (a -> Bool) -> [a] -> [a]<br />filter _pred [] = []<br />filter pred (x:xs)<br /> | pred x = x : filter pred xs<br /> | otherwise = filter pred xs<br />/end{code}<br />You can insert any valid /LaTeX{} code HERE//<br />/begin{code}<br />class Monad m => MonadPlus m where<br /> mzero :: m a<br /> mplus :: m a -> m a -> m a<br />instance MonadPlus [] where<br /> mzero = []<br /> mplus = (++)<br />instance MonadPlus Maybe where<br /> mzero = Nothing<br /> Nothing `mplus` ys = ys<br /> xs `mplus` _ys = xs<br />foldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a<br />foldM _ a [] = return a<br />foldM f a (x:xs) = f a x >>= /fax -> foldM f fax xs<br />/end{code}<br />#+end_src<br />
以上代碼一鍵自動產生:後記:以上的haskell-export.el中,目前只定義了處理抓取單個代碼框的函數,而且尚未寫入臨時檔案的清理函數,還不是一個成熟的模組,因為latex以及texi2dvi工具本身在shell中調用貌似還存在路徑尋找bug,這個問題尚未解決,所以那些檔案是留著debug的。lhs2TeX-hl和lhs2TeX本身在格式化[]時亦有衝突。成熟的模組以及函數最佳化工作要等這些問題都解決以後再說了。理想中最後應該能在org-mode中做成一個特殊的自訂標籤:#+begin_MYWORLD... ...#+end_MYWORLD其中可以任意混插latex和code block,最後能自動匯出到透明圖片。
以上工具的優點在於可以隨便的混合Latex和Haskell原始代碼,包括插入各種數學公式的排版,當然原因主要在於Haskell對於Literate Programming本身就很支援。
Footnotes:
1
感謝Emacs中文網的Org-mode系列文章
, 讓我獲益不少。
2
Babel
的網站在這裡,有心的DIYer,請一定不要錯過這裡的精彩。
3
這裡有件趣事,你會發現該作者的Git倉庫最後的留言是:Added missing file. Again…, 恩,這模組是個有趣的傢伙寫的,git提交後居然遺漏了最重要的模組,這個小辮子被我捉住了,活活活☻。
Author: Boyun Tang
Date: 2011-03-06 22:14:31 CST
HTML generated by org-mode 7.4 in emacs 23