Free cookie consent management tool by TermsFeed Policy Generator

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

Last change on this file since 14927 was 14927, checked in by gkronber, 7 years ago

#2520: changed all usages of StorableClass to use StorableType with an auto-generated GUID (did not add StorableType to other type definitions yet)

File size: 9.7 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    internal List<NewFolding> foldings = new List<NewFolding>();
33    internal IDocument document;
34
35    int GetOffset(TextLocation location) {
36      return document.GetOffset(location);
37    }
38
39    NewFolding AddFolding(TextLocation start, TextLocation end, bool isDefinition = false) {
40      if (end.Line <= start.Line || start.IsEmpty || end.IsEmpty)
41        return null;
42      NewFolding folding = new NewFolding(GetOffset(start), GetOffset(end));
43      folding.IsDefinition = isDefinition;
44      foldings.Add(folding);
45      return folding;
46    }
47
48    TextLocation GetEndOfPrev(AstNode node) {
49      do {
50        node = node.GetPrevNode();
51      } while (node.NodeType == NodeType.Whitespace);
52      return node.EndLocation;
53    }
54
55    #region usings
56    void AddUsings(AstNode parent) {
57      var firstChild = parent.Children.FirstOrDefault(child => child is UsingDeclaration || child is UsingAliasDeclaration);
58      var node = firstChild;
59      while (node != null) {
60        var next = node.GetNextNode();
61        if (next is UsingDeclaration || next is UsingAliasDeclaration) {
62          node = next;
63        } else {
64          break;
65        }
66      }
67      if (firstChild != node) {
68        NewFolding folding = AddFolding(firstChild.StartLocation, node.EndLocation);
69        if (folding != null) {
70          folding.Name = "using...";
71          folding.DefaultClosed = true;
72        }
73      }
74    }
75    public override void VisitSyntaxTree(SyntaxTree unit) {
76      AddUsings(unit);
77      base.VisitSyntaxTree(unit);
78    }
79
80    public override void VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration) {
81      AddUsings(namespaceDeclaration);
82      if (!namespaceDeclaration.RBraceToken.IsNull)
83        AddFolding(namespaceDeclaration.LBraceToken.GetPrevNode().EndLocation, namespaceDeclaration.RBraceToken.EndLocation);
84      base.VisitNamespaceDeclaration(namespaceDeclaration);
85    }
86    #endregion
87
88    #region Entity Declarations
89    public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration) {
90      if (!typeDeclaration.RBraceToken.IsNull)
91        AddFolding(GetEndOfPrev(typeDeclaration.LBraceToken), typeDeclaration.RBraceToken.EndLocation);
92      base.VisitTypeDeclaration(typeDeclaration);
93    }
94
95    public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) {
96      if (!methodDeclaration.Body.IsNull) {
97        AddFolding(GetEndOfPrev(methodDeclaration.Body.LBraceToken),
98                    methodDeclaration.Body.RBraceToken.EndLocation, true);
99      }
100      base.VisitMethodDeclaration(methodDeclaration);
101    }
102
103    public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) {
104      if (!constructorDeclaration.Body.IsNull)
105        AddFolding(GetEndOfPrev(constructorDeclaration.Body.LBraceToken),
106                    constructorDeclaration.Body.RBraceToken.EndLocation, true);
107      base.VisitConstructorDeclaration(constructorDeclaration);
108    }
109
110    public override void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration) {
111      if (!destructorDeclaration.Body.IsNull)
112        AddFolding(GetEndOfPrev(destructorDeclaration.Body.LBraceToken),
113                    destructorDeclaration.Body.RBraceToken.EndLocation, true);
114      base.VisitDestructorDeclaration(destructorDeclaration);
115    }
116
117    public override void VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) {
118      if (!operatorDeclaration.Body.IsNull)
119        AddFolding(GetEndOfPrev(operatorDeclaration.Body.LBraceToken),
120                    operatorDeclaration.Body.RBraceToken.EndLocation, true);
121      base.VisitOperatorDeclaration(operatorDeclaration);
122    }
123
124    public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) {
125      if (!propertyDeclaration.LBraceToken.IsNull)
126        AddFolding(GetEndOfPrev(propertyDeclaration.LBraceToken),
127                    propertyDeclaration.RBraceToken.EndLocation, true);
128      base.VisitPropertyDeclaration(propertyDeclaration);
129    }
130
131    public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) {
132      if (!indexerDeclaration.LBraceToken.IsNull)
133        AddFolding(GetEndOfPrev(indexerDeclaration.LBraceToken),
134                    indexerDeclaration.RBraceToken.EndLocation, true);
135      base.VisitIndexerDeclaration(indexerDeclaration);
136    }
137
138    public override void VisitCustomEventDeclaration(CustomEventDeclaration eventDeclaration) {
139      if (!eventDeclaration.LBraceToken.IsNull)
140        AddFolding(GetEndOfPrev(eventDeclaration.LBraceToken),
141                    eventDeclaration.RBraceToken.EndLocation, true);
142      base.VisitCustomEventDeclaration(eventDeclaration);
143    }
144    #endregion
145
146    #region Statements
147    public override void VisitSwitchStatement(SwitchStatement switchStatement) {
148      if (!switchStatement.RBraceToken.IsNull)
149        AddFolding(GetEndOfPrev(switchStatement.LBraceToken),
150                    switchStatement.RBraceToken.EndLocation);
151      base.VisitSwitchStatement(switchStatement);
152    }
153
154    public override void VisitBlockStatement(BlockStatement blockStatement) {
155      if (!(blockStatement.Parent is EntityDeclaration) && blockStatement.EndLocation.Line - blockStatement.StartLocation.Line > 2) {
156        AddFolding(GetEndOfPrev(blockStatement), blockStatement.EndLocation);
157      }
158
159      base.VisitBlockStatement(blockStatement);
160    }
161    #endregion
162
163    #region Preprocessor directives
164    Stack<NewFolding> regions = new Stack<NewFolding>();
165
166    public override void VisitPreProcessorDirective(PreProcessorDirective preProcessorDirective) {
167      switch (preProcessorDirective.Type) {
168        case PreProcessorDirectiveType.Region:
169          NewFolding folding = new NewFolding();
170          folding.DefaultClosed = true;
171          folding.Name = preProcessorDirective.Argument;
172          folding.StartOffset = GetOffset(preProcessorDirective.StartLocation);
173          regions.Push(folding);
174          break;
175        case PreProcessorDirectiveType.Endregion:
176          if (regions.Count > 0) {
177            folding = regions.Pop();
178            folding.EndOffset = GetOffset(preProcessorDirective.EndLocation);
179            foldings.Add(folding);
180          }
181          break;
182      }
183    }
184    #endregion
185
186    #region Comments
187    public override void VisitComment(Comment comment) {
188      if (comment.CommentType == CommentType.InactiveCode)
189        return; // don't fold the inactive code comment; instead fold the preprocessor directives
190      if (AreTwoSinglelineCommentsInConsecutiveLines(comment.PrevSibling as Comment, comment))
191        return; // already handled by previous comment
192      Comment lastComment = comment;
193      Comment nextComment;
194      while (true) {
195        nextComment = lastComment.NextSibling as Comment;
196        if (!AreTwoSinglelineCommentsInConsecutiveLines(lastComment, nextComment))
197          break;
198        lastComment = nextComment;
199      }
200      if (lastComment.EndLocation.Line - comment.StartLocation.Line > 2) {
201        var folding = AddFolding(comment.StartLocation, lastComment.EndLocation);
202        if (folding != null) {
203          switch (comment.CommentType) {
204            case CommentType.SingleLine:
205              folding.Name = "// ...";
206              break;
207            case CommentType.MultiLine:
208              folding.Name = "/* ... */";
209              break;
210            case CommentType.Documentation:
211              folding.Name = "/// ...";
212              break;
213            case CommentType.MultiLineDocumentation:
214              folding.Name = "/** ... */";
215              break;
216          }
217        }
218      }
219    }
220
221    bool AreTwoSinglelineCommentsInConsecutiveLines(Comment comment1, Comment comment2) {
222      if (comment1 == null || comment2 == null)
223        return false;
224      return comment1.CommentType == comment2.CommentType
225        && comment1.StartLocation.Line == comment1.EndLocation.Line
226        && comment1.EndLocation.Line + 1 == comment2.StartLocation.Line
227        && comment1.StartLocation.Column == comment2.StartLocation.Column
228        && comment2.StartLocation.Line == comment2.EndLocation.Line;
229    }
230    #endregion
231  }
232}
Note: See TracBrowser for help on using the repository browser.