study log

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  5 随笔 :: 6 文章 :: 123 评论 :: 2 引用

SharpDevelop浅析_3_Internationalization & TextEditor

国际化、文档编辑器、语法高亮显示……



1、Demo界面及功能解释
2、SharpDevelop的Internationalization的使用
3、SharpDevelop的Internationalization的实现分析
4、SharpDevelop的文档管理的基本概念
5、SharpDevelop的SyntaxHighlighting配置文件的定义
6、SharpDevelop的TextEditor控件的实现概述
7、待分析的部分
8、总结
Demo下载

1、Demo界面及功能解释
启动后,打开文档(默认支持.cs, .js, .java, .aspx等类型文件的语法高亮显示,详见ICSharpCode.TextEditor\Resources\SyntaxModes.xml)、切换语言界面如下:


切换为中文语言环境后的界面如下:


功能说明:可以实时改变语言环境;提供对常用编程语言的编辑:支持语法高亮显示、括号匹配、设置书签;尚未提供查找/替换、代码折叠、代码提示/自动完成等功能。

2、SharpDevelop的Internationalization的使用
多语言的实现就是在显示时根据键获取相应语言环境下的键值(Dictionary<key,value>),因此在编写程序时应该引用键,而非直接书写要显示的字符。一般地,应用程序的显示包括菜单、状态栏、提示字符等,因此Demo中的主菜单在配置文件(参见Basic.addin)中使用label = "${res:Menu.File.Open}",退出应用程序的提示字符使用string s = StringParser.Parse("${res:Info.Exit}");。ICSharpCode.Core.dll支持语言环境的实时修改(修改后语言设置不需重启应用程序),因此要在应用程序订阅语言环境改变的事件,相关代码如下:

语言环境的修改及事件响应


另外,可以看到Demo中的选项命令窗口也采用了插件模式来构造窗体(实现细节就不多谈了),容器窗体是TreeViewOption,插件窗体如:SelectCulturePanel, SelectStylePanel, DemoNothing,真是“扩展--无处不在”呀。SharpDevelop源码中的这些窗体的成员控件是通过.xfrm配置文件配置,窗体继承自XmlUserControl来根据配置文件生成控件,有一定的灵活性。
新的语言包资源文件放在\data\resources\目录下(如StringResources.cn-gb.resources),注意应用程序默认语言选项以及在未找到相关语言资源时都是引用Entry中的myRes.resx资源文件;语言声明文件是\data\resources\languages\LanguageDefinition.xml,其格式如下:
LanguageDefinition.xml

SharpPad中动态显示可选语言项的分析类是LanguageService.cs和Language.cs,此处就不多解释了。

3、SharpDevelop的Internationalization的实现分析
SharpDevelop的多语言支持的键值对是通过本地资源文件存储的,当Demo中更改语言环境设置时,引发ICSharpCode.Core.dll中的事件及方法顺序如下:
PropertyService类的属性更新引发PropertyChanged事件  ->  ResourceService响应接收到的事件并重新加载保存在内存中的资源键值对,然后引发LanguageChanged事件(由使用端接收并作相关处理,如Demo中的SharpPad.cs中的事件订阅/处理)属性更新的相关代码如下:
属性更改

资源服务类的事件响应代码如下:
ResourceService类的事件响应


4、SharpDevelop的文档管理的基本概念
代码编写工具的核心是代码编辑窗口(如打开一个.cs文件的窗口),如何在应用程序中存储该窗口内的字符内容?有些文件可能有上千行,统计下来可能有过万个字符,如果使用string对象存储字符内容,显然是不能满足性能要求,而且要实现语法高亮显示的话,还需要区分存储TextWord和相应颜色……
先看一下字符管理的基本要求:
ITextBufferStrategy接口

SharpDevelop采用的策略是使用带Gap的字符,Gap的长度小于预定规格时,按约定增加一定长度(有些类似于SqlServer的表空间管理?),插入/修改/删除字符时只是修改Gap的长度和位置和移动部分字符,核心函数如下:
GapTextBufferStrategy类

有了基本的数据容器,核心问题已经解决了,但是在绘制界面时直接使用上面的类,显然不够方便,于是就定义了LineSegment和TextWord类来分别存储行、单词,注意这两个类存储的只是int类型的offset和length信息,而不存储字符或字符串对象。注意TextWord类中有个HighlightColor类型的变量用以存储语法高亮显示信息。
有了上面的这些类,便可以组合起来补充些其它信息对外提供服务了,IDocument接口存在的目的即在于此,它封装了ITextEditorProperties(是否显示空格/Tab/Eol/HRuler等)、UndoStack、ITextBufferStrategy、IHighlightingStrategy、FoldingManager、BookmarkManager等属性对象。

5、SharpDevelop的SyntaxHighlighting配置文件的定义
是时候对语法高亮显示作一些分析了,此处不详述其实现,而重点分析其配置定义,从中亦可以猜出部分实现。
相关配置文件均保存在ICSharpCode.TextEditor项目\Resource\目录下
首先SyntaxMode.xml文件中显示了已定义的文件类型及其声明文件位置,其解析类参见\src\Document\HighlightingStrategy\SyntaxModes\FileSyntaxModeProvider.cs

SyntaxMode.xml

取CSharp-Mode.xshd(注:xshd是Xml Syntax Highlighting Definition的缩写)为例,查看其定义:

CSharp-Mode.xshd

上面的文件公摘选了部分标签,其中
<Property>标签定义了指定名称属性的指定值
<Digits>标签定义了数值的字体显示样式
<Delimiters>定义了分隔单词的字符
<Span>定义了包含在此指定Begin/End中的字符的显示样式,注意没有<End>标签的一般设stopateol(stop at end-of-line)为true
<KeyWords>指定了其子集<Key>声明的单词的显示样式

6、SharpDevelop的TextEditor控件的实现概述
接下来的任务是要显示和支持用户输入了,直接使用TextBox或RichTextBox好像都不太现实,效率上也必定有不少损失,于是SharpDevelop的方式是直接继承自Control, Panel, UserControl 的方式来实现编辑控件(参见ICSharpCode.TextEditor项目\src\Gui\...)。
首先使用TextEditorControlBase(继承自UserControl)封装当前文件路径、Encoding、IDocument对象、ITextEditorProperties对象、快捷键列表(Dictionary<Keys, IEditAction>类型)的变量,提供LoadFile()、SaveFile()等重要方法。
TextEditorControl类继承自上面的类,并声明了Panel、Splitter、TextAreaControl、PrintDocument控件,其中显示文件的核心控件是TextAreaControl,该控件在此类中被声明了两个变量,默认只有一个primaryTextArea显示,支持切分为两个窗口的显示,当有两个窗口时,Splitter控件才有效;PrinDocument控件用以打印输出内容;该类的另一个重要功能是提供了UnDo()、Redo()方法。
TextAreaControl(继承自Panel)控件,封装了TextArea、VScrollBar、HScrollBar控件,其中TextArea负责文件数据显示,另外两个控件负责文件内容大于可见尺寸时的滚动条服务。
TextArea(继承自Control)封装了TextView, IconBarMargin, GutterMargin, FoldMargin, SelectionManager, Caret 等控件或类对象,其中前四个控件均继承自AbstractMargin,代表区域对象,各自负责字符区域(较大的文件内容绘制区)、图标区域(如Bookmark图标所在列)、Gutter区域(如行号)、折叠控制区域的绘制,SelectionManager用以控制选中项,Caret用以控制光标位置调整和显示。

下面是一些我在读代码的过程中有过的疑问及解答:
a, 加载文件时发生了什么?
答:加载文件时控件根据文件的后缀名选择了相应的高亮显示策略,然后读取文件的内容并生成相应的GapTextBufferStrategy, LineSegment, TextWord 等对象,并且对所有的TextWord对象的HighlightColor类型成员变量完成分析赋值(用以在Paint函数中显示),相关代码如下:

LoadFile()相关代码

b, 控件如何响应键盘事件?
答:对于方向键及快捷功能键通过预定义的实现IEditAction接口的类响应(执行功能,不影响字符内容);其它字母/数字键直接输入,同时执行更新Folding, Bookmark, Higlighting等属性信息。相关代码:
键盘事件响应

c, 文件字符的绘制究竟是在何处?
答:在TextView.cs中的public override void Paint(Graphics g, Rectangle rect)函数中,重要函数:PaintLinePart(),辅助绘制函数:DrawDocumentWord(), DrawBracketHighlight(), DrawSpaceMarker(), DrawVerticalRuler() 等

d, 括号匹配在何处被定义和捕捉更新?
答:TextArea.cs中的List<BracketHighlightingSheme> bracketshemes  = new List<BracketHighlightingSheme>();变量存储在查找的匹配项,SearchMatchingBracket()方法中搜索并更新匹配项的显示,相关代码如下:

括号匹配



7、待分析的部分
本篇讨论暂未涉及如下(有价值?)内容的分析:
SyntaxHighlighting实现分析
BookmarkManager, FoldingManager, FormattingManager等
Paint()处理中坐标分析及转换

8、总结
本篇分析对应的《Dissecting a C# Application Inside SharpDevelop》书中的章节:
Chapter 7: Internationalization
Chapter 8: Document Management
Chapter 9: Syntax Highlighting
Chapter 11: Writing the Editor Control

对于该Demo中尚未实现的代码折叠、字符串的查找/替换功能将在下个礼拜读了10,12,13,14章后作进一步分析

 

posted on 2007-02-01 03:18 lin-zhang 阅读(3544) 评论(13)  编辑 收藏 网摘 所属分类: SharpDevelop分析

评论

非常好,收藏.
  回复  引用    

#2楼 2007-02-01 08:18 Echo[未注册用户]
功能说明:可以实时改变语言环境;提供对常用编程语言的编辑:支持语法高亮显示、括号匹配、设置书签;尚未提供查找/替换、代码折叠、代码提示/自动完成等功能。
----------------------------------------------------
支持语法高亮显示、括号匹配、设置书签直接使用控件,幾會不用太多的設置就可以使用,而代码折叠、代码提示/自动完成一直找不到SD實現的方法,樓主可否進一步分析,這樣對大家才會更有幫助!

  回复  引用    

#3楼[楼主] 2007-02-01 14:14 lin-zhang      
@虫子
谢谢 :)

@Echo
代码折叠、提示的功能我想可能是要在使用控件时用相应的 parser 类分析源文件,得到对应类型的方法、事件结构…… 对应书中相关章节:
Chapter 12: Writing the Parser
Chapter 13: Code Completion and Method Insight
Chapter 14: Navigating Code with the Class Scout and the Assembly Scout
等下周读完了这些再补充代码功能吧

  回复  引用  查看    

#4楼 2007-02-04 00:25 chaney[未注册用户]
请教ICSharpCode.TextEditor的几个问题:

如何剪切、粘贴?
如何得到当前光标所在行号及字符索引号?
如何得到当前选择文本的信息?
能否与richTextBox一样提供查找功能?

  回复  引用    

#5楼[楼主] 2007-02-04 21:58 lin-zhang      
sorry, 刚看到你的问题

1、剪切、复制、粘贴?
控件默认已提供,分别通过快捷键ctrl+X, ctrl+C, ctrl+V 完成,源码对应 TextEditorControlBase.cs 的 GenerateDefaultActions()函数中代码如下:
editactions[Keys.X | Keys.Control] = new Cut();
editactions[Keys.C | Keys.Control] = new Copy();
editactions[Keys.V | Keys.Control] = new Paste();
可见是通过已实现IEditAction接口的类, 你要加入到上下文菜单的话,可以调用相关类。 btw: 我的demo中设置ctrl+X为退出应用程序了,你可以进入Base.addin查找shortcut = "Control|X"改为其它的即可防止冲突

2、光标行及列 (假设控件的实例变量为:textEditorControl),可通过如下方式获取:
textEditorControl.ActiveTextAreaControl.Caret.Line;
textEditorControl.ActiveTextAreaControl.Caret.Column;

3、选中的文本信息
可通过textEditorControl.ActiveTextAreaControl.SelectionManager获取诸如HasSomethingSelected、SelectedText、SelectionCollection 之类的属性信息

4、查找功能
这要自己扩充其功能了,相关实现可参见SharpDevleop源码的ICSharpCode.SharpDevelop项目的\src\gui\TextEditor\SearchAndReplace\下的相关代码


SharpDevelop源码我也是在读第一遍,现在时间有点紧,所以有些方面没分析到,如SearchAndReplace,不好意思... :)

  回复  引用  查看    

#6楼 2007-02-05 09:49 chaney[未注册用户]
谢谢你的回复。
SelectedText是个只读属性,如果要改变所选文字(替换功能),则只能分段截取字符了

  回复  引用    

#7楼 2007-03-24 12:48 Jamskin[未注册用户]
1.Texteditor 如果在同一个Control 类上画出 Boomark、Fold、Line number 、update mark,效率会更高、代码更少;
2、Gap 虽然很巧妙,占用内存最小,但如果用行表会简单易行,同时可以把Boomark、Fold、Line number 、update mark以及Highlight 信息放在行表里,会很简单;
3、语法分析的XML格式较旧,使用正则表达式的话,会简单点。

  回复  引用    

#8楼 2007-03-29 16:54 Jamskin[未注册用户]
更正:
使用 正则表达式 会使运行速度变慢,使用自己编码实现语法高亮打开一个50000行的程序可以控制在2秒内,用正则表达式很难做到.越低级的代码效率越高!

  回复  引用    

#9楼[楼主] 2007-04-03 20:43 lin-zhang      
@Jamskin
谢谢评论:)
好像《Dissecting ...》电子书中说到用行表方式来存储字符信息,可能会在下个版本的SharpDevelop中使用此种方式吧,现在的版本应该是Gap方式
SharpDevelop现在版本的语法高亮显示好像未使用正则表达式匹配吧,即时括号匹配着色也是直接逐字符查找的,所以当查找字符时遇到' " 等就会宣传失败并退出查找, 期待有好的方式吧(高效和易用性)

  回复  引用  查看    

#10楼 2008-07-20 03:32 kgame[未注册用户]
請問要如何取得textEditorControl中
光棒的位置呢
我在ActiveTextAreaControl.SelectionManager的屬性中也找不到可以取得的方法

  回复  引用    

#11楼 2009-01-14 10:54 conquer's      
怎么去掉那个控件上的红色波浪线和那个换行符呢?
  回复  引用  查看    

#12楼 2009-06-04 22:43 FLYDZK[未注册用户]
请问下,定位好光标后,
如何移动光标的?

  回复  引用    

#13楼 2009-06-15 16:08 asato[未注册用户]
请问,如何设置这个控件的只读属性啊?
  回复  引用    




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 636381




相关文章:

相关链接: