使用Parser,扩充了上一篇文章的Demo,增加了代码折叠、自动完成等功能,主要内容如下:
1、Demo界面及功能解释
2、Parser实现概述
3、Parser应用: MouseHover Tooltip
4、Parser应用: CodeCompletion & MethodInsight
5、Parser应用: QuickClassBrowserPanel
6、Parser应用: Folding
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
if (logicPos.X > seg.Length - 1)
25
return;
26
string textContent = doc.TextContent;
27
ExpressionResult expressionResult = expressionFinder.FindFullExpression(textContent, seg.Offset + logicPos.X);
28
string expression = expressionResult.Expression;
29
if (expression != null && expression.Length > 0)
30
{
31
// Look if it is variable
32
ResolveResult result = ParserService.Resolve(expressionResult, logicPos.Y + 1, logicPos.X + 1, textArea.MotherTextEditorControl.FileName, textContent);
33
bool debuggerCanShowValue;
34
string toolTipText = GetText(result, expression, out debuggerCanShowValue);
35
DebuggerGridControl toolTipControl = null;
36
if (toolTipText != null)
37
{
38
if (Control.ModifierKeys == Keys.Control)
39
{
40
toolTipText = "expr: " + expressionResult.ToString() + "\n" + toolTipText;
41
}
42
else if (debuggerCanShowValue && currentDebugger != null)
43
{
44
toolTipControl = currentDebugger.GetTooltipControl(expressionResult.Expression);
45
toolTipText = null;
46
}
47
}
48
if (toolTipText != null)
49
{
50
e.ShowToolTip(toolTipText);
51
}
52
if (oldToolTipControl != null)
53
{
54
Form frm = oldToolTipControl.FindForm();
55
if (frm != null) frm.Close();
56
}
57
if (toolTipControl != null)
58
{
59
toolTipControl.ShowForm(textArea, logicPos);
60
}
61
oldToolTipControl = toolTipControl;
62
}
63
}
64
}
65
catch (Exception ex)
66
{
67
MessageBox.Show(ex.ToString());
68
}
69
}
4、Parser应用: CodeCompletion & MethodInsight
ICSharpCode.TextEditor控件使用ICompletionData类以及ICompletionDataProvider接口(GenerateCompletionData()方法)定义数据自动完成列表的数据及其提供者: SharpEditor项目中的AbstractCodeCompletionDataProvider类中由GetExpression()及AddResolveResults()两个辅助方法生成数据,代码如下:

AbstractCodeCompletionDataProvider 类
1
protected ArrayList completionData = null;
2
// 
3
protected ExpressionResult GetExpression(TextArea textArea)
4

{
5
IDocument document = textArea.Document;
6
IExpressionFinder expressionFinder = ParserService.GetExpressionFinder(fileName);
7
if (expressionFinder == null)
{
8
return new ExpressionResult(TextUtilities.GetExpressionBeforeOffset(textArea, textArea.Caret.Offset));
9
} else
{
10
ExpressionResult res = expressionFinder.FindExpression(document.GetText(0, textArea.Caret.Offset), textArea.Caret.Offset - 1);
11
if (overrideContext != null)
12
res.Context = overrideContext;
13
return res;
14
}
15
}
16
protected void AddResolveResults(ResolveResult results, ExpressionContext context)
17

{
18
insertedElements.Clear();
19
insertedPropertiesElements.Clear();
20
insertedEventElements.Clear();
21
22
if (results != null)
{
23
AddResolveResults(results.GetCompletionData(ParserService.CurrentProjectContent), context);
24
}
25
}
26
protected void AddResolveResults(ICollection list, ExpressionContext context)
27

