SharpDevelop浅析_4_TextEditor_自动完成、代码折叠……
Parser及其应用: Code Completion, Method Insight, Class Scout ...
1、Demo界面及功能解释
2、Parser实现概述
3、Parser应用: MouseHover Tooltip
4、Parser应用: CodeCompletion & MethodInsight
5、Parser应用: QuickClassBrowserPanel
6、Parser应用: Folding
7、总结
Demo下载
1、Demo界面及功能解释
启动并打开任一 .cs 文件后,界面如下:

自动完成界面如下:

可见新增功能如下(仅支持.cs文件):
a, 鼠标停留在方法、属性等位置时,会显示出相关的文档描述tooltip
b, 输入时支持自动完成
c, 编辑窗口顶部有类列表和成员(方法、变量等)列表下拉框用以快速浏览、定位
d, 编辑窗口左侧有折叠线用以方法、类等的代码折叠
相应的Demo工程中新增项目如下:
a, SharpEditor: 包含扩展TextEditor的控件, Dom结构, ParserService, 自动完成功能代码等
b, NRefactor : 代码解析功能
c, CSharpBinding: 对应 .cs 文件的具体实现支持
[题外话]:
关于代码解析(Parser)相关的代码,我没看懂,所以在这里只说个大概,更多地谈谈Parser的使用;抛砖引玉,希望有相关经验的网友提供详尽的分析。
前两周工作上的项目实施,每天都搞得比较累,所以这篇文章到现在才写了出来,明天是大年三十了,这个系列的文章也只剩下一篇Windows Form Designer,只能等过了年再放上来喽。
另外,这个系列写完后,暂不打算深究一些没明白的细节,接下来想看下os workflow 或 CommunityServer...
2、Parser实现概述
(1)首先,SharpEditor项目中的Dom下定义了以下重要类:
a, IDecoration及其子类: 代码表现的辅助对象,如IClass, IMethod, IProperty等
b, ResolveResult及其子类: 分析结果对象,如MethodResolveResult, TypeResolveResult等
c, 其它重要类: IExpressionFinder, IParser, IResolve, ICompilationUnit 等
(2)重要服务类:
ParserService: 提供 GetExpressionFinder(), Resolve(), ParseFile()等重要方法,相关重要类: ProjectContentRegistry, DefaultProjectContent, ReflectionProjectContent等
AmbienceService: 提供 IAmbience的实现类用以将分析结果转换为相应的字符描述
(3)Parser分析步骤:
以鼠标悬浮的Tooltip显示为例:DebuggerService根据文件类型返回对应的IExpressionFinder实现类,再根据鼠标位置找到并返回ExpressionResult对象,然后找到适当的IResolver实现类调用Resolve()方法返回结果ResolveResult对象,最后由相应的IAmbience实现类转换成结果字符,并调用e.ShowToolTip(toolTipText);显示。
(4)对于.NET默认类库的分析转换:
默认引进的命名空间的类结构和文档说明一般可以在"C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/"目录下找到(如System.dll和System.xml),但是如果每次都要重新分析dll代码结构和xml注释显然是比较花费时间的,于是SharpDevelop采用的方式是将分析过的数据(Dom下的类结构表示数据?,二进制的.dat文件)存储到"C:/Documents and Settings/michael/Local Settings/Temp/SharpDevelop"下,代码结构存储到DomCacheDebug目录下,文档注释存储到DocumentationCacheDebug目录下。
首先在ParserService的CreateDefaultProjectContent()中加载默认命名空间的引用:
CreateDefaultProjectContent()
1
static void CreateDefaultProjectContent()
2

{
3
LoggingService.Info("Creating default project content");
4
LoggingService.Debug("Stacktrace is:/n" + Environment.StackTrace);
5
defaultProjectContent = new DefaultProjectContent();
6
defaultProjectContent.ReferencedContents.Add(ProjectContentRegistry.Mscorlib);
7
string[] defaultReferences = new string[]
{
8
"System",
9
"System.Data",
10
"System.Drawing",
11
"System.Windows.Forms",
12
"System.XML",
13
"Microsoft.VisualBasic",
14
};
15
foreach (string defaultReference in defaultReferences)
16
{
17
//ReferenceProjectItem item = new ReferenceProjectItem(null, defaultReference);
18
IProjectContent pc = ProjectContentRegistry.GetProjectContentForReference(defaultReference);
19
if (pc != null)
20
{
21
defaultProjectContent.ReferencedContents.Add(pc);
22
}
23
}
24
}
其中ProjectContentRegistry的GetProjectContentForReference()方法如下:
GetProjectContentForReference()
1
public static IProjectContent GetProjectContentForReference(string Include)
2

