diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin
index 4824542069..47ffeea755 100644
--- a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.addin
@@ -155,6 +155,17 @@
class="CSharpBinding.Refactoring.SearchForIssuesCommand"/>
+
+
+
+
+
+
+
+
+
+
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj
index ebda626378..00027765d5 100644
--- a/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/CSharpBinding.csproj
@@ -67,16 +67,25 @@
+
+
+
+
+
+
+ InsertCtorDialog.xaml
+
+
IssueOptions.xaml
@@ -86,6 +95,10 @@
+
+ OverrideToStringMethodDialog.xaml
+
+
@@ -95,7 +108,6 @@
Code
-
@@ -181,8 +193,10 @@
+
+
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CodeManipulation.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CodeManipulation.cs
index b33ee33e57..2213a738bb 100644
--- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CodeManipulation.cs
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/CodeManipulation.cs
@@ -3,16 +3,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
-/*
+
+
+using System.Windows.Input;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.Core;
using ICSharpCode.NRefactory;
-using ICSharpCode.NRefactory.Ast;
+using ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.Editor;
+using ICSharpCode.NRefactory.PatternMatching;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor;
-using ICSharpCode.SharpDevelop.Refactoring;
-using Ast = ICSharpCode.NRefactory.Ast;
namespace CSharpBinding
{
@@ -100,8 +102,8 @@ namespace CSharpBinding
class Selection
{
- public Location Start { get; set; }
- public Location End { get; set; }
+ public TextLocation Start { get; set; }
+ public TextLocation End { get; set; }
}
void CodeManipulationSelectionChanged(object sender, EventArgs e)
@@ -134,46 +136,110 @@ namespace CSharpBinding
// move selection - find outermost node in selection, swap selection with closest child of its parent to the selection
void MoveStatement(ITextEditor editor, MoveStatementDirection direction)
{
- IList commentsBlankLines;
+ IList commentsBlankLines;
var parsedCU = ParseDocument(editor, out commentsBlankLines);
if (parsedCU == null) return;
// Find the Statement or Definition containing caret -> Extend selection to Statement or Definition
- INode currentStatement;
+ AstNode currentStatement;
Selection statementSelection = ExtendSelection(editor, parsedCU, commentsBlankLines, out currentStatement, new Type[] {
typeof(Statement),
- typeof(MemberNode),
- typeof(FieldDeclaration),
- typeof(ConstructorDeclaration),
- typeof(DestructorDeclaration) });
+ typeof(EntityDeclaration) });
if (currentStatement == null)
return;
statementSelection = TryExtendSelectionToComments(editor.Document, statementSelection, commentsBlankLines);
// Take its sibling
if (currentStatement.Parent == null)
return;
- var siblings = currentStatement.Parent.Children;
+ var siblings = currentStatement.Parent.Children.Where(c => (c.Role.GetType() == currentStatement.Role.GetType())).ToList();
int currentStatementPos = siblings.IndexOf(currentStatement);
+
int swapIndex = currentStatementPos + (direction == MoveStatementDirection.Down ? 1 : -1);
if (swapIndex < 0 || swapIndex >= siblings.Count)
return;
- INode swapSibling = siblings[swapIndex];
+ AstNode swapSibling = siblings[swapIndex];
+
+ // Expand selection to full line, if there is more than one statement in it, and set swapped sibling appropriately
+ AstNode tempSwapSibling = currentStatement;
+ int tempSwapIndex = currentStatementPos;
+ while ((tempSwapIndex == currentStatementPos) || (tempSwapSibling.StartLocation.Line == statementSelection.End.Line)) {
+ tempSwapIndex++;
+ if (tempSwapSibling.EndLocation > statementSelection.End)
+ statementSelection.End = tempSwapSibling.EndLocation;
+ if (tempSwapIndex >= siblings.Count) {
+ if (direction == MoveStatementDirection.Down)
+ return;
+ else
+ break;
+ }
+ tempSwapSibling = siblings[tempSwapIndex];
+ if (direction == MoveStatementDirection.Down) {
+ swapSibling = tempSwapSibling;
+ swapIndex = tempSwapIndex;
+ }
+ }
+ tempSwapSibling = currentStatement;
+ tempSwapIndex = currentStatementPos;
+ while ((tempSwapIndex == currentStatementPos) || (tempSwapSibling.EndLocation.Line == statementSelection.Start.Line)) {
+ tempSwapIndex--;
+ if (tempSwapSibling.StartLocation < statementSelection.Start)
+ statementSelection.Start = tempSwapSibling.StartLocation;
+ if (tempSwapIndex < 0) {
+ if (direction == MoveStatementDirection.Up)
+ return;
+ else
+ break;
+ }
+ tempSwapSibling = siblings[tempSwapIndex];
+ if (direction == MoveStatementDirection.Up) {
+ swapSibling = tempSwapSibling;
+ swapIndex = tempSwapIndex;
+ }
+ }
+
+ Selection swapSiblingSelection = ExtendSelectionToComments(editor.Document, swapSibling.StartLocation, swapSibling.EndLocation, commentsBlankLines);
+ if (swapSiblingSelection == null)
+ swapSiblingSelection = new Selection() { Start = swapSibling.StartLocation, End = swapSibling.EndLocation };
+
+ // Expand swapSiblingSelection, too, if there are > 1 statements in line
+ tempSwapSibling = swapSibling;
+ tempSwapIndex = swapIndex;
+ if (direction == MoveStatementDirection.Down) {
+ while ((tempSwapIndex == swapIndex) || (tempSwapSibling.StartLocation.Line == swapSiblingSelection.End.Line)) {
+ tempSwapIndex++;
+ if (tempSwapSibling.EndLocation > swapSiblingSelection.End)
+ swapSiblingSelection.End = tempSwapSibling.EndLocation;
+ if (tempSwapIndex >= siblings.Count)
+ break;
+ tempSwapSibling = siblings[tempSwapIndex];
+ }
+ } else if (direction == MoveStatementDirection.Up) {
+ while ((tempSwapIndex == swapIndex) || (tempSwapSibling.EndLocation.Line == swapSiblingSelection.Start.Line)) {
+ tempSwapIndex--;
+ if (tempSwapSibling.StartLocation < swapSiblingSelection.Start)
+ swapSiblingSelection.Start = tempSwapSibling.StartLocation;
+ if (tempSwapIndex < 0)
+ break;
+ tempSwapSibling = siblings[tempSwapIndex];
+ }
+ }
+
// Swap them
string currentNodeText = editor.Document.GetText(statementSelection.Start, statementSelection.End);
- SwapText(editor.Document, statementSelection.Start, statementSelection.End, swapSibling.StartLocation, swapSibling.EndLocation);
+ SwapText(editor.Document, statementSelection.Start, statementSelection.End, swapSiblingSelection.Start, swapSiblingSelection.End);
// Move caret to the start of moved statement
- Location upperLocation = new Location[] {statementSelection.Start, swapSibling.StartLocation}.Min();
+ TextLocation upperLocation = new TextLocation[] {statementSelection.Start, swapSiblingSelection.Start}.Min();
if (direction == MoveStatementDirection.Up)
- editor.Caret.Position = upperLocation;
+ editor.Caret.Location = upperLocation;
else {
// look where current statement ended up because it is hard to calculate it correctly
- int currentMovedOffset = editor.Document.Text.IndexOf(currentNodeText, editor.Document.PositionToOffset(upperLocation));
+ int currentMovedOffset = editor.Document.Text.IndexOf(currentNodeText, editor.Document.GetOffset(upperLocation));
editor.Caret.Offset = currentMovedOffset;
}
}
- Selection TryExtendSelectionToComments(IDocument document, Selection selection, IList commentsBlankLines)
+ Selection TryExtendSelectionToComments(IDocument document, Selection selection, IList commentsBlankLines)
{
var extendedToComments = ExtendSelectionToComments(document, selection, commentsBlankLines);
if (extendedToComments != null)
@@ -183,13 +249,13 @@ namespace CSharpBinding
public void ExtendSelection()
{
- INode selectedNode = null;
- IList commentsBlankLines;
+ AstNode selectedNode = null;
+ IList commentsBlankLines;
var parsedCU = ParseDocument(editor, out commentsBlankLines);
if (parsedCU == null) return;
ISegment oldSelection = new TextSegment { StartOffset = editor.SelectionStart, Length = editor.SelectionLength };
- Selection extendedSelection = ExtendSelection(editor, parsedCU, commentsBlankLines, out selectedNode, new Type[] { typeof(INode) }); // any node type
+ Selection extendedSelection = ExtendSelection(editor, parsedCU, commentsBlankLines, out selectedNode, new Type[] { typeof(AstNode) }); // any node type
SelectText(extendedSelection, editor);
@@ -198,7 +264,7 @@ namespace CSharpBinding
LoggingService.Debug("pushed: " + oldSelection);
} else {
LoggingService.Debug("not accepted: " + oldSelection);
- }
+ }
}
public void ShrinkSelection()
@@ -211,14 +277,14 @@ namespace CSharpBinding
}
// could work to extend selection to set of adjacent statements separated by blank lines
- Selection ExtendSelection(ITextEditor editor, SyntaxTree parsedCU, IList commentsBlankLines, out INode selectedResultNode, Type[] interestingNodeTypes)
+ Selection ExtendSelection(ITextEditor editor, SyntaxTree parsedCU, IList commentsBlankLines, out AstNode selectedResultNode, Type[] interestingNodeTypes)
{
selectedResultNode = null;
- var selectionStart = editor.Document.OffsetToPosition(editor.SelectionStart);
- var selectionEnd = editor.Document.OffsetToPosition(editor.SelectionStart + editor.SelectionLength);
+ var selectionStart = editor.Document.GetLocation(editor.SelectionStart);
+ var selectionEnd = editor.Document.GetLocation(editor.SelectionStart + editor.SelectionLength);
- Ast.INode currentNode = parsedCU.Children.Select(n => EditorContext.FindInnermostNodeContainingSelection(n, selectionStart, selectionEnd)).FirstOrDefault(n => n != null);
+ AstNode currentNode = parsedCU.GetNodeContaining(selectionStart, selectionEnd);
if (currentNode == null) return null;
if (!IsNodeTypeInteresting(currentNode, interestingNodeTypes)) {
// ignore uninteresting nodes in the AST
@@ -258,7 +324,7 @@ namespace CSharpBinding
// if the extended selection would contain blank lines, extend the selection only to the blank lines/comments on both sides (use siblings)
// if the selection contains blank lines or comments on both sides, dont do this
- var blankLines = commentsBlankLines.Where(s => s is BlankLine).Cast().ToList();
+ // var blankLines = commentsBlankLines.Where(s => s is BlankLine).Cast().ToList();
//if (SelectionContainsBlankLines(extendedSelectionStart, extendedLocationEnd, blankLines)) {
if (false) { // blank line separators - implement later
@@ -276,7 +342,7 @@ namespace CSharpBinding
return new Selection { Start = selectionStart, End = selectionEnd };
}
- Selection ExtendSelectionToComments(IDocument document, Selection selection, IList commentsBlankLines)
+ Selection ExtendSelectionToComments(IDocument document, Selection selection, IList commentsBlankLines)
{
if (selection == null)
throw new ArgumentNullException("selection");
@@ -286,7 +352,7 @@ namespace CSharpBinding
///
/// If there is a comment block immediately before selection, or behind selection on the same line, add it to selection.
///
- Selection ExtendSelectionToComments(IDocument document, Location selectionStart, Location selectionEnd, IList commentsBlankLines)
+ Selection ExtendSelectionToComments(IDocument document, TextLocation selectionStart, TextLocation selectionEnd, IList commentsBlankLines)
{
var comments = commentsBlankLines.Where(s => s is Comment).Cast();
// add "var i = 5; // comments" comments
@@ -302,38 +368,37 @@ namespace CSharpBinding
///
/// If there is a comment block behind selection on the same line ("var i = 5; // comment"), add it to selection.
///
- Selection ExtendSelectionToEndOfLineComments(IDocument document, Location selectionStart, Location selectionEnd, IEnumerable commentsBlankLines)
+ Selection ExtendSelectionToEndOfLineComments(IDocument document, TextLocation selectionStart, TextLocation selectionEnd, IEnumerable commentsBlankLines)
{
- var lineComment = commentsBlankLines.FirstOrDefault(c => c.StartPosition.Line == selectionEnd.Line && c.StartPosition >= selectionEnd);
+ var lineComment = commentsBlankLines.FirstOrDefault(c => c.StartLocation.Line == selectionEnd.Line && c.StartLocation >= selectionEnd);
if (lineComment == null) {
return null;
}
- bool isWholeLineSelected = IsWhitespaceBetween(document, new Location(1, selectionStart.Line), selectionStart);
- if (!isWholeLineSelected) {
- // whole line must be selected before we add the comment
- return null;
- }
- // fix the end of comment set to next line incorrectly by the parser
- int fixEndPos = document.PositionToOffset(lineComment.EndPosition) - 1;
- return new Selection { Start = selectionStart, End = document.OffsetToPosition(fixEndPos) };
+// bool isWholeLineSelected = IsWhitespaceBetween(document, new TextLocation(selectionStart.Line, 1), selectionStart);
+// if (!isWholeLineSelected) {
+// // whole line must be selected before we add the comment
+// return null;
+// }
+ int endPos = document.GetOffset(lineComment.EndLocation);
+ return new Selection { Start = selectionStart, End = document.GetLocation(endPos) };
}
///
/// If there is a comment block immediately before selection, add it to selection.
///
- Selection ExtendSelectionToSeparateComments(IDocument document, Location selectionStart, Location selectionEnd, IEnumerable commentsBlankLines)
+ Selection ExtendSelectionToSeparateComments(IDocument document, TextLocation selectionStart, TextLocation selectionEnd, IEnumerable commentsBlankLines)
{
- var comments = commentsBlankLines.Where(c => c.CommentStartsLine).ToList();
- int commentIndex = comments.FindIndex(c => c.EndPosition <= selectionStart && IsWhitespaceBetween(document, c.EndPosition, selectionStart));
+ var comments = commentsBlankLines.Where(c => (c is Comment) && ((Comment) c).StartsLine).ToList();
+ int commentIndex = comments.FindIndex(c => c.EndLocation <= selectionStart && IsWhitespaceBetween(document, c.EndLocation, selectionStart));
if (commentIndex < 0) {
return null;
}
var extendedSelection = new Selection { Start = selectionStart, End = selectionEnd };
// start at the selection and keep adding comments upwards as long as they are separated only by whitespace
- while (commentIndex >= 0 && IsWhitespaceBetween(document, comments[commentIndex].EndPosition, extendedSelection.Start)) {
+ while (commentIndex >= 0 && IsWhitespaceBetween(document, comments[commentIndex].EndLocation, extendedSelection.Start)) {
var comment = comments[commentIndex];
// Include the "//, /*, ///" since they are not included by the parser
- extendedSelection.Start = ExtendLeft(comment.StartPosition, document, "///", "/*", "//") ;
+ extendedSelection.Start = ExtendLeft(comment.StartLocation, document, "///", "/*", "//") ;
commentIndex--;
}
return extendedSelection;
@@ -342,7 +407,7 @@ namespace CSharpBinding
///
/// Searches for parent of interesting type. Skips uninteresting parents.
///
- INode GetInterestingParent(INode node, Type[] interestingNodeTypes)
+ AstNode GetInterestingParent(AstNode node, Type[] interestingNodeTypes)
{
var parent = node.Parent;
while(parent != null) {
@@ -359,15 +424,15 @@ namespace CSharpBinding
return interestingNodeTypes.Any(interestingType => interestingType.IsAssignableFrom(nodeType) || (nodeType == interestingType));
}
- bool SelectionContainsBlankLines(Location selectionStart, Location selectionEnd, List blankLines)
+ bool SelectionContainsBlankLines(TextLocation selectionStart, TextLocation selectionEnd, List blankLines)
{
- return blankLines.Exists(b => b.StartPosition >= selectionStart && b.EndPosition <= selectionEnd);
+ return blankLines.Exists(b => b.StartLocation >= selectionStart && b.EndLocation <= selectionEnd);
}
- bool IsWhitespaceBetween(IDocument document, Location startPos, Location endPos)
+ bool IsWhitespaceBetween(IDocument document, TextLocation startPos, TextLocation endPos)
{
- int startOffset = document.PositionToOffset(startPos);
- int endOffset = document.PositionToOffset(endPos);
+ int startOffset = document.GetOffset(startPos);
+ int endOffset = document.GetOffset(endPos);
if (startOffset > endOffset) {
throw new ArgumentException("Invalid range for (startPos, endPos)");
}
@@ -378,14 +443,14 @@ namespace CSharpBinding
/// If the text at startPos is preceded by any of the prefixes, moves the start backwards to include one prefix
/// (the rightmost one).
///
- Location ExtendLeft(Location startPos, IDocument document, params String[] prefixes)
+ TextLocation ExtendLeft(TextLocation startPos, IDocument document, params String[] prefixes)
{
- int startOffset = document.PositionToOffset(startPos);
+ int startOffset = document.GetOffset(startPos);
foreach (string prefix in prefixes) {
if (startOffset < prefix.Length) continue;
string realPrefix = document.GetText(startOffset - prefix.Length, prefix.Length);
if (realPrefix == prefix) {
- return document.OffsetToPosition(startOffset - prefix.Length);
+ return document.GetLocation(startOffset - prefix.Length);
}
}
// no prefixes -> do not extend
@@ -393,42 +458,34 @@ namespace CSharpBinding
}
// could depend just on IDocument
- SyntaxTree ParseDocument(ITextEditor editor, out IList parsedSpecials)
+ SyntaxTree ParseDocument(ITextEditor editor, out IList parsedSpecials)
{
parsedSpecials = null;
- var editorLang = EditorContext.GetEditorLanguage(editor);
- if (editorLang == null) return null;
- var parser = ParserFactory.CreateParser(editorLang.Value, editor.Document.CreateReader());
+ CompilerSettings compilerSettings = new CompilerSettings();
+ var parser = new CSharpParser();
if (parser == null) return null;
- parser.ParseMethodBodies = true;
- parser.Lexer.SkipAllComments = false;
- parser.Parse();
- var parsedCU = parser.SyntaxTree;
- if (parsedCU == null) return null;
- foreach (var node in parsedCU.Children) {
- // fix StartLocation / EndLocation
- node.AcceptVisitor(new ICSharpCode.NRefactory.Visitors.SetRegionInclusionVisitor(), null);
- }
- parsedSpecials = parser.Lexer.SpecialTracker.CurrentSpecials;
- return parsedCU;
+ var syntaxTree = parser.Parse(editor.Document.CreateReader());
+ if (syntaxTree == null) return null;
+ parsedSpecials = new List(syntaxTree.Descendants.OfType());
+ return syntaxTree;
}
///
/// Swaps 2 ranges of text in a document.
///
- void SwapText(IDocument document, Location start1, Location end1, Location start2, Location end2)
+ void SwapText(IDocument document, TextLocation start1, TextLocation end1, TextLocation start2, TextLocation end2)
{
if (start1 > start2) {
- Location sw;
+ TextLocation sw;
sw = start1; start1 = start2; start2 = sw;
sw = end1; end1 = end2; end2 = sw;
}
if (end1 >= start2)
throw new InvalidOperationException("Cannot swap overlaping segments");
- int offset1 = document.PositionToOffset(start1);
- int len1 = document.PositionToOffset(end1) - offset1;
- int offset2 = document.PositionToOffset(start2);
- int len2 = document.PositionToOffset(end2) - offset2;
+ int offset1 = document.GetOffset(start1);
+ int len1 = document.GetOffset(end1) - offset1;
+ int offset2 = document.GetOffset(start2);
+ int len2 = document.GetOffset(end2) - offset2;
string text1 = document.GetText(offset1, len1);
string text2 = document.GetText(offset2, len2);
@@ -445,8 +502,8 @@ namespace CSharpBinding
return;
int startOffset, endOffset;
try {
- startOffset = editor.Document.PositionToOffset(selection.Start);
- endOffset = editor.Document.PositionToOffset(selection.End);
+ startOffset = editor.Document.GetOffset(selection.Start);
+ endOffset = editor.Document.GetOffset(selection.End);
} catch (ArgumentOutOfRangeException) {
return;
}
@@ -454,4 +511,3 @@ namespace CSharpBinding
}
}
}
-*/
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs
index a31ab8adde..1c1e8ff6f5 100644
--- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/CSharpCompletionDataFactory.cs
@@ -95,7 +95,10 @@ namespace CSharpBinding.Completion
ICompletionData ICompletionDataFactory.CreateNewOverrideCompletionData(int declarationBegin, IUnresolvedTypeDefinition type, IMember m)
{
- return new OverrideCompletionData(declarationBegin, m, contextAtCaret);
+ if ((m.EntityType == EntityType.Method) && (m.Name == "ToString"))
+ return new OverrideToStringCompletionData(declarationBegin, m, contextAtCaret);
+ else
+ return new OverrideCompletionData(declarationBegin, m, contextAtCaret);
}
ICompletionData ICompletionDataFactory.CreateNewPartialCompletionData(int declarationBegin, IUnresolvedTypeDefinition type, IUnresolvedMember m)
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideCompletionData.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideCompletionData.cs
index c955563ee1..286c76eaab 100644
--- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideCompletionData.cs
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideCompletionData.cs
@@ -24,8 +24,8 @@ namespace CSharpBinding.Completion
///
class OverrideCompletionData : EntityCompletionData
{
- readonly int declarationBegin;
- readonly CSharpTypeResolveContext contextAtCaret;
+ protected readonly int declarationBegin;
+ protected readonly CSharpTypeResolveContext contextAtCaret;
public OverrideCompletionData(int declarationBegin, IMember m, CSharpTypeResolveContext contextAtCaret)
: base(m)
@@ -43,6 +43,7 @@ namespace CSharpBinding.Completion
base.Complete(context);
return;
}
+
TypeSystemAstBuilder b = new TypeSystemAstBuilder(new CSharpResolver(contextAtCaret));
b.ShowTypeParameterConstraints = false;
b.GenerateBody = true;
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideToStringCompletionData.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideToStringCompletionData.cs
new file mode 100644
index 0000000000..911ea59cfe
--- /dev/null
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Completion/OverrideToStringCompletionData.cs
@@ -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
+{
+ ///
+ /// Item for 'override' completion of "ToString()" methods.
+ ///
+ 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().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 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++;
+ }
+ }
+ }
+}
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/AbstractInlineRefactorDialog.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/AbstractInlineRefactorDialog.cs
new file mode 100644
index 0000000000..6140d3af60
--- /dev/null
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/AbstractInlineRefactorDialog.cs
@@ -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 optionBindings;
+
+ public void AddBinding(OptionBinding binding)
+ {
+ if (optionBindings == null)
+ optionBindings = new List();
+
+ 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());
+ }
+ }
+}
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreateProperties.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreateProperties.cs
new file mode 100644
index 0000000000..91f9b03df0
--- /dev/null
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreateProperties.cs
@@ -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 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 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++;
+ }
+ }
+ }
+}
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesCommand.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesCommand.cs
new file mode 100644
index 0000000000..c3aa3e8423
--- /dev/null
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesCommand.cs
@@ -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)));
+ }
+ }
+}
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesDialog.xaml b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesDialog.xaml
new file mode 100644
index 0000000000..cb0462c44d
--- /dev/null
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesDialog.xaml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesDialog.xaml.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesDialog.xaml.cs
new file mode 100644
index 0000000000..36a68cea11
--- /dev/null
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/CreatePropertiesDialog.xaml.cs
@@ -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
+{
+ ///
+ /// Interaction logic for CreatePropertiesDialog.xaml
+ ///
+ public partial class CreatePropertiesDialog : AbstractInlineRefactorDialog
+ {
+ IList fields;
+
+ public CreatePropertiesDialog(InsertionContext context, ITextEditor editor, ITextAnchor anchor, IClass current, IList 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();
+ 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 { 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 { new PrimitiveExpression(name) })
+ : (Expression)new PrimitiveExpression(name);
+ return new InvocationExpression(new IdentifierExpression("OnPropertyChanged"), new List { 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/FieldWrapper.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/FieldWrapper.cs
new file mode 100644
index 0000000000..95202b1d5c
--- /dev/null
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/FieldWrapper.cs
@@ -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
+ {
+ ///
+ /// Underlying member. Always IField or IProperty.
+ ///
+ 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));
+ }
+ }
+ }
+}
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/InlineRefactorSnippetElement.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/InlineRefactorSnippetElement.cs
new file mode 100644
index 0000000000..9bdf90d8b3
--- /dev/null
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/InlineRefactorSnippetElement.cs
@@ -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 createDialog;
+ string previewText;
+
+ public InlineRefactorSnippetElement(Func 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 } };
+ }
+ }
+}
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/InsertCtorDialog.xaml b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/InsertCtorDialog.xaml
new file mode 100644
index 0000000000..a89ebde120
--- /dev/null
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/InsertCtorDialog.xaml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/InsertCtorDialog.xaml.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/InsertCtorDialog.xaml.cs
new file mode 100644
index 0000000000..6ab730907e
--- /dev/null
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/InsertCtorDialog.xaml.cs
@@ -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
+{
+ ///
+ /// Interaction logic for InsertCtorDialog.xaml
+ ///
+ public partial class InsertCtorDialog : AbstractInlineRefactorDialog
+ {
+ IList parameterList;
+
+ public InsertCtorDialog(InsertionContext context, ITextEditor editor, ITextAnchor anchor, IUnresolvedTypeDefinition current, IList 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 filtered = this.varList.SelectedItems.OfType()
+ .OrderBy(p => p.Index)
+ .ToList();
+
+ var insertedConstructor = refactoringContext.GetNode();
+ 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() { 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() { 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() { 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()
+ .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;
+ }
+ }
+}
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/InsertCtorSnippetRefactoring.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/InsertCtorSnippetRefactoring.cs
new file mode 100644
index 0000000000..e7d203cae9
--- /dev/null
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/InsertCtorSnippetRefactoring.cs
@@ -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 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 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++;
+ }
+ }
+ }
+}
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/IssueManager.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/IssueManager.cs
index 5a2b42a80d..5230459148 100644
--- a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/IssueManager.cs
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/IssueManager.cs
@@ -51,6 +51,7 @@ namespace CSharpBinding.Refactoring
if (attributes.Length == 1) {
this.Attribute = (IssueDescriptionAttribute)attributes[0];
defaultSeverity = this.Attribute.Severity;
+ IsRedundancy = this.Attribute.Category == IssueCategories.Redundancies;
} else {
SD.Log.Warn("Issue provider without attribute: " + ProviderType);
}
@@ -59,6 +60,7 @@ namespace CSharpBinding.Refactoring
}
public Severity CurrentSeverity { get; set; }
+ public bool IsRedundancy { get; set; }
public IssueMarker MarkerType {
get { return Attribute != null ? Attribute.IssueMarker : IssueMarker.Underline; }
@@ -171,7 +173,8 @@ namespace CSharpBinding.Refactoring
Color color = GetColor(this.Severity);
color.A = 186;
marker.MarkerColor = color;
- marker.MarkerTypes = TextMarkerTypes.ScrollBarRightTriangle;
+ if (!Provider.IsRedundancy)
+ marker.MarkerTypes = TextMarkerTypes.ScrollBarRightTriangle;
switch (Provider.MarkerType) {
case IssueMarker.Underline:
marker.MarkerTypes |= TextMarkerTypes.SquigglyUnderline;
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsDialog.xaml b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsDialog.xaml
new file mode 100644
index 0000000000..90417f43aa
--- /dev/null
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsDialog.xaml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsDialog.xaml.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsDialog.xaml.cs
new file mode 100644
index 0000000000..acbadda73f
--- /dev/null
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsDialog.xaml.cs
@@ -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
+{
+ ///
+ /// Interaction logic for OverrideEqualsGetHashCodeMethodsDialog.xaml
+ ///
+ 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 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() { 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 equalsOverrides = CreateEqualsOverrides(currentClass);
+ MethodDeclaration defaultOverride = equalsOverrides.First();
+ equalsOverrides = equalsOverrides.Skip(1).ToList();
+
+ StringBuilder builder = new StringBuilder();
+
+ foreach (AbstractNode element in defaultOverride.Body.Children.OfType()) {
+ 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()) {
+ 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() { new IdentifierExpression("lhs"), new IdentifierExpression("rhs") }
+ ),
+ new ReturnStatement(new PrimitiveExpression(true))
+ ),
+ new IfElseStatement(
+ new BinaryOperatorExpression(
+ new InvocationExpression(
+ new IdentifierExpression("ReferenceEquals"),
+ new List() { new IdentifierExpression("lhs"), new PrimitiveExpression(null) }
+ ),
+ BinaryOperatorType.LogicalOr,
+ new InvocationExpression(
+ new IdentifierExpression("ReferenceEquals"),
+ new List() { 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() { 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 CreateEqualsOverrides(IClass currentClass)
+ {
+ List methods = new List();
+
+ 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 {
+ 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;
+ }
+ }
+}
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsRefactoring.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsRefactoring.cs
new file mode 100644
index 0000000000..3b77ad2b81
--- /dev/null
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideEqualsGetHashCodeMethodsRefactoring.cs
@@ -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().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)");
+ }
+ }
+}
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideToStringMethodDialog.xaml b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideToStringMethodDialog.xaml
new file mode 100644
index 0000000000..3eb80df4a9
--- /dev/null
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideToStringMethodDialog.xaml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideToStringMethodDialog.xaml.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideToStringMethodDialog.xaml.cs
new file mode 100644
index 0000000000..22ae595fc1
--- /dev/null
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/OverrideToStringMethodDialog.xaml.cs
@@ -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
+{
+ ///
+ /// Interaction logic for OverrideToStringMethodDialog.xaml
+ ///
+ public partial class OverrideToStringMethodDialog : AbstractInlineRefactorDialog
+ {
+ AstNode baseCallNode;
+ string insertedCode;
+ ITextEditor editor;
+
+ public OverrideToStringMethodDialog(InsertionContext context, ITextEditor editor, ITextAnchor startAnchor, ITextAnchor anchor, IList 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().Select(f2 => f2.MemberName).ToArray();
+ PrimitiveExpression formatString = new PrimitiveExpression(GenerateFormatString(currentClass, editor.Language.CodeGenerator, fields));
+ List param = new List() { 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);
+ }
+ }
+}
diff --git a/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/PropertyOrFieldWrapper.cs b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/PropertyOrFieldWrapper.cs
new file mode 100644
index 0000000000..7752a2f687
--- /dev/null
+++ b/src/AddIns/BackendBindings/CSharpBinding/Project/Src/Refactoring/PropertyOrFieldWrapper.cs
@@ -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
+ {
+ ///
+ /// Underlying member. Always IField or IProperty.
+ ///
+ 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));
+ }
+ }
+ }
+}
diff --git a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringContext.cs b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringContext.cs
index 578b5a6838..4270865424 100644
--- a/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringContext.cs
+++ b/src/Libraries/NRefactory/ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringContext.cs
@@ -87,6 +87,14 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring
return RootNode.GetNodeAt (Location);
}
+ public CSharpTypeResolveContext GetTypeResolveContext()
+ {
+ if (UnresolvedFile != null)
+ return UnresolvedFile.GetTypeResolveContext(Compilation, Location);
+ else
+ return null;
+ }
+
#region Text stuff
public virtual TextEditorOptions TextEditorOptions {
get {
diff --git a/src/Main/Base/Project/Dom/IModelCollection.cs b/src/Main/Base/Project/Dom/IModelCollection.cs
index f2dbbe33a8..85a8f6b60a 100644
--- a/src/Main/Base/Project/Dom/IModelCollection.cs
+++ b/src/Main/Base/Project/Dom/IModelCollection.cs
@@ -3,8 +3,6 @@
using System;
using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Collections.Specialized;
using System.Linq;
namespace ICSharpCode.SharpDevelop.Dom
diff --git a/src/Main/Base/Project/Src/Project/ProjectChangeWatcher.cs b/src/Main/Base/Project/Src/Project/ProjectChangeWatcher.cs
index 7a534922cf..f6712ab668 100644
--- a/src/Main/Base/Project/Src/Project/ProjectChangeWatcher.cs
+++ b/src/Main/Base/Project/Src/Project/ProjectChangeWatcher.cs
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.IO;
+using System.Windows.Threading;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Commands;
using ICSharpCode.SharpDevelop.Gui;
@@ -113,7 +114,7 @@ namespace ICSharpCode.SharpDevelop.Project
// delay reloading message a bit, prevents showing two messages
// when the file changes twice in quick succession; and prevents
// trying to reload the file while it is still being written
- SD.MainThread.CallLater(TimeSpan.FromSeconds(0.5), delegate { MainFormActivated(this, EventArgs.Empty); });
+ SD.MainThread.CallLater(TimeSpan.FromSeconds(0.5), delegate { MainFormActivated(); });
}
}
}
@@ -121,6 +122,12 @@ namespace ICSharpCode.SharpDevelop.Project
static bool showingMessageBox;
static void MainFormActivated(object sender, EventArgs e)
+ {
+ // delay the event so that we don't interrupt the user if he's trying to close SharpDevelop
+ SD.MainThread.InvokeAsyncAndForget(MainFormActivated, DispatcherPriority.Background);
+ }
+
+ static void MainFormActivated()
{
if (wasChangedExternally && !showingMessageBox) {