From fd78f8a2508db22bc5dad4f2832ac27e6d9eb2fa Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 28 Jan 2010 22:21:16 +0000 Subject: [PATCH] - copy modifiers from get/set accessors, when expanding an automatic property. - added ConvertToAutomaticProperty refactoring for properties with simple get/set accessors. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5456 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Commands/ClassMemberMenuBuilder.cs | 161 +++++++++++++++++- 1 file changed, 160 insertions(+), 1 deletion(-) diff --git a/src/Main/Base/Project/Src/TextEditor/Commands/ClassMemberMenuBuilder.cs b/src/Main/Base/Project/Src/TextEditor/Commands/ClassMemberMenuBuilder.cs index 2acd2de47f..4b2eef71da 100644 --- a/src/Main/Base/Project/Src/TextEditor/Commands/ClassMemberMenuBuilder.cs +++ b/src/Main/Base/Project/Src/TextEditor/Commands/ClassMemberMenuBuilder.cs @@ -7,6 +7,8 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Windows.Forms; using ICSharpCode.Core; @@ -106,10 +108,24 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Commands cmd.Tag = member; list.Add(cmd); } - if (IsAutomaticProperty(FindReferencesAndRenameHelper.OpenDefinitionFile(property, false), property)) { + ITextEditor editor = FindReferencesAndRenameHelper.OpenDefinitionFile(property, false); + string field = null; + Ast.PropertyDeclaration astProp = null; + if (IsAutomaticProperty(editor, property)) { cmd = new MenuCommand("${res:SharpDevelop.Refactoring.ExpandAutomaticProperty}", ExpandAutomaticProperty); cmd.Tag = member; list.Add(cmd); + } else if (IsSimpleProperty(editor, property, out astProp, out field)) { + cmd = new MenuCommand("${res:SharpDevelop.Refactoring.ConvertToAutomaticProperty}", + delegate { + IField fieldDef = property.DeclaringType.Fields.FirstOrDefault(f => f.Name == field); + if (fieldDef == null) + return; + ConvertToAutomaticProperty(editor, property, fieldDef, astProp); + } + ); + cmd.Tag = member; + list.Add(cmd); } } if (member is IEvent) { @@ -279,6 +295,12 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Commands p.Modifier = CodeGenerator.ConvertModifier(member.Modifiers, finder); + if (member.CanGet) + p.GetRegion.Modifier = CodeGenerator.ConvertModifier(member.GetterModifiers, finder); + + if (member.CanSet) + p.SetRegion.Modifier = CodeGenerator.ConvertModifier(member.SetterModifiers, finder); + int startOffset = textEditor.Document.PositionToOffset(member.Region.BeginLine, member.Region.BeginColumn); int endOffset = textEditor.Document.PositionToOffset(member.BodyRegion.EndLine, member.BodyRegion.EndColumn); @@ -296,5 +318,142 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Commands ParserService.ParseCurrentViewContent(); } } + + bool IsSimpleProperty(ITextEditor editor, IProperty property, out Ast.PropertyDeclaration astProoperty, out string fieldName) + { + astProoperty = null; + fieldName = null; + + if (editor == null) + return false; + + string ext = Path.GetExtension(editor.FileName); + string code = GetMemberText(property, editor); + + Ast.PropertyDeclaration p = ParseMember(ext, code); + + if (p == null) + return false; + + bool getResult = true; + bool setResult = true; + + string identifier = null; + + if (p.HasGetRegion) { + getResult = false; + var ret = p.GetRegion.Block.Children.SingleOrDefault() as Ast.ReturnStatement; + + if (ret != null) { + getResult = ret.Expression is Ast.IdentifierExpression; + identifier = getResult ? (ret.Expression as Ast.IdentifierExpression).Identifier : null; + } + } + + if (p.HasSetRegion) { + setResult = false; + var ret = p.SetRegion.Block.Children.SingleOrDefault() as Ast.ExpressionStatement; + + if (ret != null && ret.Expression is Ast.AssignmentExpression) { + var assign = ret.Expression as Ast.AssignmentExpression; + + setResult = assign.Op == Ast.AssignmentOperatorType.Assign && + IsValidMemberAssignment(assign.Left, ref identifier) && + (assign.Right is Ast.IdentifierExpression && (assign.Right as Ast.IdentifierExpression).Identifier == "value"); + } + } + + astProoperty = p; + fieldName = identifier; + + return getResult && setResult; + } + + Ast.PropertyDeclaration ParseMember(string ext, string code) + { + IParser parser = null; + + try { + if (ext.Equals(".cs", StringComparison.OrdinalIgnoreCase)) + parser = ParserFactory.CreateParser(SupportedLanguage.CSharp, new StringReader(code)); + if (ext.Equals(".vb", StringComparison.OrdinalIgnoreCase)) + parser = ParserFactory.CreateParser(SupportedLanguage.VBNet, new StringReader(code)); + + if (parser == null) + return null; + + var nodes = parser.ParseTypeMembers(); + + if (parser.Errors.Count > 0) + return null; + + return nodes.FirstOrDefault() as Ast.PropertyDeclaration; + } finally { + if (parser != null) + parser.Dispose(); + } + } + + string GetMemberText(IMember member, ITextEditor editor) + { + int startOffset = editor.Document.PositionToOffset(member.Region.BeginLine, member.Region.BeginColumn); + int endOffset = editor.Document.PositionToOffset(member.BodyRegion.EndLine, member.BodyRegion.EndColumn); + + return editor.Document.GetText(startOffset, endOffset - startOffset); + } + + bool IsValidMemberAssignment(Ast.Expression left, ref string identifier) + { + if (left is Ast.MemberReferenceExpression) { + var e = left as Ast.MemberReferenceExpression; + if (identifier == null) { + identifier = e.MemberName; + return e.TargetObject is Ast.ThisReferenceExpression; + } else + return e.TargetObject is Ast.ThisReferenceExpression && e.MemberName == identifier; + } + if (identifier == null) { + if (left is Ast.IdentifierExpression) { + identifier = (left as Ast.IdentifierExpression).Identifier; + return true; + } + return false; + } else + return (left is Ast.IdentifierExpression) && (left as Ast.IdentifierExpression).Identifier == identifier; + } + + void ConvertToAutomaticProperty(ITextEditor editor, IProperty property, IField fieldDef, Ast.PropertyDeclaration astProp) + { + CodeGenerator codeGen = property.DeclaringType.ProjectContent.Language.CodeGenerator; + + int fieldStartOffset = editor.Document.PositionToOffset(fieldDef.Region.BeginLine, fieldDef.Region.BeginColumn); + int fieldEndOffset = editor.Document.PositionToOffset(fieldDef.Region.EndLine, fieldDef.Region.EndColumn); + + int startOffset = editor.Document.PositionToOffset(property.Region.BeginLine, property.Region.BeginColumn); + int endOffset = editor.Document.PositionToOffset(property.BodyRegion.EndLine, property.BodyRegion.EndColumn); + + ITextAnchor startAnchor = editor.Document.CreateAnchor(startOffset); + ITextAnchor endAnchor = editor.Document.CreateAnchor(endOffset); + + if (astProp.HasGetRegion) + astProp.GetRegion.Block = null; + + if (!astProp.HasSetRegion) { + astProp.SetRegion = new Ast.PropertySetRegion(null, null); + astProp.SetRegion.Modifier = CodeGenerator.ConvertModifier(fieldDef.Modifiers, new ClassFinder(fieldDef)) + & (Ast.Modifiers.Private | Ast.Modifiers.Internal | Ast.Modifiers.Protected | Ast.Modifiers.Public); + } + + if (astProp.HasSetRegion) + astProp.SetRegion.Block = null; + using (AsynchronousWaitDialog monitor = AsynchronousWaitDialog.ShowWaitDialog("${res:SharpDevelop.Refactoring.ConvertToAutomaticProperty}")) { + var refs = RefactoringService.FindReferences(fieldDef, monitor); + using (editor.Document.OpenUndoGroup()) { + FindReferencesAndRenameHelper.RenameReferences(refs, property.Name); + editor.Document.Remove(fieldStartOffset, fieldEndOffset - fieldStartOffset); + editor.Document.Replace(startAnchor.Offset, endAnchor.Offset - startAnchor.Offset, codeGen.GenerateCode(astProp, "")); + } + } + } } }