{
3
// 省略部分代码
4
Assembly assembly = GetDefaultAssembly(shortName);
5
ReflectionProjectContent pc;
6
if (assembly != null)
7
{
8
pc = DomPersistence.LoadProjectContentByAssemblyName(assembly.FullName);
9
if (pc == null)
10
{
11
pc = new ReflectionProjectContent(assembly);
12
DomPersistence.SaveProjectContent(pc);
13
}
14
}
15
else
16
{
17
pc = LoadProjectContent(itemFileName, itemInclude);
18
}
19
// 省略部分代码
20
return pc;
21
}
22
static Assembly GetDefaultAssembly(string shortName)
23

{
24
// These assemblies are already loaded by SharpDevelop, so we don't need to load
25
// them in a separate AppDomain.
26
switch (shortName)
{
27
case "System": // System != mscorlib !!!
28
return SystemAssembly;
29
case "System.Data":
30
return typeof(System.Data.DataException).Assembly;
31
case "System.Design":
32
return typeof(System.ComponentModel.Design.DesignSurface).Assembly;
33
case "System.DirectoryServices":
34
return typeof(System.DirectoryServices.AuthenticationTypes).Assembly;
35
case "System.Drawing":
36
return typeof(System.Drawing.Color).Assembly;
37
case "System.Web.Services":
38
return typeof(System.Web.Services.WebService).Assembly;
39
case "System.Windows.Forms":
40
return typeof(System.Windows.Forms.Control).Assembly;
41
case "System.Xml":
42
case "System.XML":
43
return typeof(XmlReader).Assembly;
44
case "Microsoft.Build.Engine":
45
return typeof(Microsoft.Build.BuildEngine.BuildSettings).Assembly;
46
case "Microsoft.Build.Framework":
47
return typeof(Microsoft.Build.Framework.LoggerVerbosity).Assembly;
48
default:
49
return null;
50
}
51
}
可以看到DomPersistence类的作用即在加载或保存dll的代码结构数据, 如果尚未有分析过的数据,则在ReflectionProjectContnet类的构造函数中加以分析,同时调用XmlDoc类的相关方法加载、保存文档注释数据。
(5)对于文件的转换:
[略]
3、Parser应用: MouseHover Tooltip
注意在SharpEditor项目中有个DebuggerService类,它提供了一个重要方法如下:
BindTextAreaEvent()
1
public static void BindTextAreaEvent(TextEditorControl control)
2

{
3
TextArea textArea = control.ActiveTextAreaControl.TextArea;
4
//textArea.IconBarMargin.MouseDown += IconBarMouseDown;
5
textArea.ToolTipRequest -= TextArea_ToolTipRequest;
6
textArea.ToolTipRequest += TextArea_ToolTipRequest;
7
}
此方法即用以绑定需要鼠标悬浮提示的TextEditor控件,在SharpPad项目的Open菜单类方法中调用此类绑定编辑控件。
注意上面的方法中可以看到ICSharpCode.TextEditor控件是通过其TextArea的ToolTipRequest事件公开给外部,用以分析并由外部提供ToolTip数据,而TextArea_ToolTipRequest(object sender, ToolTipRequestEventArgs e)方法中根据鼠标位置分析出要显示的数据后,最终调用e.ShowToolTip(toolTipText);用以设置提供数据。
TextArea_ToolTipRequest()
1
static void TextArea_ToolTipRequest(object sender, ToolTipRequestEventArgs e)
2

{
3
try
4
{
5
TextArea textArea = (TextArea)sender;
6
if (e.ToolTipShown) return;
7
if (oldToolTipControl != null && !oldToolTipControl.AllowClose) return;
8
if (!CodeCompletionOptions.TooltipsEnabled) return;
9
10
if (CodeCompletionOptions.TooltipsOnlyWhenDebugging)
11
{
12
if (currentDebugger == null) return;
13
if (!currentDebugger.IsDebugging) return;
14
}
15
16
if (e.InDocument)
17
{
18
Point logicPos = e.LogicalPosition;
19
IDocument doc = textArea.Document;
20
IExpressionFinder expressionFinder = ParserService.GetExpressionFinder(textArea.MotherTextEditorControl.FileName);
21
if (expressionFinder == null)
22
return;
23
LineSegment seg = doc.GetLineSegment(logicPos.Y);
24