如何扩展VisualStudio编辑器
原创转自:http://www.cnblogs.com/stg609/p/3711443.html
在 Visual Studio 2010 扩张时代 Visual Studio 开发人员可以通过多种方式从宏中进行选择Add-in、MEF 和 VSPackages 自定义扩展。但宏是 Visual Studio 2012 当我被阉割时,Add-in 也在 Visual Studio 2013 内部已被擦除,这种调整对于 Visual Studio 对于那些习惯于使用宏和Add-in团队可能会感到沮丧。
本文将逐步教您如何实现 Visual Studio 代码编辑器的扩展最终将实现一个可以支持以下两个函数的扩展。
1. 自动任务标注(支持后期扩展)
任务注释为 //TODO、//FIXME 类似的东西可以是 Visual Studio 不同的批注可能需要不同的格式来处理不同的任务。例如,有些需要先注释掉方法主体,而另一些则需要在代码行之前和之后添加开始和结束标记。过去,当宏仍然受支持时,我们可以使用宏来实现此类操作。不同的项目组使用这些标记的方式不同,因此可能存在不同的要求。此工具支持自定义扩展。
2. 跳转到方法的头部或尾部
此功能可能看起来毫无用处,但是当一个方法有数百甚至数千行时,快速跳转到方法的开头或结尾可能会很麻烦。
图1 最终效果动画演示(请点击放大查看)
阅读本文需要你对以下知识点有一定的了解:
- MVVM 与 WPF -> 快点了解更多有关它的信息 (这篇文章躺在我自己的网站上,我没有时间将其同步到博客花园。每个人都会凑合着用它。
- MEF -> 快点了解更多有关它的信息
如果您想快速体验此扩展,请狠狠地打一下 这里 ! (仅限 Visual Studio 2012)
本文提纲
Editor Viewport Adornment 原理解析
在进入正文之前,让我们快速了解一下这两位剩下的英雄~
MEF 和 VSPackage
从 Visual Studio 2013 开始,对 Visual Studio 剩下的唯一扩展是 MEF 和 VSPackage。来吧,让我给大家介绍一下~~
MEF(Managed Extensibility Framework)
该框架最初独立于 .Net Framework 发布,后来集成到 .Net 4.0 中,伴奏 .Net 4.0 一起发布(包含在 System.ComponentModel.Composition.dll 程序集)。从名称可以看出,这个框架主要是为编写可扩展的应用程序而设计的。以及 .Net 4.0 另一个重大变化是推出 Visual Studio IDE编辑器,最初像其他组件一样使用 COM 方法开发,但现在它已经 WPF 技术已经取代了它。采用WPF技术架构的编辑可以完全支持使用 MEF 扩展是一个完美的改进。唯一的遗憾是,截至2013的出现,Visual Studio 其余部分仍未从 COM 从中间分离。
The MEF is a .NET library that lets you add and modify features of an application or component that follows the MEF programming model. The Visual Studio editor can both provide and consume MEF component parts. The MEF is contained in the .NET Framework version 4 System.ComponentModel.Composition.dll assembly.
因此,如果要扩展现有编辑器,可以基于它MEF开发(例如修改C#突出显示颜色、智能提示、括号补全等。在代码编辑器中。
VSPackage
可以说,除了小编, Visual Studio 就是多个 VSPackage 收集,因此使用 VSPackage 可以完美匹配 Visual Studio 集成并获取几乎所有功能。如果要开发工具栏、菜单栏,甚至是全新的编辑器(提供新语言解析、智能提示等),可以选择VSPackage。
准备工作
童鞋,请查一下食客准备好了吗?
-
英文版的 Visual Studio中文版 Visual Studio 无法看到 Editor Text Adornment 等模板。
-
想要扩展编辑器或整个 Visual Stuio,必须先下载并安装 Visual Studio SDK(VS2012版本,请点击 链接 安装后,您可以下载 其他项目类型 在模板上找到所需的模板。
图2 SDK安装后的模板
本文中的所有代码均基于 Visual Studio 2012 发展。如果您使用的是其他版本,请下载并安装适合您版本的版本 SDK。扩展开发的过程在不同版本之间可能会有细微的差异,但不会影响整体开发过程。
各种 Editor 模板的差异
安装完 SDK 之后,将有上图所示的各种扩展模板,包括四个和 Editor 相关模板之间有什么区别吗?
Editor Classifier
你可以在编辑器中修改代码的突出显示,添加一些智能标签(比如当我们修改一个变量名时,变量名下面会出现一个小破折号,当你将鼠标移到它上面时,系统会提示你是否修改所有引用的地方)等等。示例效果如下:
图3 Editor Classifier 示例
Editor Margin
在编辑器周围添加一些WPF元素(例如当前文件为只读时)可以在编辑器的底部边缘提示文件是只读的。示例效果如下:
图4 底部边缘添加了绿色信息框
Editor Text Adornment
用于修改编辑器中的文本并添加一些WPF该元素的示例效果如下所示:
图5 将所有字符包装在一个框中 a
Editor Viewport Adornment
用于修改编辑器本身并添加一些WPF元素、示例如下:
图6 在编辑器的右上角添加了一个矩形元素
使用此工具 Editor Viewport Adornment 作为模板。
Visual Studio 实验环境
为了测试这些扩展,Visual Studio 提供了 Experimental Instance 在与真实一致的实验环境中使用 Visual Studio 完全一样,只是它和真实版本各有一套唯一的配置文件,实验环境的配置不会影响真实环境。
首次启动实验环境时,将输入下图7显示的默认环境配置的界面。
图7 默认环境配置
实验环境中的数据可以初始化
SDK目录中的 Tools提供了 “Reset the Visual Studio 2012 Experimental Instance” 命令行工具,运行此工具将初始化实验环境。
图10 初始化工具
Editor Viewport Adornment 原理解析
要理解它,必须先使用它。创建新模板 Editor Viewport Adornment 在项目进行时,示例代码已经包含在内,其功能如图所示6在编辑区域添加一个紫色矩形框,如图所示。
图11 刚刚创建的外观
在运行示例代码之前,必须进行修改 source.extension.vsixmanifest 文件中的 author 字段,否则运行将报告错误。
图12 补全 Author 字段
实现原理
在介绍 MEF 和 VSPackage 当我提到整件事时 Editor 其中一部分是基于 MEF 发展想法。简单地说,Visual Studio Editor 提供了第三方扩展 产物 (Export)、 接受者 (Import)及各种 协议 第三方扩展根据相应的协议生成兼容产品,然后 VS 我们将第三方产品与我们自己的收件人相结合(例如将成型块放入盒子中)。这样,我们下次就可以开始了 VS 使用此扩展时。
图13 MEF 思想
此项目中有两个主要文件: TskCommentFactory.cs 和 TskComment.cs 。
其中,TskCommentFactory 文件中的 PurpleBoxAdornmentFactory 就是基于 IWpfTextVIewCreationListener 本协议 产物 它也是该项目的主要入口。通过使用此协议,我们可以在创建编辑器视图时添加所需的操作。最重要的是 TextViewCreated 方法,它调用 TskComment 构造函数,从而在编辑器上添加一个紫色区域。
1 [Export(typeof(IWpfTextViewCreationListener))]
2 [ContentType("text")]
3 [TextViewRole(PredefinedTextViewRoles.Document)]
4 internal sealed class PurpleBoxAdornmentFactory : IWpfTextViewCreationListener 5 {
6 ///
15 ///
TskComment 文件中主要有两种方法: 构造函数 和 onSizeChange 方法。在构造函数中,由 Brush 绘制一个紫色矩形并将其绑定到编辑器视图 onSizeChange 方法。
1 Brush brush = new SolidColorBrush(Colors.BlueViolet); 2 brush.Freeze();
3 Brush penBrush = new SolidColorBrush(Colors.Red); 4 penBrush.Freeze();
5 Pen pen = new Pen(penBrush, 0.5);
6 pen.Freeze();
7
8 //draw a square with the created brush and pen
9 System.Windows.Rect r = new System.Windows.Rect(0, 0, 30, 30);
10 Geometry g = new RectangleGeometry(r);
11 GeometryDrawing drawing = new GeometryDrawing(brush, pen, g);
12 drawing.Freeze();
13
14 DrawingImage drawingImage = new DrawingImage(drawing);
15 drawingImage.Freeze();
16
17 _image = new Image();
18 _image.Source = drawingImage;
上面的代码创建一个紫色矩形。如果你不理解它,没关系,因为这部分代码需要删除。
onSizeChange 中调用了 AddAdornment 此方法向编辑器添加一个紫色矩形。
1 public void onSizeChange() 2 {
3 //clear the adornment layer of previous adornments
4 _adornmentLayer.RemoveAllAdornments();
5
6 //Place the image in the top right hand corner of the Viewport
7 Canvas.SetLeft(_image, _view.ViewportRight - 60);
8 Canvas.SetTop(_image, _view.ViewportTop + 30);
9
10 //add the image to the adornment layer and make it relative to the viewport
11 _adornmentLayer.AddAdornment(AdornmentPositioningBehavior.ViewportRelative, null, null, _image, null);
12 }
到目前为止,原理部分已经解释过。信不信由你,你可以通过以下方式修改它 TskComment 我们已经使用这两种方法实现了我们自己的扩展。
添加控件和基本代码
如何实现我想要的功能?
首先,我们的工具需要一个交互式界面,不能简单地使用 Brush 画。因此,需要创建一个新的 WPF 控制(不能 Winform 控件,AddAdornment 只能接受 WPF 元素)。
图14 新建 WPF 用户控件
按照 MVVM 按顺序添加想法 DelegateCommand、ViewModelBase、MainViewModel 这些文件,我们所有的核心逻辑都在于 MainViewModel 中。
图15 新添加的文件
MainWindow.xaml 中的代码如下(省略了一些与逻辑无关的元素)。
1 <UserControl x:Class="TskComment.MainControl"
2 ...
3 d:DesignHeight="41" Width="300">
4
8
MainViewModel 中的代码如下(省略一些不相关的代码),其中 MoveToTopOrBottomOfBlock 和 Execute 由于缺少关键元素,这两种方法的代码都暂时为空。
1 #region Properties and Fields
2
3 private ObservableCollection
10 private BaseComment selectedItem = null;
11 public BaseComment SelectedItem
12 {
13 get { return selectedItem; }
14 set { selectedItem = value; RaisePropertyChanged("SelectedItem"); }
15 }
16
17 public DelegateCommand MoveToTopOfBlockCmd { get; set; }
18 public DelegateCommand MoveToBottomOfBlockCmd { get; set; }
19 public DelegateCommand ExecuteCmd { get; set; }
20
21 #endregion
22
23 #region ctor
24
25 public MainViewModel()
26 {
27 MoveToTopOfBlockCmd = new DelegateCommand((o) => MoveToTopOrBottomOfBlock(true));
28 MoveToBottomOfBlockCmd = new DelegateCommand((o) => MoveToTopOrBottomOfBlock(false));
29 ExecuteCmd = new DelegateCommand((o) => Comment());
30 }
31
32 #endregion
33
34 #region Methods
35
36 private void MoveToTopOrBottomOfBlock(bool up)
37 {
38 //... 缺少关键元素
39 }
40
41 private void Comment()
42 {
43 //... 缺少关键元素
44 }
45
46 #endregion
获取DTE对象
上一节中缺少的关键元素实际上是DTE此对象等效于 Visual Studio 可以通过操作此实例来控制编辑器中的内容(如果想进一步了解,请参考 参考资源[1] )例如,剪切、粘贴、创建新行、跳转到方法主体等。因此,如果要实现我前面提到的工具,则需要依赖此对象。
在宏编辑器中,获取对象很容易,但这里有点麻烦。我们必须依靠 Visual Studio 产品之一(Export) -- SVsServiceProvider。该产品的 GetService 可以获取此对象。
修改 TskCommentFactory 代码如下:
1 internal sealed class TskCommentFactory : IWpfTextViewCreationListener 2 {
3 [Import]
4 internal SVsServiceProvider ServiceProvider = null; //<-- 通过使用此代码,您可以获得 Visual Studio 的产物
5
6 //省略无关代码
7
8 public void TextViewCreated(IWpfTextView textView) 9 {
10 DTE dte = (DTE)ServiceProvider.GetService(typeof(DTE)); //<-- 获取DTE对象
11 new TskComment(textView, dte); //<-- 把dte传给 view
12 }
13
14 }
注:DTE 存在于 EnvDTE.dll 集会 SVsServiceProvider 存在于 Microsoft.VisualStudio.Shell.Immutable.10.0.dll 程序集,您需要先将这些程序集添加到项目中
完整的功能逻辑
现在我们已经获得了关键元素,我们将 MainViewModel 让我们改进代码。
1 private void MoveToTopOrBottomOfBlock(bool up) 2 {
3 if (DTE == null)
4 {
5 return;
6 }
7
8 CodeFunction func = Selection.ActivePoint.CodeElement[vsCMElement.vsCMElementFunction] as CodeFunction; 9
10 if (func != null)
11 {
12 if (up)
13 {
14 Selection.MoveToPoint(func.StartPoint);
15 }
16 else
17 {
18 Selection.MoveToPoint(func.EndPoint);
19 }
20 }
21 }
22
23 private void Comment()
24 {
25 Selection.StartOfLine();
26 Selection.NewLine();
27 Selection.LineUp();
28 Selection.Text = "//TODO:";
29 DTE.ExecuteCommand("Edit.FormatSelection");
30 }
修改 MainWindow 代码允许它接受 DTE。
1 public partial class MainWindow : UserControl 2 { 3 public MainWindow(DTE dte) 4 { 5 InitializeComponent(); 6 ((MainViewModel)this.DataContext).DTE = dte; 7 } 8 }
修改 TskComment.cs 相应部分在
1 private MainWindow _win; 2
3 //... 省略一些代码
4
5 public TskComment(IWpfTextView view,DTE dte) 6 {
7 _win = new MainWindow(dte); 8
9 //...
10 }
11
12 public void onSizeChange()
13 {
14 //...
15
16 Canvas.SetLeft(_win, _view.ViewportRight - 310); //<-- 调整位置
17 Canvas.SetTop(_win, _view.ViewportTop + 90); //<-- 调整位置
18
19 _adornmentLayer.AddAdornment(AdornmentPositioningBehavior.ViewportRelative, null, null, _win, null); //<-- 把 win 把它放在界面上
20 }
哦,现在它可以运行了!
图16 动画演示
添加扩展点以启用注释以支持以后的扩展
上面的代码已经完成,但不幸的是这个评论太不人道了。如果我想添加一个 Phase0 评论或 FixMe 您需要修改代码才能提供注释。因此,这里也遵循 MEF 这个想法是升级代码。
在这里,只会解释和解释密钥代码。其他部分请参考童鞋的源代码。
新建 “协议” 项目
添加一个独立的项目来存储协议接口,并提供一个基于该接口的抽象类。
1 public interface IComment 2 {
3 string Title { get; }
4 string Description { get; }
5 void Execute(DTE dte); // 把DTE提供给第三方,以便他们可以使用它来操纵编辑器
6 }
7
8 public abstract class BaseComment:IComment 9 {
10 public abstract string Title{get;}
11
12 public abstract string Description{get;}
13
14 public abstract void Execute(DTE dte);
15
16 public override string ToString()
17 {
18 return Title;
19 }
20
21 protected TextSelection Selection(DTE dte)
22 {
23 return dte.ActiveDocument.Selection;
24 }
25
26 protected CodeFunction Function(DTE dte)
27 {
28 return Selection(dte).ActivePoint.CodeElement[vsCMElement.vsCMElementFunction] as CodeFunction;
29 }
30 }
新建 收件人
使用协议,我们应该在我们的工具中添加一个接收器来制作它 MEF 帮助我们将第三方产品与收件人相结合。
修改 MainViewModel, 添加收件人,因为可能有多个评论,因此请使用 ImportMany。
[ImportMany(typeof(BaseComment))]
public IEnumerable Comments;
新建 组合引擎
1 private void Init() 2 {
3 //设置目录
4 var catalog = new AggregateCatalog(); 5 catalog.Catalogs.Add(new DirectoryCatalog("D:\plugin\"));
6
7 _container = new CompositionContainer(catalog); 8 try
9 {
10 this._container.ComposeParts(this);
11 }
12 catch (CompositionException compositionException)
13 {
14 Console.WriteLine(compositionException.ToString());
15 }
16
17 //将新注释绑定到集合,以便它可以显示在界面上
18 foreach (BaseComment itm in Comments)
19 {
20 CMTCollection.Add(itm);
21 }
22
23 if (Comments != null && Comments.Count() > 0)
24 {
25 SelectedItem = CMTCollection[0];
26 }
27 }
大获成功。如果你还能跟上我的步伐,恭喜你。你基本上已经掌握了它 MEF 的想法和扩展 Editor 你的能力提高了。
Why VSPackages or MEF ?
换句话说 为什么宏和Add-in不再支持 谷歌肯定有很多答案,但考虑到文章的完整性,我仍然打算做搬运工。
根据Microsoft使用情况跟踪数据已完成,Visual Studio中宏的用户数量少于开发者总数1%。这还不足以使Microsoft放弃此功能,Visual Studio宏功能的高维护成本是另一个原因。与其他功能不同,对宏的支持必须附带Visual Studio每个新版本都会更新,并完成许多令人筋疲力尽的测试。从理论上讲,用户应该能够使用宏IDE录制并播放任何功能,这给微软的维护增加了巨大的负担。 Matt Kaufman说:宏IDE多个版本尚未更新。用户启动后,它很快就会看起来像旧版本Visual Studio。更麻烦的是,它只支持Visual Basic。用户无法使用C#或其他新的.NET语言来创建宏。
微软已经放弃了Visual Studio附加组件是基础设施。根据MSDN上述声明”Visual Studio 2013丢弃的附加组件。开发人员应将附加组件升级到VSPackage展开 后来的文件表明”VSPackage是Visual Studio的主要体系结构单元也是部署、许可和安全的单元。Visual Studio大部分本身都写成VSPackage集合。”
源代码管理
CodePlex: Visual Studio Editor Extension -- TaskComment
TskComment.vsix: 您也可以直接点击 这里 获取此扩展并将其安装在您的 Visual Studio 当中。
参考资源
[2] Managed Extensibility Framework (MEF)
本文来源于: 如何扩展 Visual Studio 编辑器》
作者: stg609
本文版权归作者和博客所有。欢迎转载,但未经作者同意,必须保留此声明,且原文连接必须在文章页面的显著位置提供。否则,保留追究法律责任的权利。
版权声明
所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除