Free cookie consent management tool by TermsFeed Policy Generator

source: branches/CodeEditor/HeuristicLab.CodeEditor/3.4/LanguageFeatures/CodeFolding/CSharp/FoldingVisitor.cs @ 11700

Last change on this file since 11700 was 11700, checked in by jkarder, 9 years ago

#2077: created branch and added first version

File size: 9.2 KB
Line 
1// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19using System;
20using System.Collections.Generic;
21using System.Linq;
22
23using ICSharpCode.AvalonEdit.Folding;
24using ICSharpCode.NRefactory;
25using ICSharpCode.NRefactory.CSharp;
26using ICSharpCode.NRefactory.Editor;
27
28namespace CSharpBinding.Parser
29{
30  // TODO: move this class + NewFolding to NRefactory
31  class FoldingVisitor : DepthFirstAstVisitor
32  {
33    internal List<NewFolding> foldings = new List<NewFolding> ();
34    internal IDocument document;
35   
36    int GetOffset(TextLocation location)
37    {
38      return document.GetOffset(location);
39    }
40   
41    NewFolding AddFolding(TextLocation start, TextLocation end, bool isDefinition = false)
42    {
43      if (end.Line <= start.Line || start.IsEmpty || end.IsEmpty)
44        return null;
45      NewFolding folding = new NewFolding(GetOffset(start), GetOffset(end));
46      folding.IsDefinition = isDefinition;
47      foldings.Add(folding);
48      return folding;
49    }
50   
51    TextLocation GetEndOfPrev(AstNode node)
52    {
53      do {
54        node = node.GetPrevNode();
55      } while (node.NodeType == NodeType.Whitespace);
56      return node.EndLocation;
57    }
58   
59    #region usings
60    void AddUsings (AstNode parent)
61    {
62      var firstChild = parent.Children.FirstOrDefault (child => child is UsingDeclaration || child is UsingAliasDeclaration);
63      var node = firstChild;
64      while (node != null) {
65        var next = node.GetNextNode ();
66        if (next is UsingDeclaration || next is UsingAliasDeclaration) {
67          node = next;
68        } else {
69          break;
70        }
71      }
72      if (firstChild != node) {
73        NewFolding folding = AddFolding(firstChild.StartLocation, node.EndLocation);
74        if (folding != null) {
75          folding.Name = "using...";
76          folding.DefaultClosed = true;
77        }
78      }
79    }
80    public override void VisitSyntaxTree (SyntaxTree unit)
81    {
82      AddUsings (unit);
83      base.VisitSyntaxTree (unit);
84    }
85
86    public override void VisitNamespaceDeclaration (NamespaceDeclaration namespaceDeclaration)
87    {
88      AddUsings (namespaceDeclaration);
89      if (!namespaceDeclaration.RBraceToken.IsNull)
90        AddFolding (namespaceDeclaration.LBraceToken.GetPrevNode ().EndLocation, namespaceDeclaration.RBraceToken.EndLocation);
91      base.VisitNamespaceDeclaration (namespaceDeclaration);
92    }
93    #endregion
94   
95    #region Entity Declarations
96    public override void VisitTypeDeclaration (TypeDeclaration typeDeclaration)
97    {
98      if (!typeDeclaration.RBraceToken.IsNull)
99        AddFolding (GetEndOfPrev(typeDeclaration.LBraceToken), typeDeclaration.RBraceToken.EndLocation);
100      base.VisitTypeDeclaration (typeDeclaration);
101    }
102   
103    public override void VisitMethodDeclaration (MethodDeclaration methodDeclaration)
104    {
105      if (!methodDeclaration.Body.IsNull) {
106        AddFolding (GetEndOfPrev(methodDeclaration.Body.LBraceToken),
107                    methodDeclaration.Body.RBraceToken.EndLocation, true);
108      }
109      base.VisitMethodDeclaration (methodDeclaration);
110    }
111   
112    public override void VisitConstructorDeclaration (ConstructorDeclaration constructorDeclaration)
113    {
114      if (!constructorDeclaration.Body.IsNull)
115        AddFolding (GetEndOfPrev(constructorDeclaration.Body.LBraceToken),
116                    constructorDeclaration.Body.RBraceToken.EndLocation, true);
117      base.VisitConstructorDeclaration (constructorDeclaration);
118    }
119   
120    public override void VisitDestructorDeclaration (DestructorDeclaration destructorDeclaration)
121    {
122      if (!destructorDeclaration.Body.IsNull)
123        AddFolding (GetEndOfPrev(destructorDeclaration.Body.LBraceToken),
124                    destructorDeclaration.Body.RBraceToken.EndLocation, true);
125      base.VisitDestructorDeclaration (destructorDeclaration);
126    }
127   
128    public override void VisitOperatorDeclaration (OperatorDeclaration operatorDeclaration)
129    {
130      if (!operatorDeclaration.Body.IsNull)
131        AddFolding (GetEndOfPrev(operatorDeclaration.Body.LBraceToken),
132                    operatorDeclaration.Body.RBraceToken.EndLocation, true);
133      base.VisitOperatorDeclaration (operatorDeclaration);
134    }
135   
136    public override void VisitPropertyDeclaration (PropertyDeclaration propertyDeclaration)
137    {
138      if (!propertyDeclaration.LBraceToken.IsNull)
139        AddFolding (GetEndOfPrev(propertyDeclaration.LBraceToken),
140                    propertyDeclaration.RBraceToken.EndLocation, true);
141      base.VisitPropertyDeclaration (propertyDeclaration);
142    }
143   
144    public override void VisitIndexerDeclaration (IndexerDeclaration indexerDeclaration)
145    {
146      if (!indexerDeclaration.LBraceToken.IsNull)
147        AddFolding (GetEndOfPrev(indexerDeclaration.LBraceToken),
148                    indexerDeclaration.RBraceToken.EndLocation, true);
149      base.VisitIndexerDeclaration (indexerDeclaration);
150    }
151   
152    public override void VisitCustomEventDeclaration (CustomEventDeclaration eventDeclaration)
153    {
154      if (!eventDeclaration.LBraceToken.IsNull)
155        AddFolding (GetEndOfPrev(eventDeclaration.LBraceToken),
156                    eventDeclaration.RBraceToken.EndLocation, true);
157      base.VisitCustomEventDeclaration (eventDeclaration);
158    }
159    #endregion
160   
161    #region Statements
162    public override void VisitSwitchStatement (SwitchStatement switchStatement)
163    {
164      if (!switchStatement.RBraceToken.IsNull)
165        AddFolding (GetEndOfPrev(switchStatement.LBraceToken),
166                    switchStatement.RBraceToken.EndLocation);
167      base.VisitSwitchStatement (switchStatement);
168    }
169   
170    public override void VisitBlockStatement (BlockStatement blockStatement)
171    {
172      if (!(blockStatement.Parent is EntityDeclaration) && blockStatement.EndLocation.Line - blockStatement.StartLocation.Line > 2) {
173        AddFolding (GetEndOfPrev(blockStatement), blockStatement.EndLocation);
174      }
175
176      base.VisitBlockStatement (blockStatement);
177    }
178    #endregion
179   
180    #region Preprocessor directives
181    Stack<NewFolding> regions = new Stack<NewFolding>();
182   
183    public override void VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective)
184    {
185      switch (preProcessorDirective.Type) {
186        case PreProcessorDirectiveType.Region:
187          NewFolding folding = new NewFolding();
188          folding.DefaultClosed = true;
189          folding.Name = preProcessorDirective.Argument;
190          folding.StartOffset = GetOffset(preProcessorDirective.StartLocation);
191          regions.Push(folding);
192          break;
193        case PreProcessorDirectiveType.Endregion:
194          if (regions.Count > 0) {
195            folding = regions.Pop();
196            folding.EndOffset = GetOffset(preProcessorDirective.EndLocation);
197            foldings.Add(folding);
198          }
199          break;
200      }
201    }
202    #endregion
203   
204    #region Comments
205    public override void VisitComment(Comment comment)
206    {
207      if (comment.CommentType == CommentType.InactiveCode)
208        return; // don't fold the inactive code comment; instead fold the preprocessor directives
209      if (AreTwoSinglelineCommentsInConsecutiveLines(comment.PrevSibling as Comment, comment))
210        return; // already handled by previous comment
211      Comment lastComment = comment;
212      Comment nextComment;
213      while (true) {
214        nextComment = lastComment.NextSibling as Comment;
215        if (!AreTwoSinglelineCommentsInConsecutiveLines(lastComment, nextComment))
216          break;
217        lastComment = nextComment;
218      }
219      if (lastComment.EndLocation.Line - comment.StartLocation.Line > 2) {
220        var folding = AddFolding(comment.StartLocation, lastComment.EndLocation);
221        if (folding != null) {
222          switch (comment.CommentType) {
223            case CommentType.SingleLine:
224              folding.Name = "// ...";
225              break;
226            case CommentType.MultiLine:
227              folding.Name = "/* ... */";
228              break;
229            case CommentType.Documentation:
230              folding.Name = "/// ...";
231              break;
232            case CommentType.MultiLineDocumentation:
233              folding.Name = "/** ... */";
234              break;
235          }
236        }
237      }
238    }
239   
240    bool AreTwoSinglelineCommentsInConsecutiveLines(Comment comment1, Comment comment2)
241    {
242      if (comment1 == null || comment2 == null)
243        return false;
244      return comment1.CommentType == comment2.CommentType
245        && comment1.StartLocation.Line == comment1.EndLocation.Line
246        && comment1.EndLocation.Line + 1 == comment2.StartLocation.Line
247        && comment1.StartLocation.Column == comment2.StartLocation.Column
248        && comment2.StartLocation.Line == comment2.EndLocation.Line;
249    }
250    #endregion
251  }
252}
Note: See TracBrowser for help on using the repository browser.