2008年1月10日星期四

Code Highlight

Google的Blog确实给用户很大的灵活性,什么都可以自定义,模板的源代码都可以自己修改。

写技术Blog,贴代码的机会很多,代码的语法高亮就很重要。一开始想雄心勃勃地自己写一个,后来一是没时间,二是写得不一定会比已有的东西好。

Google了一下,语法高亮可以有好几种解决方法。一种是js来控制,这个需要在页面上引人js文件,比较适合自己的空间架的Blog。一种是用CSS来控制字符的色彩,HTML则用工具来生成,我一开始就是想自己写一个这样的工具。

后来终于找到了一个linux的开源项目source highlight,详见:
http://www.gnu.org/software/src-highlite/http://www.gnu.org/software/src-highlite/source-highlight.html

用apt-get的话无法安装最新版的,最好是去下源代码来自己编译:
ftp://ftp.gnu.org/gnu/src-highlite/去下载最新的源代码tar.gz包。解压并到解压后的目录,依次执行:

./configure

make

sudo make install

如果不能编译的话,就要先安装编译环境:

sudo apt-get install build-essential

和boost regex库

sudo apt-get install libboost-regex*


用倒是挺简单,命令行就行,具体参考它的手册。但是默认生成的色彩并不理想,不是TextMate的那种漂亮的颜色,而且高亮的也不是完全正确。比如symbol,应该是:sym这种,而它默认的却是+-=这些操作符。

幸亏source highlight提供很方便的扩展,不用改它本身的代码或配置,可以直接通过外部文件来设置格式。它本身的配置文件是xml格式的,在我Ubuntu 7.10 上的路径是:/usr/share/gtksourceview-2.0
在文件夹language-specs下是语言的定义,xx.lang,比如ruby.lang。
在文件夹styles下是高亮色彩的定义。

source highlight给我们提供了更简单的语法来定义外部的配置文件,不过参考它本身的xml格式的配置文件可以帮助我们配置一些比较复杂的规则。

历经辛苦,勉强定义了一个支持ruby大部分语法规则的.lang配置文件,里面几乎全是用正则表达式。source highlight用的是Boost regex library,而这个库又用的是Perl的正则表达式语法,很不习惯。具体配置如下:

keyword = "__FILE__|and|def|end|in|or|self|unless",
"__LINE__|begin|defined?|ensure|module|redo|super|until",
"BEGIN|break|do|false|next|rescue|then|when",
"END|case|else|for|nil|retry|true|while",
"alias|class|elsif|if|not|return|undef|yield"
keyword = "defined?"

comment start "#"

symbol = ':[a-zA-Z0-9_]+'

variable = '\$[a-zA-Z_][a-zA-Z0-9_]*',
'@@[a-zA-Z_][a-zA-Z0-9_]*',
'@[a-zA-Z_][a-zA-Z0-9_]*'

number = '(?<![\w\.])([1-9](_?[0-9])*|0)(?![\w\.])',
number = '(?<![\w\.])([1-9](_?[0-9])*|0)(?![\w\.])',
'(?<![\w\.])(( (\d(_?\d)*)?\.\d(_?\d)* | \d(_?\d)*\. ) | ( (\d(_?\d)*|(\d(_?\d)*)?\.\d(_?\d)*|\d(_?\d)*\.)[eE][+-]?\d(_?\d)* ))(?![\w\.])',
'(?<![\w\.])0[0-7](_?[0-7])*(?![\w\.])',
'(?<![\w\.])0[dD][0-9](_?[0-9])*(?![\w\.])',
'(?<![\w\.])0[xX][0-9A-Fa-f](_?[0-9A-Fa-f])*(?![\w\.])',
'(?<![\w\.])0[bB][01](_?[01])*(?![\w\.])'

regexp delim "/" "/" escape "\\" multiline
regexp delim "%r{" "}" escape "\\" multiline

string delim "'" "'" escape "\\" multiline
string delim "\"" "\"" escape "\\" multiline
string delim "%{" "}" escape "\\" multiline
string delim "%w(" ")" escape "\\" multiline


我把它保存为rb.lang,放在ruby源代码文件同一目录下。NND,写到后面才发现有个Tutorials on Language Definitions,就是那个手册后面:http://www.gnu.org/software/src-highlite/source-highlight.html#Tutorials-on-Language-Definitions,拜托啊,写手册请把tutorials写在最前面,新手从上往下看,看到后面都成高手了,谁还需要那个速成的呀。

然后就是色彩方案的定义,这个就更简单了,可以直接定义CSS文件,下面是我定义的一个:

body { background-color: black; color: white; }

.keyword { color: #DE9309; font-weight: bold; }
.string { color: #00FF00; font-family: monospace; }
.regexp { color: #0000FF; }
.comment { color: #AB50D6; font-style: italic; }
.number { color: #0080FF; }
.symbol { color: #3F92B7; }
.variable { color: #FF0000; }

我把它保存为ruby_highlight.css,放在rb.lang同一目录下。

现在就可以运行了,在命令行下敲入:

source-highlight -s ruby -f html -i demo.rb -o demo.html --css ruby_highlight.css --lang-def rb.lang

(天啊,source-highlight,16个字符的命令,真是少见地长。)
demo.rb是我要高亮的ruby源代码文件,demo.html是我要输出的html文件名,二者完全可以不同名。
生成后的HTML文件,用浏览器打开,哈哈,不是我显摆,真的非常漂亮,就和TextMate里的一样。

这种方案需要把CSS的内容导入到页面里:
/* for code highlight */
.code_bgcolor {
background-color:#E5E5E5;
}

.keyword { color: #DE9309; font-weight: bold; }
.string { color: #00DF00; font-family: monospace; }
.regexp { color: #0000FF; }
.comment { color: #AB50D6; font-style: italic; }
.number { color: #0080FF; }
.symbol { color: #3F92B7; }
.variable { color: #FF0000; }

/* for command line */
.post .cmd {
background-color:#000000;
padding: 10px;
margin: 7px 0px 7px 0px;
}

.cmd_failed { color:#ff0000; }
.cmd_normal { color:#00ff40; }

/* for quote */
.post .quote {
border: 1px solid #BFBFBF;
margin: 7px 0px;
padding:5px 20px;
}

然后,命令行可以这样写:

<p class="cmd"><span class="cmd_normal">Oh_Yes</span></p>
<p class="cmd"><span class="cmd_failed">Oh_No</span></p>

效果:

Oh_Yes

Oh_No


代码高亮可以这样写:

<pre class="quote code_bgcolor"><tt>
<span class="keyword">def<span>
</tt></pre>

效果:
def

普通引用可以这样写:

<p class="quote">some_text</p>

效果:

some_text


source highlight生成的有讨厌的<span class="normal">,我写了个脚本,可以把它去掉,顺便还加上了pre的class:
`source-highlight -s ruby -f html -i r.rb -o r.html --css ruby_highlight.css --lang-def rb.lang`

sleep 1

code_start = '<pre class="quote code_bgcolor">'

File.open("r.html").each do |line|
line.gsub!(/<pre>/) {|s| s = code_start if s}
line.gsub!(/(<span class="normal">)(.*?)(<\/span>)/) {$2}
puts line
end

然后直接在命令行里输入下面的命令:

ruby blog_scripts.rb > r2.html

哈哈,去r2.html把相应的HTML拷过来就可以了

还有另一种方案,生成的HTML里直接嵌入CSS。先定义一个rb.style(这个语法限制比CSS多,所以有些效果比如背景黑色做不出来,我通过apt-get装的是source highlight 2.4,手册上说的是2.6以后才支持背景色,我晕),放在rb.lang同一目录下。
命令行:

source-highlight -s ruby -f html -i demo.rb -o demo.html --style-file rb.style --lang-def rb.lang

这次生成的HTML就在标签的style属性里直接写CSS了。在网页设计上这是一种很糟糕的方式,很不DRY(Don't Repeat Yourself),而且用了很多已经不推荐使用的font标签,整个HTML的也变大了,膨胀了。

最后居然不知道这篇文章该用什么tag,勉强和编辑本Blog搭边,毕竟代码高亮也是为了贴的代码好看。

没有评论: