#207 Syntax Highlighting
- Download:
- source codeProject Files in Zip (94.8 KB)
- mp4Full Size H.264 Video (19.9 MB)
- m4vSmaller H.264 Video (11.6 MB)
- webmFull Size VP8 Video (27.9 MB)
- ogvFull Size Theora Video (25 MB)
有时候需要展示Rails应用程序中的代码片段,为使代码可读性更高,你可以使用语法高亮。基本上每一期我都是这么做的,但是语法高亮是怎么实现的呢?
有几种不同的方法可以向Rails应用程序中加上语法高亮功能。首先你要确定,你是想用Rails在服务器端实现语法高亮,还是想用JavaScript库在浏览器中转换页面实现语法高亮。有许多Javascript库可以用于客户端语法高亮显示,但我们这儿不讨论它们,因为它们不是Rails特有的。我们将要为你展示的是Rails世界中流行的3个语法高亮实用工具。
CodeRay
第一个是CodeRay。它可以用Gem安装,非常简单,也没有额外的依赖组件,并且CodeRay的速度非常快。然而它提供的功能特性有限,支持的语言种类也不多。但如果它提供的正是你的应用程序所需要的,那它会是非常好的解决方案,我们将在这期的后面详细的研究它。
Ultraviolet
第二个选择是Ultraviolet,这也是个Gem组件,不过它还有一些依赖的组件,所以安装的时候有些麻烦。Ultraviolet要比CodeRay慢,不过Ultraviolet拥有更多的功能特性。Ultraviolet可以使用TextMate(Mac下的著名的文本编辑器软件)的语法文件和主题。因此Ultraviolet比CodeRay支持更多种语言,对于如何高亮显示代码片段也更加灵活。
如果你选择Ultraviolet用于语法高亮,就有必要看一看Harsh。Harsh是一个Rails插件,它提供了一个非常好的helper方法,可以用来在视图文件中高亮显示代码,它还提供了一些rake任务来安装主题和管理主题。Ultraviolet的README文件对如何安装Ultraviolet提供的很有用的指导。
Pygments
第三个常用的选择是Pygments。 Pygments和CodeRay、Ultraviolet都不同,因为它是一个Python库。但由于Pygments提供命令行实用工具,所以我们的Rails应用程序可以调用它。 Pygments是一个功能非常全面的库,拥有良好的支持,并被广泛用于很多网站。但它的缺点是,当你试图在Rails应用程序中分析很大的文件时,速度会变得非常慢。所以如果你考虑在程序中使用Pygments时,有必要使用缓存。
有一个非常好的插件highlight可以使Pygments在Rails中应用起来更加简单。这个highlight插件封装了Pygments,你可以在视图中用一个很简单的helper方法来调用它。考虑Pygments速度方面的原因,我们建议使用highlight时,它产生的任何输出都要缓存。为了实现缓存,只要将所有的输出包含在一个cache
块中,像这样:
<% cache do %> <%= highlight(:ruby, 'class Test; end') -%> <% end %>
标记
我们已经大概了解了上面谈到的3种语法高亮工具,现在来比较一下它们在速度上的表现。一个应用程序可能需要渲染许多高亮的代码,所以我们需要考虑是否缓存输出的结果。为了比较这3种工具,我们将使用下面的代码:
require 'rubygems' require 'benchmark' require 'coderay' require 'uv' path = __FILE__ content = File.read(__FILE__) # run it once to initialize CodeRay.scan("print 'hello'", 'ruby').div(:css => :class) Uv.parse("print 'test'", 'xhtml', 'ruby', true, 'amy') Benchmark.bm(11) do |b| b.report 'coderay' do 50.times { CodeRay.scan(content, 'ruby').div(:css => :class) } end b.report('ultraviolet') do 50.times { Uv.parse(content, 'xhtml', 'ruby', true, 'amy') } end b.report('pygments') do 50.times { `pygmentize -f html "#{path}"` } end end
这个基准测试代码分别用3种语法高亮工具库把自身内容解析50遍,Pygments使用的是pygmentize
命令。运行结果如下:
$ ruby benchmark.rb user system total real coderay 0.310000 0.010000 0.320000 ( 0.312954) ultraviolet 4.860000 0.020000 4.880000 ( 4.886621) pygments 0.010000 0.120000 12.430000 ( 12.643173)
从上面看出每个库的表现差别很大。CodeRay显然是最快的,迭代50次只用了0.3秒左右。Ultraviolet用了CodeRay10倍多的时间,而Pygments还要更慢,完成上面测试用了超过12秒的时间。这三个语法高亮工具库速度差别如此之大,在选择到底用哪一个时,功能特性和性能表现两方面取得平衡很重要。当然,如果你使用缓存,那速度就不再是个问题。
使用CodeRay
本期接下来我们将向你展示如何在Rails应用程序中使用CodeRay。CodeRay是三个语法高亮工具库中安装起来最简单而且速度最快的,因此没有必要担心缓存问题。
为了展示CodeRay,我们将使用一个简单的博客应用程序,你在前期节目中可能见过这个程序了。我们将用CodeRay为这个博客程序加入下面功能:语法高亮显示文章中的代码片段。
当作者编辑文章时,我们希望能在代码标签中加入代码片段,像这样:
<code lang="ruby"> puts "Hello, world!" </code>
当像上面的代码片段出现在文章正文中时,需要通过适当的语法高亮显示出来。
为了实现这点,首先要在/config/environment.rb
文件中加入CodeRay gem组件的引用信息,这是针对Rails 2的情况。如果是Rails 3 则需要修改Gemfile。
Rails::Initializer.run do |config| config.gem "coderay" config.gem "RedCloth" end
我们后面将用到Textile,所以这儿要安装RedCloth gem 组件。有个gem组件捆绑了CodeRay和RedCloth,但我们这里打算分开来安装它们。
接下来我们更新ArticleController
的视图文件show
,用CodeRay解析代码标签中的内容并高亮显示它们。为此,我们需要将显示文章内容的代码传递给一个新的helper方法,这个方法叫coderay
,如下:
<% title @article.title %> <p class="author">from <%= @article.author %></p> <%= coderay(@article.content) %> <p>
The helper method will go into our application_helper
file:
# Methods added to this helper will be available to all templates in the application. module ApplicationHelper def coderay(text) text.gsub(/\<code( lang="(.+?)")?\>(.+?)\<\/code\>/m) do CodeRay.scan($3, $2).div(:css => :class) end end end
这个coderay方法非常简单,它接受一段文本text作为参数,这个text中可能包含需要高亮显示的代码片段,用text调用gsub
方法,查找匹配代码标签的内容,并将查找到的内容用代码块的返回值替换。其中的正则表达式就是用来查找以'<code
'开头并拥有'lang
'属性的代码标签,然后匹配位于代码标签开始(<code lang=
)和结束(</code>
)之间部分。代码块的内部调用了CodeRay.scan
方法。这个方法接受一段文本和语言作为参数,所以我们传递了$3
和$2
给它,$3
是正则表达式匹配出来的代码标签之间的内容(/\<code( lang="(.+?)")?\>(.+?)\<\/code\>/m)
,$2
是正则表达式中匹配出来的属性lang
的值(/\<code( lang="(.+?)")?\>(.+?)\<\/code\>/m)
。接着代码块调用了.div
,将前面的返回值包含在一个div标签中。:css => :class
选项告诉CodeRay采用什么类型的CSS样式。CodeRay有许多不同的选项用来控制输出内容的结构和样式。更多详细信息请查看这个文档。
最后,我们需要创建一张样式表,设置CodeRay每个类的样式。Ryan Bates创建一个类似Railscasts主题的样式表文件,你可以从Github上下载。我们则拷贝一份这个文件,把它放到项目目录/public/stylesheets
中。
.CodeRay { background-color: #232323; border: 1px solid black; font-family: 'Courier New', 'Terminal', monospace; color: #E6E0DB; padding: 3px 5px; overflow: auto; font-size: 12px; margin: 12px 0; } .CodeRay pre { margin: 0px; padding: 0px; } .CodeRay .an { color:#E7BE69 } /* html 属性*/ .CodeRay .c { color:#BC9358; font-style: italic; } /* 注释 */ .CodeRay .ch { color:#509E4F } /* 转义字符 */ .CodeRay .cl { color:#FFF } /* 类 */ .CodeRay .co { color:#FFF } /* 常数 */ .CodeRay .fl { color:#A4C260 } /* 浮点数 */ .CodeRay .fu { color:#FFC56D } /* 方法 */ .CodeRay .gv { color:#D0CFFE } /* 全局变量 */ .CodeRay .i { color:#A4C260 } /* 整型 */ .CodeRay .il { background:#151515 } /* 内联代码 */ .CodeRay .iv { color:#D0CFFE } /* 实例变量 */ .CodeRay .pp { color:#E7BE69 } /* 文档类型 */ .CodeRay .r { color:#CB7832 } /* 关键字 */ .CodeRay .rx { color:#A4C260 } /* 正则表达式 */ .CodeRay .s { color:#A4C260 } /* 字符串 */ .CodeRay .sy { color:#6C9CBD } /* 符号 */ .CodeRay .ta { color:#E7BE69 } /* html 标签 */ .CodeRay .pc { color:#6C9CBD } /* 布尔型 */
高亮显示的源代码中每一种标记符号都有相应的CSS类选择器,上面代码中的颜色可以根据你应用程序的需要随意改变。
为了使样式表文件起作用,需要在应用程序layout布局文件的头部将样式表文件包含进来。
<%= stylesheet_link_tag 'blog', 'coderay' %>
做完这些,我们编辑一篇文章后,查看文章时就会看到高亮显示的部分。
使用Textile
语法高亮通常和简单标记语言联合使用,如Textile和Markdown。要在文章中使用Textile,我们可以在视图文件show
中调用textilize
方法,并将coderay
方法的返回值作为参数传递给textilize
。
<%= textilize(coderay(@article.content)) %>
如果不希望coderay
方法的返回值被RedCloth解析,只需要稍微改动coderay方法,将其返回值放入一个notextile
元素中,这样就不会被解析了。
def coderay(text) text.gsub(/\<code( lang="(.+?)")?\>(.+?)\<\/code\>/m) do content_tag("notextile", CodeRay.scan($3, $2).div(:css => :class)) end end
如果我们重新编辑文章,加入一些标记代码,像这样:
The piano is a musical instrument played by means of a keyboard that produces sound by striking steel strings with felt hammers. The hammers immediately rebound allowing the strings to continue vibrating at their resonant frequency. These vibrations are transmitted through a bridge to a soundboard that amplifies them. * item * item 2 <code lang="ruby"> def hello puts 'Hello, world!' end </code>
现在文章中就会有一个Textile生成的无序列表和语法高亮显示的代码片段。
上面就是本期“语法高亮”的内容。CodeRay使Rails应用程序中高亮显示代码片段变得容易,如果你需要语法高亮的功能,值得好好研究一下。