16 changed files with 1352 additions and 4 deletions
@ -0,0 +1,148 @@
@@ -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 |
||||
{ |
||||
/// <summary>
|
||||
/// Item for 'override' completion of "ToString()" methods.
|
||||
/// </summary>
|
||||
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<Statement>().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<PropertyOrFieldWrapper> 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++; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,85 @@
@@ -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<FieldWrapper> 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<FieldWrapper> 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++; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,26 @@
@@ -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))); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
|
||||
<gui:AbstractInlineRefactorDialog x:Class="CSharpBinding.Refactoring.CreatePropertiesDialog" |
||||
Header="{sd:Localize AddIns.SharpRefactoring.CreateProperties.Title}" |
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||
xmlns:gui="clr-namespace:CSharpBinding.Refactoring" |
||||
xmlns:sd="http://icsharpcode.net/sharpdevelop/core" |
||||
xmlns:widgets="http://icsharpcode.net/sharpdevelop/widgets" |
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
||||
<Grid Cursor="Arrow" KeyboardNavigation.TabNavigation="Cycle"> |
||||
<Grid.Resources> |
||||
<Style x:Key="ListViewItemFocusHighlight" TargetType="{x:Type ListViewItem}"> |
||||
<!-- This style replaces the default ListViewItem template with one that highlights the focused item instead of the selected items --> |
||||
<Setter Property="Template"> |
||||
<Setter.Value> |
||||
<ControlTemplate TargetType="{x:Type ListViewItem}"> |
||||
<Border Name="Bd" |
||||
Background="{TemplateBinding Background}" |
||||
BorderBrush="{TemplateBinding BorderBrush}" |
||||
BorderThickness="{TemplateBinding BorderThickness}" |
||||
Padding="{TemplateBinding Padding}" |
||||
SnapsToDevicePixels="true"> |
||||
<GridViewRowPresenter Content="{TemplateBinding Content}" Columns="{TemplateBinding GridView.ColumnCollection}"/> |
||||
</Border> |
||||
<ControlTemplate.Triggers> |
||||
<Trigger Property="IsKeyboardFocusWithin" |
||||
Value="true"> |
||||
<Setter TargetName="Bd" |
||||
Property="Background" |
||||
Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/> |
||||
<Setter Property="Foreground" |
||||
Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/> |
||||
</Trigger> |
||||
</ControlTemplate.Triggers> |
||||
</ControlTemplate> |
||||
</Setter.Value> |
||||
</Setter> |
||||
</Style> |
||||
</Grid.Resources> |
||||
<Grid.RowDefinitions> |
||||
<RowDefinition Height="Auto" /> |
||||
<RowDefinition Height="Auto" /> |
||||
<RowDefinition Height="*" /> |
||||
<RowDefinition Height="Auto" /> |
||||
</Grid.RowDefinitions> |
||||
<TextBlock Margin="3" |
||||
Text="{sd:Localize AddIns.SharpRefactoring.CreateProperties.Description}" |
||||
TextWrapping="Wrap" /> |
||||
<CheckBox x:Name="implementInterface" Margin="3" Grid.Row="1" Content="{sd:Localize AddIns.SharpRefactoring.CreateProperties.Description.ImplementINotifyPropertyChanged}" /> |
||||
<ListView x:Name="listBox" Grid.Row="2" SelectionMode="Multiple" KeyboardNavigation.TabNavigation="Cycle" ItemContainerStyle="{StaticResource ListViewItemFocusHighlight}" MaxHeight="300"> |
||||
<ListView.View> |
||||
<GridView> |
||||
<GridViewColumn Header="{sd:Localize AddIns.SharpRefactoring.InsertCtor.VariableLabel}"> |
||||
<GridViewColumn.CellTemplate> |
||||
<DataTemplate> |
||||
<CheckBox Content="{Binding Text}" IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsSelected}" /> |
||||
</DataTemplate> |
||||
</GridViewColumn.CellTemplate> |
||||
</GridViewColumn> |
||||
<GridViewColumn Header="{sd:Localize AddIns.SharpRefactoring.CreateProperties.AddSetterLabel}"> |
||||
<GridViewColumn.CellTemplate> |
||||
<DataTemplate> |
||||
<CheckBox IsChecked="{Binding AddSetter, Mode=TwoWay}" IsEnabled="{Binding IsSetable}" /> |
||||
</DataTemplate> |
||||
</GridViewColumn.CellTemplate> |
||||
</GridViewColumn> |
||||
</GridView> |
||||
</ListView.View> |
||||
</ListView> |
||||
<widgets:UniformGridWithSpacing Columns="3" Margin="3" Grid.Row="3" HorizontalAlignment="Center"> |
||||
<Button Content="{sd:Localize Global.OKButtonText}" Click="OKButtonClick" Style="{x:Static sd:GlobalStyles.ButtonStyle}" /> |
||||
<Button Content="{sd:Localize Global.CancelButtonText}" Click="CancelButtonClick" Style="{x:Static sd:GlobalStyles.ButtonStyle}" /> |
||||
<ToggleButton x:Name="selectAll" Content="{sd:Localize Global.SelectAllButtonText}" IsChecked="{Binding AllSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type gui:CreatePropertiesDialog}}, Mode=TwoWay}" Checked="SelectAllChecked" Unchecked="SelectAllUnchecked" /> |
||||
</widgets:UniformGridWithSpacing> |
||||
</Grid> |
||||
</gui:AbstractInlineRefactorDialog> |
@ -0,0 +1,144 @@
@@ -0,0 +1,144 @@
|
||||
// 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.Linq; |
||||
using System.Text; |
||||
using System.Windows.Input; |
||||
|
||||
using ICSharpCode.AvalonEdit.Snippets; |
||||
using ICSharpCode.NRefactory.Ast; |
||||
using ICSharpCode.SharpDevelop.Dom; |
||||
using ICSharpCode.SharpDevelop.Editor; |
||||
|
||||
namespace CSharpBinding.Refactoring |
||||
{ |
||||
/// <summary>
|
||||
/// Interaction logic for CreatePropertiesDialog.xaml
|
||||
/// </summary>
|
||||
public partial class CreatePropertiesDialog : AbstractInlineRefactorDialog |
||||
{ |
||||
IList<FieldWrapper> fields; |
||||
|
||||
public CreatePropertiesDialog(InsertionContext context, ITextEditor editor, ITextAnchor anchor, IClass current, IList<FieldWrapper> availableFields) |
||||
: base(context, editor, anchor) |
||||
{ |
||||
InitializeComponent(); |
||||
|
||||
this.listBox.ItemsSource = fields = availableFields; |
||||
|
||||
if (!fields.Any()) |
||||
Visibility = System.Windows.Visibility.Collapsed; |
||||
|
||||
implementInterface.IsChecked = !current.IsStatic && HasOnPropertyChanged(current); |
||||
if (current.IsStatic) |
||||
implementInterface.Visibility = System.Windows.Visibility.Collapsed; |
||||
|
||||
listBox.UnselectAll(); |
||||
} |
||||
|
||||
protected override string GenerateCode(LanguageProperties language, IClass currentClass) |
||||
{ |
||||
StringBuilder builder = new StringBuilder(); |
||||
IDocumentLine line = editor.Document.GetLineForOffset(anchor.Offset); |
||||
string indent = DocumentUtilitites.GetWhitespaceAfter(editor.Document, line.Offset); |
||||
bool implementInterface = this.implementInterface.IsChecked == true; |
||||
bool hasOnPropertyChanged = HasOnPropertyChanged(currentClass); |
||||
bool useEventArgs = false; |
||||
|
||||
if (implementInterface && !currentClass.IsStatic) { |
||||
if (!hasOnPropertyChanged) { |
||||
var nodes = new List<AbstractNode>(); |
||||
var rt = new GetClassReturnType(currentClass.ProjectContent, "System.ComponentModel.INotifyPropertyChanged", 0); |
||||
if (!currentClass.ClassInheritanceTree.Any(bt => bt.FullyQualifiedName == "System.ComponentModel.INotifyPropertyChanged")) { |
||||
int insertion = editor.Document.PositionToOffset(currentClass.BodyRegion.BeginLine, currentClass.BodyRegion.BeginColumn); |
||||
if (currentClass.BaseTypes.Count > 0) |
||||
editor.Document.Insert(insertion, ", INotifyPropertyChanged"); |
||||
else |
||||
editor.Document.Insert(insertion, " : INotifyPropertyChanged"); |
||||
} |
||||
language.CodeGenerator.ImplementInterface(nodes, rt, false, currentClass); |
||||
var ev = rt.GetEvents().First(e => e.Name == "PropertyChanged"); |
||||
MethodDeclaration onEvent = language.CodeGenerator.CreateOnEventMethod(new DefaultEvent(ev.Name, ev.ReturnType, ev.Modifiers, ev.Region, ev.BodyRegion, currentClass)); |
||||
nodes.Add(onEvent); |
||||
onEvent.Parameters[0].TypeReference = new TypeReference("string", true); |
||||
onEvent.Parameters[0].ParameterName = "propertyName"; |
||||
((RaiseEventStatement)onEvent.Body.Children[0]).Arguments[1] = new ObjectCreateExpression(new TypeReference("PropertyChangedEventArgs"), new List<Expression> { new IdentifierExpression("propertyName") }); |
||||
foreach (var node in nodes) |
||||
builder.AppendLine(language.CodeGenerator.GenerateCode(node, indent)); |
||||
useEventArgs = false; |
||||
} else { |
||||
useEventArgs = currentClass.DefaultReturnType.GetMethods().First(m => m.Name == "OnPropertyChanged").Parameters[0].ReturnType.FullyQualifiedName != "System.String"; |
||||
} |
||||
} |
||||
|
||||
foreach (FieldWrapper field in listBox.SelectedItems) { |
||||
var prop = language.CodeGenerator.CreateProperty(field.Field, true, field.AddSetter); |
||||
if (!field.Field.IsStatic && !currentClass.IsStatic && field.AddSetter && implementInterface) { |
||||
var invocation = new ExpressionStatement(CreateInvocation(field.PropertyName, useEventArgs)); |
||||
var assignment = prop.SetRegion.Block.Children[0]; |
||||
prop.SetRegion.Block.Children.Clear(); |
||||
prop.SetRegion.Block.AddChild( |
||||
new IfElseStatement( |
||||
new BinaryOperatorExpression(new IdentifierExpression(field.MemberName), BinaryOperatorType.InEquality, new IdentifierExpression("value")), |
||||
new BlockStatement { Children = { assignment, invocation } } |
||||
) |
||||
); |
||||
} |
||||
builder.AppendLine(language.CodeGenerator.GenerateCode(prop, indent)); |
||||
} |
||||
|
||||
return builder.ToString().Trim(); |
||||
} |
||||
|
||||
string GetCodeFromRegion(DomRegion region) |
||||
{ |
||||
int startOffset = editor.Document.PositionToOffset(region.BeginLine, region.BeginColumn); |
||||
int endOffset = editor.Document.PositionToOffset(region.EndLine, region.EndColumn); |
||||
return editor.Document.GetText(startOffset, endOffset - startOffset); |
||||
} |
||||
|
||||
bool HasOnPropertyChanged(IClass currentClass) |
||||
{ |
||||
return currentClass.DefaultReturnType.GetMethods().Any(m => m.Name == "OnPropertyChanged"); |
||||
} |
||||
|
||||
InvocationExpression CreateInvocation(string name, bool useEventArgs) |
||||
{ |
||||
Expression arg = useEventArgs |
||||
? (Expression)new ObjectCreateExpression(new TypeReference("PropertyChangedEventArgs"), new List<Expression> { new PrimitiveExpression(name) }) |
||||
: (Expression)new PrimitiveExpression(name); |
||||
return new InvocationExpression(new IdentifierExpression("OnPropertyChanged"), new List<Expression> { arg }); |
||||
} |
||||
|
||||
void SelectAllChecked(object sender, System.Windows.RoutedEventArgs e) |
||||
{ |
||||
listBox.SelectAll(); |
||||
} |
||||
|
||||
void SelectAllUnchecked(object sender, System.Windows.RoutedEventArgs e) |
||||
{ |
||||
listBox.UnselectAll(); |
||||
} |
||||
|
||||
bool AllSelected { |
||||
get { return listBox.SelectedItems.Count == listBox.Items.Count; } |
||||
} |
||||
|
||||
protected override void OnKeyDown(KeyEventArgs e) |
||||
{ |
||||
Key? allAccessKey = GetAccessKeyFromButton(selectAll); |
||||
|
||||
if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt && allAccessKey == e.SystemKey) { |
||||
if (AllSelected) |
||||
listBox.UnselectAll(); |
||||
else |
||||
listBox.SelectAll(); |
||||
e.Handled = true; |
||||
} |
||||
|
||||
base.OnKeyDown(e); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,94 @@
@@ -0,0 +1,94 @@
|
||||
// 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.ComponentModel; |
||||
using System.Linq; |
||||
|
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
using ICSharpCode.SharpDevelop; |
||||
|
||||
namespace CSharpBinding.Refactoring |
||||
{ |
||||
public class FieldWrapper : INotifyPropertyChanged |
||||
{ |
||||
/// <summary>
|
||||
/// Underlying member. Always IField or IProperty.
|
||||
/// </summary>
|
||||
readonly IField field; |
||||
private string text; |
||||
|
||||
public IField Field { |
||||
get { return field; } |
||||
} |
||||
|
||||
public FieldWrapper(IField member) |
||||
{ |
||||
if (member == null) |
||||
throw new ArgumentNullException("member"); |
||||
|
||||
this.field = member; |
||||
addSetter = true; |
||||
|
||||
IAmbience ambience = AmbienceService.GetCurrentAmbience(); |
||||
ambience.ConversionFlags |= |
||||
ConversionFlags.ShowReturnType |
||||
| ConversionFlags.ShowModifiers |
||||
| ConversionFlags.ShowAccessibility; |
||||
this.text = ambience.ConvertEntity(member); |
||||
} |
||||
|
||||
public string MemberName { |
||||
get { return field.Name; } |
||||
} |
||||
|
||||
string propertyName; |
||||
public string PropertyName { |
||||
get { |
||||
if (propertyName == null) |
||||
propertyName = ToPropertyName(this.MemberName); |
||||
return propertyName; |
||||
} |
||||
} |
||||
|
||||
public IType Type { |
||||
get { return field.ReturnType; } |
||||
} |
||||
|
||||
public int Index { get; set; } |
||||
|
||||
public string Text { |
||||
get { return text; } |
||||
} |
||||
|
||||
bool addSetter; |
||||
public bool AddSetter { |
||||
get { return addSetter; } |
||||
set { |
||||
addSetter = value; |
||||
} |
||||
} |
||||
|
||||
public bool IsSetable { |
||||
get { |
||||
return !field.IsReadOnly && !field.IsConst; |
||||
} |
||||
} |
||||
|
||||
static string ToPropertyName(string memberName) |
||||
{ |
||||
if (string.IsNullOrEmpty(memberName)) |
||||
return memberName; |
||||
return char.ToUpper(memberName[0]) + memberName.Substring(1); |
||||
} |
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged; |
||||
|
||||
protected virtual void OnPropertyChanged(string property) |
||||
{ |
||||
if (PropertyChanged != null) { |
||||
PropertyChanged(this, new PropertyChangedEventArgs(property)); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
<gui:AbstractInlineRefactorDialog x:Class="CSharpBinding.Refactoring.OverrideEqualsGetHashCodeMethodsDialog" |
||||
Header="{sd:Localize AddIns.SharpRefactoring.OverrideEqualsGetHashCodeMethods}" |
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||
xmlns:gui="clr-namespace:CSharpBinding.Refactoring" |
||||
xmlns:addin="clr-namespace:CSharpBinding.Refactoring" |
||||
xmlns:sd="http://icsharpcode.net/sharpdevelop/core" |
||||
xmlns:widgets="http://icsharpcode.net/sharpdevelop/widgets" |
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
||||
<DockPanel Cursor="Arrow"> |
||||
<widgets:UniformGridWithSpacing Columns="2" DockPanel.Dock="Bottom" Margin="3" HorizontalAlignment="Center"> |
||||
<Button Content="{sd:Localize Global.OKButtonText}" Click="OKButtonClick" Style="{x:Static sd:GlobalStyles.ButtonStyle}" /> |
||||
<Button Content="{sd:Localize Global.CancelButtonText}" Click="CancelButtonClick" Style="{x:Static sd:GlobalStyles.ButtonStyle}" /> |
||||
</widgets:UniformGridWithSpacing> |
||||
<StackPanel Orientation="Vertical"> |
||||
<!-- TODO : remove Visibilty="Collapsed" --> |
||||
<CheckBox |
||||
x:Name="addIEquatable" |
||||
Margin="3" |
||||
Visibility="Collapsed" |
||||
IsChecked="{sd:OptionBinding addin:Options.AddIEquatableInterface}" /> |
||||
<CheckBox |
||||
x:Name="addOtherMethod" |
||||
Margin="3" |
||||
IsChecked="{sd:OptionBinding addin:Options.AddOtherMethod}" /> |
||||
<CheckBox |
||||
Content="{sd:Localize AddIns.SharpRefactoring.OverrideEqualsGetHashCodeMethods.AddOperatorOverloads}" |
||||
Margin="3" |
||||
IsChecked="{sd:OptionBinding addin:Options.AddOperatorOverloads}" /> |
||||
<CheckBox |
||||
Content="{sd:Localize AddIns.SharpRefactoring.OverrideEqualsGetHashCodeMethods.SurroundWithRegion}" |
||||
Margin="3" |
||||
IsChecked="{sd:OptionBinding addin:Options.SurroundWithRegion}" /> |
||||
</StackPanel> |
||||
</DockPanel> |
||||
</gui:AbstractInlineRefactorDialog> |
@ -0,0 +1,460 @@
@@ -0,0 +1,460 @@
|
||||
// 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.Linq; |
||||
using System.Text; |
||||
|
||||
using ICSharpCode.AvalonEdit.Snippets; |
||||
using ICSharpCode.Core; |
||||
using ICSharpCode.NRefactory.Ast; |
||||
using ICSharpCode.SharpDevelop.Dom; |
||||
using ICSharpCode.SharpDevelop.Dom.Refactoring; |
||||
using ICSharpCode.SharpDevelop.Editor; |
||||
using Dom = ICSharpCode.SharpDevelop.Dom; |
||||
|
||||
namespace CSharpBinding.Refactoring |
||||
{ |
||||
/// <summary>
|
||||
/// Interaction logic for OverrideEqualsGetHashCodeMethodsDialog.xaml
|
||||
/// </summary>
|
||||
public partial class OverrideEqualsGetHashCodeMethodsDialog : AbstractInlineRefactorDialog |
||||
{ |
||||
IClass selectedClass; |
||||
ITextAnchor startAnchor; |
||||
IMethod selectedMethod; |
||||
string baseCall; |
||||
|
||||
public OverrideEqualsGetHashCodeMethodsDialog(InsertionContext context, ITextEditor editor, ITextAnchor startAnchor, ITextAnchor endAnchor, |
||||
ITextAnchor insertionPosition, IClass selectedClass, IMethod selectedMethod, string baseCall) |
||||
: base(context, editor, insertionPosition) |
||||
{ |
||||
if (selectedClass == null) |
||||
throw new ArgumentNullException("selectedClass"); |
||||
|
||||
InitializeComponent(); |
||||
|
||||
this.selectedClass = selectedClass; |
||||
this.startAnchor = startAnchor; |
||||
this.insertionEndAnchor = endAnchor; |
||||
this.selectedMethod = selectedMethod; |
||||
this.baseCall = baseCall; |
||||
|
||||
addIEquatable.Content = string.Format(StringParser.Parse("${res:AddIns.SharpRefactoring.OverrideEqualsGetHashCodeMethods.AddInterface}"), |
||||
"IEquatable<" + selectedClass.Name + ">"); |
||||
|
||||
string otherMethod = selectedMethod.Name == "Equals" ? "GetHashCode" : "Equals"; |
||||
|
||||
addOtherMethod.Content = StringParser.Parse("${res:AddIns.SharpRefactoring.OverrideEqualsGetHashCodeMethods.AddOtherMethod}", new StringTagPair("otherMethod", otherMethod)); |
||||
|
||||
addIEquatable.IsEnabled = !selectedClass.BaseTypes.Any( |
||||
type => { |
||||
if (!type.IsGenericReturnType) |
||||
return false; |
||||
var genericType = type.CastToGenericReturnType(); |
||||
var boundTo = genericType.TypeParameter.BoundTo; |
||||
if (boundTo == null) |
||||
return false; |
||||
return boundTo.Name == selectedClass.Name; |
||||
} |
||||
); |
||||
} |
||||
|
||||
static int[] largePrimes = { |
||||
1000000007, 1000000009, 1000000021, 1000000033, |
||||
1000000087, 1000000093, 1000000097, 1000000103, |
||||
1000000123, 1000000181, 1000000207, 1000000223, |
||||
1000000241, 1000000271, 1000000289, 1000000297, |
||||
1000000321, 1000000349, 1000000363, 1000000403, |
||||
1000000409, 1000000411, 1000000427, 1000000433, |
||||
1000000439, 1000000447, 1000000453, 1000000459, |
||||
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) |
||||
{ |
||||
// 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"); |
||||
} |
||||
|
||||
static Expression TestEquality(string other, IField field) |
||||
{ |
||||
if (CanCompareEqualityWithOperator(field.ReturnType)) { |
||||
return new BinaryOperatorExpression(new MemberReferenceExpression(new ThisReferenceExpression(), field.Name), |
||||
BinaryOperatorType.Equality, |
||||
new MemberReferenceExpression(new IdentifierExpression(other), field.Name)); |
||||
} else { |
||||
InvocationExpression ie = new InvocationExpression( |
||||
new MemberReferenceExpression(new TypeReferenceExpression(new TypeReference("System.Object", true)), "Equals") |
||||
); |
||||
ie.Arguments.Add(new MemberReferenceExpression(new ThisReferenceExpression(), field.Name)); |
||||
ie.Arguments.Add(new MemberReferenceExpression(new IdentifierExpression(other), field.Name)); |
||||
return ie; |
||||
} |
||||
} |
||||
|
||||
static Expression TestEquality(string other, IProperty property) |
||||
{ |
||||
if (CanCompareEqualityWithOperator(property.ReturnType)) { |
||||
return new BinaryOperatorExpression(new MemberReferenceExpression(new ThisReferenceExpression(), property.Name), |
||||
BinaryOperatorType.Equality, |
||||
new MemberReferenceExpression(new IdentifierExpression(other), property.Name)); |
||||
} else { |
||||
InvocationExpression ie = new InvocationExpression( |
||||
new MemberReferenceExpression(new TypeReferenceExpression(new TypeReference("System.Object", true)), "Equals") |
||||
); |
||||
ie.Arguments.Add(new MemberReferenceExpression(new ThisReferenceExpression(), property.Name)); |
||||
ie.Arguments.Add(new MemberReferenceExpression(new IdentifierExpression(other), property.Name)); |
||||
return ie; |
||||
} |
||||
} |
||||
|
||||
protected override string GenerateCode(LanguageProperties language, IClass currentClass) |
||||
{ |
||||
StringBuilder code = new StringBuilder(); |
||||
|
||||
var line = editor.Document.GetLineForOffset(startAnchor.Offset); |
||||
|
||||
string indent = DocumentUtilitites.GetWhitespaceAfter(editor.Document, line.Offset); |
||||
|
||||
CodeGenerator generator = language.CodeGenerator; |
||||
|
||||
if (Options.AddIEquatableInterface) { |
||||
// TODO : add IEquatable<T> to class
|
||||
// IAmbience ambience = currentClass.CompilationUnit.Language.GetAmbience();
|
||||
//
|
||||
// IReturnType baseRType = currentClass.CompilationUnit.ProjectContent.GetClass("System.IEquatable", 1).DefaultReturnType;
|
||||
//
|
||||
// IClass newClass = new DefaultClass(currentClass.CompilationUnit, currentClass.FullyQualifiedName, currentClass.Modifiers, currentClass.Region, null);
|
||||
//
|
||||
// foreach (IReturnType type in currentClass.BaseTypes) {
|
||||
// newClass.BaseTypes.Add(type);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// newClass.BaseTypes.Add(new ConstructedReturnType(baseRType, new List<IReturnType>() { currentClass.DefaultReturnType }));
|
||||
//
|
||||
// ambience.ConversionFlags = ConversionFlags.IncludeBody;
|
||||
//
|
||||
// string a = ambience.Convert(currentClass);
|
||||
//
|
||||
// int startOffset = editor.Document.PositionToOffset(currentClass.Region.BeginLine, currentClass.Region.BeginColumn);
|
||||
// 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); |
||||
} |
||||
|
||||
string codeForMethodBody; |
||||
|
||||
if ("Equals".Equals(selectedMethod.Name, StringComparison.Ordinal)) { |
||||
IList<MethodDeclaration> equalsOverrides = CreateEqualsOverrides(currentClass); |
||||
MethodDeclaration defaultOverride = equalsOverrides.First(); |
||||
equalsOverrides = equalsOverrides.Skip(1).ToList(); |
||||
|
||||
StringBuilder builder = new StringBuilder(); |
||||
|
||||
foreach (AbstractNode element in defaultOverride.Body.Children.OfType<AbstractNode>()) { |
||||
builder.Append(language.CodeGenerator.GenerateCode(element, indent + "\t")); |
||||
} |
||||
|
||||
codeForMethodBody = builder.ToString().Trim(); |
||||
|
||||
if (addOtherMethod.IsChecked == true) { |
||||
if (equalsOverrides.Any()) |
||||
code.Append(indent + "\n" + string.Join("\n", equalsOverrides.Select(item => generator.GenerateCode(item, indent)))); |
||||
code.Append(indent + "\n" + generator.GenerateCode(CreateGetHashCodeOverride(currentClass), indent)); |
||||
} |
||||
} else { |
||||
StringBuilder builder = new StringBuilder(); |
||||
|
||||
foreach (AbstractNode element in CreateGetHashCodeOverride(currentClass).Body.Children.OfType<AbstractNode>()) { |
||||
builder.Append(language.CodeGenerator.GenerateCode(element, indent + "\t")); |
||||
} |
||||
|
||||
codeForMethodBody = builder.ToString().Trim(); |
||||
|
||||
if (addOtherMethod.IsChecked == true) |
||||
code.Append(indent + "\n" + string.Join("\n", CreateEqualsOverrides(currentClass).Select(item => generator.GenerateCode(item, indent)))); |
||||
} |
||||
|
||||
if (Options.AddOperatorOverloads) { |
||||
var checkStatements = new[] { |
||||
new IfElseStatement( |
||||
new InvocationExpression( |
||||
new IdentifierExpression("ReferenceEquals"), |
||||
new List<Expression>() { new IdentifierExpression("lhs"), new IdentifierExpression("rhs") } |
||||
), |
||||
new ReturnStatement(new PrimitiveExpression(true)) |
||||
), |
||||
new IfElseStatement( |
||||
new BinaryOperatorExpression( |
||||
new InvocationExpression( |
||||
new IdentifierExpression("ReferenceEquals"), |
||||
new List<Expression>() { new IdentifierExpression("lhs"), new PrimitiveExpression(null) } |
||||
), |
||||
BinaryOperatorType.LogicalOr, |
||||
new InvocationExpression( |
||||
new IdentifierExpression("ReferenceEquals"), |
||||
new List<Expression>() { new IdentifierExpression("rhs"), new PrimitiveExpression(null) } |
||||
) |
||||
), |
||||
new ReturnStatement(new PrimitiveExpression(false)) |
||||
) |
||||
}; |
||||
|
||||
BlockStatement equalsOpBody = new BlockStatement() { |
||||
Children = { |
||||
new ReturnStatement( |
||||
new InvocationExpression( |
||||
new MemberReferenceExpression(new IdentifierExpression("lhs"), "Equals"), |
||||
new List<Expression>() { new IdentifierExpression("rhs") } |
||||
) |
||||
) |
||||
} |
||||
}; |
||||
|
||||
if (currentClass.ClassType == Dom.ClassType.Class) { |
||||
equalsOpBody.Children.InsertRange(0, checkStatements); |
||||
} |
||||
|
||||
BlockStatement notEqualsOpBody = new BlockStatement() { |
||||
Children = { |
||||
new ReturnStatement( |
||||
new UnaryOperatorExpression( |
||||
new ParenthesizedExpression( |
||||
new BinaryOperatorExpression( |
||||
new IdentifierExpression("lhs"), |
||||
BinaryOperatorType.Equality, |
||||
new IdentifierExpression("rhs") |
||||
) |
||||
), |
||||
UnaryOperatorType.Not |
||||
) |
||||
) |
||||
} |
||||
}; |
||||
|
||||
code.Append(indent + "\n" + generator.GenerateCode(CreateOperatorOverload(OverloadableOperatorType.Equality, currentClass, equalsOpBody), indent)); |
||||
code.Append(indent + "\n" + generator.GenerateCode(CreateOperatorOverload(OverloadableOperatorType.InEquality, currentClass, notEqualsOpBody), indent)); |
||||
} |
||||
|
||||
if (Options.SurroundWithRegion) { |
||||
code.AppendLine(indent + "#endregion"); |
||||
} |
||||
|
||||
editor.Document.InsertNormalized(insertionEndAnchor.Offset, code.ToString()); |
||||
|
||||
return codeForMethodBody; |
||||
} |
||||
|
||||
List<MethodDeclaration> CreateEqualsOverrides(IClass currentClass) |
||||
{ |
||||
List<MethodDeclaration> methods = new List<MethodDeclaration>(); |
||||
|
||||
TypeReference boolReference = new TypeReference("System.Boolean", true); |
||||
TypeReference objectReference = new TypeReference("System.Object", true); |
||||
|
||||
MethodDeclaration method = new MethodDeclaration { |
||||
Name = "Equals", |
||||
Modifier = Modifiers.Public | Modifiers.Override, |
||||
TypeReference = boolReference |
||||
}; |
||||
method.Parameters.Add(new ParameterDeclarationExpression(objectReference, "obj")); |
||||
method.Body = new BlockStatement(); |
||||
|
||||
TypeReference 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 ParenthesizedExpression(expr); |
||||
expr = new BinaryOperatorExpression( |
||||
expr, BinaryOperatorType.LogicalAnd, |
||||
new InvocationExpression( |
||||
new IdentifierExpression("Equals"), |
||||
new List<Expression> { |
||||
new CastExpression(currentType, new IdentifierExpression("obj"), CastType.Cast) |
||||
})); |
||||
method.Body.AddChild(new ReturnStatement(expr)); |
||||
|
||||
methods.Add(method); |
||||
|
||||
// IEquatable implementation:
|
||||
method = new MethodDeclaration { |
||||
Name = "Equals", |
||||
Modifier = Modifiers.Public, |
||||
TypeReference = boolReference |
||||
}; |
||||
method.Parameters.Add(new ParameterDeclarationExpression(currentType, "other")); |
||||
method.Body = new BlockStatement(); |
||||
} else { |
||||
method.Body.AddChild(new LocalVariableDeclaration(new VariableDeclaration( |
||||
"other", |
||||
new CastExpression(currentType, new IdentifierExpression("obj"), CastType.TryCast), |
||||
currentType))); |
||||
method.Body.AddChild(new IfElseStatement( |
||||
new BinaryOperatorExpression(new IdentifierExpression("other"), BinaryOperatorType.ReferenceEquality, new PrimitiveExpression(null, "null")), |
||||
new ReturnStatement(new PrimitiveExpression(false, "false")))); |
||||
|
||||
// expr = new BinaryOperatorExpression(new ThisReferenceExpression(),
|
||||
// BinaryOperatorType.ReferenceEquality,
|
||||
// new IdentifierExpression("obj"));
|
||||
// method.Body.AddChild(new IfElseStatement(expr, new ReturnStatement(new PrimitiveExpression(true, "true"))));
|
||||
} |
||||
|
||||
|
||||
expr = null; |
||||
foreach (IField field in currentClass.Fields) { |
||||
if (field.IsStatic) continue; |
||||
|
||||
if (expr == null) { |
||||
expr = TestEquality("other", field); |
||||
} else { |
||||
expr = new BinaryOperatorExpression(expr, BinaryOperatorType.LogicalAnd, |
||||
TestEquality("other", field)); |
||||
} |
||||
} |
||||
|
||||
foreach (IProperty property in currentClass.Properties) { |
||||
if (property.IsStatic || !property.IsAutoImplemented()) continue; |
||||
if (expr == null) { |
||||
expr = TestEquality("other", property); |
||||
} else { |
||||
expr = new BinaryOperatorExpression(expr, BinaryOperatorType.LogicalAnd, |
||||
TestEquality("other", property)); |
||||
} |
||||
} |
||||
|
||||
method.Body.AddChild(new ReturnStatement(expr ?? new PrimitiveExpression(true, "true"))); |
||||
|
||||
methods.Add(method); |
||||
|
||||
return methods; |
||||
} |
||||
|
||||
MethodDeclaration CreateGetHashCodeOverride(IClass currentClass) |
||||
{ |
||||
TypeReference intReference = new TypeReference("System.Int32", true); |
||||
VariableDeclaration hashCodeVar = new VariableDeclaration("hashCode", new PrimitiveExpression(0, "0"), intReference); |
||||
|
||||
MethodDeclaration getHashCodeMethod = new MethodDeclaration { |
||||
Name = "GetHashCode", |
||||
Modifier = Modifiers.Public | Modifiers.Override, |
||||
TypeReference = intReference, |
||||
Body = new BlockStatement() |
||||
}; |
||||
|
||||
getHashCodeMethod.Body.AddChild(new LocalVariableDeclaration(hashCodeVar)); |
||||
|
||||
if (currentClass.Fields.Any(f => !f.IsStatic) || currentClass.Properties.Any(p => !p.IsStatic && p.IsAutoImplemented())) { |
||||
bool usePrimeMultiplication = currentClass.ProjectContent.Language == LanguageProperties.CSharp; |
||||
BlockStatement hashCalculationBlock; |
||||
|
||||
if (usePrimeMultiplication) { |
||||
hashCalculationBlock = new BlockStatement(); |
||||
getHashCodeMethod.Body.AddChild(new UncheckedStatement(hashCalculationBlock)); |
||||
} else { |
||||
hashCalculationBlock = getHashCodeMethod.Body; |
||||
} |
||||
|
||||
int fieldIndex = 0; |
||||
|
||||
foreach (IField field in currentClass.Fields) { |
||||
if (field.IsStatic) continue; |
||||
|
||||
AddToBlock(hashCodeVar, getHashCodeMethod, usePrimeMultiplication, hashCalculationBlock, ref fieldIndex, field); |
||||
} |
||||
|
||||
foreach (IProperty property in currentClass.Properties) { |
||||
if (property.IsStatic || !property.IsAutoImplemented()) continue; |
||||
|
||||
AddToBlock(hashCodeVar, getHashCodeMethod, usePrimeMultiplication, hashCalculationBlock, ref fieldIndex, property); |
||||
} |
||||
} |
||||
|
||||
getHashCodeMethod.Body.AddChild(new ReturnStatement(new IdentifierExpression(hashCodeVar.Name))); |
||||
return getHashCodeMethod; |
||||
} |
||||
|
||||
void AddToBlock(VariableDeclaration hashCodeVar, MethodDeclaration getHashCodeMethod, bool usePrimeMultiplication, BlockStatement hashCalculationBlock, ref int fieldIndex, IField field) |
||||
{ |
||||
Expression expr = new InvocationExpression(new MemberReferenceExpression(new IdentifierExpression(field.Name), "GetHashCode")); |
||||
if (usePrimeMultiplication) { |
||||
int prime = largePrimes[fieldIndex++ % largePrimes.Length]; |
||||
expr = new AssignmentExpression(new IdentifierExpression(hashCodeVar.Name), AssignmentOperatorType.Add, new BinaryOperatorExpression(new PrimitiveExpression(prime, prime.ToString()), BinaryOperatorType.Multiply, expr)); |
||||
} else { |
||||
expr = new AssignmentExpression(new IdentifierExpression(hashCodeVar.Name), AssignmentOperatorType.ExclusiveOr, expr); |
||||
} |
||||
if (IsValueType(field.ReturnType)) { |
||||
hashCalculationBlock.AddChild(new ExpressionStatement(expr)); |
||||
} else { |
||||
hashCalculationBlock.AddChild(new IfElseStatement(new BinaryOperatorExpression(new IdentifierExpression(field.Name), BinaryOperatorType.ReferenceInequality, new PrimitiveExpression(null, "null")), new ExpressionStatement(expr))); |
||||
} |
||||
} |
||||
|
||||
void AddToBlock(VariableDeclaration hashCodeVar, MethodDeclaration getHashCodeMethod, bool usePrimeMultiplication, BlockStatement hashCalculationBlock, ref int fieldIndex, IProperty property) |
||||
{ |
||||
Expression expr = new InvocationExpression(new MemberReferenceExpression(new IdentifierExpression(property.Name), "GetHashCode")); |
||||
if (usePrimeMultiplication) { |
||||
int prime = largePrimes[fieldIndex++ % largePrimes.Length]; |
||||
expr = new AssignmentExpression(new IdentifierExpression(hashCodeVar.Name), AssignmentOperatorType.Add, new BinaryOperatorExpression(new PrimitiveExpression(prime, prime.ToString()), BinaryOperatorType.Multiply, expr)); |
||||
} else { |
||||
expr = new AssignmentExpression(new IdentifierExpression(hashCodeVar.Name), AssignmentOperatorType.ExclusiveOr, expr); |
||||
} |
||||
if (IsValueType(property.ReturnType)) { |
||||
hashCalculationBlock.AddChild(new ExpressionStatement(expr)); |
||||
} else { |
||||
hashCalculationBlock.AddChild(new IfElseStatement(new BinaryOperatorExpression(new IdentifierExpression(property.Name), BinaryOperatorType.ReferenceInequality, new PrimitiveExpression(null, "null")), new ExpressionStatement(expr))); |
||||
} |
||||
} |
||||
|
||||
OperatorDeclaration CreateOperatorOverload(OverloadableOperatorType op, IClass currentClass, BlockStatement body) |
||||
{ |
||||
return new OperatorDeclaration() { |
||||
OverloadableOperator = op, |
||||
TypeReference = new TypeReference("System.Boolean", true), |
||||
Parameters = { |
||||
new ParameterDeclarationExpression(ConvertType(currentClass.DefaultReturnType), "lhs"), |
||||
new ParameterDeclarationExpression(ConvertType(currentClass.DefaultReturnType), "rhs") |
||||
}, |
||||
Modifier = Modifiers.Public | Modifiers.Static, |
||||
Body = body |
||||
}; |
||||
} |
||||
|
||||
protected override void CancelButtonClick(object sender, System.Windows.RoutedEventArgs e) |
||||
{ |
||||
base.CancelButtonClick(sender, e); |
||||
|
||||
editor.Document.Insert(anchor.Offset, baseCall); |
||||
editor.Select(anchor.Offset, baseCall.Length); |
||||
} |
||||
|
||||
protected override void OKButtonClick(object sender, System.Windows.RoutedEventArgs e) |
||||
{ |
||||
base.OKButtonClick(sender, e); |
||||
|
||||
editor.Caret.Offset = insertionEndAnchor.Offset; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,85 @@
@@ -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.Linq; |
||||
using ICSharpCode.AvalonEdit.Editing; |
||||
using ICSharpCode.AvalonEdit.Snippets; |
||||
using ICSharpCode.NRefactory.Ast; |
||||
using ICSharpCode.SharpDevelop; |
||||
using ICSharpCode.SharpDevelop.Dom; |
||||
using ICSharpCode.SharpDevelop.Dom.Refactoring; |
||||
using ICSharpCode.SharpDevelop.Editor; |
||||
using ICSharpCode.SharpDevelop.Editor.CodeCompletion; |
||||
using SharpRefactoring.Gui; |
||||
|
||||
namespace SharpRefactoring |
||||
{ |
||||
public class OverrideEqualsGetHashCodeMethodsRefactoring : ICompletionItemHandler |
||||
{ |
||||
public void Insert(CompletionContext context, ICompletionItem item) |
||||
{ |
||||
if (item == null) |
||||
throw new ArgumentNullException("item"); |
||||
|
||||
if (!(item is OverrideCompletionItem)) |
||||
throw new ArgumentException("item is not an OverrideCompletionItem"); |
||||
|
||||
OverrideCompletionItem completionItem = item as OverrideCompletionItem; |
||||
|
||||
ITextEditor textEditor = context.Editor; |
||||
|
||||
IEditorUIService uiService = textEditor.GetService(typeof(IEditorUIService)) as IEditorUIService; |
||||
|
||||
if (uiService == null) |
||||
return; |
||||
|
||||
ParseInformation parseInfo = ParserService.GetParseInformation(textEditor.FileName); |
||||
|
||||
if (parseInfo == null) |
||||
return; |
||||
|
||||
CodeGenerator generator = parseInfo.CompilationUnit.Language.CodeGenerator; |
||||
IClass current = parseInfo.CompilationUnit.GetInnermostClass(textEditor.Caret.Line, textEditor.Caret.Column); |
||||
ClassFinder finder = new ClassFinder(current, textEditor.Caret.Line, textEditor.Caret.Column); |
||||
|
||||
if (current == null) |
||||
return; |
||||
|
||||
using (textEditor.Document.OpenUndoGroup()) { |
||||
ITextAnchor startAnchor = textEditor.Document.CreateAnchor(textEditor.Caret.Offset); |
||||
startAnchor.MovementType = AnchorMovementType.BeforeInsertion; |
||||
|
||||
ITextAnchor endAnchor = textEditor.Document.CreateAnchor(textEditor.Caret.Offset); |
||||
endAnchor.MovementType = AnchorMovementType.AfterInsertion; |
||||
|
||||
MethodDeclaration member = (MethodDeclaration)generator.GetOverridingMethod(completionItem.Member, finder); |
||||
|
||||
string indent = DocumentUtilitites.GetWhitespaceBefore(textEditor.Document, textEditor.Caret.Offset); |
||||
string codeForBaseCall = generator.GenerateCode(member.Body.Children.OfType<AbstractNode>().First(), ""); |
||||
string code = generator.GenerateCode(member, indent); |
||||
int marker = code.IndexOf(codeForBaseCall); |
||||
|
||||
textEditor.Document.Insert(startAnchor.Offset, code.Substring(0, marker).TrimStart()); |
||||
|
||||
ITextAnchor insertionPos = textEditor.Document.CreateAnchor(endAnchor.Offset); |
||||
insertionPos.MovementType = AnchorMovementType.BeforeInsertion; |
||||
|
||||
InsertionContext insertionContext = new InsertionContext(textEditor.GetService(typeof(TextArea)) as TextArea, startAnchor.Offset); |
||||
|
||||
AbstractInlineRefactorDialog dialog = new OverrideEqualsGetHashCodeMethodsDialog(insertionContext, textEditor, startAnchor, endAnchor, insertionPos, current, completionItem.Member as IMethod, codeForBaseCall.Trim()); |
||||
dialog.Element = uiService.CreateInlineUIElement(insertionPos, dialog); |
||||
|
||||
textEditor.Document.InsertNormalized(endAnchor.Offset, Environment.NewLine + code.Substring(marker + codeForBaseCall.Length)); |
||||
|
||||
insertionContext.RegisterActiveElement(new InlineRefactorSnippetElement(cxt => null, ""), dialog); |
||||
insertionContext.RaiseInsertionCompleted(EventArgs.Empty); |
||||
} |
||||
} |
||||
|
||||
public bool Handles(ICompletionItem item) |
||||
{ |
||||
return item is OverrideCompletionItem && (item.Text == "GetHashCode()" || item.Text == "Equals(object obj)"); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
<gui:AbstractInlineRefactorDialog x:Class="CSharpBinding.Refactoring.OverrideToStringMethodDialog" |
||||
Header="{sd:Localize AddIns.SharpRefactoring.OverrideToStringMethod}" |
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||
xmlns:gui="clr-namespace:CSharpBinding.Refactoring" |
||||
xmlns:sd="http://icsharpcode.net/sharpdevelop/core" |
||||
xmlns:widgets="http://icsharpcode.net/sharpdevelop/widgets" |
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
||||
<Grid Cursor="Arrow" KeyboardNavigation.TabNavigation="Cycle"> |
||||
<Grid.RowDefinitions> |
||||
<RowDefinition Height="Auto" /> |
||||
<RowDefinition Height="*" /> |
||||
<RowDefinition Height="Auto" /> |
||||
</Grid.RowDefinitions> |
||||
<TextBlock Margin="3" |
||||
Text="{sd:Localize AddIns.SharpRefactoring.OverrideToStringMethod.Description}" |
||||
TextWrapping="Wrap" /> |
||||
<ListBox x:Name="listBox" Grid.Row="1" Margin="4" SelectionMode="Multiple" |
||||
ItemContainerStyle="{x:Static sd:GlobalStyles.ListBoxItemFocusHighlightStyle}"> |
||||
<ListBox.ItemTemplate> |
||||
<DataTemplate> |
||||
<!-- The checkbox is toggling the actual IsSelected property on the container (ListBoxItem), so it'll update ListBox.SelectedItems --> |
||||
<!-- Focusable=false because the ListViewItem is already focusable --> |
||||
<CheckBox Content="{Binding Text}" |
||||
IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsSelected}" /> |
||||
</DataTemplate> |
||||
</ListBox.ItemTemplate> |
||||
</ListBox> |
||||
<widgets:UniformGridWithSpacing Grid.Row="2" Columns="3" Margin="3" DockPanel.Dock="Bottom" HorizontalAlignment="Center"> |
||||
<Button Content="{sd:Localize Global.OKButtonText}" Click="OKButtonClick" Style="{x:Static sd:GlobalStyles.ButtonStyle}" /> |
||||
<Button Content="{sd:Localize Global.CancelButtonText}" Click="CancelButtonClick" Style="{x:Static sd:GlobalStyles.ButtonStyle}" /> |
||||
<ToggleButton x:Name="selectAll" Content="{sd:Localize Global.SelectAllButtonText}" IsChecked="{Binding AllSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type gui:OverrideToStringMethodDialog}}, Mode=TwoWay}" Checked="SelectAllChecked" Unchecked="SelectAllUnchecked" /> |
||||
</widgets:UniformGridWithSpacing> |
||||
</Grid> |
||||
</gui:AbstractInlineRefactorDialog> |
@ -0,0 +1,140 @@
@@ -0,0 +1,140 @@
|
||||
// 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.Linq; |
||||
using System.Text; |
||||
using System.Threading; |
||||
using System.Windows.Input; |
||||
|
||||
using ICSharpCode.AvalonEdit.Snippets; |
||||
using ICSharpCode.NRefactory.CSharp; |
||||
using ICSharpCode.NRefactory.CSharp.Refactoring; |
||||
using ICSharpCode.NRefactory.Editor; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
using ICSharpCode.SharpDevelop.Editor; |
||||
using ICSharpCode.SharpDevelop.Refactoring; |
||||
|
||||
namespace CSharpBinding.Refactoring |
||||
{ |
||||
/// <summary>
|
||||
/// Interaction logic for OverrideToStringMethodDialog.xaml
|
||||
/// </summary>
|
||||
public partial class OverrideToStringMethodDialog : AbstractInlineRefactorDialog |
||||
{ |
||||
AstNode baseCallNode; |
||||
string insertedCode; |
||||
ITextEditor editor; |
||||
|
||||
public OverrideToStringMethodDialog(InsertionContext context, ITextEditor editor, ITextAnchor startAnchor, ITextAnchor anchor, IList<PropertyOrFieldWrapper> fields, AstNode baseCallNode) |
||||
: base(context, editor, anchor) |
||||
{ |
||||
InitializeComponent(); |
||||
|
||||
this.baseCallNode = baseCallNode; |
||||
this.listBox.ItemsSource = fields; |
||||
this.editor = editor; |
||||
|
||||
listBox.SelectAll(); |
||||
} |
||||
|
||||
protected override string GenerateCode(IUnresolvedTypeDefinition currentClass) |
||||
{ |
||||
string[] fields = listBox.SelectedItems.OfType<PropertyOrFieldWrapper>().Select(f2 => f2.MemberName).ToArray(); |
||||
PrimitiveExpression formatString = new PrimitiveExpression(GenerateFormatString(currentClass, editor.Language.CodeGenerator, fields)); |
||||
List<Expression> param = new List<Expression>() { formatString }; |
||||
ReturnStatement ret = new ReturnStatement(new InvocationExpression( |
||||
new MemberReferenceExpression(new TypeReferenceExpression(new SimpleType("System.String")), "Format"), |
||||
param.Concat(fields.Select(f => new IdentifierExpression(f))).ToList() |
||||
)); |
||||
|
||||
if (baseCallNode != null) { |
||||
MethodDeclaration insertedOverrideMethod = refactoringContext.GetNode().PrevSibling as MethodDeclaration; |
||||
if (insertedOverrideMethod == null) |
||||
{ |
||||
// We are not inside of a method declaration
|
||||
return null; |
||||
} |
||||
|
||||
using (Script script = refactoringContext.StartScript()) { |
||||
// Find base method call and replace it by return statement
|
||||
script.AddTo(insertedOverrideMethod.Body, ret); |
||||
} |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
string GenerateFormatString(IUnresolvedTypeDefinition currentClass, ICodeGenerator generator, string[] fields) |
||||
{ |
||||
string fieldsString = ""; |
||||
|
||||
if (fields.Any()) { |
||||
StringBuilder formatString = new StringBuilder(); |
||||
|
||||
for (int i = 0; i < fields.Length; i++) { |
||||
if (i != 0) |
||||
formatString.Append(", "); |
||||
formatString.AppendFormat("{0}={{{1}}}", generator.GetPropertyName(fields[i]), i); |
||||
} |
||||
|
||||
fieldsString = " " + formatString.ToString(); |
||||
} |
||||
|
||||
return "[" + currentClass.Name + fieldsString + "]"; |
||||
} |
||||
|
||||
void SelectAllChecked(object sender, System.Windows.RoutedEventArgs e) |
||||
{ |
||||
listBox.SelectAll(); |
||||
} |
||||
|
||||
void SelectAllUnchecked(object sender, System.Windows.RoutedEventArgs e) |
||||
{ |
||||
listBox.UnselectAll(); |
||||
} |
||||
|
||||
bool AllSelected { |
||||
get { return listBox.SelectedItems.Count == listBox.Items.Count; } |
||||
} |
||||
|
||||
protected override void OnKeyDown(KeyEventArgs e) |
||||
{ |
||||
Key? allAccessKey = GetAccessKeyFromButton(selectAll); |
||||
|
||||
if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt && allAccessKey == e.SystemKey) { |
||||
if (AllSelected) |
||||
listBox.UnselectAll(); |
||||
else |
||||
listBox.SelectAll(); |
||||
e.Handled = true; |
||||
} |
||||
|
||||
base.OnKeyDown(e); |
||||
} |
||||
|
||||
protected override void CancelButtonClick(object sender, System.Windows.RoutedEventArgs e) |
||||
{ |
||||
base.CancelButtonClick(sender, e); |
||||
|
||||
if (baseCallNode != null) { |
||||
// Insert at least the 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, baseCallNode); |
||||
} |
||||
} |
||||
} |
||||
|
||||
protected override void OKButtonClick(object sender, System.Windows.RoutedEventArgs e) |
||||
{ |
||||
base.OKButtonClick(sender, e); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue