Browse Source

Added create changed event action.

pull/32/merge
Mike Krüger 12 years ago
parent
commit
79dbb0f9af
  1. 1
      ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
  2. 7
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/CreateClassDeclarationAction.cs
  3. 112
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/CreateEventInvocatorAction.cs
  4. 2
      ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/RemoveBackingStoreAction.cs
  5. 79
      ICSharpCode.NRefactory.CSharp/Refactoring/CreateChangedEvent.cs
  6. 144
      ICSharpCode.NRefactory.Tests/CSharp/CodeActions/CreateChangedEventTests.cs
  7. 22
      ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/EventDeclarationTests.cs
  8. 1
      ICSharpCode.NRefactory.Tests/ICSharpCode.NRefactory.Tests.csproj

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

@ -516,6 +516,7 @@ @@ -516,6 +516,7 @@
<Compile Include="Refactoring\CodeIssues\SimplifyAnonymousMethodToDelegateIssue.cs" />
<Compile Include="Refactoring\CodeIssues\CompilerErrors\CS0127ReturnMustNotBeFollowedByAnyExpression.cs" />
<Compile Include="Analysis\AbiComparer.cs" />
<Compile Include="Refactoring\CreateChangedEvent.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj">

7
ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/CreateClassDeclarationAction.cs

@ -289,8 +289,13 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -289,8 +289,13 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
var decl = new EventDeclaration {
ReturnType = context.CreateShortType(evt.ReturnType),
Modifiers = GetModifiers (evt),
Name = evt.Name
Variables = {
new VariableInitializer {
Name = evt.Name
}
}
};
decl.Variables.Add(new VariableInitializer(evt.Name));
result.AddChild(decl, Roles.TypeMemberRole);
}
}

112
ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/CreateEventInvocatorAction.cs

