浅谈代码着色(下):服务器端着色
2009-12-15 15:38 by 老赵, 6960 visits上篇文章谈了客户端着色,而现在自然就来讨论服务器端着色了。先下个定义:我在这里谈的“服务器端着色”,是指直接从服务器端输出着色效果的做法(与“客户端着色时”输出纯代码文本相对)。至于这个着色效果是如何获得的,例如是由另一个用户直接提供的,还是用户提供纯代码文本,而用服务器端逻辑“着色”,在这里就统称为“服务器端”着色了。不过接下去的讨论,我们还是会作一些区分。
客户端着色的最大问题,在于很可能会出现着色错误,例如这段C#代码:
public string Foo { get { //* int set = 0; int value = 0; string yield = "/*" + set + value + "*/"; //*/ } }
或者这段VB.NET:
Dim [Dim] As My.Dim Dim [REM] = "REM asdf"" 'Dim a As Dim""" 'Dim a As String Dim x = <Dim Rem="Dim '">'HaHa "</Dim>
很明显这两段代码的着色都有问题(如C#的set关键字和VB.NET的XML Literal)——它们是脑袋在上篇文章的评论中给出的两个例子,他解释道:
基于简单文本查找的代码着色很可能会在注释,字符串之间混杂嵌套的地方出错。比如连续两个/* */,可能会匹配到较远的一个。还有//和/*等同时出现的情况。现在的很多语言都有两种以上的注释格式。同样,字符串的""和''还有转义,都不是简单替换算法能解决的。
基于词法的着色无法解决限定标识符的问题。比如DataColumn.ReadOnly中,ReadOnly是VB的关键字,但这里是被DataColumn限定的标识符,不应该变成蓝色。一般限定名称的解析不是在词法分析阶段,所以能够解决这个问题的代码着色器极少。
另外想要达到将类型名称着色(像VS那样)必须做完整的语法分析。估计也不是现在任何着色器能做到的。
同样,您可以尝试着将代码放入Editplus或Notepad++等工具中查看,错误依旧。对于这个问题,您可以简单的理解为:由于进行“客户端着色”时缺少足够的缺少信息,因此在很多情况下着色会出现偏差。而由于VS本身使用完整的语法分析,因此它的着色效果自然无可挑剔:
class Program { public string Foo { get { //* int set = 0; int value = 0; string yield = "/*" + set + value + "*/"; //*/ } } }
由于直接使用了VS的着色规则,因此这里的set关键字也正确了,Program作为类名也有漂亮的颜色,这样整段代码看上去非常赏心悦目,它也是我在博客上用的着色方式。我使用Windows Live Writer写博客,配合Paste from Visual Studio插件,可以直接从Visual Studio里复制一段代码插入到文章内容中,例如对于上面的代码它便会生成这样的HTML:
<pre class="code"><span style="color: blue">class </span><span style="color: #2b91af">Program </span>{ <span style="color: blue">public string </span>Foo { <span style="color: blue">get </span>{ <span style="color: green">//* </span><span style="color: blue">int </span>set = <span style="color: brown">0</span>; <span style="color: blue">int </span>value = <span style="color: brown">0</span>; <span style="color: blue">string </span>yield = <span style="color: #a31515">"/*" </span>+ set + value + <span style="color: #a31515">"*/"</span>; <span style="color: green">//*/ </span>} } }</pre>
很长,还好我们平时不会去阅读这类HTML源文件表现的具体内容。我们可以发现,在这段HTML中,所有的颜色都直接出现在代码中。这么做的优点在于即便是别人通过RSS订阅了您的文章,再它的阅读器里也可以看到美观而工整的着色样式。当然,它也有一些缺点。而它最大的问题便在于样式和内容直接耦合在了一起。也就是说,这么做的显示效果其实严重依赖它所存在的环境。假设,我忽然有一点想要把博客的风格切换成很酷的黑色,这样代码块的阅读体验就十分堪忧了。同样,如果读者的RSS阅读器使用了某种花哨的风格……
要解决这个问题并不容易。一个可以尝试的做法是在HTML中使用class来标记元素,而并非直接将颜色代码写在HTML当中。例如:
<span class="keyword">class </span><span class="type">Program</span>
由于我们只是标记了HTML元素的“种类”,因此代码的样式便可以自由定义了。即便是我们换了一个新的环境,那提供另一套CSS样式即可。可惜的是,这种做法在RSS读者那边又失效了。当然,如果您也可以在服务器端的逻辑里为代码快进行重新着色……对于完美主义者,我们还是应该表示尊敬的。
可惜的是,我在博客里还是直接内嵌颜色代码,而不是使用class+CSS的方式。第一个原因是由于我可能不会对博客样式有“质的调整”,但更重要的原因是……插件没有直接支持,可能是VS直接提供了着色结果,而不是“结构化数据”。当然,我们其实也可以重新写一个插件,从特定颜色代码“识别回”类型(其实普通的查找替换应该也足够了)。既然有Paste from Visual Studio插件作为示例,做到这点其实应该也是挺容易的。
好吧,如果真的某一天我需要把博客切换为黑底,又该怎么办呢?没办法了,写一个程序,利用博客园提供的Weblogs API,批量替换一下吧。
回到文章开头,我谈到“服务器端着色”的另一种情况,是使用服务器端的逻辑将代码块进行高亮,这种做法大都出现在处理wiki标记或BBCode时进行。很可惜,这也是在一种缺少上下文信息的环境下进行着色,与“客户端着色”一样,难以出现完美的着色效果。最后可能值得一提的是,我在某段不算太长的时间内还是用过一种“不堪入目”的着色方式:
<pre class="code"><span style="color: blue">public string </span>Foo<br />{<br /> <span style="color: blue">get<br /> </span>{<br /> <span style="color: blue">return </span><span style="color: #a31515">"bar"</span>;<br /> }<br />}</pre>
感觉如何?其实它的最终结果只是如此简单:
public string Foo { get { return "bar"; } }
由于博客园的后台的某个编辑器,总是“自做主张”地将<pre />内的代码进行“优化”,把多个空白字符替换成一个,于是换行没了,缩进也没了。于是,我只能手动将换行替换为<br />,将多个空格替换为 。不过您还真别说,这个丑陋的做法还有另一个效果,那就是对于某些同样自做聪明的阅读器或浏览器(大都是移动平台上的程序),代码片断也能显示正常——而之前的所有方式,在那些设备上都只会显示为一行。
真是可歌可泣。
测试代码着色:
看来对注释处理不错。
那么字符串呢?
看来用的是正则表达式,所以这种都难不倒。