diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj index 3a9759177f..b4ac3ed070 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj @@ -73,9 +73,10 @@ - + + @@ -96,6 +97,9 @@ + + + OverrideEqualsGetHashCodeMethodsDialog.xaml diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs index b981593f37..cabc8fa3ef 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs @@ -95,10 +95,11 @@ namespace CSharpBinding.Completion ICompletionData ICompletionDataFactory.CreateNewOverrideCompletionData(int declarationBegin, IUnresolvedTypeDefinition type, IMember m) { - if ((m.EntityType == EntityType.Method) && (m.Name == "ToString")) - return new OverrideToStringCompletionData(declarationBegin, m, contextAtCaret); - else if ((m.EntityType == EntityType.Method) && (m.Name == "GetHashCode")) + if ((m.SymbolKind == SymbolKind.Method) && (m.Name == "ToString")) return new OverrideToStringCompletionData(declarationBegin, m, contextAtCaret); + else if ((m.SymbolKind == SymbolKind.Method) && ((m.Name == "GetHashCode") + || ((m.Name == "Equals") && ((((IMethod) m)).Parameters.Count == 1) && (((IMethod) m).Parameters.First().Type.FullName == "System.Object")))) + return new OverrideEqualsGetHashCodeCompletionData(declarationBegin, m, contextAtCaret); else return new OverrideCompletionData(declarationBegin, m, contextAtCaret); } diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideGetHashCodeCompletionData.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideEqualsGetHashCodeCompletionData.cs similarity index 54% rename from src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideGetHashCodeCompletionData.cs rename to src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideEqualsGetHashCodeCompletionData.cs index 2b2c8a5a6b..1f88e9e82f 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideGetHashCodeCompletionData.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideEqualsGetHashCodeCompletionData.cs @@ -26,11 +26,11 @@ using CSharpBinding.Refactoring; namespace CSharpBinding.Completion { /// - /// Item for 'override' completion of "GetHashCode()" methods. + /// Item for 'override' completion of "Equals()" and "GetHashCode()" methods. /// - class OverrideGetHashCodeCompletionData : OverrideCompletionData + class OverrideEqualsGetHashCodeCompletionData : OverrideCompletionData { - public OverrideGetHashCodeCompletionData(int declarationBegin, IMember m, CSharpTypeResolveContext contextAtCaret) + public OverrideEqualsGetHashCodeCompletionData(int declarationBegin, IMember m, CSharpTypeResolveContext contextAtCaret) : base(declarationBegin, m, contextAtCaret) { } @@ -55,7 +55,7 @@ namespace CSharpBinding.Completion if (!this.Entity.IsAbstract) { // modify body to call the base method - if (this.Entity.EntityType == EntityType.Method) { + if (this.Entity.SymbolKind == SymbolKind.Method) { var baseCall = new BaseReferenceExpression().Invoke(this.Entity.Name, new Expression[] { }); if (((IMethod)this.Entity).ReturnType.IsKnownType(KnownTypeCode.Void)) baseCallStatement = new ExpressionStatement(baseCall); @@ -88,60 +88,26 @@ namespace CSharpBinding.Completion return; } var resolvedCurrent = typeResolveContext.CurrentTypeDefinition; - var entities = FindFieldsAndProperties(resolvedCurrent).ToList(); - if (entities.Any()) { - IEditorUIService uiService = context.Editor.GetService(typeof(IEditorUIService)) as IEditorUIService; - - ITextAnchor endAnchor = context.Editor.Document.CreateAnchor(context.Editor.Caret.Offset); - endAnchor.MovementType = AnchorMovementType.AfterInsertion; - - ITextAnchor startAnchor = context.Editor.Document.CreateAnchor(context.Editor.Caret.Offset); - startAnchor.MovementType = AnchorMovementType.BeforeInsertion; - - ITextAnchor insertionPos = context.Editor.Document.CreateAnchor(endAnchor.Offset); - insertionPos.MovementType = AnchorMovementType.BeforeInsertion; + IEditorUIService uiService = context.Editor.GetService(typeof(IEditorUIService)) as IEditorUIService; + + ITextAnchor endAnchor = context.Editor.Document.CreateAnchor(context.Editor.Caret.Offset); + endAnchor.MovementType = AnchorMovementType.AfterInsertion; + + ITextAnchor startAnchor = context.Editor.Document.CreateAnchor(context.Editor.Caret.Offset); + startAnchor.MovementType = AnchorMovementType.BeforeInsertion; + + ITextAnchor insertionPos = context.Editor.Document.CreateAnchor(endAnchor.Offset); + insertionPos.MovementType = AnchorMovementType.BeforeInsertion; - InsertionContext insertionContext = new InsertionContext(context.Editor.GetService(typeof(TextArea)) as TextArea, startAnchor.Offset); - -// AbstractInlineRefactorDialog dialog = new OverrideToStringMethodDialog(insertionContext, context.Editor, startAnchor, insertionPos, entities, baseCallStatement); -// dialog.Element = uiService.CreateInlineUIElement(insertionPos, dialog); - - insertionContext.RegisterActiveElement(new InlineRefactorSnippetElement(cxt => null, ""), dialog); - insertionContext.RaiseInsertionCompleted(EventArgs.Empty); - } - else { - if (baseCallStatement != null) { - // Add default base call - MethodDeclaration insertedOverrideMethod = refactoringContext.GetNode().PrevSibling as MethodDeclaration; - if (insertedOverrideMethod == null) - { - // We are not inside of a method declaration - return; - } - using (Script script = refactoringContext.StartScript()) { - script.AddTo(insertedOverrideMethod.Body, baseCallStatement); - } - } - } - } - } - - IEnumerable FindFieldsAndProperties(IType sourceType) - { - int i = 0; - - foreach (var f in sourceType.GetFields().Where(field => !field.IsConst - && field.IsStatic == sourceType.GetDefinition().IsStatic - && field.ReturnType != null)) { - yield return new PropertyOrFieldWrapper(f) { Index = i }; - i++; - } - - foreach (var p in sourceType.GetProperties().Where(prop => prop.CanGet && !prop.IsIndexer - && prop.IsStatic == sourceType.GetDefinition().IsStatic - && prop.ReturnType != null)) { - yield return new PropertyOrFieldWrapper(p) { Index = i }; - i++; + InsertionContext insertionContext = new InsertionContext(context.Editor.GetService(typeof(TextArea)) as TextArea, startAnchor.Offset); + + var current = typeResolveContext.CurrentTypeDefinition; + AbstractInlineRefactorDialog dialog = new OverrideEqualsGetHashCodeMethodsDialog(insertionContext, context.Editor, startAnchor, endAnchor, insertionPos, current, Entity as IMethod, baseCallStatement); + + dialog.Element = uiService.CreateInlineUIElement(insertionPos, dialog); + + insertionContext.RegisterActiveElement(new InlineRefactorSnippetElement(cxt => null, ""), dialog); + insertionContext.RaiseInsertionCompleted(EventArgs.Empty); } } } diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideToStringCompletionData.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideToStringCompletionData.cs index dcdc3a5491..04f6709134 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideToStringCompletionData.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideToStringCompletionData.cs @@ -55,7 +55,7 @@ namespace CSharpBinding.Completion if (!this.Entity.IsAbstract) { // modify body to call the base method - if (this.Entity.EntityType == EntityType.Method) { + if (this.Entity.SymbolKind == SymbolKind.Method) { var baseCall = new BaseReferenceExpression().Invoke(this.Entity.Name, new Expression[] { }); if (((IMethod)this.Entity).ReturnType.IsKnownType(KnownTypeCode.Void)) baseCallStatement = new ExpressionStatement(baseCall); diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/AbstractInlineRefactorDialog.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/AbstractInlineRefactorDialog.cs index 480a48ef9b..228b28f8d5 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/AbstractInlineRefactorDialog.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/AbstractInlineRefactorDialog.cs @@ -12,6 +12,7 @@ using System.Windows.Threading; using ICSharpCode.AvalonEdit.Snippets; using ICSharpCode.Core.Presentation; using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.CSharp.Refactoring; using ICSharpCode.NRefactory.Editor; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.SharpDevelop.Parser; @@ -93,6 +94,16 @@ namespace CSharpBinding.Refactoring optionBindings.Add(binding); } + protected AstType ConvertType(KnownTypeCode knownTypeCode) + { + IType type = refactoringContext.Compilation.FindType(knownTypeCode); + if (type != null) + return ConvertType(type); + + // Backup solution + return new SimpleType(KnownTypeReference.GetCSharpNameByTypeCode(knownTypeCode)); + } + protected AstType ConvertType(IType type) { return refactoringContext.CreateShortType(type); diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/Options.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/Options.cs new file mode 100644 index 0000000000..51dbfc4c10 --- /dev/null +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/Options.cs @@ -0,0 +1,50 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Diagnostics; +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop; + +namespace CSharpBinding.Refactoring +{ + /// + /// Description of Options. + /// + public static class Options + { + static Properties properties; + + public static Properties Properties { + get { + Debug.Assert(properties != null); + return properties; + } + } + + static Options() + { + properties = SD.PropertyService.Get("SharpRefactoringOptions", new Properties()); + } + + public static bool AddIEquatableInterface { + get { return properties.Get("AddIEquatableInterface", false); } + set { properties.Set("AddIEquatableInterface", value); } + } + + public static bool AddOtherMethod { + get { return properties.Get("AddOtherMethod", true); } + set { properties.Set("AddOtherMethod", value); } + } + + public static bool SurroundWithRegion { + get { return properties.Get("SurroundWithRegion", true); } + set { properties.Set("SurroundWithRegion", value); } + } + + public static bool AddOperatorOverloads { + get { return properties.Get("AddOperatorOverloads", true); } + set { properties.Set("AddOperatorOverloads", value); } + } + } +} diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsDialog.xaml.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsDialog.xaml.cs index d16d4623f5..5bc5d5d18e 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsDialog.xaml.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsDialog.xaml.cs @@ -2,6 +2,7 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using System.CodeDom; using System.Collections.Generic; using System.Linq; using System.Text; @@ -9,11 +10,9 @@ using System.Text; using ICSharpCode.AvalonEdit.Snippets; using ICSharpCode.Core; using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.CSharp.Refactoring; using ICSharpCode.NRefactory.Editor; using ICSharpCode.NRefactory.TypeSystem; -using ICSharpCode.NRefactory.Ast; -using ICSharpCode.SharpDevelop.Dom; -using ICSharpCode.SharpDevelop.Dom.Refactoring; using ICSharpCode.SharpDevelop.Editor; using Dom = ICSharpCode.SharpDevelop.Dom; @@ -24,13 +23,13 @@ namespace CSharpBinding.Refactoring /// public partial class OverrideEqualsGetHashCodeMethodsDialog : AbstractInlineRefactorDialog { - IType selectedClass; + ITypeDefinition selectedClass; ITextAnchor startAnchor; IMethod selectedMethod; AstNode baseCallNode; public OverrideEqualsGetHashCodeMethodsDialog(InsertionContext context, ITextEditor editor, ITextAnchor startAnchor, ITextAnchor endAnchor, - ITextAnchor insertionPosition, IType selectedClass, IMethod selectedMethod, AstNode baseCallNode) + ITextAnchor insertionPosition, ITypeDefinition selectedClass, IMethod selectedMethod, AstNode baseCallNode) : base(context, editor, insertionPosition) { if (selectedClass == null) @@ -51,15 +50,19 @@ namespace CSharpBinding.Refactoring addOtherMethod.Content = StringParser.Parse("${res:AddIns.SharpRefactoring.OverrideEqualsGetHashCodeMethods.AddOtherMethod}", new StringTagPair("otherMethod", otherMethod)); - addIEquatable.IsEnabled = !selectedClass.BaseTypes.Any( + addIEquatable.IsEnabled = !selectedClass.GetAllBaseTypes().Any( type => { - if (!type.IsGenericReturnType) + if (!type.IsParameterized || (type.TypeParameterCount != 1)) return false; - var genericType = type.CastToGenericReturnType(); - var boundTo = genericType.TypeParameter.BoundTo; - if (boundTo == null) + if (type.FullName != "System.IEquatable") return false; - return boundTo.Name == selectedClass.Name; + return type.TypeArguments.First().FullName == selectedClass.FullName; + +// var genericType = type.CastToGenericReturnType(); +// var boundTo = genericType.TypeParameter.BoundTo; +// if (boundTo == null) +// return false; +// return boundTo.Name == selectedClass.Name; } ); } @@ -82,11 +85,11 @@ namespace CSharpBinding.Refactoring return type != null && type.FullName != "System.Single" && type.FullName != "System.Double" - && (!type.IsReferenceType + && (!IsReferenceType(type) || type.FullName == "System.String"); } - static Expression TestEquality(string other, IField field) + Expression TestEquality(string other, IField field) { if (CanCompareEqualityWithOperator(field.ReturnType)) { return new BinaryOperatorExpression(new MemberReferenceExpression(new ThisReferenceExpression(), field.Name), @@ -94,7 +97,7 @@ namespace CSharpBinding.Refactoring new MemberReferenceExpression(new IdentifierExpression(other), field.Name)); } else { InvocationExpression ie = new InvocationExpression( - new MemberReferenceExpression(new TypeReferenceExpression(new TypeReference("System.Object", true)), "Equals") + new MemberReferenceExpression(new TypeReferenceExpression(ConvertType(KnownTypeCode.Object)), "Equals") ); ie.Arguments.Add(new MemberReferenceExpression(new ThisReferenceExpression(), field.Name)); ie.Arguments.Add(new MemberReferenceExpression(new IdentifierExpression(other), field.Name)); @@ -102,7 +105,7 @@ namespace CSharpBinding.Refactoring } } - static Expression TestEquality(string other, IProperty property) + Expression TestEquality(string other, IProperty property) { if (CanCompareEqualityWithOperator(property.ReturnType)) { return new BinaryOperatorExpression(new MemberReferenceExpression(new ThisReferenceExpression(), property.Name), @@ -110,7 +113,7 @@ namespace CSharpBinding.Refactoring new MemberReferenceExpression(new IdentifierExpression(other), property.Name)); } else { InvocationExpression ie = new InvocationExpression( - new MemberReferenceExpression(new TypeReferenceExpression(new SimpleType("System.Object")), "Equals") + new MemberReferenceExpression(new TypeReferenceExpression(ConvertType(KnownTypeCode.Object)), "Equals") ); ie.Arguments.Add(new MemberReferenceExpression(new ThisReferenceExpression(), property.Name)); ie.Arguments.Add(new MemberReferenceExpression(new IdentifierExpression(other), property.Name)); @@ -118,6 +121,15 @@ namespace CSharpBinding.Refactoring } } + static bool IsReferenceType(IType type) + { + if (type.IsReferenceType.HasValue) { + return type.IsReferenceType.Value; + } + + return false; + } + protected override string GenerateCode(ITypeDefinition currentClass) { StringBuilder code = new StringBuilder(); @@ -126,8 +138,18 @@ namespace CSharpBinding.Refactoring string indent = DocumentUtilitites.GetWhitespaceAfter(editor.Document, line.Offset); - CodeGenerator generator = language.CodeGenerator; +// CodeGenerator generator = language.CodeGenerator; + + MethodDeclaration insertedOverrideMethod = refactoringContext.GetNode().PrevSibling as MethodDeclaration; + if (insertedOverrideMethod == null) + { + // We are not inside of a method declaration + return null; + } + using (Script script = refactoringContext.StartScript()) { + NewLineNode nextNewLineNode = insertedOverrideMethod.NextSibling as NewLineNode; + // if (Options.AddIEquatableInterface) { // TODO : add IEquatable to class // IAmbience ambience = currentClass.CompilationUnit.Language.GetAmbience(); @@ -152,120 +174,132 @@ namespace CSharpBinding.Refactoring // // editor.Document.Replace(startOffset, endOffset - startOffset, a); // } - - if (Options.SurroundWithRegion) { - editor.Document.InsertNormalized(startAnchor.Offset, "#region Equals and GetHashCode implementation\n" + indent); - } - - string codeForMethodBody; - - if ("Equals".Equals(selectedMethod.Name, StringComparison.Ordinal)) { - IList equalsOverrides = CreateEqualsOverrides(currentClass); - MethodDeclaration defaultOverride = equalsOverrides.First(); - equalsOverrides = equalsOverrides.Skip(1).ToList(); - StringBuilder builder = new StringBuilder(); - - foreach (AbstractNode element in defaultOverride.Body.Children.OfType()) { - builder.Append(language.CodeGenerator.GenerateCode(element, indent + "\t")); + if (Options.SurroundWithRegion) { + script.InsertBefore(insertedOverrideMethod, new PreProcessorDirective(PreProcessorDirectiveType.Region, "Equals and GetHashCode implementation")); } - codeForMethodBody = builder.ToString().Trim(); - - if (addOtherMethod.IsChecked == true) { - if (equalsOverrides.Any()) - code.Append(indent + "\n" + string.Join("\n", equalsOverrides.Select(item => generator.GenerateCode(item, indent)))); - code.Append(indent + "\n" + generator.GenerateCode(CreateGetHashCodeOverride(currentClass), indent)); - } - } else { - StringBuilder builder = new StringBuilder(); - - foreach (AbstractNode element in CreateGetHashCodeOverride(currentClass).Body.Children.OfType()) { - builder.Append(language.CodeGenerator.GenerateCode(element, indent + "\t")); + if ("Equals".Equals(selectedMethod.Name, StringComparison.Ordinal)) { + IList equalsOverrides = CreateEqualsOverrides(currentClass); + MethodDeclaration defaultOverride = equalsOverrides.First(); + equalsOverrides = equalsOverrides.Skip(1).ToList(); + + // Insert children of default Equals method into + foreach (AstNode element in defaultOverride.Body.Children) { + script.AddTo(insertedOverrideMethod.Body, element.Clone()); + } + + // Add other Equals() overrides after our main inserted method + if (addOtherMethod.IsChecked == true) { + if (equalsOverrides.Any()) { + foreach (var equalsMethod in equalsOverrides) { + AppendNewLine(script, insertedOverrideMethod, nextNewLineNode); + script.InsertAfter(insertedOverrideMethod, equalsMethod); + } + } + + AppendNewLine(script, insertedOverrideMethod, nextNewLineNode); + script.InsertAfter(insertedOverrideMethod, CreateGetHashCodeOverride(currentClass)); + } + } else { + StringBuilder builder = new StringBuilder(); + + foreach (AstNode element in CreateGetHashCodeOverride(currentClass).Body.Children) { + script.AddTo(insertedOverrideMethod.Body, element.Clone()); + } + + if (addOtherMethod.IsChecked == true) { + foreach (var equalsMethod in CreateEqualsOverrides(currentClass)) { + AppendNewLine(script, insertedOverrideMethod, nextNewLineNode); + script.InsertAfter(insertedOverrideMethod, equalsMethod); + } + } } - codeForMethodBody = builder.ToString().Trim(); - - if (addOtherMethod.IsChecked == true) - code.Append(indent + "\n" + string.Join("\n", CreateEqualsOverrides(currentClass).Select(item => generator.GenerateCode(item, indent)))); - } - - if (Options.AddOperatorOverloads) { - var checkStatements = new[] { - new IfElseStatement( - new InvocationExpression( - new IdentifierExpression("ReferenceEquals"), - new List() { new IdentifierExpression("lhs"), new IdentifierExpression("rhs") } - ), - new ReturnStatement(new PrimitiveExpression(true)) - ), - new IfElseStatement( - new BinaryOperatorExpression( + if (Options.AddOperatorOverloads) { + var checkStatements = new[] { + new IfElseStatement( new InvocationExpression( new IdentifierExpression("ReferenceEquals"), - new List() { new IdentifierExpression("lhs"), new PrimitiveExpression(null) } + new List() { new IdentifierExpression("lhs"), new IdentifierExpression("rhs") } ), - BinaryOperatorType.LogicalOr, - new InvocationExpression( - new IdentifierExpression("ReferenceEquals"), - new List() { new IdentifierExpression("rhs"), new PrimitiveExpression(null) } - ) + new ReturnStatement(new PrimitiveExpression(true)) ), - new ReturnStatement(new PrimitiveExpression(false)) - ) - }; - - BlockStatement equalsOpBody = new BlockStatement() { - Children = { + new IfElseStatement( + new BinaryOperatorExpression( + new InvocationExpression( + new IdentifierExpression("ReferenceEquals"), + new List() { new IdentifierExpression("lhs"), new PrimitiveExpression(null) } + ), + BinaryOperatorType.ConditionalOr, + new InvocationExpression( + new IdentifierExpression("ReferenceEquals"), + new List() { new IdentifierExpression("rhs"), new PrimitiveExpression(null) } + ) + ), + new ReturnStatement(new PrimitiveExpression(false)) + ) + }; + + BlockStatement equalsOpBody = new BlockStatement(); + + if (currentClass.Kind == TypeKind.Class) { + foreach (var statement in checkStatements) { + equalsOpBody.Add(statement); + } + } + + equalsOpBody.Add( new ReturnStatement( new InvocationExpression( new MemberReferenceExpression(new IdentifierExpression("lhs"), "Equals"), new List() { new IdentifierExpression("rhs") } ) ) - } - }; - - if (currentClass.ClassType == Dom.ClassType.Class) { - equalsOpBody.Children.InsertRange(0, checkStatements); - } + ); - BlockStatement notEqualsOpBody = new BlockStatement() { - Children = { - new ReturnStatement( - new UnaryOperatorExpression( - new ParenthesizedExpression( - new BinaryOperatorExpression( - new IdentifierExpression("lhs"), - BinaryOperatorType.Equality, - new IdentifierExpression("rhs") - ) - ), - UnaryOperatorType.Not + BlockStatement notEqualsOpBody = new BlockStatement(); + notEqualsOpBody.Add(new ReturnStatement( + new UnaryOperatorExpression( + UnaryOperatorType.Not, + new ParenthesizedExpression( + new BinaryOperatorExpression( + new IdentifierExpression("lhs"), + BinaryOperatorType.Equality, + new IdentifierExpression("rhs") + ) ) ) - } - }; + ) + ); + + AppendNewLine(script, insertedOverrideMethod, nextNewLineNode); + script.InsertAfter(insertedOverrideMethod, CreateOperatorOverload(OperatorType.Equality, currentClass, equalsOpBody)); + AppendNewLine(script, insertedOverrideMethod, nextNewLineNode); + script.InsertAfter(insertedOverrideMethod, CreateOperatorOverload(OperatorType.Inequality, currentClass, notEqualsOpBody)); + } - code.Append(indent + "\n" + generator.GenerateCode(CreateOperatorOverload(OverloadableOperatorType.Equality, currentClass, equalsOpBody), indent)); - code.Append(indent + "\n" + generator.GenerateCode(CreateOperatorOverload(OverloadableOperatorType.InEquality, currentClass, notEqualsOpBody), indent)); - } - - if (Options.SurroundWithRegion) { - code.AppendLine(indent + "#endregion"); + if (Options.SurroundWithRegion) { + AppendNewLine(script, insertedOverrideMethod, nextNewLineNode); + script.InsertAfter(insertedOverrideMethod, new PreProcessorDirective(PreProcessorDirectiveType.Endregion)); + } } - editor.Document.InsertNormalized(insertionEndAnchor.Offset, code.ToString()); - - return codeForMethodBody; + return null; + } + + void AppendNewLine(Script script, AstNode afterNode, NewLineNode newLineNode) + { + if (newLineNode != null) + script.InsertAfter(afterNode, newLineNode.Clone()); } List CreateEqualsOverrides(IType currentClass) { List methods = new List(); - AstType boolReference = new SimpleType("System.Boolean"); - AstType objectReference = new SimpleType("System.Object", true); + AstType boolReference = ConvertType(KnownTypeCode.Boolean); + AstType objectReference = ConvertType(KnownTypeCode.Object); MethodDeclaration method = new MethodDeclaration { Name = "Equals", @@ -275,22 +309,24 @@ namespace CSharpBinding.Refactoring method.Parameters.Add(new ParameterDeclaration(objectReference, "obj")); method.Body = new BlockStatement(); - AstType currentType = ConvertType(currentClass.DefaultReturnType); - + AstType currentType = ConvertType(currentClass); Expression expr = null; - if (currentClass.ClassType == Dom.ClassType.Struct) { + if (currentClass.Kind == TypeKind.Struct) { // return obj is CurrentType && Equals((CurrentType)obj); - expr = new IsExpression(new IdentifierExpression("obj"), currentType); + expr = new IsExpression() { + Expression = new IdentifierExpression("obj"), + Type = currentType.Clone() + }; expr = new ParenthesizedExpression(expr); expr = new BinaryOperatorExpression( - expr, BinaryOperatorType.LogicalAnd, + expr, BinaryOperatorType.ConditionalAnd, new InvocationExpression( new IdentifierExpression("Equals"), new List { - new CastExpression(currentType, new IdentifierExpression("obj"), CastType.Cast) + new CastExpression(currentType.Clone(), new IdentifierExpression("obj")) })); - method.Body.AddChild(new ReturnStatement(expr)); + method.Body.Add(new ReturnStatement(expr)); methods.Add(method); @@ -298,141 +334,132 @@ namespace CSharpBinding.Refactoring method = new MethodDeclaration { Name = "Equals", Modifiers = Modifiers.Public, - ReturnType = boolReference + ReturnType = boolReference.Clone() }; method.Parameters.Add(new ParameterDeclaration(currentType, "other")); method.Body = new BlockStatement(); } else { - method.Body.AddChild(new VariableDeclaration( + method.Body.Add(new VariableDeclarationStatement( + currentType.Clone(), "other", - new CastExpression(currentType, new IdentifierExpression("obj")), - currentType)); - method.Body.AddChild(new IfElseStatement( + new CastExpression(currentType.Clone(), new IdentifierExpression("obj")))); + method.Body.Add(new IfElseStatement( new BinaryOperatorExpression(new IdentifierExpression("other"), BinaryOperatorType.Equality, new PrimitiveExpression(null, "null")), new ReturnStatement(new PrimitiveExpression(false, "false")))); - -// expr = new BinaryOperatorExpression(new ThisReferenceExpression(), -// BinaryOperatorType.ReferenceEquality, -// new IdentifierExpression("obj")); -// method.Body.AddChild(new IfElseStatement(expr, new ReturnStatement(new PrimitiveExpression(true, "true")))); } - expr = null; - foreach (IField field in currentClass.Fields) { + foreach (IField field in currentClass.GetFields()) { if (field.IsStatic) continue; if (expr == null) { expr = TestEquality("other", field); } else { - expr = new BinaryOperatorExpression(expr, BinaryOperatorType.LogicalAnd, + expr = new BinaryOperatorExpression(expr, BinaryOperatorType.ConditionalAnd, TestEquality("other", field)); } } - foreach (IProperty property in currentClass.Properties) { + foreach (IProperty property in currentClass.GetProperties()) { if (property.IsStatic || !property.IsAutoImplemented()) continue; if (expr == null) { expr = TestEquality("other", property); } else { - expr = new BinaryOperatorExpression(expr, BinaryOperatorType.LogicalAnd, + expr = new BinaryOperatorExpression(expr, BinaryOperatorType.ConditionalAnd, TestEquality("other", property)); } } - method.Body.AddChild(new ReturnStatement(expr ?? new PrimitiveExpression(true, "true"))); + method.Body.Add(new ReturnStatement(expr ?? new PrimitiveExpression(true, "true"))); methods.Add(method); return methods; } - MethodDeclaration CreateGetHashCodeOverride(IClass currentClass) + MethodDeclaration CreateGetHashCodeOverride(ITypeDefinition currentClass) { - TypeReference intReference = new TypeReference("System.Int32", true); - VariableDeclaration hashCodeVar = new VariableDeclaration("hashCode", new PrimitiveExpression(0, "0"), intReference); + const string hashCodeVarName = "hashCode"; + AstType intReference = ConvertType(KnownTypeCode.Int32); + VariableDeclarationStatement hashCodeVar = new VariableDeclarationStatement(intReference, hashCodeVarName, new PrimitiveExpression(0, "0")); + // Create new method declaration (to insert after main inserted method) MethodDeclaration getHashCodeMethod = new MethodDeclaration { Name = "GetHashCode", - Modifier = Modifiers.Public | Modifiers.Override, - TypeReference = intReference, + Modifiers = Modifiers.Public | Modifiers.Override, + ReturnType = intReference.Clone(), Body = new BlockStatement() }; - getHashCodeMethod.Body.AddChild(new LocalVariableDeclaration(hashCodeVar)); + getHashCodeMethod.Body.Add(hashCodeVar); if (currentClass.Fields.Any(f => !f.IsStatic) || currentClass.Properties.Any(p => !p.IsStatic && p.IsAutoImplemented())) { - bool usePrimeMultiplication = currentClass.ProjectContent.Language == LanguageProperties.CSharp; - BlockStatement hashCalculationBlock; - - if (usePrimeMultiplication) { - hashCalculationBlock = new BlockStatement(); - getHashCodeMethod.Body.AddChild(new UncheckedStatement(hashCalculationBlock)); - } else { - hashCalculationBlock = getHashCodeMethod.Body; - } + bool usePrimeMultiplication = true; // Always leave true for C#? + BlockStatement hashCalculationBlock = new BlockStatement(); + getHashCodeMethod.Body.Add(new UncheckedStatement(hashCalculationBlock)); +// hashCalculationBlock = getHashCodeMethod.Body; int fieldIndex = 0; foreach (IField field in currentClass.Fields) { if (field.IsStatic) continue; - AddToBlock(hashCodeVar, getHashCodeMethod, usePrimeMultiplication, hashCalculationBlock, ref fieldIndex, field); + AddToBlock(hashCodeVarName, usePrimeMultiplication, hashCalculationBlock, ref fieldIndex, field); } foreach (IProperty property in currentClass.Properties) { if (property.IsStatic || !property.IsAutoImplemented()) continue; - AddToBlock(hashCodeVar, getHashCodeMethod, usePrimeMultiplication, hashCalculationBlock, ref fieldIndex, property); + AddToBlock(hashCodeVarName, usePrimeMultiplication, hashCalculationBlock, ref fieldIndex, property); } } - getHashCodeMethod.Body.AddChild(new ReturnStatement(new IdentifierExpression(hashCodeVar.Name))); + getHashCodeMethod.Body.Add(new ReturnStatement(new IdentifierExpression(hashCodeVarName))); return getHashCodeMethod; } - - void AddToBlock(VariableDeclaration hashCodeVar, MethodDeclaration getHashCodeMethod, bool usePrimeMultiplication, BlockStatement hashCalculationBlock, ref int fieldIndex, IField field) + + void AddToBlock(string hashCodeVarName, bool usePrimeMultiplication, BlockStatement hashCalculationBlock, ref int fieldIndex, IField field) { Expression expr = new InvocationExpression(new MemberReferenceExpression(new IdentifierExpression(field.Name), "GetHashCode")); if (usePrimeMultiplication) { int prime = largePrimes[fieldIndex++ % largePrimes.Length]; - expr = new AssignmentExpression(new IdentifierExpression(hashCodeVar.Name), AssignmentOperatorType.Add, new BinaryOperatorExpression(new PrimitiveExpression(prime, prime.ToString()), BinaryOperatorType.Multiply, expr)); + expr = new AssignmentExpression(new IdentifierExpression(hashCodeVarName), AssignmentOperatorType.Add, new BinaryOperatorExpression(new PrimitiveExpression(prime, prime.ToString()), BinaryOperatorType.Multiply, expr)); } else { - expr = new AssignmentExpression(new IdentifierExpression(hashCodeVar.Name), AssignmentOperatorType.ExclusiveOr, expr); + expr = new AssignmentExpression(new IdentifierExpression(hashCodeVarName), AssignmentOperatorType.ExclusiveOr, expr); } - if (IsValueType(field.ReturnType)) { - hashCalculationBlock.AddChild(new ExpressionStatement(expr)); + if (IsReferenceType(field.ReturnType)) { + hashCalculationBlock.Add(new IfElseStatement(new BinaryOperatorExpression(new IdentifierExpression(field.Name), BinaryOperatorType.InEquality, new PrimitiveExpression(null, "null")), new ExpressionStatement(expr))); } else { - hashCalculationBlock.AddChild(new IfElseStatement(new BinaryOperatorExpression(new IdentifierExpression(field.Name), BinaryOperatorType.ReferenceInequality, new PrimitiveExpression(null, "null")), new ExpressionStatement(expr))); + hashCalculationBlock.Add(new ExpressionStatement(expr)); } } - void AddToBlock(VariableDeclaration hashCodeVar, MethodDeclaration getHashCodeMethod, bool usePrimeMultiplication, BlockStatement hashCalculationBlock, ref int fieldIndex, IProperty property) + void AddToBlock(string hashCodeVarName, bool usePrimeMultiplication, BlockStatement hashCalculationBlock, ref int fieldIndex, IProperty property) { Expression expr = new InvocationExpression(new MemberReferenceExpression(new IdentifierExpression(property.Name), "GetHashCode")); if (usePrimeMultiplication) { int prime = largePrimes[fieldIndex++ % largePrimes.Length]; - expr = new AssignmentExpression(new IdentifierExpression(hashCodeVar.Name), AssignmentOperatorType.Add, new BinaryOperatorExpression(new PrimitiveExpression(prime, prime.ToString()), BinaryOperatorType.Multiply, expr)); + expr = new AssignmentExpression(new IdentifierExpression(hashCodeVarName), AssignmentOperatorType.Add, new BinaryOperatorExpression(new PrimitiveExpression(prime, prime.ToString()), BinaryOperatorType.Multiply, expr)); } else { - expr = new AssignmentExpression(new IdentifierExpression(hashCodeVar.Name), AssignmentOperatorType.ExclusiveOr, expr); + expr = new AssignmentExpression(new IdentifierExpression(hashCodeVarName), AssignmentOperatorType.ExclusiveOr, expr); } - if (IsValueType(property.ReturnType)) { - hashCalculationBlock.AddChild(new ExpressionStatement(expr)); + if (IsReferenceType(property.ReturnType)) { + hashCalculationBlock.Add(new IfElseStatement(new BinaryOperatorExpression(new IdentifierExpression(property.Name), BinaryOperatorType.InEquality, new PrimitiveExpression(null, "null")), new ExpressionStatement(expr))); } else { - hashCalculationBlock.AddChild(new IfElseStatement(new BinaryOperatorExpression(new IdentifierExpression(property.Name), BinaryOperatorType.ReferenceInequality, new PrimitiveExpression(null, "null")), new ExpressionStatement(expr))); + hashCalculationBlock.Add(new ExpressionStatement(expr)); } } - OperatorDeclaration CreateOperatorOverload(OverloadableOperatorType op, IClass currentClass, BlockStatement body) + OperatorDeclaration CreateOperatorOverload(OperatorType op, ITypeDefinition currentClass, BlockStatement body) { return new OperatorDeclaration() { - OverloadableOperator = op, - TypeReference = new TypeReference("System.Boolean", true), + OperatorType = op, + ReturnType = ConvertType(KnownTypeCode.Boolean), Parameters = { - new ParameterDeclarationExpression(ConvertType(currentClass.DefaultReturnType), "lhs"), - new ParameterDeclarationExpression(ConvertType(currentClass.DefaultReturnType), "rhs") + new ParameterDeclaration(ConvertType(currentClass), "lhs"), + new ParameterDeclaration(ConvertType(currentClass), "rhs") }, - Modifier = Modifiers.Public | Modifiers.Static, + Modifiers = Modifiers.Public | Modifiers.Static, Body = body }; } @@ -441,15 +468,28 @@ namespace CSharpBinding.Refactoring { base.CancelButtonClick(sender, e); - editor.Document.Insert(anchor.Offset, baseCall); - editor.Select(anchor.Offset, baseCall.Length); +// editor.Document.Insert(anchor.Offset, baseCall); +// editor.Select(anchor.Offset, baseCall.Length); + + if (baseCallNode != null) { + // Insert at least the base call + MethodDeclaration insertedOverrideMethod = refactoringContext.GetNode().PrevSibling as MethodDeclaration; + if (insertedOverrideMethod == null) + { + // We are not inside of a method declaration + return; + } + using (Script script = refactoringContext.StartScript()) { + script.AddTo(insertedOverrideMethod.Body, baseCallNode); + } + } } protected override void OKButtonClick(object sender, System.Windows.RoutedEventArgs e) { base.OKButtonClick(sender, e); - editor.Caret.Offset = insertionEndAnchor.Offset; +// editor.Caret.Offset = insertionEndAnchor.Offset; } } } diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsRefactoring.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsRefactoring.cs deleted file mode 100644 index 3b77ad2b81..0000000000 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsRefactoring.cs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) - -using System; -using System.Linq; -using ICSharpCode.AvalonEdit.Editing; -using ICSharpCode.AvalonEdit.Snippets; -using ICSharpCode.NRefactory.Ast; -using ICSharpCode.SharpDevelop; -using ICSharpCode.SharpDevelop.Dom; -using ICSharpCode.SharpDevelop.Dom.Refactoring; -using ICSharpCode.SharpDevelop.Editor; -using ICSharpCode.SharpDevelop.Editor.CodeCompletion; -using SharpRefactoring.Gui; - -namespace SharpRefactoring -{ - public class OverrideEqualsGetHashCodeMethodsRefactoring : ICompletionItemHandler - { - public void Insert(CompletionContext context, ICompletionItem item) - { - if (item == null) - throw new ArgumentNullException("item"); - - if (!(item is OverrideCompletionItem)) - throw new ArgumentException("item is not an OverrideCompletionItem"); - - OverrideCompletionItem completionItem = item as OverrideCompletionItem; - - ITextEditor textEditor = context.Editor; - - IEditorUIService uiService = textEditor.GetService(typeof(IEditorUIService)) as IEditorUIService; - - if (uiService == null) - return; - - ParseInformation parseInfo = ParserService.GetParseInformation(textEditor.FileName); - - if (parseInfo == null) - return; - - CodeGenerator generator = parseInfo.CompilationUnit.Language.CodeGenerator; - IClass current = parseInfo.CompilationUnit.GetInnermostClass(textEditor.Caret.Line, textEditor.Caret.Column); - ClassFinder finder = new ClassFinder(current, textEditor.Caret.Line, textEditor.Caret.Column); - - if (current == null) - return; - - using (textEditor.Document.OpenUndoGroup()) { - ITextAnchor startAnchor = textEditor.Document.CreateAnchor(textEditor.Caret.Offset); - startAnchor.MovementType = AnchorMovementType.BeforeInsertion; - - ITextAnchor endAnchor = textEditor.Document.CreateAnchor(textEditor.Caret.Offset); - endAnchor.MovementType = AnchorMovementType.AfterInsertion; - - MethodDeclaration member = (MethodDeclaration)generator.GetOverridingMethod(completionItem.Member, finder); - - string indent = DocumentUtilitites.GetWhitespaceBefore(textEditor.Document, textEditor.Caret.Offset); - string codeForBaseCall = generator.GenerateCode(member.Body.Children.OfType().First(), ""); - string code = generator.GenerateCode(member, indent); - int marker = code.IndexOf(codeForBaseCall); - - textEditor.Document.Insert(startAnchor.Offset, code.Substring(0, marker).TrimStart()); - - ITextAnchor insertionPos = textEditor.Document.CreateAnchor(endAnchor.Offset); - insertionPos.MovementType = AnchorMovementType.BeforeInsertion; - - InsertionContext insertionContext = new InsertionContext(textEditor.GetService(typeof(TextArea)) as TextArea, startAnchor.Offset); - - AbstractInlineRefactorDialog dialog = new OverrideEqualsGetHashCodeMethodsDialog(insertionContext, textEditor, startAnchor, endAnchor, insertionPos, current, completionItem.Member as IMethod, codeForBaseCall.Trim()); - dialog.Element = uiService.CreateInlineUIElement(insertionPos, dialog); - - textEditor.Document.InsertNormalized(endAnchor.Offset, Environment.NewLine + code.Substring(marker + codeForBaseCall.Length)); - - insertionContext.RegisterActiveElement(new InlineRefactorSnippetElement(cxt => null, ""), dialog); - insertionContext.RaiseInsertionCompleted(EventArgs.Empty); - } - } - - public bool Handles(ICompletionItem item) - { - return item is OverrideCompletionItem && (item.Text == "GetHashCode()" || item.Text == "Equals(object obj)"); - } - } -} diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideToStringMethodDialog.xaml.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideToStringMethodDialog.xaml.cs index c4afba82fc..f3967dcbe6 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideToStringMethodDialog.xaml.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideToStringMethodDialog.xaml.cs @@ -25,7 +25,6 @@ namespace CSharpBinding.Refactoring { AstNode baseCallNode; string insertedCode; - ITextEditor editor; public OverrideToStringMethodDialog(InsertionContext context, ITextEditor editor, ITextAnchor startAnchor, ITextAnchor anchor, IList fields, AstNode baseCallNode) : base(context, editor, anchor) @@ -34,7 +33,6 @@ namespace CSharpBinding.Refactoring this.baseCallNode = baseCallNode; this.listBox.ItemsSource = fields; - this.editor = editor; listBox.SelectAll(); } @@ -45,7 +43,7 @@ namespace CSharpBinding.Refactoring PrimitiveExpression formatString = new PrimitiveExpression(GenerateFormatString(currentClass, editor.Language.CodeGenerator, fields)); List param = new List() { formatString }; ReturnStatement ret = new ReturnStatement(new InvocationExpression( - new MemberReferenceExpression(new TypeReferenceExpression(new SimpleType("System.String")), "Format"), + new MemberReferenceExpression(new TypeReferenceExpression(ConvertType(KnownTypeCode.String)), "Format"), param.Concat(fields.Select(f => new IdentifierExpression(f))).ToList() ));