diff --git a/src/AddIns/Misc/PackageManagement/Project/PackageManagement.csproj b/src/AddIns/Misc/PackageManagement/Project/PackageManagement.csproj index ff3c471fd3..8fc60c7231 100644 --- a/src/AddIns/Misc/PackageManagement/Project/PackageManagement.csproj +++ b/src/AddIns/Misc/PackageManagement/Project/PackageManagement.csproj @@ -155,6 +155,7 @@ + @@ -222,6 +223,7 @@ + diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/ClassKindUpdater.cs b/src/AddIns/Misc/PackageManagement/Project/Src/ClassKindUpdater.cs index 003690bf3a..e355235cc7 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/ClassKindUpdater.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/ClassKindUpdater.cs @@ -26,6 +26,9 @@ namespace ICSharpCode.PackageManagement public void MakeClassPartial() { + if (Class.IsPartial) + return; + OpenFileContainingClass(); int offset = GetPartialKeywordInsertOffset(); InsertPartialKeyword(offset); diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/CodeFunction.cs b/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/CodeFunction.cs index 5e95b16e1b..b1afbed8d1 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/CodeFunction.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/CodeFunction.cs @@ -9,17 +9,19 @@ namespace ICSharpCode.PackageManagement.EnvDTE public class CodeFunction : CodeElement { IDocumentLoader documentLoader; + IVirtualMethodUpdater methodUpdater; public CodeFunction(IMethod method) - : this(method, new DocumentLoader()) + : this(method, new DocumentLoader(), new VirtualMethodUpdater(method)) { } - public CodeFunction(IMethod method, IDocumentLoader documentLoader) + public CodeFunction(IMethod method, IDocumentLoader documentLoader, IVirtualMethodUpdater methodUpdater) : base(method) { this.Method = method; this.documentLoader = documentLoader; + this.methodUpdater = methodUpdater; } public CodeFunction() @@ -66,7 +68,11 @@ namespace ICSharpCode.PackageManagement.EnvDTE public virtual bool CanOverride { get { return Method.IsOverridable; } - set { throw new NotImplementedException(); } + set { + if (value) { + methodUpdater.MakeMethodVirtual(); + } + } } public virtual vsCMFunction FunctionKind { diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/IVirtualMethodUpdater.cs b/src/AddIns/Misc/PackageManagement/Project/Src/IVirtualMethodUpdater.cs new file mode 100644 index 0000000000..1333a789ca --- /dev/null +++ b/src/AddIns/Misc/PackageManagement/Project/Src/IVirtualMethodUpdater.cs @@ -0,0 +1,15 @@ +// 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; + +namespace ICSharpCode.PackageManagement +{ + /// + /// Used to update a method's source code and make the method virtual. + /// + public interface IVirtualMethodUpdater + { + void MakeMethodVirtual(); + } +} diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/VirtualMethodUpdater.cs b/src/AddIns/Misc/PackageManagement/Project/Src/VirtualMethodUpdater.cs new file mode 100644 index 0000000000..37daf016e1 --- /dev/null +++ b/src/AddIns/Misc/PackageManagement/Project/Src/VirtualMethodUpdater.cs @@ -0,0 +1,67 @@ +// 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.SharpDevelop.Dom; +using ICSharpCode.SharpDevelop.Dom.Refactoring; + +namespace ICSharpCode.PackageManagement +{ + public class VirtualMethodUpdater : IVirtualMethodUpdater + { + public VirtualMethodUpdater(IMethod method) + : this(method, new DocumentLoader()) + { + } + + public VirtualMethodUpdater(IMethod method, IDocumentLoader documentLoader) + { + this.Method = method; + this.DocumentLoader = documentLoader; + } + + IMethod Method { get; set; } + IDocumentLoader DocumentLoader { get; set; } + IRefactoringDocument Document { get; set; } + + public void MakeMethodVirtual() + { + if (Method.IsVirtual) + return; + + OpenFileContainingMethod(); + int offset = GetVirtualKeywordInsertOffset(); + InsertVirtualKeyword(offset); + } + + void OpenFileContainingMethod() + { + Document = DocumentLoader.LoadRefactoringDocument(Method.CompilationUnit.FileName); + } + + int GetVirtualKeywordInsertOffset() + { + IRefactoringDocumentLine line = Document.GetLine(Method.Region.BeginLine); + int offset = line.Text.IndexOf("public ", StringComparison.OrdinalIgnoreCase); + if (offset >= 0) { + int publicKeywordLength = 6; + return offset + line.Offset + publicKeywordLength + 1; + } + throw new ApplicationException("Unable to find 'method' declaration."); + } + + void InsertVirtualKeyword(int offset) + { + string virtualKeyword = GetLanguageSpecificVirtualKeyword(); + Document.Insert(offset, virtualKeyword + " "); + } + + string GetLanguageSpecificVirtualKeyword() + { + if (Method.ProjectContent.Language == LanguageProperties.VBNet) { + return "Overridable"; + } + return "virtual"; + } + } +} diff --git a/src/AddIns/Misc/PackageManagement/Test/PackageManagement.Tests.csproj b/src/AddIns/Misc/PackageManagement/Test/PackageManagement.Tests.csproj index 1c5b6579af..750344421d 100644 --- a/src/AddIns/Misc/PackageManagement/Test/PackageManagement.Tests.csproj +++ b/src/AddIns/Misc/PackageManagement/Test/PackageManagement.Tests.csproj @@ -186,6 +186,7 @@ + diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/ClassKindUpdaterTests.cs b/src/AddIns/Misc/PackageManagement/Test/Src/ClassKindUpdaterTests.cs index 3490cbf352..54a92eeb0b 100644 --- a/src/AddIns/Misc/PackageManagement/Test/Src/ClassKindUpdaterTests.cs +++ b/src/AddIns/Misc/PackageManagement/Test/Src/ClassKindUpdaterTests.cs @@ -100,6 +100,20 @@ namespace PackageManagement.Tests document.AssertWasCalled(doc => doc.Insert(7, "partial ")); } + [Test] + public void MakeClassPartial_PublicCSharpClassThatIsAlreadyPartial_ClassDefinitionIsUnchanged() + { + CreatePublicCSharpClass(); + CreateClassKindUpdater(); + classHelper.MakeClassPartial(); + SetClassFileName(@"d:\projects\MyProject\MyClass.cs"); + SetClassDeclarationLine(1, "public class MyClass"); + + updater.MakeClassPartial(); + + document.AssertWasNotCalled(doc => doc.Insert(Arg.Is.Anything, Arg.Is.Anything)); + } + [Test] public void MakeClassPartial_PublicVisualBasicClassWithNoOtherModifiers_AddsVisualBasicPartialKeywordToClassDefinition() { diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/CodeFunctionTests.cs b/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/CodeFunctionTests.cs index 215e7827c1..8fcd19c0cd 100644 --- a/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/CodeFunctionTests.cs +++ b/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/CodeFunctionTests.cs @@ -2,6 +2,7 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using ICSharpCode.PackageManagement; using ICSharpCode.PackageManagement.EnvDTE; using ICSharpCode.SharpDevelop.Dom; using NUnit.Framework; @@ -15,6 +16,7 @@ namespace PackageManagement.Tests.EnvDTE { CodeFunction codeFunction; MethodHelper helper; + IVirtualMethodUpdater methodUpdater; [SetUp] public void Init() @@ -36,7 +38,8 @@ namespace PackageManagement.Tests.EnvDTE void CreateFunction() { - codeFunction = new CodeFunction(helper.Method); + methodUpdater = MockRepository.GenerateStub(); + codeFunction = new CodeFunction(helper.Method, null, methodUpdater); } void CreatePublicConstructor(string name) @@ -373,5 +376,25 @@ namespace PackageManagement.Tests.EnvDTE Assert.AreEqual(1, attributes.Count); Assert.AreEqual("System.ObsoleteAttribute", attribute.FullName); } + + [Test] + public void CanOverride_SetToTrueForFunction_VirtualKeywordAddedToFunction() + { + CreatePublicFunction("MyClass.MyFunction"); + + codeFunction.CanOverride = true; + + methodUpdater.AssertWasCalled(updater => updater.MakeMethodVirtual()); + } + + [Test] + public void CanOverride_SetToFalseForFunction_VirtualKeywordNotAddedToFunction() + { + CreatePublicFunction("MyClass.MyFunction"); + + codeFunction.CanOverride = false; + + methodUpdater.AssertWasNotCalled(updater => updater.MakeMethodVirtual()); + } } } diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/EditPointTests.cs b/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/EditPointTests.cs index 83a02b3348..1cf28aa08b 100644 --- a/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/EditPointTests.cs +++ b/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/EditPointTests.cs @@ -69,7 +69,7 @@ namespace PackageManagement.Tests.EnvDTE void CreateMethodEditPoint() { - var codeFunction = new CodeFunction(methodHelper.Method, documentLoader); + var codeFunction = new CodeFunction(methodHelper.Method, documentLoader, null); TextPoint startPoint = codeFunction.GetStartPoint(); endPoint = codeFunction.GetEndPoint(); editPoint = startPoint.CreateEditPoint(); diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/VirtualMethodUpdaterTests.cs b/src/AddIns/Misc/PackageManagement/Test/Src/VirtualMethodUpdaterTests.cs new file mode 100644 index 0000000000..01c222887e --- /dev/null +++ b/src/AddIns/Misc/PackageManagement/Test/Src/VirtualMethodUpdaterTests.cs @@ -0,0 +1,151 @@ +// 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.PackageManagement; +using ICSharpCode.SharpDevelop.Dom.Refactoring; +using NUnit.Framework; +using PackageManagement.Tests.Helpers; +using Rhino.Mocks; + +namespace PackageManagement.Tests +{ + [TestFixture] + public class VirtualMethodUpdaterTests + { + VirtualMethodUpdater updater; + MethodHelper methodHelper; + IRefactoringDocument document; + IDocumentLoader documentLoader; + + [SetUp] + public void Init() + { + methodHelper = new MethodHelper(); + document = MockRepository.GenerateStub(); + documentLoader = MockRepository.GenerateStub(); + } + + void CreatePublicCSharpFunction() + { + methodHelper.CreatePublicMethod("MyMethod"); + methodHelper.ProjectContentHelper.ProjectContentIsForCSharpProject(); + } + + void CreatePublicVisualBasicFunction() + { + methodHelper.CreatePublicMethod("MyMethod"); + methodHelper.ProjectContentHelper.ProjectContentIsForVisualBasicProject(); + } + + void SetDocumentFileName(string fileName) + { + documentLoader.Stub(loader => loader.LoadRefactoringDocument(fileName)).Return(document); + } + + void CreateVirtualMethodUpdater() + { + updater = new VirtualMethodUpdater(methodHelper.Method, documentLoader); + } + + void SetFileNameForMethod(string fileName) + { + methodHelper.SetCompilationUnitFileName(fileName); + SetDocumentFileName(fileName); + } + + void SetMethodDeclarationLineWithOffset(int line, string text, int offset) + { + methodHelper.FunctionStartsAtLine(line); + SetDocumentLineText(line, text, offset); + } + + void SetMethodDeclarationLine(int line, string text) + { + SetMethodDeclarationLineWithOffset(line, text, 0); + } + + void SetDocumentLineText(int lineNumber, string text, int offset) + { + IRefactoringDocumentLine documentLine = MockRepository.GenerateStub(); + documentLine.Stub(line => line.Text).Return(text); + documentLine.Stub(line => line.Offset).Return(offset); + document.Stub(doc => doc.GetLine(lineNumber)).Return(documentLine); + } + + [Test] + public void MakeMethodVirtual_PublicCSharpClassWithNoOtherModifiers_AddsVirtualKeywordToMethodDefinition() + { + CreatePublicCSharpFunction(); + CreateVirtualMethodUpdater(); + SetFileNameForMethod(@"d:\projects\MyProject\MyClass.cs"); + SetMethodDeclarationLine(1, "public void MyMethod()"); + + updater.MakeMethodVirtual(); + + document.AssertWasCalled(doc => doc.Insert(7, "virtual ")); + } + + [Test] + public void MakeMethodVirtual_MethodAlreadyVirtual_MethodDefinitionIsNotChanged() + { + CreatePublicCSharpFunction(); + CreateVirtualMethodUpdater(); + methodHelper.MakeMethodVirtual(); + SetFileNameForMethod(@"d:\projects\MyProject\MyClass.cs"); + SetMethodDeclarationLine(1, "public void MyMethod()"); + + updater.MakeMethodVirtual(); + + document.AssertWasNotCalled(doc => doc.Insert(Arg.Is.Anything, Arg.Is.Anything)); + } + + [Test] + public void MakeMethodVirtual_PublicVisualBasicClassWithNoOtherModifiers_AddsOverridableKeywordToMethodDefinition() + { + CreatePublicVisualBasicFunction(); + CreateVirtualMethodUpdater(); + SetFileNameForMethod(@"d:\projects\MyProject\MyClass.vb"); + SetMethodDeclarationLine(1, "Public Sub MyMethod"); + + updater.MakeMethodVirtual(); + + document.AssertWasCalled(doc => doc.Insert(7, "Overridable ")); + } + + [Test] + public void MakeMethodVirtual_NoPublicKeywordInClassDeclarationLine_ExceptionIsThrown() + { + CreatePublicCSharpFunction(); + CreateVirtualMethodUpdater(); + SetFileNameForMethod(@"d:\projects\MyProject\MyClass.cs"); + SetMethodDeclarationLine(1, "void test()"); + + Assert.Throws(() => updater.MakeMethodVirtual()); + } + + [Test] + public void MakeMethodVirtual_NoPublicKeywordButMethodNameIncludesPublicKeyword_ExceptionIsThrown() + { + CreatePublicCSharpFunction(); + CreateVirtualMethodUpdater(); + SetFileNameForMethod(@"d:\projects\MyProject\MyClass.cs"); + SetMethodDeclarationLine(1, "void publicmethod()"); + + Assert.Throws(() => updater.MakeMethodVirtual()); + } + + [Test] + public void MakeMethodVirtual_PublicCSharpMethodNotOnFirstLine_AddsVirtualKeywordToMethodDefinitionAtCorrectOffset() + { + CreatePublicCSharpFunction(); + CreateVirtualMethodUpdater(); + SetFileNameForMethod(@"d:\projects\MyProject\MyClass.cs"); + SetMethodDeclarationLineWithOffset(1, "public void MyMethod()", offset: 10); + + updater.MakeMethodVirtual(); + + document.AssertWasCalled(doc => doc.Insert(17, "virtual ")); + } + } +}