Browse Source
Conflicts: src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj src/AddIns/BackendBindings/CSharpBinding/Project/Src/CSharpTextEditorExtension.cspull/45/merge
26 changed files with 2252 additions and 86 deletions
@ -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,161 @@ |
|||||||
|
// 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.Threading; |
||||||
|
using System.Windows; |
||||||
|
using System.Windows.Controls; |
||||||
|
using System.Windows.Input; |
||||||
|
using System.Windows.Threading; |
||||||
|
|
||||||
|
using ICSharpCode.AvalonEdit.Snippets; |
||||||
|
using ICSharpCode.Core.Presentation; |
||||||
|
using ICSharpCode.NRefactory.CSharp; |
||||||
|
using ICSharpCode.NRefactory.Editor; |
||||||
|
using ICSharpCode.NRefactory.TypeSystem; |
||||||
|
using ICSharpCode.SharpDevelop.Parser; |
||||||
|
using CSharpBinding.Refactoring; |
||||||
|
using ICSharpCode.SharpDevelop; |
||||||
|
using ICSharpCode.SharpDevelop.Editor; |
||||||
|
|
||||||
|
namespace CSharpBinding.Refactoring |
||||||
|
{ |
||||||
|
public abstract class AbstractInlineRefactorDialog : GroupBox, IOptionBindingContainer, IActiveElement |
||||||
|
{ |
||||||
|
protected ITextAnchor anchor; |
||||||
|
protected ITextAnchor insertionEndAnchor; |
||||||
|
protected ITextEditor editor; |
||||||
|
|
||||||
|
protected SDRefactoringContext refactoringContext; |
||||||
|
protected InsertionContext insertionContext; |
||||||
|
|
||||||
|
public IInlineUIElement Element { get; set; } |
||||||
|
|
||||||
|
protected AbstractInlineRefactorDialog(InsertionContext context, ITextEditor editor, ITextAnchor anchor) |
||||||
|
{ |
||||||
|
if (context == null) |
||||||
|
throw new ArgumentNullException("context"); |
||||||
|
|
||||||
|
this.anchor = insertionEndAnchor = anchor; |
||||||
|
this.editor = editor; |
||||||
|
this.insertionContext = context; |
||||||
|
this.refactoringContext = SDRefactoringContext.Create(editor, CancellationToken.None); |
||||||
|
|
||||||
|
this.Background = SystemColors.ControlBrush; |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual void FocusFirstElement() |
||||||
|
{ |
||||||
|
Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(delegate { this.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); })); |
||||||
|
} |
||||||
|
|
||||||
|
protected abstract string GenerateCode(IUnresolvedTypeDefinition currentClass); |
||||||
|
|
||||||
|
protected virtual void OKButtonClick(object sender, RoutedEventArgs e) |
||||||
|
{ |
||||||
|
ParseInformation parseInfo = SD.ParserService.GetCachedParseInformation(editor.FileName); |
||||||
|
|
||||||
|
if (optionBindings != null) { |
||||||
|
foreach (OptionBinding binding in optionBindings) |
||||||
|
binding.Save(); |
||||||
|
} |
||||||
|
|
||||||
|
if (parseInfo != null) { |
||||||
|
IUnresolvedTypeDefinition current = parseInfo.UnresolvedFile.GetInnermostTypeDefinition(anchor.Line, anchor.Column); |
||||||
|
|
||||||
|
using (editor.Document.OpenUndoGroup()) { |
||||||
|
// GenerateCode could modify the document.
|
||||||
|
// So read anchor.Offset after code generation.
|
||||||
|
GenerateCode(current); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Deactivate(); |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual void CancelButtonClick(object sender, RoutedEventArgs e) |
||||||
|
{ |
||||||
|
Deactivate(); |
||||||
|
} |
||||||
|
|
||||||
|
List<OptionBinding> optionBindings; |
||||||
|
|
||||||
|
public void AddBinding(OptionBinding binding) |
||||||
|
{ |
||||||
|
if (optionBindings == null) |
||||||
|
optionBindings = new List<OptionBinding>(); |
||||||
|
|
||||||
|
optionBindings.Add(binding); |
||||||
|
} |
||||||
|
|
||||||
|
protected AstType ConvertType(IType type) |
||||||
|
{ |
||||||
|
return refactoringContext.CreateShortType(type); |
||||||
|
} |
||||||
|
|
||||||
|
bool IActiveElement.IsEditable { |
||||||
|
get { return false; } |
||||||
|
} |
||||||
|
|
||||||
|
ISegment IActiveElement.Segment { |
||||||
|
get { return null; } |
||||||
|
} |
||||||
|
|
||||||
|
void IActiveElement.OnInsertionCompleted() |
||||||
|
{ |
||||||
|
OnInsertionCompleted(); |
||||||
|
} |
||||||
|
|
||||||
|
protected virtual void OnInsertionCompleted() |
||||||
|
{ |
||||||
|
FocusFirstElement(); |
||||||
|
} |
||||||
|
|
||||||
|
void IActiveElement.Deactivate(SnippetEventArgs e) |
||||||
|
{ |
||||||
|
if (e.Reason == DeactivateReason.Deleted) { |
||||||
|
Deactivate(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (e.Reason == DeactivateReason.ReturnPressed) |
||||||
|
OKButtonClick(null, null); |
||||||
|
|
||||||
|
if (e.Reason == DeactivateReason.EscapePressed) |
||||||
|
CancelButtonClick(null, null); |
||||||
|
|
||||||
|
Deactivate(); |
||||||
|
} |
||||||
|
|
||||||
|
bool deactivated; |
||||||
|
|
||||||
|
void Deactivate() |
||||||
|
{ |
||||||
|
if (Element == null) |
||||||
|
throw new InvalidOperationException("no IInlineUIElement set!"); |
||||||
|
if (deactivated) |
||||||
|
return; |
||||||
|
|
||||||
|
deactivated = true; |
||||||
|
Element.Remove(); |
||||||
|
|
||||||
|
insertionContext.Deactivate(null); |
||||||
|
} |
||||||
|
|
||||||
|
protected Key? GetAccessKeyFromButton(ContentControl control) |
||||||
|
{ |
||||||
|
if (control == null) |
||||||
|
return null; |
||||||
|
string text = control.Content as string; |
||||||
|
if (text == null) |
||||||
|
return null; |
||||||
|
int index = text.IndexOf('_'); |
||||||
|
if (index < 0 || index > text.Length - 2) |
||||||
|
return null; |
||||||
|
char ch = text[index + 1]; |
||||||
|
// works only for letter keys!
|
||||||
|
return (Key)new KeyConverter().ConvertFrom(ch.ToString()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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 @@ |
|||||||
|
// 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 @@ |
|||||||
|
<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 @@ |
|||||||
|
// 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 @@ |
|||||||
|
// 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 @@ |
|||||||
|
// 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 System.Windows.Documents; |
||||||
|
|
||||||
|
using ICSharpCode.AvalonEdit.Snippets; |
||||||
|
|
||||||
|
namespace CSharpBinding.Refactoring |
||||||
|
{ |
||||||
|
class InlineRefactorSnippetElement : SnippetElement |
||||||
|
{ |
||||||
|
Func<InsertionContext, AbstractInlineRefactorDialog> createDialog; |
||||||
|
string previewText; |
||||||
|
|
||||||
|
public InlineRefactorSnippetElement(Func<InsertionContext, AbstractInlineRefactorDialog> createDialog, string previewText) |
||||||
|
{ |
||||||
|
this.createDialog = createDialog; |
||||||
|
this.previewText = previewText; |
||||||
|
} |
||||||
|
|
||||||
|
public override void Insert(InsertionContext context) |
||||||
|
{ |
||||||
|
AbstractInlineRefactorDialog dialog = createDialog(context); |
||||||
|
if (dialog != null) |
||||||
|
context.RegisterActiveElement(this, dialog); |
||||||
|
} |
||||||
|
|
||||||
|
public override Inline ToTextRun() |
||||||
|
{ |
||||||
|
return new Italic() { Inlines = { previewText } }; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
<ref:AbstractInlineRefactorDialog x:Class="CSharpBinding.Refactoring.InsertCtorDialog" |
||||||
|
Header="{sd:Localize AddIns.SharpRefactoring.InsertCtor}" |
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||||
|
xmlns:ref="clr-namespace:CSharpBinding.Refactoring" |
||||||
|
xmlns:sd="http://icsharpcode.net/sharpdevelop/core" |
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
||||||
|
<ref:AbstractInlineRefactorDialog.Resources> |
||||||
|
<ref:IntToBoolConverter x:Key="intToBoolConverter" /> |
||||||
|
<Style TargetType="Button" BasedOn="{x:Static sd:GlobalStyles.ButtonStyle}"/> |
||||||
|
</ref:AbstractInlineRefactorDialog.Resources> |
||||||
|
<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.InsertCtor.Description}" |
||||||
|
TextWrapping="Wrap" /> |
||||||
|
<ListView x:Name="varList" Grid.Row="1" |
||||||
|
SelectionMode="Multiple" KeyboardNavigation.TabNavigation="Cycle" |
||||||
|
ItemContainerStyle="{x:Static sd:GlobalStyles.ListViewItemFocusHighlightStyle}" 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.InsertCtor.AddCheckForNullLabel}"> |
||||||
|
<GridViewColumn.CellTemplate> |
||||||
|
<DataTemplate> |
||||||
|
<CheckBox IsChecked="{Binding AddCheckForNull, Mode=TwoWay}" IsEnabled="{Binding IsNullable}" /> |
||||||
|
</DataTemplate> |
||||||
|
</GridViewColumn.CellTemplate> |
||||||
|
</GridViewColumn> |
||||||
|
<GridViewColumn Header="{sd:Localize AddIns.SharpRefactoring.InsertCtor.AddRangeCheckLabel}"> |
||||||
|
<GridViewColumn.CellTemplate> |
||||||
|
<DataTemplate> |
||||||
|
<CheckBox IsChecked="{Binding AddRangeCheck, Mode=TwoWay}" IsEnabled="{Binding HasRange}" /> |
||||||
|
</DataTemplate> |
||||||
|
</GridViewColumn.CellTemplate> |
||||||
|
</GridViewColumn> |
||||||
|
</GridView> |
||||||
|
</ListView.View> |
||||||
|
</ListView> |
||||||
|
<DockPanel Grid.Row="2" LastChildFill="False"> |
||||||
|
<Button DockPanel.Dock="Left" Content="{sd:Localize Global.OKButtonText}" Margin="3" Click="OKButtonClick" /> |
||||||
|
<Button DockPanel.Dock="Left" Content="{sd:Localize Global.CancelButtonText}" Margin="3" Click="CancelButtonClick" /> |
||||||
|
<Button x:Name="moveDown" DockPanel.Dock="Right" Content="{sd:Localize Global.MoveDown}" Margin="3" IsEnabled="{Binding SelectedIndex, ElementName=varList, Converter={StaticResource intToBoolConverter}}" Click="DownClick" /> |
||||||
|
<Button x:Name="moveUp" DockPanel.Dock="Right" Content="{sd:Localize Global.MoveUp}" Margin="3" IsEnabled="{Binding SelectedIndex, ElementName=varList, Converter={StaticResource intToBoolConverter}}" Click="UpClick" /> |
||||||
|
<ToggleButton x:Name="selectAll" DockPanel.Dock="Right" Content="{sd:Localize Global.SelectAllButtonText}" Margin="3" IsChecked="{Binding AllSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ref:InsertCtorDialog}}, Mode=OneWay}" Checked="SelectAllChecked" Unchecked="SelectAllUnchecked" /> |
||||||
|
</DockPanel> |
||||||
|
</Grid> |
||||||
|
</ref:AbstractInlineRefactorDialog> |
@ -0,0 +1,273 @@ |
|||||||
|
// 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.Globalization; |
||||||
|
using System.IO; |
||||||
|
using System.Linq; |
||||||
|
using System.Text; |
||||||
|
using System.Windows.Controls; |
||||||
|
using System.Windows.Data; |
||||||
|
using System.Windows.Input; |
||||||
|
using System.Windows.Threading; |
||||||
|
|
||||||
|
using ICSharpCode.AvalonEdit.Snippets; |
||||||
|
using ICSharpCode.NRefactory.CSharp; |
||||||
|
using ICSharpCode.NRefactory.CSharp.Refactoring; |
||||||
|
using ICSharpCode.NRefactory.CSharp.Resolver; |
||||||
|
using ICSharpCode.NRefactory.Editor; |
||||||
|
using ICSharpCode.NRefactory.TypeSystem; |
||||||
|
using ICSharpCode.SharpDevelop; |
||||||
|
using ICSharpCode.SharpDevelop.Editor; |
||||||
|
|
||||||
|
namespace CSharpBinding.Refactoring |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Interaction logic for InsertCtorDialog.xaml
|
||||||
|
/// </summary>
|
||||||
|
public partial class InsertCtorDialog : AbstractInlineRefactorDialog |
||||||
|
{ |
||||||
|
IList<PropertyOrFieldWrapper> parameterList; |
||||||
|
|
||||||
|
public InsertCtorDialog(InsertionContext context, ITextEditor editor, ITextAnchor anchor, IUnresolvedTypeDefinition current, IList<PropertyOrFieldWrapper> possibleParameters) |
||||||
|
: base(context, editor, anchor) |
||||||
|
{ |
||||||
|
InitializeComponent(); |
||||||
|
|
||||||
|
this.varList.ItemsSource = parameterList = possibleParameters; |
||||||
|
|
||||||
|
if (!parameterList.Any()) |
||||||
|
Visibility = System.Windows.Visibility.Collapsed; |
||||||
|
} |
||||||
|
|
||||||
|
protected override string GenerateCode(IUnresolvedTypeDefinition currentClass) |
||||||
|
{ |
||||||
|
List<PropertyOrFieldWrapper> filtered = this.varList.SelectedItems.OfType<PropertyOrFieldWrapper>() |
||||||
|
.OrderBy(p => p.Index) |
||||||
|
.ToList(); |
||||||
|
|
||||||
|
var insertedConstructor = refactoringContext.GetNode<ConstructorDeclaration>(); |
||||||
|
if (insertedConstructor == null) |
||||||
|
{ |
||||||
|
// We are not inside of a constructor declaration
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
using (Script script = refactoringContext.StartScript()) { |
||||||
|
BlockStatement originalCtorBody = insertedConstructor.Body; |
||||||
|
|
||||||
|
foreach (PropertyOrFieldWrapper w in filtered) { |
||||||
|
if (w.AddCheckForNull) { |
||||||
|
// true = reference, null = generic or unknown
|
||||||
|
if (w.Type.IsReferenceType != false) |
||||||
|
script.AddTo(originalCtorBody, |
||||||
|
new IfElseStatement( |
||||||
|
new BinaryOperatorExpression(new IdentifierExpression(w.ParameterName), BinaryOperatorType.Equality, new PrimitiveExpression(null)), |
||||||
|
new ThrowStatement(new ObjectCreateExpression(new SimpleType("ArgumentNullException"), new List<Expression>() { new PrimitiveExpression(w.ParameterName, '"' + w.ParameterName + '"') })) |
||||||
|
) |
||||||
|
); |
||||||
|
else |
||||||
|
script.AddTo(originalCtorBody, |
||||||
|
new IfElseStatement( |
||||||
|
new UnaryOperatorExpression(UnaryOperatorType.Not, new MemberReferenceExpression(new IdentifierExpression(w.MemberName), "HasValue")), |
||||||
|
new ThrowStatement(new ObjectCreateExpression(new SimpleType("ArgumentNullException"), new List<Expression>() { new PrimitiveExpression(w.ParameterName, '"' + w.ParameterName + '"') })) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
if (w.AddRangeCheck) { |
||||||
|
script.AddTo(originalCtorBody, |
||||||
|
new IfElseStatement( |
||||||
|
new BinaryOperatorExpression( |
||||||
|
new BinaryOperatorExpression(new IdentifierExpression(w.ParameterName), BinaryOperatorType.LessThan, new IdentifierExpression("lower")), |
||||||
|
BinaryOperatorType.ConditionalOr, |
||||||
|
new BinaryOperatorExpression(new IdentifierExpression(w.ParameterName), BinaryOperatorType.GreaterThan, new IdentifierExpression("upper")) |
||||||
|
), |
||||||
|
new ThrowStatement( |
||||||
|
new ObjectCreateExpression( |
||||||
|
new SimpleType("ArgumentOutOfRangeException"), |
||||||
|
new List<Expression>() { new PrimitiveExpression(w.ParameterName, '"' + w.ParameterName + '"'), new IdentifierExpression(w.ParameterName), new BinaryOperatorExpression(new PrimitiveExpression("Value must be between "), BinaryOperatorType.Add, new BinaryOperatorExpression(new IdentifierExpression("lower"), BinaryOperatorType.Add, new BinaryOperatorExpression(new PrimitiveExpression(" and "), BinaryOperatorType.Add, new IdentifierExpression("upper")))) } |
||||||
|
) |
||||||
|
) |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
foreach (PropertyOrFieldWrapper w in filtered) { |
||||||
|
script.AddTo(originalCtorBody, |
||||||
|
new ExpressionStatement(new AssignmentExpression(new MemberReferenceExpression(new ThisReferenceExpression(), w.MemberName), AssignmentOperatorType.Assign, new IdentifierExpression(w.ParameterName))) |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
AnchorElement parameterListElement = insertionContext.ActiveElements |
||||||
|
.OfType<AnchorElement>() |
||||||
|
.FirstOrDefault(item => item.Name.Equals("parameterList", StringComparison.OrdinalIgnoreCase)); |
||||||
|
|
||||||
|
if (parameterListElement != null) { |
||||||
|
StringBuilder pList = new StringBuilder(); |
||||||
|
|
||||||
|
var parameters = filtered |
||||||
|
.Select(p => new ParameterDeclaration(refactoringContext.CreateShortType(p.Type), p.ParameterName)) |
||||||
|
.ToList(); |
||||||
|
|
||||||
|
using (StringWriter textWriter = new StringWriter(pList)) { |
||||||
|
// Output parameter list as string
|
||||||
|
var formattingOptions = FormattingOptionsFactory.CreateMono(); |
||||||
|
CSharpOutputVisitor outputVisitor = new CSharpOutputVisitor(textWriter, formattingOptions); |
||||||
|
for (int i = 0; i < parameters.Count; i++) { |
||||||
|
if (i > 0) |
||||||
|
textWriter.Write(","); |
||||||
|
outputVisitor.VisitParameterDeclaration(parameters[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
parameterListElement.Text = pList.ToString(); |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
void UpClick(object sender, System.Windows.RoutedEventArgs e) |
||||||
|
{ |
||||||
|
int selection = varList.SelectedIndex; |
||||||
|
|
||||||
|
if (selection <= 0) |
||||||
|
return; |
||||||
|
|
||||||
|
var curItem = parameterList.First(p => p.Index == selection); |
||||||
|
var exchangeItem = parameterList.First(p => p.Index == selection - 1); |
||||||
|
|
||||||
|
curItem.Index = selection - 1; |
||||||
|
exchangeItem.Index = selection; |
||||||
|
|
||||||
|
varList.ItemsSource = parameterList.OrderBy(p => p.Index); |
||||||
|
varList.SelectedIndex = selection - 1; |
||||||
|
} |
||||||
|
|
||||||
|
void DownClick(object sender, System.Windows.RoutedEventArgs e) |
||||||
|
{ |
||||||
|
int selection = varList.SelectedIndex; |
||||||
|
|
||||||
|
if (selection < 0 || selection >= parameterList.Count - 1) |
||||||
|
return; |
||||||
|
|
||||||
|
var curItem = parameterList.First(p => p.Index == selection); |
||||||
|
var exchangeItem = parameterList.First(p => p.Index == selection + 1); |
||||||
|
|
||||||
|
curItem.Index = selection + 1; |
||||||
|
exchangeItem.Index = selection; |
||||||
|
|
||||||
|
varList.ItemsSource = parameterList.OrderBy(p => p.Index); |
||||||
|
varList.SelectedIndex = selection + 1; |
||||||
|
} |
||||||
|
|
||||||
|
protected override void OnKeyDown(KeyEventArgs e) |
||||||
|
{ |
||||||
|
Key? downAccessKey = GetAccessKeyFromButton(moveDown); |
||||||
|
Key? upAccessKey = GetAccessKeyFromButton(moveUp); |
||||||
|
Key? allAccessKey = GetAccessKeyFromButton(selectAll); |
||||||
|
|
||||||
|
if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt && allAccessKey == e.SystemKey) { |
||||||
|
if (AllSelected) |
||||||
|
varList.UnselectAll(); |
||||||
|
else |
||||||
|
varList.SelectAll(); |
||||||
|
e.Handled = true; |
||||||
|
} |
||||||
|
if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt && upAccessKey == e.SystemKey) { |
||||||
|
UpClick(this, null); |
||||||
|
e.Handled = true; |
||||||
|
} |
||||||
|
if ((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt && downAccessKey == e.SystemKey) { |
||||||
|
DownClick(this, null); |
||||||
|
e.Handled = true; |
||||||
|
} |
||||||
|
|
||||||
|
base.OnKeyDown(e); |
||||||
|
} |
||||||
|
|
||||||
|
protected override void FocusFirstElement() |
||||||
|
{ |
||||||
|
Dispatcher.BeginInvoke((Action)TryFocusAndSelectItem, DispatcherPriority.Background); |
||||||
|
} |
||||||
|
|
||||||
|
void TryFocusAndSelectItem() |
||||||
|
{ |
||||||
|
if (!parameterList.Any()) |
||||||
|
return; |
||||||
|
|
||||||
|
object ctorParamWrapper = varList.Items.GetItemAt(0); |
||||||
|
if (ctorParamWrapper != null) { |
||||||
|
ListBoxItem item = (ListBoxItem)varList.ItemContainerGenerator.ContainerFromItem(ctorParamWrapper); |
||||||
|
item.Focus(); |
||||||
|
|
||||||
|
varList.ScrollIntoView(item); |
||||||
|
varList.SelectedItem = item; |
||||||
|
Keyboard.Focus(item); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected override void OnInsertionCompleted() |
||||||
|
{ |
||||||
|
base.OnInsertionCompleted(); |
||||||
|
|
||||||
|
Dispatcher.BeginInvoke( |
||||||
|
DispatcherPriority.Background, |
||||||
|
(Action)( |
||||||
|
() => { |
||||||
|
if (!parameterList.Any()) |
||||||
|
insertionContext.Deactivate(null); |
||||||
|
else { |
||||||
|
insertionEndAnchor = editor.Document.CreateAnchor(anchor.Offset); |
||||||
|
insertionEndAnchor.MovementType = AnchorMovementType.AfterInsertion; |
||||||
|
} |
||||||
|
} |
||||||
|
) |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
void SelectAllChecked(object sender, System.Windows.RoutedEventArgs e) |
||||||
|
{ |
||||||
|
this.varList.SelectAll(); |
||||||
|
} |
||||||
|
|
||||||
|
void SelectAllUnchecked(object sender, System.Windows.RoutedEventArgs e) |
||||||
|
{ |
||||||
|
this.varList.UnselectAll(); |
||||||
|
} |
||||||
|
|
||||||
|
bool AllSelected { |
||||||
|
get { return varList.SelectedItems.Count == varList.Items.Count; } |
||||||
|
} |
||||||
|
|
||||||
|
protected override void CancelButtonClick(object sender, System.Windows.RoutedEventArgs e) |
||||||
|
{ |
||||||
|
base.CancelButtonClick(sender, e); |
||||||
|
|
||||||
|
editor.Caret.Offset = anchor.Offset; |
||||||
|
} |
||||||
|
|
||||||
|
protected override void OKButtonClick(object sender, System.Windows.RoutedEventArgs e) |
||||||
|
{ |
||||||
|
base.OKButtonClick(sender, e); |
||||||
|
|
||||||
|
editor.Caret.Offset = insertionEndAnchor.Offset; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
[ValueConversion(typeof(int), typeof(bool))] |
||||||
|
public class IntToBoolConverter : IValueConverter |
||||||
|
{ |
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) |
||||||
|
{ |
||||||
|
return ((int)value) != -1; |
||||||
|
} |
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) |
||||||
|
{ |
||||||
|
return ((bool)value) ? 0 : -1; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,93 @@ |
|||||||
|
// 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.Threading; |
||||||
|
|
||||||
|
using ICSharpCode.AvalonEdit.Snippets; |
||||||
|
using ICSharpCode.NRefactory; |
||||||
|
using ICSharpCode.NRefactory.Editor; |
||||||
|
using ICSharpCode.NRefactory.TypeSystem; |
||||||
|
using ICSharpCode.SharpDevelop; |
||||||
|
using ICSharpCode.SharpDevelop.Parser; |
||||||
|
using CSharpBinding.Refactoring; |
||||||
|
using ICSharpCode.SharpDevelop.Editor; |
||||||
|
|
||||||
|
namespace CSharpBinding.Refactoring |
||||||
|
{ |
||||||
|
public class InsertCtorSnippetRefactoring : ISnippetElementProvider |
||||||
|
{ |
||||||
|
public SnippetElement GetElement(SnippetInfo snippetInfo) |
||||||
|
{ |
||||||
|
if ("refactoring:ctor".Equals(snippetInfo.Tag, StringComparison.OrdinalIgnoreCase)) |
||||||
|
return new InlineRefactorSnippetElement(CreateDialog, "{" + snippetInfo.Tag + "}"); |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
InsertCtorDialog 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 = SD.ParserService.GetCachedParseInformation(textEditor.FileName); |
||||||
|
|
||||||
|
if (parseInfo == null) |
||||||
|
return null; |
||||||
|
|
||||||
|
// cannot use insertion position at this point, because it might not be
|
||||||
|
// valid, because we are still generating the elements.
|
||||||
|
// DOM is not updated
|
||||||
|
TextLocation loc = context.Document.GetLocation(context.StartPosition); |
||||||
|
|
||||||
|
IUnresolvedTypeDefinition current = parseInfo.UnresolvedFile.GetInnermostTypeDefinition(loc); |
||||||
|
if (current == null) |
||||||
|
return null; |
||||||
|
|
||||||
|
var refactoringContext = SDRefactoringContext.Create(textEditor, CancellationToken.None); |
||||||
|
var resolvedCurrent = current.Resolve(refactoringContext.Resolver.TypeResolveContext); |
||||||
|
|
||||||
|
List<PropertyOrFieldWrapper> parameters = CreateCtorParams(current, resolvedCurrent).ToList(); |
||||||
|
|
||||||
|
if (!parameters.Any()) |
||||||
|
return null; |
||||||
|
|
||||||
|
ITextAnchor anchor = textEditor.Document.CreateAnchor(context.InsertionPosition); |
||||||
|
anchor.MovementType = AnchorMovementType.BeforeInsertion; |
||||||
|
|
||||||
|
InsertCtorDialog dialog = new InsertCtorDialog(context, textEditor, anchor, current, parameters); |
||||||
|
|
||||||
|
dialog.Element = uiService.CreateInlineUIElement(anchor, dialog); |
||||||
|
|
||||||
|
return dialog; |
||||||
|
} |
||||||
|
|
||||||
|
IEnumerable<PropertyOrFieldWrapper> CreateCtorParams(IUnresolvedTypeDefinition sourceType, IType resolvedSourceType) |
||||||
|
{ |
||||||
|
int i = 0; |
||||||
|
|
||||||
|
foreach (var f in resolvedSourceType.GetFields().Where(field => !field.IsConst |
||||||
|
&& field.IsStatic == sourceType.IsStatic |
||||||
|
&& field.ReturnType != null)) { |
||||||
|
yield return new PropertyOrFieldWrapper(f) { Index = i }; |
||||||
|
i++; |
||||||
|
} |
||||||
|
|
||||||
|
foreach (var p in resolvedSourceType.GetProperties().Where(prop => prop.CanSet && !prop.IsIndexer |
||||||
|
&& prop.IsStatic == sourceType.IsStatic |
||||||
|
&& prop.ReturnType != null)) { |
||||||
|
yield return new PropertyOrFieldWrapper(p) { Index = i }; |
||||||
|
i++; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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 @@ |
|||||||
|
// 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 @@ |
|||||||
|
// 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 @@ |
|||||||
|
<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 @@ |
|||||||
|
// 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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,121 @@ |
|||||||
|
// 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 PropertyOrFieldWrapper : INotifyPropertyChanged |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Underlying member. Always IField or IProperty.
|
||||||
|
/// </summary>
|
||||||
|
public readonly IMember Member; |
||||||
|
private readonly string text; |
||||||
|
|
||||||
|
public PropertyOrFieldWrapper(IMember member) |
||||||
|
{ |
||||||
|
if (member == null) |
||||||
|
throw new ArgumentNullException("member"); |
||||||
|
if (!(member is IField || member is IProperty)) |
||||||
|
throw new ArgumentException("member must be IField or IProperty"); |
||||||
|
|
||||||
|
this.Member = member; |
||||||
|
IAmbience ambience = AmbienceService.GetCurrentAmbience(); |
||||||
|
ambience.ConversionFlags |= |
||||||
|
ConversionFlags.ShowReturnType |
||||||
|
| ConversionFlags.ShowModifiers |
||||||
|
| ConversionFlags.ShowAccessibility; |
||||||
|
this.text = ambience.ConvertEntity(member); |
||||||
|
} |
||||||
|
|
||||||
|
public string MemberName { |
||||||
|
get { return Member.Name; } |
||||||
|
} |
||||||
|
|
||||||
|
string parameterName; |
||||||
|
public string ParameterName { |
||||||
|
get { |
||||||
|
if (parameterName == null) |
||||||
|
parameterName = ToParameterName(this.MemberName); |
||||||
|
return parameterName; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public IType Type { |
||||||
|
get { return Member.ReturnType; } |
||||||
|
} |
||||||
|
|
||||||
|
public string Text { |
||||||
|
get { return text; } |
||||||
|
} |
||||||
|
|
||||||
|
public int Index { get; set; } |
||||||
|
|
||||||
|
public bool IsNullable { |
||||||
|
get { |
||||||
|
// true = reference, null = generic or unknown
|
||||||
|
return Member.ReturnType.IsReferenceType != false |
||||||
|
|| (Member.ReturnType.FullName == "System.Nullable"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public bool HasRange { |
||||||
|
get { |
||||||
|
return IsTypeWithRange(Member.ReturnType) || |
||||||
|
(Member.ReturnType.FullName == "System.Nullable") |
||||||
|
&& IsTypeWithRange(Member.ReturnType.TypeArguments.First()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool addCheckForNull; |
||||||
|
public bool AddCheckForNull { |
||||||
|
get { return addCheckForNull; } |
||||||
|
set { |
||||||
|
addCheckForNull = value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool addRangeCheck; |
||||||
|
public bool AddRangeCheck { |
||||||
|
get { return addRangeCheck; } |
||||||
|
set { |
||||||
|
addRangeCheck = value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool IsTypeWithRange(IType type) |
||||||
|
{ |
||||||
|
string crtType = type.FullName; |
||||||
|
return crtType == "System.Int32" || |
||||||
|
crtType == "System.Int16" || |
||||||
|
crtType == "System.Int64" || |
||||||
|
crtType == "System.Single" || |
||||||
|
crtType == "System.Double" || |
||||||
|
crtType == "System.UInt16" || |
||||||
|
crtType == "System.UInt32" || |
||||||
|
crtType == "System.UInt64"; |
||||||
|
} |
||||||
|
|
||||||
|
static string ToParameterName(string memberName) |
||||||
|
{ |
||||||
|
if (string.IsNullOrEmpty(memberName)) |
||||||
|
return memberName; |
||||||
|
return char.ToLower(memberName[0]) + memberName.Substring(1); |
||||||
|
} |
||||||
|
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged; |
||||||
|
|
||||||
|
protected virtual void OnPropertyChanged(string property) |
||||||
|
{ |
||||||
|
if (PropertyChanged != null) { |
||||||
|
PropertyChanged(this, new PropertyChangedEventArgs(property)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue