VS简单注释插件——VS插件开发续

VS简单注释插件——VS插件开发续

前些时候,我写过一篇《VS版权信息插件——初试VS插件开发小记》分享过一个用于添加注释信息的插件,但那个插件有几个问题:

  1. 不能添加带块注释(/**/),只能用//来注释(见旧文最后处的遗留问题)
  2. 添加的注释,如果按Ctrl+Z只能一行一行的删除(而非期望的整块删除)
  3. 只有一个模板,不能对多种文件进行注释(比如模板是针对c#的,那就当然不能对xml文件注释,因为注释符号不同)
  4. 不能在发布到微软的扩展库里(不能通过VS扩展管理器来安装)

对于以上1、2两点,最后找到问题的根源,是因为之前的代码是这么写的:

复制代码
    TextSelection selectedText = _vs.ActiveDocument.Selection as TextSelection; //获取选择的文本对象

    string copyInfo = AddInHelper.Read();   //读取版权配置信息

    copyInfo = copyInfo.Replace("@time", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"));//替换时间点位符

    selectedText.Text = copyInfo;   //覆盖选择文本
复制代码

注意最后一句,直接用注释内容来替换选中文本的话,VS处理过程实际是一行一行来添加的,这也是为什么按Ctrl+Z是一行行的删除了,同理块注释(/**/)格式错乱的原因也是如此。

第3点算是新需求,至于第4点我开始以为注册个账号,发布到微软库里就行了呢,谁知道微软不支持.AddIn插件的发布,官方建议使用的是.vsix格式的来发布插件。那么接下来,我又研究了下vsix是什么个东西,怎么个开发方法,于是便有了本文。

关于VSIX

关于VSIX开发,网上中文的介绍不是很多(我没找到几个),我在博客园看到有 Visual Studio 扩展包(.vsix)制作VS2010 Extension实践 和 明年我18岁的这个翻译系列(这个系列还是比较好的,我基本上是从这里扒到对VSIX开发的一些认识,有兴趣的可以看看)。

由于对COM开发没有经验,一个小小的插件开发,苦哥我还是走了不少苦路。对于AddIn里简简单单的DTE对象,在VSIX里我网上找了好久才知道怎么获得,此步着实浪费了不少时间。最终在一个开源的插件源码里扒到(哪个插件我忘了),要像下面这样获得:

    var dte = (DTE)Package.GetGlobalService(typeof(DTE));

还有一处浪费了不少时间的就是多语言的支持(我是有强迫症的人,中文界面上因为安装个插件出现几个英文菜单,哥是很不爽的,同理英文界面上出现几个中文,哥也不能忍受),关于这点我在以上几篇博文里都没扒到, google到的几篇英文博文貌似也没讲到,最后在msdn上扒了半天,一点一点滴照着改,最终也算是完成了(下文将详细作个介绍)。

由于本人也没有研究太多,仅限于本插件的开发范围,所以对VSIX的具体介绍就此处省略1G字节了……

项目结构

还是老套路,给大家简单介绍下项目的结构,方便大家拿到源码后快速理清代码。下面是项目结构截图(注意开发VSIX扩展要安装Visual Studio SDK):

VS简单注释插件——VS插件开发续_第1张图片

初看上去文件虽然多,但其中半数是模板生成的,下面简单介绍一下:

  • Common目录下是一些辅助类(配置文件读写、xml序列化与反序列化等)
  • Core目录下是添加注释的逻辑
  • Models目录下是注释对象实体
  • Options目录下是用户控件(用于 工具|选项 窗口)
  • Resources目录下是一些资源文件
  • zh-CN目录和其他同样颜色框出的都是本地化要用的资源文件(下文会详细说明)
  • SimpleAnnotationPackage.cs是整个扩展的入口,是一个插件与VS连接的地方
  • SimpleAnnotation.vsct是定义菜单的xml文件
  • Source.extensio.vsixmanifest是用来描述插件信息的
  • Guids.cs封装一些guid(貌似在COM编辑世界里,用GUID来作为组件ID ?)
  • PkgCmdID.cs定义菜单命令的ID

遗留问题的解决

关于1、2两点遗留问题,既然找到了问题的根源,那也有找到了解决方案。其实只要使用TextSelection类型的Insert方法来插入内容就可以了,像下面这样(代码参见Core目录下的AnnotationCore.cs文件):

selectedText.Insert(annotation.Replace("@time", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));

关于第3点多种文件格式多种模板的支持,其实是在我使用上一个插件的过程中想到要加的功能,因为cs文件的注释模板明显不能用在xml文件里。这个功能实现也不复杂,只要用key/value的形式,用key来保存支持的文件扩展名,用value来保存对应的注释内容就行了。

VS简单注释插件——VS插件开发续_第2张图片

Setting文件的问题

为什么要说setting文件,因为开始我是用setting文件来保存配置好的注释模板,而不是用config或xml,因为后两者都要自己解析,处理起来远没有setting文件的强类型化用起来爽(关于setting文件,不太了解的可以参考一下园子里的这篇博文)。但是一个很奇怪的事情是,每次保存好配置,再次打开VS后都没了。。。

研究半天不得解决,最终换成用xml来保存。不知道是我使用的问题,还是在插件开发中就不能用setting文件,还请知道的园友指点!

XML序列化的问题

当我把配置方案从setting换成xml后,又出现一个小问题,开始我是用Dictionary<string,string>来保存注释模板,结果Dictionary不支持直接XML序列化,网上查了查解决方案,感觉太麻烦了。所以写了个Annotation类型:

复制代码
    public sealed class Annotation
    {

        private string _fileExtension;

        /// <summary>
        /// 支持的文件扩展名
        /// </summary>
        public string FileExtension
        {
            get { return _fileExtension; }
            set { _fileExtension = value; }
        }

        private string _content;

        /// <summary>
        /// 注释内容
        /// </summary>
        public string Content
        {
            get { return _content; }
            set { _content = value; }
        }
}
复制代码

然后用一个List来保存配置模板,这样序列化的问题就解决了,可是当我打开生成的xml文件时(文件保存在%User%\AppData\Local\SimpleAnnotation目录下),发现Content(注释内容节点)被编码了:

VS简单注释插件——VS插件开发续_第3张图片

虽然不影响功能,但非传说中的可见即可得,看上去非常不直观。所以我想到了CData节点,本以为只要在Content属性上加上某个特性(Attribute)标记一下就行了呢,一查才知道,微软这次这么不给力,竟然没有提供这样的特性。然后找到的解决方案,都是自定义一个CData类型,实现IXmlSerializable,但我感觉这样的解决方案甚是丑陋,不过在通过了解这种解决方案的时候,想到一种“更好的”办法,其实只要将string类型包装成XmlCDataSection类型给序列化器应该就行了。所以将Annotation类改成了下面的样子,一个string类型的Content属性用来在程序里操作_content字段,一个XmlCDataSection类型的CdataContent属性用来提供给XML序列化器,这样就解决了string到CData节点的序列化问题,也没有增加任何类,感觉还算比较满意(要是能不把CDataContent属性暴露出来就更完美了,可惜貌似办不到)。

复制代码
    [Serializable]
    public sealed class Annotation
    {

        private string _fileExtension;

        /// <summary>
        /// 支持的文件扩展名
        /// </summary>
        public string FileExtension
        {
            get { return _fileExtension; }
            set { _fileExtension = value; }
        }

        private string _content;

        /// <summary>
        /// 注释内容
        /// </summary>
        [XmlIgnore]
        public string Content
        {
            get { return _content; }
            set { _content = value; }
        }

        /// <summary>
        /// 内部序列化使用
        /// </summary>
        [XmlElement("Content")]
        public XmlCDataSection CDataContent
        {
            get
            {
                return new XmlDocument().CreateCDataSection(_content);
            }
            set
            {
                _content = value.InnerText;
            }
        }
    }
复制代码

本地化总结

前文也提到了我为什么要本地化,不是为了给外国人用,是哥有强迫症(T_T),也同时是为了练习一下传说中的资源文件(之前一直没用过)。在此希望我这篇博文能对国内VSIX本地化这块欠缺的内容作一点补充。

1、本地化扩展管理器里的内容

VS简单注释插件——VS插件开发续_第4张图片

这里是一个插件第一次向人展现自己的地方,这里的本地化是通过在项目根目录下,新建zh-CN目录(其他语言同种做法),然后增加一个名为Extension.vsixlangpack的xml文件,内容如下:

复制代码
<?xml version="1.0" encoding="utf-8"?>
<VsixLanguagePack Version="1.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema-lp/2010">
  <LocalizedName>Simple Annotation</LocalizedName>
  <LocalizedDescription>Simple Annotation(简单注释)是一个用于让添加注释变得更简单的VS插件。</LocalizedDescription>
  <MoreInfoUrl></MoreInfoUrl>
</VsixLanguagePack>
复制代码

同时要将属性Include in VSIX改为true,还有一点要注意,就是你的source.extension.vsixmanifest里一定不要与这里设置重复了,不然不会使用这里的配置。

VS简单注释插件——VS插件开发续_第5张图片

我开始将这里的Locale设置成中国,结果就是按上面怎么配置都不显示中文!这块内容是从msdn扒到的,有兴趣的童鞋也可自行研究。

2、本地化帮助|关于里的内容

VS简单注释插件——VS插件开发续_第6张图片

我们先看一段代码(代码在SimpleAnnotationPackage.cs文件里),

复制代码
    [PackageRegistration(UseManagedResourcesOnly = true)]
    [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]     //帮助|关于 注册
    [ProvideMenuResource("Menus.ctmenu", 1)]    //菜单 注册
    [Guid(GuidList.guidSimpleAnnotationPkgString)]
    [ProvideOptionPage(typeof(GeneralOptionPage), "SimpleAnnotation", "General", 201, 202, true)]   //工具|选项 注册
    public sealed class SimpleAnnotationPackage : Package
复制代码

以上代码是VS的扩展开发模板自动生成的,这里可以看到所谓的扩展就是自定义的Package,上面加了一大堆特性,其中几个需要注意的我已经加了注释。

帮助|关于里的内容,是通过InstalledProductRegistration特性来注册的(说法可能有问题,应该不影响理解),其构造方法如下:

    public InstalledProductRegistrationAttribute(string productName, string productDetails, string productId);

因此我们代码中的”#110”便是productName,”#112”便是productDetails,这里的”#110”和”#112”就是资源文件VSPackage.resx里资源的名称:

到这步,我很自然地想到了,新增一个资源文件VSPackage.zh-CN.resx,但我只猜对了开头,没有猜对结尾。虽然是要增加这么个资源文件,但并不是增加个这个文件就能本地化了。

最后还是要苦扒msdn ,首先要将默认的资源文件VSPackage.resx改名为VSPackage.en-US.resx(一定要改啊,擦),然后要将AssemblyInfo.cs里应对的地方改成:

    [assembly: NeutralResourcesLanguage("en-US"(此处是默认语言), ltimateResourceFallbackLocation.Satellite)]

最后要卸载项目,鼠标右键编辑项目文件,找到包含EmbeddedResource 的ItemGroup,将内容改成下面这样(msdn上这里代码贴错了,害我搞了半天没成功):

复制代码
    <EmbeddedResource Include="VSPackage.en-US.resx">
      <MergeWithCTO>true</MergeWithCTO>
      <LogicalName>VSPackage.en-US.Resources</LogicalName>
    </EmbeddedResource>
    <EmbeddedResource Include="VSPackage.zh-CN.resx">
      <MergeWithCTO>true</MergeWithCTO>
      <LogicalName>VSPackage.zh-CN.Resources</LogicalName>
    </EmbeddedResource>
复制代码

上面代码贴的是我的配置,相信再增加其他语言的支持,聪明的你肯定知道怎么处理了^_^。

3、本地化配置页里的菜单名称

VS简单注释插件——VS插件开发续_第7张图片

这里的左边菜单本地化方式同关于页里一样,只要在对应的VSPackage资源文件里增加201和202的菜单名称就可以了:

VS简单注释插件——VS插件开发续_第8张图片

右边的菜单本地化就方便多了,因为这里是一个用户控件,使用的资源文件是我添加的Resources.resx文件

VS简单注释插件——VS插件开发续_第9张图片

在用户控件初始化后,用资源来设置界面显示(代码在Options目录下GeneralOptionControl.cs里):

复制代码
        public GeneralOptionControl()
        {
            InitializeComponent();

            btnSave.Text = Resources.Save;
            btnRemove.Text = Resources.Remove;
            btnClear.Text = Resources.Clear;
            lblSetFileExtension.Text = Resources.lblSetFileExtension;
            lblSetAnnotation.Text = Resources.lblSetAnnotation;
            lblRemark.Text = Resources.lblRemark;
        }
复制代码

这里我们只要添加相应的Resources.zh-CN.resx就可以实现界面的中文化了。不过还有一点要注意一下,必须还要增加一个Resources.en-US.resx的资源(即使我这里它的内容与Resources.resx完全相同)。

4、本地化工具栏的菜单

VS简单注释插件——VS插件开发续_第10张图片

最后一处就是工具菜单里的本地化了,这里非常简单,因为菜单是通过SimpleAnnotation.vsct文件来注册的,只要增加一个带language的<strings>节点就行了(并不需要按msdn上面那样做),如下:

复制代码
      <Button guid="guidSimpleAnnotationCmdSet" id="SimpleAnnotationCommandId" priority="0x0100" type="Button">
        <Parent guid="guidSimpleAnnotationCmdSet" id="MyMenuGroup" />
        <!--图标-->
        <Icon guid="guidImages" id="bmpPic1" />
        <Strings>
          <CommandName>SimpleAnnotationCommandId</CommandName>
          <ButtonText>Insert Annotation</ButtonText>
        </Strings>
        <Strings language="zh-CN">
          <CommandName>SimpleAnnotationCommandId</CommandName>
          <ButtonText>插入注释信息</ButtonText>
        </Strings>
      </Button>
复制代码

最后!如果你在本地化过程中总是不成功,要注意一下下面几处:

  • Extension.vsixlangpack文件的Include in VSIX属性是否设置成true了
  • 所有的资源文件,生成操作是否设置成“嵌入的资源”了
  • AssemblyInfo里是否设置[assembly: NeutralResourcesLanguage("en-US"(此处是默认语言), UltimateResourceFallbackLocation.Satellite)]
  • source.extension.vsixmanifest里的locale是否设置正确了

发布时的问题

就在我将插件提交到微软官网的时候(只有发布了才能在扩展管理器找到),又出现了一个新问题,实践真是步履维艰!

老是报“VSIX中缺少图标”,最后自己慢慢摸索,发现必须要将source.extension.vsixmanifest里的图标都设置了,而且这两个图片必须在项目根目录下(开始我并不是放在根目录下),

同时图片的Include in VSIX属性要设置成true

VS简单注释插件——VS插件开发续_第11张图片

发布后,有个说明内容是要求自定义的,我上传后并没有及时做这块工作,第二天看时就成下面这样子了(T_T),所以提醒大家在发布插件时,一定要及时地把说明信息提供完整。

VS简单注释插件——VS插件开发续_第12张图片

悲剧啊,必须要发邮件与网站管理员联系,我就用我四级433的英文水平,发了封邮件过去,庆幸得是他们应该看懂了,过了一天,给我解锁了(这里是插件在微软扩展库里的地址)。

PS:由于本人VS是中文版的,希望哪位用英文版VS的园友,告诉我在英文版VS下是不是以上都正确显示成英文了。。。

总结与源码

这个插件的开发,远比我相像中遇到的问题要多,不过因此收获也多。同时深刻体会到,动手去实践是多么的重要啊,有些东西你不实践,只凭空相像,永远不知道难点(或问题)在哪里。

精力有限,暂时未做批量添加注释的功能,后期有时间再补上来吧,有兴趣的童鞋也欢迎在此基础上完成更多的功能。同时博文及源码不足之处,欢迎园友们指出!感谢!

另:关于插件的获得方式

  • 在VS扩展管理器里查找simple annotation即可
  • 下载源码,编译安装(记得要先安装VS SDK噢)
作者: 何苦丶
园子: http://hecool.cnblogs.com
作者水平有限,如果文中有任何不足之处,还请园友们指出,感谢!!!
 
分类:  C#OpenSource
标签:  VSVSIX本地化

你可能感兴趣的:(本地化,vs,VSIX)