2
// DeclareLocalVariableAction.cs
5
// Mike Krüger <mkrueger@xamarin.com>
7
// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com)
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:
16
// The above copyright notice and this permission notice shall be included in
17
// all copies or substantial portions of the Software.
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
27
using System.Threading;
28
using System.Collections.Generic;
30
using ICSharpCode.NRefactory.Semantics;
31
using ICSharpCode.NRefactory.CSharp.Resolver;
32
using ICSharpCode.NRefactory.TypeSystem;
33
using ICSharpCode.NRefactory.PatternMatching;
35
namespace ICSharpCode.NRefactory.CSharp.Refactoring
37
[ContextAction("Declare local variable", Description = "Declare a local variable out of a selected expression.")]
38
public class DeclareLocalVariableAction : ICodeActionProvider
40
public IEnumerable<CodeAction> GetActions(RefactoringContext context)
42
if (!context.IsSomethingSelected) {
45
var selected = new List<AstNode>(context.GetSelectedNodes());
46
if (selected.Count != 1 || !(selected [0] is Expression)) {
49
var expr = selected [0] as Expression;
50
var visitor = new SearchNodeVisitior(expr);
52
var node = context.GetNode <BlockStatement>();
54
node.AcceptVisitor(visitor);
57
yield return new CodeAction(context.TranslateString("Declare local variable"), script => {
58
var resolveResult = context.Resolve(expr);
59
var guessedType = resolveResult.Type;
60
if (resolveResult is MethodGroupResolveResult) {
61
guessedType = GetDelegateType(context, ((MethodGroupResolveResult)resolveResult).Methods.First(), expr);
63
var name = CreateMethodDeclarationAction.CreateBaseName(expr, guessedType);
64
var type = context.UseExplicitTypes ? context.CreateShortType(guessedType) : new SimpleType("var");
65
var varDecl = new VariableDeclarationStatement(type, name, expr.Clone());
66
if (expr.Parent is ExpressionStatement) {
67
script.Replace(expr.Parent, varDecl);
68
script.Select(varDecl.Variables.First().NameToken);
70
var containing = expr.Parent;
71
while (!(containing.Parent is BlockStatement)) {
72
containing = containing.Parent;
75
script.InsertBefore(containing, varDecl);
76
var identifierExpression = new IdentifierExpression(name);
77
script.Replace(expr, identifierExpression);
78
script.Link(varDecl.Variables.First().NameToken, identifierExpression);
82
if (visitor.Matches.Count > 1) {
83
yield return new CodeAction(string.Format(context.TranslateString("Declare local variable (replace '{0}' occurrences)"), visitor.Matches.Count), script => {
84
var resolveResult = context.Resolve(expr);
85
var guessedType = resolveResult.Type;
86
if (resolveResult is MethodGroupResolveResult) {
87
guessedType = GetDelegateType(context, ((MethodGroupResolveResult)resolveResult).Methods.First(), expr);
89
var linkedNodes = new List<AstNode>();
90
var name = CreateMethodDeclarationAction.CreateBaseName(expr, guessedType);
91
var type = context.UseExplicitTypes ? context.CreateShortType(guessedType) : new SimpleType("var");
92
var varDecl = new VariableDeclarationStatement(type, name, expr.Clone());
93
linkedNodes.Add(varDecl.Variables.First().NameToken);
94
var first = visitor.Matches [0];
95
if (first.Parent is ExpressionStatement) {
96
script.Replace(first.Parent, varDecl);
98
var containing = first.Parent;
99
while (!(containing.Parent is BlockStatement)) {
100
containing = containing.Parent;
103
script.InsertBefore(containing, varDecl);
104
var identifierExpression = new IdentifierExpression(name);
105
linkedNodes.Add(identifierExpression);
106
script.Replace(first, identifierExpression);
108
for (int i = 1; i < visitor.Matches.Count; i++) {
109
var identifierExpression = new IdentifierExpression(name);
110
linkedNodes.Add(identifierExpression);
111
script.Replace(visitor.Matches [i], identifierExpression);
113
script.Link(linkedNodes.ToArray ());
118
// Gets Action/Func delegate types for a given method.
119
IType GetDelegateType(RefactoringContext context, IMethod method, Expression expr)
121
var parameters = new List<IType>();
122
var invoke = expr.Parent as InvocationExpression;
123
if (invoke == null) {
126
foreach (var arg in invoke.Arguments) {
127
parameters.Add(context.Resolve(arg).Type);
130
ITypeDefinition genericType;
131
if (method.ReturnType.FullName == "System.Void") {
132
genericType = context.Compilation.GetAllTypeDefinitions().FirstOrDefault(t => t.FullName == "System.Action" && t.TypeParameterCount == parameters.Count);
134
parameters.Add(method.ReturnType);
135
genericType = context.Compilation.GetAllTypeDefinitions().FirstOrDefault(t => t.FullName == "System.Func" && t.TypeParameterCount == parameters.Count);
137
if (genericType == null) {
140
return new ParameterizedType(genericType, parameters);
144
class SearchNodeVisitior : DepthFirstAstVisitor
146
readonly AstNode searchForNode;
147
public readonly List<AstNode> Matches = new List<AstNode> ();
149
public SearchNodeVisitior (AstNode searchForNode)
151
this.searchForNode = searchForNode;
152
Matches.Add (searchForNode);
155
protected override void VisitChildren(AstNode node)
158
if (node.StartLocation > searchForNode.StartLocation && node.IsMatch (searchForNode))
160
base.VisitChildren (node);