{
28
if (list == null)
{
29
return;
30
}
31
completionData.Capacity += list.Count;
32
CodeCompletionData suggestedData = null;
33
foreach (object o in list)
{
34
if (context != null && !context.ShowEntry(o))
35
continue;
36
CodeCompletionData ccd = CreateItem(o, context);
37
if (object.Equals(o, context.SuggestedItem))
38
suggestedData = ccd;
39
if (ccd != null && !ccd.Text.StartsWith("___"))
40
completionData.Add(ccd);
41
}
42
if (context.SuggestedItem != null)
{
43
if (suggestedData == null)
{
44
suggestedData = CreateItem(context.SuggestedItem, context);
45
if (suggestedData != null)
{
46
completionData.Add(suggestedData);
47
}
48
}
49
if (suggestedData != null)
{
50
completionData.Sort();
51
this.DefaultIndex = completionData.IndexOf(suggestedData);
52
}
53
}
54
}
CommentCompletionDataProvider类提供注释的自动完成,相关代码如下:

CommentCompletionDataProvider 类
1
string[][] commentTags = new string[][]
{
2
new string[]
{"c", "marks text as code"},
3
new string[]
{"code", "marks text as code"},
4
new string[]
{"example", "A description of the code example\n(must have a <code> tag inside)"},
5
new string[]
{"exception cref=\"\"", "description to an exception thrown"},
6
new string[]
{"list type=\"\"", "A list"},
7
new string[]
{"listheader", "The header from the list"},
8
new string[]
{"item", "A list item"},
9
new string[]
{"term", "A term in a list"},
10
new string[]
{"description", "A description to a term in a list"},
11
new string[]
{"para", "A text paragraph"},
12
new string[]
{"param name=\"\"", "A description for a parameter"},
13
new string[]
{"paramref name=\"\"", "A reference to a parameter"},
14
new string[]
{"permission cref=\"\"", ""},
15
new string[]
{"remarks", "Gives description for a member"},
16
new string[]
{"include file=\"\" path=\"\"", "Includes comments from other files"},
17
new string[]
{"returns", "Gives description for a return value"},
18
new string[]
{"see cref=\"\"", "A reference to a member"},
19
new string[]
{"seealso cref=\"\"", "A reference to a member in the seealso section"},
20
new string[]
{"summary", "A summary of the object"},
21
new string[]
{"value", "A description of a property"}
22
};
23
public override ICompletionData[] GenerateCompletionData(string fileName, TextArea textArea, char charTyped)
24

{
25
caretLineNumber = textArea.Caret.Line;
26
caretColumn = textArea.Caret.Column;
27
LineSegment caretLine = textArea.Document.GetLineSegment(caretLineNumber);
28
string lineText = textArea.Document.GetText(caretLine.Offset, caretLine.Length);
29
if (!lineText.Trim().StartsWith("///") && !lineText.Trim().StartsWith("'''"))
{
30
return null;
31
}
32
33
ArrayList completionData = new ArrayList();
34
foreach (string[] tag in commentTags)
{
35
completionData.Add(new CommentCompletionData(tag[0], tag[1]));
36
}
37
return (ICompletionData[])completionData.ToArray(typeof(ICompletionData));
38
}
类似地,ICSharpCode.TextEditor控件提供了IInsightDataProvider接口(GetInsightData()方法),SharpEditor项目中实现类MethodInsightDataProvider和IndexerInsightDataProvider提供相关数据。
除了上面需要提供数据的类外,还要绑定TextEditor的TextArea的KeyEventHandler事件(详见SharpEditor项目的SharpDevelopTextAreaControl.cs),其中辅助类的处理代码如下:

KeyEventHandler 事件响应
1
// 默认处理 DefaultCodeCompletionBinding(详见SharpEditor项目的CodeCompletionBinding.cs):
2
public virtual bool HandleKeyPress(SharpDevelopTextAreaControl editor, char ch)
3

{
4
switch (ch)
5
{
6
case '(':
7
if (enableMethodInsight && CodeCompletionOptions.InsightEnabled)
8
{
9
editor.ShowInsightWindow(new MethodInsightDataProvider());
10
return true;
11
}
12
else
13
{
14
return false;
15
}
16
case '[':
17
if (enableIndexerInsight && CodeCompletionOptions.InsightEnabled)
18
{
19
editor.ShowInsightWindow(new IndexerInsightDataProvider());
20
return true;
21
}
22
else
23
{
24
return false;
25
}
26
case '<':
27
if (enableXmlCommentCompletion)
28
{
29
editor.ShowCompletionWindow(new CommentCompletionDataProvider(), ch);
30
return true;
31
}
32
else
33
{
34
return false;
35
}
36
case '.':
37
if (enableDotCompletion)
38
{
39
editor.ShowCompletionWindow(new CodeCompletionDataProvider(), ch);
40
return true;
41
}
42
else
43
{
44
return false;
45
}
46
case ' ':
47
if (!CodeCompletionOptions.KeywordCompletionEnabled)
48
return false;
49
string word = editor.GetWordBeforeCaret();
50
if (word != null)
51
return HandleKeyword(editor, word);
52
else
53
return false;
54
default:
55
return false;
56
}
57
}
58
// .cs 文件的处理(详见CSharpBinding项目的CSharpCompletionBinding.cs):
59
public override bool HandleKeyPress(SharpDevelopTextAreaControl editor, char ch)
60

{
61
Parser.ExpressionFinder ef = new Parser.ExpressionFinder(editor.FileName);
62
int cursor = editor.ActiveTextAreaControl.Caret.Offset;
63
ExpressionContext context = null;
64
if (ch == '(')
65
{
66
if (CodeCompletionOptions.KeywordCompletionEnabled)
67
{
68
switch (editor.GetWordBeforeCaret().Trim())
69
{
70
case "for":
71
case "lock":
72
context = ExpressionContext.Default;
73
break;
74
case "using":
75
context = ExpressionContext.TypeDerivingFrom(ReflectionReturnType.Disposable.GetUnderlyingClass(), false);
76
break;
77
case "catch":
78
context = ExpressionContext.TypeDerivingFrom(ReflectionReturnType.Exception.GetUnderlyingClass(), false);
79
break;
80
case "foreach":
81
case "typeof":
82
case "sizeof":
83
case "default":
84
context = ExpressionContext.Type;
85
break;
86
}
87
}
88
if (context != null)
89
{
90
if (IsInComment(editor)) return false;
91
editor.ShowCompletionWindow(new CtrlSpaceCompletionDataProvider(context), ch);
92
return true;
93
}
94
else if (EnableMethodInsight && CodeCompletionOptions.InsightEnabled)
95
{
96
editor.ShowInsightWindow(new MethodInsightDataProvider());
97
return true;
98
}
99
return false;
100
}
101
else if (ch == '[')
102
{
103
LineSegment line = editor.Document.GetLineSegmentForOffset(cursor);
104
if (TextUtilities.FindPrevWordStart(editor.Document, cursor) <= line.Offset)
105
{
106
// [ is first character on the line
107
// -> Attribute completion
108
editor.ShowCompletionWindow(new AttributesDataProvider(), ch);
109
return true;
110
}
111
}
112
else if (ch == ',' && CodeCompletionOptions.InsightRefreshOnComma && CodeCompletionOptions.InsightEnabled)
113
{
114
// Show MethodInsightWindow or IndexerInsightWindow
115
string documentText = editor.Text;
116
int oldCursor = cursor;
117
string textWithoutComments = ef.FilterComments(documentText, ref cursor);
118
int commentLength = oldCursor - cursor;
119
if (textWithoutComments != null)
120
{
121
Stack<ResolveResult> parameters = new Stack<ResolveResult>();
122
char c = '\0';
123
while (cursor > 0)
124
{
125
while (--cursor > 0 &&
126
((c = textWithoutComments[cursor]) == ',' ||
127
char.IsWhiteSpace(c))) ;
128
if (c == '(')
129
{
130
ShowInsight(editor, new MethodInsightDataProvider(cursor + commentLength, true), parameters, ch);
131
return true;
132
}
133
else if (c == '[')
134
{
135
ShowInsight(editor, new IndexerInsightDataProvider(cursor + commentLength, true), parameters, ch);
136
return true;
137
}
138
string expr = ef.FindExpressionInternal(textWithoutComments, cursor);
139
if (expr == null || expr.Length == 0)
140
break;
141
parameters.Push(ParserService.Resolve(new ExpressionResult(expr),
142
editor.ActiveTextAreaControl.Caret.Line,
143
editor.ActiveTextAreaControl.Caret.Column,
144
editor.FileName,
145
documentText));
146
cursor = ef.LastExpressionStartPosition;
147
}
148
}
149
}
150
else if (ch == '=')
151
{
152
LineSegment curLine = editor.Document.GetLineSegmentForOffset(cursor);
153
string documentText = editor.Text;
154
int position = editor.ActiveTextAreaControl.Caret.Offset - 2;
155
156
if (position > 0 && (documentText[position + 1] == '+'))
157
{
158
ExpressionResult result = ef.FindFullExpression(documentText, position);
159
160
if (result.Expression != null)
161
{
162
ResolveResult resolveResult = ParserService.Resolve(result, editor.ActiveTextAreaControl.Caret.Line, editor.ActiveTextAreaControl.Caret.Column, editor.FileName, documentText);
163
if (resolveResult != null && resolveResult.ResolvedType != null)
164
{
165
IClass underlyingClass = resolveResult.ResolvedType.GetUnderlyingClass();
166
if (underlyingClass != null && underlyingClass.IsTypeInInheritanceTree(ProjectContentRegistry.Mscorlib.GetClass("System.MulticastDelegate")))
167
{
168
EventHandlerCompletitionDataProvider eventHandlerProvider = new EventHandlerCompletitionDataProvider(result.Expression, resolveResult);
169
eventHandlerProvider.InsertSpace = true;
170
editor.ShowCompletionWindow(eventHandlerProvider, ch);
171
}
172
}
173
}
174
}
175
}
176
else if (ch == ';')
177
{
178
LineSegment curLine = editor.Document.GetLineSegmentForOffset(cursor);
179
// don't return true when inference succeeds, otherwise the ';' won't be added to the document.
180
TryDeclarationTypeInference(editor, curLine);
181
}
182
183
return base.HandleKeyPress(editor, ch);
184
}
5、Parser应用: QuickClassBrowserPanel
QuickClassBrowserPanel即编辑窗口顶部的类及成员快速浏览窗口,其实现如下:
首先在SharpDevelopTextAreaControl类中重写override void OnFileNameChanged(EventArgs e)方法,即打开新文件时,调用ActivateQuickClassBrowserOnDemand()来根据是否有能够分析此文件的Parser来决定是否显示控件,如果可以显示,则实例化出一个新对象,并添加进来。实例化QuickClassBrowserPanel对象的相关代码如下:

QuickClassBrowserPanel
1
private System.Windows.Forms.ComboBox classComboBox;
2
private System.Windows.Forms.ComboBox membersComboBox;
3
ICompilationUnit currentCompilationUnit;
4
// 
5
public QuickClassBrowserPanel(SharpDevelopTextAreaControl textAreaControl)
6

{
7
InitializeComponent();
8
this.membersComboBox.MaxDropDownItems = 20;
9
10
base.Dock = DockStyle.Top;
11
this.textAreaControl = textAreaControl;
12
this.textAreaControl.ActiveTextAreaControl.Caret.PositionChanged += new EventHandler(CaretPositionChanged);
13
}
14
void CaretPositionChanged(object sender, EventArgs e)
15

{
16
// ignore simple movements
17
if (e != EventArgs.Empty)
{
18
return;
19
}
20
try
{
21
22
ParseInformation parseInfo = ParserService.GetParseInformation(textAreaControl.FileName);
23
if (parseInfo != null)
{
24
if (currentCompilationUnit != (ICompilationUnit)parseInfo.MostRecentCompilationUnit)
{
25
currentCompilationUnit = (ICompilationUnit)parseInfo.MostRecentCompilationUnit;
26
if (currentCompilationUnit != null)
{
27
28
FillClassComboBox(true);
29
FillMembersComboBox();
30
}
31
}
32
UpdateClassComboBox();
33
UpdateMembersComboBox();
34
}
35
} catch (Exception ex)
{
36
MessageService.ShowError(ex);
37
}
38
}
可以看到类、成员列表的ComboBox数据的填充是在textAreaControl.ActiveTextAreaControl.Caret.PositionChanged事件后执行,因为光标位置的改变可能需要同步更新顶部列表框的显示;注意两个列表的列表项使用自定义类ComboBoxItem, 包括成员的Line, Column等信息;该类中绑定两个列表的SelectedIndexChange事件用以在编辑器中定位相应的类或成员位置:

ComboBoxSelectedIndexChanged()
1
void ComboBoxSelectedIndexChanged(object sender, System.EventArgs e)
2

{
3
ComboBox comboBox = (ComboBox)sender;
4
if (autoselect)
{
5
ComboBoxItem item = (ComboBoxItem)comboBox.Items[comboBox.SelectedIndex];
6
if (item.IsInCurrentPart)
7
{
8
textAreaControl.ActiveTextAreaControl.Caret.Position = new Point(item.Column, item.Line);
9
textAreaControl.ActiveTextAreaControl.TextArea.Focus();
10
}
11
}
12
}
6、Parser应用: Folding
ICSharpCode.TextEditor控件提供了IFoldingStrategy接口用以定义代码折叠需实现的方法:

IFoldingStrategy 接口
1
/**//// <summary>
2
/// This interface is used for the folding capabilities
3
/// of the textarea.
4
/// </summary>
5
public interface IFoldingStrategy
6

{
7
/**//// <remarks>
8
/// Calculates the fold level of a specific line.
9
/// </remarks>
10
List<FoldMarker> GenerateFoldMarkers(IDocument document, string fileName, object parseInformation);
11
}
SharpEditor项目中,SharpDevelopTextAreaControl在构造函数中指明了FoldingStrategy:

SharpDevelopTextAreaControl 构造函数
1
public SharpDevelopTextAreaControl()
2

{
3
Document.FoldingManager.FoldingStrategy = new ParserFoldingStrategy();
4
5
// 由于UpdateClassMemberBookmarks()方法中以Bookmark形式显示方法、属性、变量等
6
// 故此处需要定义Bookmark Factory 以使上面的Bookmark 与用户加入的书签区别开
7
Document.BookmarkManager.Factory = new Bookmarks.SDBookmarkFactory(Document.BookmarkManager);
8
}
9
protected override void OnFileNameChanged(EventArgs e)
10

{
11
base.OnFileNameChanged(e);
12
ActivateQuickClassBrowserOnDemand();
13
ForceFoldingUpdate();
14
}
15
void ForceFoldingUpdate()
16

{
17
ParseInformation parseInfo = ParserService.GetParseInformation(FileName);
18
if (parseInfo == null)
19
{
20
parseInfo = ParserService.ParseFile(FileName, Document.TextContent, false, false);
21
}
22
Document.FoldingManager.UpdateFoldings(FileName, parseInfo);
23
24
// 在编辑器的类、属性、变量等行左侧显示相应图标
25
UpdateClassMemberBookmarks(parseInfo);
26
}
同样需要在打开新文件时调用更新代码折叠标签,代码折叠FoldMarker生成的具体实现详见SharpEditor项目的ParserFoldingStrategy.cs
7、总结
上面简要介绍了Parser的部分实现, 期待有相关经验的网友指定我的理解错误或给予补充解释。
以及Parser的部分应用:MouseHover Tooltip, CodeCompletion & MethodInsight, QuickClassBrowserPanel, Folding;SharpDevelop中的其它应用如类视图ClassBrowser, 对象浏览AssemblyPad(SharpDevelop2.0源码中未见到?).
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!