SharpDevelop浅析_3_Internationalization & TextEditor 国际化、文档编辑器、语法高亮显示……
2008-12-11 09:21
477 查看
SharpDevelop浅析_3_Internationalization & TextEditor
国际化、文档编辑器、语法高亮显示……
1、Demo界面及功能解释
2、SharpDevelop的Internationalization的使用
3、SharpDevelop的Internationalization的实现分析
4、SharpDevelop的文档管理的基本概念
5、SharpDevelop的SyntaxHighlighting配置文件的定义
6、SharpDevelop的TextEditor控件的实现概述
7、待分析的部分
8、总结
[b]Demo下载[/b]
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支持语言环境的实时修改(修改后语言设置不需重启应用程序),因此要在应用程序订阅语言环境改变的事件,相关代码如下:
语言环境的修改及事件响应
1
//a, 设置语言
2
//引自SharpPad项目的Dialogs/SelectCulturePanel.cs
3
public override bool ReceiveDialogMessage(DialogMessage message)
4
{
5
if (message == DialogMessage.OK)
{
6
if (SelectedCulture != null)
{
7
PropertyService.Set("CoreProperties.UILanguage", SelectedCulture);
8
}
9
}
10
return true;
11
}
12
//返回当前窗体用户选择的语言设置(从显示国家国旗的ListView控件)
13
string SelectedCulture
{
14
get
{
15
if (listView.SelectedItems.Count > 0)
{
16
return listView.SelectedItems[0].SubItems[1].Text;
17
}
18
return null;
19
}
20
}
21
//b, 刷新当前应用程序
22
//引自SharpPad项目的SharpPad.cs
23
void IniFrm()
24
{
25
//
26
_menuStrip = new MenuStrip();
27
MenuService.AddItemsToMenu(_menuStrip.Items, this, "/michael/myMenus");
28
this.Controls.Add(_menuStrip);
29
30
PropertyService.PropertyChanged += new PropertyChangedEventHandler(PropertyService_PropertyChanged);
31
ResourceService.LanguageChanged += delegate
{
32
//更新菜单项的Text显示
33
foreach (ToolStripItem item in _menuStrip.Items)
34
{
35
if (item is IStatusUpdate)
36
{
37
((IStatusUpdate)item).UpdateText();
38
}
39
}
40
};
41
}
42
//引自ICSharpCode.Core的src/AddinTree/Addin/DefaultDoozers/MenuItem/Gui/IStatusUpdate.Core
43
using System;
44
namespace ICSharpCode.Core
45
{
46
public interface IStatusUpdate
47
{
48
void UpdateStatus();
49
void UpdateText();
50
}
51
}
另外,可以看到Demo中的选项命令窗口也采用了插件模式来构造窗体(实现细节就不多谈了),容器窗体是TreeViewOption,插件窗体如:SelectCulturePanel, SelectStylePanel, DemoNothing,真是“扩展--无处不在”呀。SharpDevelop源码中的这些窗体的成员控件是通过.xfrm配置文件配置,窗体继承自XmlUserControl来根据配置文件生成控件,有一定的灵活性。
新的语言包资源文件放在/data/resources/目录下(如StringResources.cn-gb.resources),注意应用程序默认语言选项以及在未找到相关语言资源时都是引用Entry中的myRes.resx资源文件;语言声明文件是/data/resources/languages/LanguageDefinition.xml,其格式如下:
LanguageDefinition.xml
1
<Languages>
2
<Languages name="Chinese (GB)" code="cn-gb" page="" icon="chinalg.png" />
3
<Languages name="German" code="de" page="" icon="germany.png" />
4
<Languages name="English" code="en" page="" icon="uk.png" />
5
</Languages>
SharpPad中动态显示可选语言项的分析类是LanguageService.cs和Language.cs,此处就不多解释了。
3、SharpDevelop的Internationalization的实现分析
SharpDevelop的多语言支持的键值对是通过本地资源文件存储的,当Demo中更改语言环境设置时,引发ICSharpCode.Core.dll中的事件及方法顺序如下:
PropertyService类的属性更新引发PropertyChanged事件 -> ResourceService响应接收到的事件并重新加载保存在内存中的资源键值对,然后引发LanguageChanged事件(由使用端接收并作相关处理,如Demo中的SharpPad.cs中的事件订阅/处理)属性更新的相关代码如下:
属性更改
1
//PropertyService类实际是提供了对Properties类的包装,因此直接看Properties类中的代码:
2
//引自ICSharpCode.Core/src/Services/PropertService/Properties.cs
3
public void Set<T>(string property, T value)
4
{
5
T oldValue = default(T);
6
if (!properties.ContainsKey(property))
{
7
properties.Add(property, value);
8
} else
{
9
oldValue = Get<T>(property, value);
10
properties[property] = value;
11
}
12
OnPropertyChanged(new PropertyChangedEventArgs(this, property, oldValue, value));
13
}
14
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
15
{
16
if (PropertyChanged != null)
{
17
PropertyChanged(this, e);
18
}
19
}
20
public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);
21
public event PropertyChangedEventHandler PropertyChanged;
资源服务类的事件响应代码如下:
ResourceService类的事件响应
1
//引自ICSharpCode.Core/src/Services/ResourceService/ResourceService.cs
2
//首先在类的初始化中订阅属性更改事件:
3
PropertyService.PropertyChanged += new PropertyChangedEventHandler(OnPropertyChange);
4
//类级别变量:
5
static Hashtable localStrings = null;
6
static Hashtable localIcons = null;
7
public static event EventHandler LanguageChanged;
8
//事件响应:
9
static void OnPropertyChange(object sender, PropertyChangedEventArgs e)
10
{
11
if (e.Key == uiLanguageProperty && e.NewValue != e.OldValue)
{
12
LoadLanguageResources((string)e.NewValue);
13
if (LanguageChanged != null)
14
LanguageChanged(null, e);
15
}
16
}
17
static void LoadLanguageResources(string language)
18
{
19
try
20
{
21
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(language);
22
}
23
catch (Exception)
24
{
25
try
26
{
27
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(language.Split('-')[0]);
28
}
29
catch (Exception)
{ }
30
}
31
32
localStrings = Load(stringResources, language);
33
if (localStrings == null && language.IndexOf('-') > 0)
34
{
35
localStrings = Load(stringResources, language.Split('-')[0]);
36
}
37
38
localIcons = Load(imageResources, language);
39
if (localIcons == null && language.IndexOf('-') > 0)
40
{
41
localIcons = Load(imageResources, language.Split('-')[0]);
42
}
43
44
localStringsResMgrs.Clear();
45
localIconsResMgrs.Clear();
46
currentLanguage = language;
47
foreach (ResourceAssembly ra in resourceAssemblies)
48
{
49
ra.Load();
50
}
51
}
52
static Hashtable Load(string name, string language)
53
{
54
return Load(resourceDirectory + Path.DirectorySeparatorChar + name + "." + language + ".resources");
55
}
56
static Hashtable Load(string fileName)
57
{
58
if (File.Exists(fileName))
{
59
Hashtable resources = new Hashtable();
60
ResourceReader rr = new ResourceReader(fileName);
61
foreach (DictionaryEntry entry in rr)
{
62
resources.Add(entry.Key, entry.Value);
63
}
64
rr.Close();
65
return resources;
66
}
67
return null;
68
}
4、SharpDevelop的文档管理的基本概念
代码编写工具的核心是代码编辑窗口(如打开一个.cs文件的窗口),如何在应用程序中存储该窗口内的字符内容?有些文件可能有上千行,统计下来可能有过万个字符,如果使用string对象存储字符内容,显然是不能满足性能要求,而且要实现语法高亮显示的话,还需要区分存储TextWord和相应颜色……
先看一下字符管理的基本要求:
ITextBufferStrategy接口
1
//引自ICSharpCode.TextEditor/src/Document/TextBufferStrategy/ITextBufferStrategy.cs
2
namespace ICSharpCode.TextEditor.Document
3
{
4
/**//// <summary>
5
/// Interface to describe a sequence of characters that can be edited.
6
/// </summary>
7
public interface ITextBufferStrategy
8
{
9
/**//// <value>
10
/// The current length of the sequence of characters that can be edited.
11
/// </value>
12
int Length
{
13
get;
14
}
15
16
/**//// <summary>
17
/// Inserts a string of characters into the sequence.
18
/// </summary>
19
/// <param name="offset">
20
/// offset where to insert the string.
21
/// </param>
22
/// <param name="text">
23
/// text to be inserted.
24
/// </param>
25
void Insert(int offset, string text);
26
27
/**//// <summary>
28
/// Removes some portion of the sequence.
29
/// </summary>
30
/// <param name="offset">
31
/// offset of the remove.
32
/// </param>
33
/// <param name="length">
34
/// number of characters to remove.
35
/// </param>
36
void Remove(int offset, int length);
37
38
/**//// <summary>
39
/// Replace some portion of the sequence.
40
/// </summary>
41
/// <param name="offset">
42
/// offset.
43
/// </param>
44
/// <param name="length">
45
/// number of characters to replace.
46
/// </param>
47
/// <param name="text">
48
/// text to be replaced with.
49
/// </param>
50
void Replace(int offset, int length, string text);
51
52
/**//// <summary>
53
/// Fetches a string of characters contained in the sequence.
54
/// </summary>
55
/// <param name="offset">
56
/// Offset into the sequence to fetch
57
/// </param>
58
/// <param name="length">
59
/// number of characters to copy.
60
/// </param>
61
string GetText(int offset, int length);
62
63
/**//// <summary>
64
/// Returns a specific char of the sequence.
65
/// </summary>
66
/// <param name="offset">
67
/// Offset of the char to get.
68
/// </param>
69
char GetCharAt(int offset);
70
71
/**//// <summary>
72
/// This method sets the stored content.
73
/// </summary>
74
/// <param name="text">
75
/// The string that represents the character sequence.
76
/// </param>
77
void SetContent(string text);
78
}
79
}
SharpDevelop采用的策略是使用带Gap的字符,Gap的长度小于预定规格时,按约定增加一定长度(有些类似于SqlServer的表空间管理?),插入/修改/删除字符时只是修改Gap的长度和位置和移动部分字符,核心函数如下:
GapTextBufferStrategy类
1
//引自ICSharpCode.TextEditor/src/Document/TextBufferStrategy/GapTextBufferStrategy.cs
2
//类级别变量:
3
char[] buffer = new char[0];
4
int gapBeginOffset = 0;
5
int gapEndOffset = 0;
6
/**//// <summary>
7
/// 小于此长度时增长Buffer/Gap
8
/// </summary>
9
int minGapLength = 32;
10
/**//// <summary>
11
/// 每次需要增长时,增长此长度的Gap
12
/// </summary>
13
int maxGapLength = 256;
14
//重要函数:
15
public void SetContent(string text)
16
{
17
if (text == null)
{
18
text = String.Empty;
19
}
20
buffer = text.ToCharArray();
21
gapBeginOffset = gapEndOffset = 0;
22
}
23
public void Insert(int offset, string text)
24
{
25
Replace(offset, 0, text);
26
}
27
public void Remove(int offset, int length)
28
{
29
Replace(offset, length, String.Empty);
30
}
31
public void Replace(int offset, int length, string text)
32
{
33
if (text == null)
{
34
text = String.Empty;
35
}
36
37
// Math.Max is used so that if we need to resize the array
38
// the new array has enough space for all old chars
39
PlaceGap(offset + length, Math.Max(text.Length - length, 0));
40
text.CopyTo(0, buffer, offset, text.Length);
41
gapBeginOffset += text.Length - length;
42
}
43
void PlaceGap(int offset, int length)
44
{
45
int deltaLength = GapLength - length;
46
// 如果Gap的长度足够大,则只是作移动相关字符的处理
47
if (minGapLength <= deltaLength && deltaLength <= maxGapLength)
{
48
int delta = gapBeginOffset - offset;
49
// check if the gap is already in place
50
if (offset == gapBeginOffset)
{
51
return;
52
} else if (offset < gapBeginOffset)
{
53
int gapLength = gapEndOffset - gapBeginOffset;
54
Array.Copy(buffer, offset, buffer, offset + gapLength, delta);
55
} else
{ //offset > gapBeginOffset
56
Array.Copy(buffer, gapEndOffset, buffer, gapBeginOffset, -delta);
57
}
58
gapBeginOffset -= delta;
59
gapEndOffset -= delta;
60
return;
61
}
62
63
// 否则,需要新分析buffer的大小,并修改offset等位置数值
64
int oldLength = GapLength;
65
int newLength = maxGapLength + length;
66
int newGapEndOffset = offset + newLength;
67
char[] newBuffer = new char[buffer.Length + newLength - oldLength]; //新分配后将有maxGapLength长度的Gap
68
69
if (oldLength == 0)
{
70
Array.Copy(buffer, 0, newBuffer, 0, offset);
71
Array.Copy(buffer, offset, newBuffer, newGapEndOffset, newBuffer.Length - newGapEndOffset);
72
} else if (offset < gapBeginOffset)
{
73
int delta = gapBeginOffset - offset;
74
Array.Copy(buffer, 0, newBuffer, 0, offset);
75
Array.Copy(buffer, offset, newBuffer, newGapEndOffset, delta);
76
Array.Copy(buffer, gapEndOffset, newBuffer, newGapEndOffset + delta, buffer.Length - gapEndOffset);
77
} else
{
78
int delta = offset - gapBeginOffset;
79
Array.Copy(buffer, 0, newBuffer, 0, gapBeginOffset);
80
Array.Copy(buffer, gapEndOffset, newBuffer, gapBeginOffset, delta);
81
Array.Copy(buffer, gapEndOffset + delta, newBuffer, newGapEndOffset, newBuffer.Length - newGapEndOffset);
82
}
83
84
buffer = newBuffer;
85
gapBeginOffset = offset;
86
gapEndOffset = newGapEndOffset;
87
}
有了基本的数据容器,核心问题已经解决了,但是在绘制界面时直接使用上面的类,显然不够方便,于是就定义了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
1
<SyntaxModes version="1.0">
2
<Mode file = "ASPX.xshd"
3
name = "ASP/XHTML"
4
extensions = ".asp;.aspx;.asax;.asmx"/>
5
6
<Mode file = "BAT-Mode.xshd"
7
name = "BAT"
8
extensions = ".bat"/>
9
10
<Mode file = "CPP-Mode.xshd"
11
name = "C++.NET"
12
extensions = ".c;.h;.cc;.C;.cpp;.hpp"/>
13
14
<Mode file = "CSharp-Mode.xshd"
15
name = "C#"
16
extensions = ".cs"/>
17
18
<Mode file = "XML-Mode.xshd"
19
name = "XML"
20
extensions = ".xml;.xsl;.xslt;.xsd;.manifest;.config;.addin;.xshd;.wxs;.proj;.csproj;.vbproj;.ilproj;.booproj;.build;.xfrm;.targets;.xaml;.xpt;.xft;.map;.wsdl;.disco"/>
21
</SyntaxModes>
取CSharp-Mode.xshd(注:xshd是Xml Syntax Highlighting Definition的缩写)为例,查看其定义:
CSharp-Mode.xshd
1
<?xml version="1.0"?>
2
<SyntaxDefinition name = "C#" extensions = ".cs">
3
<Properties>
4
<Property name="LineComment" value="//"/>
5
</Properties>
6
<Digits name = "Digits" bold = "false" italic = "false" color = "DarkBlue"/>
7
<RuleSets>
8
<RuleSet ignorecase="false">
9
<Delimiters>&<>~!%^*()-+=|/#/{}[]:;"' , .?</Delimiters>
10
<Span name = "PreprocessorDirectives" rule = "PreprocessorSet" bold="false" italic="false" color="Green" stopateol = "true">
11
<Begin>#</Begin>
12
</Span>
13
<Span name = "BlockComment" rule = "CommentMarkerSet" bold = "false" italic = "false" color = "Green" stopateol = "false">
14
<Begin>/*</Begin>
15
<End>*/</End>
16
</Span>
17
<KeyWords name = "Punctuation" bold = "false" italic = "false" color = "DarkGreen">
18
<Key word = "?" />
19
<Key word = "," />
20
<Key word = "." />
21
<Key word = ";" />
22
<Key word = "(" />
23
<Key word = ")" />
24
<Key word = "[" />
25
<Key word = "]" />
26
<Key word = "{" />
27
<Key word = "}" />
28
<Key word = "+" />
29
<Key word = "-" />
30
<Key word = "/" />
31
<Key word = "%" />
32
<Key word = "*" />
33
<Key word = "<" />
34
<Key word = ">" />
35
<Key word = "^" />
36
<Key word = "=" />
37
<Key word = "~" />
38
<Key word = "!" />
39
<Key word = "|" />
40
<Key word = "&" />
41
</KeyWords>
42
43
<KeyWords name = "AccessKeywords" bold="true" italic="false" color="Black">
44
<Key word = "this" />
45
<Key word = "base" />
46
</KeyWords>
47
48
<KeyWords name = "OperatorKeywords" bold="true" italic="false" color="DarkCyan">
49
<Key word = "as" />
50
<Key word = "is" />
51
<Key word = "new" />
52
<Key word = "sizeof" />
53
<Key word = "typeof" />
54
<Key word = "true" />
55
<Key word = "false" />
56
<Key word = "stackalloc" />
57
</KeyWords>
58
</RuleSet>
59
</RuleSets>
60
</SyntaxDefinition>
上面的文件公摘选了部分标签,其中
<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()相关代码
1
//取自TextEditorControlBase.cs
2
public void LoadFile(string fileName, bool autoLoadHighlighting, bool autodetectEncoding)
3
{
4
BeginUpdate();
5
document.TextContent = String.Empty;
6
document.UndoStack.ClearAll();
7
document.BookmarkManager.Clear();
8
if (autoLoadHighlighting)
{
9
//根据文件扩展名判断并赋值高亮显示的策略
10
document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategyForFile(fileName);
11
}
12
13
if (autodetectEncoding)
{
14
Encoding encoding = this.Encoding;
15
//赋值
16
Document.TextContent = Util.FileReader.ReadFileContent(fileName, ref encoding, this.TextEditorProperties.Encoding);
17
this.Encoding = encoding;
18
} else
{
19
using (StreamReader reader = new StreamReader(fileName, this.Encoding))
{
20
Document.TextContent = reader.ReadToEnd();
21
}
22
}
23
24
this.FileName = fileName;
25
OptionsChanged();
26
Document.UpdateQueue.Clear();
27
EndUpdate();
28
29
Refresh();
30
}
31
//引自DefaultDocument.cs
32
ITextBufferStrategy textBufferStrategy = null;
33
ILineManager lineTrackingStrategy = null;
34
public string TextContent
{
35
get
{
36
return GetText(0, textBufferStrategy.Length);
37
}
38
set
{
39
Debug.Assert(textBufferStrategy != null);
40
Debug.Assert(lineTrackingStrategy != null);
41
OnDocumentAboutToBeChanged(new DocumentEventArgs(this, 0, 0, value));
42
//赋值
43
textBufferStrategy.SetContent(value);
44
//赋值 && 分析并完成高亮分析的赋值
45
lineTrackingStrategy.SetContent(value);
46
47
OnDocumentChanged(new DocumentEventArgs(this, 0, 0, value));
48
OnTextContentChanged(EventArgs.Empty);
49
}
50
}
51
//引自DefaultLineManager.cs
52
public void SetContent(string text)
53
{
54
lineCollection.Clear();
55
if (text != null)
{
56
textLength = text.Length;
57
// 生成LineSegment集合
58
CreateLines(text, 0, 0);
59
// 高亮分析
60
RunHighlighter();
61
}
62
}
b, 控件如何响应键盘事件?
答:对于方向键及快捷功能键通过预定义的实现IEditAction接口的类响应(执行功能,不影响字符内容);其它字母/数字键直接输入,同时执行更新Folding, Bookmark, Higlighting等属性信息。相关代码:
键盘事件响应
1
//功能键的定义(引自TextEditorControlBase.cs):
2
protected Dictionary<Keys, IEditAction> editactions = new Dictionary<Keys, IEditAction>();
3
protected TextEditorControlBase()
4
{
5
GenerateDefaultActions();
6
HighlightingManager.Manager.ReloadSyntaxHighlighting += new EventHandler(ReloadHighlighting);
7
}
8
void GenerateDefaultActions()
9
{
10
editactions[Keys.Left] = new CaretLeft();
11
editactions[Keys.Left | Keys.Shift] = new ShiftCaretLeft();
12
editactions[Keys.Left | Keys.Control] = new WordLeft();
13
editactions[Keys.Left | Keys.Control | Keys.Shift] = new ShiftWordLeft();
14
editactions[Keys.Right] = new CaretRight();
15
editactions[Keys.Right | Keys.Shift] = new ShiftCaretRight();
16
editactions[Keys.Right | Keys.Control] = new WordRight();
17
editactions[Keys.Right | Keys.Control | Keys.Shift] = new ShiftWordRight();
18
editactions[Keys.Up] = new CaretUp();
19
editactions[Keys.Up | Keys.Shift] = new ShiftCaretUp();
20
editactions[Keys.Up | Keys.Control] = new ScrollLineUp();
21
editactions[Keys.Down] = new CaretDown();
22
editactions[Keys.Down | Keys.Shift] = new ShiftCaretDown();
23
editactions[Keys.Down | Keys.Control] = new ScrollLineDown();
24
25
editactions[Keys.Insert] = new ToggleEditMode();
26
editactions[Keys.Insert | Keys.Control] = new Copy();
27
editactions[Keys.Insert | Keys.Shift] = new Paste();
28
editactions[Keys.Delete] = new Delete();
29
editactions[Keys.Delete | Keys.Shift] = new Cut();
30
editactions[Keys.Home] = new Home();
31
editactions[Keys.Home | Keys.Shift] = new ShiftHome();
32
editactions[Keys.Home | Keys.Control] = new MoveToStart();
33
editactions[Keys.Home | Keys.Control | Keys.Shift] = new ShiftMoveToStart();
34
editactions[Keys.End] = new End();
35
editactions[Keys.End | Keys.Shift] = new ShiftEnd();
36
editactions[Keys.End | Keys.Control] = new MoveToEnd();
37
editactions[Keys.End | Keys.Control | Keys.Shift] = new ShiftMoveToEnd();
38
editactions[Keys.PageUp] = new MovePageUp();
39
editactions[Keys.PageUp | Keys.Shift] = new ShiftMovePageUp();
40
editactions[Keys.PageDown] = new MovePageDown();
41
editactions[Keys.PageDown | Keys.Shift] = new ShiftMovePageDown();
42
43
editactions[Keys.Return] = new Return();
44
editactions[Keys.Tab] = new Tab();
45
editactions[Keys.Tab | Keys.Shift] = new ShiftTab();
46
editactions[Keys.Back] = new Backspace();
47
editactions[Keys.Back | Keys.Shift] = new Backspace();
48
49
editactions[Keys.X | Keys.Control] = new Cut();
50
editactions[Keys.C | Keys.Control] = new Copy();
51
editactions[Keys.V | Keys.Control] = new Paste();
52
53
editactions[Keys.A | Keys.Control] = new SelectWholeDocument();
54
editactions[Keys.Escape] = new ClearAllSelections();
55
56
editactions[Keys.Divide | Keys.Control] = new ToggleComment();
57
editactions[Keys.OemQuestion | Keys.Control] = new ToggleComment();
58
59
editactions[Keys.Back | Keys.Alt] = new Actions.Undo();
60
editactions[Keys.Z | Keys.Control] = new Actions.Undo();
61
editactions[Keys.Y | Keys.Control] = new Redo();
62
63
editactions[Keys.Delete | Keys.Control] = new DeleteWord();
64
editactions[Keys.Back | Keys.Control] = new WordBackspace();
65
editactions[Keys.D | Keys.Control] = new DeleteLine();
66
editactions[Keys.D | Keys.Shift | Keys.Control] = new DeleteToLineEnd();
67
68
editactions[Keys.B | Keys.Control] = new GotoMatchingBrace();
69
}
70
internal IEditAction GetEditAction(Keys keyData)
71
{
72
if (!editactions.ContainsKey(keyData))
{
73
return null;
74
}
75
return (IEditAction)editactions[keyData];
76
}
77
//功能键的响应(取自TextArea.cs):
78
protected override bool ProcessDialogKey(Keys keyData)
79
{
80
return ExecuteDialogKey(keyData) || base.ProcessDialogKey(keyData);
81
}
82
public bool ExecuteDialogKey(Keys keyData)
83
{
84
// try, if a dialog key processor was set to use this
85
if (DoProcessDialogKey != null && DoProcessDialogKey(keyData))
{
86
return true;
87
}
88
89
if (keyData == Keys.Back || keyData == Keys.Delete || keyData == Keys.Enter)
{
90
if (TextEditorProperties.UseCustomLine == true)
{
91
if (SelectionManager.HasSomethingSelected)
{
92
if (Document.CustomLineManager.IsReadOnly(SelectionManager.SelectionCollection[0], false))
93
return true;
94
} else
{
95
int curLineNr = Document.GetLineNumberForOffset(Caret.Offset);
96
if (Document.CustomLineManager.IsReadOnly(curLineNr, false) == true)
97
return true;
98
if ((Caret.Column == 0) && (curLineNr - 1 >= 0) && keyData == Keys.Back &&
99
Document.CustomLineManager.IsReadOnly(curLineNr - 1, false) == true)
100
return true;
101
if (keyData == Keys.Delete)
{
102
LineSegment curLine = Document.GetLineSegment(curLineNr);
103
if (curLine.Offset + curLine.Length == Caret.Offset &&
104
Document.CustomLineManager.IsReadOnly(curLineNr + 1, false) == true)
{
105
return true;
106
}
107
}
108
}
109
}
110
}
111
112
// if not (or the process was 'silent', use the standard edit actions
113
IEditAction action = motherTextEditorControl.GetEditAction(keyData);
114
AutoClearSelection = true;
115
if (action != null)
{
116
motherTextEditorControl.BeginUpdate();
117
try
{
118
lock (Document)
{
119
// 执行相关的功能操作
120
action.Execute(this);
121
if (SelectionManager.HasSomethingSelected && AutoClearSelection /**//*&& caretchanged*/)
{
122
if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal)
{
123
SelectionManager.ClearSelection();
124
}
125
}
126
}
127
} finally
{
128
motherTextEditorControl.EndUpdate();
129
Caret.UpdateCaretPosition();
130
}
131
return true;
132
}
133
return false;
134
}
135
136
//输入键的响应(取自TextArea.cs):
137
protected override void OnKeyPress(KeyPressEventArgs e)
138
{
139
base.OnKeyPress(e);
140
SimulateKeyPress(e.KeyChar);
141
e.Handled = true;
142
}
143
public void SimulateKeyPress(char ch)
144
{
145
if (Document.ReadOnly)
{
146
return;
147
}
148
149
if (TextEditorProperties.UseCustomLine == true)
{
150
if (SelectionManager.HasSomethingSelected)
{
151
if (Document.CustomLineManager.IsReadOnly(SelectionManager.SelectionCollection[0], false))
152
return;
153
} else if (Document.CustomLineManager.IsReadOnly(Caret.Line, false) == true)
154
return;
155
}
156
157
if (ch < ' ')
{
158
return;
159
}
160
161
if (!HiddenMouseCursor && TextEditorProperties.HideMouseCursor)
{
162
HiddenMouseCursor = true;
163
Cursor.Hide();
164
}
165
CloseToolTip();
166
167
motherTextEditorControl.BeginUpdate();
168
// INSERT char
169
if (!HandleKeyPress(ch))
{
170
switch (Caret.CaretMode)
{
171
case CaretMode.InsertMode:
172
InsertChar(ch);
173
break;
174
case CaretMode.OverwriteMode:
175
ReplaceChar(ch);
176
break;
177
default:
178
Debug.Assert(false, "Unknown caret mode " + Caret.CaretMode);
179
break;
180
}
181
}
182
183
int currentLineNr = Caret.Line;
184
int delta = Document.FormattingStrategy.FormatLine(this, currentLineNr, Document.PositionToOffset(Caret.Position), ch);
185
186
motherTextEditorControl.EndUpdate();
187
if (delta != 0)
{
188
// this.motherTextEditorControl.UpdateLines(currentLineNr, currentLineNr);
189
}
190
}
191
//输入键通过底层的方法输入字符和更改高亮显示,如下分析(以Insert为例,取自DefaultDocument.cs):
192
public void Insert(int offset, string text)
193
{
194
if (readOnly)
{
195
return;
196
}
197
OnDocumentAboutToBeChanged(new DocumentEventArgs(this, offset, -1, text));
198
DateTime time = DateTime.Now;
199
//增加字符
200
textBufferStrategy.Insert(offset, text);
201
202
time = DateTime.Now;
203
//更新LineSegment, TextWord对象
204
lineTrackingStrategy.Insert(offset, text);
205
206
time = DateTime.Now;
207
208
undoStack.Push(new UndoableInsert(this, offset, text));
209
210
time = DateTime.Now;
211
OnDocumentChanged(new DocumentEventArgs(this, offset, -1, text));
212
}
213
// 追踪到DefaultLineManager.cs中Insert()方法实际上调用到Replace(offset,length,string.Empty)方法:
214
public void Replace(int offset, int length, string text)
215
{
216
int lineNumber = GetLineNumberForOffset(offset);
217
int insertLineNumber = lineNumber;
218
if (Remove(lineNumber, offset, length))
{
219
--lineNumber;
220
}
221
222
lineNumber += Insert(insertLineNumber, offset, text);
223
224
int delta = -length;
225
if (text != null)
{
226
delta = text.Length + delta;
227
}
228
229
if (delta != 0)
{
230
AdaptLineOffsets(lineNumber, delta);
231
}
232
//启用高亮显示分析 ----- 注:此时的 markLines 仅存储变化的相关行,而SetContent时存储的是所有行,因此只是按需分析
233
RunHighlighter();
234
}
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()方法中搜索并更新匹配项的显示,相关代码如下:
括号匹配
1
//引自TextArea.cs
2
List<BracketHighlightingSheme> bracketshemes = new List<BracketHighlightingSheme>();
3
Caret caret;
4
public TextArea(TextEditorControl motherTextEditorControl, TextAreaControl motherTextAreaControl)
5
{
6
// 省略无关代码
7
bracketshemes.Add(new BracketHighlightingSheme('{', '}'));
8
bracketshemes.Add(new BracketHighlightingSheme('(', ')'));
9
bracketshemes.Add(new BracketHighlightingSheme('[', ']'));
10
11
caret.PositionChanged += new EventHandler(SearchMatchingBracket);
12
// 省略无关代码
13
}
14
void SearchMatchingBracket(object sender, EventArgs e)
15
{
16
if (!TextEditorProperties.ShowMatchingBracket)
{
17
textView.Highlight = null;
18
return;
19
}
20
bool changed = false;
21
if (caret.Offset == 0)
{
22
if (textView.Highlight != null)
{
23
int line = textView.Highlight.OpenBrace.Y;
24
int line2 = textView.Highlight.CloseBrace.Y;
25
textView.Highlight = null;
26
UpdateLine(line);
27
UpdateLine(line2);
28
}
29
return;
30
}
31
foreach (BracketHighlightingSheme bracketsheme in bracketshemes)
{
32
// if (bracketsheme.IsInside(textareapainter.Document, textareapainter.Document.Caret.Offset)) {
33
Highlight highlight = bracketsheme.GetHighlight(Document, Caret.Offset - 1);
34
if (textView.Highlight != null && textView.Highlight.OpenBrace.Y >=0 && textView.Highlight.OpenBrace.Y < Document.TotalNumberOfLines)
{
35
//取消旧匹配项的高亮显示
36
UpdateLine(textView.Highlight.OpenBrace.Y);
37
}
38
if (textView.Highlight != null && textView.Highlight.CloseBrace.Y >=0 && textView.Highlight.CloseBrace.Y < Document.TotalNumberOfLines)
{
39
//取消旧匹配项的高亮显示
40
UpdateLine(textView.Highlight.CloseBrace.Y);
41
}
42
textView.Highlight = highlight;
43
if (highlight != null)
{
44
changed = true;
45
break;
46
}
47
// }
48
}
49
if (changed || textView.Highlight != null)
{
50
int line = textView.Highlight.OpenBrace.Y;
51
int line2 = textView.Highlight.CloseBrace.Y;
52
if (!changed)
{
53
textView.Highlight = null;
54
}
55
//更新显示新匹配项 OpenBrace
56
UpdateLine(line);
57
//更新显示新匹配项 CloseBrace
58
UpdateLine(line2);
59
}
60
}
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章后作进一步分析
国际化、文档编辑器、语法高亮显示……
1、Demo界面及功能解释
2、SharpDevelop的Internationalization的使用
3、SharpDevelop的Internationalization的实现分析
4、SharpDevelop的文档管理的基本概念
5、SharpDevelop的SyntaxHighlighting配置文件的定义
6、SharpDevelop的TextEditor控件的实现概述
7、待分析的部分
8、总结
[b]Demo下载[/b]
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支持语言环境的实时修改(修改后语言设置不需重启应用程序),因此要在应用程序订阅语言环境改变的事件,相关代码如下:
语言环境的修改及事件响应
1
//a, 设置语言
2
//引自SharpPad项目的Dialogs/SelectCulturePanel.cs
3
public override bool ReceiveDialogMessage(DialogMessage message)
4
{
5
if (message == DialogMessage.OK)
{
6
if (SelectedCulture != null)
{
7
PropertyService.Set("CoreProperties.UILanguage", SelectedCulture);
8
}
9
}
10
return true;
11
}
12
//返回当前窗体用户选择的语言设置(从显示国家国旗的ListView控件)
13
string SelectedCulture
{
14
get
{
15
if (listView.SelectedItems.Count > 0)
{
16
return listView.SelectedItems[0].SubItems[1].Text;
17
}
18
return null;
19
}
20
}
21
//b, 刷新当前应用程序
22
//引自SharpPad项目的SharpPad.cs
23
void IniFrm()
24
{
25
//
26
_menuStrip = new MenuStrip();
27
MenuService.AddItemsToMenu(_menuStrip.Items, this, "/michael/myMenus");
28
this.Controls.Add(_menuStrip);
29
30
PropertyService.PropertyChanged += new PropertyChangedEventHandler(PropertyService_PropertyChanged);
31
ResourceService.LanguageChanged += delegate
{
32
//更新菜单项的Text显示
33
foreach (ToolStripItem item in _menuStrip.Items)
34
{
35
if (item is IStatusUpdate)
36
{
37
((IStatusUpdate)item).UpdateText();
38
}
39
}
40
};
41
}
42
//引自ICSharpCode.Core的src/AddinTree/Addin/DefaultDoozers/MenuItem/Gui/IStatusUpdate.Core
43
using System;
44
namespace ICSharpCode.Core
45
{
46
public interface IStatusUpdate
47
{
48
void UpdateStatus();
49
void UpdateText();
50
}
51
}
另外,可以看到Demo中的选项命令窗口也采用了插件模式来构造窗体(实现细节就不多谈了),容器窗体是TreeViewOption,插件窗体如:SelectCulturePanel, SelectStylePanel, DemoNothing,真是“扩展--无处不在”呀。SharpDevelop源码中的这些窗体的成员控件是通过.xfrm配置文件配置,窗体继承自XmlUserControl来根据配置文件生成控件,有一定的灵活性。
新的语言包资源文件放在/data/resources/目录下(如StringResources.cn-gb.resources),注意应用程序默认语言选项以及在未找到相关语言资源时都是引用Entry中的myRes.resx资源文件;语言声明文件是/data/resources/languages/LanguageDefinition.xml,其格式如下:
LanguageDefinition.xml
1
<Languages>
2
<Languages name="Chinese (GB)" code="cn-gb" page="" icon="chinalg.png" />
3
<Languages name="German" code="de" page="" icon="germany.png" />
4
<Languages name="English" code="en" page="" icon="uk.png" />
5
</Languages>
SharpPad中动态显示可选语言项的分析类是LanguageService.cs和Language.cs,此处就不多解释了。
3、SharpDevelop的Internationalization的实现分析
SharpDevelop的多语言支持的键值对是通过本地资源文件存储的,当Demo中更改语言环境设置时,引发ICSharpCode.Core.dll中的事件及方法顺序如下:
PropertyService类的属性更新引发PropertyChanged事件 -> ResourceService响应接收到的事件并重新加载保存在内存中的资源键值对,然后引发LanguageChanged事件(由使用端接收并作相关处理,如Demo中的SharpPad.cs中的事件订阅/处理)属性更新的相关代码如下:
属性更改
1
//PropertyService类实际是提供了对Properties类的包装,因此直接看Properties类中的代码:
2
//引自ICSharpCode.Core/src/Services/PropertService/Properties.cs
3
public void Set<T>(string property, T value)
4
{
5
T oldValue = default(T);
6
if (!properties.ContainsKey(property))
{
7
properties.Add(property, value);
8
} else
{
9
oldValue = Get<T>(property, value);
10
properties[property] = value;
11
}
12
OnPropertyChanged(new PropertyChangedEventArgs(this, property, oldValue, value));
13
}
14
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
15
{
16
if (PropertyChanged != null)
{
17
PropertyChanged(this, e);
18
}
19
}
20
public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);
21
public event PropertyChangedEventHandler PropertyChanged;
资源服务类的事件响应代码如下:
ResourceService类的事件响应
1
//引自ICSharpCode.Core/src/Services/ResourceService/ResourceService.cs
2
//首先在类的初始化中订阅属性更改事件:
3
PropertyService.PropertyChanged += new PropertyChangedEventHandler(OnPropertyChange);
4
//类级别变量:
5
static Hashtable localStrings = null;
6
static Hashtable localIcons = null;
7
public static event EventHandler LanguageChanged;
8
//事件响应:
9
static void OnPropertyChange(object sender, PropertyChangedEventArgs e)
10
{
11
if (e.Key == uiLanguageProperty && e.NewValue != e.OldValue)
{
12
LoadLanguageResources((string)e.NewValue);
13
if (LanguageChanged != null)
14
LanguageChanged(null, e);
15
}
16
}
17
static void LoadLanguageResources(string language)
18
{
19
try
20
{
21
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(language);
22
}
23
catch (Exception)
24
{
25
try
26
{
27
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(language.Split('-')[0]);
28
}
29
catch (Exception)
{ }
30
}
31
32
localStrings = Load(stringResources, language);
33
if (localStrings == null && language.IndexOf('-') > 0)
34
{
35
localStrings = Load(stringResources, language.Split('-')[0]);
36
}
37
38
localIcons = Load(imageResources, language);
39
if (localIcons == null && language.IndexOf('-') > 0)
40
{
41
localIcons = Load(imageResources, language.Split('-')[0]);
42
}
43
44
localStringsResMgrs.Clear();
45
localIconsResMgrs.Clear();
46
currentLanguage = language;
47
foreach (ResourceAssembly ra in resourceAssemblies)
48
{
49
ra.Load();
50
}
51
}
52
static Hashtable Load(string name, string language)
53
{
54
return Load(resourceDirectory + Path.DirectorySeparatorChar + name + "." + language + ".resources");
55
}
56
static Hashtable Load(string fileName)
57
{
58
if (File.Exists(fileName))
{
59
Hashtable resources = new Hashtable();
60
ResourceReader rr = new ResourceReader(fileName);
61
foreach (DictionaryEntry entry in rr)
{
62
resources.Add(entry.Key, entry.Value);
63
}
64
rr.Close();
65
return resources;
66
}
67
return null;
68
}
4、SharpDevelop的文档管理的基本概念
代码编写工具的核心是代码编辑窗口(如打开一个.cs文件的窗口),如何在应用程序中存储该窗口内的字符内容?有些文件可能有上千行,统计下来可能有过万个字符,如果使用string对象存储字符内容,显然是不能满足性能要求,而且要实现语法高亮显示的话,还需要区分存储TextWord和相应颜色……
先看一下字符管理的基本要求:
ITextBufferStrategy接口
1
//引自ICSharpCode.TextEditor/src/Document/TextBufferStrategy/ITextBufferStrategy.cs
2
namespace ICSharpCode.TextEditor.Document
3
{
4
/**//// <summary>
5
/// Interface to describe a sequence of characters that can be edited.
6
/// </summary>
7
public interface ITextBufferStrategy
8
{
9
/**//// <value>
10
/// The current length of the sequence of characters that can be edited.
11
/// </value>
12
int Length
{
13
get;
14
}
15
16
/**//// <summary>
17
/// Inserts a string of characters into the sequence.
18
/// </summary>
19
/// <param name="offset">
20
/// offset where to insert the string.
21
/// </param>
22
/// <param name="text">
23
/// text to be inserted.
24
/// </param>
25
void Insert(int offset, string text);
26
27
/**//// <summary>
28
/// Removes some portion of the sequence.
29
/// </summary>
30
/// <param name="offset">
31
/// offset of the remove.
32
/// </param>
33
/// <param name="length">
34
/// number of characters to remove.
35
/// </param>
36
void Remove(int offset, int length);
37
38
/**//// <summary>
39
/// Replace some portion of the sequence.
40
/// </summary>
41
/// <param name="offset">
42
/// offset.
43
/// </param>
44
/// <param name="length">
45
/// number of characters to replace.
46
/// </param>
47
/// <param name="text">
48
/// text to be replaced with.
49
/// </param>
50
void Replace(int offset, int length, string text);
51
52
/**//// <summary>
53
/// Fetches a string of characters contained in the sequence.
54
/// </summary>
55
/// <param name="offset">
56
/// Offset into the sequence to fetch
57
/// </param>
58
/// <param name="length">
59
/// number of characters to copy.
60
/// </param>
61
string GetText(int offset, int length);
62
63
/**//// <summary>
64
/// Returns a specific char of the sequence.
65
/// </summary>
66
/// <param name="offset">
67
/// Offset of the char to get.
68
/// </param>
69
char GetCharAt(int offset);
70
71
/**//// <summary>
72
/// This method sets the stored content.
73
/// </summary>
74
/// <param name="text">
75
/// The string that represents the character sequence.
76
/// </param>
77
void SetContent(string text);
78
}
79
}
SharpDevelop采用的策略是使用带Gap的字符,Gap的长度小于预定规格时,按约定增加一定长度(有些类似于SqlServer的表空间管理?),插入/修改/删除字符时只是修改Gap的长度和位置和移动部分字符,核心函数如下:
GapTextBufferStrategy类
1
//引自ICSharpCode.TextEditor/src/Document/TextBufferStrategy/GapTextBufferStrategy.cs
2
//类级别变量:
3
char[] buffer = new char[0];
4
int gapBeginOffset = 0;
5
int gapEndOffset = 0;
6
/**//// <summary>
7
/// 小于此长度时增长Buffer/Gap
8
/// </summary>
9
int minGapLength = 32;
10
/**//// <summary>
11
/// 每次需要增长时,增长此长度的Gap
12
/// </summary>
13
int maxGapLength = 256;
14
//重要函数:
15
public void SetContent(string text)
16
{
17
if (text == null)
{
18
text = String.Empty;
19
}
20
buffer = text.ToCharArray();
21
gapBeginOffset = gapEndOffset = 0;
22
}
23
public void Insert(int offset, string text)
24
{
25
Replace(offset, 0, text);
26
}
27
public void Remove(int offset, int length)
28
{
29
Replace(offset, length, String.Empty);
30
}
31
public void Replace(int offset, int length, string text)
32
{
33
if (text == null)
{
34
text = String.Empty;
35
}
36
37
// Math.Max is used so that if we need to resize the array
38
// the new array has enough space for all old chars
39
PlaceGap(offset + length, Math.Max(text.Length - length, 0));
40
text.CopyTo(0, buffer, offset, text.Length);
41
gapBeginOffset += text.Length - length;
42
}
43
void PlaceGap(int offset, int length)
44
{
45
int deltaLength = GapLength - length;
46
// 如果Gap的长度足够大,则只是作移动相关字符的处理
47
if (minGapLength <= deltaLength && deltaLength <= maxGapLength)
{
48
int delta = gapBeginOffset - offset;
49
// check if the gap is already in place
50
if (offset == gapBeginOffset)
{
51
return;
52
} else if (offset < gapBeginOffset)
{
53
int gapLength = gapEndOffset - gapBeginOffset;
54
Array.Copy(buffer, offset, buffer, offset + gapLength, delta);
55
} else
{ //offset > gapBeginOffset
56
Array.Copy(buffer, gapEndOffset, buffer, gapBeginOffset, -delta);
57
}
58
gapBeginOffset -= delta;
59
gapEndOffset -= delta;
60
return;
61
}
62
63
// 否则,需要新分析buffer的大小,并修改offset等位置数值
64
int oldLength = GapLength;
65
int newLength = maxGapLength + length;
66
int newGapEndOffset = offset + newLength;
67
char[] newBuffer = new char[buffer.Length + newLength - oldLength]; //新分配后将有maxGapLength长度的Gap
68
69
if (oldLength == 0)
{
70
Array.Copy(buffer, 0, newBuffer, 0, offset);
71
Array.Copy(buffer, offset, newBuffer, newGapEndOffset, newBuffer.Length - newGapEndOffset);
72
} else if (offset < gapBeginOffset)
{
73
int delta = gapBeginOffset - offset;
74
Array.Copy(buffer, 0, newBuffer, 0, offset);
75
Array.Copy(buffer, offset, newBuffer, newGapEndOffset, delta);
76
Array.Copy(buffer, gapEndOffset, newBuffer, newGapEndOffset + delta, buffer.Length - gapEndOffset);
77
} else
{
78
int delta = offset - gapBeginOffset;
79
Array.Copy(buffer, 0, newBuffer, 0, gapBeginOffset);
80
Array.Copy(buffer, gapEndOffset, newBuffer, gapBeginOffset, delta);
81
Array.Copy(buffer, gapEndOffset + delta, newBuffer, newGapEndOffset, newBuffer.Length - newGapEndOffset);
82
}
83
84
buffer = newBuffer;
85
gapBeginOffset = offset;
86
gapEndOffset = newGapEndOffset;
87
}
有了基本的数据容器,核心问题已经解决了,但是在绘制界面时直接使用上面的类,显然不够方便,于是就定义了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
1
<SyntaxModes version="1.0">
2
<Mode file = "ASPX.xshd"
3
name = "ASP/XHTML"
4
extensions = ".asp;.aspx;.asax;.asmx"/>
5
6
<Mode file = "BAT-Mode.xshd"
7
name = "BAT"
8
extensions = ".bat"/>
9
10
<Mode file = "CPP-Mode.xshd"
11
name = "C++.NET"
12
extensions = ".c;.h;.cc;.C;.cpp;.hpp"/>
13
14
<Mode file = "CSharp-Mode.xshd"
15
name = "C#"
16
extensions = ".cs"/>
17
18
<Mode file = "XML-Mode.xshd"
19
name = "XML"
20
extensions = ".xml;.xsl;.xslt;.xsd;.manifest;.config;.addin;.xshd;.wxs;.proj;.csproj;.vbproj;.ilproj;.booproj;.build;.xfrm;.targets;.xaml;.xpt;.xft;.map;.wsdl;.disco"/>
21
</SyntaxModes>
取CSharp-Mode.xshd(注:xshd是Xml Syntax Highlighting Definition的缩写)为例,查看其定义:
CSharp-Mode.xshd
1
<?xml version="1.0"?>
2
<SyntaxDefinition name = "C#" extensions = ".cs">
3
<Properties>
4
<Property name="LineComment" value="//"/>
5
</Properties>
6
<Digits name = "Digits" bold = "false" italic = "false" color = "DarkBlue"/>
7
<RuleSets>
8
<RuleSet ignorecase="false">
9
<Delimiters>&<>~!%^*()-+=|/#/{}[]:;"' , .?</Delimiters>
10
<Span name = "PreprocessorDirectives" rule = "PreprocessorSet" bold="false" italic="false" color="Green" stopateol = "true">
11
<Begin>#</Begin>
12
</Span>
13
<Span name = "BlockComment" rule = "CommentMarkerSet" bold = "false" italic = "false" color = "Green" stopateol = "false">
14
<Begin>/*</Begin>
15
<End>*/</End>
16
</Span>
17
<KeyWords name = "Punctuation" bold = "false" italic = "false" color = "DarkGreen">
18
<Key word = "?" />
19
<Key word = "," />
20
<Key word = "." />
21
<Key word = ";" />
22
<Key word = "(" />
23
<Key word = ")" />
24
<Key word = "[" />
25
<Key word = "]" />
26
<Key word = "{" />
27
<Key word = "}" />
28
<Key word = "+" />
29
<Key word = "-" />
30
<Key word = "/" />
31
<Key word = "%" />
32
<Key word = "*" />
33
<Key word = "<" />
34
<Key word = ">" />
35
<Key word = "^" />
36
<Key word = "=" />
37
<Key word = "~" />
38
<Key word = "!" />
39
<Key word = "|" />
40
<Key word = "&" />
41
</KeyWords>
42
43
<KeyWords name = "AccessKeywords" bold="true" italic="false" color="Black">
44
<Key word = "this" />
45
<Key word = "base" />
46
</KeyWords>
47
48
<KeyWords name = "OperatorKeywords" bold="true" italic="false" color="DarkCyan">
49
<Key word = "as" />
50
<Key word = "is" />
51
<Key word = "new" />
52
<Key word = "sizeof" />
53
<Key word = "typeof" />
54
<Key word = "true" />
55
<Key word = "false" />
56
<Key word = "stackalloc" />
57
</KeyWords>
58
</RuleSet>
59
</RuleSets>
60
</SyntaxDefinition>
上面的文件公摘选了部分标签,其中
<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()相关代码
1
//取自TextEditorControlBase.cs
2
public void LoadFile(string fileName, bool autoLoadHighlighting, bool autodetectEncoding)
3
{
4
BeginUpdate();
5
document.TextContent = String.Empty;
6
document.UndoStack.ClearAll();
7
document.BookmarkManager.Clear();
8
if (autoLoadHighlighting)
{
9
//根据文件扩展名判断并赋值高亮显示的策略
10
document.HighlightingStrategy = HighlightingStrategyFactory.CreateHighlightingStrategyForFile(fileName);
11
}
12
13
if (autodetectEncoding)
{
14
Encoding encoding = this.Encoding;
15
//赋值
16
Document.TextContent = Util.FileReader.ReadFileContent(fileName, ref encoding, this.TextEditorProperties.Encoding);
17
this.Encoding = encoding;
18
} else
{
19
using (StreamReader reader = new StreamReader(fileName, this.Encoding))
{
20
Document.TextContent = reader.ReadToEnd();
21
}
22
}
23
24
this.FileName = fileName;
25
OptionsChanged();
26
Document.UpdateQueue.Clear();
27
EndUpdate();
28
29
Refresh();
30
}
31
//引自DefaultDocument.cs
32
ITextBufferStrategy textBufferStrategy = null;
33
ILineManager lineTrackingStrategy = null;
34
public string TextContent
{
35
get
{
36
return GetText(0, textBufferStrategy.Length);
37
}
38
set
{
39
Debug.Assert(textBufferStrategy != null);
40
Debug.Assert(lineTrackingStrategy != null);
41
OnDocumentAboutToBeChanged(new DocumentEventArgs(this, 0, 0, value));
42
//赋值
43
textBufferStrategy.SetContent(value);
44
//赋值 && 分析并完成高亮分析的赋值
45
lineTrackingStrategy.SetContent(value);
46
47
OnDocumentChanged(new DocumentEventArgs(this, 0, 0, value));
48
OnTextContentChanged(EventArgs.Empty);
49
}
50
}
51
//引自DefaultLineManager.cs
52
public void SetContent(string text)
53
{
54
lineCollection.Clear();
55
if (text != null)
{
56
textLength = text.Length;
57
// 生成LineSegment集合
58
CreateLines(text, 0, 0);
59
// 高亮分析
60
RunHighlighter();
61
}
62
}
b, 控件如何响应键盘事件?
答:对于方向键及快捷功能键通过预定义的实现IEditAction接口的类响应(执行功能,不影响字符内容);其它字母/数字键直接输入,同时执行更新Folding, Bookmark, Higlighting等属性信息。相关代码:
键盘事件响应
1
//功能键的定义(引自TextEditorControlBase.cs):
2
protected Dictionary<Keys, IEditAction> editactions = new Dictionary<Keys, IEditAction>();
3
protected TextEditorControlBase()
4
{
5
GenerateDefaultActions();
6
HighlightingManager.Manager.ReloadSyntaxHighlighting += new EventHandler(ReloadHighlighting);
7
}
8
void GenerateDefaultActions()
9
{
10
editactions[Keys.Left] = new CaretLeft();
11
editactions[Keys.Left | Keys.Shift] = new ShiftCaretLeft();
12
editactions[Keys.Left | Keys.Control] = new WordLeft();
13
editactions[Keys.Left | Keys.Control | Keys.Shift] = new ShiftWordLeft();
14
editactions[Keys.Right] = new CaretRight();
15
editactions[Keys.Right | Keys.Shift] = new ShiftCaretRight();
16
editactions[Keys.Right | Keys.Control] = new WordRight();
17
editactions[Keys.Right | Keys.Control | Keys.Shift] = new ShiftWordRight();
18
editactions[Keys.Up] = new CaretUp();
19
editactions[Keys.Up | Keys.Shift] = new ShiftCaretUp();
20
editactions[Keys.Up | Keys.Control] = new ScrollLineUp();
21
editactions[Keys.Down] = new CaretDown();
22
editactions[Keys.Down | Keys.Shift] = new ShiftCaretDown();
23
editactions[Keys.Down | Keys.Control] = new ScrollLineDown();
24
25
editactions[Keys.Insert] = new ToggleEditMode();
26
editactions[Keys.Insert | Keys.Control] = new Copy();
27
editactions[Keys.Insert | Keys.Shift] = new Paste();
28
editactions[Keys.Delete] = new Delete();
29
editactions[Keys.Delete | Keys.Shift] = new Cut();
30
editactions[Keys.Home] = new Home();
31
editactions[Keys.Home | Keys.Shift] = new ShiftHome();
32
editactions[Keys.Home | Keys.Control] = new MoveToStart();
33
editactions[Keys.Home | Keys.Control | Keys.Shift] = new ShiftMoveToStart();
34
editactions[Keys.End] = new End();
35
editactions[Keys.End | Keys.Shift] = new ShiftEnd();
36
editactions[Keys.End | Keys.Control] = new MoveToEnd();
37
editactions[Keys.End | Keys.Control | Keys.Shift] = new ShiftMoveToEnd();
38
editactions[Keys.PageUp] = new MovePageUp();
39
editactions[Keys.PageUp | Keys.Shift] = new ShiftMovePageUp();
40
editactions[Keys.PageDown] = new MovePageDown();
41
editactions[Keys.PageDown | Keys.Shift] = new ShiftMovePageDown();
42
43
editactions[Keys.Return] = new Return();
44
editactions[Keys.Tab] = new Tab();
45
editactions[Keys.Tab | Keys.Shift] = new ShiftTab();
46
editactions[Keys.Back] = new Backspace();
47
editactions[Keys.Back | Keys.Shift] = new Backspace();
48
49
editactions[Keys.X | Keys.Control] = new Cut();
50
editactions[Keys.C | Keys.Control] = new Copy();
51
editactions[Keys.V | Keys.Control] = new Paste();
52
53
editactions[Keys.A | Keys.Control] = new SelectWholeDocument();
54
editactions[Keys.Escape] = new ClearAllSelections();
55
56
editactions[Keys.Divide | Keys.Control] = new ToggleComment();
57
editactions[Keys.OemQuestion | Keys.Control] = new ToggleComment();
58
59
editactions[Keys.Back | Keys.Alt] = new Actions.Undo();
60
editactions[Keys.Z | Keys.Control] = new Actions.Undo();
61
editactions[Keys.Y | Keys.Control] = new Redo();
62
63
editactions[Keys.Delete | Keys.Control] = new DeleteWord();
64
editactions[Keys.Back | Keys.Control] = new WordBackspace();
65
editactions[Keys.D | Keys.Control] = new DeleteLine();
66
editactions[Keys.D | Keys.Shift | Keys.Control] = new DeleteToLineEnd();
67
68
editactions[Keys.B | Keys.Control] = new GotoMatchingBrace();
69
}
70
internal IEditAction GetEditAction(Keys keyData)
71
{
72
if (!editactions.ContainsKey(keyData))
{
73
return null;
74
}
75
return (IEditAction)editactions[keyData];
76
}
77
//功能键的响应(取自TextArea.cs):
78
protected override bool ProcessDialogKey(Keys keyData)
79
{
80
return ExecuteDialogKey(keyData) || base.ProcessDialogKey(keyData);
81
}
82
public bool ExecuteDialogKey(Keys keyData)
83
{
84
// try, if a dialog key processor was set to use this
85
if (DoProcessDialogKey != null && DoProcessDialogKey(keyData))
{
86
return true;
87
}
88
89
if (keyData == Keys.Back || keyData == Keys.Delete || keyData == Keys.Enter)
{
90
if (TextEditorProperties.UseCustomLine == true)
{
91
if (SelectionManager.HasSomethingSelected)
{
92
if (Document.CustomLineManager.IsReadOnly(SelectionManager.SelectionCollection[0], false))
93
return true;
94
} else
{
95
int curLineNr = Document.GetLineNumberForOffset(Caret.Offset);
96
if (Document.CustomLineManager.IsReadOnly(curLineNr, false) == true)
97
return true;
98
if ((Caret.Column == 0) && (curLineNr - 1 >= 0) && keyData == Keys.Back &&
99
Document.CustomLineManager.IsReadOnly(curLineNr - 1, false) == true)
100
return true;
101
if (keyData == Keys.Delete)
{
102
LineSegment curLine = Document.GetLineSegment(curLineNr);
103
if (curLine.Offset + curLine.Length == Caret.Offset &&
104
Document.CustomLineManager.IsReadOnly(curLineNr + 1, false) == true)
{
105
return true;
106
}
107
}
108
}
109
}
110
}
111
112
// if not (or the process was 'silent', use the standard edit actions
113
IEditAction action = motherTextEditorControl.GetEditAction(keyData);
114
AutoClearSelection = true;
115
if (action != null)
{
116
motherTextEditorControl.BeginUpdate();
117
try
{
118
lock (Document)
{
119
// 执行相关的功能操作
120
action.Execute(this);
121
if (SelectionManager.HasSomethingSelected && AutoClearSelection /**//*&& caretchanged*/)
{
122
if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal)
{
123
SelectionManager.ClearSelection();
124
}
125
}
126
}
127
} finally
{
128
motherTextEditorControl.EndUpdate();
129
Caret.UpdateCaretPosition();
130
}
131
return true;
132
}
133
return false;
134
}
135
136
//输入键的响应(取自TextArea.cs):
137
protected override void OnKeyPress(KeyPressEventArgs e)
138
{
139
base.OnKeyPress(e);
140
SimulateKeyPress(e.KeyChar);
141
e.Handled = true;
142
}
143
public void SimulateKeyPress(char ch)
144
{
145
if (Document.ReadOnly)
{
146
return;
147
}
148
149
if (TextEditorProperties.UseCustomLine == true)
{
150
if (SelectionManager.HasSomethingSelected)
{
151
if (Document.CustomLineManager.IsReadOnly(SelectionManager.SelectionCollection[0], false))
152
return;
153
} else if (Document.CustomLineManager.IsReadOnly(Caret.Line, false) == true)
154
return;
155
}
156
157
if (ch < ' ')
{
158
return;
159
}
160
161
if (!HiddenMouseCursor && TextEditorProperties.HideMouseCursor)
{
162
HiddenMouseCursor = true;
163
Cursor.Hide();
164
}
165
CloseToolTip();
166
167
motherTextEditorControl.BeginUpdate();
168
// INSERT char
169
if (!HandleKeyPress(ch))
{
170
switch (Caret.CaretMode)
{
171
case CaretMode.InsertMode:
172
InsertChar(ch);
173
break;
174
case CaretMode.OverwriteMode:
175
ReplaceChar(ch);
176
break;
177
default:
178
Debug.Assert(false, "Unknown caret mode " + Caret.CaretMode);
179
break;
180
}
181
}
182
183
int currentLineNr = Caret.Line;
184
int delta = Document.FormattingStrategy.FormatLine(this, currentLineNr, Document.PositionToOffset(Caret.Position), ch);
185
186
motherTextEditorControl.EndUpdate();
187
if (delta != 0)
{
188
// this.motherTextEditorControl.UpdateLines(currentLineNr, currentLineNr);
189
}
190
}
191
//输入键通过底层的方法输入字符和更改高亮显示,如下分析(以Insert为例,取自DefaultDocument.cs):
192
public void Insert(int offset, string text)
193
{
194
if (readOnly)
{
195
return;
196
}
197
OnDocumentAboutToBeChanged(new DocumentEventArgs(this, offset, -1, text));
198
DateTime time = DateTime.Now;
199
//增加字符
200
textBufferStrategy.Insert(offset, text);
201
202
time = DateTime.Now;
203
//更新LineSegment, TextWord对象
204
lineTrackingStrategy.Insert(offset, text);
205
206
time = DateTime.Now;
207
208
undoStack.Push(new UndoableInsert(this, offset, text));
209
210
time = DateTime.Now;
211
OnDocumentChanged(new DocumentEventArgs(this, offset, -1, text));
212
}
213
// 追踪到DefaultLineManager.cs中Insert()方法实际上调用到Replace(offset,length,string.Empty)方法:
214
public void Replace(int offset, int length, string text)
215
{
216
int lineNumber = GetLineNumberForOffset(offset);
217
int insertLineNumber = lineNumber;
218
if (Remove(lineNumber, offset, length))
{
219
--lineNumber;
220
}
221
222
lineNumber += Insert(insertLineNumber, offset, text);
223
224
int delta = -length;
225
if (text != null)
{
226
delta = text.Length + delta;
227
}
228
229
if (delta != 0)
{
230
AdaptLineOffsets(lineNumber, delta);
231
}
232
//启用高亮显示分析 ----- 注:此时的 markLines 仅存储变化的相关行,而SetContent时存储的是所有行,因此只是按需分析
233
RunHighlighter();
234
}
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()方法中搜索并更新匹配项的显示,相关代码如下:
括号匹配
1
//引自TextArea.cs
2
List<BracketHighlightingSheme> bracketshemes = new List<BracketHighlightingSheme>();
3
Caret caret;
4
public TextArea(TextEditorControl motherTextEditorControl, TextAreaControl motherTextAreaControl)
5
{
6
// 省略无关代码
7
bracketshemes.Add(new BracketHighlightingSheme('{', '}'));
8
bracketshemes.Add(new BracketHighlightingSheme('(', ')'));
9
bracketshemes.Add(new BracketHighlightingSheme('[', ']'));
10
11
caret.PositionChanged += new EventHandler(SearchMatchingBracket);
12
// 省略无关代码
13
}
14
void SearchMatchingBracket(object sender, EventArgs e)
15
{
16
if (!TextEditorProperties.ShowMatchingBracket)
{
17
textView.Highlight = null;
18
return;
19
}
20
bool changed = false;
21
if (caret.Offset == 0)
{
22
if (textView.Highlight != null)
{
23
int line = textView.Highlight.OpenBrace.Y;
24
int line2 = textView.Highlight.CloseBrace.Y;
25
textView.Highlight = null;
26
UpdateLine(line);
27
UpdateLine(line2);
28
}
29
return;
30
}
31
foreach (BracketHighlightingSheme bracketsheme in bracketshemes)
{
32
// if (bracketsheme.IsInside(textareapainter.Document, textareapainter.Document.Caret.Offset)) {
33
Highlight highlight = bracketsheme.GetHighlight(Document, Caret.Offset - 1);
34
if (textView.Highlight != null && textView.Highlight.OpenBrace.Y >=0 && textView.Highlight.OpenBrace.Y < Document.TotalNumberOfLines)
{
35
//取消旧匹配项的高亮显示
36
UpdateLine(textView.Highlight.OpenBrace.Y);
37
}
38
if (textView.Highlight != null && textView.Highlight.CloseBrace.Y >=0 && textView.Highlight.CloseBrace.Y < Document.TotalNumberOfLines)
{
39
//取消旧匹配项的高亮显示
40
UpdateLine(textView.Highlight.CloseBrace.Y);
41
}
42
textView.Highlight = highlight;
43
if (highlight != null)
{
44
changed = true;
45
break;
46
}
47
// }
48
}
49
if (changed || textView.Highlight != null)
{
50
int line = textView.Highlight.OpenBrace.Y;
51
int line2 = textView.Highlight.CloseBrace.Y;
52
if (!changed)
{
53
textView.Highlight = null;
54
}
55
//更新显示新匹配项 OpenBrace
56
UpdateLine(line);
57
//更新显示新匹配项 CloseBrace
58
UpdateLine(line2);
59
}
60
}
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章后作进一步分析
相关文章推荐
- [转]SharpDevelop浅析_3_文档编辑器、语法高亮显示
- 使用ICSharpCode.TextEditor制作一个语法高亮显示的XML编辑器
- 使用ICSharpCode.TextEditor制作一个语法高亮显示的XML编辑器
- 使用ICSharpCode.TextEditor制作一个语法高亮显示的XML编辑器
- 使用ICSharpCode.TextEditor制作一个语法高亮显示的XML编辑器
- 关于XmlTextWriter的 "处于状态 Epilog 的标记 StartElement 将导致无效的 XML 文档"报错原因
- php显示乱码的处理header("Content-type:text/html;charset=utf-8");
- struts2 访问国际化资源 <s:text>作为属性
- Android的TextView/EditText使用CharacterStyle&SpannableString来处理图片显示、字体样式、超链接等
- textField Length = 0 不显示 length > 0 显示
- 允许Python文档中显示中文 & 允许SublimeText2输入中文 & 允许Python输出unicode字符
- 利用ICSharpCode.TextEditor实现多文件类型的文本语法高亮
- 原来设置ICSharpCode.TextEditor语法高亮这么简单
- MSDN帮助文档 "无法显示该网页" 的问题解决方案(转)
- Android 图标自适应:用 TextView 来显示图片 & 生成自己的字体库
- Android的TextView/EditText使用CharacterStyle&SpannableString来处理图片显示、字体样式、超链接等
- JSF标签 h:outputText & f:convertDateTime 解决date正常显示的问题
- vim显示行号、语法高亮、自动缩进的设置 转自:http://blog.chinaunix.net/space.php?uid=20629471&do=blog&cuid=407799
- android定位已获取经纬度,上传google解析后textview不显示,基础问题,求大神解答~~>_<`
- Unity3D Editor 编辑器扩展2 选取物体、撤销操作和窗口小部件的显示