diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj index 00027765d5..3a9759177f 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj @@ -73,6 +73,7 @@ + @@ -95,6 +96,9 @@ + + OverrideEqualsGetHashCodeMethodsDialog.xaml + OverrideToStringMethodDialog.xaml @@ -196,6 +200,7 @@ + diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs index 1c1e8ff6f5..b981593f37 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs @@ -97,6 +97,8 @@ namespace CSharpBinding.Completion { if ((m.EntityType == EntityType.Method) && (m.Name == "ToString")) return new OverrideToStringCompletionData(declarationBegin, m, contextAtCaret); + else if ((m.EntityType == EntityType.Method) && (m.Name == "GetHashCode")) + return new OverrideToStringCompletionData(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/OverrideGetHashCodeCompletionData.cs new file mode 100644 index 0000000000..2b2c8a5a6b --- /dev/null +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideGetHashCodeCompletionData.cs @@ -0,0 +1,148 @@ +// 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.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using ICSharpCode.AvalonEdit.Editing; +using ICSharpCode.AvalonEdit.Snippets; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.SharpDevelop.Parser; +using CSharpBinding.FormattingStrategy; +using CSharpBinding.Parser; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.CSharp.Refactoring; +using ICSharpCode.NRefactory.CSharp.Resolver; +using ICSharpCode.NRefactory.CSharp.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Editor; +using ICSharpCode.SharpDevelop.Editor.CodeCompletion; +using CSharpBinding.Refactoring; + +namespace CSharpBinding.Completion +{ + /// + /// Item for 'override' completion of "GetHashCode()" methods. + /// + class OverrideGetHashCodeCompletionData : OverrideCompletionData + { + public OverrideGetHashCodeCompletionData(int declarationBegin, IMember m, CSharpTypeResolveContext contextAtCaret) + : base(declarationBegin, m, contextAtCaret) + { + } + + public override void Complete(CompletionContext context) + { + if (declarationBegin > context.StartOffset) { + base.Complete(context); + return; + } + + TypeSystemAstBuilder b = new TypeSystemAstBuilder(new CSharpResolver(contextAtCaret)); + b.ShowTypeParameterConstraints = false; + b.GenerateBody = true; + + var entityDeclaration = b.ConvertEntity(this.Entity); + entityDeclaration.Modifiers &= ~(Modifiers.Virtual | Modifiers.Abstract); + entityDeclaration.Modifiers |= Modifiers.Override; + + var body = entityDeclaration.GetChildByRole(Roles.Body); + Statement baseCallStatement = body.Children.OfType().FirstOrDefault(); + + if (!this.Entity.IsAbstract) { + // modify body to call the base method + if (this.Entity.EntityType == EntityType.Method) { + var baseCall = new BaseReferenceExpression().Invoke(this.Entity.Name, new Expression[] { }); + if (((IMethod)this.Entity).ReturnType.IsKnownType(KnownTypeCode.Void)) + baseCallStatement = new ExpressionStatement(baseCall); + else + baseCallStatement = new ReturnStatement(baseCall); + + // Clear body of inserted method + entityDeclaration.GetChildByRole(Roles.Body).Statements.Clear(); + } + } + + var document = context.Editor.Document; + StringWriter w = new StringWriter(); + var formattingOptions = FormattingOptionsFactory.CreateSharpDevelop(); + var segmentDict = SegmentTrackingOutputFormatter.WriteNode(w, entityDeclaration, formattingOptions, context.Editor.Options); + + using (document.OpenUndoGroup()) { + string newText = w.ToString().TrimEnd(); + document.Replace(declarationBegin, context.EndOffset - declarationBegin, newText); + var throwStatement = entityDeclaration.Descendants.FirstOrDefault(n => n is ThrowStatement); + if (throwStatement != null) { + var segment = segmentDict[throwStatement]; + context.Editor.Select(declarationBegin + segment.Offset, segment.Length); + } + CSharpFormatterHelper.Format(context.Editor, declarationBegin, newText.Length, formattingOptions); + + var refactoringContext = SDRefactoringContext.Create(context.Editor, CancellationToken.None); + var typeResolveContext = refactoringContext.GetTypeResolveContext(); + if (typeResolveContext == null) { + 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; + + 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++; + } + } + } +} diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/AbstractInlineRefactorDialog.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/AbstractInlineRefactorDialog.cs index 6140d3af60..480a48ef9b 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/AbstractInlineRefactorDialog.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/AbstractInlineRefactorDialog.cs @@ -50,7 +50,7 @@ namespace CSharpBinding.Refactoring Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(delegate { this.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); })); } - protected abstract string GenerateCode(IUnresolvedTypeDefinition currentClass); + protected abstract string GenerateCode(ITypeDefinition currentClass); protected virtual void OKButtonClick(object sender, RoutedEventArgs e) { @@ -62,7 +62,11 @@ namespace CSharpBinding.Refactoring } if (parseInfo != null) { - IUnresolvedTypeDefinition current = parseInfo.UnresolvedFile.GetInnermostTypeDefinition(anchor.Line, anchor.Column); + var typeResolveContext = refactoringContext.GetTypeResolveContext(); + if (typeResolveContext == null) { + return; + } + var current = typeResolveContext.CurrentTypeDefinition; using (editor.Document.OpenUndoGroup()) { // GenerateCode could modify the document. diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/InsertCtorDialog.xaml.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/InsertCtorDialog.xaml.cs index 6ab730907e..1e80c9324d 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/InsertCtorDialog.xaml.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/InsertCtorDialog.xaml.cs @@ -41,7 +41,7 @@ namespace CSharpBinding.Refactoring Visibility = System.Windows.Visibility.Collapsed; } - protected override string GenerateCode(IUnresolvedTypeDefinition currentClass) + protected override string GenerateCode(ITypeDefinition currentClass) { List filtered = this.varList.SelectedItems.OfType() .OrderBy(p => p.Index) 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 acbadda73f..d16d4623f5 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsDialog.xaml.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsDialog.xaml.cs @@ -8,6 +8,9 @@ using System.Text; using ICSharpCode.AvalonEdit.Snippets; using ICSharpCode.Core; +using ICSharpCode.NRefactory.CSharp; +using ICSharpCode.NRefactory.Editor; +using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.Ast; using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Dom.Refactoring; @@ -21,13 +24,13 @@ namespace CSharpBinding.Refactoring /// public partial class OverrideEqualsGetHashCodeMethodsDialog : AbstractInlineRefactorDialog { - IClass selectedClass; + IType selectedClass; ITextAnchor startAnchor; IMethod selectedMethod; - string baseCall; + AstNode baseCallNode; public OverrideEqualsGetHashCodeMethodsDialog(InsertionContext context, ITextEditor editor, ITextAnchor startAnchor, ITextAnchor endAnchor, - ITextAnchor insertionPosition, IClass selectedClass, IMethod selectedMethod, string baseCall) + ITextAnchor insertionPosition, IType selectedClass, IMethod selectedMethod, AstNode baseCallNode) : base(context, editor, insertionPosition) { if (selectedClass == null) @@ -39,7 +42,7 @@ namespace CSharpBinding.Refactoring this.startAnchor = startAnchor; this.insertionEndAnchor = endAnchor; this.selectedMethod = selectedMethod; - this.baseCall = baseCall; + this.baseCallNode = baseCallNode; addIEquatable.Content = string.Format(StringParser.Parse("${res:AddIns.SharpRefactoring.OverrideEqualsGetHashCodeMethods.AddInterface}"), "IEquatable<" + selectedClass.Name + ">"); @@ -72,23 +75,15 @@ namespace CSharpBinding.Refactoring 1000000483, 1000000513, 1000000531, 1000000579 }; - static bool IsValueType(IReturnType type) - { - IClass c = type.GetUnderlyingClass(); - return c != null && (c.ClassType == Dom.ClassType.Struct || c.ClassType == Dom.ClassType.Enum); - } - - static bool CanCompareEqualityWithOperator(IReturnType type) + static bool CanCompareEqualityWithOperator(IType type) { // return true for value types except float and double // return false for reference types except string. - IClass c = type.GetUnderlyingClass(); - return c != null - && c.FullyQualifiedName != "System.Single" - && c.FullyQualifiedName != "System.Double" - && (c.ClassType == Dom.ClassType.Struct - || c.ClassType == Dom.ClassType.Enum - || c.FullyQualifiedName == "System.String"); + return type != null + && type.FullName != "System.Single" + && type.FullName != "System.Double" + && (!type.IsReferenceType + || type.FullName == "System.String"); } static Expression TestEquality(string other, IField field) @@ -115,7 +110,7 @@ namespace CSharpBinding.Refactoring new MemberReferenceExpression(new IdentifierExpression(other), property.Name)); } else { InvocationExpression ie = new InvocationExpression( - new MemberReferenceExpression(new TypeReferenceExpression(new TypeReference("System.Object", true)), "Equals") + new MemberReferenceExpression(new TypeReferenceExpression(new SimpleType("System.Object")), "Equals") ); ie.Arguments.Add(new MemberReferenceExpression(new ThisReferenceExpression(), property.Name)); ie.Arguments.Add(new MemberReferenceExpression(new IdentifierExpression(other), property.Name)); @@ -123,17 +118,17 @@ namespace CSharpBinding.Refactoring } } - protected override string GenerateCode(LanguageProperties language, IClass currentClass) + protected override string GenerateCode(ITypeDefinition currentClass) { StringBuilder code = new StringBuilder(); - var line = editor.Document.GetLineForOffset(startAnchor.Offset); + var line = editor.Document.GetLineByOffset(startAnchor.Offset); string indent = DocumentUtilitites.GetWhitespaceAfter(editor.Document, line.Offset); CodeGenerator generator = language.CodeGenerator; - if (Options.AddIEquatableInterface) { +// if (Options.AddIEquatableInterface) { // TODO : add IEquatable to class // IAmbience ambience = currentClass.CompilationUnit.Language.GetAmbience(); // @@ -156,7 +151,7 @@ namespace CSharpBinding.Refactoring // int endOffset = editor.Document.PositionToOffset(currentClass.BodyRegion.EndLine, currentClass.BodyRegion.EndColumn); // // editor.Document.Replace(startOffset, endOffset - startOffset, a); - } +// } if (Options.SurroundWithRegion) { editor.Document.InsertNormalized(startAnchor.Offset, "#region Equals and GetHashCode implementation\n" + indent); @@ -265,28 +260,28 @@ namespace CSharpBinding.Refactoring return codeForMethodBody; } - List CreateEqualsOverrides(IClass currentClass) + List CreateEqualsOverrides(IType currentClass) { List methods = new List(); - TypeReference boolReference = new TypeReference("System.Boolean", true); - TypeReference objectReference = new TypeReference("System.Object", true); + AstType boolReference = new SimpleType("System.Boolean"); + AstType objectReference = new SimpleType("System.Object", true); MethodDeclaration method = new MethodDeclaration { Name = "Equals", - Modifier = Modifiers.Public | Modifiers.Override, - TypeReference = boolReference + Modifiers = Modifiers.Public | Modifiers.Override, + ReturnType = boolReference }; - method.Parameters.Add(new ParameterDeclarationExpression(objectReference, "obj")); + method.Parameters.Add(new ParameterDeclaration(objectReference, "obj")); method.Body = new BlockStatement(); - TypeReference currentType = ConvertType(currentClass.DefaultReturnType); + AstType currentType = ConvertType(currentClass.DefaultReturnType); Expression expr = null; if (currentClass.ClassType == Dom.ClassType.Struct) { // return obj is CurrentType && Equals((CurrentType)obj); - expr = new TypeOfIsExpression(new IdentifierExpression("obj"), currentType); + expr = new IsExpression(new IdentifierExpression("obj"), currentType); expr = new ParenthesizedExpression(expr); expr = new BinaryOperatorExpression( expr, BinaryOperatorType.LogicalAnd, @@ -302,18 +297,18 @@ namespace CSharpBinding.Refactoring // IEquatable implementation: method = new MethodDeclaration { Name = "Equals", - Modifier = Modifiers.Public, - TypeReference = boolReference + Modifiers = Modifiers.Public, + ReturnType = boolReference }; - method.Parameters.Add(new ParameterDeclarationExpression(currentType, "other")); + method.Parameters.Add(new ParameterDeclaration(currentType, "other")); method.Body = new BlockStatement(); } else { - method.Body.AddChild(new LocalVariableDeclaration(new VariableDeclaration( + method.Body.AddChild(new VariableDeclaration( "other", - new CastExpression(currentType, new IdentifierExpression("obj"), CastType.TryCast), - currentType))); + new CastExpression(currentType, new IdentifierExpression("obj")), + currentType)); method.Body.AddChild(new IfElseStatement( - new BinaryOperatorExpression(new IdentifierExpression("other"), BinaryOperatorType.ReferenceEquality, new PrimitiveExpression(null, "null")), + new BinaryOperatorExpression(new IdentifierExpression("other"), BinaryOperatorType.Equality, new PrimitiveExpression(null, "null")), new ReturnStatement(new PrimitiveExpression(false, "false")))); // expr = new BinaryOperatorExpression(new ThisReferenceExpression(), 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 22ae595fc1..c4afba82fc 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideToStringMethodDialog.xaml.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideToStringMethodDialog.xaml.cs @@ -39,7 +39,7 @@ namespace CSharpBinding.Refactoring listBox.SelectAll(); } - protected override string GenerateCode(IUnresolvedTypeDefinition currentClass) + protected override string GenerateCode(ITypeDefinition currentClass) { string[] fields = listBox.SelectedItems.OfType().Select(f2 => f2.MemberName).ToArray(); PrimitiveExpression formatString = new PrimitiveExpression(GenerateFormatString(currentClass, editor.Language.CodeGenerator, fields)); @@ -66,7 +66,7 @@ namespace CSharpBinding.Refactoring return null; } - string GenerateFormatString(IUnresolvedTypeDefinition currentClass, ICodeGenerator generator, string[] fields) + string GenerateFormatString(ITypeDefinition currentClass, ICodeGenerator generator, string[] fields) { string fieldsString = "";