Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/HeuristicLab.CodeEditor/3.4/LanguageFeatures/CodeFolding/CSharp/FoldingVisitor.cs

Last change on this file was 16565, checked in by gkronber, 6 years ago

#2520: merged changes from PersistenceOverhaul branch (r16451:16564) into trunk

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