Browse Source

- OverrideToString completion now works.

- Added RefactoringContext.GetTypeResolveContext() method.
pull/45/merge
Andreas Weizel 12 years ago
parent
commit
738529e4aa
  1. 7
      src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin
  2. 6
      src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj
  3. 5
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs
  4. 5
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideCompletionData.cs
  5. 148
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideToStringCompletionData.cs
  6. 85
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreateProperties.cs
  7. 26
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesCommand.cs
  8. 74
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesDialog.xaml
  9. 144
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesDialog.xaml.cs
  10. 94
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/FieldWrapper.cs
  11. 35
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsDialog.xaml
  12. 460
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsDialog.xaml.cs
  13. 85
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsRefactoring.cs
  14. 34
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideToStringMethodDialog.xaml
  15. 140
      src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideToStringMethodDialog.xaml.cs
  16. 8
      src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringContext.cs

7
src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin

@ -155,7 +155,12 @@ @@ -155,7 +155,12 @@
class="CSharpBinding.Refactoring.SearchForIssuesCommand"/>
</Path>
<Path name="/SharpDevelop/ViewContent/AvalonEdit/SnippetElementProviders">
<Path name="/SharpDevelop/ViewContent/TextEditor/OverrideCompletionHandler">
<Class id="toString" class="CSharpBinding.Refactoring.OverrideToStringMethodRefactoring" />
<!-- <Class id="equalsAndGetHashCode" class="CSharpBinding.Refactoring.OverrideEqualsGetHashCodeMethodsRefactoring" />-->
</Path>
<Path name="/SharpDevelop/ViewContent/TextEditor/SnippetElementProviders">
<Class id="ctor" class="CSharpBinding.Refactoring.InsertCtorSnippetRefactoring" />
<!--<Class id="switch" class="CSharpBinding.Refactoring.SwitchSnippetProvider" />
<Class id="propall" class="CSharpBinding.Refactoring.CreateProperties" />-->

6
src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj

@ -73,6 +73,7 @@ @@ -73,6 +73,7 @@
<Compile Include="Src\Completion\CSharpMethodInsight.cs" />
<Compile Include="Src\Completion\ImportCompletionData.cs" />
<Compile Include="Src\Completion\OverrideCompletionData.cs" />
<Compile Include="Src\Completion\OverrideToStringCompletionData.cs" />
<Compile Include="Src\Completion\SegmentTrackingOutputFormatter.cs" />
<Compile Include="Src\CSharpTextEditorExtension.cs" />
<Compile Include="Src\FormattingStrategy\CSharpFormatter.cs" />
@ -80,6 +81,7 @@ @@ -80,6 +81,7 @@
<Compile Include="Src\Refactoring\AbstractInlineRefactorDialog.cs" />
<Compile Include="Src\Refactoring\CSharpCodeGenerator.cs" />
<Compile Include="Src\Refactoring\CSharpSyntaxIssue.cs" />
<Compile Include="Src\Refactoring\FieldWrapper.cs" />
<Compile Include="Src\Refactoring\InlineRefactorSnippetElement.cs" />
<Compile Include="Src\Refactoring\InsertCtorDialog.xaml.cs">
<DependentUpon>InsertCtorDialog.xaml</DependentUpon>
@ -95,6 +97,9 @@ @@ -95,6 +97,9 @@
<Compile Include="Src\Refactoring\IssueOptionsViewModel.cs" />
<Compile Include="Src\Refactoring\MoveTypeToFileContextAction.cs" />
<Compile Include="Src\Refactoring\RenameContextAction.cs" />
<Compile Include="Src\Refactoring\OverrideToStringMethodDialog.xaml.cs">
<DependentUpon>OverrideToStringMethodDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Src\Refactoring\PropertyOrFieldWrapper.cs" />
<Compile Include="Src\Refactoring\SDNamingConventionService.cs" />
<Compile Include="Src\Refactoring\SDRedundantUsingIssue.cs" />
@ -193,6 +198,7 @@ @@ -193,6 +198,7 @@
<Page Include="Src\Refactoring\InsertCtorDialog.xaml" />
<Page Include="Src\Refactoring\IssueOptions.xaml" />
<Page Include="Src\OptionPanels\BuildOptions.xaml" />
<Page Include="Src\Refactoring\OverrideToStringMethodDialog.xaml" />
<Page Include="Src\Refactoring\SearchForIssuesDialog.xaml" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />

5
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs

@ -95,7 +95,10 @@ namespace CSharpBinding.Completion @@ -95,7 +95,10 @@ namespace CSharpBinding.Completion
ICompletionData ICompletionDataFactory.CreateNewOverrideCompletionData(int declarationBegin, IUnresolvedTypeDefinition type, IMember m)
{
return new OverrideCompletionData(declarationBegin, m, contextAtCaret);
if ((m.EntityType == EntityType.Method) && (m.Name == "ToString"))
return new OverrideToStringCompletionData(declarationBegin, m, contextAtCaret);
else
return new OverrideCompletionData(declarationBegin, m, contextAtCaret);
}
ICompletionData ICompletionDataFactory.CreateNewPartialCompletionData(int declarationBegin, IUnresolvedTypeDefinition type, IUnresolvedMember m)

5
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideCompletionData.cs

@ -24,8 +24,8 @@ namespace CSharpBinding.Completion @@ -24,8 +24,8 @@ namespace CSharpBinding.Completion
/// </summary>
class OverrideCompletionData : EntityCompletionData
{
readonly int declarationBegin;
readonly CSharpTypeResolveContext contextAtCaret;
protected readonly int declarationBegin;
protected readonly CSharpTypeResolveContext contextAtCaret;
public OverrideCompletionData(int declarationBegin, IMember m, CSharpTypeResolveContext contextAtCaret)
: base(m)
@ -43,6 +43,7 @@ namespace CSharpBinding.Completion @@ -43,6 +43,7 @@ namespace CSharpBinding.Completion
base.Complete(context);
return;
}
TypeSystemAstBuilder b = new TypeSystemAstBuilder(new CSharpResolver(contextAtCaret));
b.ShowTypeParameterConstraints = false;
b.GenerateBody = true;

148
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideToStringCompletionData.cs

@ -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++;
}
}
}
}

85
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreateProperties.cs

@ -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++;
}
}
}
}

26
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesCommand.cs

@ -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)));
}
}
}

74
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesDialog.xaml

@ -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>

144
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesDialog.xaml.cs

@ -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);
}
}
}

94
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/FieldWrapper.cs

@ -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));
}
}
}
}

35
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsDialog.xaml

@ -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>

460
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsDialog.xaml.cs

@ -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;
}
}
}

85
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsRefactoring.cs

@ -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)");
}
}
}

34
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideToStringMethodDialog.xaml

@ -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>

140
src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideToStringMethodDialog.xaml.cs

@ -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);
}
}
}

8
src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringContext.cs

@ -87,6 +87,14 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring @@ -87,6 +87,14 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
return RootNode.GetNodeAt<T> (Location);
}
public CSharpTypeResolveContext GetTypeResolveContext()
{
if (UnresolvedFile != null)
return UnresolvedFile.GetTypeResolveContext(Compilation, Location);
else
return null;
}
#region Text stuff
public virtual TextEditorOptions TextEditorOptions {
get {

Loading…
Cancel
Save