Visual Studio 2010 的代码拼写检查器扩展 (VSX)






4.98/5 (41投票s)
构建一个源代码拼写检查器,

引言
我坚信应用程序的质量取决于是否使用了正确的拼写和语法。有人可能会争辩说这是吹毛求疵,并且效率比这更重要。虽然效率很重要,但必须记住,任何一行代码被阅读的次数都比编写它的次数多。
好的程序由于程序员的疏忽不应包含语言错误。编写的代码需要在各个级别上都是正确的,从设计、名称的正确性和一致性,一直到注释中的标点符号。
并非每个人都以英语为母语,包括我自己,而且还有一些有特定障碍的人,例如阅读或书写标准英语有困难的阅读障碍者。这并不是说这些人不能编写出好的应用程序。我想说的是,编写的代码应该传达作者的意图。
为了实现这一点,我认为编写一个 Visual Studio 2010 的拼写检查扩展会很有趣,它具有以下功能:
- 检查(XML)注释中的单词拼写
- 使用 MS.NET 命名约定检查声明中的名称拼写
- 检查字符串字面量中的单词拼写
- 用红色波浪线标记拼写错误的单词
- 为拼写错误的单词提供建议列表
- 拼写错误的单词可以添加到自定义词典中
- 自定义词典保存在源代码树中。这样可以方便地进行源代码控制,并在团队合作的情况下在团队之间同步。
第一步:在文本上添加红色波浪线
事实证明这非常容易。首先使用项目模板来熟悉 MEF (Managed Extension Framework) 的一些基本属性。
Visual Studio 2010 有一个称为“标记”的机制,订阅者可以在其中向文本块添加元数据。重要的概念包括:
Span
,本质上是文本内的坐标(起始点 + 长度)ITagger
,它告诉 IDE 在哪里放置标记ITag
,它告诉 IDE 渲染什么(IErrorTag
被渲染为红色波浪线)TagSpan
,Tag
和Span
的组合,即“在哪里”和“什么”
interface ITagger<IErrorTag>
有一个名为 GetTags
的方法,该方法接受一个 span
集合,并必须返回一个 TagSpan
对象枚举。提供的 span
集合标记了 IDE 感兴趣的区域。这可能是因为用户已更改或以其他方式触发了视图中的更改,因此需要重新标记。
下面的示例代码展示了此方法的实现,该方法在每个“A
”下添加一个波浪线。这些波浪线还将“C
”显示为伴随的工具提示。
public IEnumerable<ITagSpan<IErrorTag>> GetTags (NormalizedSnapshotSpanCollection spans)
{
foreach (var span in spans)
{
var text = span.Snapshot.GetText (span);
for (int i = 0; i < text.Length; i++)
{
if (text[i] == 'A')
{
yield return new TagSpan<ErrorTag>
(
new SnapshotSpan
(
span.Snapshot,
span.Start + i,
1
),
new ErrorTag ("B", "C")
);
}
}
}
}
第二步:为文本块添加上下文菜单标记(也称为智能标签)
这同样非常容易。通过使用相同的 ITagger
接口来实现,这次使用 SmartTag
类作为类型参数。SmartTag
是动作(分组在集合中)的占位符。用户会通过一个小的蓝色矩形提示其存在。当用户将鼠标悬停在矩形上时,它会显示一个包含可用动作的下拉列表。
ISmartTagAction
接口有两个重要成员:
DisplayText
属性,显示在下拉列表中的用户Invoke
方法,提供实际实现
第三步:检查拼写
我记得 WPF 的一个功能就是拼写检查,于是决定研究一下这是否足够。尽管在 WPF 4.0 中增加了对自定义词典的支持,但仅限于英语、法语、德语和西班牙语。这不是问题,因为 Microsoft .NET 命名指南规定标识符应以英语声明。
然而,实际实现被隐藏了(标记为内部),我可以通过反射来 hack 它,或者在底层使用实际的 WPF 文本框。**提示**:当您使用 WPF 文本框时,如果用户使用的输入语言区域与前面提到的语言(+方言)不同,拼写检查器会自动禁用。您可以使用附加属性“xml:lang
”和 ISO 3166 代码,例如“en-us
”或“en-gb
”,将此用户设置覆盖为特定语言。
我在这两种策略上都挣扎了一段时间,最终得出结论,它们都缺乏我所寻找的东西……因此,我决定寻找替代方案,并偶然发现了“Hunspell
”,这是一个开源的拼写检查库,目前被 Open Office、Firefox 等使用。它附带了大量的词典,并且还具有同义词、连字符和语法功能。
不幸的是,它没有作为 COM 暴露,所以无法进行标准的 PInvoke。使用一些老式的 *kernel32.dll* 技巧,我们可以加载和卸载该库。但真正的魔法在于 GetProcAddress
结合 Marshal.GetDelegateForFunctionPointer
允许您创建一个可以调用该库的委托。(更多详细信息,请查看 Hunspell
命名空间。)
第四步:获取需要检查拼写的代码片段
Roman Golovin 和 Noah Richards 创建的拼写检查器使用了一种有趣的技术。这个拼写检查器是/将是 VS SDK 的一部分,并使用一个称为分类器的服务。语言服务使用此机制根据文本的上下文和通用含义对其进行分类。我猜测这被用于语法着色过程。
不幸的是,它在声明名称与使用名称之间没有提供任何细节。因此,我决定自己构建 C# 扫描器和简单的解析器(因为这是我选择的语言)。尽管扫描器和词法标记已完成,但解析器的目的是仅正确识别所有不同类型的名称声明。
屏幕截图
从这个截图可以看出,“misspellled”(有 3 个 L)和 Namespace 都被标记了。它还显示了命名空间、别名和委托(带参数和默认值)的声明。

通过将鼠标悬停在智能标签指示器(蓝色矩形)上,下拉菜单将变得可见。

添加到自定义词典的第一个单词会导致 CustomWords.txt 文件被添加到解决方案的“Solution items”标准文件夹中。

带属性和注释的枚举

带各种成员的接口

带各种成员的类

已知bug
- 未处理 C# 预编译器指令(已修复)
未来版本
- 包含单元测试
- 添加 MSBuild 任务
- 添加一个 VS 命令,用于扫描解决方案中的所有文件,类似于查找/替换方法
- 添加 (T) SQL 语言支持
- 添加 VB.NET 语言支持
- 添加 F# 语言支持
- 使用托管拼写检查器,也许自己写一个玩
- 建议或错误?