@ -27,7 +27,6 @@ using System; @@ -27,7 +27,6 @@ using System;
using System.Linq;
using System.Collections.Generic;
using ICSharpCode.NRefactory.TypeSystem;
using System.Threading;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
@ -43,6 +42,63 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -43,6 +42,63 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
set;
}
public static MethodDeclaration CreateEventInvocator (RefactoringContext context, TypeDeclaration declaringType, EventDeclaration eventDeclaration, VariableInitializer initializer, IMethod invokeMethod, bool useExplictType)
{
bool hasSenderParam = false;
IEnumerable<IParameter> pars = invokeMethod.Parameters;
if (invokeMethod.Parameters.Any()) {
var first = invokeMethod.Parameters [0];
if (first.Name == "sender" /*&& first.Type == "System.Object"*/) {
hasSenderParam = true;
pars = invokeMethod.Parameters.Skip(1);
}
}
const string handlerName = "handler";
var arguments = new List<Expression>();
if (hasSenderParam)
arguments.Add(eventDeclaration.HasModifier (Modifiers.Static) ? (Expression)new PrimitiveExpression (null) : new ThisReferenceExpression());
bool useThisMemberReference = false;
foreach (var par in pars) {
arguments.Add(new IdentifierExpression(par.Name));
useThisMemberReference |= par.Name == initializer.Name;
}
var proposedHandlerName = GetNameProposal(initializer);
var modifiers = eventDeclaration.HasModifier(Modifiers.Static) ? Modifiers.Static : Modifiers.Protected | Modifiers.Virtual;
if (declaringType.HasModifier (Modifiers.Sealed)) {
modifiers = Modifiers.None;
}
var methodDeclaration = new MethodDeclaration {
Name = proposedHandlerName,
ReturnType = new PrimitiveType ("void"),
Modifiers = modifiers,
Body = new BlockStatement {
new VariableDeclarationStatement (
useExplictType ? eventDeclaration.ReturnType.Clone () : new PrimitiveType ("var"), handlerName,
useThisMemberReference ?
(Expression)new MemberReferenceExpression (new ThisReferenceExpression (), initializer.Name)
: new IdentifierExpression (initializer.Name)
),
new IfElseStatement {
Condition = new BinaryOperatorExpression (new IdentifierExpression (handlerName), BinaryOperatorType.InEquality, new PrimitiveExpression (null)),
TrueStatement = new ExpressionStatement (new InvocationExpression (new IdentifierExpression (handlerName), arguments))
}
}
};
foreach (var par in pars) {
var typeName = context.CreateShortType(par.Type);
var decl = new ParameterDeclaration(typeName, par.Name);
methodDeclaration.Parameters.Add(decl);
}
return methodDeclaration;
}
static string GetNameProposal(VariableInitializer initializer)
{
return "On" + char.ToUpper(initializer.Name[0]) + initializer.Name.Substring(1);
}
public IEnumerable<CodeAction> GetActions(RefactoringContext context)
{
VariableInitializer initializer;
@ -51,8 +107,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -51,8 +107,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
yield break;
}
var type = (TypeDeclaration)eventDeclaration.Parent;
var proposedHandlerName = "On" + char.ToUpper(initializer.Name [0]) + initializer.Name.Substring(1);
if (type.Members.Any(m => m is MethodDeclaration && ((MethodDeclaration)m).Name == proposedHandlerName)) {
var proposedHandlerName = GetNameProposal(initializer);
if (type.Members.Any(m => m is MethodDeclaration && m.Name == proposedHandlerName)) {
yield break;
}
var resolvedType = context.Resolve(eventDeclaration.ReturnType).Type;
@ -64,50 +120,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -64,50 +120,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
yield break;
}
yield return new CodeAction(context.TranslateString("Create event invocator"), script => {
bool hasSenderParam = false;
IEnumerable<IParameter> pars = invokeMethod.Parameters;
if (invokeMethod.Parameters.Any()) {
var first = invokeMethod.Parameters [0];
if (first.Name == "sender" /*&& first.Type == "System.Object"*/) {
hasSenderParam = true;
pars = invokeMethod.Parameters.Skip(1);
}
}
const string handlerName = "handler";
var arguments = new List<Expression>();
if (hasSenderParam)
arguments.Add(eventDeclaration.HasModifier (Modifiers.Static) ? (Expression)new PrimitiveExpression (null) : new ThisReferenceExpression());
bool useThisMemberReference = false;
foreach (var par in pars) {
arguments.Add(new IdentifierExpression(par.Name));
useThisMemberReference |= par.Name == initializer.Name;
}
var methodDeclaration = new MethodDeclaration() {
Name = proposedHandlerName,
ReturnType = new PrimitiveType ("void"),
Modifiers = eventDeclaration.HasModifier (Modifiers.Static) ? Modifiers.Static : Modifiers.Protected | Modifiers.Virtual,
Body = new BlockStatement () {
new VariableDeclarationStatement (
UseExplictType ? eventDeclaration.ReturnType.Clone () : new PrimitiveType ("var"), handlerName,
useThisMemberReference ?
(Expression)new MemberReferenceExpression (new ThisReferenceExpression (), initializer.Name)
: new IdentifierExpression (initializer.Name)
),
new IfElseStatement () {
Condition = new BinaryOperatorExpression (new IdentifierExpression (handlerName), BinaryOperatorType.InEquality, new PrimitiveExpression (null)),
TrueStatement = new ExpressionStatement (new InvocationExpression (new IdentifierExpression (handlerName), arguments))
}
}
};
foreach (var par in pars) {
var typeName = context.CreateShortType(par.Type);
var decl = new ParameterDeclaration(typeName, par.Name);
methodDeclaration.Parameters.Add(decl);
}
var methodDeclaration = CreateEventInvocator (context, type, eventDeclaration, initializer, invokeMethod, UseExplictType);
script.InsertWithCursor(
context.TranslateString("Create event invocator"),
Script.InsertPosition.After,
@ -115,10 +128,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -115,10 +128,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
);
}, initializer);
}
static EventDeclaration GetEventDeclaration (RefactoringContext context, out VariableInitializer initializer)
{
var result = context.GetNode<EventDeclaration> ();

2
ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/RemoveBackingStoreAction.cs

@ -75,7 +75,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -75,7 +75,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
//
static readonly Version csharp3 = new Version(3, 0);
static IField GetBackingField (RefactoringContext context)
internal static IField GetBackingField (RefactoringContext context)
{
var propertyDeclaration = context.GetNode<PropertyDeclaration> ();
// automatic properties always need getter & setter

79
ICSharpCode.NRefactory.CSharp/Refactoring/CreateChangedEvent.cs

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
//
// CreateChangedEvent.cs
//
// Author:
// Mike Krüger <mkrueger@xamarin.com>
//
// Copyright (c) 2013 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 System.Linq;
using ICSharpCode.NRefactory.CSharp.Resolver;
using ICSharpCode.NRefactory.Semantics;
using ICSharpCode.NRefactory.TypeSystem;
using System.Threading;
using System.Collections.Generic;
namespace ICSharpCode.NRefactory.CSharp.Refactoring
{
[ContextAction("Create changed event for property", Description = "Creates a changed event for an property.")]
public class CreateChangedEvent : ICodeActionProvider
{
public IEnumerable<CodeAction> GetActions(RefactoringContext context)
{
var property = context.GetNode<PropertyDeclaration>();
var field = RemoveBackingStoreAction.GetBackingField(context);
if (field == null)
yield break;
var resolvedType = ReflectionHelper.ParseReflectionName ("System.EventHandler").Resolve (context.Compilation);
if (resolvedType == null)
yield break;
var type = (TypeDeclaration)property.Parent;
yield return new CodeAction(context.TranslateString("Create changed event"), script => {
var eventDeclaration = CreateChangedEventDeclaration (context, property);
var methodDeclaration = CreateEventInvocatorAction.CreateEventInvocator (context, type, eventDeclaration, eventDeclaration.Variables.First (), resolvedType.GetDelegateInvokeMethod (), false);
script.InsertBefore (property.Setter.Body.RBraceToken, new ExpressionStatement (new InvocationExpression (
new IdentifierExpression (methodDeclaration.Name),
new MemberReferenceExpression (new TypeReferenceExpression (context.CreateShortType("System", "EventArgs")), "Empty")
)));
script.InsertWithCursor(
context.TranslateString("Create event invocator"),
Script.InsertPosition.After,
new AstNode[] { eventDeclaration, methodDeclaration }
);
}, property.NameToken);
}
EventDeclaration CreateChangedEventDeclaration (RefactoringContext context, PropertyDeclaration propertyDeclaration)
{
return new EventDeclaration {
Modifiers = propertyDeclaration.HasModifier (Modifiers.Static) ? Modifiers.Public | Modifiers.Static : Modifiers.Public,
ReturnType = context.CreateShortType("System", "EventHandler"),
Variables = {
new VariableInitializer {
Name = propertyDeclaration.Name + "Changed"
}
}
};
}
}
}

144
ICSharpCode.NRefactory.Tests/CSharp/CodeActions/CreateChangedEventTests.cs

@ -0,0 +1,144 @@ @@ -0,0 +1,144 @@
//
// CreateChangedEventTests.cs
//
// Author:
// Mike Krüger <mkrueger@xamarin.com>
//
// Copyright (c) 2013 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 NUnit.Framework;
using ICSharpCode.NRefactory.CSharp.Refactoring;
namespace ICSharpCode.NRefactory.CSharp.CodeActions
{
[TestFixture]
public class CreateChangedEventTests : ContextActionTestBase
{
[Test]
public void TestSimpleCase ()
{
Test<CreateChangedEvent> (@"class TestClass
{
string test;
public string $Test {
get {
return test;
}
set {
test = value;
}
}
}", @"class TestClass
{
string test;
public event System.EventHandler TestChanged;
protected virtual void OnTestChanged (System.EventArgs e)
{
var handler = TestChanged;
if (handler != null)
handler (this, e);
}
public string Test {
get {
return test;
}
set {
test = value;
OnTestChanged (System.EventArgs.Empty);
}
}
}");
}
[Test]
public void TestStaticClassCase ()
{
Test<CreateChangedEvent> (@"static class TestClass
{
static string test;
public static string $Test {
get {
return test;
}
set {
test = value;
}
}
}", @"static class TestClass
{
static string test;
public static event System.EventHandler TestChanged;
static void OnTestChanged (System.EventArgs e)
{
var handler = TestChanged;
if (handler != null)
handler (null, e);
}
public static string Test {
get {
return test;
}
set {
test = value;
OnTestChanged (System.EventArgs.Empty);
}
}
}");
}
[Test]
public void TestSealedCase ()
{
Test<CreateChangedEvent> (@"sealed class TestClass
{
string test;
public string $Test {
get {
return test;
}
set {
test = value;
}
}
}", @"sealed class TestClass
{
string test;
public event System.EventHandler TestChanged;
void OnTestChanged (System.EventArgs e)
{
var handler = TestChanged;
if (handler != null)
handler (this, e);
}
public string Test {
get {
return test;
}
set {
test = value;
OnTestChanged (System.EventArgs.Empty);
}
}
}");
}
}
}

22
ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/EventDeclarationTests.cs

@ -45,17 +45,17 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.TypeMembers @@ -45,17 +45,17 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.TypeMembers
"public event EventHandler A = null, B = delegate {};",
new EventDeclaration {
Modifiers = Modifiers.Public,
ReturnType = new SimpleType("EventHandler"),
Variables = {
new VariableInitializer {
Name = "A",
Initializer = new NullReferenceExpression()
},
new VariableInitializer {
Name = "B",
Initializer = new AnonymousMethodExpression() { Body = new BlockStatement ()}
}
}});
ReturnType = new SimpleType("EventHandler"),
Variables = {
new VariableInitializer {
Name = "A",
Initializer = new NullReferenceExpression()
},
new VariableInitializer {
Name = "B",
Initializer = new AnonymousMethodExpression() { Body = new BlockStatement ()}
}
}});
}
[Test]

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

@ -395,6 +395,7 @@ @@ -395,6 +395,7 @@
<Compile Include="CSharp\CodeIssues\CS0127ReturnMustNotBeFollowedByAnyExpressionTests.cs" />
<Compile Include="CSharp\Analysis\SemanticHighlightingTests.cs" />
<Compile Include="CSharp\Analysis\AbiComparerTests.cs" />
<Compile Include="CSharp\CodeActions\CreateChangedEventTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\cecil\Mono.Cecil.csproj">

Loading…
Cancel
Save