我有完全相同的问题,现在已经浏览了几个小时的网络,直到我能够理解并解释你需要如何开始使用这样的扩展。
在下面的示例中,我们将创建一个小而笨拙的扩展,当进行编辑时,它总是会在代码文件的开头添加“Hello”。这是非常基本的,但应该让你知道如何继续开发这个东西。
请注意:您必须完全自行解析代码文件 - Visual Studio 不会为您提供任何关于类、方法或其他内容的位置以及它们包含的内容的信息。这是在使用代码格式化工具时要采取的最大障碍,并且不会在此答案中涵盖。[*]
对于那些跳过此答案的人,请确保您首先下载并安装了 Visual Studio SDK,否则您将找不到第一步中提到的项目类型。
创建项目
首先创建一个类型为“Visual C# > 可扩展性 > VSIX 项目”的新项目(仅当您选择 .NET Framework 4 作为目标框架时才可见)。 请注意,您可能必须选择“编辑器分类器”项目类型而不是“VSIX 项目”类型才能使其工作,s。在下面发表评论。
项目创建后,“source.extension.vsixmanifest”文件将打开,您可以设置产品名称、作者、版本、描述、图标等。我认为这一步非常不言自明,您可以现在关闭选项卡,稍后通过打开 vsixmanifest 文件将其恢复。
创建一个侦听器类以获取有关文本编辑器实例创建的通知
接下来,我们需要在 Visual Studio 中创建文本编辑器时进行监听,并将我们的代码格式化工具绑定到它。 VS2010 中的文本编辑器是IWpfTextView 的一个实例。
向我们的项目添加一个新类并将其命名为TextViewCreationListener。这个类必须实现Microsoft.VisualStudio.Text.Editor.IWpfTextViewCreationListener 接口。您需要在项目中添加对 Microsoft.VisualStudio.Text.UI.Wpf 的引用。程序集 DLL 位于 VisualStudioIntegration\Common\Assemblies\v4.0 下的 Visual Studio SDK 目录中。
你必须实现接口的TextViewCreated方法。此方法有一个参数指定已创建的文本编辑器的实例。我们将创建一个新的代码格式化类,稍后将此实例传递给该类。
我们需要通过指定属性[Export(typeof(IWpfTextViewCreationListener))] 使TextViewCreationListener 类对Visual Studio 可见。将 System.ComponentModel.Composition 的引用添加到您的项目中以获取 Export 属性。
此外,我们需要指定代码格式化程序应与哪些类型的文件绑定到文本编辑器。我们只喜欢格式化代码文件而不是纯文本文件,所以我们将属性[ContentType("code")] 添加到监听器类中。为此,您必须在项目中添加对 Microsoft.VisualStudio.CoreUtility 的引用。
此外,我们只想更改可编辑代码,而不是更改其周围的颜色或装饰(如示例项目中所示),因此我们将属性 [TextViewRole(PredefinedTextViewRoles.Editable)] 添加到类中。您再次需要一个新的参考,这次是 Microsoft.VisualStudio.Text.UI。
-
将该类标记为内部密封。至少这是我的建议。现在你的类应该看起来像这样:
[ContentType("code")]
[Export(typeof(IWpfTextViewCreationListener))]
[TextViewRole(PredefinedTextViewRoles.Editable)]
internal sealed class TextViewCreationListener : IWpfTextViewCreationListener
{
public void TextViewCreated(IWpfTextView textView)
{
}
}
为代码格式化创建一个类
接下来,我们需要一个类来处理代码格式化逻辑、排序方法等。同样,在此示例中,只要进行了编辑,它就会简单地将“Hello”添加到文件的开头。
向您的项目添加一个名为 Formatter 的新类。
添加一个接受一个IWpfTextView 参数的构造函数。请记住,我们希望在侦听器类的 TextViewCreated 方法中将创建的编辑器实例传递给这个格式化类(只需将 new Formatter(textView); 添加到那里的方法中)。
-
将传递的实例保存在成员变量中。稍后格式化代码时它会变得很方便(例如,用于检索插入符号的位置)。还要绑定编辑器实例的TextBuffer 属性的Changed 和PostChanged 事件:
public Formatter(IWpfTextView view)
{
_view = view;
_view.TextBuffer.Changed += new EventHandler<TextContentChangedEventArgs>(TextBuffer_Changed);
_view.TextBuffer.PostChanged += new EventHandler(TextBuffer_PostChanged);
}
-
每次进行编辑(例如输入字符、粘贴代码或程序更改)时都会调用Changed 事件。因为它也会对程序更改做出反应,所以我使用 bool 来确定我们的扩展程序或用户/其他任何东西是否正在更改代码,并且只有在我们的扩展程序尚未编辑时才调用我的自定义 FormatCode() 方法。否则,您将递归调用此方法,这会导致 Visual Studio 崩溃:
private void TextBuffer_Changed(object sender, TextContentChangedEventArgs e)
{
if (!_isChangingText)
{
_isChangingText = true;
FormatCode(e);
}
}
我们必须将PostChanged事件处理程序中的这个布尔成员变量再次重置为false。
-
让我们将Changed 事件的事件参数传递给我们的自定义FormatCode 方法,因为它们包含上次编辑和现在之间发生的变化。这些编辑存储在INormalizedTextChangeCollection 类型的数组e.Changes 中(我的帖子末尾的链接以获取有关此类型的更多信息)。我们遍历所有这些编辑,并使用此编辑生成的新文本调用我们的自定义 HandleChange 方法。
private void FormatCode(TextContentChangedEventArgs e)
{
if (e.Changes != null)
{
for (int i = 0; i < e.Changes.Count; i++)
{
HandleChange(e.Changes[0].NewText);
}
}
}
-
在HandleChange 方法中,我们实际上可以扫描关键字以以特定方式处理这些关键字(请记住,您必须自己解析任何代码!) - 但这里我们只是在开头添加“Hello”用于测试目的的文件。例如。我们必须更改编辑器实例的TextBuffer。为此,我们需要创建一个ITextEdit 对象,我们可以使用它来操作文本并在之后应用它的更改。代码非常不言自明:
private void HandleChange(string newText)
{
ITextEdit edit = _view.TextBuffer.CreateEdit();
edit.Insert(0, "Hello");
edit.Apply();
}
编译此加载项时,Visual Studio 的实验性配置单元启动时仅加载了我们的扩展。创建一个新的 C# 文件并开始输入以查看结果。
我希望这能给您一些关于如何继续这个主题的想法。我现在必须自己去探索。
我强烈推荐 MSDN 上有关编辑器文本模型的文档,以获取有关如何执行此操作的提示。
http://msdn.microsoft.com/en-us/library/dd885240.aspx#textmodel
脚注
[*] 请注意,Visual Studio 2015 或更新版本带有 Rosyln 编译器平台,它确实已经为您分析了 C# 和 VB.NET 文件(可能还有其他预安装的语言)并公开了它们的层次语法结构,但是我不是该主题的专家,尚未就如何使用这些新服务给出答案。无论如何,启动编辑器扩展的基本进度与此答案中描述的相同。请注意 - 如果您使用这些服务 - 您将依赖于 Visual Studio 2015+,并且该扩展在早期版本中将不起作用。