11 changed files with 427 additions and 33 deletions
@ -0,0 +1,154 @@
@@ -0,0 +1,154 @@
|
||||
//
|
||||
// DeclareLocalVariableAction.cs
|
||||
//
|
||||
// Author:
|
||||
// Mike Krüger <mkrueger@xamarin.com>
|
||||
//
|
||||
// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System.Threading; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using ICSharpCode.NRefactory.Semantics; |
||||
using ICSharpCode.NRefactory.CSharp.Resolver; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
using ICSharpCode.NRefactory.PatternMatching; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp.Refactoring |
||||
{ |
||||
[ContextAction("Declare local variable", Description = "Declare a local variable out of a selected expression.")] |
||||
public class DeclareLocalVariableAction : ICodeActionProvider |
||||
{ |
||||
public IEnumerable<CodeAction> GetActions(RefactoringContext context) |
||||
{ |
||||
if (!context.IsSomethingSelected) { |
||||
yield break; |
||||
} |
||||
var selected = new List<AstNode>(context.GetSelectedNodes()); |
||||
if (selected.Count != 1 || !(selected [0] is Expression)) { |
||||
yield break; |
||||
} |
||||
var expr = selected [0] as Expression; |
||||
var visitor = new SearchNodeVisitior(expr); |
||||
|
||||
var node = context.GetNode <BlockStatement>(); |
||||
if (node != null) { |
||||
node.AcceptVisitor(visitor); |
||||
} |
||||
|
||||
yield return new CodeAction(context.TranslateString("Declare local variable"), script => { |
||||
var resolveResult = context.Resolve(expr); |
||||
var guessedType = resolveResult.Type; |
||||
if (resolveResult is MethodGroupResolveResult) { |
||||
guessedType = GetDelegateType(context, ((MethodGroupResolveResult)resolveResult).Methods.First(), expr); |
||||
} |
||||
var name = CreateMethodDeclarationAction.CreateBaseName(expr, guessedType); |
||||
var varDecl = new VariableDeclarationStatement(context.CreateShortType(guessedType), name, expr.Clone()); |
||||
if (expr.Parent is ExpressionStatement) { |
||||
script.Replace(expr.Parent, varDecl); |
||||
} else { |
||||
var containing = expr.Parent; |
||||
while (!(containing.Parent is BlockStatement)) { |
||||
containing = containing.Parent; |
||||
} |
||||
|
||||
script.InsertBefore(containing, varDecl); |
||||
script.Replace(expr, new IdentifierExpression(name)); |
||||
} |
||||
}); |
||||
|
||||
if (visitor.Matches.Count > 1) { |
||||
yield return new CodeAction(string.Format(context.TranslateString("Declare local variable (replace '{0}' occurrences)"), visitor.Matches), script => { |
||||
var resolveResult = context.Resolve(expr); |
||||
var guessedType = resolveResult.Type; |
||||
if (resolveResult is MethodGroupResolveResult) { |
||||
guessedType = GetDelegateType(context, ((MethodGroupResolveResult)resolveResult).Methods.First(), expr); |
||||
} |
||||
var name = CreateMethodDeclarationAction.CreateBaseName(expr, guessedType); |
||||
var varDecl = new VariableDeclarationStatement(context.CreateShortType(guessedType), name, expr.Clone()); |
||||
var first = visitor.Matches [0]; |
||||
if (first.Parent is ExpressionStatement) { |
||||
script.Replace(first.Parent, varDecl); |
||||
} else { |
||||
var containing = first.Parent; |
||||
while (!(containing.Parent is BlockStatement)) { |
||||
containing = containing.Parent; |
||||
} |
||||
|
||||
script.InsertBefore(containing, varDecl); |
||||
script.Replace(first, new IdentifierExpression(name)); |
||||
} |
||||
for (int i = 1; i < visitor.Matches.Count; i++) { |
||||
script.Replace(visitor.Matches[i], new IdentifierExpression(name)); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
|
||||
// Gets Action/Func delegate types for a given method.
|
||||
IType GetDelegateType(RefactoringContext context, IMethod method, Expression expr) |
||||
{ |
||||
var parameters = new List<IType>(); |
||||
var invoke = expr.Parent as InvocationExpression; |
||||
if (invoke == null) { |
||||
return null; |
||||
} |
||||
foreach (var arg in invoke.Arguments) { |
||||
parameters.Add(context.Resolve(arg).Type); |
||||
} |
||||
|
||||
ITypeDefinition genericType; |
||||
if (method.ReturnType.FullName == "System.Void") { |
||||
genericType = context.Compilation.GetAllTypeDefinitions().FirstOrDefault(t => t.FullName == "System.Action" && t.TypeParameterCount == parameters.Count); |
||||
} else { |
||||
parameters.Add(method.ReturnType); |
||||
genericType = context.Compilation.GetAllTypeDefinitions().FirstOrDefault(t => t.FullName == "System.Func" && t.TypeParameterCount == parameters.Count); |
||||
} |
||||
if (genericType == null) { |
||||
return null; |
||||
} |
||||
return new ParameterizedType(genericType, parameters); |
||||
} |
||||
|
||||
|
||||
class SearchNodeVisitior : DepthFirstAstVisitor |
||||
{ |
||||
readonly AstNode searchForNode; |
||||
public readonly List<AstNode> Matches = new List<AstNode> (); |
||||
|
||||
public SearchNodeVisitior (AstNode searchForNode) |
||||
{ |
||||
this.searchForNode = searchForNode; |
||||
Matches.Add (searchForNode); |
||||
} |
||||
|
||||
protected override void VisitChildren(AstNode node) |
||||
{ |
||||
|
||||
if (node.StartLocation > searchForNode.StartLocation && node.IsMatch (searchForNode)) |
||||
Matches.Add (node); |
||||
base.VisitChildren (node); |
||||
} |
||||
} |
||||
|
||||
|
||||
} |
||||
} |
||||
@ -0,0 +1,175 @@
@@ -0,0 +1,175 @@
|
||||
//
|
||||
// DeclareLocalVariableTests.cs
|
||||
//
|
||||
// Author:
|
||||
// Mike Krüger <mkrueger@xamarin.com>
|
||||
//
|
||||
// Copyright (c) 2012 Xamarin Inc. (http://xamarin.com)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using NUnit.Framework; |
||||
using ICSharpCode.NRefactory.CSharp.Refactoring; |
||||
|
||||
namespace ICSharpCode.NRefactory.CSharp.CodeActions |
||||
{ |
||||
[TestFixture] |
||||
public class DeclareLocalVariableTests : ContextActionTestBase |
||||
{ |
||||
[Test()] |
||||
public void TestSimpleInline () |
||||
{ |
||||
Test<DeclareLocalVariableAction> (@"class TestClass
|
||||
{ |
||||
int Foo() {} |
||||
void Test () |
||||
{ |
||||
<-Foo()->; |
||||
} |
||||
}", @"class TestClass |
||||
{ |
||||
int Foo() {} |
||||
void Test () |
||||
{ |
||||
int i = Foo (); |
||||
} |
||||
}");
|
||||
} |
||||
|
||||
[Test()] |
||||
public void TestReplaceAll () |
||||
{ |
||||
Test<DeclareLocalVariableAction> (@"class TestClass
|
||||
{ |
||||
void Test () |
||||
{ |
||||
Console.WriteLine (<-5 + 3->); |
||||
Console.WriteLine (5 + 3); |
||||
Console.WriteLine (5 + 3); |
||||
} |
||||
}", @"class TestClass |
||||
{ |
||||
void Test () |
||||
{ |
||||
int i = 5 + 3; |
||||
Console.WriteLine (i); |
||||
Console.WriteLine (i); |
||||
Console.WriteLine (i); |
||||
} |
||||
}", 1);
|
||||
} |
||||
|
||||
[Test()] |
||||
public void DeclareLocalExpressionTest () |
||||
{ |
||||
Test<DeclareLocalVariableAction> (@"class TestClass
|
||||
{ |
||||
void Test () |
||||
{ |
||||
Console.WriteLine (1 +<- 9 ->+ 5); |
||||
} |
||||
} |
||||
", @"class TestClass |
||||
{ |
||||
void Test () |
||||
{ |
||||
int i = 9; |
||||
Console.WriteLine (1 + i + 5); |
||||
} |
||||
} |
||||
");
|
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Bug 693855 - Extracting variable from ELSE IF puts it in the wrong place
|
||||
/// </summary>
|
||||
[Test()] |
||||
public void TestBug693855 () |
||||
{ |
||||
Test<DeclareLocalVariableAction> (@"class TestClass
|
||||
{ |
||||
void Test () |
||||
{ |
||||
string str = ""test""; |
||||
if (str == ""something"") { |
||||
//do A
|
||||
} else if (<-str == ""other""->) { |
||||
//do B
|
||||
} else { |
||||
//do C
|
||||
} |
||||
} |
||||
}",
|
||||
@"class TestClass
|
||||
{ |
||||
void Test () |
||||
{ |
||||
string str = ""test""; |
||||
bool b = str == ""other""; |
||||
if (str == ""something"") { |
||||
//do A
|
||||
} else if (b) { |
||||
//do B
|
||||
} else { |
||||
//do C
|
||||
} |
||||
} |
||||
}");
|
||||
} |
||||
|
||||
|
||||
/// <summary>
|
||||
/// Bug 693875 - Extract Local on just method name leaves brackets in wrong place
|
||||
/// </summary>
|
||||
[Test()] |
||||
public void TestBug693875 () |
||||
{ |
||||
Test<DeclareLocalVariableAction> (@"class TestClass
|
||||
{ |
||||
void DoStuff() |
||||
{ |
||||
if (<-GetInt->() == 0) { |
||||
} |
||||
} |
||||
|
||||
int GetInt() |
||||
{ |
||||
return 1; |
||||
} |
||||
}",
|
||||
@"class TestClass
|
||||
{ |
||||
void DoStuff() |
||||
{ |
||||
System.Func<int> getInt = GetInt; |
||||
if (getInt() == 0) { |
||||
} |
||||
} |
||||
|
||||
int GetInt() |
||||
{ |
||||
return 1; |
||||
} |
||||
}");
|
||||
} |
||||
|
||||
|
||||
} |
||||
} |
||||
Loading…
Reference in new issue