Hello World
Spiga

浅谈代码着色(上):客户端着色

2009-12-14 19:53 by 老赵, 7187 visits

作为程序员,写文章时总免不了插入代码,而对代码进行着色几乎已经成为一个基础,一个事实标准。代码着色的确可以大大加强代码的可读性,因此即便是再不待见IDE的朋友,代码着色永远是必不可少的。不过在网页中进行代码着色的方式有很多,现在我们就来对比一下。记得之前也有朋友写过,但我总觉得不够完整,于是还是自己写一下吧。

这样过瘾。

现在先来谈“客户端着色”。这种着色方式的基本做法,在于在HTML中显示(几乎)完全普通的文本,而在页面加载过程中在浏览器里进行着色(通常是使用JavaScript)。由于HTML是服务器端输出的,但它不负责着色,因此我把这个做法叫作是“客户端着色”。例如,著名的问答网站Stack Overflow便使用的是客户端着色——可惜,我不知道它使用了何种着色方式。

如果我们要使用客户端着色,那么可以使用Syntax Highlighter这个JavaScript组件。这个组件现在非常流行。博客园便提供了内置的Syntax Hilighter支持。例如,它最基本的使用方式如下:

<pre class="brush:csharp">
static void Main(string[] args)
{ 
    // comment
    var i = 1;
    var s
}
</pre>

便可生成如下的效果:

static void Main(string[] args)
{ 
    // comment
    var i = 1;
    var s
}

然而,Syntax Highlighter的强大之处,在于它的可配置性。例如您可以如此来折叠代码,并高亮其第1和第3行:

<pre class="brush:csharp;collapse:true;highlight:[1,3]">
static void Main(string[] args)
{ 
    // comment
    var i = 1;
    var s
}
</pre>

效果如下(不过博客园的样式似乎不太好看):

static void Main(string[] args)
{ 
    // comment
    var i = 1;
    var s
}

其他更多配置还所有“轻量模式(没有右上角的浮动)”,“起始行号”等等——至少我看了之后就直接震精了。

客户端着色的最大优势,便是在于良好的分离。话说我们为什么要提倡XHTML + CSS的网页编写方式呢?其关键也就是“分离”:“数据”和“样式”的分离。客户端着色的方式,由服务器提供最纯粹的数据,没有任何修饰的数据,而在客户端通过JavaScript来进行“改写”——就好比用CSS来在客户端把平淡的XHTML变得丰富多彩一样。

既然分离,灵活性就高了。例如Syntax Highlighter也提供了一些“代码着色”以外的部分。因为可以使用JavaScript来操作页面,于是我们可以在页面上放置任意灵活的DOM结构。此外,如Syntax Highlighter是将HTML着色成带有不同class(如keyword或comment)的<code>标记。于是,我们可以通过不同的CSS定义来表示不同的着色方式。这样即便需要有所改变(如博客版式变化),那么好,我们重新提供一套着色样式即可。

不过客户端着色也有缺点。首先,您可能已经观察到了,如果使用Syntax Highlighter进行着色,在内容显示的时候是非常平淡的,这一切直到页面加载完毕后才改变,这是因为Syntax Highlighter在windows的load事件中才能进行着色。如果页面加载比较慢(如我的博客),可能就会等待较长时间才能看到着色效果。还有一个缺点可能不太被人关注:如果有人通过RSS等方式订阅了您的文章,那么它便无法获得着色效果了。

如果您只是一个普通用户,那么对于这两个缺点只能选择默默承受。不过,如果您是这个系统的开发人员,那么理论上还有的救,毕竟事在人为么。例如对于第一个缺点,我们可以在每个代码块下方插入一段用于“立即着色”的脚本,这样便可以避免着色的延迟。而第二个缺点,也可以在RSS内容输出之时,为文章里的代码块在服务器端进行着色。

于是这又带来了新的考量:如果系统使用wiki标记或BBCode等方式来保存文章内容,那么便要修改其转化逻辑。如果系统直接保存的是HTML代码,那么我们必须制定一个规则,让服务器端逻辑可以从文章的内容中识别出代码块,这样才能插入“立即着色”的代码,或是将其在服务器端进行着色。那么,您是每次输出时进行着色,还是在系统中直接保存修改好的内容?还有……

可见,“精益求精”也是有代价的。投入产出比是否值得,只有您自己可以作出判断了。

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

27 条回复

  1. 老赵
    admin
    链接

    老赵 2009-12-14 19:53:00

    似乎写的有些乱,回家后再看看改改吧……

  2. GuaiKe
    *.*.*.*
    链接

    GuaiKe 2009-12-14 20:06:00

    如果我贴的代码上又有其他的样式的话,如果再次客户端通过JavaScript来进行“改写”,是否就无法通用了。

  3. 老赵
    admin
    链接

    老赵 2009-12-14 20:21:00

    @GuaiKe
    既然用客户端着色,贴得就应该是纯文本咯,否则就没意义了。

  4. .netlover
    *.*.*.*
    链接

    .netlover 2009-12-14 20:42:00

  5. ITniao
    *.*.*.*
    链接

    ITniao 2009-12-14 20:45:00

    一直关注这个,没见到比较完美的.

  6. BoyLee
    *.*.*.*
    链接

    BoyLee 2009-12-14 20:56:00

    震精的话可以去看一下男科。哈哈。


    貌似我也用的这个。

  7. 鹤冲天
    *.*.*.*
    链接

    鹤冲天 2009-12-14 21:39:00

    用了多个版本的Highlighter,几乎都不行。
    再尝试最后一次,不行还是手工吧。

  8. DiryBoy
    *.*.*.*
    链接

    DiryBoy 2009-12-14 21:48:00

    能用Js着色又Rss Friendly最好,可惜现在都不能兼顾。要不以后让大牛们讨论在HTML6加个代码标签,直接浏览器支持算了~~
    或者搞个什么 Dev 社区插件,专门解释代码标签。

  9. oec2003
    *.*.*.*
    链接

    oec2003 2009-12-14 22:05:00

    @DiryBoy
    如果浏览器能直接支持了 那最好了 呵呵

  10. 老赵
    admin
    链接

    老赵 2009-12-14 22:25:00

    @DiryBoy
    语言层出不穷的,怎么可能内置啊,呵呵。

  11. 老赵
    admin
    链接

    老赵 2009-12-14 22:26:00

    @鹤冲天
    什么叫做多个版本的highlighter?

  12. Daniel Chow
    *.*.*.*
    链接

    Daniel Chow 2009-12-14 23:18:00

    我觉得 Stack Overflow 上用的是 google-code-prettify , 他们自己改了一些!

  13. Rain Shan
    *.*.*.*
    链接

    Rain Shan 2009-12-15 00:08:00

    明天在仔细看看吧!

  14. ☆用心生活☆
    *.*.*.*
    链接

    ☆用心生活☆ 2009-12-15 00:11:00

    老赵就是老赵,总能读到你的 新鲜文章,受益,支持下,学习了。

  15. 装配脑袋
    *.*.*.*
    链接

    装配脑袋 2009-12-15 09:26:00

    基于简单文本查找的代码着色很可能会在注释,字符串之间混杂嵌套的地方出错。比如连续两个/* */,可能会匹配到较远的一个。还有//和/*等同时出现的情况。现在的很多语言都有两种以上的注释格式。同样,字符串的""和''还有转义,都不是简单替换算法能解决的。

    基于词法的着色无法解决限定标识符的问题。比如DataColumn.ReadOnly中,ReadOnly是VB的关键字,但这里是被DataColumn限定的标识符,不应该变成蓝色。一般限定名称的解析不是在词法分析阶段,所以能够解决这个问题的代码着色器极少。

    另外想要达到将类型名称着色(像VS那样)必须做完整的语法分析。估计也不是现在任何着色器能做到的。。

  16. 老赵
    admin
    链接

    老赵 2009-12-15 09:37:00

    @装配脑袋
    脑袋总是那么精辟,所以俺下面谈的“服务器端着色”还是有无可比拟的优势滴……

  17. Jeffery.Sun
    *.*.*.*
    链接

    Jeffery.Sun 2009-12-15 09:40:00

    老赵这里好有人气啊!

  18. 极品拖拉机
    *.*.*.*
    链接

    极品拖拉机 2009-12-15 10:15:00

    打劫你的知识
    快点交出来

  19. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-12-15 10:38:00

    装配脑袋:
    基于简单文本查找的代码着色很可能会在注释,字符串之间混杂嵌套的地方出错。比如连续两个/* */,可能会匹配到较远的一个。还有//和/*等同时出现的情况。现在的很多语言都有两种以上的注释格式。同样,字符串的""和''还有转义,都不是简单替换算法能解决的。

    基于词法的着色无法解决限定标识符的问题。比如DataColumn.ReadOnly中,ReadOnly是VB的关键字,但这里是被DataColumn限定的标识符,不应该变成蓝色。一般限定名称的解析不是在词法分析阶段,所以能够解决这个问题的代码着色器极少。

    另外想要达到将类型名称着色(像VS那样)必须做完整的语法分析。估计也不是现在任何着色器能做到的。。




    VB的关键字都是上下文相关的么?

  20. 张荣华
    *.*.*.*
    链接

    张荣华 2009-12-15 10:59:00

    老赵的文章和脑袋的回复均是上品,要是脑袋也像老赵这么高产就好了。

  21. 四有青年
    *.*.*.*
    链接

    四有青年 2009-12-15 11:56:00

    楼主的涉猎面真广,俺从不关注这个,哈哈学习了。

  22. 装配脑袋
    *.*.*.*
    链接

    装配脑袋 2009-12-15 11:58:00

    @Ivony...

    不都是,但是这种带限定的,任何关键字都可以做标识符使用。还有就是Custom, Option, Explicit, Strict, Infer, Compare, Assembly等等这些都是上下文相关的关键字。还有不是关键字但是也要着色的比如My。

    VB的代码着色绝对是最难的,因为VB连词法分析都是上下文相关的。例如XML字面量和XML表达式哪里。

    其实C#也是一样啊,yield, get, set, field, property, method, assembly, module, add, remove, value……等都是上下文相关的关键字,仅在特定语法才需要变蓝

  23. 装配脑袋
    *.*.*.*
    链接

    装配脑袋 2009-12-15 14:00:00

    做一些试验哈

    public string Foo
    {
        get
        {
            //*
            int set = 0;
            int value = 0;
            string yield = "/*" + set + value + "*/";
            //*/
        }
    }
    


    Dim [Dim] As My.Dim
    Dim [REM] = "REM asdf"" 'Dim a As Dim""" 'Dim a As String
    Dim x = <Dim Rem='Dim '>'HaHa "</Dim>
    

  24. 老赵
    admin
    链接

    老赵 2009-12-15 14:03:00

    @装配脑袋
    果然不合适哉

  25. shenzhen
    *.*.*.*
    链接

    shenzhen 2009-12-15 15:11:00

    谢谢赵老师啊。。又学到新东西

  26. 菜鸟毛
    *.*.*.*
    链接

    菜鸟毛 2009-12-22 16:51:00

    原来着色也有"插件"可用,我这只菜鸟刚学会走路.又吓得不会走了.

  27. Ju2ender
    116.228.217.*
    链接

    Ju2ender 2012-01-11 11:11:41

    正准备换个代码作色插件,学习了 :D

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我