Paste from Visual Studio 是一个很好用的代码高亮插件,对于使用Visual Studio编写代码,用WLW写博客的我们来说实在是不二之选。但是作者从2007年11月22日之后就停止了该插件的更新。而到这时,这个插件还有一些不如人意的地方。
很多人都尝试过对这个插件的改造工作(参见《自己改造VSPaste插件》、《定制Paste from Visual Studio插件(上)》、《定制Paste from Visual Studio插件(下)》),在他们研究的过程中,都由于.NET Reflector无法完成工作而借助了IL。而我在尝试的时候,却发现只是用 .NET Reflector 就完成了对插件源代码的改造工作。以下是具体方法:
用.NET Reflector打开VSPaste.dll文件,选择Export:
导出后就会得到一个C#的项目:
然后,用Visual Studio打开这个项目。我的目的是解决以下几个问题:
-
当IDE的颜色主题做了修改之后(主要是背景),粘贴时自动将背景颜色设置到<pre>标签而不是<span>标签(一般情况下,背景色的改变都不会是针对关键字而是对整个文本编辑器的);
-
如果从代码中间段复制(缩进层次比较多),粘贴时自动将多余的缩进去除。
这两个工作在《自己改造VSPaste插件》中其实都做了,唯一的区别就是DiryBoy是去除背景,而我要保留背景,只是换个地方放置。
第一步,参照DiryBoy的代码直接实现缩进的去除,在VSPaste.cs文件中找到Undent方法,替换其中的源代码:
public static string Undent(string s) { var beginSpaces = new System.Text.RegularExpressions.Regex("^(?:<span[^>]*>|)( +)"); var depth = beginSpaces.Match(s).Groups[1].Value.Length; if (depth == 0) return s; var space2trim = new System.Text.RegularExpressions.Regex ( "^(<span[^>]*>|) {" + depth + "}" , System.Text.RegularExpressions.RegexOptions.Multiline ); var tmp = space2trim.Replace(s, "$1"); var garbageSpan = new System.Text.RegularExpressions.Regex ( "^<span[^>]*></span>" , System.Text.RegularExpressions.RegexOptions.Multiline ); return garbageSpan.Replace(tmp, String.Empty); }
接下来是要将背景设置在<pre>标签中,从源代码里可以发现,<pre>标签的生成是在VSPaste.cs的CreateContent方法中,而背景颜色的判断和设置却不在这里,于是我首先想到的是让具体负责背景颜色的代码将颜色值传递出来,所以对CreateContent的方法我做了这样的修改:
if (Clipboard.ContainsData(DataFormats.Rtf)) { string allBackColor = string.Empty; string htmlContent = Undent(HTMLRootProcessor.FromRTF( (string)Clipboard.GetData(DataFormats.Rtf), out allBackColor)); if(string.IsNullOrEmpty(allBackColor)) { newContent = string.Format( "<pre class=\"code\">{0}</pre><p></p>", htmlContent); } else { newContent = string.Format( "<pre class=\"code\" style=\"background:{0};\">{1}</pre><p></p>", allBackColor, htmlContent); } return DialogResult.OK; }
原来的代码是:
if (Clipboard.ContainsData(DataFormats.Rtf)) { newContent = "<pre class=\"code\">" + Undent(HTMLRootProcessor.FromRTF((string) Clipboard.GetData(DataFormats.Rtf))) + "</pre><p></p>"; return DialogResult.OK; }
我的改变就是用为HTMLRootProcessor类的FromRTF方法增加一个传出参数,通过这个参数来获取背景色,从而可以在生成<pre>标签时直接使用。接下来,打开HTMLRootProcessor这个方法,通过阅读其源代码,可以知道,由int类型的颜色转成string类型是通过ColorProcessor的CssColor方法实现的,而每个HTMLRootProcessor方法里面有一个colors的私有字段就是ColorProcessor的实例。所以,我将HTMLRootProcessor的FromRTF方法改造如下:
public static string FromRTF(string rtf, out string allBackColor) { string str; using (StringWriter writer = new StringWriter()) { using (StringReader reader = new StringReader(rtf)) { ProcessorStack stack = new ProcessorStack(); HTMLRootProcessor processor = new HTMLRootProcessor(stack, writer); stack.Push(processor); Scanner scanner = new Scanner(reader); new Parser(scanner, stack).Parse(); allBackColor = processor.background.HasValue ? processor.colors.CssColor(processor.background.Value) : string.Empty; str = writer.ToString(); } } return str; }
其中高亮的代码就是重点,首先我利用HTMLRootProcessor自带的background属性来存储背景色,然后调用colors实例的CssColor方法来为其赋值,如果没有背景色,则返回空字符串。最后一步,就是修改对background属性赋值的语句,使其只需要判断一次即可(整个代码总共就一种背景色,没必要反复赋值了)。这段代码在HTMLRootProcessor的SyncColors方法中,改造后的代码如下:
private void SyncColors(bool bgOnly) { int? nullable; int? nullable2; if ((this.background != this.nextBackground) || ((((nullable = this.color).GetValueOrDefault() != (nullable2 = this.nextColor).GetValueOrDefault()) || (nullable.HasValue != nullable2.HasValue)) && !bgOnly)) { if (this.color.HasValue || this.background.HasValue) { this.writer.Write("</span>"); } this.color = this.nextColor; if(this.background.HasValue == false) this.background = this.nextBackground; if (this.color.HasValue || this.background.HasValue) { this.writer.Write("<span style=\""); if (this.color.HasValue) { this.writer.Write("color:"); this.writer.Write(this.colors.CssColor(this.color.Value)); } //if (this.background.HasValue) //{ // if (this.color.HasValue) // { // this.writer.Write(';'); // } // this.writer.Write("background:"); // this.writer.Write(this.colors.CssColor(this.background.Value)); //} this.writer.Write("\">"); } } }
其中做了两处修改,第一处是在对background赋值前判断其是否已经有值,如果有了,则不再进行赋值操作。第二处是将原来给<span>标签增加背景色的代码注释掉。
至此已经全部改造完毕,然后进行编译,顺利通过。将得到的VSPaste.dll覆盖到C:\Program Files\Windows Live\Writer\Plugins 目录下,启动WLW,随便从Visual Studio中赋值一段代码,粘贴,得到了本文中代码显示的效果。为了确认,打开没有改变颜色主题的Visual Web Developer 2008 Exress,复制一段代码,粘贴到WLW中,得到了没有背景色的代码样式(这里就不演示了)。至此,大功告成。
最后,对编译修改后的源代码做一点补充说明:如果生成配置是Debug,则没有任何问题,改成Release则一旦使用该插件就会造成WLW崩溃。如果要使用Release配置编译,则应该做如下调整:
这是经过我反复调整测试后得出的,关键在于“定义Debug常量”,其它选项都可选可不选。
本人水平有限,虽然通过尝试得出了正确结果,但是对于编译时发生的事情却不知其所以然,也无法解释其原因。望高人指点。
==================================================
修改后的VSPaste.dll下载:vspaste.zip
作者:小李刀刀
原文链接:VSPaste 的持续改造
裁纸刀下版权所有,允许非商业用途转载,转载时请原样转载并标明来源、作者,保留原文链接。