Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2521_ProblemRefactoring/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Completion/CSharpParameterCompletionEngine.cs

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

#2077: created branch and added first version

File size: 13.9 KB
Line 
1//
2// CSharpParameterCompletionEngine.cs
3// 
4// Author:
5//       Mike Krüger <mkrueger@xamarin.com>
6//
7// Copyright (c) 2011 Xamarin Inc. (http://xamarin.com)
8//
9// Permission is hereby granted, free of charge, to any person obtaining a copy
10// of this software and associated documentation files (the "Software"), to deal
11// in the Software without restriction, including without limitation the rights
12// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13// copies of the Software, and to permit persons to whom the Software is
14// furnished to do so, subject to the following conditions:
15//
16// The above copyright notice and this permission notice shall be included in
17// all copies or substantial portions of the Software.
18//
19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25// THE SOFTWARE.
26using System;
27using ICSharpCode.NRefactory.Editor;
28using ICSharpCode.NRefactory.Completion;
29using System.Collections.Generic;
30using ICSharpCode.NRefactory.Semantics;
31using ICSharpCode.NRefactory.TypeSystem;
32using ICSharpCode.NRefactory.CSharp.Resolver;
33using ICSharpCode.NRefactory.CSharp.TypeSystem;
34using System.Linq;
35
36namespace ICSharpCode.NRefactory.CSharp.Completion
37{
38  public class CSharpParameterCompletionEngine : CSharpCompletionEngineBase
39  {
40    internal IParameterCompletionDataFactory factory;
41   
42    public CSharpParameterCompletionEngine(IDocument document, ICompletionContextProvider completionContextProvider, IParameterCompletionDataFactory factory, IProjectContent content, CSharpTypeResolveContext ctx) : base (content, completionContextProvider, ctx)
43    {
44      if (document == null) {
45        throw new ArgumentNullException("document");
46      }
47      if (factory == null) {
48        throw new ArgumentNullException("factory");
49      }
50      this.document = document;
51      this.factory = factory;
52    }
53
54    public ExpressionResult GetIndexerBeforeCursor()
55    {
56      SyntaxTree baseUnit;
57      if (currentMember == null && currentType == null) {
58        return null;
59      }
60      baseUnit = ParseStub("x]");
61     
62      //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin;
63      var mref = baseUnit.GetNodeAt(location, n => n is IndexerExpression);
64      AstNode expr;
65      if (mref is IndexerExpression) {
66        expr = ((IndexerExpression)mref).Target;
67      } else {
68        return null;
69      }
70     
71      return new ExpressionResult((AstNode)expr, baseUnit);
72    }
73   
74    public ExpressionResult GetConstructorInitializerBeforeCursor()
75    {
76      SyntaxTree baseUnit;
77      if (currentMember == null && currentType == null) {
78        return null;
79      }
80      baseUnit = ParseStub("a) {}", false);
81     
82      var expr = baseUnit.GetNodeAt <ConstructorInitializer>(location);
83      if (expr == null) {
84        return null;
85      }
86      return new ExpressionResult((AstNode)expr, baseUnit);
87    }
88   
89    public ExpressionResult GetTypeBeforeCursor()
90    {
91      SyntaxTree baseUnit;
92      if (currentMember == null && currentType == null) {
93        return null;
94      }
95      baseUnit = ParseStub("x> a");
96     
97      //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin;
98      var expr = baseUnit.GetNodeAt<AstType>(location.Line, location.Column + 1);
99      if (expr == null)
100        return null;
101      // '>' position
102      return new ExpressionResult((AstNode)expr, baseUnit);
103    }
104
105    public ExpressionResult GetMethodTypeArgumentInvocationBeforeCursor()
106    {
107      SyntaxTree baseUnit;
108      if (currentMember == null && currentType == null) {
109        return null;
110      }
111      baseUnit = ParseStub("x>.A ()");
112     
113      //var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin;
114      var expr = baseUnit.GetNodeAt<MemberReferenceExpression>(location.Line, location.Column + 1);
115      if (expr == null)
116        return null;
117      return new ExpressionResult((AstNode)expr, baseUnit);
118    }
119
120
121
122    IEnumerable<IMethod> CollectMethods(AstNode resolvedNode, MethodGroupResolveResult resolveResult)
123    {
124      var lookup = new MemberLookup(ctx.CurrentTypeDefinition, Compilation.MainAssembly);
125      bool onlyStatic = false;
126      if (resolvedNode is IdentifierExpression && currentMember != null && currentMember.IsStatic || resolveResult.TargetResult is TypeResolveResult) {
127        onlyStatic = true;
128      }
129      var methods = new List<IMethod>();
130      foreach (var method in resolveResult.Methods) {
131        if (method.IsConstructor) {
132          continue;
133        }
134        if (!lookup.IsAccessible (method, true))
135          continue;
136        if (onlyStatic && !method.IsStatic) {
137          continue;
138        }
139        if (method.IsShadowing) {
140          for (int j = 0; j < methods.Count; j++) {
141            if (ParameterListComparer.Instance.Equals(methods[j].Parameters, method.Parameters)) {
142              methods.RemoveAt (j);
143              j--;
144            }
145          }
146        }
147        methods.Add (method);
148      }
149      foreach (var m in methods) {
150        yield return m;
151      }
152      foreach (var extMethods in resolveResult.GetEligibleExtensionMethods (true)) {
153        foreach (var method in extMethods) {
154          if (methods.Contains (method))
155            continue;
156          yield return new ReducedExtensionMethod (method);
157        }
158      }
159    }
160
161    IEnumerable<IProperty> GetAccessibleIndexers(IType type)
162    {
163      var lookup = new MemberLookup(ctx.CurrentTypeDefinition, Compilation.MainAssembly);
164      var properties = new List<IProperty>();
165      foreach (var property in type.GetProperties ()) {
166        if (!property.IsIndexer)
167          continue;
168        if (!lookup.IsAccessible (property, true))
169          continue;
170        if (property.IsShadowing) {
171          for (int j = 0; j < properties.Count; j++) {
172            if (ParameterListComparer.Instance.Equals(properties[j].Parameters, property.Parameters)) {
173              properties.RemoveAt (j);
174              j--;
175            }
176          }
177        }
178
179        properties.Add (property);
180      }
181      return properties;
182    }
183   
184    public IParameterDataProvider GetParameterDataProvider(int offset, char completionChar)
185    {
186      //Ignoring completionChar == '\0' because it usually means moving with arrow keys, tab or enter
187      //we don't want to trigger on those events but it probably should be handled somewhere else
188      //since our job is to resolve method and not to decide when to display tooltip or not
189      if (offset <= 0 || completionChar == '\0') {
190        return null;
191      }
192      SetOffset (offset);
193      int startOffset;
194      string text;
195      if (currentMember == null && currentType == null) {
196        //In case of attributes parse all file
197        startOffset = 0;
198        text = document.Text;
199      } else {
200        var memberText = GetMemberTextToCaret ();
201        text = memberText.Item1;
202        startOffset = document.GetOffset (memberText.Item2);
203      }
204
205      var parenStack = new Stack<int> ();
206      var chevronStack = new Stack<int> ();
207      var squareStack = new Stack<int> ();
208      var bracketStack = new Stack<int> ();
209
210      var lex = new MiniLexer (text);
211      bool failed = lex.Parse ((ch, off) => {
212        if (lex.IsInString || lex.IsInChar || lex.IsInVerbatimString || lex.IsInSingleComment || lex.IsInMultiLineComment || lex.IsInPreprocessorDirective)
213          return false;
214        switch (ch) {
215        case '(':
216          parenStack.Push (startOffset + off);
217          break;
218        case ')':
219          if (parenStack.Count == 0) {
220            return true;
221          }
222          parenStack.Pop ();
223          break;
224        case '<':
225          chevronStack.Push (startOffset + off);
226          break;
227        case '>':
228          //Don't abort if we don't have macthing '<' for '>' it could be if (i > 0) Foo($
229          if (chevronStack.Count == 0) {
230            return false;
231          }
232          chevronStack.Pop ();
233          break;
234        case '[':
235          squareStack.Push (startOffset + off);
236          break;
237        case ']':
238          if (squareStack.Count == 0) {
239            return true;
240          }
241          squareStack.Pop ();
242          break;
243        case '{':
244          bracketStack.Push (startOffset + off);
245          break;
246        case '}':
247          if (bracketStack.Count == 0) {
248            return true;
249          }
250          bracketStack.Pop ();
251          break;
252        }
253        return false;
254      });
255      if (failed)
256        return null;
257      int result = -1;
258      if (parenStack.Count > 0)
259        result = parenStack.Pop ();
260      if (squareStack.Count > 0)
261        result = Math.Max (result, squareStack.Pop ());
262      if (chevronStack.Count > 0)
263        result = Math.Max (result, chevronStack.Pop ());
264
265      //If we are inside { bracket we don't want to display anything
266      if (bracketStack.Count > 0 && bracketStack.Pop () > result)
267        return null;
268      if (result == -1)
269        return null;
270      SetOffset (result + 1);
271      ResolveResult resolveResult;
272      switch (document.GetCharAt (result)) {
273        case '(':
274          var invoke = GetInvocationBeforeCursor(true) ?? GetConstructorInitializerBeforeCursor();
275          if (invoke == null) {
276            return null;
277          }
278          if (invoke.Node is ConstructorInitializer) {
279            var init = (ConstructorInitializer)invoke.Node;
280            if (init.ConstructorInitializerType == ConstructorInitializerType.This) {
281              return factory.CreateConstructorProvider(document.GetOffset(invoke.Node.StartLocation), ctx.CurrentTypeDefinition, init);
282            } else {
283              var baseType = ctx.CurrentTypeDefinition.DirectBaseTypes.FirstOrDefault(bt => bt.Kind != TypeKind.Interface);
284              if (baseType == null) {
285                return null;
286              }
287              return factory.CreateConstructorProvider(document.GetOffset(invoke.Node.StartLocation), baseType);
288            }
289          }
290          if (invoke.Node is ObjectCreateExpression) {
291            var createType = ResolveExpression(((ObjectCreateExpression)invoke.Node).Type);
292            if (createType.Result.Type.Kind == TypeKind.Unknown)
293              return null;
294            return factory.CreateConstructorProvider(document.GetOffset(invoke.Node.StartLocation), createType.Result.Type);
295          }
296         
297          if (invoke.Node is ICSharpCode.NRefactory.CSharp.Attribute) {
298            var attribute = ResolveExpression(invoke);
299            if (attribute == null || attribute.Result == null) {
300              return null;
301            }
302            return factory.CreateConstructorProvider(document.GetOffset(invoke.Node.StartLocation), attribute.Result.Type);
303          }
304          var invocationExpression = ResolveExpression(invoke);
305          if (invocationExpression == null || invocationExpression.Result == null || invocationExpression.Result.IsError) {
306            return null;
307          }
308          resolveResult = invocationExpression.Result;
309          if (resolveResult is MethodGroupResolveResult) {
310            return factory.CreateMethodDataProvider(document.GetOffset(invoke.Node.StartLocation), CollectMethods(invoke.Node, resolveResult as MethodGroupResolveResult));
311          }
312          if (resolveResult is MemberResolveResult) {
313            var mr = resolveResult as MemberResolveResult;
314            if (mr.Member is IMethod) {
315              return factory.CreateMethodDataProvider(document.GetOffset(invoke.Node.StartLocation), new [] { (IMethod)mr.Member });
316            }
317          }
318         
319          if (resolveResult.Type.Kind == TypeKind.Delegate) {
320            return factory.CreateDelegateDataProvider(document.GetOffset(invoke.Node.StartLocation), resolveResult.Type);
321          }
322         
323          //       
324          //        if (result.ExpressionContext == ExpressionContext.BaseConstructorCall) {
325          //          if (resolveResult is ThisResolveResult)
326          //            return new NRefactoryParameterDataProvider (textEditorData, resolver, resolveResult as ThisResolveResult);
327          //          if (resolveResult is BaseResolveResult)
328          //            return new NRefactoryParameterDataProvider (textEditorData, resolver, resolveResult as BaseResolveResult);
329          //        }
330          //        IType resolvedType = resolver.SearchType (resolveResult.ResolvedType);
331          //        if (resolvedType != null && resolvedType.ClassType == ClassType.Delegate) {
332          //          return new NRefactoryParameterDataProvider (textEditorData, result.Expression, resolvedType);
333          //        }
334          break;
335        case '<':
336          invoke = GetMethodTypeArgumentInvocationBeforeCursor();
337          if (invoke != null) {
338            var tExpr2 = ResolveExpression(invoke);
339            if (tExpr2 != null && tExpr2.Result is MethodGroupResolveResult && !tExpr2.Result.IsError) {
340              return factory.CreateTypeParameterDataProvider(document.GetOffset(invoke.Node.StartLocation), CollectMethods(invoke.Node, tExpr2.Result as MethodGroupResolveResult));
341            }
342          }
343          invoke = GetTypeBeforeCursor();
344          if (invoke == null || invoke.Node.StartLocation.IsEmpty) {
345            return null;
346          }
347          var tExpr = ResolveExpression(invoke);
348          if (tExpr == null || tExpr.Result == null || tExpr.Result.IsError) {
349            return null;
350          }
351
352          return factory.CreateTypeParameterDataProvider(document.GetOffset(invoke.Node.StartLocation), CollectAllTypes(tExpr.Result.Type));
353        case '[':
354          invoke = GetIndexerBeforeCursor();
355          if (invoke == null) {
356            return null;
357          }
358          if (invoke.Node is ArrayCreateExpression) {
359            return null;
360          }
361          var indexerExpression = ResolveExpression(invoke);
362          if (indexerExpression == null || indexerExpression.Result == null || indexerExpression.Result.IsError) {
363            return null;
364          }
365          return factory.CreateIndexerParameterDataProvider(document.GetOffset(invoke.Node.StartLocation), indexerExpression.Result.Type, GetAccessibleIndexers (indexerExpression.Result.Type), invoke.Node);
366      }
367      return null;
368    }
369   
370    IEnumerable<IType> CollectAllTypes(IType baseType)
371    {
372      var state = GetState();
373      for (var n = state.CurrentUsingScope; n != null; n = n.Parent) {
374        foreach (var u in n.Usings) {
375          foreach (var type in u.Types) {
376            if (type.TypeParameterCount > 0 && type.Name == baseType.Name) {
377              yield return type;
378            }
379          }
380        }
381       
382        foreach (var type in n.Namespace.Types) {
383          if (type.TypeParameterCount > 0 && type.Name == baseType.Name) {
384            yield return type;
385          }
386        }
387      }
388    }
389   
390    List<string> GetUsedNamespaces()
391    {
392      var scope = ctx.CurrentUsingScope;
393      var result = new List<string>();
394      while (scope != null) {
395        result.Add(scope.Namespace.FullName);
396       
397        foreach (var ns in scope.Usings) {
398          result.Add(ns.FullName);
399        }
400        scope = scope.Parent;
401      }
402      return result;
403    }
404   
405   
406  }
407}
408
Note: See TracBrowser for help on using the repository browser.