65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 ICSharpCode.TextEditor

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.82/5 (58投票s)

2008年11月12日

MIT

8分钟阅读

viewsIcon

407096

downloadIcon

23601

使用 TextEditorControl 在您的应用程序中添加一个支持语法高亮的编辑器。

  • 下载源代码 - 396 KB
  • 注意:ICSharpCode.TextEditor 和示例应用程序需要 C# 3.0 编译器,但它们配置为目标 .NET Framework 2.0。通常,我们从 SharpDevelop 源代码中获取 ICSharpCode.TextEditor,但示例项目本身包含了最新版本 (3.0.0.3437)。

texteditor.png

引言

SharpDevelop 拥有一个功能丰富(尽管文档不那么丰富)的文本编辑器控件。本文附带的演示展示了如何使用它来加载和保存文件,在文档中查找和替换字符串,执行剪贴板操作,使用 TextMarkers 来高亮显示字符串,使用 FoldingStrategy 允许用户折叠文档区域,使用书签,以及更改一些显示设置。

希望本文的剩余部分能提供足够的信息,指引您进行文本编辑器行为的任何定制。

ICSharpCode.TextEditor 的设计

文本编辑器实际上包含三个紧密耦合的嵌套控件

  • 顶层是 TextEditorControl,它包含一个或两个 TextAreaControl。当“分割”时,它有两个 TextEditorControl,如截图所示。
  • TextAreaControl 封装了水平和垂直滚动条以及一个 TextArea
  • TextArea 是实际获得焦点的控件。它负责绘制文本并处理键盘输入。

如果有什么比控件类更重要,那就是 IDocument 接口。IDocument 接口,由 DefaultDocument 类实现,是提供对 SharpDevelop 文本编辑器大多数功能访问的中心:撤销/重做,标记,书签,代码折叠,自动缩进,语法高亮,设置,以及最重要的,文本缓冲区管理!

这些基本类的图示如下

classes1.png

注意:在 Visual Studio 类图上,我无法显示一个类到它实现的接口的派生箭头,所以我用一个“接口棒棒糖”指向 DefaultDocument 的接口。

详细说明

下面是一个更完整的图,包括可以通过 TextAreaIDocument 的属性访问的其他类和接口。文本编辑器的功能,如代码折叠和语法高亮,被整齐地划分到单独的类或接口中,其中大多数可以被替换为自定义或派生版本,如果您需要的话。

TextEditor(以及 SharpDevelop,总的来说)经常使用“策略”模式,所以您会经常看到这个词。在策略模式中,一个接口定义了所需的功能,但没有定义如何实现它。如果您需要不同的实现,可以编写自己的实现,并在 IDocument 中调用适当的 setter 来更改策略。至少理论上是这样。由于某些原因,MarkerStrategy 是一个 sealed 类,没有对应的接口,因此无法替换。

让我们来谈谈从 IDocument 分支出来的功能。

  • 文档自动提供无限的撤销/重做功能。您无需做任何特殊操作即可确保程序化更改可以被撤销;只需确保使用 IDocument 中的方法修改文档,而不是使用 ITextBufferStrategy 中的方法(后者会绕过撤销堆栈)。您可以通过将组放在匹配的 IDocument.UndoStack.StartUndoGroup()IDocument.UndoStack.EndUndoGroup() 调用之间,将多个操作组合在一起,以便一个“撤销”命令可以撤销它们所有。
  • 标记(TextMarker 类的实例)是文本范围(具有起始和结束位置)。在将标记注册到文档的 MarkerStrategy 后,当文档被修改时,标记的起始和结束点会自动移动。标记可以是可见的或不可见的;如果可见,标记可以用来下划线文本(带有拼写检查器式的波浪线),或者覆盖其覆盖区域的语法高亮。示例应用程序使用标记来实现其“高亮全部”命令。
  • 奇怪的是,还有一个类具有类似的功能:TextAnchor 锚定到单个点,并在文档更改时自动移动,但您不能使用此类,因为它的构造函数是 internal 的。

  • 书签是显示在“图标栏”边距中的矩形标记,用户可以通过按 F2 跳转到这些书签。示例项目演示了如何切换书签和在它们之间移动。
  • 代码折叠允许折叠文本块。ISharpCode.TextEditor 没有(可用的)代码折叠策略,因此如果您想创建一个具有代码折叠的编辑器,可以参考 SharpDevelop 的源代码。在演示中,我实现了一个简单的折叠策略,只支持 #region/#endregion 块。DefaultDocumentTextEditorControl 不会自动更新代码折叠标记,因此在演示中,折叠仅在文件首次加载时计算。
  • 在代码折叠存在的情况下,有两种行号。

    • “逻辑”行号是显示在边距中的“真实”行号。
    • “可见”行号是应用折叠后的行号。术语“行号”本身通常指的是逻辑行号。
  • 自动缩进以及在用户键入时对文档进行格式化的相关功能,旨在通过实现 IFormattingStrategy 来提供。DefaultFormattingStrategy 在按 Enter 键时简单地匹配前一行的缩进。同样,可以在 SharpDevelop 的源代码中找到更复杂的策略。
  • IFormattingStrategy 还包含向后或向前搜索匹配括号的方法,以便它们可以被高亮显示,但这只是高亮匹配括号机制的一部分,该机制的实现跨越了多个类,包括 TextUtilitiesBracketHighlightingShemeBracketHighlightTextArea。总之,似乎 TextArea 是硬编码为仅提供 ()[]{} 的括号匹配。

  • 语法高亮通常由 DefaultHighlightingStrategy 实例提供,该实例根据具有“xshd”扩展名的 XML 文件来高亮显示文件。文本编辑器 DLL 中内置了十几个这样的文件作为资源,TextEditorControl 在加载文件时会根据文件的扩展名自动选择一个高亮器。它不会在文件名更改时更改高亮器;因此,演示的 DoSaveAs 方法使用 HighlightingStrategyFactory 来获取相应策略。网上有一些关于添加更多基于 XSHD 的高亮器的文章,例如这篇文章这篇文章
  • 文本缓冲区策略管理文本缓冲区。默认的 GapTextBufferStrategy 背后的算法在 Wikipedia在 CodeProject 上有描述。
  • ITextEditorProperties 封装了各种选项,例如是否显示行号以及制表符的宽度。
  • ITextEditorProperties 无法通知任何其他对象其属性已更改。如果您直接更改这些属性之一,并且它影响了控件的外观,控件不会自动重绘。因此,TextEditorControlBaseITextEditorProperties 中它需要监视的每个属性都提供了一个包装器。例如,TextEditorControlBase.TabIndentITextEditorProperties.TabIndent 的一个包装器。顺便说一句,您可以共享一个 ITextEditorProperties 对象给多个文本编辑器,我在演示中就是这样做的。

除此之外,ICSharpCode.TextEditor 项目还包含一些与通常称为“智能感知”相关的代码:一个“提示窗口”(一个类似工具提示的窗口,通常用于显示方法签名)和一个“代码补全”列表。

ICSharpCode.TextEditor 本身实际上不执行智能感知,但它包含一些用于这些功能的 GUI 的代码。然而,这段代码不被文本编辑器直接使用,而且我的演示也没有展示它(事实上,我也不知道如何使用它)。

文本编辑器库非常庞大;图上还有一些其他杂项类未能包含,我也没有时间在这篇文章中描述它们。其中值得注意的包括 TextWord,它是语法高亮的基本单位;LineManagerDefaultDocument 用它来转换“偏移量”为“位置”;以及 TextUtilities,一个静态方法集合。

这里有一些额外的提示

  • 文档中的一个位置可以通过两种方式表示。首先,一个位置可以表示为行-列对,它们被打包在 TextLocation 结构中。更根本地说,您可以将文档视为一个字符数组,其长度为 IDocument.TextLength。这个数组的索引称为“偏移量”(类型:int)。偏移量表示似乎更常见,但有些代码(例如 SelectionManager)要求位置以 TextLocation 的形式提供。您可以使用 IDocument.OffsetToPositionIDocument.PositionToOffset 在这两种表示之间进行转换。
  • Caret”是闪烁的光标。您可以通过更改 CaretLineColumnPosition 属性来移动光标。
  • 在 SharpDevelop 中可以使用键盘组合键调用的所有文本编辑器操作都被封装在 ICSharpCode.TextEditor.Actions.IEditAction 的实现中。示例应用程序的“编辑”菜单处理程序演示了一些这些操作。
  • TextArea 的左侧显示多达三个边距,由上面图表中未显示的三个类表示。它们不是独立的控件,而是 TextArea 将鼠标和绘制命令传递给它们。
    • FoldMargin 显示用于折叠或展开区域的小的 + 和 - 图标。如果您不使用代码折叠,恐怕没有办法隐藏该边距(好吧,您可以修改源代码)。
    • IconBarMargin 显示书签(或 SharpDevelop 中的断点)等图标。可见性由 ITextEditorProperties.IsIconBarVisible 控制。
    • GutterMargin 显示行号。可见性由 ITextEditorProperties.ShowLineNumbers 控制。
  • 文档对使用它的控件没有引用,所以我假设我们可以将同一个文档用于多个控件,管理一个没有控件的文档,或者编写一个新的控件实现。编辑器控件通过订阅文档的事件来获得文档更改的通知。
  • ICSharpCode.TextEditor 中最耗费资源的部分是其语法高亮,它可能消耗的内存是正在编辑的文本文件大小的十倍。绘制这些文本的代码会消耗大量 CPU 资源并分配大量的临时对象。
  • 我不是真正的专家;我只学到了足够关于 ICSharpCode.TextEditor 的知识来写这篇文章!玩得开心!

历史

  • 2008 年 11 月 13 日 - 首次发布。
  • 注意:尽管我的示例是在 MIT 许可下提供的,但 ICSharpCode.TextEditor 本身是根据 GNU LGPL 条款提供的。

© . All rights reserved.