Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Refactoring/UsingHelper.cs @ 13397

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

#2077: created branch and added first version

File size: 7.0 KB
Line 
1// Copyright (c) 2010-2013 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.Diagnostics;
22using System.Linq;
23using ICSharpCode.NRefactory.CSharp.Resolver;
24using ICSharpCode.NRefactory.Semantics;
25
26namespace ICSharpCode.NRefactory.CSharp.Refactoring
27{
28  /// <summary>
29  /// Helper methods for managing using declarations.
30  /// </summary>
31  public class UsingHelper
32  {
33    /// <summary>
34    /// Inserts 'using ns;' in the current scope, and then removes all explicit
35    /// usages of ns that were made redundant by the new using.
36    /// </summary>
37    public static void InsertUsingAndRemoveRedundantNamespaceUsage(RefactoringContext context, Script script, string ns)
38    {
39      InsertUsing(context, script, new UsingDeclaration(ns));
40      // TODO: remove the usages that were made redundant
41    }
42   
43    /// <summary>
44    /// Inserts 'newUsing' in the current scope.
45    /// This method will try to insert new usings in the correct position (depending on
46    /// where the existing usings are; and maintaining the sort order).
47    /// </summary>
48    public static void InsertUsing(RefactoringContext context, Script script, AstNode newUsing)
49    {
50      UsingInfo newUsingInfo = new UsingInfo(newUsing, context);
51      AstNode enclosingNamespace = context.GetNode<NamespaceDeclaration>() ?? context.RootNode;
52      // Find nearest enclosing parent that has usings:
53      AstNode usingParent = enclosingNamespace;
54      while (usingParent != null && !usingParent.Children.OfType<UsingDeclaration>().Any())
55        usingParent = usingParent.Parent;
56      if (usingParent == null) {
57        // No existing usings at all -> use the default location
58        if (script.FormattingOptions.UsingPlacement == UsingPlacement.TopOfFile) {
59          usingParent = context.RootNode;
60        } else {
61          usingParent = enclosingNamespace;
62        }
63      }
64      // Find the main block of using declarations in the chosen scope:
65      AstNode blockStart = usingParent.Children.FirstOrDefault(IsUsingDeclaration);
66      AstNode insertionPoint;
67      bool insertAfter = false;
68      if (blockStart == null) {
69        // no using declarations in the file
70        Debug.Assert(SyntaxTree.MemberRole == NamespaceDeclaration.MemberRole);
71        insertionPoint = usingParent.GetChildrenByRole(SyntaxTree.MemberRole).SkipWhile(CanAppearBeforeUsings).FirstOrDefault();
72      } else {
73        insertionPoint = blockStart;
74        while (IsUsingFollowing (ref insertionPoint) && newUsingInfo.CompareTo(new UsingInfo(insertionPoint, context)) > 0)
75          insertionPoint = insertionPoint.NextSibling;
76        if (!IsUsingDeclaration(insertionPoint)) {
77          // Insert after last using instead of before next node
78          // This affects where empty lines get placed.
79          insertionPoint = insertionPoint.PrevSibling;
80          insertAfter = true;
81        }
82      }
83      if (insertionPoint != null) {
84        if (insertAfter)
85          script.InsertAfter(insertionPoint, newUsing);
86        else
87          script.InsertBefore(insertionPoint, newUsing);
88      }
89    }
90
91    static bool IsUsingFollowing(ref AstNode insertionPoint)
92    {
93      var node = insertionPoint;
94      while (node != null && node.Role == Roles.NewLine)
95        node = node.NextSibling;
96      if (IsUsingDeclaration(node)) {
97        insertionPoint = node;
98        return true;
99      }
100      return false;
101    }
102   
103    static bool IsUsingDeclaration(AstNode node)
104    {
105      return node is UsingDeclaration || node is UsingAliasDeclaration;
106    }
107   
108    static bool CanAppearBeforeUsings(AstNode node)
109    {
110      if (node is ExternAliasDeclaration)
111        return true;
112      if (node is PreProcessorDirective)
113        return true;
114      if (node is NewLineNode)
115        return true;
116      Comment c = node as Comment;
117      if (c != null)
118        return !c.IsDocumentation;
119      return false;
120    }
121   
122    /// <summary>
123    /// Sorts the specified usings.
124    /// </summary>
125    public static IEnumerable<AstNode> SortUsingBlock(IEnumerable<AstNode> nodes, BaseRefactoringContext context)
126    {
127      var infos = nodes.Select(_ => new UsingInfo(_, context));
128      var orderedInfos = infos.OrderBy(_ => _);
129      var orderedNodes = orderedInfos.Select(_ => _.Node);
130
131      return orderedNodes;
132    }
133
134
135    private sealed class UsingInfo : IComparable<UsingInfo>
136    {
137      public AstNode Node;
138
139      public string Alias;
140      public string Name;
141
142      public bool IsAlias;
143      public bool HasTypesFromOtherAssemblies;
144      public bool IsSystem;
145
146      public UsingInfo(AstNode node, BaseRefactoringContext context)
147      {
148        var importAndAlias = GetImportAndAlias(node);
149
150        Node = node;
151
152        Alias = importAndAlias.Item2;
153        Name = importAndAlias.Item1.ToString();
154
155        IsAlias = Alias != null;
156
157        ResolveResult rr;
158        if (node.Ancestors.Contains(context.RootNode)) {
159          rr = context.Resolve(importAndAlias.Item1);
160        } else {
161          // It's possible that we're looking at a new using that
162          // isn't part of the AST.
163          var resolver = new CSharpAstResolver(new CSharpResolver(context.Compilation), node);
164          rr = resolver.Resolve(importAndAlias.Item1);
165        }
166       
167        var nrr = rr as NamespaceResolveResult;
168        HasTypesFromOtherAssemblies = nrr != null && nrr.Namespace.ContributingAssemblies.Any(a => !a.IsMainAssembly);
169
170        IsSystem = HasTypesFromOtherAssemblies && (Name == "System" || Name.StartsWith("System.", StringComparison.Ordinal));
171      }
172
173      private static Tuple<AstType, string> GetImportAndAlias(AstNode node)
174      {
175        var plainUsing = node as UsingDeclaration;
176        if (plainUsing != null)
177          return Tuple.Create(plainUsing.Import, (string)null);
178       
179        var aliasUsing = node as UsingAliasDeclaration;
180        if (aliasUsing != null)
181          return Tuple.Create(aliasUsing.Import, aliasUsing.Alias);
182
183        throw new InvalidOperationException(string.Format("Invalid using node: {0}", node));
184      }
185
186      public int CompareTo(UsingInfo y)
187      {
188        UsingInfo x = this;
189        if (x.IsAlias != y.IsAlias)
190          return x.IsAlias ? 1 : -1;
191        if (x.IsAlias)
192          return StringComparer.OrdinalIgnoreCase.Compare(x.Alias, y.Alias);
193//        if (x.HasTypesFromOtherAssemblies != y.HasTypesFromOtherAssemblies)
194//          return x.HasTypesFromOtherAssemblies ? -1 : 1;
195        if (x.IsSystem != y.IsSystem)
196          return x.IsSystem ? -1 : 1;
197        return StringComparer.OrdinalIgnoreCase.Compare(x.Name, y.Name);
198      }
199    }
200  }
201}
Note: See TracBrowser for help on using the repository browser.