Browse Source
git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/vbnet@5816 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61pull/1/head
4 changed files with 310 additions and 240 deletions
@ -0,0 +1,304 @@
@@ -0,0 +1,304 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <owner name="Siegfried Pammer" email="siegfriedpammer@gmail.com" />
|
||||
// <version>$Revision$</version>
|
||||
// </file>
|
||||
|
||||
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<System.Windows.Forms.ToolStripItem> items = new List<System.Windows.Forms.ToolStripItem>(); |
||||
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<PropertyDeclaration>(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<Ast.PropertyDeclaration>(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<T>(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<Ast.FieldDeclaration>(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, "")); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue