使用ICSharpCode.TextEditor制作一个语法高亮显示的XML编辑器

使用ICSharpCode.TextEditor制作一个语法高亮显示的XML编辑器

品高工作流 的流程模拟器中使用了一个具有语法高亮和折叠功能的XML编辑器,其核心就是用了SharpDevelop中的ICSharpCode.TextEditor控件,如下图所示:

 

 

 

 SharpDevelop的源代码中有一个功能丰富,支持语法高亮,代码折叠的文本编辑器控件(位置:\SharpDevelop\src\Libraries\ICSharpCode.TextEditor)。本文附件的Demo工程中将演示如何加载和保存文件,查找和替换文本中的字符串,执行剪贴板操作,使用TextMarkers来高亮突出显示字符串,使用FoldingStrategy让文本可以折叠,支持使用书签,并通过改变一些显示属性设置更方便我们进行文本编辑。

 希望本文有足够的信息来帮助你知道如何编写文本编辑器中其他你想要的功能。

 

 

 

 

 

 

 

 

 

 

 

 

 

一、ICSharpCode.TextEditor的设计

 

 

该文本编辑器实际上包含三个彼此紧密结合的内部控件:

  • 在最顶层是TextEditorControl,它包含一个或者两个TextAreaControl。当分割窗口时它有两个TextEditorControl,如上面的截图所示。
  • TextAreaControl封装了水平和垂直滚动条以及一个TextArea 。
  • TextArea是真正获得焦点之后的控件。它用于绘制文本以及处理键盘输入。

 

比这些控件类都重要的是IDocument接口。DefaultDocument类实现了IDocument接口,该接口提供了访问SharpDevelop文本编辑器的大多数特性:undo/redo,标记(Markers),书签,代码折叠,自动缩进,语法高亮显示,属性设置以及管理文本缓冲区。

 

以下是这些基本类的类图:

 

 

二、详情

 

下图是一个更完整的类图,其中包括通过TextArea和IDocument的属性所能访问的其他的类和接口。文本编辑器的语法高亮和代码折叠被划分为单独的类或接口,如果你需要,它们中的大部分可以被自定义或派生版本的类或者接口所取代。

TextEditor(和SharpDevelop一样)经常使用策略模式,所以你会在很多地方看到Strategy。在策略模式中,一个接口的定义是根据功能需要,但并不实现这些功能。如果你需要不同的功能实现,你可以写一个实现了IDocument接口的新策略。由于某些原因,MarkerStrategy是一个sealed接口类,没有实现相应的接口,因此它不能被替换掉。

 

 

  

让我们来看看IDocument的特性:

 

1.  该编辑器自动提供无限次的undo/redo。你不需要做任何特别的编程去实现undo功能;只是一定要使用IDocument接口中的方法,而不是使用ITextBufferStrategy中的方法 (后者绕过undo堆栈)。你可以将多个文本操作合在一起,将它们包含在IDocument.UndoStack.StartUndoGroup()和IDocument.UndoStack.EndUndoGroup()之间,这样就可以通过一个undo命令来撤销这些操作。

 

2.  标记(Markers)(TextMarker类的实例)表示一段文本的范围(包括开始和结束位置)。在通过文档的MarkerStrategy注册一个标记之后,当文档中的文本被修改之后,这个注册标记的开始和结束位置会自动移动。标记(Markers)可以是可见或不可见的;若可见,一个标记可以显示文本的拼写检查风格的波浪型下划线,或者高亮显示一个标记所覆盖的区域。本文的Demo应用程序使用标记来实现查找功能中的“查找全部并高亮显示”命令。

奇怪的是,还有一个类也可以达到类似的目的: TextAnchor可以锚到一个点,并根据文档的更改而自动移动,但是你却不能使用这个类,因为它的构造函数是internal的。

 

3.  书签是显示在编辑器左边图标栏的矩形标记,用户可以通过按F2键在矩形标记中转换。本文Demo工程中实现了切换书签以及在它们之间前后移动的功能。

 

4.  代码折叠允许文本块被收起来。ISharpCode.TextEditor中没有内建的代码折叠策略,所以你如果需要制作一个支持代码折叠的文本编辑器,可以考虑研究SharpDevelop的源代码来实现一个代码折叠策略类。在本文的Demo中,使用了SharpDevelop源代码中的XmlFoldingStrategy类用来支持XML文本的折叠。DefaultDocument和TextEditorControl并不会自动更新代码折叠标记,所以在本文的Demo中,折叠会在程序初始化的时候以及在文件加载后更新。

 

在代码折叠的显示中,有两种类型的行号。

²  逻辑行号是指是显示在左边图标栏中的真实行号。

²  可见行号则是应用代码折叠后所显示的行号。可见行号本身的号码来源于逻辑行号。

 

5.  自动缩进由继承了IFormattingStrategy接口的类来提供支持,当用户有输入时,自动缩进会自动格式化编辑器中的文本。DefaultFormattingStrategy实现了当Enter键被按下时根据上一行来自动缩进的简单功能。在 SharpDevelop的源代码中你可以找到更多的缩进策略。

IFormattingStrategy接口中定义了包括向后搜索或者向前搜索匹配条件文本的方法以使结果能够高亮显示,实现该接口的类有:TextUtilities,BracketHighlightingSheme,BracketHighlight和TextArea。TextArea中通过硬编码来实现 ()、[]和{}等括号的自动缩进。

 

6.  语法高亮通常是由一个DefaultHighlightingStrategy类的实例来提供支持,它使用以”xshd”为扩展名的 XML文件来高亮显示各种文本。有超过十个这样的XML文件作为资源嵌入在文本编辑器DLL中,当加载一个文件时,TextEditorControl类会根据文件的扩展名来自动选择一个高亮规则文件。修改编辑器中的文本文件的名称并不会改变之前选择的高亮规则,所以本文Demo中的大部分都是手动设置语法高亮规则的。网上有很多关于如何添加基于XSHD高亮规则的文章,如:这里 和 这里

 

7.  文本缓冲区策略用来管理文本缓冲区。默认的GapTextBufferStrategy类的算法在维基百科上有描述以及在CodeProject上也有。

 

8.  ITextEditorProperties接口中封装了多个属性设置选项,如:是否显示行号和制表符显示几个字符宽等属性。

ITextEditorProperties没有任何方式去通知其他的对象其属性已被更改。如果你更改了其中的一个属性,将直接影响到编辑器控件的显示,编辑器控件也不会自动重新绘制。由于这样,TextEditorControlBase将ITextEditorProperties的每个需要监管的属性都进行了封装。例如:TextEditorControlBase.TabIndent是包装了ITextEditorProperties.TabIndent。顺便说一下,你可以共享一个ITextEditorProperties实例对象给许多文本编辑器控件使用。

 

除了这些,在ICSharpCode.TextEditor项目还包括被称为"智能感知"的相关代码:用于在编辑器内部弹出一个列表框用来显示标识符和已有的方法。

 

ICSharpCode.TextEditor本身并没有实际执行智能感知代码,但它包含支持这种功能的图形界面的代码。但是,这些代码不能直接被文本编辑器使用,本文只是一个XML编辑器,并不涉及代码自动完成。

 

文本编辑器中源代码中包含了许多的类;一些类可能不适合显示在类图之上,在本文中并没有涉及到它们。比如说TextWord,用作语法高亮显示的基本单元;LineManager则是被DefaultDocument类用来转换"offsets" 为"positions";还有TextUtilities,一个静态的方法集合类。

  

三、更多细节:

 

•    文档的位置可以用两种方式表示。首先,一个文档的位置可以用行列方式来表示,在TextLocation结构中可以找到它们。其实一个文档就是一个字符数组,它的长度为IDocument.TextLength。该字符数组的索引叫做偏移量(offset 类型:int)。偏移量在ICSharpCode.TextEditor中被普遍使用,但有一些类(例如SelectionManager)则使用Location来表示文档的位置,例如TextLocation。你可以使用IDocument.OffsetToPosition和IDocument.PositionToOffset将Location和Offset这两种方式互相转换。

 

•    Caret表示闪烁的光标。你可以移动光标改变Caret的Line ,Column ,或Position的属性。

 

•    所有文本编辑器的动作Action,都可以在SharpDevelop中通过组合键来调用,它们都被封装在继承了ICSharpCode.TextEditor.Actions.IEditAction接口的类中 。其中的一部分Action在本文的Demo工程的编辑菜单栏中会被使用到。

 

•    在TextArea左边显示了三个边空,上面的类图中并没有它们。它们不是单独的控件,但TextArea通过鼠标和绘制来控制它们。

²  FoldMargin显示代码折叠的+或者-符号的图标,如果你不使用代码折叠,恐怕是没有办法来它(当然,你可以更改源代码了)。

²  IconBarMargin显示图标,如书签(或SharpDevelop中断点)。它的可见性是受ITextEditorProperties.IsIconBarVisible控制。

²  GutterMargin显示行号。可见性是受ITextEditorProperties.ShowLineNumbers控制。

 

•    ICSharpCode.TextEditor中最重量级的部分就是它的语法高亮,使用了大约正在编辑文本文件10倍大小的内存。它绘制这些文本使用了大量的CPU资源以及在内存中分配了大量的临时对象。

 

•    更多细节请自行研究ICSharpCode.TextEditor的源代码,本文的附件Demo中将提供一些最基本的功能实现。

  

 

•   本文大部分代码及文章翻译来源: CodeProject: Using ICSharpCode.TextEditor 特别感谢原作者:Qwertie

  

•    本文的Demo源代码:Bingosoft.Workflow.XmlPad.rar

posted @ 2017-04-06 18:09  jiftle  阅读(546)  评论(0编辑  收藏  举报