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()中加载默认命名空间的引用:  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()方法如下:  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类,它提供了一个重要方法如下:
 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);用以设置提供数据。  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()两个辅助方法生成数据,代码如下:
 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类提供注释的自动完成,相关代码如下:  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),其中辅助类的处理代码如下:  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对象的相关代码如下:
 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事件用以在编辑器中定位相应的类或成员位置:  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接口用以定义代码折叠需实现的方法:
 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:  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源码中未见到?).
|