diff --git a/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.addin b/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.addin index 7c2e86ccf0..65554121f4 100644 --- a/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.addin +++ b/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.addin @@ -48,11 +48,15 @@ - + + + + + diff --git a/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.csproj b/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.csproj index b58b3ddf02..f1a603fac5 100644 --- a/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.csproj +++ b/src/AddIns/Misc/SharpRefactoring/Project/SharpRefactoring.csproj @@ -106,6 +106,7 @@ + diff --git a/src/AddIns/Misc/SharpRefactoring/Project/Src/PropertyRefactoringMenuBuilder.cs b/src/AddIns/Misc/SharpRefactoring/Project/Src/PropertyRefactoringMenuBuilder.cs new file mode 100644 index 0000000000..97f710ceb4 --- /dev/null +++ b/src/AddIns/Misc/SharpRefactoring/Project/Src/PropertyRefactoringMenuBuilder.cs @@ -0,0 +1,304 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Windows.Documents; + +using ICSharpCode.Core.Presentation; +using ICSharpCode.Core.WinForms; +using ICSharpCode.NRefactory; +using ICSharpCode.NRefactory.Ast; +using ICSharpCode.SharpDevelop; +using ICSharpCode.SharpDevelop.Dom; +using ICSharpCode.SharpDevelop.Dom.Refactoring; +using ICSharpCode.SharpDevelop.Editor; +using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.SharpDevelop.Refactoring; +using Ast = ICSharpCode.NRefactory.Ast; + +namespace SharpRefactoring +{ + public class PropertyRefactoringMenuBuilder : ISubmenuBuilder, IMenuItemBuilder + { + public System.Collections.ICollection BuildItems(ICSharpCode.Core.Codon codon, object owner) + { + return BuildSubmenu(codon, owner).TranslateToWpf(); + } + + public System.Windows.Forms.ToolStripItem[] BuildSubmenu(ICSharpCode.Core.Codon codon, object owner) + { + List items = new List(); + MenuCommand cmd; + + if (!(owner is ICSharpCode.SharpDevelop.Gui.ClassBrowser.MemberNode)) + return items.ToArray(); + + IProperty property = (owner as ICSharpCode.SharpDevelop.Gui.ClassBrowser.MemberNode).Member as IProperty; + + if (property == null) + return items.ToArray(); + + ITextEditor editor = FindReferencesAndRenameHelper.OpenDefinitionFile(property, false); + string field = null; + PropertyDeclaration astProp = null; + if (IsAutomaticProperty(editor, property)) { + cmd = new MenuCommand("${res:SharpDevelop.Refactoring.ExpandAutomaticProperty}", ExpandAutomaticProperty); + cmd.Tag = property; + items.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 = property; + items.Add(cmd); + } + + return items.ToArray(); + } + + bool IsAutomaticProperty(ITextEditor editor, IProperty property) + { + if (editor == null) + return false; + + bool isAutomatic = false; + + if (property.CanGet) { + if (property.GetterRegion.IsEmpty) + isAutomatic = true; + else { + int getterStartOffset = editor.Document.PositionToOffset(property.GetterRegion.BeginLine, property.GetterRegion.BeginColumn); + int getterEndOffset = editor.Document.PositionToOffset(property.GetterRegion.EndLine, property.GetterRegion.EndColumn); + + string text = editor.Document.GetText(getterStartOffset, getterEndOffset - getterStartOffset) + .Replace(" ", "").Replace("\t", "").Replace("\n", "").Replace("\r", ""); + + isAutomatic = text == "get;"; + } + } + + if (property.CanSet) { + if (property.SetterRegion.IsEmpty) + isAutomatic |= true; + else { + int setterStartOffset = editor.Document.PositionToOffset(property.SetterRegion.BeginLine, property.SetterRegion.BeginColumn); + int setterEndOffset = editor.Document.PositionToOffset(property.SetterRegion.EndLine, property.SetterRegion.EndColumn); + + string text = editor.Document.GetText(setterStartOffset, setterEndOffset - setterStartOffset) + .Replace(" ", "").Replace("\t", "").Replace("\n", "").Replace("\r", ""); + + isAutomatic |= text == "set;"; + } + } + + return isAutomatic; + } + + void ExpandAutomaticProperty(object sender, EventArgs e) + { + MenuCommand item = (MenuCommand)sender; + IProperty member = (IProperty)item.Tag; + ITextEditor textEditor = FindReferencesAndRenameHelper.JumpToDefinition(member); + + if (textEditor != null) { + CodeGenerator codeGen = member.DeclaringType.ProjectContent.Language.CodeGenerator; + IField field = new DefaultField(member.ReturnType, codeGen.GetFieldName(member.Name), + member.Modifiers & ModifierEnum.Static, DomRegion.Empty, member.DeclaringType); + ClassFinder finder = new ClassFinder(member); + + Ast.PropertyDeclaration p = codeGen.CreateProperty(field, member.CanGet, member.CanSet); + + p.Modifier = CodeGenerator.ConvertModifier(member.Modifiers, finder); + + PropertyDeclaration oldProp = ParseMember(Path.GetExtension(textEditor.FileName), GetMemberText(member, textEditor)); + + 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.Region.EndLine, member.Region.EndColumn); + + if (!member.BodyRegion.IsEmpty) + endOffset = textEditor.Document.PositionToOffset(member.BodyRegion.EndLine, member.BodyRegion.EndColumn); + + FieldDeclaration f = CodeGenerator.ConvertMember(field, finder); + + f.Fields[0].Initializer = oldProp.Initializer; + + using (textEditor.Document.OpenUndoGroup()) { + textEditor.Document.Remove(startOffset, endOffset - startOffset); + + if (codeGen.Options.EmptyLinesBetweenMembers) { + var line = textEditor.Document.GetLine(member.Region.BeginLine); + textEditor.Document.Remove(line.Offset, line.TotalLength); + } + + codeGen.InsertCodeInClass(member.DeclaringType, new RefactoringDocumentAdapter(textEditor.Document), member.Region.BeginLine - 1, f, p); + } + + ParserService.ParseCurrentViewContent(); + } + } + + bool IsSimpleProperty(ITextEditor editor, IProperty property, out Ast.PropertyDeclaration astProperty, out string fieldName) + { + astProperty = 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.FirstOrDefault() 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.FirstOrDefault() as Ast.ExpressionStatement; + + if (ret != null && p.SetRegion.Block.Children.Count == 1 && 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"); + } + } + + astProperty = p; + fieldName = identifier; + + return getResult && setResult; + } + + T ParseMember(string ext, string code) where T : INode + { + 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 default(T); + + var nodes = parser.ParseTypeMembers(); + + if (parser.Errors.Count > 0) + return default(T); + + return (T)nodes.FirstOrDefault(); + } 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; + + if (member.BodyRegion.IsEmpty) + endOffset = editor.Document.PositionToOffset(member.Region.EndLine, member.Region.EndColumn); + else + 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); + } + + Ast.FieldDeclaration f = ParseMember(Path.GetExtension(editor.FileName), + GetMemberText(fieldDef, editor)); + astProp.Initializer = f.Fields.First().Initializer; + + 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, "")); + } + } + } + } +} diff --git a/src/Main/Base/Project/Src/TextEditor/Commands/ClassMemberMenuBuilder.cs b/src/Main/Base/Project/Src/TextEditor/Commands/ClassMemberMenuBuilder.cs index 0fb3326004..91026a971e 100644 --- a/src/Main/Base/Project/Src/TextEditor/Commands/ClassMemberMenuBuilder.cs +++ b/src/Main/Base/Project/Src/TextEditor/Commands/ClassMemberMenuBuilder.cs @@ -10,7 +10,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Windows.Forms; - using ICSharpCode.Core; using ICSharpCode.Core.Presentation; using ICSharpCode.Core.WinForms; @@ -23,7 +22,6 @@ using ICSharpCode.SharpDevelop.Editor.Search; using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Gui.ClassBrowser; using ICSharpCode.SharpDevelop.Refactoring; -using Ast = ICSharpCode.NRefactory.Ast; namespace ICSharpCode.SharpDevelop.DefaultEditor.Commands { @@ -108,25 +106,6 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Commands cmd.Tag = member; list.Add(cmd); } - 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) { if (canGenerateCode) { @@ -246,223 +225,5 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Commands RefactoringService.FindReferences(member, monitor)); } } - - bool IsAutomaticProperty(ITextEditor editor, IProperty property) - { - if (editor == null) - return false; - - bool isAutomatic = false; - - if (property.CanGet) { - if (property.GetterRegion.IsEmpty) - isAutomatic = true; - else { - int getterStartOffset = editor.Document.PositionToOffset(property.GetterRegion.BeginLine, property.GetterRegion.BeginColumn); - int getterEndOffset = editor.Document.PositionToOffset(property.GetterRegion.EndLine, property.GetterRegion.EndColumn); - - string text = editor.Document.GetText(getterStartOffset, getterEndOffset - getterStartOffset) - .Replace(" ", "").Replace("\t", "").Replace("\n", "").Replace("\r", ""); - - isAutomatic = text == "get;"; - } - } - - if (property.CanSet) { - if (property.SetterRegion.IsEmpty) - isAutomatic |= true; - else { - int setterStartOffset = editor.Document.PositionToOffset(property.SetterRegion.BeginLine, property.SetterRegion.BeginColumn); - int setterEndOffset = editor.Document.PositionToOffset(property.SetterRegion.EndLine, property.SetterRegion.EndColumn); - - string text = editor.Document.GetText(setterStartOffset, setterEndOffset - setterStartOffset) - .Replace(" ", "").Replace("\t", "").Replace("\n", "").Replace("\r", ""); - - isAutomatic |= text == "set;"; - } - } - - return isAutomatic; - } - - void ExpandAutomaticProperty(object sender, EventArgs e) - { - MenuCommand item = (MenuCommand)sender; - IProperty member = (IProperty)item.Tag; - ITextEditor textEditor = FindReferencesAndRenameHelper.JumpToDefinition(member); - - if (textEditor != null) { - CodeGenerator codeGen = member.DeclaringType.ProjectContent.Language.CodeGenerator; - IField field = new DefaultField(member.ReturnType, codeGen.GetFieldName(member.Name), - member.Modifiers & ModifierEnum.Static, DomRegion.Empty, member.DeclaringType); - ClassFinder finder = new ClassFinder(member); - - Ast.PropertyDeclaration p = codeGen.CreateProperty(field, member.CanGet, member.CanSet); - - 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.Region.EndLine, member.Region.EndColumn); - - if (!member.BodyRegion.IsEmpty) - endOffset = textEditor.Document.PositionToOffset(member.BodyRegion.EndLine, member.BodyRegion.EndColumn); - - using (textEditor.Document.OpenUndoGroup()) { - textEditor.Document.Remove(startOffset, endOffset - startOffset); - - if (codeGen.Options.EmptyLinesBetweenMembers) { - var line = textEditor.Document.GetLine(member.Region.BeginLine); - textEditor.Document.Remove(line.Offset, line.TotalLength); - } - - codeGen.InsertCodeInClass(member.DeclaringType, new RefactoringDocumentAdapter(textEditor.Document), member.Region.BeginLine - 1, CodeGenerator.ConvertMember(field, finder), p); - } - - ParserService.ParseCurrentViewContent(); - } - } - - bool IsSimpleProperty(ITextEditor editor, IProperty property, out Ast.PropertyDeclaration astProperty, out string fieldName) - { - astProperty = 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.FirstOrDefault() 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.FirstOrDefault() as Ast.ExpressionStatement; - - if (ret != null && p.SetRegion.Block.Children.Count == 1 && 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"); - } - } - - astProperty = 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, "")); - } - } - } } }