Browse Source

Added declare local variable action.

newNRvisualizers
Mike Krüger 14 years ago
parent
commit
1ca41d1e90
  1. 1
      ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
  2. 48
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/CreateMethodDeclarationAction.cs
  3. 154
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/DeclareLocalVariableAction.cs
  4. 3
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/InlineLocalVariableAction.cs
  5. 11
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/IntroduceFormatItemAction.cs
  6. 3
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/SplitDeclarationAndAssignmentAction.cs
  7. 45
      ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringContext.cs
  8. 8
      ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ContextActionTestBase.cs
  9. 175
      ICSharpCode.NRefactory.Tests/CSharp/CodeActions/DeclareLocalVariableTests.cs
  10. 11
      ICSharpCode.NRefactory.Tests/CSharp/CodeActions/TestRefactoringContext.cs
  11. 1
      ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj

1
ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj

@ -352,6 +352,7 @@ @@ -352,6 +352,7 @@
<Compile Include="Refactoring\CodeIssues\InconsistentNamingIssue\NamingConventionService.cs" />
<Compile Include="Refactoring\CodeActions\CreateIndexerAction.cs" />
<Compile Include="Refactoring\CodeActions\InlineLocalVariableAction.cs" />
<Compile Include="Refactoring\CodeActions\DeclareLocalVariableAction.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj">

48
ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/CreateMethodDeclarationAction.cs

@ -228,7 +228,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -228,7 +228,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
return sb.Length == 0 ? "str" : sb.ToString();
}
static string CreateBaseName(AstNode node, IType type)
public static string CreateBaseName(AstNode node, IType type)
{
string name = null;
if (node is DirectionExpression) {
@ -243,16 +243,58 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -243,16 +243,58 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
if (pe.Value is string) {
name = CreateBaseNameFromString(pe.Value.ToString());
} else {
name = char.ToLower(type.Name [0]).ToString();
return char.ToLower(type.Name [0]).ToString();
}
} else {
name = type.Kind == TypeKind.Unknown ? "par" : type.Name;
if (type.Kind == TypeKind.Unknown) {
return "par";
}
name = GuessNameFromType(type);
}
name = char.ToLower(name [0]) + name.Substring(1);
return name;
}
static string GuessNameFromType(IType returnType)
{
switch (returnType.ReflectionName) {
case "System.Byte":
case "System.SByte":
return "b";
case "System.Int16":
case "System.UInt16":
case "System.Int32":
case "System.UInt32":
case "System.Int64":
case "System.UInt64":
return "i";
case "System.Boolean":
return "b";
case "System.DateTime":
return "date";
case "System.Char":
return "ch";
case "System.String":
return "str";
case "System.Exception":
return "e";
case "System.Object":
return "obj";
case "System.Func":
return "func";
case "System.Action":
return "action";
}
return returnType.Name;
}
string GetMethodName(InvocationExpression invocation)
{
if (invocation.Target is IdentifierExpression) {

154
ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/DeclareLocalVariableAction.cs

@ -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);
}
}
}
}

3
ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/InlineLocalVariableAction.cs

@ -38,6 +38,9 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -38,6 +38,9 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
static FindReferences refFinder = new FindReferences();
public IEnumerable<CodeAction> GetActions(RefactoringContext context)
{
if (context.IsSomethingSelected) {
yield break;
}
var node = context.GetNode<VariableDeclarationStatement>();
if (node == null || node.Variables.Count != 1) {
yield break;

11
ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/IntroduceFormatItemAction.cs

@ -93,12 +93,13 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -93,12 +93,13 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
return new PrimitiveExpression (context.SelectedText);
}
static PrimitiveExpression CreateFormatString (RefactoringContext context, PrimitiveExpression pExpr, int argumentNumber)
static PrimitiveExpression CreateFormatString(RefactoringContext context, PrimitiveExpression pExpr, int argumentNumber)
{
var start = context.GetOffset (pExpr.StartLocation);
var end = context.GetOffset (pExpr.EndLocation);
return new PrimitiveExpression ("", context.GetText (start, context.SelectionStart - start) + "{" + argumentNumber + "}" + context.GetText (context.SelectionEnd, end - context.SelectionEnd));
var start = context.GetOffset(pExpr.StartLocation);
var end = context.GetOffset(pExpr.EndLocation);
var sStart = context.GetOffset(context.SelectionStart);
var sEnd = context.GetOffset(context.SelectionEnd);
return new PrimitiveExpression("", context.GetText(start, sStart - start) + "{" + argumentNumber + "}" + context.GetText(sEnd, end - sEnd));
}
}
}

3
ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/SplitDeclarationAndAssignmentAction.cs

@ -36,6 +36,9 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -36,6 +36,9 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
public IEnumerable<CodeAction> GetActions(RefactoringContext context)
{
if (context.IsSomethingSelected) {
yield break;
}
AstType type;
var varDecl = GetVariableDeclarationStatement(context, out type);
if (varDecl == null) {

45
ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringContext.cs

@ -33,11 +33,14 @@ using ICSharpCode.NRefactory.Semantics; @@ -33,11 +33,14 @@ using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using ICSharpCode.NRefactory.Editor;
using System.Collections.Generic;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
public abstract class RefactoringContext : BaseRefactoringContext
{
public RefactoringContext(CSharpAstResolver resolver, CancellationToken cancellationToken) : base (resolver, cancellationToken)
{
}
@ -51,17 +54,27 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -51,17 +54,27 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
return builder.ConvertType(fullType);
}
public AstType CreateShortType (string ns, string name, int typeParameterCount = 0)
public AstType CreateShortType(string ns, string name, int typeParameterCount = 0)
{
foreach (var asm in Compilation.Assemblies) {
var def = asm.GetTypeDefinition (ns, name, typeParameterCount);
if (def != null)
return CreateShortType (def);
var def = asm.GetTypeDefinition(ns, name, typeParameterCount);
if (def != null) {
return CreateShortType(def);
}
}
return new MemberType (new SimpleType (ns), name);
return new MemberType(new SimpleType(ns), name);
}
public virtual IEnumerable<AstNode> GetSelectedNodes()
{
if (!IsSomethingSelected) {
return Enumerable.Empty<AstNode> ();
}
return RootNode.GetNodesBetween(SelectionStart, SelectionEnd);
}
public AstNode GetNode ()
{
return RootNode.GetNodeAt (Location);
@ -83,21 +96,25 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -83,21 +96,25 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
}
public virtual bool IsSomethingSelected {
get { return this.SelectionLength > 0; }
get {
return SelectionStart != TextLocation.Empty;
}
}
public virtual string SelectedText {
get { return string.Empty; }
}
public virtual int SelectionStart {
get { return 0; }
}
public virtual int SelectionEnd {
get { return 0; }
public virtual TextLocation SelectionStart {
get {
return TextLocation.Empty;
}
}
public virtual int SelectionLength {
get { return 0; }
public virtual TextLocation SelectionEnd {
get {
return TextLocation.Empty;
}
}
public abstract int GetOffset (TextLocation location);

8
ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ContextActionTestBase.cs

@ -53,9 +53,9 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions @@ -53,9 +53,9 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions
return sb.ToString ();
}
public void Test<T> (string input, string output) where T : ICodeActionProvider, new ()
public void Test<T> (string input, string output, int action = 0) where T : ICodeActionProvider, new ()
{
string result = RunContextAction (new T (), HomogenizeEol (input));
string result = RunContextAction (new T (), HomogenizeEol (input), action);
bool passed = result == output;
if (!passed) {
Console.WriteLine ("-----------Expected:");
@ -67,7 +67,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions @@ -67,7 +67,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions
}
protected static string RunContextAction (ICodeActionProvider action, string input)
protected static string RunContextAction (ICodeActionProvider action, string input, int actionIndex = 0)
{
var context = TestRefactoringContext.Create (input);
bool isValid = action.GetActions (context).Any ();
@ -76,7 +76,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions @@ -76,7 +76,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions
Console.WriteLine ("invalid node is:" + context.GetNode ());
Assert.IsTrue (isValid, action.GetType () + " is invalid.");
using (var script = context.StartScript ()) {
action.GetActions (context).First ().Run (script);
action.GetActions (context).Skip (actionIndex).First ().Run (script);
}
return context.doc.Text;

175
ICSharpCode.NRefactory.Tests/CSharp/CodeActions/DeclareLocalVariableTests.cs

@ -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;
}
}");
}
}
}

11
ICSharpCode.NRefactory.Tests/CSharp/CodeActions/TestRefactoringContext.cs

@ -112,17 +112,15 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions @@ -112,17 +112,15 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions
#region Text stuff
public override string EolMarker { get { return Environment.NewLine; } }
public override bool IsSomethingSelected { get { return SelectionStart > 0; } }
public override bool IsSomethingSelected { get { return selectionStart > 0; } }
public override string SelectedText { get { return IsSomethingSelected ? doc.GetText (SelectionStart, SelectionLength) : ""; } }
public override string SelectedText { get { return IsSomethingSelected ? doc.GetText (selectionStart, selectionEnd - selectionStart) : ""; } }
int selectionStart;
public override int SelectionStart { get { return selectionStart; } }
public override TextLocation SelectionStart { get { return doc.GetLocation (selectionStart); } }
int selectionEnd;
public override int SelectionEnd { get { return selectionEnd; } }
public override int SelectionLength { get { return IsSomethingSelected ? SelectionEnd - SelectionStart : 0; } }
public override TextLocation SelectionEnd { get { return doc.GetLocation (selectionEnd); } }
public override int GetOffset (TextLocation location)
{
@ -190,7 +188,6 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions @@ -190,7 +188,6 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions
TextLocation location = TextLocation.Empty;
if (idx >= 0)
location = doc.GetLocation (idx);
Console.WriteLine ("idx:" + location);
return new TestRefactoringContext(doc, location, resolver) {
selectionStart = selectionStart,
selectionEnd = selectionEnd

1
ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj

@ -255,6 +255,7 @@ @@ -255,6 +255,7 @@
<Compile Include="CSharp\CodeActions\CreateMethodDeclarationTests.cs" />
<Compile Include="CSharp\CodeActions\CreateIndexerTests.cs" />
<Compile Include="CSharp\CodeActions\InlineLocalVariableTests.cs" />
<Compile Include="CSharp\CodeActions\DeclareLocalVariableTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Mono.Cecil\Mono.Cecil.csproj">

Loading…
Cancel
Save