├── .gitignore ├── IntegrationTests.testsettings ├── LICENSE ├── README.md ├── RemoveTrailingWhitespaces.sln ├── RemoveTrailingWhitespaces ├── GlobalSuppressions.cs ├── Guids.cs ├── Key.snk ├── LICENSE.txt ├── PkcCmdIDList.cs ├── Properties │ └── AssemblyInfo.cs ├── RemoveTrailingWhitespaces.csproj ├── RemoveTrailingWhitespaces.vsct ├── RemoveTrailingWhitespacesPackage.cs ├── RemoveTrailingWhitespaces_IntegrationTests │ ├── IntegrationTest Library │ │ ├── DialogboxPurger.cs │ │ ├── NativeMethods.cs │ │ └── Utils.cs │ ├── Key.snk │ ├── PackageTest.cs │ ├── RemoveTrailingWhitespaces_IntegrationTests.csproj │ └── SignOff-Tests │ │ ├── CPPProjectTests.cs │ │ ├── CSharpProjectTests.cs │ │ ├── SolutionTests.cs │ │ └── VBProjectTests.cs ├── RemoveTrailingWhitespaces_UnitTests │ ├── Key.snk │ ├── PackageTest.cs │ └── RemoveTrailingWhitespaces_UnitTests.csproj ├── Resources.Designer.cs ├── Resources.resx ├── Resources │ ├── menuicon.png │ ├── packageicon.png │ └── preview.png ├── VSPackage.resx ├── app.config └── source.extension.vsixmanifest └── UnitTests.testsettings /.gitignore: -------------------------------------------------------------------------------- 1 | #Visual Studio files 2 | *.[Oo]bj 3 | *.user 4 | *.aps 5 | *.pch 6 | *.vspscc 7 | *.vssscc 8 | *_i.c 9 | *_p.c 10 | *.ncb 11 | *.suo 12 | *.tlb 13 | *.tlh 14 | *.bak 15 | *.[Cc]ache 16 | *.ilk 17 | *.log 18 | *.lib 19 | *.sbr 20 | *.sdf 21 | *.opensdf 22 | *.unsuccessfulbuild 23 | ipch/ 24 | [Oo]bj/ 25 | [Bb]in 26 | [Dd]ebug*/ 27 | [Rr]elease*/ 28 | Ankh.NoLoad 29 | .vs/ 30 | #NuGet 31 | packages/ 32 | -------------------------------------------------------------------------------- /IntegrationTests.testsettings: -------------------------------------------------------------------------------- 1 | 2 | 7 | This test run configuration uses the VS IDE host type in the test run. 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Sergey Semushin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RemoveTrailingWhitespaces 2 | Visual Studio extension. Trailing whitespace removal tool. Removes either manually or on file save. 3 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.539 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A688CC3C-273D-4418-ABD7-5047BF9B32C2}" 7 | ProjectSection(SolutionItems) = preProject 8 | IntegrationTests.testsettings = IntegrationTests.testsettings 9 | UnitTests.testsettings = UnitTests.testsettings 10 | EndProjectSection 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemoveTrailingWhitespaces", "RemoveTrailingWhitespaces\RemoveTrailingWhitespaces.csproj", "{33B06D46-74C6-4F60-BB43-B3FDB8BDCEB0}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {33B06D46-74C6-4F60-BB43-B3FDB8BDCEB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {33B06D46-74C6-4F60-BB43-B3FDB8BDCEB0}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {33B06D46-74C6-4F60-BB43-B3FDB8BDCEB0}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {33B06D46-74C6-4F60-BB43-B3FDB8BDCEB0}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {2596E577-D090-421F-B128-6D2DB3A369B2} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. Project-level 3 | // suppressions either have no target or are given a specific target 4 | // and scoped to a namespace, type, member, etc. 5 | // 6 | // To add a suppression to this file, right-click the message in the 7 | // Error List, point to "Suppress Message(s)", and click "In Project 8 | // Suppression File". You do not need to add suppressions to this 9 | // file manually. 10 | 11 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")] 12 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/Guids.cs: -------------------------------------------------------------------------------- 1 | // Guids.cs 2 | // MUST match guids.h 3 | using System; 4 | 5 | namespace Predelnik.RemoveTrailingWhitespaces 6 | { 7 | static class GuidList 8 | { 9 | public const string guidRemoveTrailingWhitespacesPkgString = "70f718a3-a985-44e9-9e00-4c767c708ace"; 10 | public const string guidRemoveTrailingWhitespacesCmdSetString = "9880ef45-cb7d-4531-bccf-d228fccbb119"; 11 | 12 | public static readonly Guid guidRemoveTrailingWhitespacesCmdSet = new Guid(guidRemoveTrailingWhitespacesCmdSetString); 13 | }; 14 | } -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Predelnik/RemoveTrailingWhitespaces/934f165bc70de78c66832c4957b65768b8736cb0/RemoveTrailingWhitespaces/Key.snk -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Sergey Semushin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/PkcCmdIDList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Predelnik.RemoveTrailingWhitespaces 8 | { 9 | static class PkgCmdIDList 10 | { 11 | public const uint cmdIdRemoveTrailingWhitespaces = 0x1010; 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Resources; 4 | using System.Runtime.CompilerServices; 5 | using System.Runtime.InteropServices; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("RemoveTrailingWhitespaces")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("Predelnik")] 14 | [assembly: AssemblyProduct("RemoveTrailingWhitespaces")] 15 | [assembly: AssemblyCopyright("")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | [assembly: ComVisible(false)] 19 | [assembly: CLSCompliant(false)] 20 | [assembly: NeutralResourcesLanguage("en-US")] 21 | 22 | // Version information for an assembly consists of the following four values: 23 | // 24 | // Major Version 25 | // Minor Version 26 | // Build Number 27 | // Revision 28 | // 29 | // You can specify all the values or you can default the Revision and Build Numbers 30 | // by using the '*' as shown below: 31 | 32 | [assembly: AssemblyVersion("1.0.0.0")] 33 | [assembly: AssemblyFileVersion("1.0.0.0")] 34 | 35 | [assembly: InternalsVisibleTo("RemoveTrailingWhitespaces_IntegrationTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010025f2f501a5b7d0e729d71d613472ae5dacbf8ef9316ee2ed91bec9b6edc254fc19d4a35e9b15138dc11786a83be77f98de3612b527c83de9781f6c9103cffd361bad95e79656694d05e64d51d710a7288229d0d8f5b3606c138c5ead16001989fadc0e936625d608a0cd5cd86912ac7236b5c12b3b56f41c68977643ee0238bc")] 36 | [assembly: InternalsVisibleTo("RemoveTrailingWhitespaces_UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010025f2f501a5b7d0e729d71d613472ae5dacbf8ef9316ee2ed91bec9b6edc254fc19d4a35e9b15138dc11786a83be77f98de3612b527c83de9781f6c9103cffd361bad95e79656694d05e64d51d710a7288229d0d8f5b3606c138c5ead16001989fadc0e936625d608a0cd5cd86912ac7236b5c12b3b56f41c68977643ee0238bc")] 37 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/RemoveTrailingWhitespaces.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 15.0 5 | 12.0 6 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 7 | 8 | 9 | 10 | 11 | 12.0 12 | publish\ 13 | true 14 | Disk 15 | false 16 | Foreground 17 | 7 18 | Days 19 | false 20 | false 21 | true 22 | 0 23 | 1.0.0.%2a 24 | false 25 | false 26 | true 27 | 28 | 29 | 30 | 31 | 32 | 33 | Debug 34 | AnyCPU 35 | 2.0 36 | {33B06D46-74C6-4F60-BB43-B3FDB8BDCEB0} 37 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 38 | Library 39 | Properties 40 | Predelnik.RemoveTrailingWhitespaces 41 | RemoveTrailingWhitespaces 42 | True 43 | Key.snk 44 | v4.8 45 | 46 | 47 | true 48 | full 49 | false 50 | bin\Debug\ 51 | DEBUG;TRACE 52 | prompt 53 | 4 54 | true 55 | 56 | 57 | pdbonly 58 | true 59 | bin\Release\ 60 | TRACE 61 | prompt 62 | 4 63 | true 64 | 65 | 66 | 67 | True 68 | 69 | 70 | True 71 | 72 | 73 | True 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | {00020430-0000-0000-C000-000000000046} 109 | 2 110 | 0 111 | 0 112 | primary 113 | False 114 | False 115 | 116 | 117 | 118 | 119 | 120 | 121 | True 122 | True 123 | Resources.resx 124 | 125 | 126 | 127 | Component 128 | 129 | 130 | 131 | 132 | 133 | ResXFileCodeGenerator 134 | Resources.Designer.cs 135 | Designer 136 | 137 | 138 | true 139 | VSPackage 140 | Designer 141 | 142 | 143 | 144 | 145 | 146 | Designer 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | Menus.ctmenu 155 | Designer 156 | 157 | 158 | 159 | 160 | Always 161 | true 162 | 163 | 164 | Always 165 | true 166 | 167 | 168 | true 169 | 170 | 171 | 172 | 173 | False 174 | .NET Framework 3.5 SP1 175 | false 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 17.0.487 184 | 185 | 186 | 17.0.491 187 | 188 | 189 | 190 | true 191 | 192 | 193 | 194 | 201 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/RemoveTrailingWhitespaces.vsct: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | 36 | 37 | 38 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 52 | 53 | 60 | 61 | 62 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/RemoveTrailingWhitespacesPackage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Globalization; 4 | using System.Runtime.InteropServices; 5 | using System.ComponentModel.Design; 6 | using Microsoft.VisualStudio; 7 | using Microsoft.VisualStudio.Shell.Interop; 8 | using Microsoft.VisualStudio.Shell; 9 | using System.ComponentModel; 10 | using Microsoft.VisualStudio.Text; 11 | using Microsoft.VisualStudio.Text.Operations; 12 | using Microsoft.VisualStudio.ComponentModelHost; 13 | using Microsoft.VisualStudio.TextManager.Interop; 14 | using Microsoft.VisualStudio.Editor; 15 | using System.Linq; 16 | using EnvDTE; 17 | using System.Collections.Generic; 18 | 19 | using Task = System.Threading.Tasks.Task; 20 | using Microsoft; 21 | 22 | namespace Predelnik.RemoveTrailingWhitespaces 23 | { 24 | [CLSCompliant(false), ComVisible(true)] 25 | public class OptionsPage : DialogPage 26 | { 27 | private bool removeTrailingWhitespacesOnSave = true; 28 | [Category("All")] 29 | [DisplayName("Remove Trailing Whitespaces on Save")] 30 | public bool RemoveTrailingWhitespacesOnSave 31 | { 32 | get { return removeTrailingWhitespacesOnSave; } 33 | set { removeTrailingWhitespacesOnSave = value; } 34 | } 35 | }; 36 | 37 | internal class RunningDocTableEvents : IVsRunningDocTableEvents3 38 | { 39 | readonly RemoveTrailingWhitespacesPackage _pkg; 40 | 41 | public RunningDocTableEvents(RemoveTrailingWhitespacesPackage pkg) 42 | { 43 | _pkg = pkg; 44 | } 45 | 46 | public int OnBeforeSave(uint docCookie) 47 | { 48 | if (_pkg.RemoveOnSave()) 49 | { 50 | RunningDocumentInfo runningDocumentInfo = new RunningDocumentInfo(_pkg.rdt, docCookie); 51 | EnvDTE.Document document = _pkg.dte.Documents.OfType().SingleOrDefault(x => x.FullName == runningDocumentInfo.Moniker); 52 | if (document == null) 53 | return VSConstants.S_OK; 54 | if (document.Object("TextDocument") is TextDocument textDoc) 55 | _pkg.RemoveTrailingWhiteSpaces(textDoc); 56 | } 57 | return VSConstants.S_OK; 58 | } 59 | 60 | public int OnAfterAttributeChange(uint docCookie, uint grfAttribs) { return VSConstants.S_OK; } 61 | public int OnAfterAttributeChangeEx(uint docCookie, uint grfAttribs, IVsHierarchy pHierOld, 62 | uint itemidOld, string pszMkDocumentOld, IVsHierarchy pHierNew, 63 | uint itemidNew, string pszMkDocumentNew) 64 | { 65 | return VSConstants.S_OK; 66 | } 67 | 68 | public int OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame) { return VSConstants.S_OK; } 69 | public int OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) 70 | { 71 | return VSConstants.S_OK; 72 | } 73 | 74 | public int OnAfterSave(uint docCookie) { return VSConstants.S_OK; } 75 | public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame) { return VSConstants.S_OK; } 76 | 77 | public int OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) 78 | { 79 | return VSConstants.S_OK; 80 | } 81 | } 82 | 83 | /// 84 | /// This is the class that implements the package exposed by this assembly. 85 | /// 86 | /// The minimum requirement for a class to be considered a valid package for Visual Studio 87 | /// is to implement the IVsPackage interface and register itself with the shell. 88 | /// This package uses the helper classes defined inside the Managed Package Framework (MPF) 89 | /// to do it: it derives from the Package class that provides the implementation of the 90 | /// IVsPackage interface and uses the registration attributes defined in the framework to 91 | /// register itself and its components with the shell. 92 | /// 93 | // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is 94 | // a package. 95 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] 96 | // This attribute is used to register the information needed to show this package 97 | // in the Help/About dialog of Visual Studio. 98 | [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)] 99 | [Guid(GuidList.guidRemoveTrailingWhitespacesPkgString)] 100 | [ProvideOptionPage(typeof(OptionsPage), "Remove Trailing Whitespaces", "Options", 1000, 1001, true)] 101 | [ProvideMenuResource("Menus.ctmenu", 1)] 102 | [ProvideAutoLoad("{f1536ef8-92ec-443c-9ed7-fdadf150da82}", PackageAutoLoadFlags.BackgroundLoad)] 103 | public sealed class RemoveTrailingWhitespacesPackage : AsyncPackage 104 | { 105 | /// 106 | /// Default constructor of the package. 107 | /// Inside this method you can place any initialization code that does not require 108 | /// any Visual Studio service because at this point the package object is created but 109 | /// not sited yet inside Visual Studio environment. The place to do all the other 110 | /// initialization is the Initialize method. 111 | /// 112 | 113 | public RemoveTrailingWhitespacesPackage() 114 | { 115 | Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this.ToString())); 116 | } 117 | 118 | ///////////////////////////////////////////////////////////////////////////// 119 | // Overridden Package Implementation 120 | #region Package Members 121 | public DTE dte; 122 | public IVsRunningDocumentTable rdt; 123 | public IFindService findService; 124 | private uint rdtCookie; 125 | public IComponentModel componentModel; 126 | 127 | /// 128 | /// Initialization of the package; this method is called right after the package is sited, so this is the place 129 | /// where you can put all the initialization code that rely on services provided by VisualStudio. 130 | /// 131 | protected override async Task InitializeAsync(System.Threading.CancellationToken cancellationToken, IProgress progress) 132 | { 133 | dte = await GetServiceAsync(typeof(EnvDTE.DTE)) as EnvDTE.DTE; 134 | Assumes.Present(dte); 135 | rdt = await GetServiceAsync(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; 136 | Assumes.Present(rdt); 137 | componentModel = GetGlobalService(typeof(SComponentModel)) as IComponentModel; 138 | await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); 139 | InitializePackage(); 140 | } 141 | 142 | private void InitializePackage () 143 | { 144 | rdt.AdviseRunningDocTableEvents(new RunningDocTableEvents(this), out rdtCookie); 145 | if (GetService(typeof(IMenuCommandService)) is OleMenuCommandService mcs) 146 | { 147 | // Create the command for the menu item. 148 | CommandID menuCommandID = new CommandID( 149 | GuidList.guidRemoveTrailingWhitespacesCmdSet, (int)PkgCmdIDList.cmdIdRemoveTrailingWhitespaces); 150 | OleMenuCommand menuItem = new OleMenuCommand(OnRemoveTrailingWhitespacesPressed, menuCommandID); 151 | menuItem.BeforeQueryStatus += OnBeforeQueryStatus; 152 | mcs.AddCommand(menuItem); 153 | } 154 | } 155 | 156 | private void OnBeforeQueryStatus(object sender, EventArgs e) 157 | { 158 | var cmd = (OleMenuCommand)sender; 159 | 160 | cmd.Visible = IsNeededForActiveDocument(); 161 | cmd.Enabled = cmd.Visible; 162 | } 163 | 164 | private bool IsNeededForActiveDocument() 165 | { 166 | var doc = dte.ActiveDocument; 167 | if (doc == null) 168 | { 169 | return false; 170 | } 171 | 172 | if (doc.ReadOnly) 173 | { 174 | return false; 175 | } 176 | 177 | if (!(doc.Object("TextDocument") is TextDocument)) 178 | { 179 | return false; 180 | } 181 | 182 | return true; 183 | } 184 | 185 | private void OnRemoveTrailingWhitespacesPressed(object sender, EventArgs e) 186 | { 187 | if (dte.ActiveDocument == null) return; 188 | if (!(dte.ActiveDocument.Object() is TextDocument textDocument)) return; 189 | RemoveTrailingWhiteSpaces(textDocument); 190 | } 191 | 192 | private IFinder GetFinder(string findWhat, string replacement, ITextBuffer textBuffer) 193 | { 194 | var findService = componentModel.GetService (); 195 | var finderFactory = findService.CreateFinderFactory(findWhat, replacement, FindOptions.UseRegularExpressions); 196 | return finderFactory.Create(textBuffer.CurrentSnapshot); 197 | } 198 | 199 | internal static ITextBuffer GettextBufferAt(TextDocument textDocument, IComponentModel componentModel, IServiceProvider serviceProvider) 200 | { 201 | ThreadHelper.ThrowIfNotOnUIThread(); 202 | IVsWindowFrame windowFrame; 203 | if (VsShellUtilities.IsDocumentOpen( 204 | serviceProvider, 205 | textDocument.Parent.FullName, 206 | Guid.Empty, 207 | out var _, 208 | out var _, 209 | out windowFrame)) 210 | { 211 | IVsTextView view = VsShellUtilities.GetTextView(windowFrame); 212 | IVsTextLines lines; 213 | if (view.GetBuffer(out lines) == 0) 214 | { 215 | var buffer = lines as IVsTextBuffer; 216 | if (buffer != null) 217 | { 218 | var editorAdapterFactoryService = componentModel.GetService(); 219 | return editorAdapterFactoryService.GetDataBuffer(buffer); 220 | } 221 | } 222 | } 223 | 224 | return null; 225 | } 226 | private static void ReplaceAll(ITextBuffer textBuffer, IEnumerable replacements) 227 | { 228 | if (replacements.Any()) 229 | { 230 | using (var edit = textBuffer.CreateEdit()) 231 | { 232 | foreach (var match in replacements) 233 | { 234 | edit.Replace(match.Match, match.Replace); 235 | } 236 | 237 | edit.Apply(); 238 | } 239 | } 240 | } 241 | 242 | public void RemoveTrailingWhiteSpaces(TextDocument textDocument) 243 | { 244 | var textBuffer = GettextBufferAt(textDocument, componentModel, this); 245 | ReplaceAll(textBuffer, GetFinder("[^\\S\\r\\n]+(?=\\r?$)", "", textBuffer).FindForReplaceAll()); 246 | } 247 | 248 | public bool RemoveOnSave() 249 | { 250 | var props = dte.get_Properties("Remove Trailing Whitespaces", "Options"); 251 | return (bool)props.Item("RemoveTrailingWhitespacesOnSave").Value; 252 | } 253 | 254 | 255 | #endregion 256 | 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/RemoveTrailingWhitespaces_IntegrationTests/IntegrationTest Library/DialogboxPurger.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace Microsoft.VsSDK.IntegrationTestLibrary 3 | { 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | using System.Runtime.InteropServices; 8 | using System.Threading; 9 | using Microsoft.VisualStudio.Shell.Interop; 10 | using Microsoft.VisualStudio.Shell; 11 | 12 | /// 13 | /// This class is responsible to close dialog boxes that pop up during different VS Calls 14 | /// 15 | internal class DialogBoxPurger : IDisposable 16 | { 17 | /// 18 | /// The default number of milliseconds to wait for the threads to signal to terminate. 19 | /// 20 | private const int DefaultMillisecondsToWait = 3500; 21 | 22 | /// 23 | /// Object used for synchronization between thread calls. 24 | /// 25 | internal static volatile object Mutex = new object(); 26 | 27 | /// 28 | /// The IVsUIShell. This cannot be queried on the working thread from the service provider. Must be done in the main thread.!! 29 | /// 30 | private IVsUIShell uiShell; 31 | 32 | /// 33 | /// The button to "press" on the dialog. 34 | /// 35 | private int buttonAction; 36 | 37 | /// 38 | /// Thread signales to the calling thread that it is done. 39 | /// 40 | private bool exitThread = false; 41 | 42 | /// 43 | /// Calling thread signales to this thread to die. 44 | /// 45 | private AutoResetEvent threadDone = new AutoResetEvent(false); 46 | 47 | /// 48 | /// The queued thread started. 49 | /// 50 | private AutoResetEvent threadStarted = new AutoResetEvent(false); 51 | 52 | /// 53 | /// The result of the dialogbox closing for all the dialog boxes. That is if there are two of them and one fails this will be false. 54 | /// 55 | private bool dialogBoxCloseResult = false; 56 | 57 | /// 58 | /// The expected text to see on the dialog box. If set the thread will continue finding the dialog box with this text. 59 | /// 60 | private string expectedDialogBoxText = String.Empty; 61 | 62 | /// 63 | /// The number of the same dialog boxes to wait for. 64 | /// This is for scenarios when two dialog boxes with the same text are popping up. 65 | /// 66 | private int numberOfDialogsToWaitFor = 1; 67 | 68 | /// 69 | /// Has the object been disposed. 70 | /// 71 | private bool isDisposed; 72 | 73 | /// 74 | /// Overloaded ctor. 75 | /// 76 | /// The botton to "press" on the dialog box. 77 | /// The number of dialog boxes with the same message to wait for. This is the situation when the same action pops up two of the same dialog boxes 78 | /// The expected dialog box message to check for. 79 | internal DialogBoxPurger(int buttonAction, int numberOfDialogsToWaitFor, string expectedDialogMesssage) 80 | { 81 | this.buttonAction = buttonAction; 82 | this.numberOfDialogsToWaitFor = numberOfDialogsToWaitFor; 83 | this.expectedDialogBoxText = expectedDialogMesssage; 84 | } 85 | 86 | /// 87 | /// Overloaded ctor. 88 | /// 89 | /// The botton to "press" on the dialog box. 90 | /// The number of dialog boxes with the same message to wait for. This is the situation when the same action pops up two of the same dialog boxes 91 | internal DialogBoxPurger(int buttonAction, int numberOfDialogsToWaitFor) 92 | { 93 | this.buttonAction = buttonAction; 94 | this.numberOfDialogsToWaitFor = numberOfDialogsToWaitFor; 95 | } 96 | 97 | /// 98 | /// Overloaded ctor. 99 | /// 100 | /// The botton to "press" on the dialog box. 101 | /// The expected dialog box message to check for. 102 | internal DialogBoxPurger(int buttonAction, string expectedDialogMesssage) 103 | { 104 | this.buttonAction = buttonAction; 105 | this.expectedDialogBoxText = expectedDialogMesssage; 106 | } 107 | 108 | /// 109 | /// Overloaded ctor. 110 | /// 111 | /// The botton to "press" on the dialog box. 112 | internal DialogBoxPurger(int buttonAction) 113 | { 114 | this.buttonAction = buttonAction; 115 | } 116 | 117 | /// 118 | #region IDisposable Members 119 | 120 | void IDisposable.Dispose() 121 | { 122 | if (this.isDisposed) 123 | { 124 | return; 125 | } 126 | 127 | this.WaitForDialogThreadToTerminate(); 128 | 129 | this.isDisposed = true; 130 | } 131 | 132 | /// 133 | /// Spawns a thread that will start listening to dialog boxes. 134 | /// 135 | internal void Start() 136 | { 137 | // We ask for the uishell here since we cannot do that on the therad that we will spawn. 138 | IVsUIShell uiShell = Package.GetGlobalService(typeof(SVsUIShell)) as IVsUIShell; 139 | 140 | if (uiShell == null) 141 | { 142 | throw new InvalidOperationException("Could not get the uiShell from the serviceProvider"); 143 | } 144 | 145 | this.uiShell = uiShell; 146 | 147 | System.Threading.Thread thread = new System.Threading.Thread(new ThreadStart(this.HandleDialogBoxes)); 148 | thread.Start(); 149 | 150 | // We should never deadlock here, hence do not use the lock. Wait to be sure that the thread started. 151 | this.threadStarted.WaitOne(3500, false); 152 | } 153 | 154 | /// 155 | /// Waits for the dialog box close thread to terminate. If the thread does not signal back within millisecondsToWait that it is shutting down, 156 | /// then it will tell to the thread to do it. 157 | /// 158 | internal bool WaitForDialogThreadToTerminate() 159 | { 160 | return this.WaitForDialogThreadToTerminate(DefaultMillisecondsToWait); 161 | } 162 | 163 | /// 164 | /// Waits for the dialog box close thread to terminate. If the thread does not signal back within millisecondsToWait that it is shutting down, 165 | /// then it will tell to the thread to do it. 166 | /// 167 | /// The number milliseconds to wait for until the dialog purger thread is signaled to terminate. This is just for safe precaution that we do not hang. 168 | /// The result of the dialog boxes closing 169 | internal bool WaitForDialogThreadToTerminate(int numberOfMillisecondsToWait) 170 | { 171 | bool signaled = false; 172 | 173 | // We give millisecondsToWait sec to bring up and close the dialog box. 174 | signaled = this.threadDone.WaitOne(numberOfMillisecondsToWait, false); 175 | 176 | // Kill the thread since a timeout occured. 177 | if (!signaled) 178 | { 179 | lock (Mutex) 180 | { 181 | // Set the exit thread to true. Next time the thread will kill itselfes if it sees 182 | this.exitThread = true; 183 | } 184 | 185 | // Wait for the thread to finish. We should never deadlock here. 186 | this.threadDone.WaitOne(); 187 | } 188 | 189 | return this.dialogBoxCloseResult; 190 | } 191 | 192 | /// 193 | /// This is the thread method. 194 | /// 195 | private void HandleDialogBoxes() 196 | { 197 | // No synchronization numberOfDialogsToWaitFor since it is readonly 198 | IntPtr[] hwnds = new IntPtr[this.numberOfDialogsToWaitFor]; 199 | bool[] dialogBoxCloseResults = new bool[this.numberOfDialogsToWaitFor]; 200 | 201 | try 202 | { 203 | // Signal that we started 204 | lock (Mutex) 205 | { 206 | this.threadStarted.Set(); 207 | } 208 | 209 | // The loop will be exited either if a message is send by the caller thread or if we found the dialog. If a message box text is specified the loop will not exit until the dialog is found. 210 | bool stayInLoop = true; 211 | int dialogBoxesToWaitFor = 1; 212 | 213 | while (stayInLoop) 214 | { 215 | int hwndIndex = dialogBoxesToWaitFor - 1; 216 | 217 | // We need to lock since the caller might set context to null. 218 | lock (Mutex) 219 | { 220 | if (this.exitThread) 221 | { 222 | break; 223 | } 224 | 225 | // We protect the shell too from reentrency. 226 | this.uiShell.GetDialogOwnerHwnd(out hwnds[hwndIndex]); 227 | 228 | } 229 | 230 | if (hwnds[hwndIndex] != IntPtr.Zero) 231 | { 232 | StringBuilder windowClassName = new StringBuilder(256); 233 | NativeMethods.GetClassName(hwnds[hwndIndex], windowClassName, windowClassName.Capacity); 234 | 235 | // The #32770 is the class name of a messagebox dialog. 236 | if (windowClassName.ToString().Contains("#32770")) 237 | { 238 | IntPtr unmanagedMemoryLocation = IntPtr.Zero; 239 | string dialogBoxText = String.Empty; 240 | try 241 | { 242 | unmanagedMemoryLocation = Marshal.AllocHGlobal(10 * 1024); 243 | NativeMethods.EnumChildWindows(hwnds[hwndIndex], new NativeMethods.CallBack(FindMessageBoxString), unmanagedMemoryLocation); 244 | dialogBoxText = Marshal.PtrToStringUni(unmanagedMemoryLocation); 245 | } 246 | finally 247 | { 248 | if (unmanagedMemoryLocation != IntPtr.Zero) 249 | { 250 | Marshal.FreeHGlobal(unmanagedMemoryLocation); 251 | } 252 | } 253 | 254 | lock (Mutex) 255 | { 256 | 257 | // Since this is running on the main thread be sure that we close the dialog. 258 | bool dialogCloseResult = false; 259 | if (this.buttonAction != 0) 260 | { 261 | dialogCloseResult = NativeMethods.EndDialog(hwnds[hwndIndex], this.buttonAction); 262 | } 263 | 264 | // Check if we have found the right dialog box. 265 | if (String.IsNullOrEmpty(this.expectedDialogBoxText) || (!String.IsNullOrEmpty(dialogBoxText) && String.Compare(this.expectedDialogBoxText, dialogBoxText.Trim(), StringComparison.OrdinalIgnoreCase) == 0)) 266 | { 267 | dialogBoxCloseResults[hwndIndex] = dialogCloseResult; 268 | if (dialogBoxesToWaitFor++ >= this.numberOfDialogsToWaitFor) 269 | { 270 | stayInLoop = false; 271 | } 272 | } 273 | } 274 | } 275 | } 276 | } 277 | } 278 | finally 279 | { 280 | //Let the main thread run a possible close command. 281 | System.Threading.Thread.Sleep(2000); 282 | 283 | foreach (IntPtr hwnd in hwnds) 284 | { 285 | // At this point the dialog should be closed, if not attempt to close it. 286 | if (hwnd != IntPtr.Zero) 287 | { 288 | NativeMethods.SendMessage(hwnd, NativeMethods.WM_CLOSE, 0, new IntPtr(0)); 289 | } 290 | } 291 | 292 | lock (Mutex) 293 | { 294 | // Be optimistic. 295 | this.dialogBoxCloseResult = true; 296 | 297 | for (int i = 0; i < dialogBoxCloseResults.Length; i++) 298 | { 299 | if (!dialogBoxCloseResults[i]) 300 | { 301 | this.dialogBoxCloseResult = false; 302 | break; 303 | } 304 | } 305 | 306 | this.threadDone.Set(); 307 | } 308 | } 309 | } 310 | 311 | /// 312 | /// Finds a messagebox string on a messagebox. 313 | /// 314 | /// The windows handle of the dialog 315 | /// A pointer to the memorylocation the string will be written to 316 | /// True if found. 317 | private static bool FindMessageBoxString(IntPtr hwnd, IntPtr unmanagedMemoryLocation) 318 | { 319 | StringBuilder sb = new StringBuilder(512); 320 | NativeMethods.GetClassName(hwnd, sb, sb.Capacity); 321 | 322 | if (sb.ToString().ToLower().Contains("static")) 323 | { 324 | StringBuilder windowText = new StringBuilder(2048); 325 | NativeMethods.GetWindowText(hwnd, windowText, windowText.Capacity); 326 | 327 | if (windowText.Length > 0) 328 | { 329 | IntPtr stringAsPtr = IntPtr.Zero; 330 | try 331 | { 332 | stringAsPtr = Marshal.StringToHGlobalAnsi(windowText.ToString()); 333 | char[] stringAsArray = windowText.ToString().ToCharArray(); 334 | 335 | // Since unicode characters are copied check if we are out of the allocated length. 336 | // If not add the end terminating zero. 337 | if ((2 * stringAsArray.Length) + 1 < 2048) 338 | { 339 | Marshal.Copy(stringAsArray, 0, unmanagedMemoryLocation, stringAsArray.Length); 340 | Marshal.WriteInt32(unmanagedMemoryLocation, 2 * stringAsArray.Length, 0); 341 | } 342 | } 343 | finally 344 | { 345 | if (stringAsPtr != IntPtr.Zero) 346 | { 347 | Marshal.FreeHGlobal(stringAsPtr); 348 | } 349 | } 350 | return false; 351 | } 352 | } 353 | 354 | return true; 355 | } 356 | 357 | #endregion 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/RemoveTrailingWhitespaces_IntegrationTests/IntegrationTest Library/NativeMethods.cs: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | This code is licensed under the Visual Studio SDK license terms. 5 | THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF 6 | ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY 7 | IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR 8 | PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. 9 | 10 | ***************************************************************************/ 11 | 12 | namespace Microsoft.VsSDK.IntegrationTestLibrary 13 | { 14 | using System; 15 | using System.Collections.Generic; 16 | using System.Text; 17 | using System.Runtime.InteropServices; 18 | using System.Threading; 19 | using Microsoft.VisualStudio.Shell.Interop; 20 | 21 | /// 22 | /// Defines pinvoked utility methods and internal VS Constants 23 | /// 24 | internal static class NativeMethods 25 | { 26 | internal delegate bool CallBack(IntPtr hwnd, IntPtr lParam); 27 | 28 | // Declare two overloaded SendMessage functions 29 | [DllImport("user32.dll")] 30 | internal static extern UInt32 SendMessage(IntPtr hWnd, UInt32 Msg, 31 | UInt32 wParam, IntPtr lParam); 32 | 33 | [DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)] 34 | internal static extern bool PeekMessage([In, Out] ref Microsoft.VisualStudio.OLE.Interop.MSG msg, HandleRef hwnd, int msgMin, int msgMax, int remove); 35 | 36 | [DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)] 37 | internal static extern bool TranslateMessage([In, Out] ref Microsoft.VisualStudio.OLE.Interop.MSG msg); 38 | 39 | [DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)] 40 | internal static extern int DispatchMessage([In] ref Microsoft.VisualStudio.OLE.Interop.MSG msg); 41 | 42 | [DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)] 43 | internal static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool attach); 44 | 45 | [DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)] 46 | internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); 47 | 48 | [DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)] 49 | internal static extern uint GetCurrentThreadId(); 50 | 51 | [DllImport("user32")] 52 | internal static extern int EnumChildWindows(IntPtr hwnd, CallBack x, IntPtr y); 53 | 54 | [DllImport("user32")] 55 | internal static extern bool IsWindowVisible(IntPtr hDlg); 56 | 57 | [DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)] 58 | internal static extern IntPtr SetFocus(IntPtr hWnd); 59 | 60 | [DllImport("user32")] 61 | internal static extern int GetClassName(IntPtr hWnd, 62 | StringBuilder className, 63 | int stringLength); 64 | [DllImport("user32")] 65 | internal static extern int GetWindowText(IntPtr hWnd, StringBuilder className, int stringLength); 66 | 67 | 68 | [DllImport("user32")] 69 | internal static extern bool EndDialog(IntPtr hDlg, int result); 70 | 71 | [DllImport("Kernel32")] 72 | internal static extern long GetLastError(); 73 | 74 | internal const int QS_KEY = 0x0001, 75 | QS_MOUSEMOVE = 0x0002, 76 | QS_MOUSEBUTTON = 0x0004, 77 | QS_POSTMESSAGE = 0x0008, 78 | QS_TIMER = 0x0010, 79 | QS_PAINT = 0x0020, 80 | QS_SENDMESSAGE = 0x0040, 81 | QS_HOTKEY = 0x0080, 82 | QS_ALLPOSTMESSAGE = 0x0100, 83 | QS_MOUSE = QS_MOUSEMOVE | QS_MOUSEBUTTON, 84 | QS_INPUT = QS_MOUSE | QS_KEY, 85 | QS_ALLEVENTS = QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY, 86 | QS_ALLINPUT = QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY | QS_SENDMESSAGE; 87 | 88 | internal const int Facility_Win32 = 7; 89 | 90 | internal const int WM_CLOSE = 0x0010; 91 | 92 | internal const int 93 | S_FALSE = 0x00000001, 94 | S_OK = 0x00000000, 95 | 96 | IDOK = 1, 97 | IDCANCEL = 2, 98 | IDABORT = 3, 99 | IDRETRY = 4, 100 | IDIGNORE = 5, 101 | IDYES = 6, 102 | IDNO = 7, 103 | IDCLOSE = 8, 104 | IDHELP = 9, 105 | IDTRYAGAIN = 10, 106 | IDCONTINUE = 11; 107 | 108 | internal static long HResultFromWin32(long error) 109 | { 110 | if (error <= 0) 111 | { 112 | return error; 113 | } 114 | 115 | return ((error & 0x0000FFFF) | (Facility_Win32 << 16) | 0x80000000); 116 | } 117 | 118 | /// 119 | /// Please use this "approved" method to compare file names. 120 | /// 121 | public static bool IsSamePath(string file1, string file2) 122 | { 123 | if (file1 == null || file1.Length == 0) 124 | { 125 | return (file2 == null || file2.Length == 0); 126 | } 127 | 128 | Uri uri1 = null; 129 | Uri uri2 = null; 130 | 131 | try 132 | { 133 | if (!Uri.TryCreate(file1, UriKind.Absolute, out uri1) || !Uri.TryCreate(file2, UriKind.Absolute, out uri2)) 134 | { 135 | return false; 136 | } 137 | 138 | if (uri1 != null && uri1.IsFile && uri2 != null && uri2.IsFile) 139 | { 140 | return 0 == String.Compare(uri1.LocalPath, uri2.LocalPath, StringComparison.OrdinalIgnoreCase); 141 | } 142 | 143 | return file1 == file2; 144 | } 145 | catch (UriFormatException e) 146 | { 147 | System.Diagnostics.Trace.WriteLine("Exception " + e.Message); 148 | } 149 | 150 | return false; 151 | } 152 | 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/RemoveTrailingWhitespaces_IntegrationTests/IntegrationTest Library/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using System.Reflection; 5 | using System.Diagnostics; 6 | using System.Collections; 7 | using System.Collections.Generic; 8 | using System.ComponentModel.Design; 9 | using System.Runtime.InteropServices; 10 | using Microsoft.VisualStudio.Shell.Interop; 11 | using Microsoft.VisualStudio.Shell; 12 | using EnvDTE; 13 | using EnvDTE80; 14 | using Microsoft.Win32; 15 | using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; 16 | using Microsoft.VisualStudio.TestTools.UnitTesting; 17 | using Microsoft.VSSDK.Tools.VsIdeTesting; 18 | using Microsoft.VisualStudio; 19 | 20 | namespace Microsoft.VsSDK.IntegrationTestLibrary 21 | { 22 | /// 23 | /// 24 | public class TestUtils 25 | { 26 | 27 | #region Methods: Handling embedded resources 28 | /// 29 | /// Gets the embedded file identified by the resource name, and converts the 30 | /// file into a string. 31 | /// 32 | /// In VS, is DefaultNamespace.FileName? 33 | /// 34 | public static string GetEmbeddedStringResource(Assembly assembly, string resourceName) 35 | { 36 | string result = null; 37 | 38 | // Use the .NET procedure for loading a file embedded in the assembly 39 | Stream stream = assembly.GetManifestResourceStream(resourceName); 40 | if (stream != null) 41 | { 42 | // Convert bytes to string 43 | byte[] fileContentsAsBytes = new byte[stream.Length]; 44 | stream.Read(fileContentsAsBytes, 0, (int)stream.Length); 45 | result = Encoding.Default.GetString(fileContentsAsBytes); 46 | } 47 | else 48 | { 49 | // Embedded resource not found - list available resources 50 | Debug.WriteLine("Unable to find the embedded resource file '" + resourceName + "'."); 51 | Debug.WriteLine(" Available resources:"); 52 | foreach (string aResourceName in assembly.GetManifestResourceNames()) 53 | { 54 | Debug.WriteLine(" " + aResourceName); 55 | } 56 | } 57 | 58 | return result; 59 | } 60 | /// 61 | /// 62 | /// 63 | /// 64 | /// 65 | /// 66 | /// 67 | public static void WriteEmbeddedResourceToFile(Assembly assembly, string embeddedResourceName, string fileName) 68 | { 69 | // Get file contents 70 | string fileContents = GetEmbeddedStringResource(assembly, embeddedResourceName); 71 | if (fileContents == null) 72 | throw new ApplicationException("Failed to get embedded resource '" + embeddedResourceName + "' from assembly '" + assembly.FullName); 73 | 74 | // Write to file 75 | StreamWriter sw = new StreamWriter(fileName); 76 | sw.Write(fileContents); 77 | sw.Close(); 78 | } 79 | 80 | /// 81 | /// Writes an embedded resource to a file. 82 | /// 83 | /// The name of the assembly that the embedded resource is defined. 84 | /// The name of the embedded resource. 85 | /// The file to write the embedded resource's content. 86 | public static void WriteEmbeddedResourceToBinaryFile(Assembly assembly, string embeddedResourceName, string fileName) 87 | { 88 | // Get file contents 89 | Stream stream = assembly.GetManifestResourceStream(embeddedResourceName); 90 | if (stream == null) 91 | throw new InvalidOperationException("Failed to get embedded resource '" + embeddedResourceName + "' from assembly '" + assembly.FullName); 92 | 93 | // Write to file 94 | BinaryWriter sw = null; 95 | FileStream fs = null; 96 | try 97 | { 98 | byte[] fileContentsAsBytes = new byte[stream.Length]; 99 | stream.Read(fileContentsAsBytes, 0, (int)stream.Length); 100 | 101 | FileMode mode = FileMode.CreateNew; 102 | if (File.Exists(fileName)) 103 | { 104 | mode = FileMode.Truncate; 105 | } 106 | 107 | fs = new FileStream(fileName, mode); 108 | 109 | sw = new BinaryWriter(fs); 110 | sw.Write(fileContentsAsBytes); 111 | } 112 | finally 113 | { 114 | if (fs != null) 115 | { 116 | fs.Close(); 117 | } 118 | if (sw != null) 119 | { 120 | sw.Close(); 121 | } 122 | } 123 | } 124 | 125 | #endregion 126 | 127 | #region Methods: Handling temporary files and directories 128 | /// 129 | /// Returns the first available file name on the form 130 | /// [baseFileName]i.[extension] 131 | /// where [i] starts at 1 and increases until there is an available file name 132 | /// in the given directory. Also creates an empty file with that name to mark 133 | /// that file as occupied. 134 | /// 135 | /// Directory that the file should live in. 136 | /// 137 | /// may be null, in which case the .[extension] part 138 | /// is not added. 139 | /// Full file name. 140 | public static string GetNewFileName(string directory, string baseFileName, string extension) 141 | { 142 | // Get the new file name 143 | string fileName = GetNewFileOrDirectoryNameWithoutCreatingAnything(directory, baseFileName, extension); 144 | 145 | // Create an empty file to mark it as taken 146 | StreamWriter sw = new StreamWriter(fileName); 147 | 148 | sw.Write(""); 149 | sw.Close(); 150 | return fileName; 151 | } 152 | /// 153 | /// Returns the first available directory name on the form 154 | /// [baseDirectoryName]i 155 | /// where [i] starts at 1 and increases until there is an available directory name 156 | /// in the given directory. Also creates the directory to mark it as occupied. 157 | /// 158 | /// Directory that the file should live in. 159 | /// 160 | /// Full directory name. 161 | public static string GetNewDirectoryName(string directory, string baseDirectoryName) 162 | { 163 | // Get the new file name 164 | string directoryName = GetNewFileOrDirectoryNameWithoutCreatingAnything(directory, baseDirectoryName, null); 165 | 166 | // Create an empty directory to make it as occupied 167 | Directory.CreateDirectory(directoryName); 168 | 169 | return directoryName; 170 | } 171 | 172 | /// 173 | /// 174 | /// 175 | /// 176 | /// 177 | /// 178 | /// 179 | private static string GetNewFileOrDirectoryNameWithoutCreatingAnything(string directory, string baseFileName, string extension) 180 | { 181 | // - get a file name that we can use 182 | string fileName; 183 | int i = 1; 184 | 185 | string fullFileName = null; 186 | while (true) 187 | { 188 | // construct next file name 189 | fileName = baseFileName + i; 190 | if (extension != null) 191 | fileName += '.' + extension; 192 | 193 | // check if that file exists in the directory 194 | fullFileName = Path.Combine(directory, fileName); 195 | 196 | if (!File.Exists(fullFileName) && !Directory.Exists(fullFileName)) 197 | break; 198 | else 199 | i++; 200 | } 201 | 202 | return fullFileName; 203 | } 204 | #endregion 205 | 206 | #region Methods: Handling solutions 207 | /// 208 | /// Closes the currently open solution (if any), and creates a new solution with the given name. 209 | /// 210 | /// Name of new solution. 211 | public void CreateEmptySolution(string directory, string solutionName) 212 | { 213 | CloseCurrentSolution(__VSSLNSAVEOPTIONS.SLNSAVEOPT_NoSave); 214 | 215 | string solutionDirectory = GetNewDirectoryName(directory, solutionName); 216 | 217 | // Create and force save solution 218 | IVsSolution solutionService = (IVsSolution)VsIdeTestHostContext.ServiceProvider.GetService(typeof(IVsSolution)); 219 | solutionService.CreateSolution(solutionDirectory, solutionName, (uint)__VSCREATESOLUTIONFLAGS.CSF_SILENT); 220 | solutionService.SaveSolutionElement((uint)__VSSLNSAVEOPTIONS.SLNSAVEOPT_ForceSave, null, 0); 221 | DTE dte = VsIdeTestHostContext.Dte; 222 | Assert.AreEqual(solutionName + ".sln", Path.GetFileName(dte.Solution.FileName), "Newly created solution has wrong Filename"); 223 | } 224 | 225 | public void CloseCurrentSolution(__VSSLNSAVEOPTIONS saveoptions) 226 | { 227 | // Get solution service 228 | IVsSolution solutionService = (IVsSolution)VsIdeTestHostContext.ServiceProvider.GetService(typeof(IVsSolution)); 229 | 230 | // Close already open solution 231 | solutionService.CloseSolutionElement((uint)saveoptions, null, 0); 232 | } 233 | 234 | public void ForceSaveSolution() 235 | { 236 | // Get solution service 237 | IVsSolution solutionService = (IVsSolution)VsIdeTestHostContext.ServiceProvider.GetService(typeof(IVsSolution)); 238 | 239 | // Force-save the solution 240 | solutionService.SaveSolutionElement((uint)__VSSLNSAVEOPTIONS.SLNSAVEOPT_ForceSave, null, 0); 241 | } 242 | 243 | /// 244 | /// Get current number of open project in solution 245 | /// 246 | /// 247 | public int ProjectCount() 248 | { 249 | // Get solution service 250 | IVsSolution solutionService = (IVsSolution)VsIdeTestHostContext.ServiceProvider.GetService(typeof(IVsSolution)); 251 | object projectCount; 252 | solutionService.GetProperty((int)__VSPROPID.VSPROPID_ProjectCount, out projectCount); 253 | return (int)projectCount; 254 | } 255 | #endregion 256 | 257 | #region Methods: Handling projects 258 | /// 259 | /// Creates a project. 260 | /// 261 | /// Name of new project. 262 | /// Name of project template to use 263 | /// language 264 | /// New project. 265 | public void CreateProjectFromTemplate(string projectName, string templateName, string language, bool exclusive) 266 | { 267 | DTE dte = (DTE)VsIdeTestHostContext.ServiceProvider.GetService(typeof(DTE)); 268 | 269 | Solution2 sol = dte.Solution as Solution2; 270 | string projectTemplate = sol.GetProjectTemplate(templateName, language); 271 | 272 | // - project name and directory 273 | string solutionDirectory = Directory.GetParent(dte.Solution.FullName).FullName; 274 | string projectDirectory = GetNewDirectoryName(solutionDirectory, projectName); 275 | 276 | dte.Solution.AddFromTemplate(projectTemplate, projectDirectory, projectName, false); 277 | } 278 | #endregion 279 | 280 | #region Methods: Handling project items 281 | /// 282 | /// Create a new item in the project 283 | /// 284 | /// the parent collection for the new item 285 | /// 286 | /// 287 | /// 288 | /// 289 | public ProjectItem AddNewItemFromVsTemplate(ProjectItems parent, string templateName, string language, string name) 290 | { 291 | if (parent == null) 292 | throw new ArgumentException("project"); 293 | if (name == null) 294 | throw new ArgumentException("name"); 295 | 296 | DTE dte = (DTE)VsIdeTestHostContext.ServiceProvider.GetService(typeof(DTE)); 297 | 298 | Solution2 sol = dte.Solution as Solution2; 299 | 300 | string filename = sol.GetProjectItemTemplate(templateName, language); 301 | 302 | parent.AddFromTemplate(filename, name); 303 | 304 | return parent.Item(name); 305 | } 306 | 307 | /// 308 | /// Save an open document. 309 | /// 310 | /// for filebased documents this is the full path to the document 311 | public void SaveDocument(string documentMoniker) 312 | { 313 | // Get document cookie and hierarchy for the file 314 | IVsRunningDocumentTable runningDocumentTableService = (IVsRunningDocumentTable)VsIdeTestHostContext.ServiceProvider.GetService(typeof(IVsRunningDocumentTable)); 315 | uint docCookie; 316 | IntPtr docData; 317 | IVsHierarchy hierarchy; 318 | uint itemId; 319 | runningDocumentTableService.FindAndLockDocument( 320 | (uint)Microsoft.VisualStudio.Shell.Interop._VSRDTFLAGS.RDT_NoLock, 321 | documentMoniker, 322 | out hierarchy, 323 | out itemId, 324 | out docData, 325 | out docCookie); 326 | 327 | // Save the document 328 | IVsSolution solutionService = (IVsSolution)VsIdeTestHostContext.ServiceProvider.GetService(typeof(IVsSolution)); 329 | solutionService.SaveSolutionElement((uint)__VSSLNSAVEOPTIONS.SLNSAVEOPT_ForceSave, hierarchy, docCookie); 330 | } 331 | 332 | public void CloseInEditorWithoutSaving(string fullFileName) 333 | { 334 | // Get the RDT service 335 | IVsRunningDocumentTable runningDocumentTableService = (IVsRunningDocumentTable)VsIdeTestHostContext.ServiceProvider.GetService(typeof(IVsRunningDocumentTable)); 336 | Assert.IsNotNull(runningDocumentTableService, "Failed to get the Running Document Table Service"); 337 | 338 | // Get our document cookie and hierarchy for the file 339 | uint docCookie; 340 | IntPtr docData; 341 | IVsHierarchy hierarchy; 342 | uint itemId; 343 | runningDocumentTableService.FindAndLockDocument( 344 | (uint)Microsoft.VisualStudio.Shell.Interop._VSRDTFLAGS.RDT_NoLock, 345 | fullFileName, 346 | out hierarchy, 347 | out itemId, 348 | out docData, 349 | out docCookie); 350 | 351 | // Get the SolutionService 352 | IVsSolution solutionService = VsIdeTestHostContext.ServiceProvider.GetService(typeof(IVsSolution)) as IVsSolution; 353 | Assert.IsNotNull(solutionService, "Failed to get IVsSolution service"); 354 | 355 | // Close the document 356 | solutionService.CloseSolutionElement( 357 | (uint)__VSSLNSAVEOPTIONS.SLNSAVEOPT_NoSave, 358 | hierarchy, 359 | docCookie); 360 | } 361 | #endregion 362 | 363 | #region Methods: Handling Toolwindows 364 | public bool CanFindToolwindow(Guid persistenceGuid) 365 | { 366 | IVsUIShell uiShellService = VsIdeTestHostContext.ServiceProvider.GetService(typeof(SVsUIShell)) as IVsUIShell; 367 | Assert.IsNotNull(uiShellService); 368 | IVsWindowFrame windowFrame; 369 | int hr = uiShellService.FindToolWindow((uint)__VSFINDTOOLWIN.FTW_fFindFirst, ref persistenceGuid, out windowFrame); 370 | Assert.IsTrue(hr == VSConstants.S_OK); 371 | 372 | return (windowFrame != null); 373 | } 374 | #endregion 375 | 376 | #region Methods: Loading packages 377 | public IVsPackage LoadPackage(Guid packageGuid) 378 | { 379 | IVsShell shellService = (IVsShell)VsIdeTestHostContext.ServiceProvider.GetService(typeof(SVsShell)); 380 | IVsPackage package; 381 | shellService.LoadPackage(ref packageGuid, out package); 382 | Assert.IsNotNull(package, "Failed to load package"); 383 | return package; 384 | } 385 | #endregion 386 | 387 | /// 388 | /// Executes a Command (menu item) in the given context 389 | /// 390 | public void ExecuteCommand(CommandID cmd) 391 | { 392 | object Customin = null; 393 | object Customout = null; 394 | string guidString = cmd.Guid.ToString("B").ToUpper(); 395 | int cmdId = cmd.ID; 396 | DTE dte = VsIdeTestHostContext.Dte; 397 | dte.Commands.Raise(guidString, cmdId, ref Customin, ref Customout); 398 | } 399 | 400 | } 401 | } 402 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/RemoveTrailingWhitespaces_IntegrationTests/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Predelnik/RemoveTrailingWhitespaces/934f165bc70de78c66832c4957b65768b8736cb0/RemoveTrailingWhitespaces/RemoveTrailingWhitespaces_IntegrationTests/Key.snk -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/RemoveTrailingWhitespaces_IntegrationTests/PackageTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using Microsoft.VSSDK.Tools.VsIdeTesting; 7 | using Microsoft.VisualStudio.Shell.Interop; 8 | using Microsoft.VisualStudio.Shell; 9 | using EnvDTE; 10 | 11 | namespace RemoveTrailingWhitespaces_IntegrationTests 12 | { 13 | /// 14 | /// Integration test for package validation 15 | /// 16 | [TestClass] 17 | public class PackageTest 18 | { 19 | private delegate void ThreadInvoker(); 20 | 21 | private TestContext testContextInstance; 22 | 23 | /// 24 | ///Gets or sets the test context which provides 25 | ///information about and functionality for the current test run. 26 | /// 27 | public TestContext TestContext 28 | { 29 | get 30 | { 31 | return testContextInstance; 32 | } 33 | set 34 | { 35 | testContextInstance = value; 36 | } 37 | } 38 | 39 | [TestMethod] 40 | [HostType("VS IDE")] 41 | public void PackageLoadTest() 42 | { 43 | UIThreadInvoker.Invoke((ThreadInvoker)delegate() 44 | { 45 | 46 | //Get the Shell Service 47 | IVsShell shellService = VsIdeTestHostContext.ServiceProvider.GetService(typeof(SVsShell)) as IVsShell; 48 | Assert.IsNotNull(shellService); 49 | 50 | //Validate package load 51 | IVsPackage package; 52 | Guid packageGuid = new Guid(Predelnik.RemoveTrailingWhitespaces.GuidList.guidRemoveTrailingWhitespacesPkgString); 53 | Assert.IsTrue(0 == shellService.LoadPackage(ref packageGuid, out package)); 54 | Assert.IsNotNull(package, "Package failed to load"); 55 | 56 | }); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/RemoveTrailingWhitespaces_IntegrationTests/RemoveTrailingWhitespaces_IntegrationTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 2.0 7 | {C96EE774-20BF-48A2-AE59-09A0F2709808} 8 | Library 9 | Properties 10 | RemoveTrailingWhitespaces_IntegrationTests 11 | RemoveTrailingWhitespaces_IntegrationTests 12 | v4.6 13 | 512 14 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | Key.snk 36 | 37 | 38 | true 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | true 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | {33B06D46-74C6-4F60-BB43-B3FDB8BDCEB0} 77 | RemoveTrailingWhitespaces 78 | 79 | 80 | 81 | 88 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/RemoveTrailingWhitespaces_IntegrationTests/SignOff-Tests/CPPProjectTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using Microsoft.VsSDK.IntegrationTestLibrary; 6 | using Microsoft.VSSDK.Tools.VsIdeTesting; 7 | using EnvDTE; 8 | using System.IO; 9 | 10 | namespace RemoveTrailingWhitespaces_IntegrationTests.IntegrationTests 11 | { 12 | [TestClass] 13 | public class CPPProjectTests 14 | { 15 | #region fields 16 | private delegate void ThreadInvoker(); 17 | private TestContext _testContext; 18 | #endregion 19 | 20 | #region properties 21 | /// 22 | ///Gets or sets the test context which provides 23 | ///information about and functionality for the current test run. 24 | /// 25 | public TestContext TestContext 26 | { 27 | get { return _testContext; } 28 | set { _testContext = value; } 29 | } 30 | #endregion 31 | 32 | #region ctors 33 | public CPPProjectTests() 34 | { 35 | } 36 | #endregion 37 | 38 | #region Additional test attributes 39 | // 40 | // You can use the following additional attributes as you write your tests: 41 | // 42 | // Use ClassInitialize to run code before running the first test in the class 43 | // [ClassInitialize()] 44 | // public static void MyClassInitialize(TestContext testContext) { } 45 | // 46 | // Use ClassCleanup to run code after all tests in a class have run 47 | // [ClassCleanup()] 48 | // public static void MyClassCleanup() { } 49 | // 50 | // Use TestInitialize to run code before running each test 51 | // [TestInitialize()] 52 | // public void MyTestInitialize() { } 53 | // 54 | // Use TestCleanup to run code after each test has run 55 | // [TestCleanup()] 56 | // public void MyTestCleanup() { } 57 | // 58 | #endregion 59 | 60 | [HostType("VS IDE")] 61 | [TestMethod] 62 | public void CPPWinformsApplication() 63 | { 64 | UIThreadInvoker.Invoke((ThreadInvoker)delegate() 65 | { 66 | //Solution and project creation parameters 67 | string solutionName = "CPPWinApp"; 68 | string projectName = "CPPWinApp"; 69 | 70 | //Template parameters 71 | string projectType = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"; 72 | string projectTemplateName = Path.Combine("vcNet", "mc++appwiz.vsz"); 73 | 74 | string itemTemplateName = "newc++file.cpp"; 75 | string newFileName = "Test.cpp"; 76 | 77 | DTE dte = (DTE)VsIdeTestHostContext.ServiceProvider.GetService(typeof(DTE)); 78 | 79 | TestUtils testUtils = new TestUtils(); 80 | 81 | testUtils.CreateEmptySolution(TestContext.TestDir, solutionName); 82 | Assert.AreEqual(0, testUtils.ProjectCount()); 83 | 84 | //Add new CPP Windows application project to existing solution 85 | string solutionDirectory = Directory.GetParent(dte.Solution.FullName).FullName; 86 | string projectDirectory = TestUtils.GetNewDirectoryName(solutionDirectory, projectName); 87 | string projectTemplatePath = Path.Combine(dte.Solution.get_TemplatePath(projectType), projectTemplateName); 88 | Assert.IsTrue(File.Exists(projectTemplatePath), string.Format("Could not find template file: {0}", projectTemplatePath)); 89 | dte.Solution.AddFromTemplate(projectTemplatePath, projectDirectory, projectName, false); 90 | 91 | //Verify that the new project has been added to the solution 92 | Assert.AreEqual(1, testUtils.ProjectCount()); 93 | 94 | //Get the project 95 | Project project = dte.Solution.Item(1); 96 | Assert.IsNotNull(project); 97 | Assert.IsTrue(string.Compare(project.Name, projectName, StringComparison.InvariantCultureIgnoreCase) == 0); 98 | 99 | //Verify Adding new code file to project 100 | string newItemTemplatePath = Path.Combine(dte.Solution.ProjectItemsTemplatePath(projectType), itemTemplateName); 101 | Assert.IsTrue(File.Exists(newItemTemplatePath)); 102 | ProjectItem item = project.ProjectItems.AddFromTemplate(newItemTemplatePath, newFileName); 103 | Assert.IsNotNull(item); 104 | 105 | }); 106 | } 107 | 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/RemoveTrailingWhitespaces_IntegrationTests/SignOff-Tests/CSharpProjectTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using Microsoft.VsSDK.IntegrationTestLibrary; 6 | using Microsoft.VSSDK.Tools.VsIdeTesting; 7 | 8 | namespace RemoveTrailingWhitespaces_IntegrationTests.IntegrationTests 9 | { 10 | [TestClass] 11 | public class CSharpProjectTests 12 | { 13 | #region fields 14 | private delegate void ThreadInvoker(); 15 | private TestContext _testContext; 16 | #endregion 17 | 18 | #region properties 19 | /// 20 | ///Gets or sets the test context which provides 21 | ///information about and functionality for the current test run. 22 | /// 23 | public TestContext TestContext 24 | { 25 | get { return _testContext; } 26 | set { _testContext = value; } 27 | } 28 | #endregion 29 | 30 | #region ctors 31 | public CSharpProjectTests() 32 | { 33 | } 34 | #endregion 35 | 36 | #region Additional test attributes 37 | // 38 | // You can use the following additional attributes as you write your tests: 39 | // 40 | // Use ClassInitialize to run code before running the first test in the class 41 | // [ClassInitialize()] 42 | // public static void MyClassInitialize(TestContext testContext) { } 43 | // 44 | // Use ClassCleanup to run code after all tests in a class have run 45 | // [ClassCleanup()] 46 | // public static void MyClassCleanup() { } 47 | // 48 | // Use TestInitialize to run code before running each test 49 | // [TestInitialize()] 50 | // public void MyTestInitialize() { } 51 | // 52 | // Use TestCleanup to run code after each test has run 53 | // [TestCleanup()] 54 | // public void MyTestCleanup() { } 55 | // 56 | #endregion 57 | 58 | [TestMethod] 59 | [HostType("VS IDE")] 60 | public void WinformsApplication() 61 | { 62 | UIThreadInvoker.Invoke((ThreadInvoker)delegate() 63 | { 64 | TestUtils testUtils = new TestUtils(); 65 | 66 | testUtils.CreateEmptySolution(TestContext.TestDir, "CSWinApp"); 67 | Assert.AreEqual(0, testUtils.ProjectCount()); 68 | 69 | //Create Winforms application project 70 | //TestUtils.CreateProjectFromTemplate("MyWindowsApp", "Windows Application", "CSharp", false); 71 | //Assert.AreEqual(1, TestUtils.ProjectCount()); 72 | 73 | //TODO Verify that we can debug launch the application 74 | 75 | //TODO Set Break point and verify that will hit 76 | 77 | //TODO Verify Adding new project item to project 78 | 79 | }); 80 | } 81 | 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/RemoveTrailingWhitespaces_IntegrationTests/SignOff-Tests/SolutionTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | using Microsoft.VisualStudio.Shell.Interop; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using Microsoft.VSSDK.Tools.VsIdeTesting; 7 | using EnvDTE; 8 | using System.IO; 9 | using Microsoft.VsSDK.IntegrationTestLibrary; 10 | 11 | 12 | namespace RemoveTrailingWhitespaces_IntegrationTests.IntegrationTests 13 | { 14 | [TestClass] 15 | public class SolutionTests 16 | { 17 | #region fields 18 | private delegate void ThreadInvoker(); 19 | private TestContext _testContext; 20 | #endregion 21 | 22 | #region properties 23 | /// 24 | ///Gets or sets the test context which provides 25 | ///information about and functionality for the current test run. 26 | /// 27 | public TestContext TestContext 28 | { 29 | get { return _testContext; } 30 | set { _testContext = value; } 31 | } 32 | #endregion 33 | 34 | 35 | #region ctors 36 | public SolutionTests() 37 | { 38 | } 39 | 40 | #endregion 41 | 42 | [TestMethod] 43 | [HostType("VS IDE")] 44 | public void CreateEmptySolution() 45 | { 46 | UIThreadInvoker.Invoke((ThreadInvoker)delegate() 47 | { 48 | TestUtils testUtils = new TestUtils(); 49 | testUtils.CloseCurrentSolution(__VSSLNSAVEOPTIONS.SLNSAVEOPT_NoSave); 50 | testUtils.CreateEmptySolution(TestContext.TestDir, "EmptySolution"); 51 | }); 52 | } 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/RemoveTrailingWhitespaces_IntegrationTests/SignOff-Tests/VBProjectTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using Microsoft.VsSDK.IntegrationTestLibrary; 6 | using Microsoft.VSSDK.Tools.VsIdeTesting; 7 | using EnvDTE; 8 | 9 | namespace RemoveTrailingWhitespaces_IntegrationTests.IntegrationTests 10 | { 11 | [TestClass] 12 | public class VisualBasicProjectTests 13 | { 14 | #region fields 15 | private delegate void ThreadInvoker(); 16 | private TestContext _testContext; 17 | #endregion 18 | 19 | #region properties 20 | /// 21 | ///Gets or sets the test context which provides 22 | ///information about and functionality for the current test run. 23 | /// 24 | public TestContext TestContext 25 | { 26 | get { return _testContext; } 27 | set { _testContext = value; } 28 | } 29 | #endregion 30 | 31 | #region ctors 32 | public VisualBasicProjectTests() 33 | { 34 | } 35 | #endregion 36 | 37 | #region Additional test attributes 38 | // 39 | // You can use the following additional attributes as you write your tests: 40 | // 41 | // Use ClassInitialize to run code before running the first test in the class 42 | // [ClassInitialize()] 43 | // public static void MyClassInitialize(TestContext testContext) { } 44 | // 45 | // Use ClassCleanup to run code after all tests in a class have run 46 | // [ClassCleanup()] 47 | // public static void MyClassCleanup() { } 48 | // 49 | // Use TestInitialize to run code before running each test 50 | // [TestInitialize()] 51 | // public void MyTestInitialize() { } 52 | // 53 | // Use TestCleanup to run code after each test has run 54 | // [TestCleanup()] 55 | // public void MyTestCleanup() { } 56 | // 57 | #endregion 58 | 59 | [HostType("VS IDE")] 60 | [TestMethod] 61 | public void VBWinformsApplication() 62 | { 63 | UIThreadInvoker.Invoke((ThreadInvoker)delegate() 64 | { 65 | //Solution and project creation parameters 66 | string solutionName = "VBWinApp"; 67 | string projectName = "VBWinApp"; 68 | 69 | //Template parameters 70 | string language = "VisualBasic"; 71 | string projectTemplateName = "WindowsApplication.Zip"; 72 | string itemTemplateName = "CodeFile.zip"; 73 | string newFileName = "Test.vb"; 74 | 75 | DTE dte = (DTE)VsIdeTestHostContext.ServiceProvider.GetService(typeof(DTE)); 76 | 77 | TestUtils testUtils = new TestUtils(); 78 | 79 | testUtils.CreateEmptySolution(TestContext.TestDir, solutionName); 80 | Assert.AreEqual(0, testUtils.ProjectCount()); 81 | 82 | //Add new Windows application project to existing solution 83 | testUtils.CreateProjectFromTemplate(projectName, projectTemplateName, language, false); 84 | 85 | //Verify that the new project has been added to the solution 86 | Assert.AreEqual(1, testUtils.ProjectCount()); 87 | 88 | //Get the project 89 | Project project = dte.Solution.Item(1); 90 | Assert.IsNotNull(project); 91 | Assert.IsTrue(string.Compare(project.Name, projectName, StringComparison.InvariantCultureIgnoreCase) == 0); 92 | 93 | //Verify Adding new code file to project 94 | ProjectItem newCodeFileItem = testUtils.AddNewItemFromVsTemplate(project.ProjectItems, itemTemplateName, language, newFileName); 95 | Assert.IsNotNull(newCodeFileItem, "Could not create new project item"); 96 | 97 | }); 98 | } 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/RemoveTrailingWhitespaces_UnitTests/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Predelnik/RemoveTrailingWhitespaces/934f165bc70de78c66832c4957b65768b8736cb0/RemoveTrailingWhitespaces/RemoveTrailingWhitespaces_UnitTests/Key.snk -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/RemoveTrailingWhitespaces_UnitTests/PackageTest.cs: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | This code is licensed under the Visual Studio SDK license terms. 5 | THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF 6 | ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY 7 | IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR 8 | PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. 9 | 10 | ***************************************************************************/ 11 | 12 | using System; 13 | using System.Collections; 14 | using System.Text; 15 | using System.Reflection; 16 | using Microsoft.VsSDK.UnitTestLibrary; 17 | using Microsoft.VisualStudio.Shell.Interop; 18 | using Microsoft.VisualStudio.TestTools.UnitTesting; 19 | using Predelnik.RemoveTrailingWhitespaces; 20 | 21 | namespace RemoveTrailingWhitespaces_UnitTests 22 | { 23 | [TestClass()] 24 | public class PackageTest 25 | { 26 | [TestMethod()] 27 | public void CreateInstance() 28 | { 29 | RemoveTrailingWhitespacesPackage package = new RemoveTrailingWhitespacesPackage(); 30 | } 31 | 32 | [TestMethod()] 33 | public void IsIVsPackage() 34 | { 35 | RemoveTrailingWhitespacesPackage package = new RemoveTrailingWhitespacesPackage(); 36 | Assert.IsNotNull(package as IVsPackage, "The object does not implement IVsPackage"); 37 | } 38 | 39 | [TestMethod()] 40 | public void SetSite() 41 | { 42 | // Create the package 43 | IVsPackage package = new RemoveTrailingWhitespacesPackage() as IVsPackage; 44 | Assert.IsNotNull(package, "The object does not implement IVsPackage"); 45 | 46 | // Create a basic service provider 47 | OleServiceProvider serviceProvider = OleServiceProvider.CreateOleServiceProviderWithBasicServices(); 48 | 49 | // Site the package 50 | Assert.AreEqual(0, package.SetSite(serviceProvider), "SetSite did not return S_OK"); 51 | 52 | // Unsite the package 53 | Assert.AreEqual(0, package.SetSite(null), "SetSite(null) did not return S_OK"); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/RemoveTrailingWhitespaces_UnitTests/RemoveTrailingWhitespaces_UnitTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 2.0 7 | {81EA4FD4-B565-4F47-A7CF-7D4580B0186A} 8 | Library 9 | Properties 10 | RemoveTrailingWhitespaces_UnitTests 11 | RemoveTrailingWhitespaces_UnitTests 12 | v4.6 13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | Key.snk 35 | 36 | 37 | true 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | true 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | {33B06D46-74C6-4F60-BB43-B3FDB8BDCEB0} 73 | RemoveTrailingWhitespaces 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Predelnik.RemoveTrailingWhitespaces { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Predelnik.RemoveTrailingWhitespaces.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 11 | 12 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | text/microsoft-resx 119 | 120 | 121 | 2.0 122 | 123 | 124 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 125 | 126 | 127 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 128 | 129 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/Resources/menuicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Predelnik/RemoveTrailingWhitespaces/934f165bc70de78c66832c4957b65768b8736cb0/RemoveTrailingWhitespaces/Resources/menuicon.png -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/Resources/packageicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Predelnik/RemoveTrailingWhitespaces/934f165bc70de78c66832c4957b65768b8736cb0/RemoveTrailingWhitespaces/Resources/packageicon.png -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/Resources/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Predelnik/RemoveTrailingWhitespaces/934f165bc70de78c66832c4957b65768b8736cb0/RemoveTrailingWhitespaces/Resources/preview.png -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/VSPackage.resx: -------------------------------------------------------------------------------- 1 |  2 | 12 | 13 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | text/microsoft-resx 120 | 121 | 122 | 2.0 123 | 124 | 125 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 126 | 127 | 128 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 129 | 130 | 131 | 132 | RemoveTrailingWhitespaces 133 | 134 | 135 | Trailing whitespace removal tool. Removes either manually or on file save. 136 | 137 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /RemoveTrailingWhitespaces/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | RemoveTrailingWhitespaces 6 | Trailing whitespace removal tool. Removes either manually or on file save. 7 | LICENSE.txt 8 | Resources\packageicon.png 9 | Resources\preview.png 10 | Formatting, Trailing Whitespaces, Utility 11 | 12 | 13 | 14 | amd64 15 | 16 | 17 | amd64 18 | 19 | 20 | amd64 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /UnitTests.testsettings: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | This test run configuration is used for running the unit tests 9 | 10 | --------------------------------------------------------------------------------