From 738529e4aae0e5ef9696ee5ebd746bd1d81c26e6 Mon Sep 17 00:00:00 2001 From: Andreas Weizel Date: Thu, 13 Jun 2013 16:54:00 +0200 Subject: [PATCH] - OverrideToString completion now works. - Added RefactoringContext.GetTypeResolveContext() method. --- .../CSharpBinding/Project/CSharpBinding.addin | 7 +- .../Project/CSharpBinding.csproj | 6 + .../Completion/CSharpCompletionDataFactory.cs | 5 +- .../Src/Completion/OverrideCompletionData.cs | 5 +- .../OverrideToStringCompletionData.cs | 148 ++++++ .../Src/Refactoring/CreateProperties.cs | 85 ++++ .../Refactoring/CreatePropertiesCommand.cs | 26 + .../Refactoring/CreatePropertiesDialog.xaml | 74 +++ .../CreatePropertiesDialog.xaml.cs | 144 ++++++ .../Project/Src/Refactoring/FieldWrapper.cs | 94 ++++ ...verrideEqualsGetHashCodeMethodsDialog.xaml | 35 ++ ...rideEqualsGetHashCodeMethodsDialog.xaml.cs | 460 ++++++++++++++++++ ...rideEqualsGetHashCodeMethodsRefactoring.cs | 85 ++++ .../OverrideToStringMethodDialog.xaml | 34 ++ .../OverrideToStringMethodDialog.xaml.cs | 140 ++++++ .../Refactoring/RefactoringContext.cs | 8 + 16 files changed, 1352 insertions(+), 4 deletions(-) create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideToStringCompletionData.cs create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreateProperties.cs create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesCommand.cs create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesDialog.xaml create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesDialog.xaml.cs create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/FieldWrapper.cs create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsDialog.xaml create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsDialog.xaml.cs create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsRefactoring.cs create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideToStringMethodDialog.xaml create mode 100644 src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideToStringMethodDialog.xaml.cs diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin index 016735c4c1..3a38c6e99a 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin @@ -155,7 +155,12 @@ class="CSharpBinding.Refactoring.SearchForIssuesCommand"/> - + + + + + + diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj index 64906ad045..9c92375b70 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj @@ -73,6 +73,7 @@ + @@ -80,6 +81,7 @@ + InsertCtorDialog.xaml @@ -95,6 +97,9 @@ + + OverrideToStringMethodDialog.xaml + @@ -193,6 +198,7 @@ + diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs index a31ab8adde..1c1e8ff6f5 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs @@ -95,7 +95,10 @@ namespace CSharpBinding.Completion ICompletionData ICompletionDataFactory.CreateNewOverrideCompletionData(int declarationBegin, IUnresolvedTypeDefinition type, IMember m) { - return new OverrideCompletionData(declarationBegin, m, contextAtCaret); + if ((m.EntityType == EntityType.Method) && (m.Name == "ToString")) + return new OverrideToStringCompletionData(declarationBegin, m, contextAtCaret); + else + return new OverrideCompletionData(declarationBegin, m, contextAtCaret); } ICompletionData ICompletionDataFactory.CreateNewPartialCompletionData(int declarationBegin, IUnresolvedTypeDefinition type, IUnresolvedMember m) diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideCompletionData.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideCompletionData.cs index c955563ee1..286c76eaab 100644 --- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideCompletionData.cs +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideCompletionData.cs @@ -24,8 +24,8 @@ namespace CSharpBinding.Completion /// class OverrideCompletionData : EntityCompletionData { - readonly int declarationBegin; - readonly CSharpTypeResolveContext contextAtCaret; + protected readonly int declarationBegin; + protected readonly CSharpTypeResolveContext contextAtCaret; public OverrideCompletionData(int declarationBegin, IMember m, CSharpTypeResolveContext contextAtCaret) : base(m) @@ -43,6 +43,7 @@ namespace CSharpBinding.Completion base.Complete(context); return; } + TypeSystemAstBuilder b = new TypeSystemAstBuilder(new CSharpResolver(contextAtCaret)); b.ShowTypeParameterConstraints = false; b.GenerateBody = true; diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideToStringCompletionData.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideToStringCompletionData.cs new file mode 100644 index 0000000000..911ea59cfe --- /dev/null +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideToStringCompletionData.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 "ToString()" methods. + /// + class OverrideToStringCompletionData : OverrideCompletionData + { + public OverrideToStringCompletionData(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/CreateProperties.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreateProperties.cs new file mode 100644 index 0000000000..91f9b03df0 --- /dev/null +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreateProperties.cs @@ -0,0 +1,85 @@ +// 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.Generic; +using System.ComponentModel; +using System.Linq; +using System.Windows.Forms; + +using ICSharpCode.AvalonEdit.Snippets; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Dom; +using ICSharpCode.SharpDevelop.Dom.Refactoring; +using ICSharpCode.SharpDevelop.Editor; +using ICSharpCode.SharpDevelop.Editor.AvalonEdit; +using SharpRefactoring.Gui; + +namespace CSharpBinding.Refactoring +{ + public class CreateProperties : ISnippetElementProvider + { + public SnippetElement GetElement(SnippetInfo snippetInfo) + { + if ("refactoring:propall".Equals(snippetInfo.Tag, StringComparison.OrdinalIgnoreCase)) + return new InlineRefactorSnippetElement(context => CreateDialog(context), "{" + snippetInfo.Tag + "}"); + + return null; + } + + internal static CreatePropertiesDialog CreateDialog(InsertionContext context) + { + ITextEditor textEditor = context.TextArea.GetService(typeof(ITextEditor)) as ITextEditor; + + if (textEditor == null) + return null; + + IEditorUIService uiService = textEditor.GetService(typeof(IEditorUIService)) as IEditorUIService; + + if (uiService == null) + return null; + + ParseInformation parseInfo = ParserService.GetParseInformation(textEditor.FileName); + + if (parseInfo == null) + return null; + + CodeGenerator generator = parseInfo.CompilationUnit.Language.CodeGenerator; + + // cannot use insertion position at this point, because it might not be + // valid, because we are still generating the elements. + // DOM is not updated + ICSharpCode.AvalonEdit.Document.TextLocation loc = context.Document.GetLocation(context.StartPosition); + + IClass current = parseInfo.CompilationUnit.GetInnermostClass(loc.Line, loc.Column); + + if (current == null) + return null; + + List parameters = FindFields(current).Where(f => !current.Properties.Any(p => p.Name == f.PropertyName)).ToList(); + + if (!parameters.Any()) + return null; + + ITextAnchor anchor = textEditor.Document.CreateAnchor(context.InsertionPosition); + anchor.MovementType = AnchorMovementType.BeforeInsertion; + + CreatePropertiesDialog dialog = new CreatePropertiesDialog(context, textEditor, anchor, current, parameters); + + dialog.Element = uiService.CreateInlineUIElement(anchor, dialog); + + return dialog; + } + + static IEnumerable FindFields(IClass sourceClass) + { + int i = 0; + + foreach (var f in sourceClass.Fields.Where(field => !field.IsConst + && field.ReturnType != null)) { + yield return new FieldWrapper(f) { Index = i }; + i++; + } + } + } +} diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesCommand.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesCommand.cs new file mode 100644 index 0000000000..c3aa3e8423 --- /dev/null +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesCommand.cs @@ -0,0 +1,26 @@ +// 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 ICSharpCode.AvalonEdit.Editing; +using ICSharpCode.AvalonEdit.Snippets; +using ICSharpCode.SharpDevelop.Dom.Refactoring; +using ICSharpCode.SharpDevelop.Editor; +using ICSharpCode.SharpDevelop.Editor.AvalonEdit; +using ICSharpCode.SharpDevelop.Refactoring; +using SharpRefactoring.Gui; + +namespace CSharpBinding.Refactoring +{ + public class CreatePropertiesCommand : AbstractRefactoringCommand + { + protected override void Run(ITextEditor textEditor, RefactoringProvider provider) + { + new Snippet { + Elements = { + new InlineRefactorSnippetElement(context => CreateProperties.CreateDialog(context), "") + } + }.Insert((TextArea)textEditor.GetService(typeof(TextArea))); + } + } +} diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesDialog.xaml b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesDialog.xaml new file mode 100644 index 0000000000..cb0462c44d --- /dev/null +++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesDialog.xaml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +