diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/ClassCodeGenerator.cs b/src/AddIns/Misc/PackageManagement/Project/Src/ClassCodeGenerator.cs index 6f80994c8c..fe42e69154 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/ClassCodeGenerator.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/ClassCodeGenerator.cs @@ -52,9 +52,9 @@ namespace ICSharpCode.PackageManagement FieldDeclaration CreateField(string name, string type) { return new FieldDeclaration(new List()) { - TypeReference = new TypeReference(type), + Fields = CreateVariableDeclarations(name), Modifier = Modifiers.Public, - Fields = CreateVariableDeclarations(name) + TypeReference = new TypeReference(type) }; } @@ -72,10 +72,14 @@ namespace ICSharpCode.PackageManagement } IField FindField(IRefactoringDocumentView view) + { + return FindMatchingClass(view).Fields.Last(); + } + + IClass FindMatchingClass(IRefactoringDocumentView view) { ICompilationUnit unit = view.Parse(); - IClass matchedClass = FindMatchingClass(unit); - return matchedClass.Fields.Last(); + return FindMatchingClass(unit); } IClass FindMatchingClass(ICompilationUnit unit) @@ -87,5 +91,32 @@ namespace ICSharpCode.PackageManagement } return null; } + + public CodeFunction AddPublicMethod(string name, string type) + { + CodeGenerator codeGenerator = GetCodeGenerator(); + IRefactoringDocumentView view = LoadClassDocumentView(); + codeGenerator.InsertCodeAtEnd(Class.Region, view.RefactoringDocument, CreateMethod(name, type)); + return GetMethodInserted(view); + } + + MethodDeclaration CreateMethod(string name, string type) + { + return new MethodDeclaration { + Name = name, + TypeReference = new TypeReference(type) + }; + } + + CodeFunction GetMethodInserted(IRefactoringDocumentView view) + { + IMethod method = FindMethod(view); + return new CodeFunction(method); + } + + IMethod FindMethod(IRefactoringDocumentView view) + { + return FindMatchingClass(view).Methods.Last(); + } } } diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/CodeElementsInNamespace.cs b/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/CodeElementsInNamespace.cs index aaf2c53102..342c31b77b 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/CodeElementsInNamespace.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/CodeElementsInNamespace.cs @@ -55,7 +55,11 @@ namespace ICSharpCode.PackageManagement.EnvDTE void AddCodeClass(IClass c) { - AddCodeElement(new CodeClass2(projectContent, c)); + if (c.ClassType == ClassType.Interface) { + AddCodeElement(new CodeInterface(projectContent, c)); + } else { + AddCodeElement(new CodeClass2(projectContent, c)); + } } } } diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/CodeInterface.cs b/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/CodeInterface.cs index 9fd917228d..1331663668 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/CodeInterface.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/CodeInterface.cs @@ -40,7 +40,8 @@ namespace ICSharpCode.PackageManagement.EnvDTE public CodeFunction AddFunction(string name, vsCMFunction kind, object type, object Position = null, vsCMAccess Access = vsCMAccess.vsCMAccessPublic) { - throw new NotImplementedException(); + var codeGenerator = new ClassCodeGenerator(Class); + return codeGenerator.AddPublicMethod(name, (string)type); } public override string FullName { diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/IMethodOrPropertyExtensions.cs b/src/AddIns/Misc/PackageManagement/Project/Src/IMethodOrPropertyExtensions.cs index b40b2885c5..aea93f505e 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/IMethodOrPropertyExtensions.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/IMethodOrPropertyExtensions.cs @@ -15,7 +15,15 @@ namespace ICSharpCode.PackageManagement public static FilePosition GetEndPosition(this IMethodOrProperty method) { + if (method.DeclaringTypeIsInterface()) { + return method.Region.ToEndPosition(method.CompilationUnit); + } return method.BodyRegion.ToEndPosition(method.CompilationUnit); } + + public static bool DeclaringTypeIsInterface(this IMethodOrProperty method) + { + return method.DeclaringType.ClassType == ClassType.Interface; + } } } diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/ClassCodeGeneratorTests.cs b/src/AddIns/Misc/PackageManagement/Test/Src/ClassCodeGeneratorTests.cs index 582e874257..48c68bac01 100644 --- a/src/AddIns/Misc/PackageManagement/Test/Src/ClassCodeGeneratorTests.cs +++ b/src/AddIns/Misc/PackageManagement/Test/Src/ClassCodeGeneratorTests.cs @@ -19,6 +19,7 @@ namespace PackageManagement.Tests ClassHelper helper; ClassCodeGenerator codeGenerator; CodeVariable codeVariable; + CodeFunction codeFunction; IDocumentLoader documentLoader; IRefactoringDocument document; FakeCodeGenerator fakeCodeGenerator; @@ -37,6 +38,7 @@ namespace PackageManagement.Tests void CreateClass(string name) { helper.CreatePublicClass(name); + helper.SetClassType(ICSharpCode.SharpDevelop.Dom.ClassType.Class); } void CreateCodeGenerator() @@ -92,13 +94,39 @@ namespace PackageManagement.Tests return helper; } + void AddMethodToClassForReparse(string name) + { + AddMethodToClassForReparse(name, DomRegion.Empty, DomRegion.Empty); + } + + void AddMethodToClassForReparse(string name, DomRegion region, DomRegion bodyRegion) + { + ClassHelper helper = CreateClassHelper("MyClass"); + helper.AddMethodToClass(name, region, bodyRegion); + AddClassesToReparsedCompilationUnit(helper); + } + + void AddMethodsToClassForReparse(params string[] names) + { + ClassHelper helper = CreateClassHelper("MyClass"); + foreach (string name in names) { + helper.AddMethodToClass(name); + } + AddClassesToReparsedCompilationUnit(helper); + } + void AddPublicVariable(string name, string type) { codeVariable = codeGenerator.AddPublicVariable(name, type); } + void AddPublicMethod(string name, string type) + { + codeFunction = codeGenerator.AddPublicMethod(name, type); + } + [Test] - public void AddPublicVariable_VariableNameAndTypeIsString_ReturnsCodeVariable() + public void AddPublicVariable_VariableTypeIsString_ReturnsCodeVariable() { CreateClass("MyClass"); CreateCodeGenerator(); @@ -119,7 +147,7 @@ namespace PackageManagement.Tests } [Test] - public void AddPublicVariable_VariableNameAndTypeIsCustomType_CodeForFieldAddedAtEndOfClass() + public void AddPublicVariable_VariableTypeIsCustomType_CodeForFieldAddedAtEndOfClass() { CreateClass("MyClass"); CreateCodeGenerator(); @@ -175,5 +203,87 @@ namespace PackageManagement.Tests Assert.AreEqual("MyVariable", codeVariable.Name); } + + [Test] + public void AddPublicMethod_MethodReturnTypeIsCustomType_CodeForMethodAddedAtEndOfClass() + { + CreateClass("MyClass"); + CreateCodeGenerator(); + var classRegion = new DomRegion(1, 2, 3, 4); + helper.SetClassRegion(classRegion); + string fileName = @"d:\projects\myproject\MyClass.cs"; + SetClassFileName(fileName); + SetDocumentFileName(fileName); + AddMethodToClassForReparse("MyClass.MyMethod"); + + AddPublicMethod("MyMethod", "MyType"); + + MethodDeclaration method = fakeCodeGenerator.NodePassedToInsertCodeAtEnd as MethodDeclaration; + Assert.AreEqual(classRegion, fakeCodeGenerator.RegionPassedToInsertCodeAtEnd); + Assert.AreEqual(document, fakeCodeGenerator.DocumentPassedToInsertCodeAtEnd); + Assert.AreEqual(Modifiers.None, method.Modifier); + Assert.AreEqual("MyType", method.TypeReference.Type); + Assert.AreEqual("MyMethod", method.Name); + Assert.IsNotNull(method.Body); + Assert.IsTrue(method.Body.IsNull); + } + + [Test] + public void AddPublicMethod_MethodReturnTypeIsString_ReturnsCodeMethod() + { + CreateClass("MyClass"); + CreateCodeGenerator(); + string fileName = @"d:\projects\myproject\MyClass.cs"; + SetClassFileName(fileName); + SetDocumentFileName(fileName); + + AddMethodToClassForReparse("MyClass.MyMethod", new DomRegion(1, 2, 1, 5), new DomRegion(1, 5, 3, 3)); + + AddPublicMethod("MyMethod", "System.String"); + + TextPoint start = codeFunction.GetStartPoint(); + TextPoint end = codeFunction.GetEndPoint(); + Assert.AreEqual("MyMethod", codeFunction.Name); + Assert.AreEqual(1, start.Line); + Assert.AreEqual(2, start.LineCharOffset); + Assert.AreEqual(3, end.Line); + Assert.AreEqual(3, end.LineCharOffset); + } + + [Test] + public void AddPublicMethod_ReparsedClassHasTwoMethods_LastMethodReturned() + { + CreateClass("MyClass"); + CreateCodeGenerator(); + string fileName = @"d:\projects\myproject\MyClass.cs"; + SetClassFileName(fileName); + SetDocumentFileName(fileName); + AddMethodsToClassForReparse("MyClass.First", "MyClass.MyMethod"); + + AddPublicMethod("MyMethod", "System.String"); + + Assert.AreEqual("MyMethod", codeFunction.Name); + } + + [Test] + public void AddPublicFunction_ReparsedCompilationUnitHasThreeClasses_MethodReturnedFromCorrectClass() + { + CreateClass("MyClass2"); + CreateCodeGenerator(); + string fileName = @"d:\projects\myproject\MyClass2.cs"; + SetClassFileName(fileName); + SetDocumentFileName(fileName); + ClassHelper class1 = CreateClassHelper("MyClass1"); + ClassHelper class2 = CreateClassHelper("MyClass2"); + ClassHelper class3 = CreateClassHelper("MyClass3"); + + class2.AddMethodToClass("MyClass2.MyMethod"); + + AddClassesToReparsedCompilationUnit(class1, class2, class3); + + AddPublicMethod("MyMethod", "System.String"); + + Assert.AreEqual("MyMethod", codeFunction.Name); + } } } diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/CodeGeneratorTests.cs b/src/AddIns/Misc/PackageManagement/Test/Src/CodeGeneratorTests.cs index 89b0dcdcdc..15717a6ab0 100644 --- a/src/AddIns/Misc/PackageManagement/Test/Src/CodeGeneratorTests.cs +++ b/src/AddIns/Misc/PackageManagement/Test/Src/CodeGeneratorTests.cs @@ -34,5 +34,40 @@ namespace PackageManagement.Tests Assert.AreEqual(expectedCode, code); } + + [Test] + public void GenerateCode_Method_CreatesMethod() + { + CreateCodeGenerator(); + var method = new MethodDeclaration(); + method.Name = "MyMethod"; + method.TypeReference = new TypeReference("MyReturnType"); + method.Modifier = Modifiers.Public; + method.Body = new BlockStatement(); + + string code = codeGenerator.GenerateCode(method, String.Empty); + + string expectedCode = + "public MyReturnType MyMethod()\r\n" + + "{\r\n" + + "}\r\n"; + + Assert.AreEqual(expectedCode, code); + } + + [Test] + public void GenerateCode_InterfaceMethodDeclaration_CreatesMethod() + { + CreateCodeGenerator(); + var method = new MethodDeclaration(); + method.Name = "MyMethod"; + method.TypeReference = new TypeReference("MyReturnType"); + + string code = codeGenerator.GenerateCode(method, String.Empty); + + string expectedCode = "MyReturnType MyMethod();\r\n"; + + Assert.AreEqual(expectedCode, code); + } } } diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/CodeElementsInNamespaceTests.cs b/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/CodeElementsInNamespaceTests.cs index 0a41c74649..3f00fdcbf1 100644 --- a/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/CodeElementsInNamespaceTests.cs +++ b/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/CodeElementsInNamespaceTests.cs @@ -132,5 +132,16 @@ namespace PackageManagement.Tests.EnvDTE Assert.AreEqual("Test.MyClass", codeClass.FullName); } + + [Test] + public void Item_OneInterfaceCompletionEntryAndItemSelectedByName_ReturnsOneCodeInterface() + { + helper.AddInterfaceToProjectContentAndCompletionEntries("Test", "Test.IClass"); + CreateCodeElements("Test"); + + CodeInterface codeInterface = codeElements.Item("IClass") as CodeInterface; + + Assert.AreEqual("Test.IClass", codeInterface.FullName); + } } } diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/CodeFunctionTests.cs b/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/CodeFunctionTests.cs index 0c5887e1ef..54c7ccdc09 100644 --- a/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/CodeFunctionTests.cs +++ b/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/CodeFunctionTests.cs @@ -39,6 +39,16 @@ namespace PackageManagement.Tests.EnvDTE codeFunction = new CodeFunction(helper.Method); } + void SetDeclaringTypeAsInterface(string name) + { + helper.AddDeclaringTypeAsInterface(name); + } + + void SetDeclaringType(string name) + { + helper.AddDeclaringType(name); + } + [Test] public void Access_PublicFunction_ReturnsPublic() { @@ -63,6 +73,7 @@ namespace PackageManagement.Tests.EnvDTE public void GetStartPoint_FunctionStartsAtColumn3_ReturnsPointWithOffset3() { CreatePublicFunction("Class1.MyFunction"); + SetDeclaringType("Class1"); helper.FunctionStartsAtColumn(3); TextPoint point = codeFunction.GetStartPoint(); @@ -75,6 +86,7 @@ namespace PackageManagement.Tests.EnvDTE public void GetStartPoint_FunctionStartsAtLine2_ReturnsPointWithLine2() { CreatePublicFunction("Class1.MyFunction"); + SetDeclaringType("Class1"); helper.FunctionStartsAtLine(2); TextPoint point = codeFunction.GetStartPoint(); @@ -87,6 +99,7 @@ namespace PackageManagement.Tests.EnvDTE public void GetEndPoint_FunctionBodyEndsAtColumn4_ReturnsPointWithOffset4() { CreatePublicFunction("Class1.MyFunction"); + SetDeclaringType("Class1"); helper.FunctionBodyEndsAtColumn(4); TextPoint point = codeFunction.GetEndPoint(); @@ -99,6 +112,7 @@ namespace PackageManagement.Tests.EnvDTE public void GetEndPoint_FunctionBodyEndsAtLine4_ReturnsPointWithLine4() { CreatePublicFunction("Class1.MyFunction"); + SetDeclaringType("Class1"); helper.FunctionBodyEndsAtLine(4); TextPoint point = codeFunction.GetEndPoint(); @@ -116,5 +130,19 @@ namespace PackageManagement.Tests.EnvDTE Assert.AreEqual(vsCMElement.vsCMElementFunction, kind); } + + [Test] + public void GetEndPoint_InterfaceMethod_ReturnsMethodsDeclarationRegionEndNotBodyRegionEnd() + { + CreatePublicFunction("MyInterface.MyMethod"); + SetDeclaringTypeAsInterface("MyInterface"); + helper.SetRegion(new DomRegion(1, 1, 1, 10)); + helper.SetBodyRegion(new DomRegion(1, 10, 0, 0)); + + TextPoint point = codeFunction.GetEndPoint(); + + Assert.AreEqual(1, point.Line); + Assert.AreEqual(10, point.LineCharOffset); + } } } diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/EditPointTests.cs b/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/EditPointTests.cs index 98d8b22316..329f9cdea0 100644 --- a/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/EditPointTests.cs +++ b/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/EditPointTests.cs @@ -105,6 +105,7 @@ namespace PackageManagement.Tests.EnvDTE var methodRegion = new DomRegion(1, 5, 1, 10); var methodBodyRegion = new DomRegion(1, 10, 3, 12); CreateMethod(fileName, methodRegion, methodBodyRegion); + methodHelper.AddDeclaringType("MyClass"); DocumentOffsetToReturn(line: 1, column: 5, offset: 5); DocumentOffsetToReturn(line: 3, column: 12, offset: 20); DocumentFileName(fileName); diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/ClassHelper.cs b/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/ClassHelper.cs index 8e16fa91d2..6b4b76dcbd 100644 --- a/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/ClassHelper.cs +++ b/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/ClassHelper.cs @@ -93,10 +93,18 @@ namespace PackageManagement.Tests.Helpers /// Name should include the class prefix (e.g. "Class1.MyMethod"); /// public void AddMethodToClass(string fullyQualifiedName) + { + AddMethodToClass(fullyQualifiedName, DomRegion.Empty, DomRegion.Empty); + } + + public void AddMethodToClass(string fullyQualifiedName, DomRegion region, DomRegion bodyRegion) { var helper = new MethodHelper(); helper.ProjectContentHelper = ProjectContentHelper; helper.CreateMethod(fullyQualifiedName); + helper.SetRegion(region); + helper.SetBodyRegion(bodyRegion); + helper.SetDeclaringType(Class); methods.Add(helper.Method); } @@ -144,5 +152,10 @@ namespace PackageManagement.Tests.Helpers { CompilationUnitHelper.SetFileName(fileName); } + + public void SetClassType(ClassType type) + { + Class.Stub(c => c.ClassType).Return(type); + } } } diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/MethodHelper.cs b/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/MethodHelper.cs index c211b62fc0..e81627768d 100644 --- a/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/MethodHelper.cs +++ b/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/MethodHelper.cs @@ -85,5 +85,22 @@ namespace PackageManagement.Tests.Helpers unit.FileName = fileName; Method.Stub(m => m.CompilationUnit).Return(unit); } + + public void AddDeclaringTypeAsInterface(string name) + { + IClass declaringType = ProjectContentHelper.AddInterfaceToProjectContent(name); + SetDeclaringType(declaringType); + } + + public void SetDeclaringType(IClass declaringType) + { + Method.Stub(m => m.DeclaringType).Return(declaringType); + } + + public void AddDeclaringType(string name) + { + IClass declaringType = ProjectContentHelper.AddClassToProjectContent(name); + SetDeclaringType(declaringType); + } } } diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/ProjectContentHelper.cs b/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/ProjectContentHelper.cs index 833ba6e6d8..157e85ff5e 100644 --- a/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/ProjectContentHelper.cs +++ b/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/ProjectContentHelper.cs @@ -92,6 +92,16 @@ namespace PackageManagement.Tests.Helpers return fakeClass; } + public IClass AddInterfaceToProjectContentAndCompletionEntries(string namespaceName, string className) + { + IClass fakeClass = AddInterfaceToProjectContent(className); + var namespaceContents = new List(); + namespaceContents.Add(fakeClass); + AddCompletionEntriesToNamespace(namespaceName, namespaceContents); + + return fakeClass; + } + public void AddNamespaceNameToProjectContent(string name) { NamespaceNames.Add(name);