diff --git a/SharpDevelop.sln b/SharpDevelop.sln
index b66998c1ff..74352a6006 100644
--- a/SharpDevelop.sln
+++ b/SharpDevelop.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
-# SharpDevelop 4.0.0.5293
+# SharpDevelop 4.0.0.5401
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Main", "Main", "{256F5C28-532C-44C0-8AB8-D8EC5E492E01}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
index 89d80923f3..2a38523149 100644
--- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
+++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
@@ -175,6 +175,10 @@
Form
+
+ IntroduceMethodDialog.xaml
+ Code
+
NewProjectDialog.cs
@@ -807,6 +811,7 @@
GotoDialog.cs
+
diff --git a/src/Main/Base/Project/Src/Gui/Dialogs/IntroduceMethodDialog.xaml b/src/Main/Base/Project/Src/Gui/Dialogs/IntroduceMethodDialog.xaml
new file mode 100644
index 0000000000..68546692a7
--- /dev/null
+++ b/src/Main/Base/Project/Src/Gui/Dialogs/IntroduceMethodDialog.xaml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Main/Base/Project/Src/Gui/Dialogs/IntroduceMethodDialog.xaml.cs b/src/Main/Base/Project/Src/Gui/Dialogs/IntroduceMethodDialog.xaml.cs
new file mode 100644
index 0000000000..93e5762942
--- /dev/null
+++ b/src/Main/Base/Project/Src/Gui/Dialogs/IntroduceMethodDialog.xaml.cs
@@ -0,0 +1,92 @@
+//
+//
+//
+//
+// $Revision$
+//
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+
+using ICSharpCode.NRefactory.Ast;
+using ICSharpCode.SharpDevelop.Dom;
+using ICSharpCode.SharpDevelop.Dom.Refactoring;
+using ICSharpCode.SharpDevelop.Editor;
+using ICSharpCode.SharpDevelop.Project;
+
+namespace ICSharpCode.SharpDevelop.Gui
+{
+ ///
+ /// Interaction logic for IntroduceMethodDialog.xaml
+ ///
+ public partial class IntroduceMethodDialog : Window
+ {
+ public IClass CallingClass { get; private set; }
+
+ ITextEditor editor;
+ MethodDeclaration method;
+
+ public IntroduceMethodDialog(IClass callingClass, MethodDeclaration method, ITextEditor editor)
+ {
+ InitializeComponent();
+
+ CallingClass = callingClass;
+ this.editor = editor;
+ this.method = method;
+
+ var classes = GetAllStaticClasses(callingClass.ProjectContent.Project as IProject);
+
+ if (!classes.Any())
+ rbNew.IsChecked = true;
+ else {
+ classList.ItemsSource = classes;
+ classList.SelectedItem = classes.FirstOrDefault(c => c.Name.Contains("Extensions")) ?? classes.First();
+ }
+ }
+
+ IEnumerable GetAllStaticClasses(IProject project)
+ {
+ IProjectContent projectContent = ParserService.GetProjectContent(project);
+ if (projectContent != null) {
+ foreach (IClass c in projectContent.Classes) {
+ if (c.IsStatic)
+ yield return c;
+ }
+ }
+ }
+
+ void OKButtonClick(object sender, RoutedEventArgs e)
+ {
+ CodeGenerator gen = CallingClass.ProjectContent.Language.CodeGenerator;
+
+ if (rbExisting.IsChecked == true) {
+ IClass c = (IClass)classList.SelectedItem;
+ gen.InsertCodeAtEnd(c.BodyRegion, new RefactoringDocumentAdapter(editor.Document), method);
+ }
+ if (rbNew.IsChecked == true) {
+ TypeDeclaration type = new TypeDeclaration(Modifiers.Static, null);
+
+ type.Name = newClassName.Text;
+
+ type.AddChild(method);
+
+ gen.InsertCodeAfter(CallingClass, new RefactoringDocumentAdapter(editor.Document), type);
+ }
+ Close();
+ }
+
+ void CancelButtonClick(object sender, RoutedEventArgs e)
+ {
+ // do nothing
+ Close();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringMenuBuilder.cs b/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringMenuBuilder.cs
index 67959ce270..9694bf5361 100644
--- a/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringMenuBuilder.cs
+++ b/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringMenuBuilder.cs
@@ -16,12 +16,16 @@ using System.Windows.Input;
using ICSharpCode.Core;
using ICSharpCode.Core.Presentation;
+using ICSharpCode.NRefactory.Visitors;
using ICSharpCode.SharpDevelop.Bookmarks;
using ICSharpCode.SharpDevelop.Dom;
+using ICSharpCode.SharpDevelop.Dom.NRefactoryResolver;
+using ICSharpCode.SharpDevelop.Dom.Refactoring;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Gui.ClassBrowser;
using ICSharpCode.SharpDevelop.Project;
+using Ast = ICSharpCode.NRefactory.Ast;
namespace ICSharpCode.SharpDevelop.Refactoring
{
@@ -129,6 +133,9 @@ namespace ICSharpCode.SharpDevelop.Refactoring
} else if (rr is UnknownConstructorCallResolveResult) {
item = MakeItemForResolveError((UnknownConstructorCallResolveResult)rr, expressionResult.Context, textEditor);
insertIndex = 0; // Insert menu item at the topmost position.
+ } else if (rr is UnknownMethodResolveResult) {
+ item = MakeItemForResolveError((UnknownMethodResolveResult)rr, expressionResult.Context, textEditor);
+ insertIndex = 0; // Insert menu item at the topmost position.
}
if (item != null) {
resultItems.Insert(insertIndex, item);
@@ -173,6 +180,181 @@ namespace ICSharpCode.SharpDevelop.Refactoring
return MakeItemForUnknownClass(rr.CallingClass, rr.TypeName, textArea);
}
+ MenuItem MakeItemForResolveError(UnknownMethodResolveResult rr, ExpressionContext context, ITextEditor editor)
+ {
+ // TODO : make easy testable (hide dialog in test mode)
+ // TODO : add unit tests
+
+ if (rr.Target == null)
+ return null;
+
+ MenuItem item = new MenuItem() {
+ Header = "Introduce method " + rr.CallName + " in " + rr.Target.FullyQualifiedName,
+ Icon = ClassBrowserIconService.GotoArrow.CreateImage()
+ };
+
+ IClass targetClass = rr.Target.GetUnderlyingClass();
+
+ CodeGenerator gen = targetClass.ProjectContent.Language.CodeGenerator;
+ IAmbience ambience = targetClass.ProjectContent.Language.GetAmbience();
+
+ ClassFinder finder = new ClassFinder(rr.CallingMember);
+
+ ModifierEnum modifiers = ModifierEnum.None;
+
+ if (rr.CallingClass == targetClass) {
+ if (rr.CallingMember != null)
+ modifiers |= (rr.CallingMember.Modifiers & ModifierEnum.Static);
+ } else {
+ modifiers |= ModifierEnum.Public;
+ if (rr.IsStaticContext)
+ modifiers |= ModifierEnum.Static;
+ }
+
+ NRefactoryResolver resolver = new NRefactoryResolver(rr.CallingClass.ProjectContent.Language);
+ resolver.Initialize(ParserService.GetParseInformation(editor.FileName), editor.Caret.Line, editor.Caret.Column);
+
+ Ast.INode node = resolver.ParseCurrentMember(editor.Document.Text);
+ resolver.RunLookupTableVisitor(node);
+
+ InvocationExpressionLookupVisitor visitor = new InvocationExpressionLookupVisitor(editor);
+ node.AcceptVisitor(visitor, null);
+
+ if (visitor.Expression == null)
+ return null;
+
+ IReturnType type = resolver.GetExpectedTypeFromContext(visitor.Expression);
+ Ast.TypeReference typeRef = CodeGenerator.ConvertType(type, finder);
+
+ if (typeRef.IsNull) {
+ if (visitor.Expression.Parent is Ast.ExpressionStatement)
+ typeRef = new Ast.TypeReference("void", true);
+ else
+ typeRef = new Ast.TypeReference("object", true);
+ }
+
+ item.Click += delegate {
+ Ast.MethodDeclaration method = new Ast.MethodDeclaration {
+ Name = rr.CallName,
+ Modifier = CodeGenerator.ConvertModifier(modifiers, finder),
+ TypeReference = typeRef,
+ Parameters = CreateParameters(rr, finder, visitor.Expression).ToList(),
+ Body = CodeGenerator.CreateNotImplementedBlock()
+ };
+
+ if (targetClass.BodyRegion.IsEmpty) {
+ method.Parameters.Insert(0, new Ast.ParameterDeclarationExpression(CodeGenerator.ConvertType(rr.Target, finder), "thisInstance"));
+ method.IsExtensionMethod = true;
+ method.Modifier |= Ast.Modifiers.Static;
+ // TODO : combine code from IntroduceMethodDialog to remove code duplication
+ IntroduceMethodDialog dialog = new IntroduceMethodDialog(rr.CallingClass, method, editor);
+ dialog.Owner = WorkbenchSingleton.MainWindow;
+ dialog.ShowDialog();
+ return;
+ }
+ gen.InsertCodeAtEnd(targetClass.BodyRegion, new RefactoringDocumentAdapter(editor.Document), method);
+ ParserService.ParseCurrentViewContent();
+ // does not work yet, wrong method selected -> new method not yet present
+ // TODO : need to retrieve updated IClass instance
+ //IMethod newMember = targetClass.Methods.Last();
+ //IDocumentLine line = editor.Document.GetLine(newMember.BodyRegion.BeginLine + 1);
+ //int indentLength = DocumentUtilitites.GetWhitespaceAfter(editor.Document, line.Offset).Length;
+ //editor.Select(line.Offset + indentLength, "new NotImplementedException();".Length);
+ };
+
+ return item;
+ }
+
+ IEnumerable CreateParameters(UnknownMethodResolveResult rr, ClassFinder context, Ast.InvocationExpression invocation)
+ {
+ List usedNames = new List();
+
+ for (int i = 0; i < rr.Arguments.Count; i++) {
+ IReturnType type = rr.Arguments[i];
+ var typeRef = CodeGenerator.ConvertType(type, context);
+ typeRef = typeRef.IsNull ? new Ast.TypeReference("object", true) : typeRef;
+
+ Ast.Expression ex = invocation.Arguments[i];
+ string paramName = IsNumericType(type) ? "num" + i : type.Name + i.ToString();
+
+ if (ex is Ast.IdentifierExpression) {
+ paramName = (ex as Ast.IdentifierExpression).Identifier;
+ }
+
+ if (ex is Ast.MemberReferenceExpression) {
+ paramName = (ex as Ast.MemberReferenceExpression).MemberName;
+ }
+
+ Ast.ParameterModifiers mod = Ast.ParameterModifiers.None;
+
+ if (ex is Ast.DirectionExpression) {
+ var dex = ex as Ast.DirectionExpression;
+
+ if (dex.Expression is Ast.IdentifierExpression) {
+ paramName = (dex.Expression as Ast.IdentifierExpression).Identifier;
+ }
+
+ if (dex.Expression is Ast.MemberReferenceExpression) {
+ paramName = (dex.Expression as Ast.MemberReferenceExpression).MemberName;
+ }
+
+ mod = dex.FieldDirection == Ast.FieldDirection.Out ? Ast.ParameterModifiers.Out : (dex.FieldDirection == Ast.FieldDirection.Ref ? Ast.ParameterModifiers.Ref : Ast.ParameterModifiers.None);
+ }
+
+ paramName = rr.CallingClass.ProjectContent.Language.CodeGenerator.GetParameterName(paramName);
+
+ if (usedNames.Contains(paramName))
+ paramName += i.ToString();
+
+ usedNames.Add(paramName);
+
+ yield return new Ast.ParameterDeclarationExpression(typeRef, paramName) {
+ ParamModifier = mod
+ };
+ }
+ }
+
+ bool IsNumericType(IReturnType type)
+ {
+ return type.FullyQualifiedName == "System.Int32" ||
+ type.FullyQualifiedName == "System.Int16" ||
+ type.FullyQualifiedName == "System.Int64" ||
+ type.FullyQualifiedName == "System.Single" ||
+ type.FullyQualifiedName == "System.Double" ||
+ type.FullyQualifiedName == "System.UInt16" ||
+ type.FullyQualifiedName == "System.UInt32" ||
+ type.FullyQualifiedName == "System.UInt64";
+ }
+
+ class InvocationExpressionLookupVisitor : AbstractAstVisitor
+ {
+ ITextEditor editor;
+ Ast.InvocationExpression expression;
+
+ public Ast.InvocationExpression Expression {
+ get { return expression; }
+ }
+
+ public InvocationExpressionLookupVisitor(ITextEditor editor)
+ {
+ this.editor = editor;
+ this.expression = null;
+ }
+
+ public override object VisitInvocationExpression(Ast.InvocationExpression invocationExpression, object data)
+ {
+ int startOffset = editor.Document.PositionToOffset(invocationExpression.TargetObject.StartLocation.Line, invocationExpression.TargetObject.StartLocation.Column);
+ int endOffset = editor.Document.PositionToOffset(invocationExpression.EndLocation.Line, invocationExpression.EndLocation.Column);
+
+ int offset = editor.Caret.Offset;
+
+ if (offset >= startOffset && offset <= endOffset)
+ expression = invocationExpression;
+
+ return base.VisitInvocationExpression(invocationExpression, data);
+ }
+ }
+
MenuItem MakeItemForUnknownClass(IClass callingClass, string unknownClassName, ITextEditor textArea)
{
if (callingClass == null)
diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryResolver.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryResolver.cs
index 03a59504c0..edb0ba862e 100644
--- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryResolver.cs
+++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/NRefactoryResolver.cs
@@ -1347,6 +1347,18 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
collectionType = null;
if (collectionType != null)
return new ElementReturnType(projectContent, collectionType);
+ } else if (expr.Parent is IndexerExpression) {
+ IndexerExpression indexerExpr = expr.Parent as IndexerExpression;
+ MemberResolveResult indexer = ResolveInternal(indexerExpr, ExpressionContext.Default)
+ as MemberResolveResult;
+
+ if (indexer != null && indexer.ResolvedMember is IProperty) {
+ IProperty p = indexer.ResolvedMember as IProperty;
+ int position = indexerExpr.Indexes.IndexOf(expr);
+ if (position < 0 || position >= p.Parameters.Count)
+ return null;
+ return p.Parameters[position].ReturnType;
+ }
}
return null;
}
diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/ResolveVisitor.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/ResolveVisitor.cs
index a51d1eef59..b2ca0598db 100644
--- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/ResolveVisitor.cs
+++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/NRefactoryResolver/ResolveVisitor.cs
@@ -324,7 +324,33 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
if (resolver.Language == SupportedLanguage.VBNet) {
return CreateMemberResolveResult(GetVisualBasicIndexer(invocationExpression));
}
- return null;
+
+ return CreateUnknownMethodResolveResult(invocationExpression);
+ }
+
+ UnknownMethodResolveResult CreateUnknownMethodResolveResult(InvocationExpression invocationExpression)
+ {
+ var arguments = invocationExpression.Arguments.Select(a => Resolve(a).ResolvedType).ToList();
+
+ IReturnType target = resolver.CallingClass.DefaultReturnType;
+ string methodName = "";
+ bool isStatic = false;
+
+ if (invocationExpression.TargetObject is IdentifierExpression) {
+ IdentifierExpression ie = invocationExpression.TargetObject as IdentifierExpression;
+ methodName = ie.Identifier;
+ isStatic = resolver.CallingMember.Modifiers.HasFlag(ModifierEnum.Static);
+ }
+
+ if (invocationExpression.TargetObject is MemberReferenceExpression) {
+ MemberReferenceExpression mre = invocationExpression.TargetObject as MemberReferenceExpression;
+ var rr = Resolve(mre.TargetObject);
+ isStatic = rr is TypeResolveResult;
+ target = rr.ResolvedType;
+ methodName = mre.MemberName;
+ }
+
+ return new UnknownMethodResolveResult(resolver.CallingClass, resolver.CallingMember, target, methodName, isStatic, arguments);
}
ResolveResult FallbackResolveMethod(InvocationExpression invocation, MethodGroupResolveResult mgrr, IReturnType[] argumentTypes)
@@ -342,8 +368,7 @@ namespace ICSharpCode.SharpDevelop.Dom.NRefactoryResolver
}
}
- // TODO: method still not found, now return invalid ResolveResult describing the expected method
- return null;
+ return CreateUnknownMethodResolveResult(invocation);
}
public override object VisitLambdaExpression(LambdaExpression lambdaExpression, object data)
diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/CodeGenerator.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/CodeGenerator.cs
index 82cea4c798..76bba250bb 100644
--- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/CodeGenerator.cs
+++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Refactoring/CodeGenerator.cs
@@ -298,6 +298,12 @@ namespace ICSharpCode.SharpDevelop.Dom.Refactoring
}
#region Code generation / insertion
+ public virtual void InsertCodeAfter(IClass @class, IRefactoringDocument document, params AbstractNode[] nodes)
+ {
+ InsertCodeAfter(@class.BodyRegion.EndLine, document,
+ GetIndentation(document, @class.BodyRegion.BeginLine), nodes);
+ }
+
public virtual void InsertCodeAfter(IMember member, IRefactoringDocument document, params AbstractNode[] nodes)
{
if (member is IMethodOrProperty) {
diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ResolveResult.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ResolveResult.cs
index 6696bd5711..3872bc2432 100644
--- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ResolveResult.cs
+++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ResolveResult.cs
@@ -9,6 +9,8 @@ using System;
using System.Collections;
using System.Collections.Generic;
+using ICSharpCode.NRefactory.Ast;
+
namespace ICSharpCode.SharpDevelop.Dom
{
#region ResolveResult
@@ -941,6 +943,53 @@ namespace ICSharpCode.SharpDevelop.Dom
}
#endregion
+ #region UnknownMethodResolveResult
+ ///
+ /// Used for calls to unknown methods.
+ ///
+ public class UnknownMethodResolveResult : ResolveResult
+ {
+ string callName;
+ bool isStaticContext;
+ List arguments;
+ IReturnType target;
+
+ public UnknownMethodResolveResult(IClass callingClass, IMember callingMember, IReturnType target, string callName, bool isStaticContext, List arguments)
+ : base(callingClass, callingMember, null)
+ {
+ this.target = target == null ? callingClass.DefaultReturnType : target;
+ this.callName = callName;
+ this.arguments = arguments;
+ this.isStaticContext = isStaticContext;
+ }
+
+ public bool IsStaticContext {
+ get { return isStaticContext; }
+ }
+
+ public string CallName {
+ get { return callName; }
+ }
+
+ public IReturnType Target {
+ get { return target; }
+ }
+
+ public List Arguments {
+ get { return arguments; }
+ }
+
+ public override bool IsValid {
+ get { return false; }
+ }
+
+ public override ResolveResult Clone()
+ {
+ return new UnknownMethodResolveResult(this.CallingClass, this.CallingMember, this.target, this.callName, this.isStaticContext, this.arguments);
+ }
+ }
+ #endregion
+
#region UnknownConstructorCallResolveResult
///
/// Used for constructor calls on unknown types.