定制Paste from Visual Studio插件(下)
2009-12-16 14:27 by 老赵, 5820 visits上一篇文章里我们进行了简单的实验,验证了通过修改IL生成新插件的可行性,不过我们要做的事情还有很多,因为我们实际要做的事情其实是……插入行号。这需要我们补充新的逻辑,并且对CreateContent进行修改。那么我们又该如何写这大段大段的IL呢?没关系,其实这些事情不懂IL也可以做。
添加行号
首先,我们需要写一个AddLines方法,修改一下HTML:
public static string AddLines(string html) { var lines = html.Trim().Split('\n'); string pattern = "<span style=\"color:black; font-weight:bold;\">{0:" + new String('0', lines.Length.ToString().Length) + "}: </span>"; for (int i = 0; i < lines.Length; i++) { lines[i] = String.Format(pattern, i + 1) + lines[i]; } return String.Join("\n", lines.ToArray()); }
这段方法的作用是在每行HTML之前加入一个<span />来显示行号,其中通过总行数来计算行号的“位数”,这样便可以在显示如“01”、“02”这样的行号。那么,这段方法又如何给CreateContent方法调用呢?
生成结构相同的IL
别急,还是先来简单观察一下目前CreateContent方法的IL代码——不求看懂,但求了解个两行内容:
.method public hidebysig virtual instance valuetype [System....
CreateContent(class [System.Windows.Forms]System.Win...
string& newContent) cil managed...
{
// Code size 101 (0x65)
.maxstack 4
.locals init (valuetype [System.Windows.Forms]System.Windo...
bool V_1)
IL_0000: nop
.try
{
IL_0001: nop
IL_0002: ldsfld string [System.Windows.Forms]System...
IL_0007: call bool [System.Windows.Forms]System.W...
IL_000c: ldc.i4.0
IL_000d: ceq
IL_000f: stloc.1
IL_0010: ldloc.1
IL_0011: brtrue.s IL_0042
...
IL_0029: call string HTMLRootProcessor::FromRTF(string)
IL_002e: call string VSPaste.VSPaste::Undent(string)
IL_0033: ldstr "</pre>"
IL_0038: call string [mscorlib]System.String::Concat(string,
string,
string)
...
别的不管,看到两个call指令吗?相信即便您不懂IL,也可以推测出它们是在“调用方法”。从中我们也可以发现,这个call指令……不就是指定一个方法的名称(包括命名空间)和参数吗?既然如此,我们构建一个类似代码“结构”也实在太容易了:
public class HTMLRootProcessor { public static string FromRTF(string s) { return null; } } namespace VSPaste { public class VSPaste { public static string AddLines(string html) { /* ... */ } public static string Undent(string s) { return null; } public DialogResult CreateContent( IWin32Window dialogOwner, ref string newContent) { try { if (Clipboard.ContainsData(DataFormats.Rtf)) { var content = (string)Clipboard.GetData(DataFormats.Rtf); var html = Undent(HTMLRootProcessor.FromRTF(content)); var withLines = AddLines(html); newContent = "<pre class=\"code\">" + withLines + "</pre>"; return DialogResult.OK; } } catch { MessageBox.Show( "VS Paste could not convert that content.", "VS Paste Problem", MessageBoxButtons.OK, MessageBoxIcon.Hand); } return DialogResult.Cancel; } } }
于是,我们把这段代码随意放在某个程序集中,然后同样使用ildasm.exe获得其IL代码:
.class public auto ansi beforefieldinit VSPaste.VSPaste extends [mscorlib]System.Object { .method public hidebysig static string AddLines(string html) cil managed { // Code size 121 (0x79) ... } // end of method VSPaste::AddLines ... .method public hidebysig instance valuetype [System.Windows.Forms]Syste CreateContent(class [System.Windows.Forms]System.Windows.Forms. string& newContent) cil managed { // Code size 97 (0x61) .maxstack 4 ... IL_001d: call string HTMLRootProcessor::FromRTF(string) IL_0022: call string VSPaste.VSPaste::Undent(string) IL_0027: stloc.1 IL_0028: ldloc.1 IL_0029: call string VSPaste.VSPaste::AddLines(string) IL_002e: stloc.2 IL_002f: ldarg.2 IL_0030: ldstr "<pre class=\"code\">" IL_0035: ldloc.2 IL_0036: ldstr "</pre>" IL_003b: call string [mscorlib]System.String::Concat(string, string, string) ... } // end of method VSPaste::CreateContent } // end of class VSPaste.VSPaste
可以看到,在CreateContent方法中,也有和之前相同的FromRTF方法与Undent方法的调用,以及我们新的AddLines方法。因此,我们只要打开VSPaste.il,先把AddLines方法的IL复制到VSPaste类中,然后用新的CreateContent方法体(即从.maxstack开始的部分)替换旧的实现即可。
成果
保存VSPaste.il,使用ilasm.exe生成dll,再把它复制到Windows Live Writer的Plugins目录中。那么我就第一个试用吧:
01: public static string AddLines(string html) 02: { 03: var lines = html.Trim().Split('\n'); 04: string pattern = 05: "<span style=\"color:black; font-weight:bold;\">{0:" + 06: new String('0', lines.Length.ToString().Length) + 07: "}: </span>"; 08: 09: for (int i = 0; i < lines.Length; i++) 10: { 11: lines[i] = String.Format(pattern, i + 1) + lines[i]; 12: } 13: 14: return String.Join("\n", lines.ToArray()); 15: }
效果如何,还不错吧?当然,这个逻辑其实还不够成熟,不过通过这个思路,您可以把VSPaste修改为任何需要的样子。当然,这两篇文章只是一种“体验”,这种修改方式并不“健康”也不值得提倡。而且我忽然意识到,如果我们真只是为了使用VSPaste中RTF转HTML的功能,其实也可以简单地引用它,然后调用其中的方法……反正都是public的……
相关文章
- 定制Paste from Visual Studio插件(上)
- 定制Paste from Visual Studio插件(下)
老赵速度好快啊