├── .gitattributes ├── .gitignore ├── .nuget ├── NuGet.Config ├── NuGet.exe └── NuGet.targets ├── Data ├── EditorUtils2010.nuspec ├── EditorUtils2012.nuspec ├── EditorUtils2013.nuspec └── EditorUtils2015.nuspec ├── Deploy.ps1 ├── EditorUtils.settings ├── EditorUtils.sln ├── Key.publickey ├── Key.snk ├── License.txt ├── ReadMe.md ├── References ├── Vs2010 │ ├── App.config │ ├── Microsoft.VisualStudio.CoreUtility.dll │ ├── Microsoft.VisualStudio.Language.Intellisense.dll │ ├── Microsoft.VisualStudio.Language.StandardClassification.dll │ ├── Microsoft.VisualStudio.Text.Data.dll │ ├── Microsoft.VisualStudio.Text.Logic.dll │ ├── Microsoft.VisualStudio.Text.UI.Wpf.dll │ └── Microsoft.VisualStudio.Text.UI.dll ├── Vs2012 │ ├── App.config │ ├── Microsoft.VisualStudio.CoreUtility.dll │ ├── Microsoft.VisualStudio.Language.Intellisense.dll │ ├── Microsoft.VisualStudio.Language.StandardClassification.dll │ ├── Microsoft.VisualStudio.Text.Data.dll │ ├── Microsoft.VisualStudio.Text.Logic.dll │ ├── Microsoft.VisualStudio.Text.UI.Wpf.dll │ └── Microsoft.VisualStudio.Text.UI.dll ├── Vs2013 │ ├── App.config │ ├── Microsoft.VisualStudio.CoreUtility.dll │ ├── Microsoft.VisualStudio.Language.Intellisense.dll │ ├── Microsoft.VisualStudio.Language.StandardClassification.dll │ ├── Microsoft.VisualStudio.Text.Data.dll │ ├── Microsoft.VisualStudio.Text.Logic.dll │ ├── Microsoft.VisualStudio.Text.UI.Wpf.dll │ └── Microsoft.VisualStudio.Text.UI.dll └── Vs2015 │ ├── App.config │ ├── Microsoft.VisualStudio.CoreUtility.dll │ ├── Microsoft.VisualStudio.Language.Intellisense.dll │ ├── Microsoft.VisualStudio.Language.StandardClassification.dll │ ├── Microsoft.VisualStudio.Text.Data.dll │ ├── Microsoft.VisualStudio.Text.Logic.dll │ ├── Microsoft.VisualStudio.Text.UI.Wpf.dll │ └── Microsoft.VisualStudio.Text.UI.dll ├── Src ├── EditorApp │ ├── App.config │ ├── App.xaml │ ├── App.xaml.cs │ ├── EditorApp.csproj │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ └── Properties │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.resx │ │ ├── Settings.Designer.cs │ │ └── Settings.settings ├── EditorUtils.settings └── EditorUtils │ ├── AsyncTaggerSource.cs │ ├── Constants.cs │ ├── Contract.cs │ ├── EditorHost.cs │ ├── EditorHostFactory.UndoExportProvider.cs │ ├── EditorHostFactory.cs │ ├── EditorUtils.csproj │ ├── EditorUtilsFactory.cs │ ├── EditorUtilsResources.cs │ ├── EditorUtilsTrace.cs │ ├── EditorVersion.cs │ ├── Extensions.cs │ ├── IAdhocOutliner.cs │ ├── IAsyncTaggerSource.cs │ ├── IBasicTaggerSource.cs │ ├── IBasicUndoHistoryRegistry.cs │ ├── IProtectedOperations.cs │ ├── Implementation │ ├── BasicUndo │ │ ├── BasicUndoHistory.cs │ │ ├── BasicUndoHistoryRegistry.cs │ │ └── BasicUndoTransaction.cs │ ├── Tagging │ │ ├── AsyncTagger.Channel.cs │ │ ├── AsyncTagger.cs │ │ ├── BasicTagger.cs │ │ ├── Classifier.cs │ │ ├── CountedClassifier.cs │ │ ├── CountedTagger.cs │ │ ├── CountedValue.cs │ │ └── TaggerUtil.cs │ └── Utilities │ │ ├── AdhocOutliner.cs │ │ ├── EqualityUtility.cs │ │ ├── NormalizedLineRangeCollection.cs │ │ ├── ProtectedOperations.cs │ │ └── ReadOnlyStack.cs │ ├── Key.snk │ ├── LineRange.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── SnapshotLineRange.cs │ └── UsedInBackgroundThreadAttribute.cs ├── Test ├── Cats │ ├── CatTagger.cs │ ├── CatTaggerFormat.cs │ ├── CatTaggerProvider.cs │ ├── Cats.csproj │ ├── Contstants.cs │ ├── DogClassifier.cs │ ├── DogClassifierProvider.cs │ ├── DogTaggerFormat.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── source.extension.vsixmanifest ├── EditorUtils.settings ├── EditorUtilsTest │ ├── AdhocOutlinerTest.cs │ ├── App.config │ ├── AsyncTaggerTest.cs │ ├── BasicTaggerTest.cs │ ├── BasicUndoHistoryTest.cs │ ├── ChannelTest.cs │ ├── ClassifierTest.cs │ ├── CountedClassifierTest.cs │ ├── CountedTaggerTest.cs │ ├── EditorHostFactoryTest.cs │ ├── EditorHostTest.cs │ ├── EditorUtilsTest.csproj │ ├── EqualityUtil.cs │ ├── Extensions.cs │ ├── ExtensionsTest.cs │ ├── Key.snk │ ├── LineRangeTest.cs │ ├── MemoryLeakTest.cs │ ├── MockFactory.cs │ ├── NormalizedLineRangeCollectionTest.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── ProtectedOperationsTest.cs │ ├── ReadOnlyStackTest.cs │ ├── SnapshotLineRangeTest.cs │ ├── TaggerCommonTest.cs │ ├── TaggerUtilTest.cs │ ├── TestUtils.cs │ ├── TestableSynchronizationContext.cs │ ├── TextTaggerSource.cs │ ├── VersioningTest.cs │ └── packages.config └── WordUnderCaret │ ├── Constants.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── WordUnderCaret.csproj │ ├── WordUnderCaret.sln │ ├── WordUnderCaretFormat.cs │ ├── WordUnderCaretTagger.cs │ ├── WordUnderCaretTaggerProvider.cs │ └── source.extension.vsixmanifest ├── Tools ├── xunit.console.clr4.x86.exe ├── xunit.console.clr4.x86.exe.config ├── xunit.runner.utility.dll └── xunit.runner.utility.xml └── appveyor.yml /.gitattributes: -------------------------------------------------------------------------------- 1 | * -text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # git-ls-files --others --exclude-from=.git/info/exclude 2 | # Lines that start with '#' are comments. 3 | # For a project mostly in C, the following would be a good set of 4 | # exclude patterns (uncomment them if you want to use them): 5 | # *.[oa] 6 | # *~ 7 | 8 | # Binary files 9 | *.exe 10 | *.dbg 11 | *.pcb 12 | *.ncb 13 | *.swp 14 | *.snk 15 | *.pdb 16 | *.msi 17 | 18 | # Project Files 19 | *.user 20 | *.suo 21 | *.db 22 | *.psess 23 | TestResult.xml 24 | 25 | # NUnit files 26 | *.VisualState.xml 27 | 28 | # Resharper 29 | _ReSharper.* 30 | 31 | # This solution uses NuGet package restore so don't check in 32 | # the actual binaries 33 | packages/ 34 | 35 | # Roslyn generated folder 36 | *.sln.ide/ 37 | 38 | # Ignore NCrunch files for the moment. Still experimenting and 39 | # don't want to put them in there just yet 40 | *ncrunchproject 41 | *ncrunchsolution 42 | *crunchsolution.cache 43 | 44 | # Directories 45 | obj/ 46 | bin/ 47 | TestResults/ 48 | Debug/ 49 | Release/ 50 | Deploy/ 51 | Samples/HighlightCommentKinds/bin 52 | Samples/HighlightCommentKinds/obj 53 | 54 | # Profiling Output 55 | *.vsp 56 | 57 | # Hacky build items 58 | EditorUtilsVsix.vsix 59 | Test/EditorUtilsTest/App.config 60 | 61 | -------------------------------------------------------------------------------- /.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/.nuget/NuGet.exe -------------------------------------------------------------------------------- /.nuget/NuGet.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildProjectDirectory)\..\ 5 | 6 | 7 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) 8 | $([System.IO.Path]::Combine($(ProjectDir), "packages.config")) 9 | $([System.IO.Path]::Combine($(SolutionDir), "packages")) 10 | 11 | 12 | $(SolutionDir).nuget 13 | packages.config 14 | $(SolutionDir)packages 15 | 16 | 17 | $(NuGetToolsPath)\nuget.exe 18 | "$(NuGetExePath)" 19 | mono --runtime=v4.0.30319 $(NuGetExePath) 20 | 21 | $(TargetDir.Trim('\\')) 22 | 23 | 24 | "" 25 | 26 | 27 | false 28 | 29 | 30 | false 31 | 32 | 33 | $(NuGetCommand) install "$(PackagesConfig)" -source $(PackageSources) -o "$(PackagesDir)" 34 | $(NuGetCommand) pack "$(ProjectPath)" -p Configuration=$(Configuration) -o "$(PackageOutputDir)" -symbols 35 | 36 | 37 | 38 | RestorePackages; 39 | $(BuildDependsOn); 40 | 41 | 42 | 43 | 44 | $(BuildDependsOn); 45 | BuildPackage; 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 57 | 58 | 61 | 62 | 63 | 64 | 66 | 67 | 70 | 71 | -------------------------------------------------------------------------------- /Data/EditorUtils2010.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | EditorUtils2010 5 | $version$ 6 | EditorUtils 2010 7 | JaredPar 8 | JaredPar 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | false 11 | Utility DLL for writing Visual Studio extensions 12 | Initial Release 13 | Copyright 2012 14 | VisualStudio; VSIX 15 | 16 | 17 | -------------------------------------------------------------------------------- /Data/EditorUtils2012.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | EditorUtils2012 5 | $version$ 6 | EditorUtils 2012 7 | JaredPar 8 | JaredPar 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | false 11 | Utility DLL for writing Visual Studio extensions (2012 and above) 12 | Initial Release 13 | Copyright 2012 14 | VisualStudio; VSIX 15 | 16 | 17 | -------------------------------------------------------------------------------- /Data/EditorUtils2013.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | EditorUtils2013 5 | $version$ 6 | EditorUtils 2013 7 | JaredPar 8 | JaredPar 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | false 11 | Utility DLL for writing Visual Studio extensions (2013 and above) 12 | Initial Release 13 | Copyright 2012 14 | VisualStudio; VSIX 15 | 16 | 17 | -------------------------------------------------------------------------------- /Data/EditorUtils2015.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | EditorUtils2015 5 | $version$ 6 | EditorUtils 2015 7 | JaredPar 8 | JaredPar 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | false 11 | Utility DLL for writing Visual Studio extensions 12 | Initial Release 13 | Copyright 2012 14 | VisualStudio; VSIX 15 | 16 | 17 | -------------------------------------------------------------------------------- /EditorUtils.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(MSBuildThisFileDirectory)References\Vs2010\App.config 6 | 7 | 8 | 9 | $(MSBuildThisFileDirectory)References\Vs2012\App.config 10 | 11 | 12 | 13 | $(MSBuildThisFileDirectory)References\Vs2013\App.config 14 | 15 | 16 | 17 | $(MSBuildThisFileDirectory)References\Vs2015\App.config 18 | 19 | 20 | 21 | $(ReferencePath);$(MSBuildThisFileDirectory)References\Vs2010 22 | 2010 23 | 10.0.0.0 24 | 4.0 25 | $(DefineConstants);VS2010 26 | 27 | 28 | 29 | $(ReferencePath);$(MSBuildThisFileDirectory)References\Vs2012 30 | 2012 31 | 11.0.0.0 32 | 4.5 33 | $(DefineConstants);VS2012 34 | 35 | 36 | 37 | $(ReferencePath);$(MSBuildThisFileDirectory)References\Vs2013 38 | 2013 39 | 12.0.0.0 40 | 4.5 41 | $(DefineConstants);VS2013 42 | 43 | 44 | 45 | $(ReferencePath);$(MSBuildThisFileDirectory)References\Vs2015 46 | 2015 47 | 14.0.0.0 48 | 4.5 49 | $(DefineConstants);VS2015 50 | 51 | 52 | 53 | 54 | 55 | False 56 | 57 | 58 | -------------------------------------------------------------------------------- /EditorUtils.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.22823.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EditorUtils", "Src\EditorUtils\EditorUtils.csproj", "{FB418222-C105-4942-8EEB-832DDCFFD89D}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EditorUtilsTest", "Test\EditorUtilsTest\EditorUtilsTest.csproj", "{BAED09B7-25D9-4DD8-8558-BAC9730BA3F3}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WordUnderCaret", "Test\WordUnderCaret\WordUnderCaret.csproj", "{620BEC49-DF9C-406A-9492-BD157BAB95BC}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cats", "Test\Cats\Cats.csproj", "{1B8425A4-6DBB-4227-92E7-EC8CC379DA63}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EditorApp", "Src\EditorApp\EditorApp.csproj", "{EE06DCE3-203C-4503-9AFA-0A419F43F2B3}" 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Release|Any CPU = Release|Any CPU 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {FB418222-C105-4942-8EEB-832DDCFFD89D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {FB418222-C105-4942-8EEB-832DDCFFD89D}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {FB418222-C105-4942-8EEB-832DDCFFD89D}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {FB418222-C105-4942-8EEB-832DDCFFD89D}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {BAED09B7-25D9-4DD8-8558-BAC9730BA3F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {BAED09B7-25D9-4DD8-8558-BAC9730BA3F3}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {BAED09B7-25D9-4DD8-8558-BAC9730BA3F3}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {BAED09B7-25D9-4DD8-8558-BAC9730BA3F3}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {620BEC49-DF9C-406A-9492-BD157BAB95BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {620BEC49-DF9C-406A-9492-BD157BAB95BC}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {620BEC49-DF9C-406A-9492-BD157BAB95BC}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {620BEC49-DF9C-406A-9492-BD157BAB95BC}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {1B8425A4-6DBB-4227-92E7-EC8CC379DA63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {1B8425A4-6DBB-4227-92E7-EC8CC379DA63}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {1B8425A4-6DBB-4227-92E7-EC8CC379DA63}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {1B8425A4-6DBB-4227-92E7-EC8CC379DA63}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {EE06DCE3-203C-4503-9AFA-0A419F43F2B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {EE06DCE3-203C-4503-9AFA-0A419F43F2B3}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {EE06DCE3-203C-4503-9AFA-0A419F43F2B3}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {EE06DCE3-203C-4503-9AFA-0A419F43F2B3}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | EndGlobal 47 | -------------------------------------------------------------------------------- /Key.publickey: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/Key.publickey -------------------------------------------------------------------------------- /Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/Key.snk -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | Copyright 2012 Jared Parsons 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | ### Editor Utilities Library 2 | 3 | This is a utility library to be used with VSIX projects. It abstracts away many of the problem areas of the Visual Studio API into simple to use types. 4 | 5 | This project is available as a set of NuGet packages. Which one to choose depends on which version of Visual Studio you are targetting 6 | 7 | - EditorUtils: VS 2010 and above 8 | - EditorUtils2012: VS 2012 and above 9 | - EditorUtils2013: VS 2013 and above 10 | 11 | If you don't target VS 2010 then use EditorUtils2012 instead of EditorUtils. This avoids some tooling issues in the Visual Studio build around missing references 12 | 13 | AppVeyor: [![Build status](https://ci.appveyor.com/api/projects/status/vbgvb4ixgwbld943)](https://ci.appveyor.com/project/jaredpar/editorutils) 14 | 15 | ### Features 16 | 17 | EditorUtils is a collection of abstractions for building VSIX projects. It is broken down into the following feature areas 18 | 19 | #### Tagging 20 | 21 | Syntax highlighting, brace completion, intra text adornments, etc ... are all features provided by the [ITagger](http://msdn.microsoft.com/en-us/library/dd885020.aspx) interface. The interface is simple enough but the rules and scenarios around this interface are complex and largely undocumented. EditorUtils abstracts away much of this complexity by providing a much simpler interface IBasicTaggerSource. 22 | 23 | #### Async Tagging 24 | 25 | All [ITagger](http://msdn.microsoft.com/en-us/library/dd885020.aspx) implementations are driven through the UI thread of Visual Studio. Any delay in tagger implementations is felt immediately by the user. Simple taggers can afford to be synchronous but more complex taggers must be asynchronous in order to keep the UI responsive. EditorUtils provides the `IAsyncTaggerSource`to abstract away all of the complexities of async tagging. 26 | 27 | ```csharp 28 | class MyAsyncTaggerSource : IAsyncTaggerSource { ... } 29 | 30 | MyAsyncTaggerSource myAsyncTaggerSource = new MyAsyncTaggerSource(); 31 | ITagger tagger = EditorUtilsFactory.CreateAsyncTaggerRaw(myAsyncTaggerSource); 32 | ``` 33 | 34 | 35 | #### Editor Hosting 36 | 37 | At its core the Visual Studio editor is just a WPF control that is completely independent of Visual Studio itself. The ability to host this control outside of Visual Studio is critical to thorough testing of VSIX plugins. EditorUtils makes this extremely easy to do with the EditorHost feature. 38 | 39 | ```csharp 40 | var editorHost = new EditorHost(); 41 | var textView = editorHost.CreateTextView(); 42 | ``` 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /References/Vs2010/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /References/Vs2010/Microsoft.VisualStudio.CoreUtility.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2010/Microsoft.VisualStudio.CoreUtility.dll -------------------------------------------------------------------------------- /References/Vs2010/Microsoft.VisualStudio.Language.Intellisense.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2010/Microsoft.VisualStudio.Language.Intellisense.dll -------------------------------------------------------------------------------- /References/Vs2010/Microsoft.VisualStudio.Language.StandardClassification.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2010/Microsoft.VisualStudio.Language.StandardClassification.dll -------------------------------------------------------------------------------- /References/Vs2010/Microsoft.VisualStudio.Text.Data.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2010/Microsoft.VisualStudio.Text.Data.dll -------------------------------------------------------------------------------- /References/Vs2010/Microsoft.VisualStudio.Text.Logic.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2010/Microsoft.VisualStudio.Text.Logic.dll -------------------------------------------------------------------------------- /References/Vs2010/Microsoft.VisualStudio.Text.UI.Wpf.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2010/Microsoft.VisualStudio.Text.UI.Wpf.dll -------------------------------------------------------------------------------- /References/Vs2010/Microsoft.VisualStudio.Text.UI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2010/Microsoft.VisualStudio.Text.UI.dll -------------------------------------------------------------------------------- /References/Vs2012/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 | -------------------------------------------------------------------------------- /References/Vs2012/Microsoft.VisualStudio.CoreUtility.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2012/Microsoft.VisualStudio.CoreUtility.dll -------------------------------------------------------------------------------- /References/Vs2012/Microsoft.VisualStudio.Language.Intellisense.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2012/Microsoft.VisualStudio.Language.Intellisense.dll -------------------------------------------------------------------------------- /References/Vs2012/Microsoft.VisualStudio.Language.StandardClassification.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2012/Microsoft.VisualStudio.Language.StandardClassification.dll -------------------------------------------------------------------------------- /References/Vs2012/Microsoft.VisualStudio.Text.Data.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2012/Microsoft.VisualStudio.Text.Data.dll -------------------------------------------------------------------------------- /References/Vs2012/Microsoft.VisualStudio.Text.Logic.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2012/Microsoft.VisualStudio.Text.Logic.dll -------------------------------------------------------------------------------- /References/Vs2012/Microsoft.VisualStudio.Text.UI.Wpf.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2012/Microsoft.VisualStudio.Text.UI.Wpf.dll -------------------------------------------------------------------------------- /References/Vs2012/Microsoft.VisualStudio.Text.UI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2012/Microsoft.VisualStudio.Text.UI.dll -------------------------------------------------------------------------------- /References/Vs2013/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 | -------------------------------------------------------------------------------- /References/Vs2013/Microsoft.VisualStudio.CoreUtility.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2013/Microsoft.VisualStudio.CoreUtility.dll -------------------------------------------------------------------------------- /References/Vs2013/Microsoft.VisualStudio.Language.Intellisense.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2013/Microsoft.VisualStudio.Language.Intellisense.dll -------------------------------------------------------------------------------- /References/Vs2013/Microsoft.VisualStudio.Language.StandardClassification.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2013/Microsoft.VisualStudio.Language.StandardClassification.dll -------------------------------------------------------------------------------- /References/Vs2013/Microsoft.VisualStudio.Text.Data.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2013/Microsoft.VisualStudio.Text.Data.dll -------------------------------------------------------------------------------- /References/Vs2013/Microsoft.VisualStudio.Text.Logic.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2013/Microsoft.VisualStudio.Text.Logic.dll -------------------------------------------------------------------------------- /References/Vs2013/Microsoft.VisualStudio.Text.UI.Wpf.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2013/Microsoft.VisualStudio.Text.UI.Wpf.dll -------------------------------------------------------------------------------- /References/Vs2013/Microsoft.VisualStudio.Text.UI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2013/Microsoft.VisualStudio.Text.UI.dll -------------------------------------------------------------------------------- /References/Vs2015/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 | -------------------------------------------------------------------------------- /References/Vs2015/Microsoft.VisualStudio.CoreUtility.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2015/Microsoft.VisualStudio.CoreUtility.dll -------------------------------------------------------------------------------- /References/Vs2015/Microsoft.VisualStudio.Language.Intellisense.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2015/Microsoft.VisualStudio.Language.Intellisense.dll -------------------------------------------------------------------------------- /References/Vs2015/Microsoft.VisualStudio.Language.StandardClassification.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2015/Microsoft.VisualStudio.Language.StandardClassification.dll -------------------------------------------------------------------------------- /References/Vs2015/Microsoft.VisualStudio.Text.Data.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2015/Microsoft.VisualStudio.Text.Data.dll -------------------------------------------------------------------------------- /References/Vs2015/Microsoft.VisualStudio.Text.Logic.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2015/Microsoft.VisualStudio.Text.Logic.dll -------------------------------------------------------------------------------- /References/Vs2015/Microsoft.VisualStudio.Text.UI.Wpf.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2015/Microsoft.VisualStudio.Text.UI.Wpf.dll -------------------------------------------------------------------------------- /References/Vs2015/Microsoft.VisualStudio.Text.UI.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/References/Vs2015/Microsoft.VisualStudio.Text.UI.dll -------------------------------------------------------------------------------- /Src/EditorApp/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Src/EditorApp/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Src/EditorApp/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace EditorApp 10 | { 11 | /// 12 | /// Interaction logic for App.xaml 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Src/EditorApp/EditorApp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | {EE06DCE3-203C-4503-9AFA-0A419F43F2B3} 9 | WinExe 10 | Properties 11 | EditorApp 12 | EditorApp 13 | v4.5 14 | 512 15 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 16 | 4 17 | 18 | 19 | AnyCPU 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | AnyCPU 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | MSBuild:Compile 59 | Designer 60 | 61 | 62 | MSBuild:Compile 63 | Designer 64 | 65 | 66 | App.xaml 67 | Code 68 | 69 | 70 | MainWindow.xaml 71 | Code 72 | 73 | 74 | 75 | 76 | Code 77 | 78 | 79 | True 80 | True 81 | Resources.resx 82 | 83 | 84 | True 85 | Settings.settings 86 | True 87 | 88 | 89 | ResXFileCodeGenerator 90 | Resources.Designer.cs 91 | 92 | 93 | SettingsSingleFileGenerator 94 | Settings.Designer.cs 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | {fb418222-c105-4942-8eeb-832ddcffd89d} 104 | EditorUtils 105 | 106 | 107 | 108 | 115 | -------------------------------------------------------------------------------- /Src/EditorApp/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Src/EditorApp/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | using EditorUtils; 16 | 17 | namespace EditorApp 18 | { 19 | /// 20 | /// Interaction logic for MainWindow.xaml 21 | /// 22 | public partial class MainWindow : Window 23 | { 24 | public MainWindow() 25 | { 26 | InitializeComponent(); 27 | 28 | var editorHostFactory = new EditorHostFactory(EditorVersion.Vs2015); 29 | var editorHost = editorHostFactory.CreateEditorHost(); 30 | 31 | var textBuffer = editorHost.TextBufferFactoryService.CreateTextBuffer(); 32 | textBuffer.Insert(0, "Hello Editor"); 33 | 34 | var wpfTextView = editorHost.TextEditorFactoryService.CreateTextView(textBuffer); 35 | var wpfTextViewHost = editorHost.TextEditorFactoryService.CreateTextViewHost(wpfTextView, setFocus: true); 36 | Content = wpfTextViewHost.HostControl; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Src/EditorApp/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 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("EditorApp")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("EditorApp")] 15 | [assembly: AssemblyCopyright("Copyright © 2015")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /Src/EditorApp/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.0 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 EditorApp.Properties 12 | { 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", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Returns the cached ResourceManager instance used by this class. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("EditorApp.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Overrides the current thread's CurrentUICulture property for all 56 | /// resource lookups using this strongly typed resource class. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Src/EditorApp/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.0 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 EditorApp.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Src/EditorApp/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Src/EditorUtils.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Src/EditorUtils/AsyncTaggerSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Threading; 5 | using Microsoft.VisualStudio.Text; 6 | using Microsoft.VisualStudio.Text.Editor; 7 | using Microsoft.VisualStudio.Text.Tagging; 8 | 9 | namespace EditorUtils 10 | { 11 | public abstract class AsyncTaggerSource : IAsyncTaggerSource 12 | where TTag : ITag 13 | { 14 | private readonly ITextView _textViewOptional; 15 | private readonly ITextBuffer _textBuffer; 16 | private event EventHandler _changedEvent; 17 | 18 | public ITextView TextViewOptional 19 | { 20 | get { return _textViewOptional; } 21 | } 22 | 23 | public ITextBuffer TextBuffer 24 | { 25 | get { return _textBuffer; } 26 | } 27 | 28 | protected AsyncTaggerSource(ITextView textView) 29 | { 30 | Contract.Requires(textView != null); 31 | _textViewOptional = textView; 32 | _textBuffer = textView.TextBuffer; 33 | } 34 | 35 | protected AsyncTaggerSource(ITextBuffer textBuffer) 36 | { 37 | Contract.Requires(textBuffer != null); 38 | _textBuffer = textBuffer; 39 | } 40 | 41 | protected void RaiseChanged() 42 | { 43 | if (_changedEvent != null) 44 | { 45 | _changedEvent(this, EventArgs.Empty); 46 | } 47 | } 48 | 49 | protected virtual bool TryGetTagsPrompt(SnapshotSpan span, out IEnumerable> tags) 50 | { 51 | tags = null; 52 | return false; 53 | } 54 | 55 | /// 56 | /// Get the data needed in the background thread from the specified SnapshotSpan. This is called on 57 | /// the main thread 58 | /// 59 | protected abstract TData GetDataForSnapshot(ITextSnapshot snapshot); 60 | 61 | /// 62 | /// Get the tags for the specified span. This is called on the background thread 63 | /// 64 | protected abstract ReadOnlyCollection> GetTagsInBackground(TData data, SnapshotSpan span, CancellationToken cancellationToken); 65 | 66 | #region IAsyncTaggerSource 67 | 68 | int? IAsyncTaggerSource.Delay 69 | { 70 | get { return Constants.DefaultAsyncDelay; } 71 | } 72 | 73 | ITextSnapshot IAsyncTaggerSource.TextSnapshot 74 | { 75 | get { return TextBuffer.CurrentSnapshot; } 76 | } 77 | 78 | ITextView IAsyncTaggerSource.TextViewOptional 79 | { 80 | get { return TextViewOptional; } 81 | } 82 | 83 | TData IAsyncTaggerSource.GetDataForSnapshot(ITextSnapshot snapshot) 84 | { 85 | return GetDataForSnapshot(snapshot); 86 | } 87 | 88 | ReadOnlyCollection> IAsyncTaggerSource.GetTagsInBackground(TData data, SnapshotSpan span, CancellationToken cancellationToken) 89 | { 90 | return GetTagsInBackground(data, span, cancellationToken); 91 | } 92 | 93 | bool IAsyncTaggerSource.TryGetTagsPrompt(SnapshotSpan span, out IEnumerable> tags) 94 | { 95 | return TryGetTagsPrompt(span, out tags); 96 | } 97 | 98 | event EventHandler IAsyncTaggerSource.Changed 99 | { 100 | add { _changedEvent += value; } 101 | remove { _changedEvent -= value; } 102 | } 103 | 104 | #endregion 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Src/EditorUtils/Constants.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace EditorUtils 3 | { 4 | public static class Constants 5 | { 6 | /// 7 | /// The version of the assembly. This must be changed every time a new version of the utility 8 | /// library is published to NuGet 9 | /// 10 | #if DEBUG 11 | internal const string AssemblyVersion = "99.0.0.0"; 12 | #else 13 | internal const string AssemblyVersion = "1.5.0.0"; 14 | #endif 15 | 16 | /// 17 | /// Standard delay for asynchronous taggers 18 | /// 19 | public const int DefaultAsyncDelay = 100; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Src/EditorUtils/Contract.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace EditorUtils 5 | { 6 | internal static class Contract 7 | { 8 | [Serializable] 9 | internal sealed class ContractException : Exception 10 | { 11 | internal ContractException() { } 12 | internal ContractException(string message) : base(message) { } 13 | internal ContractException(string message, Exception inner) : base(message, inner) { } 14 | internal ContractException( 15 | System.Runtime.Serialization.SerializationInfo info, 16 | System.Runtime.Serialization.StreamingContext context) 17 | : base(info, context) { } 18 | } 19 | 20 | internal static void Fail() 21 | { 22 | Requires(false); 23 | } 24 | 25 | internal static void Requires(bool condition) 26 | { 27 | if (!condition) 28 | { 29 | throw new ContractException(); 30 | } 31 | } 32 | 33 | [Conditional("DEBUG")] 34 | internal static void Assert(bool condition) 35 | { 36 | Requires(condition); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Src/EditorUtils/EditorHostFactory.UndoExportProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.Composition; 4 | using System.ComponentModel.Composition.Hosting; 5 | using System.ComponentModel.Composition.Primitives; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Reflection; 9 | using System.Text; 10 | using Microsoft.VisualStudio.Text.Operations; 11 | using Microsoft.Win32; 12 | 13 | namespace EditorUtils 14 | { 15 | public sealed partial class EditorHostFactory 16 | { 17 | /// 18 | /// In order to host the editor we need to provide an ITextUndoHistory export. However 19 | /// we can't simply export it from the DLL because it would conflict with Visual Studio's 20 | /// export of ITextUndoHistoryRegistry in the default scenario. This ComposablePartCatalog 21 | /// is simply here to hand export the type in the hosted scenario only 22 | /// 23 | private sealed class UndoExportProvider : ExportProvider 24 | { 25 | private readonly IBasicUndoHistoryRegistry _basicUndoHistoryRegistry; 26 | private readonly string _textUndoHistoryRegistryContractName; 27 | private readonly string _basicUndoHistoryRegistryContractName; 28 | private readonly Export _export; 29 | 30 | internal UndoExportProvider() 31 | { 32 | _textUndoHistoryRegistryContractName = AttributedModelServices.GetContractName(typeof(ITextUndoHistoryRegistry)); 33 | _basicUndoHistoryRegistryContractName = AttributedModelServices.GetContractName(typeof(IBasicUndoHistoryRegistry)); 34 | _basicUndoHistoryRegistry = EditorUtilsFactory.CreateBasicUndoHistoryRegistry(); 35 | _export = new Export(_textUndoHistoryRegistryContractName, () => _basicUndoHistoryRegistry); 36 | } 37 | 38 | protected override IEnumerable GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition) 39 | { 40 | if (definition.ContractName == _textUndoHistoryRegistryContractName || 41 | definition.ContractName == _basicUndoHistoryRegistryContractName) 42 | { 43 | yield return _export; 44 | } 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Src/EditorUtils/EditorUtilsFactory.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | using System.Linq; 4 | using EditorUtils.Implementation.Tagging; 5 | using EditorUtils.Implementation.BasicUndo; 6 | using Microsoft.VisualStudio.Text.Tagging; 7 | using Microsoft.VisualStudio.Utilities; 8 | using Microsoft.VisualStudio.Text; 9 | using System.Collections.Generic; 10 | using EditorUtils.Implementation.Utilities; 11 | using Microsoft.VisualStudio.Text.Classification; 12 | 13 | namespace EditorUtils 14 | { 15 | /// 16 | /// Importable interface which produces ITagger implementations based on sources 17 | /// 18 | public static class EditorUtilsFactory 19 | { 20 | /// 21 | /// Create an ITagger implementation for the IAsyncTaggerSource. 22 | /// 23 | public static ITagger CreateTaggerRaw(IAsyncTaggerSource asyncTaggerSource) 24 | where TTag : ITag 25 | { 26 | return new AsyncTagger(asyncTaggerSource); 27 | } 28 | 29 | /// 30 | /// Create an ITagger implementation for the IBasicTaggerSource 31 | /// 32 | public static ITagger CreateTaggerRaw(IBasicTaggerSource basicTaggerSource) 33 | where TTag : ITag 34 | { 35 | return new BasicTagger(basicTaggerSource); 36 | } 37 | 38 | /// 39 | /// Create an ITagger implementation for the IAsyncTaggerSource. This instance will be a counted 40 | /// wrapper over the single IAsyncTaggerSource represented by the specified key 41 | /// 42 | public static ITagger CreateTagger(PropertyCollection propertyCollection, object key, Func> createFunc) 43 | where TTag : ITag 44 | { 45 | return new CountedTagger( 46 | propertyCollection, 47 | key, 48 | () => new AsyncTagger(createFunc())); 49 | } 50 | 51 | /// 52 | /// Create an ITagger implementation for the IBasicTaggerSource. This instance will be a counted 53 | /// wrapper over the single IBasicTaggerSource represented by the specified key 54 | /// 55 | public static ITagger CreateTagger(PropertyCollection propertyCollection, object key, Func> createFunc) 56 | where TTag : ITag 57 | { 58 | return new CountedTagger( 59 | propertyCollection, 60 | key, 61 | () => new BasicTagger(createFunc())); 62 | } 63 | 64 | public static IClassifier CreateClassifierRaw(IBasicTaggerSource basicTaggerSource) 65 | { 66 | return new Classifier(CreateTaggerRaw(basicTaggerSource)); 67 | } 68 | 69 | public static IClassifier CreateClassifierRaw(IAsyncTaggerSource asyncTaggerSource) 70 | { 71 | return new Classifier(CreateTaggerRaw(asyncTaggerSource)); 72 | } 73 | 74 | public static IClassifier CreateClassifier(PropertyCollection propertyCollection, object key, Func> createFunc) 75 | { 76 | return new CountedClassifier( 77 | propertyCollection, 78 | key, 79 | () => CreateClassifierRaw(createFunc())); 80 | } 81 | 82 | public static IClassifier CreateClassifier(PropertyCollection propertyCollection, object key, Func> createFunc) 83 | { 84 | return new CountedClassifier( 85 | propertyCollection, 86 | key, 87 | () => CreateClassifierRaw(createFunc())); 88 | } 89 | 90 | public static IBasicUndoHistoryRegistry CreateBasicUndoHistoryRegistry() 91 | { 92 | return new BasicTextUndoHistoryRegistry(); 93 | } 94 | 95 | public static IProtectedOperations CreateProtectedOperations(IEnumerable> errorHandlers) 96 | { 97 | return new ProtectedOperations(errorHandlers); 98 | } 99 | 100 | public static IProtectedOperations CreateProtectedOperations(IEnumerable errorHandlers) 101 | { 102 | var lazyList = errorHandlers.Select(x => new Lazy(() => x)).ToList(); 103 | return new ProtectedOperations(lazyList); 104 | } 105 | 106 | /// 107 | /// Get or create the IAdhocOutliner instance for the given ITextBuffer. This return will be useless 108 | /// unless the code which calls this method exports an ITaggerProvider which proxies the return 109 | /// of GetOrCreateOutlinerTagger 110 | /// 111 | public static IAdhocOutliner GetOrCreateOutliner(ITextBuffer textBuffer) 112 | { 113 | return GetOrCreateOutlinerCore(textBuffer); 114 | } 115 | 116 | /// 117 | /// This is the ITagger implementation for IAdhocOutliner 118 | /// 119 | public static ITagger CreateOutlinerTagger(ITextBuffer textBuffer) 120 | { 121 | return CreateTagger( 122 | textBuffer.Properties, 123 | AdhocOutliner.OutlinerTaggerKey, 124 | () => GetOrCreateOutlinerCore(textBuffer)); 125 | } 126 | 127 | private static AdhocOutliner GetOrCreateOutlinerCore(ITextBuffer textBuffer) 128 | { 129 | return textBuffer.Properties.GetOrCreateSingletonProperty(AdhocOutliner.OutlinerKey, () => new AdhocOutliner(textBuffer)); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /Src/EditorUtils/EditorUtilsResources.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace EditorUtils 3 | { 4 | internal static class EditorUtilsResources 5 | { 6 | internal static string InvalidLineNumber = "Invalid Line Number"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Src/EditorUtils/EditorUtilsTrace.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace EditorUtils 4 | { 5 | public static class EditorUtilsTrace 6 | { 7 | static readonly TraceSwitch s_traceSwitch = new TraceSwitch("EditorUtils", "EditorUtils Trace") { Level = TraceLevel.Off }; 8 | 9 | public static TraceSwitch TraceSwitch 10 | { 11 | get { return s_traceSwitch; } 12 | } 13 | 14 | [Conditional("TRACE")] 15 | public static void TraceInfo(string msg) 16 | { 17 | Trace.WriteLineIf(s_traceSwitch.TraceInfo, "EditorUtils: " + msg); 18 | } 19 | 20 | [Conditional("TRACE")] 21 | public static void TraceInfo(string msg, params object[] args) 22 | { 23 | Trace.WriteLineIf(s_traceSwitch.TraceInfo, "EditorUtils: " + string.Format(msg, args)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Src/EditorUtils/EditorVersion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace EditorUtils 7 | { 8 | /// 9 | /// The supported list of editor versions 10 | /// 11 | /// These must be listed in ascending version order 12 | public enum EditorVersion 13 | { 14 | Vs2010, 15 | Vs2012, 16 | Vs2013, 17 | Vs2015, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Src/EditorUtils/IAdhocOutliner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.Text; 3 | using Microsoft.VisualStudio.Text.Tagging; 4 | using System.Collections.ObjectModel; 5 | 6 | namespace EditorUtils 7 | { 8 | public struct OutliningRegion 9 | { 10 | public readonly OutliningRegionTag Tag; 11 | public readonly SnapshotSpan Span; 12 | public readonly int Cookie; 13 | 14 | public OutliningRegion( 15 | OutliningRegionTag tag, 16 | SnapshotSpan span, 17 | int cookie) 18 | { 19 | Tag = tag; 20 | Span = span; 21 | Cookie = cookie; 22 | } 23 | } 24 | 25 | /// 26 | /// Allows callers to create outlining regions over arbitrary SnapshotSpan values 27 | /// 28 | public interface IAdhocOutliner 29 | { 30 | /// 31 | /// Get the ITextBuffer associated with this instance 32 | /// 33 | ITextBuffer TextBuffer { get; } 34 | 35 | /// 36 | /// Get all of the regions in the given ITextSnapshot 37 | /// 38 | ReadOnlyCollection GetOutliningRegions(SnapshotSpan span); 39 | 40 | /// 41 | /// Create an outlining region over the given SnapshotSpan. The int value returned is 42 | /// a cookie for later deleting the region 43 | /// 44 | OutliningRegion CreateOutliningRegion(SnapshotSpan span, SpanTrackingMode spanTrackingMode, string text, string hint); 45 | 46 | /// 47 | /// Delete the previously created outlining region with the given cookie 48 | /// 49 | bool DeleteOutliningRegion(int cookie); 50 | 51 | /// 52 | /// Raised when any outlining regions change 53 | /// 54 | event EventHandler Changed; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Src/EditorUtils/IAsyncTaggerSource.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | using System.Collections.Generic; 4 | using System.Collections.ObjectModel; 5 | using System.Threading; 6 | using Microsoft.VisualStudio.Text; 7 | using Microsoft.VisualStudio.Text.Editor; 8 | using Microsoft.VisualStudio.Text.Tagging; 9 | namespace EditorUtils 10 | { 11 | /// 12 | /// A tagger source for asynchronous taggers. This interface is consumed from multiple threads 13 | /// and each method which is called on the background thread is labelled as such 14 | /// be called on any thread 15 | /// 16 | public interface IAsyncTaggerSource 17 | where TTag : ITag 18 | { 19 | /// 20 | /// Delay in milliseconds which should occur between the call to GetTags and the kicking off 21 | /// of a background task 22 | /// 23 | int? Delay { get; } 24 | 25 | /// 26 | /// The current Snapshot. 27 | /// 28 | /// Called from the main thread only 29 | /// 30 | ITextSnapshot TextSnapshot { get; } 31 | 32 | /// 33 | /// The current ITextView if this tagger is attached to a ITextView. This is an optional 34 | /// value 35 | /// 36 | /// Called from the main thread only 37 | /// 38 | ITextView TextViewOptional { get; } 39 | 40 | /// 41 | /// This method is called to gather data on the UI thread which will then be passed 42 | /// down to the background thread for processing 43 | /// 44 | /// Called from the main thread only 45 | /// 46 | TData GetDataForSnapshot(ITextSnapshot snapshot); 47 | 48 | /// 49 | /// Return the applicable tags for the given SnapshotSpan instance. This will be 50 | /// called on a background thread and should respect the provided CancellationToken 51 | /// 52 | /// Called from the background thread only 53 | /// 54 | [UsedInBackgroundThread] 55 | ReadOnlyCollection> GetTagsInBackground(TData data, SnapshotSpan span, CancellationToken cancellationToken); 56 | 57 | /// 58 | /// To prevent needless spawning of Task values the async tagger has the option 59 | /// of providing prompt data. This method should only be used when determination 60 | /// of the tokens requires no calculation. 61 | /// 62 | /// Called from the main thread only 63 | /// 64 | bool TryGetTagsPrompt(SnapshotSpan span, out IEnumerable> tags); 65 | 66 | /// 67 | /// Raised by the source when the underlying source has changed. All previously 68 | /// provided data should be considered incorrect after this event 69 | /// 70 | event EventHandler Changed; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Src/EditorUtils/IBasicTaggerSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | 6 | namespace EditorUtils 7 | { 8 | public interface IBasicTaggerSource 9 | where TTag : ITag 10 | { 11 | /// 12 | /// Get the tags for the given SnapshotSpan 13 | /// 14 | ReadOnlyCollection> GetTags(SnapshotSpan span); 15 | 16 | /// 17 | /// Raised when the source changes in some way 18 | /// 19 | event EventHandler Changed; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Src/EditorUtils/IBasicUndoHistoryRegistry.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Text.Operations; 2 | 3 | namespace EditorUtils 4 | { 5 | /// 6 | /// In certain hosted scenarios the default ITextUndoHistoryRegistry won't be 7 | /// available. This is a necessary part of editor composition though and some 8 | /// implementation needs to be provided. Importing this type will provide a 9 | /// very basic implementation 10 | /// 11 | /// This type intentionally doesn't ever export ITextUndoHistoryRegistry. Doing 12 | /// this would conflict with Visual Studios export and cause a MEF composition 13 | /// error. It's instead exposed via this interface 14 | /// 15 | /// In general this type won't be used except in testing 16 | /// 17 | public interface IBasicUndoHistoryRegistry 18 | { 19 | /// 20 | /// Get the basic implementation of the ITextUndoHistoryRegistry 21 | /// 22 | ITextUndoHistoryRegistry TextUndoHistoryRegistry { get; } 23 | 24 | /// 25 | /// Try and get the IBasicUndoHistory for the given context 26 | /// 27 | bool TryGetBasicUndoHistory(object context, out IBasicUndoHistory basicUndoHistory); 28 | } 29 | 30 | public interface IBasicUndoHistory : ITextUndoHistory 31 | { 32 | /// 33 | /// Clear out all of the state including the undo and redo stacks 34 | /// 35 | void Clear(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Src/EditorUtils/IProtectedOperations.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Threading; 3 | 4 | namespace EditorUtils 5 | { 6 | /// 7 | /// Alternate method of dispatching calls. This wraps the Dispatcher type and will 8 | /// gracefully handle dispatch errors. Without this layer exceptions coming from a 9 | /// dispatched operation will go directly to the dispatch loop and crash the host 10 | /// application 11 | /// 12 | public interface IProtectedOperations 13 | { 14 | /// 15 | /// Get an Action delegate which invokes the original action and handles any 16 | /// thrown Exceptions by passing them off the the available IExtensionErrorHandler 17 | /// values 18 | /// 19 | Action GetProtectedAction(Action action); 20 | 21 | /// 22 | /// Get an EventHandler delegate which invokes the original action and handles any 23 | /// thrown Exceptions by passing them off the the available IExtensionErrorHandler 24 | /// values 25 | /// 26 | EventHandler GetProtectedEventHandler(EventHandler eventHandler); 27 | 28 | /// 29 | /// Dispatch the given delegate for action. If it fails the editor error 30 | /// handling system will be notified 31 | /// 32 | void BeginInvoke(Action action); 33 | 34 | /// 35 | /// Dispatch the given delegate for action. If it fails the editor error 36 | /// handling system will be notified 37 | /// 38 | void BeginInvoke(Action action, DispatcherPriority dispatcherPriority); 39 | 40 | /// 41 | /// Report an Exception to the IExtensionErrorHandlers 42 | /// 43 | void Report(Exception ex); 44 | } 45 | } 46 | 47 | 48 | -------------------------------------------------------------------------------- /Src/EditorUtils/Implementation/BasicUndo/BasicUndoHistoryRegistry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using System.Runtime.CompilerServices; 4 | using Microsoft.VisualStudio.Text.Operations; 5 | 6 | namespace EditorUtils.Implementation.BasicUndo 7 | { 8 | /// 9 | /// This class is intended to be a very simple ITextUndoHistoryRegistry implementation for hosts that 10 | /// don't have a built-in undo mechanism 11 | /// 12 | internal sealed class BasicTextUndoHistoryRegistry : ITextUndoHistoryRegistry, IBasicUndoHistoryRegistry 13 | { 14 | private readonly ConditionalWeakTable _map = new ConditionalWeakTable(); 15 | 16 | internal BasicTextUndoHistoryRegistry() 17 | { 18 | 19 | } 20 | 21 | private bool TryGetHistory(object context, out IBasicUndoHistory basicUndoHistory) 22 | { 23 | return _map.TryGetValue(context, out basicUndoHistory); 24 | } 25 | 26 | #region ITextUndoHistoryRegistry 27 | 28 | /// 29 | /// Easy to implement but the Visual Studio implementation throws a NotSupportedException 30 | /// 31 | void ITextUndoHistoryRegistry.AttachHistory(object context, ITextUndoHistory history) 32 | { 33 | throw new NotSupportedException(); 34 | } 35 | 36 | ITextUndoHistory ITextUndoHistoryRegistry.GetHistory(object context) 37 | { 38 | IBasicUndoHistory history; 39 | _map.TryGetValue(context, out history); 40 | return history; 41 | } 42 | 43 | ITextUndoHistory ITextUndoHistoryRegistry.RegisterHistory(object context) 44 | { 45 | IBasicUndoHistory history; 46 | if (!_map.TryGetValue(context, out history)) 47 | { 48 | history = new BasicUndoHistory(context); 49 | _map.Add(context, history); 50 | } 51 | return history; 52 | } 53 | 54 | void ITextUndoHistoryRegistry.RemoveHistory(ITextUndoHistory history) 55 | { 56 | var basicUndoHistory = history as BasicUndoHistory; 57 | if (basicUndoHistory != null) 58 | { 59 | _map.Remove(basicUndoHistory.Context); 60 | basicUndoHistory.Clear(); 61 | } 62 | } 63 | 64 | bool ITextUndoHistoryRegistry.TryGetHistory(object context, out ITextUndoHistory history) 65 | { 66 | IBasicUndoHistory basicUndoHistory; 67 | if (TryGetHistory(context, out basicUndoHistory)) 68 | { 69 | history = basicUndoHistory; 70 | return true; 71 | } 72 | 73 | history = null; 74 | return false; 75 | } 76 | 77 | #endregion 78 | 79 | #region IBasciUndoHistoryRegistry 80 | 81 | ITextUndoHistoryRegistry IBasicUndoHistoryRegistry.TextUndoHistoryRegistry 82 | { 83 | get { return this; } 84 | } 85 | 86 | bool IBasicUndoHistoryRegistry.TryGetBasicUndoHistory(object context, out IBasicUndoHistory basicUndoHistory) 87 | { 88 | return TryGetHistory(context, out basicUndoHistory); 89 | } 90 | 91 | #endregion 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Src/EditorUtils/Implementation/BasicUndo/BasicUndoTransaction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.VisualStudio.Text.Operations; 4 | 5 | namespace EditorUtils.Implementation.BasicUndo 6 | { 7 | internal sealed class BasicUndoTransaction : ITextUndoTransaction 8 | { 9 | private readonly BasicUndoHistory _textUndoHistory; 10 | private readonly List _primitiveList = new List(); 11 | 12 | internal string Description 13 | { 14 | get; 15 | set; 16 | } 17 | 18 | internal List UndoPrimitives 19 | { 20 | get { return _primitiveList; } 21 | } 22 | 23 | internal BasicUndoTransaction(BasicUndoHistory textUndoHistory, string description) 24 | { 25 | _textUndoHistory = textUndoHistory; 26 | Description = description; 27 | } 28 | 29 | #region ITextUndoTransaction 30 | 31 | void ITextUndoTransaction.AddUndo(ITextUndoPrimitive undo) 32 | { 33 | _primitiveList.Add(undo); 34 | } 35 | 36 | /// 37 | /// Visual Studio implementation throw so duplicate here 38 | /// 39 | bool ITextUndoTransaction.CanRedo 40 | { 41 | get { throw new NotSupportedException(); } 42 | } 43 | 44 | /// 45 | /// Visual Studio implementation throw so duplicate here 46 | /// 47 | bool ITextUndoTransaction.CanUndo 48 | { 49 | get { throw new NotSupportedException(); } 50 | } 51 | 52 | ITextUndoHistory ITextUndoTransaction.History 53 | { 54 | get { return _textUndoHistory; } 55 | } 56 | 57 | IMergeTextUndoTransactionPolicy ITextUndoTransaction.MergePolicy 58 | { 59 | get; 60 | set; 61 | } 62 | 63 | ITextUndoTransaction ITextUndoTransaction.Parent 64 | { 65 | get { throw new NotSupportedException(); } 66 | } 67 | 68 | IList ITextUndoTransaction.UndoPrimitives 69 | { 70 | get { return UndoPrimitives; } 71 | } 72 | 73 | UndoTransactionState ITextUndoTransaction.State 74 | { 75 | get { throw new NotSupportedException(); } 76 | } 77 | 78 | string ITextUndoTransaction.Description 79 | { 80 | get { return Description; } 81 | set { Description = value; } 82 | } 83 | 84 | void ITextUndoTransaction.Cancel() 85 | { 86 | _textUndoHistory.OnTransactionClosed(this, didComplete: false); 87 | } 88 | 89 | void ITextUndoTransaction.Complete() 90 | { 91 | _textUndoHistory.OnTransactionClosed(this, didComplete: true); 92 | } 93 | 94 | void ITextUndoTransaction.Do() 95 | { 96 | for (var i = 0; i < _primitiveList.Count; i++) 97 | { 98 | _primitiveList[i].Do(); 99 | } 100 | } 101 | 102 | void ITextUndoTransaction.Undo() 103 | { 104 | for (var i = _primitiveList.Count - 1; i >= 0; i--) 105 | { 106 | _primitiveList[i].Undo(); 107 | } 108 | } 109 | 110 | #endregion 111 | 112 | #region IDisposable 113 | 114 | void IDisposable.Dispose() 115 | { 116 | _textUndoHistory.OnTransactionClosed(this, didComplete: false); 117 | } 118 | 119 | #endregion 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Src/EditorUtils/Implementation/Tagging/AsyncTagger.Channel.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Threading; 3 | using EditorUtils.Implementation.Utilities; 4 | namespace EditorUtils.Implementation.Tagging 5 | { 6 | internal sealed partial class AsyncTagger 7 | { 8 | /// 9 | /// This class is used to support the one way transfer of SnapshotLineRange values between 10 | /// the foreground thread of the tagger and the background processing thread. It understands 11 | /// the priority placed on the visible UI lines and will transfer those lines at a higher 12 | /// priority than normal requests 13 | /// 14 | [UsedInBackgroundThread] 15 | internal sealed class Channel 16 | { 17 | /// 18 | /// Need another type here because SnapshotLineRange is a struct and we need atomic assignment 19 | /// guarantees to use Interlocked.Exchange 20 | /// 21 | private sealed class TextViewLineRange 22 | { 23 | public readonly SnapshotLineRange LineRange; 24 | internal TextViewLineRange(SnapshotLineRange lineRange) 25 | { 26 | LineRange = lineRange; 27 | } 28 | } 29 | 30 | /// 31 | /// This is the normal request stack from the main thread. More recently requested items 32 | /// are given higher priority than older items 33 | /// 34 | private ReadOnlyStack _stack; 35 | 36 | /// 37 | /// When set this is represents the visible line range of the text view. It has the highest 38 | /// priority for the background thread 39 | /// 40 | private TextViewLineRange _textViewLineRange; 41 | 42 | /// 43 | /// Version number tracks the number of writes to the channel 44 | /// 45 | private int _version; 46 | 47 | /// 48 | /// The current state of the request stack 49 | /// 50 | internal ReadOnlyStack CurrentStack 51 | { 52 | get { return _stack; } 53 | } 54 | 55 | /// 56 | /// This number is incremented after every write to the channel. It is a hueristic only and 57 | /// not an absolute indicator. It is not set atomically with every write but instead occurs 58 | /// some time after the write. 59 | /// 60 | internal int CurrentVersion 61 | { 62 | get { return _version; } 63 | } 64 | 65 | internal Channel() 66 | { 67 | _stack = ReadOnlyStack.Empty; 68 | } 69 | 70 | internal void WriteVisibleLines(SnapshotLineRange lineRange) 71 | { 72 | var textViewLineRange = new TextViewLineRange(lineRange); 73 | Interlocked.Exchange(ref _textViewLineRange, textViewLineRange); 74 | Interlocked.Increment(ref _version); 75 | } 76 | 77 | internal void WriteNormal(SnapshotLineRange lineRange) 78 | { 79 | bool success; 80 | do 81 | { 82 | var oldStack = _stack; 83 | var newStack = _stack.Push(lineRange); 84 | success = oldStack == Interlocked.CompareExchange(ref _stack, newStack, oldStack); 85 | } while (!success); 86 | 87 | Interlocked.Increment(ref _version); 88 | } 89 | 90 | internal SnapshotLineRange? Read() 91 | { 92 | var lineRange = ReadVisibleLines(); 93 | if (lineRange.HasValue) 94 | { 95 | return lineRange; 96 | } 97 | 98 | return ReadNormal(); 99 | } 100 | 101 | private SnapshotLineRange? ReadVisibleLines() 102 | { 103 | do 104 | { 105 | var oldTextViewLineRange = _textViewLineRange; 106 | if (oldTextViewLineRange == null) 107 | { 108 | return null; 109 | } 110 | 111 | var success = oldTextViewLineRange == Interlocked.CompareExchange(ref _textViewLineRange, null, oldTextViewLineRange); 112 | if (success) 113 | { 114 | return oldTextViewLineRange.LineRange; 115 | } 116 | } 117 | while (true); 118 | } 119 | 120 | private SnapshotLineRange? ReadNormal() 121 | { 122 | do 123 | { 124 | var oldStack = _stack; 125 | if (oldStack.IsEmpty) 126 | { 127 | return null; 128 | } 129 | 130 | var newStack = _stack.Pop(); 131 | var success = oldStack == Interlocked.CompareExchange(ref _stack, newStack, oldStack); 132 | if (success) 133 | { 134 | return oldStack.Value; 135 | } 136 | } while (true); 137 | } 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /Src/EditorUtils/Implementation/Tagging/BasicTagger.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Microsoft.VisualStudio.Text; 6 | using Microsoft.VisualStudio.Text.Tagging; 7 | namespace EditorUtils.Implementation.Tagging 8 | { 9 | internal sealed class BasicTagger : ITagger, IDisposable 10 | where TTag : ITag 11 | { 12 | private readonly IBasicTaggerSource _basicTaggerSource; 13 | private SnapshotSpan? _cachedRequestSpan; 14 | 15 | internal SnapshotSpan? CachedRequestSpan 16 | { 17 | get { return _cachedRequestSpan; } 18 | set { _cachedRequestSpan = value; } 19 | } 20 | 21 | internal event EventHandler TagsChanged; 22 | 23 | internal BasicTagger(IBasicTaggerSource basicTaggerSource) 24 | { 25 | Contract.Requires(basicTaggerSource != null); 26 | _basicTaggerSource = basicTaggerSource; 27 | _basicTaggerSource.Changed += OnBasicTaggerSourceChanged; 28 | } 29 | 30 | private void Dispose() 31 | { 32 | _basicTaggerSource.Changed -= OnBasicTaggerSourceChanged; 33 | var disposable = _basicTaggerSource as IDisposable; 34 | if (disposable != null) 35 | { 36 | disposable.Dispose(); 37 | } 38 | } 39 | 40 | private void AdjustRequestSpan(NormalizedSnapshotSpanCollection col) 41 | { 42 | if (col.Count > 0) 43 | { 44 | var requestSpan = col.GetOverarchingSpan(); 45 | _cachedRequestSpan = TaggerUtil.AdjustRequestedSpan(_cachedRequestSpan, requestSpan); 46 | } 47 | } 48 | 49 | private IEnumerable> GetTags(NormalizedSnapshotSpanCollection col) 50 | { 51 | AdjustRequestSpan(col); 52 | if (col.Count == 0) 53 | { 54 | return Enumerable.Empty>(); 55 | } 56 | 57 | // Even though it's easier don't do a GetTags request for the overarching SnapshotSpan 58 | // of the request. It's possible for the overarching SnapshotSpan to have an order 59 | // magnitudes more lines than the items in the collection. This is very possible when 60 | // large folded regions or on screen. Instead just request the individual ones 61 | return col.Count == 1 62 | ? _basicTaggerSource.GetTags(col[0]) 63 | : col.SelectMany(_basicTaggerSource.GetTags); 64 | } 65 | 66 | private void OnBasicTaggerSourceChanged(object sender, EventArgs e) 67 | { 68 | var list = TagsChanged; 69 | if (_cachedRequestSpan.HasValue && list != null) 70 | { 71 | var args = new SnapshotSpanEventArgs(_cachedRequestSpan.Value); 72 | list(this, args); 73 | } 74 | } 75 | 76 | #region ITagger 77 | 78 | IEnumerable> ITagger.GetTags(NormalizedSnapshotSpanCollection col) 79 | { 80 | return GetTags(col); 81 | } 82 | 83 | event EventHandler ITagger.TagsChanged 84 | { 85 | add { TagsChanged += value; } 86 | remove { TagsChanged -= value; } 87 | } 88 | 89 | #endregion 90 | 91 | #region IDisposable 92 | 93 | void IDisposable.Dispose() 94 | { 95 | Dispose(); 96 | } 97 | 98 | #endregion 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Src/EditorUtils/Implementation/Tagging/Classifier.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Text.Classification; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using Microsoft.VisualStudio.Text; 7 | using Microsoft.VisualStudio.Text.Tagging; 8 | 9 | namespace EditorUtils.Implementation.Tagging 10 | { 11 | internal sealed class Classifier : IClassifier, IDisposable 12 | { 13 | private readonly ITagger _tagger; 14 | private event EventHandler _classificationChanged; 15 | 16 | internal Classifier(ITagger tagger) 17 | { 18 | _tagger = tagger; 19 | _tagger.TagsChanged += OnTagsChanged; 20 | } 21 | 22 | private void Dispose() 23 | { 24 | _tagger.TagsChanged -= OnTagsChanged; 25 | var disposable = _tagger as IDisposable; 26 | if (disposable != null) 27 | { 28 | disposable.Dispose(); 29 | } 30 | } 31 | 32 | private void OnTagsChanged(object sender, SnapshotSpanEventArgs e) 33 | { 34 | var list = _classificationChanged; 35 | if (list != null) 36 | { 37 | list(this, new ClassificationChangedEventArgs(e.Span)); 38 | } 39 | } 40 | 41 | #region IClassifier 42 | 43 | event EventHandler IClassifier.ClassificationChanged 44 | { 45 | add { _classificationChanged += value; } 46 | remove { _classificationChanged -= value; } 47 | } 48 | 49 | IList IClassifier.GetClassificationSpans(SnapshotSpan span) 50 | { 51 | return _tagger 52 | .GetTags(new NormalizedSnapshotSpanCollection(span)) 53 | .Select(x => new ClassificationSpan(x.Span, x.Tag.ClassificationType)) 54 | .ToList(); 55 | } 56 | 57 | #endregion 58 | 59 | #region IDisposable 60 | 61 | void IDisposable.Dispose() 62 | { 63 | Dispose(); 64 | } 65 | 66 | #endregion 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Src/EditorUtils/Implementation/Tagging/CountedClassifier.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | using EditorUtils; 7 | using Microsoft.VisualStudio.Text.Classification; 8 | 9 | namespace EditorUtils.Implementation.Tagging 10 | { 11 | /// 12 | /// This solves the same problem as CountedTagger but for IClassifier 13 | /// 14 | internal sealed class CountedClassifier : IClassifier, IDisposable 15 | { 16 | private readonly CountedValue _countedValue; 17 | 18 | internal IClassifier Classifier 19 | { 20 | get { return _countedValue.Value; } 21 | } 22 | 23 | internal CountedClassifier( 24 | PropertyCollection propertyCollection, 25 | object key, 26 | Func createFunc) 27 | { 28 | _countedValue = CountedValue.GetOrCreate(propertyCollection, key, createFunc); 29 | } 30 | 31 | internal void Dispose() 32 | { 33 | _countedValue.Release(); 34 | } 35 | 36 | #region IDisposable 37 | 38 | void IDisposable.Dispose() 39 | { 40 | Dispose(); 41 | } 42 | 43 | #endregion 44 | 45 | #region IClassifier 46 | 47 | event EventHandler IClassifier.ClassificationChanged 48 | { 49 | add { Classifier.ClassificationChanged += value; } 50 | remove { Classifier.ClassificationChanged -= value; } 51 | } 52 | 53 | 54 | IList IClassifier.GetClassificationSpans(SnapshotSpan span) 55 | { 56 | return Classifier.GetClassificationSpans(span); 57 | } 58 | 59 | #endregion 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Src/EditorUtils/Implementation/Tagging/CountedTagger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | using EditorUtils; 7 | 8 | namespace EditorUtils.Implementation.Tagging 9 | { 10 | /// 11 | /// It's possible, and very likely, for an ITagger to be requested multiple times for the 12 | /// same scenario via the ITaggerProvider. This happens when extensions spin up custom 13 | /// ITagAggregator instances or simple manually query for a new ITagger. Having multiple taggers 14 | /// for the same data is often very unnecessary. Produces a lot of duplicate work. For example 15 | /// consider having multiple :hlsearch taggers for the same ITextView. 16 | /// 17 | /// CountedTagger helps solve this by using a ref counted solution over the raw ITagger. It allows 18 | /// for only one ITagger to be created for the same scenario 19 | /// 20 | internal sealed class CountedTagger : ITagger, IDisposable 21 | where TTag : ITag 22 | { 23 | private readonly CountedValue> _countedValue; 24 | 25 | internal ITagger Tagger 26 | { 27 | get { return _countedValue.Value; } 28 | } 29 | 30 | internal CountedTagger( 31 | PropertyCollection propertyCollection, 32 | object key, 33 | Func> createFunc) 34 | { 35 | _countedValue = CountedValue>.GetOrCreate(propertyCollection, key, createFunc); 36 | } 37 | 38 | internal void Dispose() 39 | { 40 | _countedValue.Release(); 41 | } 42 | 43 | #region ITagger 44 | 45 | IEnumerable> ITagger.GetTags(NormalizedSnapshotSpanCollection col) 46 | { 47 | return Tagger.GetTags(col); 48 | } 49 | 50 | event EventHandler ITagger.TagsChanged 51 | { 52 | add { Tagger.TagsChanged += value; } 53 | remove { Tagger.TagsChanged -= value; } 54 | } 55 | 56 | #endregion 57 | 58 | #region IDisposable 59 | 60 | void IDisposable.Dispose() 61 | { 62 | Dispose(); 63 | } 64 | 65 | #endregion 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Src/EditorUtils/Implementation/Tagging/CountedValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | using EditorUtils; 7 | using Microsoft.VisualStudio.Text.Classification; 8 | 9 | namespace EditorUtils.Implementation.Tagging 10 | { 11 | /// 12 | /// This solves the same problem as CountedTagger but for IClassifier 13 | /// 14 | internal sealed class CountedValue 15 | { 16 | private readonly T _value; 17 | private readonly object _key; 18 | private readonly PropertyCollection _propertyCollection; 19 | private int _count; 20 | 21 | internal T Value 22 | { 23 | get { return _value; } 24 | } 25 | 26 | private CountedValue( 27 | PropertyCollection propertyCollection, 28 | object key, 29 | T value) 30 | { 31 | _value = value; 32 | _key = key; 33 | _propertyCollection = propertyCollection; 34 | _count = 1; 35 | } 36 | 37 | internal void Release() 38 | { 39 | _count--; 40 | if (_count == 0) 41 | { 42 | var disposable = _value as IDisposable; 43 | if (disposable != null) 44 | { 45 | disposable.Dispose(); 46 | } 47 | _propertyCollection.RemoveProperty(_key); 48 | } 49 | } 50 | 51 | internal static CountedValue GetOrCreate( 52 | PropertyCollection propertyCollection, 53 | object key, 54 | Func createFunc) 55 | { 56 | CountedValue countedValue; 57 | if (propertyCollection.TryGetPropertySafe(key, out countedValue)) 58 | { 59 | countedValue._count++; 60 | return countedValue; 61 | } 62 | 63 | countedValue = new CountedValue(propertyCollection, key, createFunc()); 64 | propertyCollection[key] = countedValue; 65 | return countedValue; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Src/EditorUtils/Implementation/Tagging/TaggerUtil.cs: -------------------------------------------------------------------------------- 1 |  2 | using Microsoft.VisualStudio.Text; 3 | namespace EditorUtils.Implementation.Tagging 4 | { 5 | internal static class TaggerUtil 6 | { 7 | /// 8 | /// The simple taggers when changed need to provide an initial SnapshotSpan 9 | /// for the TagsChanged event. It's important that this SnapshotSpan be kept as 10 | /// small as possible. If it's incorrectly large it can have a negative performance 11 | /// impact on the editor. In particular 12 | /// 13 | /// 1. The value is directly provided in ITagAggregator::TagsChanged. This value 14 | /// is acted on directly by many editor components. Providing a large range 15 | /// unnecessarily increases their work load. 16 | /// 2. It can cause a ripple effect in Visual Studio 2010 RTM. The SnapshotSpan 17 | /// returned will be immediately be the vale passed to GetTags for every other 18 | /// ITagger in the system (TextMarkerVisualManager issue). 19 | /// 20 | /// In order to provide the minimum possible valid SnapshotSpan the simple taggers 21 | /// cache the overarching SnapshotSpan for the latest ITextSnapshot of all requests 22 | /// to which they are given. 23 | /// 24 | internal static SnapshotSpan AdjustRequestedSpan(SnapshotSpan? cachedRequestSpan, SnapshotSpan requestSpan) 25 | { 26 | if (!cachedRequestSpan.HasValue) 27 | { 28 | return requestSpan; 29 | } 30 | 31 | var cachedSnapshot = cachedRequestSpan.Value.Snapshot; 32 | var requestSnapshot = requestSpan.Snapshot; 33 | 34 | if (cachedSnapshot == requestSnapshot) 35 | { 36 | // Same snapshot so we just need the overarching SnapshotSpan 37 | return cachedRequestSpan.Value.CreateOverarching(requestSpan); 38 | } 39 | 40 | if (cachedSnapshot.Version.VersionNumber < requestSnapshot.Version.VersionNumber) 41 | { 42 | // Request for a span on a new ITextSnapshot. Translate the old SnapshotSpan 43 | // to the new ITextSnapshot and get the overarching value 44 | var trackingSpan = cachedSnapshot.CreateTrackingSpan(cachedRequestSpan.Value.Span, SpanTrackingMode.EdgeInclusive); 45 | var traslatedSpan = trackingSpan.GetSpanSafe(requestSnapshot); 46 | if (traslatedSpan.HasValue) 47 | { 48 | return traslatedSpan.Value.CreateOverarching(requestSpan); 49 | } 50 | 51 | // If we can't translate the previous SnapshotSpan forward then simply use the 52 | // entire ITextSnapshot. This is a correct value, it just has the potential for 53 | // some inefficiencies 54 | return requestSnapshot.GetExtent(); 55 | } 56 | 57 | // It's a request for a value in the past. This is a very rare scenario that is almost 58 | // always followed by a request for a value on the current snapshot. Just return the 59 | // entire ITextSnapshot. This is a correct value, it just has the potential for 60 | // some inefficiencies 61 | return requestSpan.Snapshot.GetExtent(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Src/EditorUtils/Implementation/Utilities/EqualityUtility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace EditorUtils.Implementation.Utilities 5 | { 6 | internal static class EqualityUtility 7 | { 8 | internal sealed class DelegateComparer : IEqualityComparer 9 | { 10 | private Func _equalsFunc; 11 | private Func _getHashCodeFunc; 12 | 13 | internal DelegateComparer(Func equalsFunc, Func getHashCodeFunc) 14 | { 15 | _equalsFunc = equalsFunc; 16 | _getHashCodeFunc = getHashCodeFunc; 17 | } 18 | 19 | bool IEqualityComparer.Equals(T x, T y) 20 | { 21 | return _equalsFunc(x, y); 22 | } 23 | 24 | int IEqualityComparer.GetHashCode(T obj) 25 | { 26 | return _getHashCodeFunc(obj); 27 | } 28 | } 29 | 30 | internal static IEqualityComparer Create( 31 | Func equalsFunc, 32 | Func getHashCodeFunc) 33 | { 34 | return new DelegateComparer(equalsFunc, getHashCodeFunc); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Src/EditorUtils/Implementation/Utilities/ProtectedOperations.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.Composition; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Windows.Threading; 7 | using Microsoft.VisualStudio.Text; 8 | 9 | namespace EditorUtils.Implementation.Utilities 10 | { 11 | /// 12 | /// Implements the safe dispatching interface which prevents application crashes for 13 | /// exceptions reaching the dispatcher loop 14 | /// 15 | internal sealed class ProtectedOperations : IProtectedOperations 16 | { 17 | private readonly List> _errorHandlers; 18 | 19 | internal ProtectedOperations(IEnumerable> errorHandlers) 20 | { 21 | _errorHandlers = errorHandlers.ToList(); 22 | } 23 | 24 | internal ProtectedOperations(IExtensionErrorHandler errorHandler) 25 | { 26 | var lazy = new Lazy(() => errorHandler); 27 | _errorHandlers = new List>(new [] { lazy }); 28 | } 29 | 30 | /// 31 | /// Create a SafeDispatcher instance which doesn't have any backing IExtensionErrorHandler 32 | /// values. Useful for test scenarios 33 | /// 34 | internal ProtectedOperations() 35 | { 36 | _errorHandlers = new List>(); 37 | } 38 | 39 | /// 40 | /// Produce a delegate that can safely execute the given action. If it throws an exception 41 | /// then make sure to alert the error handlers 42 | /// 43 | private Action GetProtectedAction(Action action) 44 | { 45 | Action protectedAction = 46 | () => 47 | { 48 | 49 | try 50 | { 51 | action(); 52 | } 53 | catch (Exception e) 54 | { 55 | AlertAll(e); 56 | } 57 | }; 58 | return protectedAction; 59 | } 60 | 61 | private EventHandler GetProtectedEventHandler(EventHandler eventHandler) 62 | { 63 | EventHandler protectedEventHandler = 64 | (sender, e) => 65 | { 66 | try 67 | { 68 | eventHandler(sender, e); 69 | } 70 | catch (Exception exception) 71 | { 72 | AlertAll(exception); 73 | } 74 | }; 75 | 76 | return protectedEventHandler; 77 | } 78 | 79 | /// 80 | /// Alert all of the IExtensionErrorHandlers that the given Exception occurred. Be careful to guard 81 | /// against them for Exceptions as we are still on the dispatcher loop here and exceptions would be 82 | /// fatal 83 | /// 84 | private void AlertAll(Exception originalException) 85 | { 86 | foreach (var errorHandler in _errorHandlers) 87 | { 88 | try 89 | { 90 | errorHandler.Value.HandleError(this, originalException); 91 | } 92 | catch (Exception exception) 93 | { 94 | Debug.Fail("Error Handler Threw: " + exception.Message); 95 | } 96 | } 97 | } 98 | 99 | #region IProtectedOperations 100 | 101 | void IProtectedOperations.BeginInvoke(Action action) 102 | { 103 | var protectedAction = GetProtectedAction(action); 104 | Dispatcher.CurrentDispatcher.BeginInvoke(protectedAction, null); 105 | } 106 | 107 | void IProtectedOperations.BeginInvoke(Action action, DispatcherPriority dispatcherPriority) 108 | { 109 | var protectedAction = GetProtectedAction(action); 110 | Dispatcher.CurrentDispatcher.BeginInvoke(protectedAction, dispatcherPriority); 111 | } 112 | 113 | Action IProtectedOperations.GetProtectedAction(Action action) 114 | { 115 | return GetProtectedAction(action); 116 | } 117 | 118 | EventHandler IProtectedOperations.GetProtectedEventHandler(EventHandler eventHandler) 119 | { 120 | return GetProtectedEventHandler(eventHandler); 121 | } 122 | 123 | void IProtectedOperations.Report(Exception ex) 124 | { 125 | AlertAll(ex); 126 | } 127 | 128 | #endregion 129 | } 130 | } 131 | 132 | -------------------------------------------------------------------------------- /Src/EditorUtils/Implementation/Utilities/ReadOnlyStack.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace EditorUtils.Implementation.Utilities 5 | { 6 | /// 7 | /// Make this a ReadOnly stack 8 | /// 9 | [UsedInBackgroundThread] 10 | internal sealed class ReadOnlyStack : IEnumerable 11 | { 12 | internal static readonly ReadOnlyStack Empty = new ReadOnlyStack(); 13 | 14 | private readonly ReadOnlyStack _next; 15 | private readonly T _value; 16 | private readonly int _count; 17 | 18 | internal bool IsEmpty 19 | { 20 | get { return _next == null; } 21 | } 22 | 23 | internal int Count 24 | { 25 | get { return _count; } 26 | } 27 | 28 | internal T Value 29 | { 30 | get 31 | { 32 | ThrowIfEmpty(); 33 | return _value; 34 | } 35 | } 36 | 37 | private ReadOnlyStack() 38 | { 39 | 40 | } 41 | 42 | private ReadOnlyStack(T lineRange, ReadOnlyStack next) 43 | { 44 | _value = lineRange; 45 | _next = next; 46 | _count = next.Count + 1; 47 | } 48 | 49 | internal ReadOnlyStack Pop() 50 | { 51 | ThrowIfEmpty(); 52 | return _next; 53 | } 54 | 55 | internal ReadOnlyStack Push(T lineRange) 56 | { 57 | return new ReadOnlyStack(lineRange, this); 58 | } 59 | 60 | private void ThrowIfEmpty() 61 | { 62 | if (IsEmpty) 63 | { 64 | throw new Exception("Stack is empty"); 65 | } 66 | } 67 | 68 | public IEnumerator GetEnumerator() 69 | { 70 | var top = this; 71 | while (!top.IsEmpty) 72 | { 73 | yield return top.Value; 74 | top = top.Pop(); 75 | } 76 | } 77 | 78 | #region IEnumerable 79 | 80 | IEnumerator IEnumerable.GetEnumerator() 81 | { 82 | return GetEnumerator(); 83 | } 84 | 85 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 86 | { 87 | return GetEnumerator(); 88 | } 89 | 90 | #endregion 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Src/EditorUtils/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/Src/EditorUtils/Key.snk -------------------------------------------------------------------------------- /Src/EditorUtils/LineRange.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace EditorUtils 6 | { 7 | /// 8 | /// A simple line range 9 | /// 10 | public struct LineRange : IEquatable 11 | { 12 | private readonly int _startLine; 13 | private readonly int _count; 14 | 15 | public int StartLineNumber 16 | { 17 | get { return _startLine; } 18 | } 19 | 20 | public int LastLineNumber 21 | { 22 | get { return _startLine + (_count - 1); } 23 | } 24 | 25 | public int Count 26 | { 27 | get { return _count; } 28 | } 29 | 30 | public IEnumerable LineNumbers 31 | { 32 | get { return Enumerable.Range(_startLine, _count); } 33 | } 34 | 35 | public LineRange(int startLine, int count) 36 | { 37 | _startLine = startLine; 38 | _count = count; 39 | } 40 | 41 | public bool ContainsLineNumber(int lineNumber) 42 | { 43 | return lineNumber >= _startLine && lineNumber <= LastLineNumber; 44 | } 45 | 46 | public bool Contains(LineRange lineRange) 47 | { 48 | return 49 | StartLineNumber <= lineRange.StartLineNumber && 50 | LastLineNumber >= lineRange.LastLineNumber; 51 | } 52 | 53 | public bool Intersects(LineRange lineRange) 54 | { 55 | return 56 | ContainsLineNumber(lineRange.StartLineNumber) || 57 | ContainsLineNumber(lineRange.LastLineNumber) || 58 | LastLineNumber + 1 == lineRange.StartLineNumber || 59 | StartLineNumber == lineRange.LastLineNumber + 1; 60 | } 61 | 62 | public override int GetHashCode() 63 | { 64 | return _startLine ^ _count; 65 | } 66 | 67 | public override bool Equals(object obj) 68 | { 69 | if (obj is LineRange) 70 | { 71 | return Equals((LineRange)obj); 72 | } 73 | 74 | return false; 75 | } 76 | 77 | public bool Equals(LineRange lineRange) 78 | { 79 | return StartLineNumber == lineRange.StartLineNumber && Count == lineRange.Count; 80 | } 81 | 82 | public override string ToString() 83 | { 84 | return String.Format("[{0} - {1}]", StartLineNumber, LastLineNumber); 85 | } 86 | 87 | public static bool operator ==(LineRange left, LineRange right) 88 | { 89 | return left.Equals(right); 90 | } 91 | 92 | public static bool operator !=(LineRange left, LineRange right) 93 | { 94 | return !left.Equals(right); 95 | } 96 | 97 | public static LineRange CreateFromBounds(int startLineNumber, int lastLineNumber) 98 | { 99 | if (lastLineNumber < startLineNumber) 100 | { 101 | throw new ArgumentOutOfRangeException("lastLineNumber"); 102 | } 103 | 104 | var count = (lastLineNumber - startLineNumber) + 1; 105 | return new LineRange(startLineNumber, count); 106 | } 107 | 108 | public static LineRange CreateOverarching(LineRange left, LineRange right) 109 | { 110 | var startLineNumber = Math.Min(left.StartLineNumber, right.StartLineNumber); 111 | var lastLineNumber = Math.Max(left.LastLineNumber, right.LastLineNumber); 112 | return CreateFromBounds(startLineNumber, lastLineNumber); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Src/EditorUtils/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | using EditorUtils; 5 | 6 | // General Information about an assembly is controlled through the following 7 | // set of attributes. Change these attribute values to modify the information 8 | // associated with an assembly. 9 | [assembly: AssemblyTitle("EditorUtils")] 10 | [assembly: AssemblyDescription("")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyProduct("EditorUtils")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("4826ca00-1f05-42e8-8dea-427bf872ae5e")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion(Constants.AssemblyVersion)] 35 | [assembly: AssemblyFileVersion(Constants.AssemblyVersion)] 36 | [assembly: InternalsVisibleTo("EditorUtils.UnitTest, PublicKey=" + 37 | "0024000004800000940000000602000000240000525341310004000001000100c90913d9ce09ec" + 38 | "960c03fe50c463b3a5f214fdebdcd9c33b1f5f8cddd3e82bc4ba2bf3846bbb4ddd5cd8a322a40a" + 39 | "dc41311155ebf6a1789c66c77345923153e49003049cb798836053198008405812d4e5ff468369" + 40 | "1e25db5930f79a1d206b864a852e1653ddf8d2bc73e085a98ef023681bf8453dc3bd29577b1ad3" + 41 | "55f863e7")] 42 | 43 | -------------------------------------------------------------------------------- /Src/EditorUtils/UsedInBackgroundThreadAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EditorUtils 4 | { 5 | /// 6 | /// Used to indicate a given operation occurs on background threads 7 | /// 8 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Interface)] 9 | public sealed class UsedInBackgroundThreadAttribute : Attribute 10 | { 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Test/Cats/CatTagger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Diagnostics; 5 | using System.Threading; 6 | using EditorUtils; 7 | using Microsoft.VisualStudio.Text; 8 | using Microsoft.VisualStudio.Text.Editor; 9 | using Microsoft.VisualStudio.Text.Tagging; 10 | 11 | namespace Cats 12 | { 13 | /// 14 | /// Tag all occurences of "cat" in the code base 15 | /// 16 | internal sealed class CatTagger : AsyncTaggerSource 17 | { 18 | #region BackgroundWorker 19 | 20 | sealed class BackgroundWorker 21 | { 22 | private readonly string _word = "cat"; 23 | private readonly CancellationToken _cancellationToken; 24 | 25 | internal BackgroundWorker(CancellationToken cancellationToken) 26 | { 27 | _cancellationToken = cancellationToken; 28 | } 29 | 30 | internal ReadOnlyCollection> GetTags(SnapshotSpan span) 31 | { 32 | var tags = new List>(); 33 | var lineRange = SnapshotLineRange.CreateForSpan(span); 34 | Debug.WriteLine("Cat Version {0}, Lines {1} - {2}", span.Snapshot.Version.VersionNumber, lineRange.StartLineNumber, lineRange.LastLineNumber); 35 | foreach (var snapshotLine in lineRange.Lines) 36 | { 37 | AddWordsOnLine(tags, snapshotLine); 38 | _cancellationToken.ThrowIfCancellationRequested(); 39 | } 40 | 41 | // Cats need naps 42 | Debug.WriteLine("Cat Nap Time"); 43 | Thread.Sleep(TimeSpan.FromSeconds(1)); 44 | Debug.WriteLine("Cat Wake Up"); 45 | 46 | return tags.ToReadOnlyCollectionShallow(); 47 | } 48 | 49 | private void AddWordsOnLine(List> tags, ITextSnapshotLine snapshotLine) 50 | { 51 | var snapshot = snapshotLine.Snapshot; 52 | var tag = new TextMarkerTag(Constants.FormatName); 53 | 54 | var i = 0; 55 | while (i < snapshotLine.Length) 56 | { 57 | var point = snapshot.GetPoint(i + snapshotLine.Start.Position); 58 | if (IsWord(point)) 59 | { 60 | var span = new SnapshotSpan(snapshot, snapshotLine.Start.Position + i, _word.Length); 61 | tags.Add(new TagSpan(span, tag)); 62 | i += _word.Length; 63 | } 64 | else 65 | { 66 | i++; 67 | } 68 | } 69 | } 70 | 71 | private bool IsWord(SnapshotPoint point) 72 | { 73 | var snapshot = point.Snapshot; 74 | int i; 75 | for (i = 0; i + point.Position < snapshot.Length && i < _word.Length; i++) 76 | { 77 | if (snapshot.GetChar(i + point.Position) != _word[i]) 78 | { 79 | return false; 80 | } 81 | } 82 | 83 | if (i < snapshot.Length && Char.IsLetter(snapshot.GetChar(i + point.Position))) 84 | { 85 | return false; 86 | } 87 | 88 | return true; 89 | } 90 | } 91 | 92 | #endregion 93 | 94 | internal CatTagger(ITextView textView) 95 | : base(textView) 96 | { 97 | 98 | } 99 | 100 | #region AsyncTaggerSource 101 | 102 | protected override bool TryGetTagsPrompt(SnapshotSpan span, out IEnumerable> tags) 103 | { 104 | tags = null; 105 | return false; 106 | } 107 | 108 | protected override string GetDataForSnapshot(ITextSnapshot snapshot) 109 | { 110 | return string.Empty; 111 | } 112 | 113 | protected override ReadOnlyCollection> GetTagsInBackground(string data, SnapshotSpan span, CancellationToken cancellationToken) 114 | { 115 | var backgroundWorker = new BackgroundWorker(cancellationToken); 116 | return backgroundWorker.GetTags(span); 117 | } 118 | 119 | #endregion 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Test/Cats/CatTaggerFormat.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using System.Windows.Media; 3 | using Microsoft.VisualStudio.Text.Classification; 4 | using Microsoft.VisualStudio.Utilities; 5 | 6 | namespace Cats 7 | { 8 | [Export(typeof(EditorFormatDefinition))] 9 | [Name(Constants.FormatName)] 10 | [UserVisible(true)] 11 | internal sealed class CatTaggerFormat : MarkerFormatDefinition 12 | { 13 | public CatTaggerFormat() 14 | { 15 | DisplayName = Constants.FormatDisplayName; 16 | BackgroundColor = Colors.BlueViolet; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Test/Cats/CatTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using EditorUtils; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Editor; 5 | using Microsoft.VisualStudio.Text.Tagging; 6 | using Microsoft.VisualStudio.Utilities; 7 | 8 | namespace Cats 9 | { 10 | [Export(typeof(IViewTaggerProvider))] 11 | [ContentType("any")] 12 | [TextViewRole(PredefinedTextViewRoles.Document)] 13 | [TagType(typeof(TextMarkerTag))] 14 | internal sealed class CatTaggerProvider : IViewTaggerProvider 15 | { 16 | private readonly object _key = new object(); 17 | 18 | [ImportingConstructor] 19 | internal CatTaggerProvider() 20 | { 21 | #if DEBUG 22 | EditorUtilsTrace.TraceSwitch.Level = System.Diagnostics.TraceLevel.Info; 23 | #endif 24 | } 25 | 26 | #region IViewTaggerProvider 27 | 28 | ITagger IViewTaggerProvider.CreateTagger(ITextView textView, ITextBuffer textBuffer) 29 | { 30 | if (textView.TextBuffer != textBuffer) 31 | { 32 | return null; 33 | } 34 | 35 | var tagger = EditorUtilsFactory.CreateTagger( 36 | textView.Properties, 37 | _key, 38 | () => new CatTagger(textView)); 39 | 40 | return (ITagger)(object)tagger; 41 | } 42 | 43 | #endregion 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Test/Cats/Contstants.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace Cats 3 | { 4 | internal static class Constants 5 | { 6 | internal const string FormatName = "Cats"; 7 | internal const string FormatDisplayName = "Cats"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Test/Cats/DogClassifier.cs: -------------------------------------------------------------------------------- 1 | using EditorUtils; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using Microsoft.VisualStudio.Text; 7 | using Microsoft.VisualStudio.Text.Classification; 8 | using System.Collections.ObjectModel; 9 | using Microsoft.VisualStudio.Text.Tagging; 10 | 11 | namespace Cats 12 | { 13 | internal sealed class DogClassifier : IBasicTaggerSource 14 | { 15 | private readonly IClassificationType _classificationType; 16 | #pragma warning disable 67 17 | public event EventHandler Changed; 18 | #pragma warning restore 67 19 | 20 | internal DogClassifier(IClassificationType classificationType) 21 | { 22 | _classificationType = classificationType; 23 | } 24 | 25 | public ReadOnlyCollection> GetTags(SnapshotSpan span) 26 | { 27 | var list = new List>(); 28 | var position = span.Start.Position; 29 | while (position < span.End.Position) 30 | { 31 | var point = new SnapshotPoint(span.Snapshot, position); 32 | if (IsDog(point)) 33 | { 34 | var dogSpan = new SnapshotSpan(point, 3); 35 | var tagSpan = new TagSpan(dogSpan, new ClassificationTag(_classificationType)); 36 | list.Add(tagSpan); 37 | position += 3; 38 | } 39 | else 40 | { 41 | position += 1; 42 | } 43 | } 44 | 45 | return list.ToReadOnlyCollectionShallow(); 46 | } 47 | 48 | private static bool IsDog(SnapshotPoint point) 49 | { 50 | var snapshot = point.Snapshot; 51 | if (point.Position + 2 < snapshot.Length && 52 | snapshot[point.Position] == 'd' && 53 | snapshot[point.Position + 1] == 'o' && 54 | snapshot[point.Position + 2] == 'g') 55 | { 56 | return true; 57 | } 58 | 59 | return false; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Test/Cats/DogClassifierProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Text.Classification; 2 | using Microsoft.VisualStudio.Utilities; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.Composition; 6 | using System.Linq; 7 | using System.Text; 8 | using Microsoft.VisualStudio.Text; 9 | using EditorUtils; 10 | 11 | namespace Cats 12 | { 13 | [Export(typeof(IClassifierProvider))] 14 | [ContentType("text")] 15 | internal sealed class DogClassifierProvider : IClassifierProvider 16 | { 17 | IClassificationTypeRegistryService _classificationTypeRegistryService; 18 | 19 | [ImportingConstructor] 20 | internal DogClassifierProvider(IClassificationTypeRegistryService classificationTypeRegistryService) 21 | { 22 | _classificationTypeRegistryService = classificationTypeRegistryService; 23 | } 24 | 25 | public IClassifier GetClassifier(ITextBuffer textBuffer) 26 | { 27 | var classificationType = _classificationTypeRegistryService.GetClassificationType(DogClassificationFormatDefinition.Name); 28 | return EditorUtilsFactory.CreateClassifierRaw(new DogClassifier(classificationType)); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Test/Cats/DogTaggerFormat.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Text.Classification; 2 | using Microsoft.VisualStudio.Utilities; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.Composition; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Windows.Media; 9 | 10 | namespace Cats 11 | { 12 | [Export(typeof(EditorFormatDefinition))] 13 | [ClassificationType(ClassificationTypeNames = Name)] 14 | [Name(Name)] 15 | internal sealed class DogClassificationFormatDefinition : ClassificationFormatDefinition 16 | { 17 | internal const string Name = "Dog info"; 18 | 19 | internal DogClassificationFormatDefinition() 20 | { 21 | ForegroundColor = Colors.Green; 22 | } 23 | } 24 | 25 | internal sealed class DogClassifications 26 | { 27 | [Name(DogClassificationFormatDefinition.Name)] 28 | [Export] 29 | internal ClassificationTypeDefinition DogClassificationType { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Test/Cats/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("CatTagger")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("CatTagger")] 13 | [assembly: AssemblyCopyright("")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 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 Build and Revision Numbers 30 | // by using the '*' as shown below: 31 | // [assembly: AssemblyVersion("1.0.*")] 32 | [assembly: AssemblyVersion("1.0.0.0")] 33 | [assembly: AssemblyFileVersion("1.0.0.0")] 34 | -------------------------------------------------------------------------------- /Test/Cats/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | CatTagger 5 | Microsoft IT 6 | 1.0 7 | This is a sample classifier extension to the Visual Studio Editor. 8 | 1033 9 | 10 | 11 | Pro 12 | 13 | 14 | 15 | 16 | 17 | 18 | |%CurrentProject%| 19 | 20 | 21 | -------------------------------------------------------------------------------- /Test/EditorUtils.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/AdhocOutlinerTest.cs: -------------------------------------------------------------------------------- 1 | using EditorUtils.Implementation.Utilities; 2 | using Microsoft.VisualStudio.Text; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using Xunit; 8 | 9 | namespace EditorUtils.UnitTest 10 | { 11 | public sealed class AdhocOutlinerTest : EditorHostTest 12 | { 13 | private readonly ITextBuffer _textBuffer; 14 | private readonly AdhocOutliner _outlinerRaw; 15 | private readonly IAdhocOutliner _outliner; 16 | 17 | public AdhocOutlinerTest() 18 | { 19 | _textBuffer = CreateTextBuffer(); 20 | _outlinerRaw = new AdhocOutliner(_textBuffer); 21 | _outliner = _outlinerRaw; 22 | _textBuffer.Properties.AddProperty(AdhocOutliner.OutlinerTaggerKey, null); 23 | } 24 | 25 | [Fact] 26 | public void CreateDeleteSequence() 27 | { 28 | _textBuffer.SetText("hello world"); 29 | var region = _outliner.CreateOutliningRegion(_textBuffer.GetExtent(), SpanTrackingMode.EdgeExclusive, "", ""); 30 | Assert.Equal(1, _outliner.GetOutliningRegions(_textBuffer.GetExtent()).Count); 31 | _outliner.DeleteOutliningRegion(region.Cookie); 32 | Assert.Equal(0, _outliner.GetOutliningRegions(_textBuffer.GetExtent()).Count); 33 | } 34 | 35 | [Fact] 36 | public void Properties() 37 | { 38 | _textBuffer.SetText("hello world"); 39 | var region = _outliner.CreateOutliningRegion(_textBuffer.GetSpan(0, 1), SpanTrackingMode.EdgeExclusive, "text", "hint"); 40 | Assert.Equal("text", region.Tag.CollapsedForm); 41 | Assert.Equal("hint", region.Tag.CollapsedHintForm); 42 | } 43 | 44 | [Fact] 45 | public void CheckTagger() 46 | { 47 | _textBuffer.Properties.RemoveProperty(AdhocOutliner.OutlinerTaggerKey); 48 | Assert.Throws(() => _outliner.GetOutliningRegions(_textBuffer.GetExtent())); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/ClassifierTest.cs: -------------------------------------------------------------------------------- 1 | using EditorUtils.Implementation.Tagging; 2 | using Microsoft.VisualStudio.Text; 3 | using Microsoft.VisualStudio.Text.Classification; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using Xunit; 10 | 11 | namespace EditorUtils.UnitTest 12 | { 13 | public abstract class ClassifierTest : EditorHostTest 14 | { 15 | public sealed class BasicTest : ClassifierTest 16 | { 17 | private readonly ITextBuffer _textBuffer; 18 | private readonly TextBasicTaggerSource _source; 19 | private readonly IClassifier _classifier; 20 | 21 | public BasicTest() 22 | { 23 | _textBuffer = CreateTextBuffer(); 24 | 25 | var classificationType = EditorHost.ClassificationTypeRegistryService.GetOrCreateClassificationType("classifier test"); 26 | _source = new TextBasicTaggerSource(new ClassificationTag(classificationType)); 27 | _classifier = EditorUtilsFactory.CreateClassifierRaw(_source); 28 | } 29 | 30 | [Fact] 31 | public void SimpleGet() 32 | { 33 | _source.Text = "cat"; 34 | _textBuffer.SetText("cat a cat"); 35 | var list = _classifier.GetClassificationSpans(_textBuffer.GetExtent()); 36 | Assert.Equal(2, list.Count); 37 | Assert.Equal( 38 | new [] { new Span(0, 3), new Span(6, 3) }, 39 | list.Select(x => x.Span.Span)); 40 | } 41 | 42 | [Fact] 43 | public void ChangeEvent() 44 | { 45 | int count = 0; 46 | _source.Text = "dog"; 47 | _source.Changed += delegate { count++; }; 48 | _source.Text = "bar"; 49 | Assert.Equal(1, count); 50 | _source.Text = "bar"; 51 | Assert.Equal(1, count); 52 | } 53 | } 54 | 55 | public sealed class AsyncTest : ClassifierTest 56 | { 57 | private readonly ITextBuffer _textBuffer; 58 | private readonly TextAsyncTaggerSource _source; 59 | private readonly AsyncTagger, IClassificationTag> _asyncTagger; 60 | private readonly IClassifier _classifier; 61 | 62 | public AsyncTest() 63 | { 64 | _textBuffer = CreateTextBuffer(); 65 | 66 | var classificationType = EditorHost.ClassificationTypeRegistryService.GetOrCreateClassificationType("classifier test"); 67 | _source = new TextAsyncTaggerSource(new ClassificationTag(classificationType), _textBuffer); 68 | _asyncTagger = new AsyncTagger, IClassificationTag>(_source); 69 | _classifier = new Classifier(_asyncTagger); 70 | } 71 | 72 | IList GetClassificationSpansFull(SnapshotSpan span) 73 | { 74 | _classifier.GetClassificationSpans(span); 75 | _asyncTagger.WaitForBackgroundToComplete(TestableSynchronizationContext); 76 | return _classifier.GetClassificationSpans(span); 77 | } 78 | 79 | [Fact] 80 | public void SimpleGet() 81 | { 82 | _source.Text = "cat"; 83 | _textBuffer.SetText("cat a cat"); 84 | 85 | var list = GetClassificationSpansFull(_textBuffer.GetExtent()); 86 | Assert.Equal(2, list.Count); 87 | Assert.Equal( 88 | new [] { new Span(0, 3), new Span(6, 3) }, 89 | list.Select(x => x.Span.Span)); 90 | } 91 | 92 | [Fact] 93 | public void ChangeFromComplete() 94 | { 95 | _source.Text = "cat"; 96 | _textBuffer.SetText("cat a cat"); 97 | 98 | _classifier.GetClassificationSpans(_textBuffer.GetExtent()); 99 | var count = 0; 100 | _classifier.ClassificationChanged += delegate { count++; }; 101 | _asyncTagger.WaitForBackgroundToComplete(TestableSynchronizationContext); 102 | Assert.Equal(1, count); 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/CountedClassifierTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using EditorUtils.Implementation.Tagging; 3 | using Microsoft.VisualStudio.Text.Tagging; 4 | using Microsoft.VisualStudio.Utilities; 5 | using Moq; 6 | using Xunit; 7 | using Microsoft.VisualStudio.Text.Classification; 8 | 9 | namespace EditorUtils.UnitTest 10 | { 11 | public sealed class CountedClassifierTest : EditorHostTest 12 | { 13 | private readonly MockRepository _factory; 14 | private readonly object _key; 15 | private readonly PropertyCollection _propertyCollection; 16 | 17 | public CountedClassifierTest() 18 | { 19 | _factory = new MockRepository(MockBehavior.Strict); 20 | _key = new object(); 21 | _propertyCollection = new PropertyCollection(); 22 | } 23 | 24 | /// 25 | /// First create on an key should actually call the create function 26 | /// 27 | [Fact] 28 | public void Create_DoCreate() 29 | { 30 | var didRun = false; 31 | var result = new CountedClassifier( 32 | _propertyCollection, 33 | _key, 34 | () => 35 | { 36 | didRun = true; 37 | return _factory.Create().Object; 38 | }); 39 | Assert.True(didRun); 40 | } 41 | 42 | /// 43 | /// Second create should just grab the value from the property collection 44 | /// 45 | [Fact] 46 | public void Create_GetFromCache() 47 | { 48 | var runCount = 0; 49 | Func func = 50 | () => 51 | { 52 | runCount++; 53 | return _factory.Create().Object; 54 | }; 55 | var result1 = new CountedClassifier(_propertyCollection, _key, func); 56 | var result2 = new CountedClassifier(_propertyCollection, _key, func); 57 | Assert.Equal(1, runCount); 58 | Assert.Same(result1.Classifier, result2.Classifier); 59 | } 60 | 61 | /// 62 | /// Disposing the one containing CountedTagger should dispose the underlying instance 63 | /// 64 | [Fact] 65 | public void Dispose_OneInstance() 66 | { 67 | var tagger = _factory.Create(); 68 | var disposable = tagger.As(); 69 | var result = new CountedClassifier(_propertyCollection, _key, () => tagger.Object); 70 | 71 | disposable.Setup(x => x.Dispose()).Verifiable(); 72 | result.Dispose(); 73 | disposable.Verify(); 74 | } 75 | 76 | /// 77 | /// Must dispose all of the outer CountedTagger instances before the inner ITagger is disposed 78 | /// 79 | [Fact] 80 | public void Dispose_ManyInstance() 81 | { 82 | var tagger = _factory.Create(); 83 | var disposable = tagger.As(); 84 | var result1 = new CountedClassifier(_propertyCollection, _key, () => tagger.Object); 85 | var result2 = new CountedClassifier(_propertyCollection, _key, () => tagger.Object); 86 | 87 | result1.Dispose(); 88 | disposable.Setup(x => x.Dispose()).Verifiable(); 89 | result2.Dispose(); 90 | disposable.Verify(); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/CountedTaggerTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using EditorUtils.Implementation.Tagging; 3 | using Microsoft.VisualStudio.Text.Tagging; 4 | using Microsoft.VisualStudio.Utilities; 5 | using Moq; 6 | using Xunit; 7 | 8 | namespace EditorUtils.UnitTest 9 | { 10 | public sealed class CountedTaggerTest : EditorHostTest 11 | { 12 | private readonly MockRepository _factory; 13 | private readonly object _key; 14 | private readonly PropertyCollection _propertyCollection; 15 | 16 | public CountedTaggerTest() 17 | { 18 | _factory = new MockRepository(MockBehavior.Strict); 19 | _key = new object(); 20 | _propertyCollection = new PropertyCollection(); 21 | } 22 | 23 | private CountedTagger Create(object key, PropertyCollection propertyCollection, Func> func) 24 | { 25 | return new CountedTagger(propertyCollection, key, func); 26 | } 27 | 28 | /// 29 | /// First create on an key should actually call the create function 30 | /// 31 | [Fact] 32 | public void Create_DoCreate() 33 | { 34 | var didRun = false; 35 | var result = Create( 36 | _key, 37 | _propertyCollection, 38 | () => 39 | { 40 | didRun = true; 41 | return _factory.Create>().Object; 42 | }); 43 | Assert.True(didRun); 44 | } 45 | 46 | /// 47 | /// Second create should just grab the value from the property collection 48 | /// 49 | [Fact] 50 | public void Create_GetFromCache() 51 | { 52 | var runCount = 0; 53 | Func> func = 54 | () => 55 | { 56 | runCount++; 57 | return _factory.Create>().Object; 58 | }; 59 | var result1 = Create(_key, _propertyCollection, func); 60 | var result2 = Create(_key, _propertyCollection, func); 61 | Assert.Equal(1, runCount); 62 | Assert.NotSame(result1, result2); 63 | Assert.Same(result1.Tagger, result2.Tagger); 64 | } 65 | 66 | /// 67 | /// Disposing the one containing CountedTagger should dispose the underlying instance 68 | /// 69 | [Fact] 70 | public void Dispose_OneInstance() 71 | { 72 | var tagger = _factory.Create>(); 73 | var disposable = tagger.As(); 74 | var result = Create(_key, _propertyCollection, () => tagger.Object); 75 | 76 | disposable.Setup(x => x.Dispose()).Verifiable(); 77 | result.Dispose(); 78 | disposable.Verify(); 79 | } 80 | 81 | /// 82 | /// Must dispose all of the outer CountedTagger instances before the inner ITagger is disposed 83 | /// 84 | [Fact] 85 | public void Dispose_ManyInstance() 86 | { 87 | var tagger = _factory.Create>(); 88 | var disposable = tagger.As(); 89 | var result1 = Create(_key, _propertyCollection, () => tagger.Object); 90 | var result2 = Create(_key, _propertyCollection, () => tagger.Object); 91 | 92 | result1.Dispose(); 93 | disposable.Setup(x => x.Dispose()).Verifiable(); 94 | result2.Dispose(); 95 | disposable.Verify(); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/EditorHostFactoryTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Xunit; 6 | 7 | namespace EditorUtils.UnitTest 8 | { 9 | public sealed class EditorHostFactoryTest 10 | { 11 | [Fact] 12 | public void GetShortVersionStringAll() 13 | { 14 | foreach (var e in Enum.GetValues(typeof(EditorVersion)).Cast()) 15 | { 16 | var value = EditorHostFactory.GetShortVersionString(e); 17 | Assert.NotNull(value); 18 | } 19 | } 20 | 21 | [Fact] 22 | public void GetVersionNumberAll() 23 | { 24 | Assert.Equal(10, EditorHostFactory.GetVersionNumber(EditorVersion.Vs2010)); 25 | Assert.Equal(11, EditorHostFactory.GetVersionNumber(EditorVersion.Vs2012)); 26 | Assert.Equal(12, EditorHostFactory.GetVersionNumber(EditorVersion.Vs2013)); 27 | Assert.Equal(14, EditorHostFactory.GetVersionNumber(EditorVersion.Vs2015)); 28 | } 29 | 30 | [Fact] 31 | public void MaxEditorVersionIsMax() 32 | { 33 | var max = EditorHostFactory.GetVersionNumber(EditorHostFactory.MaxEditorVersion); 34 | foreach (var e in Enum.GetValues(typeof(EditorVersion)).Cast()) 35 | { 36 | var number = EditorHostFactory.GetVersionNumber(e); 37 | Assert.True(number <= max); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/EditorHostTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Microsoft.VisualStudio.Text; 6 | using Microsoft.VisualStudio.Text.Editor; 7 | using Microsoft.VisualStudio.Text.Operations; 8 | using Microsoft.VisualStudio.Text.Outlining; 9 | using Microsoft.VisualStudio.Text.Projection; 10 | using Microsoft.VisualStudio.Utilities; 11 | 12 | namespace EditorUtils.UnitTest 13 | { 14 | public abstract class EditorHostTest : IDisposable 15 | { 16 | private static EditorHost EditorHostCache; 17 | 18 | private readonly EditorHost _editorHost; 19 | private readonly TestableSynchronizationContext _synchronizationContext; 20 | 21 | public EditorHost EditorHost 22 | { 23 | get { return _editorHost; } 24 | } 25 | 26 | public ISmartIndentationService SmartIndentationService 27 | { 28 | get { return _editorHost.SmartIndentationService; } 29 | } 30 | 31 | public ITextBufferFactoryService TextBufferFactoryService 32 | { 33 | get { return _editorHost.TextBufferFactoryService; } 34 | } 35 | 36 | public ITextEditorFactoryService TextEditorFactoryService 37 | { 38 | get { return _editorHost.TextEditorFactoryService; } 39 | } 40 | 41 | public IProjectionBufferFactoryService ProjectionBufferFactoryService 42 | { 43 | get { return _editorHost.ProjectionBufferFactoryService; } 44 | } 45 | 46 | public IEditorOperationsFactoryService EditorOperationsFactoryService 47 | { 48 | get { return _editorHost.EditorOperationsFactoryService; } 49 | } 50 | 51 | public IEditorOptionsFactoryService EditorOptionsFactoryService 52 | { 53 | get { return _editorHost.EditorOptionsFactoryService; } 54 | } 55 | 56 | public ITextSearchService TextSearchService 57 | { 58 | get { return _editorHost.TextSearchService; } 59 | } 60 | 61 | public ITextBufferUndoManagerProvider TextBufferUndoManagerProvider 62 | { 63 | get { return _editorHost.TextBufferUndoManagerProvider; } 64 | } 65 | 66 | public IOutliningManagerService OutliningManagerService 67 | { 68 | get { return _editorHost.OutliningManagerService; } 69 | } 70 | 71 | public IContentTypeRegistryService ContentTypeRegistryService 72 | { 73 | get { return _editorHost.ContentTypeRegistryService; } 74 | } 75 | 76 | public IProtectedOperations ProtectedOperations 77 | { 78 | get { return _editorHost.ProtectedOperations; } 79 | } 80 | 81 | public IBasicUndoHistoryRegistry BasicUndoHistoryRegistry 82 | { 83 | get { return _editorHost.BasicUndoHistoryRegistry; } 84 | } 85 | 86 | public TestableSynchronizationContext TestableSynchronizationContext 87 | { 88 | get { return _synchronizationContext; } 89 | } 90 | 91 | public EditorHostTest() 92 | { 93 | _editorHost = GetOrCreateEditorHost(); 94 | _synchronizationContext = new TestableSynchronizationContext(); 95 | _synchronizationContext.Install(); 96 | } 97 | 98 | protected virtual void Dispose() 99 | { 100 | _synchronizationContext.Uninstall(); 101 | } 102 | 103 | private EditorHost GetOrCreateEditorHost() 104 | { 105 | if (EditorHostCache != null) 106 | { 107 | return EditorHostCache; 108 | } 109 | 110 | var editorHostFactory = new EditorHostFactory(); 111 | EditorHostCache = editorHostFactory.CreateEditorHost(); 112 | return EditorHostCache; 113 | } 114 | 115 | public ITextBuffer CreateTextBuffer(params string[] lines) 116 | { 117 | return _editorHost.CreateTextBuffer(lines); 118 | } 119 | 120 | public ITextBuffer CreateTextBuffer(IContentType contentType, params string[] lines) 121 | { 122 | return _editorHost.CreateTextBuffer(contentType, lines); 123 | } 124 | 125 | public IProjectionBuffer CreateProjectionBuffer(params SnapshotSpan[] spans) 126 | { 127 | return _editorHost.CreateProjectionBuffer(spans); 128 | } 129 | 130 | public IWpfTextView CreateTextView(params string[] lines) 131 | { 132 | return _editorHost.CreateTextView(lines); 133 | } 134 | 135 | public IWpfTextView CreateTextView(IContentType contentType, params string[] lines) 136 | { 137 | return _editorHost.CreateTextView(contentType, lines); 138 | } 139 | 140 | /// 141 | /// Get or create a content type of the specified name with the specified base content type 142 | /// 143 | public IContentType GetOrCreateContentType(string type, string baseType) 144 | { 145 | return _editorHost.GetOrCreateContentType(type, baseType); 146 | } 147 | 148 | void IDisposable.Dispose() 149 | { 150 | Dispose(); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/EditorUtilsTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 8.0.30703 8 | 2.0 9 | {BAED09B7-25D9-4DD8-8558-BAC9730BA3F3} 10 | Library 11 | Properties 12 | EditorUtils.UnitTest 13 | EditorUtils.UnitTest 14 | $(EditorFrameworkVersion) 15 | 512 16 | ..\ 17 | true 18 | 19 | 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | $(DefineConstants);DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | pdbonly 30 | true 31 | bin\Release\ 32 | $(DefineConstants);TRACE 33 | prompt 34 | 4 35 | 36 | 37 | true 38 | 39 | 40 | Key.snk 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | ..\..\packages\Moq.4.0.10827\lib\NET40\Moq.dll 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | ..\..\packages\xunit.1.9.1\lib\net20\xunit.dll 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 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 | {FB418222-C105-4942-8EEB-832DDCFFD89D} 96 | EditorUtils 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/ExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Microsoft.VisualStudio.Utilities; 3 | using Xunit; 4 | 5 | namespace EditorUtils.UnitTest 6 | { 7 | public sealed class ExtensionsTest : EditorHostTest 8 | { 9 | [Fact] 10 | public void GetSourceBuffersRecursive_Simple() 11 | { 12 | var textBuffer1 = CreateTextBuffer("hello"); 13 | var textBuffer2 = CreateTextBuffer(" world"); 14 | var projectionBuffer = CreateProjectionBuffer( 15 | textBuffer1.GetExtent(), 16 | textBuffer2.GetExtent()); 17 | 18 | Assert.Equal("hello world", projectionBuffer.GetLine(0).GetText()); 19 | var all = projectionBuffer.GetSourceBuffersRecursive().ToList(); 20 | Assert.Equal(2, all.Count); 21 | Assert.True(all.Contains(textBuffer1)); 22 | Assert.True(all.Contains(textBuffer2)); 23 | } 24 | 25 | [Fact] 26 | public void GetSourceBuffersRecursive_Nested() 27 | { 28 | var textBuffer1 = CreateTextBuffer("hello"); 29 | var textBuffer2 = CreateTextBuffer(" "); 30 | var textBuffer3 = CreateTextBuffer("world"); 31 | var projectionBuffer1 = CreateProjectionBuffer( 32 | textBuffer1.GetExtent(), 33 | textBuffer2.GetExtent()); 34 | var projectionBuffer2 = CreateProjectionBuffer( 35 | projectionBuffer1.GetExtent(), 36 | textBuffer3.GetExtent()); 37 | 38 | Assert.Equal("hello world", projectionBuffer2.GetLine(0).GetText()); 39 | var all = projectionBuffer2.GetSourceBuffersRecursive().ToList(); 40 | Assert.Equal(3, all.Count); 41 | Assert.True(all.Contains(textBuffer1)); 42 | Assert.True(all.Contains(textBuffer2)); 43 | Assert.True(all.Contains(textBuffer3)); 44 | } 45 | 46 | [Fact] 47 | public void TryGetPropertySafe_Found() 48 | { 49 | var col = new PropertyCollection(); 50 | var key = new object(); 51 | col.AddProperty(key, "target"); 52 | 53 | string value; 54 | Assert.True(col.TryGetPropertySafe(key, out value)); 55 | Assert.Equal("target", value); 56 | } 57 | 58 | [Fact] 59 | public void TryGetPropertySafe_NotFound() 60 | { 61 | var col = new PropertyCollection(); 62 | var key = new object(); 63 | 64 | string value; 65 | Assert.False(col.TryGetPropertySafe(key, out value)); 66 | } 67 | 68 | /// 69 | /// Make sure it doesn't throw if the value is the wrong type 70 | /// 71 | [Fact] 72 | public void TryGetPropertySafe_WrongType() 73 | { 74 | var col = new PropertyCollection(); 75 | var key = new object(); 76 | col.AddProperty(key, this); 77 | 78 | string value; 79 | Assert.False(col.TryGetPropertySafe(key, out value)); 80 | } 81 | 82 | [Fact] 83 | public void GetLastLine_WithNonEmptyLastLine_ReturnsCorrectLastLine() 84 | { 85 | var textBuffer = CreateTextBuffer("hello","World", "Foo"); 86 | var lastLine = textBuffer.GetSpan(0, textBuffer.CurrentSnapshot.Length).GetLastLine(); 87 | 88 | Assert.True(lastLine.LineNumber == 2); 89 | } 90 | 91 | [Fact] 92 | public void GetLastLine_WithEmptyLastLine_ReturnsCorrectLastLine() 93 | { 94 | var textBuffer = CreateTextBuffer("hello","World", ""); 95 | var lastLine = textBuffer.GetSpan(0, textBuffer.CurrentSnapshot.Length).GetLastLine(); 96 | 97 | Assert.True(lastLine.LineNumber == 2); 98 | } 99 | 100 | 101 | 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/Test/EditorUtilsTest/Key.snk -------------------------------------------------------------------------------- /Test/EditorUtilsTest/LineRangeTest.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using System; 3 | 4 | namespace EditorUtils.UnitTest 5 | { 6 | public abstract class LineRangeTest 7 | { 8 | public sealed class IntersectsTest : LineRangeTest 9 | { 10 | /// 11 | /// Set of not intersecting ranges 12 | /// 13 | [Fact] 14 | public void Intersects_SimpleDoesnt() 15 | { 16 | var left = LineRange.CreateFromBounds(0, 1); 17 | var right = LineRange.CreateFromBounds(3, 4); 18 | Assert.False(left.Intersects(right)); 19 | } 20 | 21 | /// 22 | /// Set of intersecting ranges 23 | /// 24 | [Fact] 25 | public void Intersects_SimpleDoes() 26 | { 27 | var left = LineRange.CreateFromBounds(0, 2); 28 | var right = LineRange.CreateFromBounds(1, 4); 29 | Assert.True(left.Intersects(right)); 30 | } 31 | 32 | /// 33 | /// The intersect if they have the same boundary lines (essentially if they touch 34 | /// each other) 35 | /// 36 | [Fact] 37 | public void Intersects_DoesAtBorder() 38 | { 39 | var left = LineRange.CreateFromBounds(0, 2); 40 | var right = LineRange.CreateFromBounds(3, 4); 41 | Assert.True(left.Intersects(right)); 42 | } 43 | } 44 | 45 | public sealed class CreateFromBoundsTest : LineRangeTest 46 | { 47 | [Fact] 48 | public void Simple() 49 | { 50 | var lineRange = LineRange.CreateFromBounds(1, 3); 51 | Assert.Equal(3, lineRange.Count); 52 | Assert.Equal(1, lineRange.StartLineNumber); 53 | Assert.Equal(3, lineRange.LastLineNumber); 54 | } 55 | 56 | [Fact] 57 | public void BadBounds() 58 | { 59 | Assert.Throws(() => LineRange.CreateFromBounds(3, 1)); 60 | } 61 | } 62 | 63 | public sealed class EqualityTest : LineRangeTest 64 | { 65 | void Run(EqualityUnit equalityUnit) 66 | { 67 | EqualityUtil.RunAll( 68 | (x, y) => x == y, 69 | (x, y) => x != y, 70 | equalityUnit); 71 | } 72 | 73 | [Fact] 74 | public void Test1() 75 | { 76 | var equalityUnit = EqualityUnit 77 | .Create(new LineRange(1, 1)) 78 | .WithEqualValues(new LineRange(1, 1)) 79 | .WithNotEqualValues(new LineRange(2, 1), new LineRange(1, 3)); 80 | Run(equalityUnit); 81 | } 82 | 83 | [Fact] 84 | public void Test2() 85 | { 86 | var equalityUnit = EqualityUnit 87 | .Create(new LineRange(2, 3)) 88 | .WithEqualValues(new LineRange(2, 3)) 89 | .WithNotEqualValues(new LineRange(2, 1), new LineRange(1, 3)); 90 | Run(equalityUnit); 91 | } 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/MemoryLeakTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Threading; 3 | using Xunit; 4 | using Microsoft.VisualStudio.Text.Editor; 5 | using Microsoft.VisualStudio.Text; 6 | 7 | namespace EditorUtils.UnitTest 8 | { 9 | /// 10 | /// Used for detecting leaks in our components 11 | /// 12 | public sealed class MemoryLeakTest : EditorHostTest 13 | { 14 | private void RunGarbageCollector() 15 | { 16 | for (var i = 0; i < 15; i++) 17 | { 18 | Dispatcher.CurrentDispatcher.DoEvents(); 19 | GC.Collect(); 20 | GC.WaitForPendingFinalizers(); 21 | GC.Collect(); 22 | } 23 | } 24 | 25 | private void ClearUndoHistory(ITextBuffer textBuffer) 26 | { 27 | IBasicUndoHistory basicUndoHistory; 28 | Assert.True(BasicUndoHistoryRegistry.TryGetBasicUndoHistory(textBuffer, out basicUndoHistory)); 29 | basicUndoHistory.Clear(); 30 | } 31 | 32 | private void DoWork(Action action) 33 | { 34 | action(); 35 | } 36 | 37 | /// 38 | /// Ensure the undo history doesn't keep the ITextView alive due to a simple transaction 39 | /// 40 | [Fact] 41 | public void UndoTransactionSimple() 42 | { 43 | var textBuffer = CreateTextBuffer(""); 44 | var textView = TextEditorFactoryService.CreateTextView(textBuffer); 45 | var weakTextView = new WeakReference(textView); 46 | DoWork( 47 | () => 48 | { 49 | var undoManager = TextBufferUndoManagerProvider.GetTextBufferUndoManager(textBuffer); 50 | using (var transaction = undoManager.TextBufferUndoHistory.CreateTransaction("Test Edit")) 51 | { 52 | textBuffer.SetText("hello world"); 53 | transaction.Complete(); 54 | } 55 | }); 56 | 57 | textView.Close(); 58 | textView = null; 59 | RunGarbageCollector(); 60 | Assert.Null(weakTextView.Target); 61 | } 62 | 63 | /// 64 | /// Ensure the undo history doesn't keep the ITextView alive after an undo primitive 65 | /// surrounding the caret is added to the undo stack 66 | /// 67 | [Fact] 68 | public void UndoTransactionWithCaretPrimitive() 69 | { 70 | var textBuffer = CreateTextBuffer(""); 71 | var textView = TextEditorFactoryService.CreateTextView(textBuffer); 72 | var weakTextView = new WeakReference(textView); 73 | DoWork( 74 | () => 75 | { 76 | var undoManager = TextBufferUndoManagerProvider.GetTextBufferUndoManager(textBuffer); 77 | using (var transaction = undoManager.TextBufferUndoHistory.CreateTransaction("Test Edit")) 78 | { 79 | var operations = EditorOperationsFactoryService.GetEditorOperations(textView); 80 | operations.AddBeforeTextBufferChangePrimitive(); 81 | textBuffer.SetText("hello world"); 82 | transaction.Complete(); 83 | } 84 | }); 85 | 86 | textView.Close(); 87 | textView = null; 88 | 89 | // The AddBeforeTextBufferChangePrimitive put the ITextView into the undo stack 90 | // so we need to clear it out here 91 | ClearUndoHistory(textBuffer); 92 | 93 | RunGarbageCollector(); 94 | Assert.Null(weakTextView.Target); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/MockFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Text; 2 | using Microsoft.VisualStudio.Text.Editor; 3 | using Microsoft.VisualStudio.Text.Formatting; 4 | using Moq; 5 | 6 | namespace EditorUtils.UnitTest 7 | { 8 | internal sealed class MockFactory 9 | { 10 | MockRepository _factory; 11 | 12 | internal MockFactory(MockRepository factory = null) 13 | { 14 | _factory = factory ?? new MockRepository(MockBehavior.Strict); 15 | } 16 | 17 | internal Mock CreateTextViewLine(SnapshotLineRange lineRange) 18 | { 19 | var mock = _factory.Create(); 20 | mock.SetupGet(x => x.Start).Returns(lineRange.Start); 21 | mock.SetupGet(x => x.End).Returns(lineRange.EndIncludingLineBreak); 22 | return mock; 23 | } 24 | 25 | internal Mock CreateTextViewLineCollection(SnapshotLineRange lineRange) 26 | { 27 | var mock = _factory.Create(); 28 | var firstLineRange = new SnapshotLineRange(lineRange.Snapshot, lineRange.StartLineNumber, 1); 29 | var firstLine = CreateTextViewLine(firstLineRange); 30 | mock.SetupGet(x => x.FirstVisibleLine).Returns(firstLine.Object); 31 | 32 | var lastLineRange = new SnapshotLineRange(lineRange.Snapshot, lineRange.LastLineNumber, 1); 33 | var lastLine = CreateTextViewLine(lastLineRange); 34 | mock.SetupGet(x => x.LastVisibleLine).Returns(lastLine.Object); 35 | 36 | return mock; 37 | } 38 | 39 | internal Mock CreateTextView(ITextBuffer textBuffer) 40 | { 41 | var mock = _factory.Create(); 42 | mock.SetupGet(x => x.TextBuffer).Returns(textBuffer); 43 | mock.SetupGet(x => x.TextSnapshot).Returns(() => textBuffer.CurrentSnapshot); 44 | mock.SetupGet(x => x.InLayout).Returns(true); 45 | return mock; 46 | } 47 | 48 | internal void SetVisibleLineRange(Mock textView, SnapshotLineRange lineRange) 49 | { 50 | textView.SetupGet(x => x.InLayout).Returns(false); 51 | textView.SetupGet(x => x.TextViewLines).Returns(CreateTextViewLineCollection(lineRange).Object); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("EditorUtilsTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft IT")] 12 | [assembly: AssemblyProduct("EditorUtilsTest")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft IT 2011")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("655a9ede-a3f9-4a60-bc41-faf214c270a6")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/ProtectedOperationsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Threading; 3 | using EditorUtils.Implementation.Utilities; 4 | using Microsoft.VisualStudio.Text; 5 | using Moq; 6 | using Xunit; 7 | 8 | namespace EditorUtils.UnitTest 9 | { 10 | public sealed class ProtectedOperationsTest 11 | { 12 | private readonly Mock _errorHandler; 13 | private readonly ProtectedOperations _protectedOperationsRaw; 14 | private readonly IProtectedOperations _protectedOperations; 15 | 16 | public ProtectedOperationsTest() 17 | { 18 | _errorHandler = new Mock(MockBehavior.Strict); 19 | _protectedOperationsRaw = new ProtectedOperations(_errorHandler.Object); 20 | _protectedOperations = _protectedOperationsRaw; 21 | } 22 | 23 | /// 24 | /// Verify the returned action will execute the original one 25 | /// 26 | [Fact] 27 | public void GetProtectedAction_Standard() 28 | { 29 | var didRun = false; 30 | var protectedAction = _protectedOperations.GetProtectedAction(delegate { didRun = true; }); 31 | protectedAction(); 32 | Assert.True(didRun); 33 | } 34 | 35 | /// 36 | /// Verify that when the original action throws that it is passed on to the 37 | /// listed IExtensionErrorHandlers 38 | /// 39 | [Fact] 40 | public void GetProtectedAction_Throws() 41 | { 42 | var exception = new Exception("hello world"); 43 | _errorHandler.Setup(x => x.HandleError(It.IsAny(), exception)).Verifiable(); 44 | var protectedAction = _protectedOperations.GetProtectedAction(delegate { throw exception; }); 45 | protectedAction(); 46 | _errorHandler.Verify(); 47 | } 48 | 49 | /// 50 | /// Verify the returned EventHandler will execute the original one 51 | /// 52 | [Fact] 53 | public void GetProtectedEventHandler_Standard() 54 | { 55 | var didRun = false; 56 | var protectedEventHandler = _protectedOperations.GetProtectedEventHandler(delegate { didRun = true; }); 57 | protectedEventHandler(null, EventArgs.Empty); 58 | Assert.True(didRun); 59 | } 60 | 61 | /// 62 | /// Verify that when the original EventHandler throws that it is passed on to the 63 | /// listed IExtensionErrorHandlers 64 | /// 65 | [Fact] 66 | public void GetProtectedEventHandler_Throws() 67 | { 68 | var exception = new Exception("hello world"); 69 | _errorHandler.Setup(x => x.HandleError(It.IsAny(), exception)).Verifiable(); 70 | var protectedEventHandler = _protectedOperations.GetProtectedEventHandler(delegate { throw exception; }); 71 | protectedEventHandler(null, EventArgs.Empty); 72 | _errorHandler.Verify(); 73 | } 74 | 75 | /// 76 | /// Verify that the BeginInvoke will actually schedule the original action 77 | /// 78 | [Fact] 79 | public void BeginInvoke_Priority_Standard() 80 | { 81 | var didRun = false; 82 | _protectedOperations.BeginInvoke(delegate { didRun = true; }, DispatcherPriority.Normal); 83 | Dispatcher.CurrentDispatcher.DoEvents(); 84 | Assert.True(didRun); 85 | } 86 | 87 | /// 88 | /// Verify that when an exception is thrown during processing that it makes it's way to the 89 | /// IExtensionErrorHandler 90 | /// 91 | [Fact] 92 | public void BeginInvoke_Priority_Throws() 93 | { 94 | var exception = new Exception("hello world"); 95 | _errorHandler.Setup(x => x.HandleError(It.IsAny(), exception)).Verifiable(); 96 | _protectedOperations.BeginInvoke(delegate { throw exception; }, DispatcherPriority.Normal); 97 | Dispatcher.CurrentDispatcher.DoEvents(); 98 | _errorHandler.Verify(); 99 | } 100 | 101 | /// 102 | /// Verify that the BeginInvoke will actually schedule the original action 103 | /// 104 | [Fact] 105 | public void BeginInvoke_NoPriority_Standard() 106 | { 107 | var didRun = false; 108 | _protectedOperations.BeginInvoke(delegate { didRun = true; }); 109 | Dispatcher.CurrentDispatcher.DoEvents(); 110 | Assert.True(didRun); 111 | } 112 | 113 | /// 114 | /// Verify that when an exception is thrown during processing that it makes it's way to the 115 | /// IExtensionErrorHandler 116 | /// 117 | [Fact] 118 | public void BeginInvoke_NoPriority_Throws() 119 | { 120 | var exception = new Exception("hello world"); 121 | _errorHandler.Setup(x => x.HandleError(It.IsAny(), exception)).Verifiable(); 122 | _protectedOperations.BeginInvoke(delegate { throw exception; }); 123 | Dispatcher.CurrentDispatcher.DoEvents(); 124 | _errorHandler.Verify(); 125 | } 126 | } 127 | } 128 | 129 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/ReadOnlyStackTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using EditorUtils.Implementation.Utilities; 4 | using Xunit; 5 | 6 | namespace EditorUtils.UnitTest 7 | { 8 | public abstract class ReadOnlyStackTest 9 | { 10 | public sealed class PopTest : ReadOnlyStackTest 11 | { 12 | [Fact] 13 | public void EmptyThrows() 14 | { 15 | var stack = ReadOnlyStack.Empty; 16 | Assert.Throws(() => stack.Pop()); 17 | } 18 | 19 | [Fact] 20 | public void Simple() 21 | { 22 | var stack = ReadOnlyStack.Empty; 23 | stack = stack.Push(1); 24 | stack = stack.Pop(); 25 | Assert.True(stack.IsEmpty); 26 | Assert.Equal(0, stack.Count); 27 | } 28 | } 29 | 30 | public sealed class PushTest : ReadOnlyStackTest 31 | { 32 | [Fact] 33 | public void EnumerateFirstInLastOut() 34 | { 35 | var stack = ReadOnlyStack.Empty; 36 | for (int i = 0; i < 3; i++) 37 | { 38 | stack = stack.Push(i); 39 | } 40 | 41 | Assert.Equal(new[] { 2, 1, 0 }, stack.ToList()); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/SnapshotLineRangeTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.Text; 3 | using Xunit; 4 | 5 | namespace EditorUtils.UnitTest 6 | { 7 | public abstract class SnapshotLineRangeTest : EditorHostTest 8 | { 9 | private ITextBuffer _textBuffer; 10 | 11 | public void Create(params string[] lines) 12 | { 13 | _textBuffer = EditorHost.CreateTextBuffer(lines); 14 | } 15 | 16 | public sealed class CreateTest : SnapshotLineRangeTest 17 | { 18 | [Fact] 19 | public void ForExtent() 20 | { 21 | Create("cat", "dog"); 22 | var lineRange = SnapshotLineRange.CreateForExtent(_textBuffer.CurrentSnapshot); 23 | Assert.Equal(2, lineRange.Count); 24 | Assert.Equal(_textBuffer.CurrentSnapshot.Length, lineRange.ExtentIncludingLineBreak.Length); 25 | } 26 | 27 | [Fact] 28 | public void ForLine() 29 | { 30 | Create("cat", "dog"); 31 | var line = _textBuffer.CurrentSnapshot.GetLineFromLineNumber(1); 32 | var lineRange = SnapshotLineRange.CreateForLine(line); 33 | Assert.Equal(1, lineRange.StartLineNumber); 34 | Assert.Equal(1, lineRange.Count); 35 | Assert.Equal("dog", lineRange.GetText()); 36 | } 37 | } 38 | 39 | public sealed class MiscTest : SnapshotLineRangeTest 40 | { 41 | [Fact] 42 | public void GetText() 43 | { 44 | Create("cat", "dog"); 45 | Assert.Equal("dog", _textBuffer.GetLineRange(1).GetText()); 46 | } 47 | 48 | [Fact] 49 | public void GetTextIncludingLineBreak() 50 | { 51 | Create("cat", "dog"); 52 | Assert.Equal("cat" + Environment.NewLine, _textBuffer.GetLineRange(0).GetTextIncludingLineBreak()); 53 | } 54 | 55 | [Fact] 56 | public void Lines1() 57 | { 58 | Create("a", "b"); 59 | var lineRange = SnapshotLineRange.CreateForLineAndMaxCount(_textBuffer.GetLine(0), 400); 60 | Assert.Equal(2, lineRange.Count); 61 | } 62 | } 63 | 64 | public sealed class EqualityTest : SnapshotLineRangeTest 65 | { 66 | void Run(EqualityUnit equalityUnit) 67 | { 68 | EqualityUtil.RunAll( 69 | (x, y) => x == y, 70 | (x, y) => x != y, 71 | equalityUnit); 72 | } 73 | 74 | [Fact] 75 | public void Simple() 76 | { 77 | Create("cat", "dog", "fish"); 78 | var equalityUnit = EqualityUnit 79 | .Create(_textBuffer.GetLineRange(1)) 80 | .WithEqualValues(_textBuffer.GetLineRange(1)) 81 | .WithNotEqualValues(_textBuffer.GetLineRange(1, 2), _textBuffer.GetLineRange(2)); 82 | Run(equalityUnit); 83 | } 84 | 85 | [Fact] 86 | public void DifferentSnapshots() 87 | { 88 | Create("cat", "dog", "fish"); 89 | var otherTextBuffer = CreateTextBuffer("cat", "dog", "fish"); 90 | var equalityUnit = EqualityUnit 91 | .Create(_textBuffer.GetLineRange(1)) 92 | .WithEqualValues(_textBuffer.GetLineRange(1)) 93 | .WithNotEqualValues(otherTextBuffer.GetLineRange(1)); 94 | Run(equalityUnit); 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/TaggerUtilTest.cs: -------------------------------------------------------------------------------- 1 | using EditorUtils.Implementation.Tagging; 2 | using Microsoft.VisualStudio.Text; 3 | using Xunit; 4 | 5 | namespace EditorUtils.UnitTest 6 | { 7 | public abstract class TaggerUtilTest : EditorHostTest 8 | { 9 | public sealed class AdjustRequestSpanTest : TaggerUtilTest 10 | { 11 | private ITextBuffer _textBuffer; 12 | 13 | private void Create(params string[] lines) 14 | { 15 | _textBuffer = CreateTextBuffer(lines); 16 | } 17 | 18 | /// 19 | /// When there is a new request it becomes the entire request that we are looking at 20 | /// 21 | [Fact] 22 | public void NewRequest() 23 | { 24 | Create("cat dog"); 25 | var span = _textBuffer.GetSpan(0, 3); 26 | Assert.Equal(span, TaggerUtil.AdjustRequestedSpan(null, span)); 27 | } 28 | 29 | [Fact] 30 | public void BiggerAtEnd() 31 | { 32 | Create("cat dog"); 33 | var span1 = _textBuffer.GetSpan(0, 1); 34 | var span2 = _textBuffer.GetSpan(3, 1); 35 | var overarching = span1.CreateOverarching(span2); 36 | Assert.Equal(overarching, TaggerUtil.AdjustRequestedSpan(span1, span2)); 37 | } 38 | 39 | [Fact] 40 | public void BiggerAtStart() 41 | { 42 | Create("cat dog"); 43 | var span1 = _textBuffer.GetSpan(3, 1); 44 | var span2 = _textBuffer.GetSpan(0, 1); 45 | var overarching = span1.CreateOverarching(span2); 46 | Assert.Equal(overarching, TaggerUtil.AdjustRequestedSpan(span1, span2)); 47 | } 48 | 49 | /// 50 | /// For a forward edit we need to move the old span forward and then get the overarching 51 | /// value 52 | /// 53 | [Fact] 54 | public void ForwardEditSpan() 55 | { 56 | Create("cat dog"); 57 | var span1 = _textBuffer.GetSpan(0, 1); 58 | _textBuffer.Insert(4, "fish "); 59 | var span2 = _textBuffer.GetSpan(4, 4); 60 | var overarching = _textBuffer.GetSpan(0, 8); 61 | Assert.Equal(overarching, TaggerUtil.AdjustRequestedSpan(span1, span2)); 62 | } 63 | 64 | /// 65 | /// It is possible to request spans in the past (previous edit). This can happen when 66 | /// dealing with projection buffers (most specifically with web applications). In this 67 | /// case just default back to the entire buffer. This value isn't used for any of our caching 68 | /// but instead is just the value we provide to the editor when we raise a changed event 69 | /// 70 | [Fact] 71 | public void BackwardEditSpan() 72 | { 73 | Create("cat dog"); 74 | var oldSpan = _textBuffer.GetSpan(0, 1); 75 | var oldAll = _textBuffer.CurrentSnapshot.GetExtent(); 76 | _textBuffer.Insert(4, "fish "); 77 | var newSpan = _textBuffer.GetSpan(4, 4); 78 | Assert.Equal(oldAll, TaggerUtil.AdjustRequestedSpan(newSpan, oldSpan)); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/TestUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Collections.ObjectModel; 6 | using Microsoft.VisualStudio.Text.Tagging; 7 | using Microsoft.VisualStudio.Text; 8 | 9 | namespace EditorUtils.UnitTest 10 | { 11 | internal static class TestUtils 12 | { 13 | internal static ITagSpan CreateTagSpan(SnapshotSpan span) 14 | { 15 | return new TagSpan(span, new TextMarkerTag("my tag")); 16 | } 17 | 18 | internal static IEnumerable GetDogSpans(string text) 19 | { 20 | var index = text.IndexOf("dog"); 21 | while (index >= 0) 22 | { 23 | yield return new Span(index, 3); 24 | index = text.IndexOf("dog", index + 1); 25 | } 26 | } 27 | 28 | internal static ReadOnlyCollection> GetDogTags(SnapshotSpan span) 29 | { 30 | var text = span.GetText(); 31 | var list = new List>(); 32 | foreach (var current in GetDogSpans(text)) 33 | { 34 | var tagSpan = new SnapshotSpan(span.Start.Add(current.Start), current.Length); 35 | list.Add(CreateTagSpan(tagSpan)); 36 | } 37 | 38 | return list.ToReadOnlyCollectionShallow(); 39 | } 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/TestableSynchronizationContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | 5 | namespace EditorUtils.UnitTest 6 | { 7 | public sealed class TestableSynchronizationContext : SynchronizationContext 8 | { 9 | private SynchronizationContext _oldSynchronizationContext; 10 | private List _list = new List(); 11 | public bool IsEmpty 12 | { 13 | get { return 0 == _list.Count; } 14 | } 15 | 16 | public override void Post(SendOrPostCallback d, object state) 17 | { 18 | _list.Add(() => d(state)); 19 | } 20 | 21 | public void RunOne() 22 | { 23 | if (_list.Count == 0) 24 | { 25 | return; 26 | } 27 | 28 | try 29 | { 30 | _list[0](); 31 | _list.RemoveAt(0); 32 | } 33 | catch 34 | { 35 | 36 | } 37 | } 38 | 39 | public void RunAll() 40 | { 41 | while (_list.Count > 0) 42 | { 43 | RunOne(); 44 | } 45 | } 46 | 47 | public void Install() 48 | { 49 | _oldSynchronizationContext = SynchronizationContext.Current; 50 | SynchronizationContext.SetSynchronizationContext(this); 51 | } 52 | 53 | public void Uninstall() 54 | { 55 | if (_oldSynchronizationContext != null) 56 | { 57 | SynchronizationContext.SetSynchronizationContext(_oldSynchronizationContext); 58 | _oldSynchronizationContext = null; 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/TextTaggerSource.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Text.Tagging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using Microsoft.VisualStudio.Text; 7 | using System.Collections.ObjectModel; 8 | using Microsoft.VisualStudio.Text.Editor; 9 | using System.Threading; 10 | 11 | namespace EditorUtils.UnitTest 12 | { 13 | /// 14 | /// Tags all occurences of the specified text in the buffer 15 | /// 16 | internal abstract class TextTaggerSource 17 | where T : ITag 18 | { 19 | private readonly T _tag; 20 | private string _text; 21 | 22 | internal event EventHandler Changed; 23 | 24 | internal string Text 25 | { 26 | get { return _text; } 27 | set 28 | { 29 | if (!StringComparer.Ordinal.Equals(_text, value)) 30 | { 31 | _text = value; 32 | RaiseChanged(); 33 | } 34 | } 35 | } 36 | 37 | internal T Tag 38 | { 39 | get { return _tag; } 40 | } 41 | 42 | internal TextTaggerSource(T tag) 43 | { 44 | _tag = tag; 45 | } 46 | 47 | internal void RaiseChanged() 48 | { 49 | var list = Changed; 50 | if (list != null) 51 | { 52 | list(this, EventArgs.Empty); 53 | } 54 | } 55 | 56 | internal static bool IsMatch(ITextSnapshot snapshot, int position, string text) 57 | { 58 | if (position + text.Length > snapshot.Length || string.IsNullOrEmpty(text)) 59 | { 60 | return false; 61 | } 62 | 63 | for (int i = 0; i < text.Length; i++) 64 | { 65 | if (snapshot[i + position] != text[i]) 66 | { 67 | return false; 68 | } 69 | } 70 | 71 | return true; 72 | } 73 | 74 | internal static ReadOnlyCollection> GetTags(string text, T tag, SnapshotSpan span) 75 | { 76 | var list = new List>(); 77 | var position = span.Start.Position; 78 | var snapshot = span.Snapshot; 79 | 80 | while (position < span.Length) 81 | { 82 | if (IsMatch(snapshot, position, text)) 83 | { 84 | var tagSpan = new SnapshotSpan(snapshot, start: position, length: text.Length); 85 | list.Add(new TagSpan(tagSpan, tag)); 86 | position += text.Length; 87 | } 88 | else 89 | { 90 | position++; 91 | } 92 | } 93 | 94 | return list.ToReadOnlyCollectionShallow(); 95 | } 96 | } 97 | 98 | /// 99 | /// Tags all occurences of the specified text in the buffer 100 | /// 101 | internal sealed class TextBasicTaggerSource : TextTaggerSource, IBasicTaggerSource 102 | where T : ITag 103 | { 104 | internal TextBasicTaggerSource(T tag) : base(tag) 105 | { 106 | 107 | } 108 | 109 | event EventHandler IBasicTaggerSource.Changed 110 | { 111 | add { Changed += value; } 112 | remove { Changed -= value; } 113 | } 114 | 115 | ReadOnlyCollection> IBasicTaggerSource.GetTags(SnapshotSpan span) 116 | { 117 | return GetTags(Text, Tag, span); 118 | } 119 | } 120 | 121 | internal sealed class TextAsyncTaggerSource : TextTaggerSource, IAsyncTaggerSource, T> 122 | where T : ITag 123 | { 124 | private readonly ITextBuffer _textBuffer; 125 | private readonly ITextView _textView; 126 | 127 | internal TextAsyncTaggerSource(T tag, ITextBuffer textBuffer) : base(tag) 128 | { 129 | _textBuffer = textBuffer; 130 | } 131 | 132 | internal TextAsyncTaggerSource(T tag, ITextView textView) : base(tag) 133 | { 134 | _textView = textView; 135 | _textBuffer = textView.TextBuffer; 136 | } 137 | 138 | int? IAsyncTaggerSource, T>.Delay 139 | { 140 | get { return 100; } 141 | } 142 | 143 | ITextSnapshot IAsyncTaggerSource, T>.TextSnapshot 144 | { 145 | get { return _textBuffer.CurrentSnapshot; } 146 | } 147 | 148 | ITextView IAsyncTaggerSource, T>.TextViewOptional 149 | { 150 | get { return _textView; } 151 | } 152 | 153 | event EventHandler IAsyncTaggerSource, T>.Changed 154 | { 155 | add { Changed += value; } 156 | remove { Changed -= value; } 157 | } 158 | 159 | Tuple IAsyncTaggerSource, T>.GetDataForSnapshot(ITextSnapshot snapshot) 160 | { 161 | return Tuple.Create(Text, Tag); 162 | } 163 | 164 | ReadOnlyCollection> IAsyncTaggerSource, T>.GetTagsInBackground(Tuple data, SnapshotSpan span, CancellationToken cancellationToken) 165 | { 166 | return GetTags(data.Item1, data.Item2, span); 167 | } 168 | 169 | bool IAsyncTaggerSource, T>.TryGetTagsPrompt(SnapshotSpan span, out IEnumerable> tags) 170 | { 171 | tags = null; 172 | return false; 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/VersioningTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using Xunit; 5 | using System.Reflection; 6 | using System.ComponentModel.Composition; 7 | 8 | namespace EditorUtils.UnitTest 9 | { 10 | public sealed class VersioningTest 11 | { 12 | /// 13 | /// Get all type defined in the system 14 | /// 15 | private List GetAllTypes() 16 | { 17 | var list = new List(); 18 | var seen = new HashSet(); 19 | var toVisit = new Stack(typeof(EditorUtilsFactory).Assembly.GetTypes()); 20 | while (toVisit.Count > 0) 21 | { 22 | var current = toVisit.Pop(); 23 | if (!seen.Add(current)) 24 | { 25 | continue; 26 | } 27 | 28 | list.Add(current); 29 | foreach (var cur in current.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic)) 30 | { 31 | toVisit.Push(cur); 32 | } 33 | } 34 | 35 | return list; 36 | } 37 | 38 | /// 39 | /// Make sure that there are no Export values in the system. EditorUtils does not use MEF to 40 | /// provide parts to consumers 41 | /// 42 | [Fact] 43 | public void EnsureNoExports() 44 | { 45 | var assembly = typeof(EditorUtilsFactory).Assembly; 46 | foreach (var cur in GetAllTypes()) 47 | { 48 | var all = cur 49 | .GetCustomAttributes(typeof(ExportAttribute), false) 50 | .Cast(); 51 | Assert.Equal(0, all.Count()); 52 | } 53 | } 54 | 55 | /// 56 | /// Make sure there are no Import values in the system. EditorUtils does not use MEF hence 57 | /// there can be no [ImportingConstructors] 58 | /// 59 | [Fact] 60 | public void EnsureNoImportingConstructors() 61 | { 62 | var assembly = typeof(EditorUtilsFactory).Assembly; 63 | foreach (var cur in GetAllTypes()) 64 | { 65 | foreach (var constructorInfo in cur.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic)) 66 | { 67 | var all = constructorInfo.GetCustomAttributes(typeof(ImportingConstructorAttribute), false); 68 | Assert.Equal(0, all.Count()); 69 | } 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Test/EditorUtilsTest/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Test/WordUnderCaret/Constants.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace WordUnderCaret 3 | { 4 | internal sealed class Constants 5 | { 6 | internal const string FormatName = "WordUnderCaret"; 7 | internal const string FormatDisplayName = "Word Under Caret"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Test/WordUnderCaret/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("WordUnderCaret")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("WordUnderCaret")] 13 | [assembly: AssemblyCopyright("")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 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 Build and Revision Numbers 30 | // by using the '*' as shown below: 31 | // [assembly: AssemblyVersion("1.0.*")] 32 | [assembly: AssemblyVersion("1.0.0.0")] 33 | [assembly: AssemblyFileVersion("1.0.0.0")] 34 | -------------------------------------------------------------------------------- /Test/WordUnderCaret/WordUnderCaret.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 10.0.20305 8 | 2.0 9 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10 | {620BEC49-DF9C-406A-9492-BD157BAB95BC} 11 | Library 12 | Properties 13 | WordUnderCaret 14 | WordUnderCaret 15 | v4.0 16 | 512 17 | false 18 | $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\VSSDK\Microsoft.VsSDK.targets 19 | $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\VSSDK\Microsoft.VsSDK.targets 20 | Program 21 | $(DevEnvDir)\devenv.exe 22 | /rootsuffix Exp 23 | 24 | 25 | 26 | 11.0 27 | 28 | 29 | 30 | 12.0 31 | 32 | 33 | 34 | 14.0 35 | 36 | 37 | true 38 | full 39 | false 40 | bin\Debug\ 41 | DEBUG;TRACE 42 | prompt 43 | 4 44 | 45 | 46 | pdbonly 47 | true 48 | bin\Release\ 49 | TRACE 50 | prompt 51 | 4 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 | 80 | 81 | 82 | Designer 83 | 84 | 85 | 86 | 87 | {FB418222-C105-4942-8EEB-832DDCFFD89D} 88 | EditorUtils 89 | 90 | 91 | 92 | 93 | 100 | 101 | -------------------------------------------------------------------------------- /Test/WordUnderCaret/WordUnderCaret.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WordUnderCaret", "WordUnderCaret.csproj", "{620BEC49-DF9C-406A-9492-BD157BAB95BC}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {620BEC49-DF9C-406A-9492-BD157BAB95BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {620BEC49-DF9C-406A-9492-BD157BAB95BC}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {620BEC49-DF9C-406A-9492-BD157BAB95BC}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {620BEC49-DF9C-406A-9492-BD157BAB95BC}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /Test/WordUnderCaret/WordUnderCaretFormat.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using System.Windows.Media; 3 | using Microsoft.VisualStudio.Text.Classification; 4 | using Microsoft.VisualStudio.Utilities; 5 | 6 | namespace WordUnderCaret 7 | { 8 | [Export(typeof(EditorFormatDefinition))] 9 | [Name(Constants.FormatName)] 10 | [UserVisible(true)] 11 | internal sealed class WordUnderCaretFormat : MarkerFormatDefinition 12 | { 13 | public WordUnderCaretFormat() 14 | { 15 | DisplayName = Constants.FormatDisplayName; 16 | BackgroundColor = Colors.BlueViolet; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Test/WordUnderCaret/WordUnderCaretTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using EditorUtils; 6 | using Microsoft.VisualStudio.Text.Tagging; 7 | using System.ComponentModel.Composition; 8 | using Microsoft.VisualStudio.Text; 9 | using Microsoft.VisualStudio.Text.Editor; 10 | using Microsoft.VisualStudio.Utilities; 11 | 12 | namespace WordUnderCaret 13 | { 14 | [Export(typeof(IViewTaggerProvider))] 15 | [ContentType("any")] 16 | [TextViewRole(PredefinedTextViewRoles.Document)] 17 | [TagType(typeof(TextMarkerTag))] 18 | internal sealed class WordUnderCaretTaggerProvider : IViewTaggerProvider 19 | { 20 | private readonly object _key = new object(); 21 | 22 | #region IViewTaggerProvider 23 | 24 | ITagger IViewTaggerProvider.CreateTagger(ITextView textView, ITextBuffer textBuffer) 25 | { 26 | if (textView.TextBuffer != textBuffer) 27 | { 28 | return null; 29 | } 30 | 31 | var tagger = EditorUtilsFactory.CreateTagger( 32 | textView.Properties, 33 | _key, 34 | () => new WordUnderCaretTagger(textView)); 35 | 36 | return (ITagger)(object)tagger; 37 | } 38 | 39 | #endregion 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Test/WordUnderCaret/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WordUnderCaret 5 | Microsoft IT 6 | 1.0 7 | This is a sample classifier extension to the Visual Studio Editor. 8 | 1033 9 | 10 | 11 | Pro 12 | 13 | 14 | 15 | 16 | 17 | 18 | |%CurrentProject%| 19 | 20 | 21 | -------------------------------------------------------------------------------- /Tools/xunit.console.clr4.x86.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/Tools/xunit.console.clr4.x86.exe -------------------------------------------------------------------------------- /Tools/xunit.console.clr4.x86.exe.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 14 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Tools/xunit.runner.utility.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredpar/EditorUtils/f1133e915fd309f4df76be4c39824ea27886561a/Tools/xunit.runner.utility.dll -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | 2 | # Branchs to build 3 | branches: 4 | only: 5 | - master 6 | 7 | configuration: Debug 8 | 9 | build: 10 | project: EditorUtils.sln 11 | verbosity: minimal 12 | 13 | test_script: 14 | - Tools\xunit.console.clr4.x86.exe Test\EditorUtilsTest\bin\Debug\EditorUtils.UnitTest.dll /silent 15 | 16 | --------------------------------------------------------------------------------