├── .github ├── FUNDING.yml └── workflows │ └── build.yaml ├── .gitignore ├── Directory.Build.props ├── LICENSE ├── README.md ├── example.png ├── nuget.config └── src ├── .editorconfig ├── AnalyticsConfig.cs ├── BaseOptionModel.cs ├── CSInlineColorViz.Tests ├── AllTaggersTests.cs ├── BaseTaggerTests.cs ├── CSInlineColorViz.Tests.csproj ├── Color32TaggerTests.cs ├── ColorCtorTaggerTests.cs ├── ColorFromArgbTaggerTests.cs ├── ColorListHelperTests.cs ├── FakeTextBuffer.cs ├── FunColorTaggerTests.cs ├── Properties │ └── AssemblyInfo.cs ├── SvgTaggerTests.cs ├── TaggerTestHelper.cs └── UnityTaggerTests.cs ├── Color32Tagger.cs ├── Color32TaggerProvider.cs ├── ColorAdornment.cs ├── ColorAdornmentTagger.cs ├── ColorAdornmentTaggerProvider.cs ├── ColorArgbTagger.cs ├── ColorArgbTaggerProvider.cs ├── ColorCtorTagger.cs ├── ColorCtorTaggerProvider.cs ├── ColorFromIntTagger.cs ├── ColorFromIntTaggerProvider.cs ├── ColorFromNameTagger.cs ├── ColorFromNameTaggerProvider.cs ├── ColorHslaTagger.cs ├── ColorHslaTaggerProvider.cs ├── ColorHsvaTagger.cs ├── ColorHsvaTaggerProvider.cs ├── ColorListHelper.cs ├── ColorRgbTagger.cs ├── ColorRgbTaggerProvider.cs ├── ColorRgbaTagger.cs ├── ColorRgbaTaggerProvider.cs ├── ColorSelectionDialog.cs ├── ColorTag.cs ├── ColorTagger.cs ├── ColorTaggerProvider.cs ├── ConsoleColorDialog.cs ├── CsInlineColorViz.csproj ├── CsInlineColorViz.sln ├── CsInlineColorVizPackage.cs ├── FunColorTagger.cs ├── FunColorTaggerProvider.cs ├── FunColorsDialog.cs ├── HexIntTagger.cs ├── HexIntTaggerProvider.cs ├── HexStringTagger.cs ├── HexStringTaggerProvider.cs ├── ITestableRegexColorTagger.cs ├── InternalSettings.cs ├── IntraTextAdornmentTagger.cs ├── KnownColorDialog.cs ├── LICENSE.txt ├── MauiProjTagger.cs ├── MauiProjTaggerProvider.cs ├── NamedColorDialog.cs ├── OutlineTextControl.cs ├── OutputPane.cs ├── PopupType.cs ├── Properties └── AssemblyInfo.cs ├── RegexTagger.cs ├── Resources └── Icon.png ├── SponsorDetector.cs ├── SponsorRequestHelper.cs ├── SvgTagger.cs ├── SvgTaggerProvider.cs ├── SystemColorsDialog.cs ├── TextDocumentHelper.cs ├── UnityColorsDialog.cs ├── UnityTagger.cs ├── UnityTaggerProvider.cs ├── UnityTextTagger.cs ├── UnityTextTaggerProvider.cs ├── filestosign.txt ├── signvsix.targets ├── source.extension.cs └── source.extension.vsixmanifest /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: mrlacey # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 16 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | name: "Build" 3 | 4 | on: 5 | push: 6 | branches: [main] 7 | paths-ignore: 8 | - '*.md' 9 | pull_request: 10 | branches: [main] 11 | paths-ignore: 12 | - '*.md' 13 | 14 | jobs: 15 | build: 16 | outputs: 17 | version: ${{ steps.vsix_version.outputs.version-number }} 18 | name: Build 19 | runs-on: windows-2022 20 | env: 21 | Configuration: Debug 22 | DeployExtension: False 23 | VsixManifestPath: src\source.extension.vsixmanifest 24 | VsixManifestSourcePath: src\source.extension.cs 25 | 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - name: Setup .NET build dependencies 30 | uses: timheuer/bootstrap-dotnet@v2 31 | with: 32 | nuget: 'false' 33 | sdk: 'false' 34 | msbuild: 'true' 35 | 36 | - name: Increment VSIX version 37 | id: vsix_version 38 | uses: timheuer/vsix-version-stamp@v2 39 | with: 40 | manifest-file: ${{ env.VsixManifestPath }} 41 | vsix-token-source-file: ${{ env.VsixManifestSourcePath }} 42 | 43 | - name: Build 44 | run: msbuild /v:m -restore /p:OutDir=\_built ./src/CsInlineColorViz.sln 45 | 46 | - name: Upload artifact 47 | uses: actions/upload-artifact@v4 48 | with: 49 | name: ${{ github.event.repository.name }}.vsix 50 | path: /_built/**/*.vsix 51 | 52 | - name: Run Tests 53 | # See https://github.com/microsoft/vstest-action/issues/31 54 | # uses: microsoft/vstest-action@v1.0.0 55 | uses: josepho0918/vstest-action@main 56 | with: 57 | searchFolder: /_built/ 58 | testAssembly: /**/*Tests.dll 59 | 60 | - name: Publish Test Results 61 | uses: EnricoMi/publish-unit-test-result-action/windows@v2 62 | id: test-results 63 | with: 64 | files: testresults\**\*.trx 65 | 66 | - name: Set badge color 67 | shell: bash 68 | run: | 69 | case ${{ fromJSON( steps.test-results.outputs.json ).conclusion }} in 70 | success) 71 | echo "BADGE_COLOR=31c653" >> $GITHUB_ENV 72 | ;; 73 | failure) 74 | echo "BADGE_COLOR=800000" >> $GITHUB_ENV 75 | ;; 76 | neutral) 77 | echo "BADGE_COLOR=696969" >> $GITHUB_ENV 78 | ;; 79 | esac 80 | 81 | - name: Create badge 82 | uses: emibcn/badge-action@808173dd03e2f30c980d03ee49e181626088eee8 83 | with: 84 | label: Tests 85 | status: '${{ fromJSON( steps.test-results.outputs.json ).formatted.stats.tests }} tests: ${{ fromJSON( steps.test-results.outputs.json ).conclusion }}' 86 | color: ${{ env.BADGE_COLOR }} 87 | path: CSInlineColorViz.badge.svg 88 | 89 | - name: Upload badge to Gist 90 | # Upload only for main branch 91 | if: > 92 | github.event_name == 'workflow_run' && github.event.workflow_run.head_branch == 'main' || 93 | github.event_name != 'workflow_run' && github.ref == 'refs/heads/main' 94 | uses: andymckay/append-gist-action@6e8d64427fe47cbacf4ab6b890411f1d67c07f3e 95 | with: 96 | token: ${{ secrets.GIST_TOKEN }} 97 | gistURL: https://gist.githubusercontent.com/mrlacey/c586ff0f495b4a8dd76ab0dbdf9c89e0 98 | file: CSInlineColorViz.badge.svg 99 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 12.0 5 | 6 | 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-2024 Matt Lacey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C# Inline Color Visualizer 2 | 3 | ![Works with Visual Studio 2022](https://img.shields.io/static/v1.svg?label=VS&message=2022&color=A853C7) 4 | [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) 5 | 6 | [![Build](https://github.com/mrlacey/CSInlineColorViz/actions/workflows/build.yaml/badge.svg)](https://github.com/mrlacey/CSInlineColorViz/actions/workflows/build.yaml) 7 | ![Tests](https://gist.githubusercontent.com/mrlacey/c586ff0f495b4a8dd76ab0dbdf9c89e0/raw/CSInlineColorViz.badge.svg) 8 | 9 | See samples of the colors you use within your C# code. 10 | Never again be forced to remember what a named color looks like or work out what color a HEX value represents. 11 | 12 | ![Example](example.png) 13 | 14 | Works with `*.cs`, `*.cshtml`, and `*.razor` files. 15 | -------------------------------------------------------------------------------- /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrlacey/CSInlineColorViz/15142ec27948ef6135379acc7594ce77137a810d/example.png -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/.editorconfig: -------------------------------------------------------------------------------- 1 | # Remove the line below if you want to inherit .editorconfig settings from higher directories 2 | root = true 3 | 4 | # C# files 5 | [*.cs] 6 | 7 | #### Core EditorConfig Options #### 8 | 9 | # Indentation and spacing 10 | indent_size = 4 11 | indent_style = tab 12 | tab_width = 4 13 | 14 | # New line preferences 15 | end_of_line = crlf 16 | insert_final_newline = false 17 | 18 | #### .NET Coding Conventions #### 19 | 20 | # Organize usings 21 | dotnet_separate_import_directive_groups = false 22 | dotnet_sort_system_directives_first = true 23 | file_header_template = unset 24 | 25 | # this. and Me. preferences 26 | dotnet_style_qualification_for_event = false 27 | dotnet_style_qualification_for_field = false 28 | dotnet_style_qualification_for_method = false 29 | dotnet_style_qualification_for_property = false 30 | 31 | # Language keywords vs BCL types preferences 32 | dotnet_style_predefined_type_for_locals_parameters_members = true 33 | dotnet_style_predefined_type_for_member_access = true 34 | 35 | # Parentheses preferences 36 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity 37 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity 38 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary 39 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity 40 | 41 | # Modifier preferences 42 | dotnet_style_require_accessibility_modifiers = for_non_interface_members 43 | 44 | # Expression-level preferences 45 | dotnet_style_coalesce_expression = true 46 | dotnet_style_collection_initializer = true 47 | dotnet_style_explicit_tuple_names = true 48 | dotnet_style_namespace_match_folder = true 49 | dotnet_style_null_propagation = true 50 | dotnet_style_object_initializer = true 51 | dotnet_style_operator_placement_when_wrapping = beginning_of_line 52 | dotnet_style_prefer_auto_properties = true 53 | dotnet_style_prefer_collection_expression = when_types_loosely_match 54 | dotnet_style_prefer_compound_assignment = true 55 | dotnet_style_prefer_conditional_expression_over_assignment = true 56 | dotnet_style_prefer_conditional_expression_over_return = true 57 | dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed 58 | dotnet_style_prefer_inferred_anonymous_type_member_names = true 59 | dotnet_style_prefer_inferred_tuple_names = true 60 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true 61 | dotnet_style_prefer_simplified_boolean_expressions = true 62 | dotnet_style_prefer_simplified_interpolation = true 63 | 64 | # Field preferences 65 | dotnet_style_readonly_field = true 66 | 67 | # Parameter preferences 68 | dotnet_code_quality_unused_parameters = all 69 | 70 | # Suppression preferences 71 | dotnet_remove_unnecessary_suppression_exclusions = none 72 | 73 | # New line preferences 74 | dotnet_style_allow_multiple_blank_lines_experimental = true 75 | dotnet_style_allow_statement_immediately_after_block_experimental = true 76 | 77 | #### C# Coding Conventions #### 78 | 79 | # var preferences 80 | csharp_style_var_elsewhere = false 81 | csharp_style_var_for_built_in_types = false 82 | csharp_style_var_when_type_is_apparent = false 83 | 84 | # Expression-bodied members 85 | csharp_style_expression_bodied_accessors = true 86 | csharp_style_expression_bodied_constructors = false 87 | csharp_style_expression_bodied_indexers = true 88 | csharp_style_expression_bodied_lambdas = true 89 | csharp_style_expression_bodied_local_functions = false 90 | csharp_style_expression_bodied_methods = false 91 | csharp_style_expression_bodied_operators = false 92 | csharp_style_expression_bodied_properties = true 93 | 94 | # Pattern matching preferences 95 | csharp_style_pattern_matching_over_as_with_null_check = true 96 | csharp_style_pattern_matching_over_is_with_cast_check = true 97 | csharp_style_prefer_extended_property_pattern = true 98 | csharp_style_prefer_not_pattern = true 99 | csharp_style_prefer_pattern_matching = true 100 | csharp_style_prefer_switch_expression = true 101 | 102 | # Null-checking preferences 103 | csharp_style_conditional_delegate_call = true 104 | 105 | # Modifier preferences 106 | csharp_prefer_static_local_function = true 107 | csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async 108 | csharp_style_prefer_readonly_struct = true 109 | csharp_style_prefer_readonly_struct_member = true 110 | 111 | # Code-block preferences 112 | csharp_prefer_braces = true 113 | csharp_prefer_simple_using_statement = true 114 | csharp_style_namespace_declarations = block_scoped 115 | csharp_style_prefer_method_group_conversion = true 116 | csharp_style_prefer_primary_constructors = true 117 | csharp_style_prefer_top_level_statements = true 118 | 119 | # Expression-level preferences 120 | csharp_prefer_simple_default_expression = true 121 | csharp_style_deconstructed_variable_declaration = true 122 | csharp_style_implicit_object_creation_when_type_is_apparent = true 123 | csharp_style_inlined_variable_declaration = true 124 | csharp_style_prefer_index_operator = true 125 | csharp_style_prefer_local_over_anonymous_function = true 126 | csharp_style_prefer_null_check_over_type_check = true 127 | csharp_style_prefer_range_operator = true 128 | csharp_style_prefer_tuple_swap = true 129 | csharp_style_prefer_utf8_string_literals = true 130 | csharp_style_throw_expression = true 131 | csharp_style_unused_value_assignment_preference = discard_variable 132 | csharp_style_unused_value_expression_statement_preference = discard_variable 133 | 134 | # 'using' directive preferences 135 | csharp_using_directive_placement = outside_namespace 136 | 137 | # New line preferences 138 | csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true 139 | csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true 140 | csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true 141 | csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true 142 | csharp_style_allow_embedded_statements_on_same_line_experimental = true 143 | 144 | #### C# Formatting Rules #### 145 | 146 | # New line preferences 147 | csharp_new_line_before_catch = true 148 | csharp_new_line_before_else = true 149 | csharp_new_line_before_finally = true 150 | csharp_new_line_before_members_in_anonymous_types = true 151 | csharp_new_line_before_members_in_object_initializers = true 152 | csharp_new_line_before_open_brace = all 153 | csharp_new_line_between_query_expression_clauses = true 154 | 155 | # Indentation preferences 156 | csharp_indent_block_contents = true 157 | csharp_indent_braces = false 158 | csharp_indent_case_contents = true 159 | csharp_indent_case_contents_when_block = true 160 | csharp_indent_labels = one_less_than_current 161 | csharp_indent_switch_labels = true 162 | 163 | # Space preferences 164 | csharp_space_after_cast = false 165 | csharp_space_after_colon_in_inheritance_clause = true 166 | csharp_space_after_comma = true 167 | csharp_space_after_dot = false 168 | csharp_space_after_keywords_in_control_flow_statements = true 169 | csharp_space_after_semicolon_in_for_statement = true 170 | csharp_space_around_binary_operators = before_and_after 171 | csharp_space_around_declaration_statements = false 172 | csharp_space_before_colon_in_inheritance_clause = true 173 | csharp_space_before_comma = false 174 | csharp_space_before_dot = false 175 | csharp_space_before_open_square_brackets = false 176 | csharp_space_before_semicolon_in_for_statement = false 177 | csharp_space_between_empty_square_brackets = false 178 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 179 | csharp_space_between_method_call_name_and_opening_parenthesis = false 180 | csharp_space_between_method_call_parameter_list_parentheses = false 181 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 182 | csharp_space_between_method_declaration_name_and_open_parenthesis = false 183 | csharp_space_between_method_declaration_parameter_list_parentheses = false 184 | csharp_space_between_parentheses = false 185 | csharp_space_between_square_brackets = false 186 | 187 | # Wrapping preferences 188 | csharp_preserve_single_line_blocks = true 189 | csharp_preserve_single_line_statements = true 190 | 191 | #### Naming styles #### 192 | 193 | # Naming rules 194 | 195 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion 196 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface 197 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i 198 | 199 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion 200 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types 201 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case 202 | 203 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion 204 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members 205 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case 206 | 207 | # Symbol specifications 208 | 209 | dotnet_naming_symbols.interface.applicable_kinds = interface 210 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 211 | dotnet_naming_symbols.interface.required_modifiers = 212 | 213 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum 214 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 215 | dotnet_naming_symbols.types.required_modifiers = 216 | 217 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method 218 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 219 | dotnet_naming_symbols.non_field_members.required_modifiers = 220 | 221 | # Naming styles 222 | 223 | dotnet_naming_style.pascal_case.required_prefix = 224 | dotnet_naming_style.pascal_case.required_suffix = 225 | dotnet_naming_style.pascal_case.word_separator = 226 | dotnet_naming_style.pascal_case.capitalization = pascal_case 227 | 228 | dotnet_naming_style.begins_with_i.required_prefix = I 229 | dotnet_naming_style.begins_with_i.required_suffix = 230 | dotnet_naming_style.begins_with_i.word_separator = 231 | dotnet_naming_style.begins_with_i.capitalization = pascal_case 232 | -------------------------------------------------------------------------------- /src/AnalyticsConfig.cs: -------------------------------------------------------------------------------- 1 | namespace CsInlineColorViz 2 | { 3 | public static class AnalyticsConfig 4 | { 5 | public static string TelemetryConnectionString { get; set; } = ""; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/BaseOptionModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Runtime.Serialization.Formatters.Binary; 7 | using System.Threading.Tasks; 8 | using Microsoft; 9 | using Microsoft.VisualStudio.Settings; 10 | using Microsoft.VisualStudio.Shell; 11 | using Microsoft.VisualStudio.Shell.Interop; 12 | using Microsoft.VisualStudio.Shell.Settings; 13 | using Microsoft.VisualStudio.Threading; 14 | using Task = System.Threading.Tasks.Task; 15 | 16 | namespace CsInlineColorViz 17 | { 18 | /// 19 | /// A base class for specifying options. 20 | /// From https://github.com/microsoft/VSSDK-Extensibility-Samples/blob/master/Options/src/Options/BaseOptionModel.cs 21 | /// 22 | internal abstract class BaseOptionModel where T : BaseOptionModel, new() 23 | { 24 | private static readonly AsyncLazy _liveModel = new(CreateAsync, ThreadHelper.JoinableTaskFactory); 25 | private static readonly AsyncLazy _settingsManager = new(GetSettingsManagerAsync, ThreadHelper.JoinableTaskFactory); 26 | 27 | protected BaseOptionModel() 28 | { } 29 | 30 | /// 31 | /// A singleton instance of the options. MUST be called from UI thread only. 32 | /// 33 | /// 34 | /// Call instead if on a background thread or in an async context on the main thread. 35 | /// 36 | public static T Instance 37 | { 38 | get 39 | { 40 | ThreadHelper.ThrowIfNotOnUIThread(); 41 | 42 | return ThreadHelper.JoinableTaskFactory.Run(GetLiveInstanceAsync); 43 | } 44 | } 45 | 46 | /// 47 | /// Get the singleton instance of the options. Thread safe. 48 | /// 49 | public static Task GetLiveInstanceAsync() => _liveModel.GetValueAsync(); 50 | 51 | /// 52 | /// Creates a new instance of the options class and loads the values from the store. For internal use only 53 | /// 54 | /// 55 | public static async Task CreateAsync() 56 | { 57 | var instance = new T(); 58 | await instance.LoadAsync(); 59 | return instance; 60 | } 61 | 62 | /// 63 | /// The name of the options collection as stored in the registry. 64 | /// 65 | protected virtual string CollectionName { get; } = typeof(T).FullName; 66 | 67 | /// 68 | /// Hydrates the properties from the registry. 69 | /// 70 | public virtual void Load() 71 | { 72 | ThreadHelper.JoinableTaskFactory.Run(LoadAsync); 73 | } 74 | 75 | /// 76 | /// Hydrates the properties from the registry asyncronously. 77 | /// 78 | public virtual async Task LoadAsync() 79 | { 80 | ShellSettingsManager manager = await _settingsManager.GetValueAsync(); 81 | SettingsStore settingsStore = manager.GetReadOnlySettingsStore(SettingsScope.UserSettings); 82 | 83 | if (!settingsStore.CollectionExists(CollectionName)) 84 | { 85 | return; 86 | } 87 | 88 | foreach (PropertyInfo property in GetOptionProperties()) 89 | { 90 | try 91 | { 92 | string serializedProp = settingsStore.GetString(CollectionName, property.Name); 93 | object value = DeserializeValue(serializedProp, property.PropertyType); 94 | property.SetValue(this, value); 95 | } 96 | catch (Exception ex) 97 | { 98 | System.Diagnostics.Debug.Write(ex); 99 | } 100 | } 101 | } 102 | 103 | /// 104 | /// Saves the properties to the registry. 105 | /// 106 | public virtual void Save() 107 | { 108 | ThreadHelper.JoinableTaskFactory.Run(SaveAsync); 109 | } 110 | 111 | /// 112 | /// Saves the properties to the registry asyncronously. 113 | /// 114 | public virtual async Task SaveAsync() 115 | { 116 | ShellSettingsManager manager = await _settingsManager.GetValueAsync(); 117 | WritableSettingsStore settingsStore = manager.GetWritableSettingsStore(SettingsScope.UserSettings); 118 | 119 | if (!settingsStore.CollectionExists(CollectionName)) 120 | { 121 | settingsStore.CreateCollection(CollectionName); 122 | } 123 | 124 | foreach (PropertyInfo property in GetOptionProperties()) 125 | { 126 | string output = SerializeValue(property.GetValue(this)); 127 | settingsStore.SetString(CollectionName, property.Name, output); 128 | } 129 | 130 | T liveModel = await GetLiveInstanceAsync(); 131 | 132 | if (this != liveModel) 133 | { 134 | await liveModel.LoadAsync(); 135 | } 136 | } 137 | 138 | /// 139 | /// Serializes an object value to a string using the binary serializer. 140 | /// 141 | protected virtual string SerializeValue(object value) 142 | { 143 | using var stream = new MemoryStream(); 144 | var formatter = new BinaryFormatter(); 145 | formatter.Serialize(stream, value); 146 | stream.Flush(); 147 | return Convert.ToBase64String(stream.ToArray()); 148 | } 149 | 150 | /// 151 | /// Deserializes a string to an object using the binary serializer. 152 | /// 153 | protected virtual object DeserializeValue(string value, Type type) 154 | { 155 | byte[] b = Convert.FromBase64String(value); 156 | 157 | using var stream = new MemoryStream(b); 158 | var formatter = new BinaryFormatter(); 159 | return formatter.Deserialize(stream); 160 | } 161 | 162 | private static async Task GetSettingsManagerAsync() 163 | { 164 | #pragma warning disable VSTHRD010 165 | // False-positive in Threading Analyzers. Bug tracked here https://github.com/Microsoft/vs-threading/issues/230 166 | var svc = await AsyncServiceProvider.GlobalProvider.GetServiceAsync(typeof(SVsSettingsManager)) as IVsSettingsManager; 167 | #pragma warning restore VSTHRD010 168 | 169 | Assumes.Present(svc); 170 | 171 | return new ShellSettingsManager(svc); 172 | } 173 | 174 | private IEnumerable GetOptionProperties() 175 | { 176 | return GetType() 177 | .GetProperties() 178 | .Where(p => p.PropertyType.IsSerializable && p.PropertyType.IsPublic); 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/CSInlineColorViz.Tests/AllTaggersTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace CSInlineColorViz.Tests; 6 | 7 | [TestClass] 8 | public class AllTaggersTests : BaseTaggerTests 9 | { 10 | [DataTestMethod] 11 | [DynamicData(nameof(TaggerTestHelper.AllTaggerRegexs), typeof(TaggerTestHelper), DynamicDataSourceType.Method)] 12 | public void DoesNotMatch_InconsequentialText(Regex type) 13 | { 14 | base.DoesNotMatch_Text(type, "just some inconsequential text string"); 15 | } 16 | 17 | [DataTestMethod] 18 | [DynamicData(nameof(TaggerTestHelper.AllTaggerRegexs), typeof(TaggerTestHelper), DynamicDataSourceType.Method)] 19 | public void DoesNotMatch_EmptyString(Regex type) 20 | { 21 | base.DoesNotMatch_Text(type, string.Empty); 22 | } 23 | 24 | [DataTestMethod] 25 | [DynamicData(nameof(TaggerTestHelper.AllTaggerRegexs), typeof(TaggerTestHelper), DynamicDataSourceType.Method)] 26 | public void DoesNotMatch_Whitespace(Regex type) 27 | { 28 | base.DoesNotMatch_Text(type, " "); 29 | } 30 | } -------------------------------------------------------------------------------- /src/CSInlineColorViz.Tests/BaseTaggerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace CSInlineColorViz.Tests; 6 | 7 | public class BaseTaggerTests 8 | { 9 | public void DoesNotMatch_Text(Regex regex, string text) 10 | { 11 | var matches = regex.Matches(text).Cast(); 12 | 13 | Assert.AreEqual(0, matches.Count()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/CSInlineColorViz.Tests/CSInlineColorViz.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {D3A4F760-435E-4399-81C0-8BADC9C725CD} 8 | Library 9 | Properties 10 | CSInlineColorViz.Tests 11 | CSInlineColorViz.Tests 12 | v4.8.1 13 | 512 14 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 15.0 16 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 17 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 18 | False 19 | UnitTest 20 | 21 | 22 | 23 | 24 | 25 | true 26 | full 27 | false 28 | bin\Debug\ 29 | DEBUG;TRACE 30 | prompt 31 | 4 32 | 33 | 34 | pdbonly 35 | true 36 | bin\Release\ 37 | TRACE 38 | prompt 39 | 4 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 4.7.36 62 | 63 | 64 | 3.6.4 65 | 66 | 67 | 3.6.4 68 | 69 | 70 | 3.6.4 71 | runtime; build; native; contentfiles; analyzers; buildtransitive 72 | all 73 | 74 | 75 | 1.7.0 76 | runtime; build; native; contentfiles; analyzers; buildtransitive 77 | all 78 | 79 | 80 | 81 | 82 | {7e6e7299-f72b-4236-b833-b0f2beb4bffd} 83 | CsInlineColorViz 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/CSInlineColorViz.Tests/Color32TaggerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | using CsInlineColorViz; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using Microsoft.VisualStudio.Text.Tagging; 6 | 7 | namespace CSInlineColorViz.Tests; 8 | 9 | // https://docs.unity3d.com/ScriptReference/Color32-ctor.html 10 | [TestClass] 11 | public class Color32TaggerTests : BaseTaggerTests 12 | { 13 | [TestMethod] 14 | public void CanMatchSingleColor() 15 | { 16 | var sut = new Color32Tagger(new FakeTextBuffer()); 17 | 18 | Assert.IsNotNull(sut); 19 | 20 | var lineWithColor = "var testValue = new Color32(255, 0, 0, 255)"; 21 | 22 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 23 | 24 | Assert.AreEqual(1, matches.Count()); 25 | 26 | var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 27 | 28 | Assert.IsNotNull(tag); 29 | } 30 | 31 | [TestMethod] 32 | public void CanMatchSingleColor_NoSpaces() 33 | { 34 | var sut = new Color32Tagger(new FakeTextBuffer()); 35 | 36 | Assert.IsNotNull(sut); 37 | 38 | var lineWithColor = "var testValue = new Color32(255,0,0,255)"; 39 | 40 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 41 | 42 | Assert.AreEqual(1, matches.Count()); 43 | 44 | var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 45 | 46 | Assert.IsNotNull(tag); 47 | } 48 | 49 | // Not yet supported. Can be added later if people really write code like this. 50 | //[TestMethod] 51 | //public void CanMatchSingleColor_SpacesBeforeCommas() 52 | //{ 53 | // var sut = new Color32Tagger(new FakeTextBuffer()); 54 | 55 | // Assert.IsNotNull(sut); 56 | 57 | // var lineWithColor = "var testValue = new Color32(255 ,0 ,0 ,255)"; 58 | 59 | // var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 60 | 61 | // Assert.AreEqual(1, matches.Count()); 62 | 63 | // var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 64 | 65 | // Assert.IsNotNull(tag); 66 | //} 67 | } 68 | -------------------------------------------------------------------------------- /src/CSInlineColorViz.Tests/ColorCtorTaggerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | using CsInlineColorViz; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace CSInlineColorViz.Tests; 7 | 8 | // https://docs.unity3d.com/ScriptReference/Color-ctor.html 9 | [TestClass] 10 | public class ColorCtorTaggerTests : BaseTaggerTests 11 | { 12 | [TestMethod] 13 | public void CanMatchSingleColor_3Bytes() 14 | { 15 | var sut = new ColorCtorTagger(new FakeTextBuffer()); 16 | 17 | Assert.IsNotNull(sut); 18 | 19 | var lineWithColor = "var testValue = new Color(255, 0, 0);"; 20 | 21 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 22 | 23 | Assert.AreEqual(1, matches.Count()); 24 | 25 | var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 26 | 27 | Assert.IsNotNull(tag); 28 | } 29 | 30 | [TestMethod] 31 | public void CanMatchSingleColor_3Floats() 32 | { 33 | var sut = new ColorCtorTagger(new FakeTextBuffer()); 34 | 35 | Assert.IsNotNull(sut); 36 | 37 | var lineWithColor = "var testValue = new Color(1.0f, 0.0f, 0.0f)"; 38 | 39 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 40 | 41 | Assert.AreEqual(1, matches.Count()); 42 | 43 | var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 44 | 45 | Assert.IsNotNull(tag); 46 | } 47 | 48 | [TestMethod] 49 | public void CanMatchSingleColor_4Floats() 50 | { 51 | var sut = new ColorCtorTagger(new FakeTextBuffer()); 52 | 53 | Assert.IsNotNull(sut); 54 | 55 | var lineWithColor = "var testValue = new Color(0.0f, 0.0f, 0.0f, 0.5f)"; 56 | 57 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 58 | 59 | Assert.AreEqual(1, matches.Count()); 60 | 61 | var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 62 | 63 | Assert.IsNotNull(tag); 64 | } 65 | } 66 | 67 | //[TestClass] 68 | //public class FloatColor32TaggerTests : BaseTaggerTests 69 | //{ 70 | // [TestMethod] 71 | // public void CanMatchSingleColor_NoDecimals() 72 | // { 73 | // var sut = new Color32Tagger(new FakeTextBuffer()); 74 | 75 | // Assert.IsNotNull(sut); 76 | 77 | // var lineWithColor = "var testValue = new Color32(1, 0, 0, 1)"; 78 | 79 | // var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 80 | 81 | // Assert.AreEqual(1, matches.Count()); 82 | 83 | // var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 84 | 85 | // Assert.IsNotNull(tag); 86 | // } 87 | 88 | // [TestMethod] 89 | // public void CanMatchSingleColor_WithDecimals() 90 | // { 91 | // var sut = new Color32Tagger(new FakeTextBuffer()); 92 | 93 | // Assert.IsNotNull(sut); 94 | 95 | // var lineWithColor = "var testValue = new Color32(1.0, 1.0, 0.0, 1.0)"; 96 | 97 | // var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 98 | 99 | // Assert.AreEqual(1, matches.Count()); 100 | 101 | // var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 102 | 103 | // Assert.IsNotNull(tag); 104 | // } 105 | 106 | // [TestMethod] 107 | // public void CanMatchSingleColor_WithDecimalsAndF() 108 | // { 109 | // var sut = new Color32Tagger(new FakeTextBuffer()); 110 | 111 | // Assert.IsNotNull(sut); 112 | 113 | // var lineWithColor = "var testValue = new Color32(0.0f, 1.0f, 1.0f, 1.0f)"; 114 | 115 | // var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 116 | 117 | // Assert.AreEqual(1, matches.Count()); 118 | 119 | // var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 120 | 121 | // Assert.IsNotNull(tag); 122 | // } 123 | 124 | // [TestMethod] 125 | // public void CanMatchSingleColor_WithDecimalsAndFButNoSpaces() 126 | // { 127 | // var sut = new Color32Tagger(new FakeTextBuffer()); 128 | 129 | // Assert.IsNotNull(sut); 130 | 131 | // var lineWithColor = "var testValue = new Color32(0.0f,1.0f,1.0f,1.0f)"; 132 | 133 | // var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 134 | 135 | // Assert.AreEqual(1, matches.Count()); 136 | 137 | // var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 138 | 139 | // Assert.IsNotNull(tag); 140 | // } 141 | 142 | // [TestMethod] 143 | // public void DoesNotMatch_InconsequentialText() 144 | // { 145 | // base.DoesNotMatch_InconsequentialText(); 146 | // } 147 | //} 148 | 149 | -------------------------------------------------------------------------------- /src/CSInlineColorViz.Tests/ColorFromArgbTaggerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | using CsInlineColorViz; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace CSInlineColorViz.Tests; 7 | 8 | [TestClass] 9 | public class ColorFromArgbTaggerTests : BaseTaggerTests 10 | { 11 | [TestMethod] 12 | public void NoNameSpace() 13 | { 14 | var testLine = "var color = Color.FromArgb(255, 125, 125, 125); "; 15 | 16 | CreatesOneMatchAndTag(testLine); 17 | } 18 | 19 | [TestMethod] 20 | public void WithNameSpace_System_Drawing() 21 | { 22 | var testLine = "var color = System.Drawing.Color.FromArgb(255, 255, 125, 0); "; 23 | 24 | CreatesOneMatchAndTag(testLine); 25 | } 26 | 27 | [TestMethod] 28 | public void WithNameSpace_Windows_UI() 29 | { 30 | var testLine = "var color = Windows.UI.Color.FromArgb(255, 125, 125, 125); "; 31 | 32 | CreatesOneMatchAndTag(testLine); 33 | } 34 | 35 | private void CreatesOneMatchAndTag(string lineOfCode) 36 | { 37 | var sut = new ColorArgbTagger(new FakeTextBuffer()); 38 | 39 | Assert.IsNotNull(sut, "Failed to create tagger"); 40 | 41 | var matches = sut.ColorExpression.Matches(lineOfCode).Cast(); 42 | 43 | Assert.AreEqual(1, matches.Count()); 44 | 45 | var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineOfCode); 46 | 47 | Assert.IsNotNull(tag, "Failed to create a tag from the match"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/CSInlineColorViz.Tests/ColorListHelperTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using CsInlineColorViz; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace CSInlineColorViz.Tests; 7 | 8 | [TestClass] 9 | public class ColorListHelperTests 10 | { 11 | [TestMethod] 12 | public void Ensure_ConsoleColor_AlphabeticalList_AsEnum() 13 | { 14 | var enumValues = Enum.GetValues(typeof(System.ConsoleColor)).Cast().ToList().OrderBy(o => o.ToString()).Select(o => o.ToString()).ToList(); 15 | 16 | var helperValues = ColorListHelper.ConsoleColorsNamesAlphabetical().ToList(); 17 | 18 | Assert.AreEqual(enumValues.Count(), helperValues.Count); 19 | 20 | for (int i = 0; i < enumValues.Count(); i++) 21 | { 22 | Assert.AreEqual(enumValues[i], helperValues[i]); 23 | } 24 | } 25 | 26 | [TestMethod] 27 | public void SystemDrawingColors_AlphabeticalAndSpectrumLists_ContainSame() 28 | { 29 | var alphaValues = ColorListHelper.SystemDrawingColorsAlphabetical().ToList(); 30 | 31 | var spectrumValues = ColorListHelper.SystemDrawingColorsSpectrum().ToList(); 32 | 33 | Assert.AreEqual(alphaValues.Count(), spectrumValues.Count()); 34 | } 35 | 36 | [TestMethod] 37 | public void SystemDrawingColors_Alphabetical_NoDuplicates() 38 | { 39 | var alphaValues = ColorListHelper.SystemDrawingColorsAlphabetical().ToList(); 40 | var alphaValuesDistinct = ColorListHelper.SystemDrawingColorsAlphabetical().ToList().Distinct(); 41 | 42 | Assert.AreEqual(alphaValues.Count(), alphaValuesDistinct.Count()); 43 | } 44 | 45 | [TestMethod] 46 | public void SystemDrawingColors_SpectrumLists_NoDuplicates() 47 | { 48 | var spectrumValues = ColorListHelper.SystemDrawingColorsSpectrum().ToList(); 49 | var spectrumValuesDistinct = ColorListHelper.SystemDrawingColorsSpectrum().ToList().Distinct(); 50 | 51 | Assert.AreEqual(spectrumValues.Count(), spectrumValuesDistinct.Count()); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/CSInlineColorViz.Tests/FakeTextBuffer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.Text; 3 | using Microsoft.VisualStudio.Utilities; 4 | 5 | namespace CSInlineColorViz.Tests; 6 | 7 | public class FakeTextBuffer : ITextBuffer 8 | { 9 | public FakeTextBuffer() 10 | { 11 | } 12 | 13 | event EventHandler ITextBuffer.ReadOnlyRegionsChanged 14 | { 15 | add { } 16 | 17 | remove 18 | { 19 | } 20 | } 21 | 22 | event EventHandler ITextBuffer.Changed 23 | { 24 | add 25 | { 26 | } 27 | 28 | remove 29 | { 30 | } 31 | } 32 | 33 | event EventHandler ITextBuffer.Changing 34 | { 35 | add 36 | { 37 | } 38 | 39 | remove 40 | { 41 | } 42 | } 43 | 44 | event EventHandler ITextBuffer.PostChanged 45 | { 46 | add 47 | { 48 | } 49 | 50 | remove 51 | { 52 | } 53 | } 54 | 55 | event EventHandler ITextBuffer.ContentTypeChanged 56 | { 57 | add 58 | { 59 | } 60 | 61 | remove 62 | { 63 | } 64 | } 65 | 66 | #pragma warning disable CS0067 // events never used 67 | public event EventHandler ContentTypeChanged; 68 | public event EventHandler PostChanged; 69 | public event EventHandler ReadOnlyRegionsChanged; 70 | public event EventHandler Changed; 71 | public event EventHandler Changing; 72 | public event EventHandler ChangedLowPriority; 73 | public event EventHandler ChangedHighPriority; 74 | #pragma warning restore CS0067 75 | 76 | public IContentType ContentType { get; } 77 | public ITextSnapshot CurrentSnapshot { get; } 78 | IContentType ITextBuffer.ContentType { get; } 79 | public bool EditInProgress { get; } 80 | public PropertyCollection Properties { get; } 81 | 82 | public ITextEdit CreateEdit() => throw new NotImplementedException(); 83 | public ITextEdit CreateEdit(EditOptions options, int? reiteratedVersionNumber, object editTag) => throw new NotImplementedException(); 84 | public IReadOnlyRegionEdit CreateReadOnlyRegionEdit() => throw new NotImplementedException(); 85 | public void TakeThreadOwnership() => throw new NotImplementedException(); 86 | 87 | public bool CheckEditAccess() 88 | { 89 | throw new NotImplementedException(); 90 | } 91 | 92 | public void ChangeContentType(IContentType newContentType, object editTag) 93 | { 94 | throw new NotImplementedException(); 95 | } 96 | 97 | public ITextSnapshot Insert(int position, string text) 98 | { 99 | throw new NotImplementedException(); 100 | } 101 | 102 | public ITextSnapshot Delete(Span deleteSpan) 103 | { 104 | throw new NotImplementedException(); 105 | } 106 | 107 | public ITextSnapshot Replace(Span replaceSpan, string replaceWith) 108 | { 109 | throw new NotImplementedException(); 110 | } 111 | 112 | public bool IsReadOnly(int position) 113 | { 114 | throw new NotImplementedException(); 115 | } 116 | 117 | public bool IsReadOnly(int position, bool isEdit) 118 | { 119 | throw new NotImplementedException(); 120 | } 121 | 122 | public bool IsReadOnly(Span span) 123 | { 124 | throw new NotImplementedException(); 125 | } 126 | 127 | public bool IsReadOnly(Span span, bool isEdit) 128 | { 129 | throw new NotImplementedException(); 130 | } 131 | 132 | public NormalizedSpanCollection GetReadOnlyExtents(Span span) 133 | { 134 | throw new NotImplementedException(); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/CSInlineColorViz.Tests/FunColorTaggerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | using CsInlineColorViz; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace CSInlineColorViz.Tests; 7 | 8 | // https://github.com/maddymontaquila/funcolors/blob/main/FunColor.cs 9 | [TestClass] 10 | public class FunColorTaggerTests : BaseTaggerTests 11 | { 12 | [TestMethod] 13 | public void CanMatchKnownColor_Alphabetical() 14 | { 15 | var testLine = "var color = FunColor.BarbiePink; "; 16 | 17 | CreatesOneMatchAndTag(testLine); 18 | } 19 | 20 | [TestMethod] 21 | public void CanMatchKnownColor_AlphaNumeric() 22 | { 23 | var testLine = "var color = FunColor.DotNetPurple2024; "; 24 | 25 | CreatesOneMatchAndTag(testLine); 26 | } 27 | 28 | [TestMethod] 29 | public void CanMatchKnownColor_FullyQualified() 30 | { 31 | var testLine = "var color = FunColors.FunColor.ShrekGreen; "; 32 | 33 | CreatesOneMatchAndTag(testLine); 34 | } 35 | 36 | private void CreatesOneMatchAndTag(string lineOfCode) 37 | { 38 | var sut = new FunColorTagger(new FakeTextBuffer()); 39 | 40 | Assert.IsNotNull(sut, "Failed to create tagger"); 41 | 42 | var matches = sut.ColorExpression.Matches(lineOfCode).Cast(); 43 | 44 | Assert.AreEqual(1, matches.Count()); 45 | 46 | var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineOfCode); 47 | 48 | Assert.IsNotNull(tag, "Failed to create a tag from the match"); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/CSInlineColorViz.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("CSInlineColorViz.Tests")] 6 | [assembly: AssemblyDescription("")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("")] 9 | [assembly: AssemblyProduct("CSInlineColorViz.Tests")] 10 | [assembly: AssemblyCopyright("Copyright © 2024")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | 14 | [assembly: ComVisible(false)] 15 | 16 | [assembly: Guid("d3a4f760-435e-4399-81c0-8badc9c725cd")] 17 | 18 | // [assembly: AssemblyVersion("1.0.*")] 19 | [assembly: AssemblyVersion("1.0.0.0")] 20 | [assembly: AssemblyFileVersion("1.0.0.0")] 21 | -------------------------------------------------------------------------------- /src/CSInlineColorViz.Tests/SvgTaggerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | using CsInlineColorViz; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace CSInlineColorViz.Tests; 7 | 8 | [TestClass] 9 | public class SvgTaggerTests : BaseTaggerTests 10 | { 11 | [TestMethod] 12 | public void CanMatch_Attribute_SixChar() 13 | { 14 | var testLine = ""; 15 | 16 | CreatesOneMatchAndTag(testLine); 17 | } 18 | 19 | [TestMethod] 20 | public void CanMatch_Attribute_SixChar_LowerCaseAlpha() 21 | { 22 | var testLine = ""; 23 | 24 | CreatesOneMatchAndTag(testLine); 25 | } 26 | 27 | [TestMethod] 28 | public void CanMatch_Attribute_EightChar() 29 | { 30 | var testLine = ""; 31 | 32 | CreatesOneMatchAndTag(testLine); 33 | } 34 | 35 | [TestMethod] 36 | public void CanMatch_Style_SixChar() 37 | { 38 | var testLine = ""; 39 | 40 | CreatesOneMatchAndTag(testLine); 41 | } 42 | 43 | [TestMethod] 44 | public void CanMatch_Style_Multiples() 45 | { 46 | var testLine = ""; 47 | 48 | CreatesExpectedMatchAndTag(testLine, 2); 49 | } 50 | 51 | private void CreatesOneMatchAndTag(string lineOfCode) 52 | { 53 | CreatesExpectedMatchAndTag(lineOfCode, numberExpected:1); 54 | } 55 | 56 | private void CreatesExpectedMatchAndTag(string lineOfCode, int numberExpected) 57 | { 58 | var sut = new SvgTagger(new FakeTextBuffer()); 59 | 60 | Assert.IsNotNull(sut, "Failed to create tagger"); 61 | 62 | var matches = sut.ColorExpression.Matches(lineOfCode).Cast(); 63 | 64 | Assert.AreEqual(numberExpected, matches.Count()); 65 | 66 | var matchList = matches.ToList(); 67 | 68 | for (int i = 0; i < numberExpected; i++) 69 | { 70 | var tag = sut.TryCreateTagForMatch(matchList[i], 0, 0, 0, lineOfCode); 71 | 72 | Assert.IsNotNull(tag, $"Failed to create a tag from the match: {matchList[i]}"); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/CSInlineColorViz.Tests/TaggerTestHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using CsInlineColorViz; 3 | 4 | namespace CSInlineColorViz.Tests; 5 | 6 | public class TaggerTestHelper 7 | { 8 | public static IEnumerable AllTaggerRegexs() 9 | { 10 | yield return [ColorArgbTagger.regularExpression]; 11 | yield return [ColorCtorTagger.regularExpression]; 12 | yield return [ColorFromIntTagger.regularExpression]; 13 | yield return [ColorFromNameTagger.regularExpression]; 14 | yield return [ColorHslaTagger.regularExpression]; 15 | yield return [ColorHsvaTagger.regularExpression]; 16 | yield return [ColorRgbaTagger.regularExpression]; 17 | yield return [ColorRgbTagger.regularExpression]; 18 | yield return [ColorTagger.regularExpression]; 19 | yield return [FunColorTagger.regularExpression]; 20 | yield return [HexIntTagger.regularExpression]; 21 | yield return [HexStringTagger.regularExpression]; 22 | yield return [MauiProjTagger.regularExpression]; 23 | yield return [UnityTagger.regularExpression]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/CSInlineColorViz.Tests/UnityTaggerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | using CsInlineColorViz; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using Microsoft.VisualStudio.Text.Tagging; 6 | 7 | namespace CSInlineColorViz.Tests; 8 | 9 | [TestClass] 10 | public class UnityTaggerTests : BaseTaggerTests 11 | { 12 | [TestMethod] 13 | public void CanMatchSingleColor_Name() 14 | { 15 | var sut = new UnityTextTagger(new FakeTextBuffer()); 16 | 17 | Assert.IsNotNull(sut); 18 | 19 | var lineWithColor = ""; 20 | 21 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 22 | 23 | Assert.AreEqual(1, matches.Count()); 24 | 25 | var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 26 | 27 | Assert.IsNotNull(tag); 28 | } 29 | 30 | [TestMethod] 31 | public void CanMatchSingleColor_Name_TagLowercase() 32 | { 33 | var sut = new UnityTextTagger(new FakeTextBuffer()); 34 | 35 | Assert.IsNotNull(sut); 36 | 37 | var lineWithColor = ""; 38 | 39 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 40 | 41 | Assert.AreEqual(1, matches.Count()); 42 | 43 | var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 44 | 45 | Assert.IsNotNull(tag); 46 | } 47 | 48 | [TestMethod] 49 | public void CanMatchSingleColor_Name_InQuotes() 50 | { 51 | var sut = new UnityTextTagger(new FakeTextBuffer()); 52 | 53 | Assert.IsNotNull(sut); 54 | 55 | var lineWithColor = ""; 56 | 57 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 58 | 59 | Assert.AreEqual(1, matches.Count()); 60 | 61 | var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 62 | 63 | Assert.IsNotNull(tag); 64 | } 65 | 66 | [TestMethod] 67 | public void CanMatchSingleColor_Name_InEscapedQuotes() 68 | { 69 | var sut = new UnityTextTagger(new FakeTextBuffer()); 70 | 71 | Assert.IsNotNull(sut); 72 | 73 | var lineWithColor = ""; 74 | 75 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 76 | 77 | Assert.AreEqual(1, matches.Count()); 78 | 79 | var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 80 | 81 | Assert.IsNotNull(tag); 82 | } 83 | 84 | [TestMethod] 85 | public void CanMatchSingleColor_Name_InDoubleQuotes() 86 | { 87 | var sut = new UnityTextTagger(new FakeTextBuffer()); 88 | 89 | Assert.IsNotNull(sut); 90 | 91 | var lineWithColor = ""; 92 | 93 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 94 | 95 | Assert.AreEqual(1, matches.Count()); 96 | 97 | var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 98 | 99 | Assert.IsNotNull(tag); 100 | } 101 | 102 | [TestMethod] 103 | public void CanMatchSingleColor_Name_CheckGeneratedTagValues() 104 | { 105 | var sut = new UnityTextTagger(new FakeTextBuffer()); 106 | 107 | Assert.IsNotNull(sut); 108 | 109 | var lineWithColor = ""; 110 | 111 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 112 | 113 | Assert.AreEqual(1, matches.Count()); 114 | 115 | var tag = sut.TryCreateTagForMatch(matches.First(), 1, 2, 3, lineWithColor); 116 | 117 | Assert.IsNotNull(tag); 118 | Assert.AreEqual(1, tag.LineNumber); 119 | Assert.AreEqual(2, tag.LineCharOffset); 120 | } 121 | 122 | [TestMethod] 123 | public void CanMatchSingleColor_3HexChars() 124 | { 125 | var sut = new UnityTagger(new FakeTextBuffer()); 126 | 127 | Assert.IsNotNull(sut); 128 | 129 | var lineWithColor = ""; 130 | 131 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 132 | 133 | Assert.AreEqual(1, matches.Count()); 134 | 135 | var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 136 | 137 | Assert.IsNotNull(tag); 138 | } 139 | 140 | [TestMethod] 141 | public void CanMatchSingleColor_3HexChars_CheckGeneratedTagValues() 142 | { 143 | var sut = new UnityTagger(new FakeTextBuffer()); 144 | 145 | Assert.IsNotNull(sut); 146 | 147 | var lineWithColor = ""; 148 | 149 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 150 | 151 | Assert.AreEqual(1, matches.Count()); 152 | 153 | var tag = sut.TryCreateTagForMatch(matches.First(), 1, 2, 3, lineWithColor); 154 | 155 | Assert.IsNotNull(tag); 156 | Assert.AreEqual(1, tag.LineNumber); 157 | Assert.AreEqual(2, tag.LineCharOffset); 158 | } 159 | 160 | [TestMethod] 161 | public void CanMatchSingleColor_4HexChars() 162 | { 163 | var sut = new UnityTagger(new FakeTextBuffer()); 164 | 165 | Assert.IsNotNull(sut); 166 | 167 | var lineWithColor = ""; 168 | 169 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 170 | 171 | Assert.AreEqual(1, matches.Count()); 172 | 173 | var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 174 | 175 | Assert.IsNotNull(tag); 176 | } 177 | 178 | [TestMethod] 179 | public void CanMatchSingleColor_6HexChars() 180 | { 181 | var sut = new UnityTagger(new FakeTextBuffer()); 182 | 183 | Assert.IsNotNull(sut); 184 | 185 | var lineWithColor = ""; 186 | 187 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 188 | 189 | Assert.AreEqual(1, matches.Count()); 190 | 191 | var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 192 | 193 | Assert.IsNotNull(tag); 194 | } 195 | 196 | [TestMethod] 197 | public void CanMatchSingleColor_8HexChars() 198 | { 199 | var sut = new UnityTagger(new FakeTextBuffer()); 200 | 201 | Assert.IsNotNull(sut); 202 | 203 | var lineWithColor = ""; 204 | 205 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 206 | 207 | Assert.AreEqual(1, matches.Count()); 208 | 209 | var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 210 | 211 | Assert.IsNotNull(tag); 212 | } 213 | 214 | [TestMethod] 215 | public void DoesNotMatch_2HexChars() 216 | { 217 | var sut = new UnityTagger(new FakeTextBuffer()); 218 | 219 | Assert.IsNotNull(sut); 220 | 221 | var lineWithColor = ""; 222 | 223 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 224 | 225 | Assert.AreEqual(0, matches.Count()); 226 | } 227 | 228 | [TestMethod] 229 | public void DoesNotMatch_9HexChars() 230 | { 231 | var sut = new UnityTagger(new FakeTextBuffer()); 232 | 233 | Assert.IsNotNull(sut); 234 | 235 | var lineWithColor = ""; 236 | 237 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 238 | 239 | Assert.AreEqual(0, matches.Count()); 240 | } 241 | 242 | [TestMethod] 243 | public void MatchesButDoesNotProduceTag_5HexChars() 244 | { 245 | var sut = new UnityTagger(new FakeTextBuffer()); 246 | 247 | Assert.IsNotNull(sut); 248 | 249 | var lineWithColor = ""; 250 | 251 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 252 | 253 | Assert.AreEqual(1, matches.Count()); 254 | 255 | var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 256 | 257 | Assert.IsNull(tag); 258 | } 259 | 260 | [TestMethod] 261 | public void MatchesButDoesNotProduceTag_7HexChars() 262 | { 263 | var sut = new UnityTagger(new FakeTextBuffer()); 264 | 265 | Assert.IsNotNull(sut); 266 | 267 | var lineWithColor = ""; 268 | 269 | var matches = sut.ColorExpression.Matches(lineWithColor).Cast(); 270 | 271 | Assert.AreEqual(1, matches.Count()); 272 | 273 | var tag = sut.TryCreateTagForMatch(matches.First(), 0, 0, 0, lineWithColor); 274 | 275 | Assert.IsNull(tag); 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /src/Color32Tagger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text.RegularExpressions; 4 | using System.Windows.Media; 5 | using Microsoft.VisualStudio.Text; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | internal sealed class Color32Tagger : RegexTagger, ITestableRegexColorTagger 10 | { 11 | internal static Regex regularExpression = new(@"(new Color32\(|new UnityEngine.Color32\()([0-9]{1,3})(,{1} ?)([0-9]{1,3})(,{1} ?)([0-9]{1,3})(,{1} ?)([0-9]{1,3})(\))", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 12 | 13 | public Regex ColorExpression => regularExpression; 14 | 15 | public Color32Tagger(ITextBuffer buffer) 16 | : base(buffer, [regularExpression]) 17 | { 18 | } 19 | 20 | internal override ColorTag TryCreateTagForMatch(Match match, int lineNumber, int lineStart, int spanStart, string lineText) 21 | { 22 | if (lineText.Contains(match.Value) && match.Groups.Count == 10) 23 | { 24 | try 25 | { 26 | var r = byte.Parse(match.Groups[2].Value); 27 | var g = byte.Parse(match.Groups[4].Value); 28 | var b = byte.Parse(match.Groups[6].Value); 29 | var a = byte.Parse(match.Groups[8].Value); 30 | 31 | try 32 | { 33 | return new ColorTag(Color.FromArgb(a, r, g, b), match, lineNumber, lineStart, PopupType.None); 34 | } 35 | catch (Exception) 36 | { 37 | System.Diagnostics.Debug.WriteLine($"Failed to create color from r='{r}' g='{g}' b='{b}' a='{a}'"); 38 | } 39 | } 40 | catch (Exception exc) 41 | { 42 | System.Diagnostics.Debug.WriteLine($"Failed to create color from '{match.Value}' Exception:'{exc.Message}'"); 43 | } 44 | } 45 | 46 | return null; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Color32TaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | [Export(typeof(ITaggerProvider))] 10 | [ContentType("CSharp")] 11 | [ContentType("Razor")] 12 | [ContentType("LegacyRazorCSharp")] 13 | [TagType(typeof(ColorTag))] 14 | internal sealed class Color32TaggerProvider : ITaggerProvider 15 | { 16 | public ITagger CreateTagger(ITextBuffer buffer) 17 | where T : ITag 18 | { 19 | if (buffer == null) 20 | { 21 | throw new ArgumentNullException(nameof(buffer)); 22 | } 23 | 24 | return buffer.Properties.GetOrCreateSingletonProperty(() => new Color32Tagger(buffer)) as ITagger; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ColorAdornment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using System.Windows.Input; 5 | using System.Windows.Media; 6 | using EnvDTE; 7 | using Microsoft.VisualStudio; 8 | using Microsoft.VisualStudio.Shell; 9 | using Microsoft.VisualStudio.Shell.Interop; 10 | 11 | namespace CsInlineColorViz; 12 | 13 | internal sealed class ColorAdornment : Border 14 | { 15 | private static readonly SolidColorBrush _borderColor = (SolidColorBrush)Application.Current.Resources[VsBrushes.CaptionTextKey]; 16 | 17 | public ColorAdornment(ColorTag tag) 18 | { 19 | ThreadHelper.ThrowIfNotOnUIThread(); 20 | 21 | ClrTag = tag; 22 | 23 | Padding = new Thickness(0); 24 | BorderThickness = new Thickness(1); 25 | BorderBrush = _borderColor; 26 | Height = GetFontSize() + 2; ; 27 | Width = Height; 28 | VerticalAlignment = System.Windows.VerticalAlignment.Center; 29 | Margin = new System.Windows.Thickness(0, 0, 2, 3); 30 | SetBackground(); 31 | 32 | this.MouseLeftButtonDown += OnMouseLeftButtonDown; 33 | } 34 | 35 | private ColorSelectionDialog CreateDialogForPopupType(PopupType popupType) 36 | => popupType switch 37 | { 38 | PopupType.NamedColors => new NamedColorDialog(), 39 | PopupType.ConsoleColors => new ConsoleColorDialog(), 40 | PopupType.KnownColors => new KnownColorDialog(), 41 | PopupType.SystemColors => new SystemColorsDialog(), 42 | PopupType.FunColors => new FunColorsDialog(), 43 | PopupType.UnityColors => new UnityColorsDialog(), 44 | _ => throw new NotImplementedException(), 45 | }; 46 | 47 | #pragma warning disable VSTHRD100 // Avoid async void methods - It's from a button click! 48 | private async void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e) 49 | #pragma warning restore VSTHRD100 // Avoid async void methods 50 | { 51 | if (e.ClickCount == 2 && ClrTag.PopupType != PopupType.None) 52 | { 53 | var dlg = CreateDialogForPopupType(ClrTag.PopupType); 54 | var dlgResult = dlg.ShowModal(); 55 | 56 | if (dlgResult == true) 57 | { 58 | // We always should be on the UI thread here as the user just clicked on the dialog. 59 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 60 | 61 | await CsInlineColorVizPackage.EnsureInstanceLoadedAsync(); 62 | 63 | var dte = (DTE)Package.GetGlobalService(typeof(DTE)); 64 | 65 | if (dte.ActiveDocument.Object("TextDocument") is EnvDTE.TextDocument txtDoc) 66 | { 67 | var find = ClrTag.Match.Groups[0].Value; 68 | var replace = $"{ClrTag.Match.Groups[1].Value}{ClrTag.Match.Groups[2].Value}{dlg.SelectedName}"; 69 | 70 | // System.Windows.Colors end with the suffix, but System.Drawing.Colors do not 71 | // The dialog doesn't include the suffix so add it if needed 72 | if (find.EndsWith("Color") && !replace.EndsWith("Color")) 73 | { 74 | replace += "Color"; 75 | } 76 | 77 | var matches = await TextDocumentHelper.FindMatches(txtDoc, find); 78 | 79 | if (!dte.UndoContext.IsOpen) 80 | { 81 | dte.UndoContext.Open($"Changing color to: {replace}"); 82 | } 83 | 84 | foreach (var matchPoint in matches) 85 | { 86 | // TODO: Need to account for the line number in the tag not having been updated even if the line has changed 87 | // Add one to the line number as the EditPoint is 1-based 88 | if (matchPoint.Line == ClrTag.LineNumber + 1) 89 | { 90 | var replacementMade = await TextDocumentHelper.MakeReplacements(txtDoc, matchPoint, find, replace); 91 | 92 | if (!replacementMade) 93 | { 94 | System.Diagnostics.Debug.WriteLine($"Failed to find '{find}' on line {ClrTag.LineNumber}."); 95 | await OutputPane.Instance.WriteAsync($"Failed to find '{find}' on line {ClrTag.LineNumber}."); 96 | } 97 | } 98 | } 99 | 100 | dte.UndoContext.Close(); 101 | } 102 | } 103 | } 104 | } 105 | 106 | public ColorTag ClrTag { get; private set; } 107 | 108 | internal void Update(ColorTag dataTag) 109 | { 110 | ClrTag = dataTag; 111 | SetBackground(); 112 | } 113 | 114 | private void SetBackground() 115 | { 116 | this.Background = new System.Windows.Media.SolidColorBrush(ClrTag.Clr); 117 | } 118 | 119 | private static int GetFontSize() 120 | { 121 | ThreadHelper.ThrowIfNotOnUIThread(); 122 | 123 | try 124 | { 125 | IVsFontAndColorStorage storage = (IVsFontAndColorStorage)Package.GetGlobalService(typeof(IVsFontAndColorStorage)); 126 | Guid guid = new("A27B4E24-A735-4d1d-B8E7-9716E1E3D8E0"); 127 | if (storage != null && storage.OpenCategory(ref guid, (uint)(__FCSTORAGEFLAGS.FCSF_READONLY | __FCSTORAGEFLAGS.FCSF_LOADDEFAULTS)) == VSConstants.S_OK) 128 | { 129 | LOGFONTW[] Fnt = new LOGFONTW[] { new() }; 130 | FontInfo[] Info = new FontInfo[] { new() }; 131 | storage.GetFont(Fnt, Info); 132 | return Info[0].wPointSize; 133 | } 134 | 135 | } 136 | catch { } 137 | 138 | return 12; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/ColorAdornmentTagger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Editor; 5 | using Microsoft.VisualStudio.Text.Tagging; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | internal sealed class ColorAdornmentTagger 10 | : IntraTextAdornmentTagger 11 | { 12 | private readonly ITagAggregator tagger; 13 | 14 | private ColorAdornmentTagger(IWpfTextView view, ITagAggregator tagger) 15 | : base(view) 16 | { 17 | this.tagger = tagger; 18 | } 19 | 20 | public void Dispose() 21 | { 22 | this.tagger.Dispose(); 23 | 24 | this.view.Properties.RemoveProperty(typeof(ColorTagger)); 25 | } 26 | 27 | internal static ITagger GetTagger(IWpfTextView view, Lazy> tagger) 28 | { 29 | return view.Properties.GetOrCreateSingletonProperty( 30 | () => new ColorAdornmentTagger(view, tagger.Value)); 31 | } 32 | 33 | protected override IEnumerable> GetAdornmentData(NormalizedSnapshotSpanCollection spans) 34 | { 35 | if (spans.Count == 0) 36 | { 37 | yield break; 38 | } 39 | 40 | ITextSnapshot snapshot = spans[0].Snapshot; 41 | 42 | var clTags = this.tagger.GetTags(spans); 43 | 44 | foreach (IMappingTagSpan dataTagSpan in clTags) 45 | { 46 | NormalizedSnapshotSpanCollection linkTagSpans = dataTagSpan.Span.GetSpans(snapshot); 47 | 48 | // Ignore data tags that are split by projection. 49 | // This is theoretically possible but unlikely in current scenarios. 50 | if (linkTagSpans.Count != 1) 51 | { 52 | continue; 53 | } 54 | 55 | var adornmentSpan = new SnapshotSpan(linkTagSpans[0].Start, 0); 56 | 57 | yield return Tuple.Create(adornmentSpan, (PositionAffinity?)PositionAffinity.Successor, dataTagSpan.Tag); 58 | } 59 | 60 | yield break; 61 | } 62 | 63 | protected override ColorAdornment CreateAdornment(ColorTag dataTag, SnapshotSpan span) 64 | { 65 | return new ColorAdornment(dataTag); 66 | } 67 | 68 | protected override bool UpdateAdornment(ColorAdornment adornment, ColorTag dataTag) 69 | { 70 | adornment.Update(dataTag); 71 | return true; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/ColorAdornmentTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 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 CsInlineColorViz; 9 | 10 | [Export(typeof(IViewTaggerProvider))] 11 | [ContentType("text")] 12 | [ContentType("projection")] 13 | [TagType(typeof(IntraTextAdornmentTag))] 14 | internal sealed class ColorAdornmentTaggerProvider : IViewTaggerProvider 15 | { 16 | #pragma warning disable 649 // "field never assigned to" -- field is set by MEF. 17 | [Import] 18 | #pragma warning disable SA1401 // Fields should be private 19 | internal IBufferTagAggregatorFactoryService BufferTagAggregatorFactoryService; 20 | #pragma warning restore SA1401 // Fields should be private 21 | #pragma warning restore 649 22 | 23 | public ITagger CreateTagger(ITextView textView, ITextBuffer buffer) 24 | where T : ITag 25 | { 26 | if (textView == null) 27 | { 28 | throw new ArgumentNullException("textView"); 29 | } 30 | 31 | if (buffer == null) 32 | { 33 | throw new ArgumentNullException("buffer"); 34 | } 35 | 36 | if (buffer != textView.TextBuffer) 37 | { 38 | return null; 39 | } 40 | 41 | return ColorAdornmentTagger.GetTagger( 42 | (IWpfTextView)textView, 43 | new Lazy>( 44 | () => this.BufferTagAggregatorFactoryService.CreateTagAggregator(textView.TextBuffer))) 45 | as ITagger; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/ColorArgbTagger.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | using System.Windows.Media; 4 | using Microsoft.VisualStudio.Text; 5 | using WpfColorHelper; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | internal sealed class ColorArgbTagger : RegexTagger, ITestableRegexColorTagger 10 | { 11 | internal static Regex regularExpression = new(@"(System.Drawing.Color.FromArgb\(|Windows.UI.Color.FromArgb\(|Color.FromArgb\()([0-9, ]{1,}|[0-9, ]{2,}Color.[a-zA-Z]{3,})(\))", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 12 | 13 | public Regex ColorExpression => regularExpression; 14 | 15 | internal ColorArgbTagger(ITextBuffer buffer) 16 | : base(buffer, [regularExpression]) 17 | { 18 | } 19 | 20 | internal override ColorTag TryCreateTagForMatch(Match match, int lineNumber, int lineStart, int spanStart, string lineText) 21 | { 22 | if (lineText.Contains(match.Value) && match.Groups.Count == 4) 23 | { 24 | var value = match.Groups[2].Value; 25 | var precedingChar = match.Index > 0 ? lineText[match.Index - 1] : ' '; 26 | 27 | // Do this check here rather than as part of the RegEx so don't have to adjust the insertion point for the adornment 28 | if (new[] { ' ', ',', '(' }.Contains(precedingChar)) 29 | { 30 | if (ColorHelper.TryGetArgbColor(value, out Color clr)) 31 | { 32 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.None); 33 | } 34 | else 35 | { 36 | System.Diagnostics.Debug.WriteLine($"Failed to understand '{value}' as a valid color."); 37 | } 38 | } 39 | } 40 | 41 | return null; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ColorArgbTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | [Export(typeof(ITaggerProvider))] 10 | [ContentType("CSharp")] 11 | [ContentType("Razor")] 12 | [ContentType("LegacyRazorCSharp")] 13 | [TagType(typeof(ColorTag))] 14 | internal sealed class ColorArgbTaggerProvider : ITaggerProvider 15 | { 16 | public ITagger CreateTagger(ITextBuffer buffer) 17 | where T : ITag 18 | { 19 | if (buffer == null) 20 | { 21 | throw new ArgumentNullException(nameof(buffer)); 22 | } 23 | 24 | return buffer.Properties.GetOrCreateSingletonProperty(() => new ColorArgbTagger(buffer)) as ITagger; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ColorCtorTagger.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using System.Windows.Media; 3 | using Microsoft.VisualStudio.Text; 4 | using WpfColorHelper; 5 | 6 | namespace CsInlineColorViz; 7 | 8 | internal sealed class ColorCtorTagger : RegexTagger, ITestableRegexColorTagger 9 | { 10 | internal static Regex regularExpression = new(@"(new Color\(|new UnityEngine.Color\(|new Microsoft.Maui.Graphics.Color\()([0-9.f]{1,6})(,{1} ?)([0-9.f]{1,6})(,{1} ?)([0-9.f]{1,6})([, ]*)([0-9.f]*)(\))", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 11 | 12 | public Regex ColorExpression => regularExpression; 13 | 14 | public ColorCtorTagger(ITextBuffer buffer) 15 | : base(buffer, [regularExpression]) 16 | { 17 | } 18 | 19 | internal override ColorTag TryCreateTagForMatch(Match match, int lineNumber, int lineStart, int spanStart, string lineText) 20 | { 21 | if (lineText.Contains(match.Value) && match.Groups.Count == 10) 22 | { 23 | var r = match.Groups[2].Value; 24 | var g = match.Groups[4].Value; 25 | var b = match.Groups[6].Value; 26 | var a = match.Groups[8].Value; 27 | 28 | // Alpha will be an empty string if not provided 29 | if (string.IsNullOrEmpty(a)) 30 | { 31 | a = "1"; 32 | } 33 | 34 | try 35 | { 36 | static byte ParseToByte(string value) => ColorHelper.FloatToByte(float.Parse(value.Replace("f", string.Empty).Replace("F", string.Empty))); 37 | 38 | if (r.Contains(".") || g.Contains(".") || b.Contains(".") || a.Contains(".")) 39 | { 40 | return new ColorTag(Color.FromArgb(ParseToByte(a), ParseToByte(r), ParseToByte(g), ParseToByte(b)), match, lineNumber, lineStart, PopupType.None); 41 | } 42 | 43 | return new ColorTag(Color.FromRgb(byte.Parse(r), byte.Parse(g), byte.Parse(b)), match, lineNumber, lineStart, PopupType.None); 44 | } 45 | catch (System.Exception) 46 | { 47 | System.Diagnostics.Debug.WriteLine($"Failed to create color from r='{r}' g='{g}' b='{b}'"); 48 | } 49 | } 50 | 51 | return null; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/ColorCtorTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | [Export(typeof(ITaggerProvider))] 10 | [ContentType("CSharp")] 11 | [ContentType("Razor")] 12 | [ContentType("LegacyRazorCSharp")] 13 | [TagType(typeof(ColorTag))] 14 | internal sealed class ColorCtorTaggerProvider : ITaggerProvider 15 | { 16 | public ITagger CreateTagger(ITextBuffer buffer) 17 | where T : ITag 18 | { 19 | if (buffer == null) 20 | { 21 | throw new ArgumentNullException(nameof(buffer)); 22 | } 23 | 24 | return buffer.Properties.GetOrCreateSingletonProperty(() => new ColorCtorTagger(buffer)) as ITagger; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ColorFromIntTagger.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | using System.Windows.Media; 4 | using Microsoft.VisualStudio.Text; 5 | using WpfColorHelper; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | internal sealed class ColorFromIntTagger : RegexTagger, ITestableRegexColorTagger 10 | { 11 | internal static Regex regularExpression = new(@"(Microsoft.Maui.Graphics.Color.FromInt\(|Color.FromInt\(|Microsoft.Maui.Graphics.Color.FromUint\(|Color.FromUint\()([0-9]{1,})(\))", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 12 | 13 | public Regex ColorExpression => regularExpression; 14 | 15 | internal ColorFromIntTagger(ITextBuffer buffer) 16 | : base(buffer, [regularExpression]) 17 | { 18 | } 19 | 20 | internal override ColorTag TryCreateTagForMatch(Match match, int lineNumber, int lineStart, int spanStart, string lineText) 21 | { 22 | if (lineText.Contains(match.Value) && match.Groups.Count == 4) 23 | { 24 | var value = match.Groups[2].Value; 25 | var precedingChar = match.Index > 0 ? lineText[match.Index - 1] : ' '; 26 | 27 | // Do this check here rather than as part of the RegEx so don't have to adjust the insertion point for the adornment 28 | if (new[] { ' ', ',', '(' }.Contains(precedingChar)) 29 | { 30 | if (match.Groups[0].Value.Contains("Uint")) 31 | { 32 | if (ColorHelper.TryGetFromUint(value, out Color clr)) 33 | { 34 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.None); 35 | } 36 | } 37 | else 38 | { 39 | if (ColorHelper.TryGetFromInt(value, out Color clr)) 40 | { 41 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.None); 42 | } 43 | } 44 | 45 | System.Diagnostics.Debug.WriteLine($"Failed to understand '{value}' as a valid color name."); 46 | } 47 | } 48 | 49 | return null; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/ColorFromIntTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | [Export(typeof(ITaggerProvider))] 10 | [ContentType("CSharp")] 11 | [ContentType("Razor")] 12 | [ContentType("LegacyRazorCSharp")] 13 | [TagType(typeof(ColorTag))] 14 | internal sealed class ColorFromIntTaggerProvider : ITaggerProvider 15 | { 16 | public ITagger CreateTagger(ITextBuffer buffer) 17 | where T : ITag 18 | { 19 | if (buffer == null) 20 | { 21 | throw new ArgumentNullException(nameof(buffer)); 22 | } 23 | 24 | return buffer.Properties.GetOrCreateSingletonProperty(() => new ColorFromIntTagger(buffer)) as ITagger; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ColorFromNameTagger.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | using System.Windows.Media; 4 | using Microsoft.VisualStudio.Text; 5 | using WpfColorHelper; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | internal sealed class ColorFromNameTagger : RegexTagger, ITestableRegexColorTagger 10 | { 11 | internal static Regex regularExpression = new(@"(System.Drawing.Color.FromName\(""|Color.FromName\("")([a-z,A-Z]{3,})(""\))", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 12 | 13 | public Regex ColorExpression => regularExpression; 14 | 15 | internal ColorFromNameTagger(ITextBuffer buffer) 16 | : base(buffer, [regularExpression]) 17 | { 18 | } 19 | 20 | internal override ColorTag TryCreateTagForMatch(Match match, int lineNumber, int lineStart, int spanStart, string lineText) 21 | { 22 | if (lineText.Contains(match.Value) && match.Groups.Count == 4) 23 | { 24 | var value = match.Groups[2].Value; 25 | var precedingChar = match.Index > 0 ? lineText[match.Index - 1] : ' '; 26 | 27 | // Do this check here rather than as part of the RegEx so don't have to adjust the insertion point for the adornment 28 | if (new[] { ' ', ',', '(' }.Contains(precedingChar)) 29 | { 30 | if (ColorHelper.TryGetFromName(value, out Color clr)) 31 | { 32 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.None); 33 | } 34 | else 35 | { 36 | System.Diagnostics.Debug.WriteLine($"Failed to understand '{value}' as a valid color name."); 37 | } 38 | } 39 | } 40 | 41 | return null; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ColorFromNameTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | [Export(typeof(ITaggerProvider))] 10 | [ContentType("CSharp")] 11 | [ContentType("Razor")] 12 | [ContentType("LegacyRazorCSharp")] 13 | [TagType(typeof(ColorTag))] 14 | internal sealed class ColorFromNameTaggerProvider : ITaggerProvider 15 | { 16 | public ITagger CreateTagger(ITextBuffer buffer) 17 | where T : ITag 18 | { 19 | if (buffer == null) 20 | { 21 | throw new ArgumentNullException(nameof(buffer)); 22 | } 23 | 24 | return buffer.Properties.GetOrCreateSingletonProperty(() => new ColorFromNameTagger(buffer)) as ITagger; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ColorHslaTagger.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | using System.Windows.Media; 4 | using Microsoft.VisualStudio.Text; 5 | using WpfColorHelper; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | internal sealed class ColorHslaTagger : RegexTagger, ITestableRegexColorTagger 10 | { 11 | internal static Regex regularExpression = new(@"(Microsoft.Maui.Graphics.Color.FromHsla\(|Color.FromHsla\()([0-9fFDd., ]{5,})(\))", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 12 | 13 | public Regex ColorExpression => regularExpression; 14 | 15 | internal ColorHslaTagger(ITextBuffer buffer) 16 | : base(buffer, [regularExpression]) 17 | { 18 | } 19 | 20 | internal override ColorTag TryCreateTagForMatch(Match match, int lineNumber, int lineStart, int spanStart, string lineText) 21 | { 22 | if (lineText.Contains(match.Value) && match.Groups.Count == 4) 23 | { 24 | var value = match.Groups[2].Value; 25 | var precedingChar = match.Index > 0 ? lineText[match.Index - 1] : ' '; 26 | 27 | // Do this check here rather than as part of the RegEx so don't have to adjust the insertion point for the adornment 28 | if (new[] { ' ', ',', '(' }.Contains(precedingChar)) 29 | { 30 | if (ColorHelper.TryGetHslaColor(value, out Color clr)) 31 | { 32 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.None); 33 | } 34 | else 35 | { 36 | System.Diagnostics.Debug.WriteLine($"Failed to understand '{value}' as a valid color."); 37 | } 38 | } 39 | } 40 | 41 | return null; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ColorHslaTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | [Export(typeof(ITaggerProvider))] 10 | [ContentType("CSharp")] 11 | [ContentType("Razor")] 12 | [ContentType("LegacyRazorCSharp")] 13 | [TagType(typeof(ColorTag))] 14 | internal sealed class ColorHslaTaggerProvider : ITaggerProvider 15 | { 16 | public ITagger CreateTagger(ITextBuffer buffer) 17 | where T : ITag 18 | { 19 | if (buffer == null) 20 | { 21 | throw new ArgumentNullException(nameof(buffer)); 22 | } 23 | 24 | return buffer.Properties.GetOrCreateSingletonProperty(() => new ColorHslaTagger(buffer)) as ITagger; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ColorHsvaTagger.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | using System.Windows.Media; 4 | using Microsoft.VisualStudio.Text; 5 | using WpfColorHelper; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | internal sealed class ColorHsvaTagger : RegexTagger 10 | { 11 | internal static Regex regularExpression = new(@"(Microsoft.Maui.Graphics.Color.FromHsva\(|Color.FromHsva\(|Microsoft.Maui.Graphics.Color.FromHsv\(|Color.FromHsv\()([0-9fFDd., ]{5,})(\))", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 12 | 13 | public Regex ColorExpression => regularExpression; 14 | 15 | internal ColorHsvaTagger(ITextBuffer buffer) 16 | : base(buffer, [regularExpression]) 17 | { 18 | } 19 | 20 | internal override ColorTag TryCreateTagForMatch(Match match, int lineNumber, int lineStart, int spanStart, string lineText) 21 | { 22 | if (lineText.Contains(match.Value) && match.Groups.Count == 4) 23 | { 24 | var value = match.Groups[2].Value; 25 | var precedingChar = match.Index > 0 ? lineText[match.Index - 1] : ' '; 26 | 27 | // Do this check here rather than as part of the RegEx so don't have to adjust the insertion point for the adornment 28 | if (new[] { ' ', ',', '(' }.Contains(precedingChar)) 29 | { 30 | if (ColorHelper.TryGetHsvaColor(value, out Color clr)) 31 | { 32 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.None); 33 | } 34 | else 35 | { 36 | System.Diagnostics.Debug.WriteLine($"Failed to understand '{value}' as a valid color."); 37 | } 38 | } 39 | } 40 | 41 | return null; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ColorHsvaTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | [Export(typeof(ITaggerProvider))] 10 | [ContentType("CSharp")] 11 | [ContentType("Razor")] 12 | [ContentType("LegacyRazorCSharp")] 13 | [TagType(typeof(ColorTag))] 14 | internal sealed class ColorHsvaTaggerProvider : ITaggerProvider 15 | { 16 | public ITagger CreateTagger(ITextBuffer buffer) 17 | where T : ITag 18 | { 19 | if (buffer == null) 20 | { 21 | throw new ArgumentNullException(nameof(buffer)); 22 | } 23 | 24 | return buffer.Properties.GetOrCreateSingletonProperty(() => new ColorHsvaTagger(buffer)) as ITagger; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ColorListHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows.Media; 5 | using WpfColorHelper; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | public static class ColorListHelper 10 | { 11 | // Note that SystemColors are KnownColors 12 | // Also note that System.Windows.SystemColors are the same as System.Drawing.SystemColors, but with "Color" on the end 13 | public static IEnumerable SystemColorsAlphabetically() 14 | { 15 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.ActiveBorder); 16 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.ActiveCaption); 17 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.ActiveCaptionText); 18 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.AppWorkspace); 19 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.ButtonFace); 20 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.ButtonHighlight); 21 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.ButtonShadow); 22 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.Control); 23 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.ControlDark); 24 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.ControlDarkDark); 25 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.ControlLight); 26 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.ControlLightLight); 27 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.ControlText); 28 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.Desktop); 29 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.GradientActiveCaption); 30 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.GradientInactiveCaption); 31 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.Highlight); 32 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.HighlightText); 33 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.HotTrack); 34 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.InactiveBorder); 35 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.InactiveCaption); 36 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.InactiveCaptionText); 37 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.Info); 38 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.InfoText); 39 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.Menu); 40 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.MenuBar); 41 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.MenuHighlight); 42 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.MenuText); 43 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.ScrollBar); 44 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.Window); 45 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.WindowFrame); 46 | yield return System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.WindowText); 47 | } 48 | 49 | // [In theory] hardcoding this list to save work at runtime. 50 | public static IEnumerable ConsoleColorsNamesAlphabetical() 51 | { 52 | yield return nameof(System.ConsoleColor.Black); 53 | yield return nameof(System.ConsoleColor.Blue); 54 | yield return nameof(System.ConsoleColor.Cyan); 55 | yield return nameof(System.ConsoleColor.DarkBlue); 56 | yield return nameof(System.ConsoleColor.DarkCyan); 57 | yield return nameof(System.ConsoleColor.DarkGray); 58 | yield return nameof(System.ConsoleColor.DarkGreen); 59 | yield return nameof(System.ConsoleColor.DarkMagenta); 60 | yield return nameof(System.ConsoleColor.DarkRed); 61 | yield return nameof(System.ConsoleColor.DarkYellow); 62 | yield return nameof(System.ConsoleColor.Gray); 63 | yield return nameof(System.ConsoleColor.Green); 64 | yield return nameof(System.ConsoleColor.Magenta); 65 | yield return nameof(System.ConsoleColor.Red); 66 | yield return nameof(System.ConsoleColor.White); 67 | yield return nameof(System.ConsoleColor.Yellow); 68 | } 69 | 70 | // https://docs.unity3d.com/Packages/com.unity.ugui@1.0/manual/StyledText.html#supported-colors 71 | public static IEnumerable UnityColorsAlphabetical() 72 | { 73 | yield return nameof(System.Drawing.Color.Aqua); 74 | yield return nameof(System.Drawing.Color.Black); 75 | yield return nameof(System.Drawing.Color.Blue); 76 | yield return nameof(System.Drawing.Color.Brown); 77 | yield return nameof(System.Drawing.Color.Cyan); 78 | yield return nameof(System.Drawing.Color.DarkBlue); 79 | yield return nameof(System.Drawing.Color.Fuchsia); 80 | yield return nameof(System.Drawing.Color.Green); 81 | yield return nameof(System.Drawing.Color.Gray); 82 | yield return nameof(System.Drawing.Color.LightBlue); 83 | yield return nameof(System.Drawing.Color.Lime); 84 | yield return nameof(System.Drawing.Color.Magenta); 85 | yield return nameof(System.Drawing.Color.Maroon); 86 | yield return nameof(System.Drawing.Color.Navy); 87 | yield return nameof(System.Drawing.Color.Olive); 88 | yield return nameof(System.Drawing.Color.Orange); 89 | yield return nameof(System.Drawing.Color.Purple); 90 | yield return nameof(System.Drawing.Color.Red); 91 | yield return nameof(System.Drawing.Color.Silver); 92 | yield return nameof(System.Drawing.Color.Teal); 93 | yield return nameof(System.Drawing.Color.White); 94 | yield return nameof(System.Drawing.Color.Yellow); 95 | } 96 | 97 | public static IEnumerable SystemDrawingColorsAlphabetical() 98 | { 99 | yield return System.Drawing.Color.AliceBlue; 100 | yield return System.Drawing.Color.AntiqueWhite; 101 | yield return System.Drawing.Color.Aqua; 102 | yield return System.Drawing.Color.Aquamarine; 103 | yield return System.Drawing.Color.Azure; 104 | yield return System.Drawing.Color.Beige; 105 | yield return System.Drawing.Color.Bisque; 106 | yield return System.Drawing.Color.Black; 107 | yield return System.Drawing.Color.BlanchedAlmond; 108 | yield return System.Drawing.Color.Blue; 109 | yield return System.Drawing.Color.BlueViolet; 110 | yield return System.Drawing.Color.Brown; 111 | yield return System.Drawing.Color.BurlyWood; 112 | yield return System.Drawing.Color.CadetBlue; 113 | yield return System.Drawing.Color.Chartreuse; 114 | yield return System.Drawing.Color.Chocolate; 115 | yield return System.Drawing.Color.Coral; 116 | yield return System.Drawing.Color.CornflowerBlue; 117 | yield return System.Drawing.Color.Cornsilk; 118 | yield return System.Drawing.Color.Crimson; 119 | yield return System.Drawing.Color.Cyan; 120 | yield return System.Drawing.Color.DarkBlue; 121 | yield return System.Drawing.Color.DarkCyan; 122 | yield return System.Drawing.Color.DarkGoldenrod; 123 | yield return System.Drawing.Color.DarkGray; 124 | yield return System.Drawing.Color.DarkGreen; 125 | yield return System.Drawing.Color.DarkKhaki; 126 | yield return System.Drawing.Color.DarkMagenta; 127 | yield return System.Drawing.Color.DarkOliveGreen; 128 | yield return System.Drawing.Color.DarkOrange; 129 | yield return System.Drawing.Color.DarkOrchid; 130 | yield return System.Drawing.Color.DarkRed; 131 | yield return System.Drawing.Color.DarkSalmon; 132 | yield return System.Drawing.Color.DarkSeaGreen; 133 | yield return System.Drawing.Color.DarkSlateBlue; 134 | yield return System.Drawing.Color.DarkSlateGray; 135 | yield return System.Drawing.Color.DarkTurquoise; 136 | yield return System.Drawing.Color.DarkViolet; 137 | yield return System.Drawing.Color.DeepPink; 138 | yield return System.Drawing.Color.DeepSkyBlue; 139 | yield return System.Drawing.Color.DimGray; 140 | yield return System.Drawing.Color.DodgerBlue; 141 | yield return System.Drawing.Color.Firebrick; 142 | yield return System.Drawing.Color.FloralWhite; 143 | yield return System.Drawing.Color.ForestGreen; 144 | yield return System.Drawing.Color.Fuchsia; 145 | yield return System.Drawing.Color.Gainsboro; 146 | yield return System.Drawing.Color.GhostWhite; 147 | yield return System.Drawing.Color.Gold; 148 | yield return System.Drawing.Color.Goldenrod; 149 | yield return System.Drawing.Color.Gray; 150 | yield return System.Drawing.Color.Green; 151 | yield return System.Drawing.Color.GreenYellow; 152 | yield return System.Drawing.Color.Honeydew; 153 | yield return System.Drawing.Color.HotPink; 154 | yield return System.Drawing.Color.IndianRed; 155 | yield return System.Drawing.Color.Indigo; 156 | yield return System.Drawing.Color.Ivory; 157 | yield return System.Drawing.Color.Khaki; 158 | yield return System.Drawing.Color.Lavender; 159 | yield return System.Drawing.Color.LavenderBlush; 160 | yield return System.Drawing.Color.LawnGreen; 161 | yield return System.Drawing.Color.LemonChiffon; 162 | yield return System.Drawing.Color.LightBlue; 163 | yield return System.Drawing.Color.LightCoral; 164 | yield return System.Drawing.Color.LightCyan; 165 | yield return System.Drawing.Color.LightGreen; 166 | yield return System.Drawing.Color.LightPink; 167 | yield return System.Drawing.Color.LightSalmon; 168 | yield return System.Drawing.Color.LightSeaGreen; 169 | yield return System.Drawing.Color.LightSkyBlue; 170 | yield return System.Drawing.Color.LightSlateGray; 171 | yield return System.Drawing.Color.LightSteelBlue; 172 | yield return System.Drawing.Color.LightYellow; 173 | yield return System.Drawing.Color.Lime; 174 | yield return System.Drawing.Color.LimeGreen; 175 | yield return System.Drawing.Color.Linen; 176 | yield return System.Drawing.Color.Magenta; 177 | yield return System.Drawing.Color.Maroon; 178 | yield return System.Drawing.Color.MediumAquamarine; 179 | yield return System.Drawing.Color.MediumBlue; 180 | yield return System.Drawing.Color.MediumOrchid; 181 | yield return System.Drawing.Color.MediumPurple; 182 | yield return System.Drawing.Color.MediumSeaGreen; 183 | yield return System.Drawing.Color.MediumSlateBlue; 184 | yield return System.Drawing.Color.MediumSpringGreen; 185 | yield return System.Drawing.Color.MediumTurquoise; 186 | yield return System.Drawing.Color.MediumVioletRed; 187 | yield return System.Drawing.Color.MidnightBlue; 188 | yield return System.Drawing.Color.MintCream; 189 | yield return System.Drawing.Color.MistyRose; 190 | yield return System.Drawing.Color.Moccasin; 191 | yield return System.Drawing.Color.NavajoWhite; 192 | yield return System.Drawing.Color.Navy; 193 | yield return System.Drawing.Color.OldLace; 194 | yield return System.Drawing.Color.Olive; 195 | yield return System.Drawing.Color.OliveDrab; 196 | yield return System.Drawing.Color.Orange; 197 | yield return System.Drawing.Color.OrangeRed; 198 | yield return System.Drawing.Color.Orchid; 199 | yield return System.Drawing.Color.PaleGoldenrod; 200 | yield return System.Drawing.Color.PaleGreen; 201 | yield return System.Drawing.Color.PaleTurquoise; 202 | yield return System.Drawing.Color.PaleVioletRed; 203 | yield return System.Drawing.Color.PapayaWhip; 204 | yield return System.Drawing.Color.PeachPuff; 205 | yield return System.Drawing.Color.Peru; 206 | yield return System.Drawing.Color.Pink; 207 | yield return System.Drawing.Color.Plum; 208 | yield return System.Drawing.Color.PowderBlue; 209 | yield return System.Drawing.Color.Purple; 210 | yield return System.Drawing.Color.Red; 211 | yield return System.Drawing.Color.RosyBrown; 212 | yield return System.Drawing.Color.RoyalBlue; 213 | yield return System.Drawing.Color.SaddleBrown; 214 | yield return System.Drawing.Color.Salmon; 215 | yield return System.Drawing.Color.SandyBrown; 216 | yield return System.Drawing.Color.SeaGreen; 217 | yield return System.Drawing.Color.SeaShell; 218 | yield return System.Drawing.Color.Sienna; 219 | yield return System.Drawing.Color.Silver; 220 | yield return System.Drawing.Color.SkyBlue; 221 | yield return System.Drawing.Color.SlateBlue; 222 | yield return System.Drawing.Color.SlateGray; 223 | yield return System.Drawing.Color.Snow; 224 | yield return System.Drawing.Color.SpringGreen; 225 | yield return System.Drawing.Color.SteelBlue; 226 | yield return System.Drawing.Color.Tan; 227 | yield return System.Drawing.Color.Teal; 228 | yield return System.Drawing.Color.Thistle; 229 | yield return System.Drawing.Color.Tomato; 230 | yield return System.Drawing.Color.Transparent; 231 | yield return System.Drawing.Color.Turquoise; 232 | yield return System.Drawing.Color.Violet; 233 | yield return System.Drawing.Color.Wheat; 234 | yield return System.Drawing.Color.White; 235 | yield return System.Drawing.Color.WhiteSmoke; 236 | yield return System.Drawing.Color.Yellow; 237 | yield return System.Drawing.Color.YellowGreen; 238 | } 239 | 240 | public static IEnumerable SystemDrawingColorsSpectrum() 241 | { 242 | yield return System.Drawing.Color.Tan; 243 | yield return System.Drawing.Color.RosyBrown; 244 | yield return System.Drawing.Color.Sienna; 245 | yield return System.Drawing.Color.SaddleBrown; 246 | yield return System.Drawing.Color.DarkRed; 247 | yield return System.Drawing.Color.Maroon; 248 | yield return System.Drawing.Color.Brown; 249 | yield return System.Drawing.Color.Firebrick; 250 | yield return System.Drawing.Color.Crimson; 251 | yield return System.Drawing.Color.Red; 252 | yield return System.Drawing.Color.OrangeRed; 253 | yield return System.Drawing.Color.IndianRed; 254 | yield return System.Drawing.Color.Tomato; 255 | yield return System.Drawing.Color.Chocolate; 256 | yield return System.Drawing.Color.DarkSalmon; 257 | yield return System.Drawing.Color.Coral; 258 | yield return System.Drawing.Color.Salmon; 259 | yield return System.Drawing.Color.LightCoral; 260 | yield return System.Drawing.Color.Peru; 261 | yield return System.Drawing.Color.DarkOrange; 262 | yield return System.Drawing.Color.SandyBrown; 263 | yield return System.Drawing.Color.LightSalmon; 264 | yield return System.Drawing.Color.Orange; 265 | yield return System.Drawing.Color.Goldenrod; 266 | yield return System.Drawing.Color.BurlyWood; 267 | yield return System.Drawing.Color.DarkGoldenrod; 268 | yield return System.Drawing.Color.DarkKhaki; 269 | yield return System.Drawing.Color.AntiqueWhite; 270 | yield return System.Drawing.Color.Beige; 271 | yield return System.Drawing.Color.Cornsilk; 272 | yield return System.Drawing.Color.OldLace; 273 | yield return System.Drawing.Color.Linen; 274 | yield return System.Drawing.Color.Honeydew; 275 | yield return System.Drawing.Color.LightYellow; 276 | yield return System.Drawing.Color.LemonChiffon; 277 | yield return System.Drawing.Color.BlanchedAlmond; 278 | yield return System.Drawing.Color.Bisque; 279 | yield return System.Drawing.Color.Wheat; 280 | yield return System.Drawing.Color.Moccasin; 281 | yield return System.Drawing.Color.NavajoWhite; 282 | yield return System.Drawing.Color.PeachPuff; 283 | yield return System.Drawing.Color.PapayaWhip; 284 | yield return System.Drawing.Color.PaleGoldenrod; 285 | yield return System.Drawing.Color.Khaki; 286 | yield return System.Drawing.Color.Gold; 287 | yield return System.Drawing.Color.Yellow; 288 | yield return System.Drawing.Color.GreenYellow; 289 | yield return System.Drawing.Color.YellowGreen; 290 | yield return System.Drawing.Color.Chartreuse; 291 | yield return System.Drawing.Color.LawnGreen; 292 | yield return System.Drawing.Color.PaleGreen; 293 | yield return System.Drawing.Color.LightGreen; 294 | yield return System.Drawing.Color.MediumSpringGreen; 295 | yield return System.Drawing.Color.SpringGreen; 296 | yield return System.Drawing.Color.Lime; 297 | yield return System.Drawing.Color.LimeGreen; 298 | yield return System.Drawing.Color.MediumSeaGreen; 299 | yield return System.Drawing.Color.ForestGreen; 300 | yield return System.Drawing.Color.Green; 301 | yield return System.Drawing.Color.DarkGreen; 302 | yield return System.Drawing.Color.SeaGreen; 303 | yield return System.Drawing.Color.DarkSeaGreen; 304 | yield return System.Drawing.Color.OliveDrab; 305 | yield return System.Drawing.Color.Olive; 306 | yield return System.Drawing.Color.DarkOliveGreen; 307 | yield return System.Drawing.Color.Teal; 308 | yield return System.Drawing.Color.DarkCyan; 309 | yield return System.Drawing.Color.CadetBlue; 310 | yield return System.Drawing.Color.LightSeaGreen; 311 | yield return System.Drawing.Color.MediumAquamarine; 312 | yield return System.Drawing.Color.MediumTurquoise; 313 | yield return System.Drawing.Color.Turquoise; 314 | yield return System.Drawing.Color.Aquamarine; 315 | yield return System.Drawing.Color.PaleTurquoise; 316 | yield return System.Drawing.Color.LightBlue; 317 | yield return System.Drawing.Color.LightCyan; 318 | yield return System.Drawing.Color.PowderBlue; 319 | yield return System.Drawing.Color.SkyBlue; 320 | yield return System.Drawing.Color.LightSkyBlue; 321 | yield return System.Drawing.Color.LightSteelBlue; 322 | yield return System.Drawing.Color.DeepSkyBlue; 323 | yield return System.Drawing.Color.Aqua; 324 | yield return System.Drawing.Color.Cyan; 325 | yield return System.Drawing.Color.DarkTurquoise; 326 | yield return System.Drawing.Color.DodgerBlue; 327 | yield return System.Drawing.Color.CornflowerBlue; 328 | yield return System.Drawing.Color.SteelBlue; 329 | yield return System.Drawing.Color.RoyalBlue; 330 | yield return System.Drawing.Color.MediumBlue; 331 | yield return System.Drawing.Color.Blue; 332 | yield return System.Drawing.Color.DarkBlue; 333 | yield return System.Drawing.Color.Navy; 334 | yield return System.Drawing.Color.MidnightBlue; 335 | yield return System.Drawing.Color.DarkSlateBlue; 336 | yield return System.Drawing.Color.Indigo; 337 | yield return System.Drawing.Color.SlateBlue; 338 | yield return System.Drawing.Color.MediumSlateBlue; 339 | yield return System.Drawing.Color.MediumPurple; 340 | yield return System.Drawing.Color.BlueViolet; 341 | yield return System.Drawing.Color.DarkViolet; 342 | yield return System.Drawing.Color.DarkOrchid; 343 | yield return System.Drawing.Color.Purple; 344 | yield return System.Drawing.Color.DarkMagenta; 345 | yield return System.Drawing.Color.MediumOrchid; 346 | yield return System.Drawing.Color.Magenta; 347 | yield return System.Drawing.Color.Fuchsia; 348 | yield return System.Drawing.Color.Orchid; 349 | yield return System.Drawing.Color.Violet; 350 | yield return System.Drawing.Color.Plum; 351 | yield return System.Drawing.Color.PaleVioletRed; 352 | yield return System.Drawing.Color.MediumVioletRed; 353 | yield return System.Drawing.Color.DeepPink; 354 | yield return System.Drawing.Color.HotPink; 355 | yield return System.Drawing.Color.Thistle; 356 | yield return System.Drawing.Color.LightPink; 357 | yield return System.Drawing.Color.Pink; 358 | yield return System.Drawing.Color.MistyRose; 359 | yield return System.Drawing.Color.Lavender; 360 | yield return System.Drawing.Color.LavenderBlush; 361 | yield return System.Drawing.Color.AliceBlue; 362 | yield return System.Drawing.Color.Azure; 363 | yield return System.Drawing.Color.FloralWhite; 364 | yield return System.Drawing.Color.WhiteSmoke; 365 | yield return System.Drawing.Color.SeaShell; 366 | yield return System.Drawing.Color.Ivory; 367 | yield return System.Drawing.Color.MintCream; 368 | yield return System.Drawing.Color.Snow; 369 | yield return System.Drawing.Color.White; 370 | yield return System.Drawing.Color.GhostWhite; 371 | yield return System.Drawing.Color.Gainsboro; 372 | yield return System.Drawing.Color.Silver; 373 | yield return System.Drawing.Color.DarkGray; 374 | yield return System.Drawing.Color.Gray; 375 | yield return System.Drawing.Color.DimGray; 376 | yield return System.Drawing.Color.LightSlateGray; 377 | yield return System.Drawing.Color.SlateGray; 378 | yield return System.Drawing.Color.DarkSlateGray; 379 | yield return System.Drawing.Color.Black; 380 | yield return System.Drawing.Color.Transparent; 381 | } 382 | 383 | // This was created from : https://github.com/maddymontaquila/funcolors/blob/main/FunColor.cs 384 | // Last updated 9-dec-2024 (5b11965) 385 | public static IEnumerable<(string Name, Color Color)> FunColors() 386 | { 387 | yield return ("DotNetPurple2024", (Color)ColorConverter.ConvertFromString("#FF512BD4")); 388 | yield return ("XamarinBlue2011", (Color)ColorConverter.ConvertFromString("#FF5596D8")); 389 | yield return ("AndroidBotGreen2024", (Color)ColorConverter.ConvertFromString("#FFA7CD45")); 390 | yield return ("RubberDuckYellow", (Color)ColorConverter.ConvertFromString("#FFFFD700")); 391 | yield return ("GameBoyGreen", (Color)ColorConverter.ConvertFromString("#FF9BBC0F")); 392 | yield return ("BarbiePink", (Color)ColorConverter.ConvertFromString("#FFDA1884")); 393 | yield return ("PotatoHeadBrown", (Color)ColorConverter.ConvertFromString("#FF8B4513")); 394 | yield return ("TickleMeElmoRed", (Color)ColorConverter.ConvertFromString("#FFFF3F3F")); 395 | yield return ("LegoRed", (Color)ColorConverter.ConvertFromString("#FFB40000")); 396 | yield return ("HotWheelsBlue", (Color)ColorConverter.ConvertFromString("#FF005BAC")); 397 | yield return ("NerfOrange", (Color)ColorConverter.ConvertFromString("#FFFF6F00")); 398 | yield return ("PlayDohYellow", (Color)ColorConverter.ConvertFromString("#FFFBE870")); 399 | yield return ("MyLittlePonyPurple", (Color)ColorConverter.ConvertFromString("#FFDDA0DD")); 400 | yield return ("TransformersSilver", (Color)ColorConverter.ConvertFromString("#FFC0C0C0")); 401 | yield return ("GIJoeGreen", (Color)ColorConverter.ConvertFromString("#FF4B5320")); 402 | yield return ("PowerRangersRed", (Color)ColorConverter.ConvertFromString("#FFFF0000")); 403 | yield return ("TeenageMutantNinjaTurtlesGreen", (Color)ColorConverter.ConvertFromString("#FF008000")); 404 | yield return ("CareBearsRainbow", (Color)ColorConverter.ConvertFromString("#FFFF69B4")); 405 | yield return ("AppleIIBeige", (Color)ColorConverter.ConvertFromString("#FFD3D3D3")); 406 | yield return ("Commodore64Brown", (Color)ColorConverter.ConvertFromString("#FF6C4F3D")); 407 | yield return ("Atari800Blue", (Color)ColorConverter.ConvertFromString("#FF0057A0")); 408 | yield return ("IBMPCGray", (Color)ColorConverter.ConvertFromString("#FF808080")); 409 | yield return ("TRS80Silver", (Color)ColorConverter.ConvertFromString("#FFC0C0C0")); 410 | yield return ("ZXSpectrumBlack", (Color)ColorConverter.ConvertFromString("#FF000000")); 411 | yield return ("AmigaWhite", (Color)ColorConverter.ConvertFromString("#FFFFFFFF")); 412 | yield return ("MSXBlue", (Color)ColorConverter.ConvertFromString("#FF0000FF")); 413 | yield return ("BratSummer", (Color)ColorConverter.ConvertFromString("#FF8ACE00")); 414 | yield return ("BratAutumn", (Color)ColorConverter.ConvertFromString("#FFFF8C00")); 415 | yield return ("NeonElectricBlue", (Color)ColorConverter.ConvertFromString("#FF154FEE")); 416 | yield return ("NeonVividMagenta", (Color)ColorConverter.ConvertFromString("#FFFF08FC")); 417 | yield return ("PukePink", (Color)ColorConverter.ConvertFromString("#FFFF3AC6")); 418 | yield return ("MushyPeas", (Color)ColorConverter.ConvertFromString("#FF5FA41C")); 419 | yield return ("PainfulRed", (Color)ColorConverter.ConvertFromString("#FFFF1A00")); 420 | yield return ("ShrekGreen", (Color)ColorConverter.ConvertFromString("#FF009B00")); 421 | } 422 | } 423 | -------------------------------------------------------------------------------- /src/ColorRgbTagger.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | using System.Windows.Media; 4 | using Microsoft.VisualStudio.Text; 5 | using WpfColorHelper; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | internal sealed class ColorRgbTagger : RegexTagger, ITestableRegexColorTagger 10 | { 11 | internal static Regex regularExpression = new(@"(Color.FromRgb\()([0-9, ]{5,})(\))", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 12 | 13 | public Regex ColorExpression => regularExpression; 14 | 15 | internal ColorRgbTagger(ITextBuffer buffer) 16 | : base(buffer, [regularExpression]) 17 | { 18 | } 19 | 20 | internal override ColorTag TryCreateTagForMatch(Match match, int lineNumber, int lineStart, int spanStart, string lineText) 21 | { 22 | if (lineText.Contains(match.Value) && match.Groups.Count == 4) 23 | { 24 | var value = match.Groups[2].Value; 25 | var precedingChar = match.Index > 0 ? lineText[match.Index - 1] : ' '; 26 | 27 | // Do this check here rather than as part of the RegEx so don't have to adjust the insertion point for the adornment 28 | if (new[] { ' ', ',', '(' }.Contains(precedingChar)) 29 | { 30 | if (ColorHelper.TryGetRgbColor(value, out Color clr)) 31 | { 32 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.None); 33 | } 34 | else 35 | { 36 | System.Diagnostics.Debug.WriteLine($"Failed to understand '{value}' as a valid color."); 37 | } 38 | } 39 | } 40 | 41 | return null; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ColorRgbTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | [Export(typeof(ITaggerProvider))] 10 | [ContentType("CSharp")] 11 | [ContentType("Razor")] 12 | [ContentType("LegacyRazorCSharp")] 13 | [TagType(typeof(ColorTag))] 14 | internal sealed class ColorRgbTaggerProvider : ITaggerProvider 15 | { 16 | public ITagger CreateTagger(ITextBuffer buffer) 17 | where T : ITag 18 | { 19 | if (buffer == null) 20 | { 21 | throw new ArgumentNullException(nameof(buffer)); 22 | } 23 | 24 | return buffer.Properties.GetOrCreateSingletonProperty(() => new ColorRgbTagger(buffer)) as ITagger; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ColorRgbaTagger.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | using System.Windows.Media; 4 | using Microsoft.VisualStudio.Text; 5 | using WpfColorHelper; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | internal sealed class ColorRgbaTagger : RegexTagger, ITestableRegexColorTagger 10 | { 11 | internal static Regex regularExpression = new(@"(Color.FromRgba\()([0-9, ]{7,})(\))", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 12 | 13 | public Regex ColorExpression => regularExpression; 14 | 15 | internal ColorRgbaTagger(ITextBuffer buffer) 16 | : base(buffer, [regularExpression]) 17 | { 18 | } 19 | 20 | internal override ColorTag TryCreateTagForMatch(Match match, int lineNumber, int lineStart, int spanStart, string lineText) 21 | { 22 | if (lineText.Contains(match.Value) && match.Groups.Count == 4) 23 | { 24 | var value = match.Groups[2].Value; 25 | var precedingChar = match.Index > 0 ? lineText[match.Index - 1] : ' '; 26 | 27 | // Do this check here rather than as part of the RegEx so don't have to adjust the insertion point for the adornment 28 | if (new[] { ' ', ',', '(' }.Contains(precedingChar)) 29 | { 30 | if (ColorHelper.TryGetRgbaColor(value, out Color clr)) 31 | { 32 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.None); 33 | } 34 | else 35 | { 36 | System.Diagnostics.Debug.WriteLine($"Failed to understand '{value}' as a valid color."); 37 | } 38 | } 39 | } 40 | 41 | return null; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/ColorRgbaTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | [Export(typeof(ITaggerProvider))] 10 | [ContentType("CSharp")] 11 | [ContentType("Razor")] 12 | [ContentType("LegacyRazorCSharp")] 13 | [TagType(typeof(ColorTag))] 14 | internal sealed class ColorRgbaTaggerProvider : ITaggerProvider 15 | { 16 | public ITagger CreateTagger(ITextBuffer buffer) 17 | where T : ITag 18 | { 19 | if (buffer == null) 20 | { 21 | throw new ArgumentNullException(nameof(buffer)); 22 | } 23 | 24 | return buffer.Properties.GetOrCreateSingletonProperty(() => new ColorRgbaTagger(buffer)) as ITagger; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ColorSelectionDialog.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | using System.Windows.Media; 3 | using Microsoft.VisualStudio.PlatformUI; 4 | 5 | namespace CsInlineColorViz; 6 | 7 | class ColorSelectionDialog : DialogWindow 8 | { 9 | public string SelectedName { get; set; } 10 | 11 | public ColorSelectionDialog() 12 | { 13 | this.HasMaximizeButton = false; 14 | this.HasMinimizeButton = false; 15 | 16 | this.Width = 500; 17 | this.Height = 400; 18 | this.MinHeight = 200; 19 | this.MinWidth = 200; 20 | } 21 | 22 | internal Button CreateButton(string name, System.Windows.Media.Color clr) 23 | { 24 | var cbtn = new Button 25 | { 26 | Content = name, 27 | Background = new SolidColorBrush(clr), 28 | Height = 30, 29 | }; 30 | cbtn.Click += (s, e) => { SelectedName = name; this.DialogResult = true; }; 31 | return cbtn; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/ColorTag.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using System.Windows.Media; 3 | using Microsoft.VisualStudio.Text.Tagging; 4 | 5 | namespace CsInlineColorViz; 6 | 7 | public class ColorTag : ITag 8 | { 9 | public ColorTag(Color clr, Match match, int lineNumber, int lineCharOffset, PopupType popupType) 10 | { 11 | Clr = clr; 12 | Match = match; 13 | LineNumber = lineNumber; 14 | LineCharOffset = lineCharOffset; 15 | PopupType = popupType; 16 | } 17 | 18 | public Color Clr { get; } 19 | public Match Match { get; } 20 | public int LineNumber { get; } 21 | public int LineCharOffset { get; } 22 | public PopupType PopupType { get; } 23 | } 24 | -------------------------------------------------------------------------------- /src/ColorTagger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text.RegularExpressions; 4 | using System.Windows.Media; 5 | using Microsoft.VisualStudio.Text; 6 | using WpfColorHelper; 7 | 8 | namespace CsInlineColorViz; 9 | 10 | internal sealed class ColorTagger : RegexTagger, ITestableRegexColorTagger 11 | { 12 | internal static Regex regularExpression = new(@"(Color|Colors|ConsoleColor|System.ConsoleColor|System.Windows.Media.Colors|System.Drawing.Color|KnownColor|System.Drawing.KnownColor|Microsoft.UI.Colors|SystemColors|System.Drawing.SystemColors|System.Windows.SystemColors)([\.]{1})(?!From)([a-zA-Z]{3,})", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 13 | 14 | public Regex ColorExpression => regularExpression; 15 | 16 | private bool haveLoggedUseCount = false; 17 | 18 | public ColorTagger(ITextBuffer buffer) 19 | : base(buffer, [regularExpression]) 20 | { 21 | } 22 | 23 | internal override ColorTag TryCreateTagForMatch(Match match, int lineNumber, int lineStart, int spanStart, string lineText) 24 | { 25 | if (lineText.Contains(match.Value) && match.Groups.Count == 4) 26 | { 27 | var value = match.Groups[3].Value; 28 | var precedingChar = match.Index > 0 ? lineText[match.Index - 1] : ' '; 29 | 30 | // Do this check here rather than as part of the RegEx so don't have to adjust the insertion point for the adornment 31 | if (new[] { ' ', ',', '(', '\t' }.Contains(precedingChar)) 32 | { 33 | if (match.Groups[1].Value.EndsWith("KnownColor") || match.Groups[1].Value.EndsWith("SystemColors")) 34 | { 35 | if (Enum.TryParse(value, out System.Drawing.KnownColor knownColor)) 36 | { 37 | value = ColorHelper.ToHex(System.Drawing.Color.FromKnownColor(knownColor)); 38 | } 39 | } 40 | 41 | if (ColorHelper.TryGetColor(value, out Color clr)) 42 | { 43 | if (!haveLoggedUseCount) 44 | { 45 | // This Tagger is loaded once per open document 46 | // Only record once per document regardless of how many times an adorner is created 47 | haveLoggedUseCount = true; 48 | 49 | _ = System.Threading.Tasks.Task.Run(async () => 50 | { 51 | var settings = await InternalSettings.GetLiveInstanceAsync(); 52 | settings.UseCount += 1; 53 | await settings.SaveAsync(); 54 | }); 55 | } 56 | 57 | // TODO: Need to handle all the different popup types to support 58 | if (match.Groups[1].Value.EndsWith(".Colors") || match.Groups[1].Value.EndsWith(".Color") || match.Groups[1].Value == "Colors" || match.Groups[1].Value == "Color") 59 | { 60 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.NamedColors); 61 | } 62 | else if (match.Groups[1].Value.EndsWith("ConsoleColor")) 63 | { 64 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.ConsoleColors); 65 | } 66 | else if (match.Groups[1].Value.EndsWith("KnownColor")) 67 | { 68 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.KnownColors); 69 | } 70 | else if (match.Groups[1].Value.EndsWith("SystemColors")) 71 | { 72 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.SystemColors); 73 | } 74 | else 75 | { 76 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.None); 77 | } 78 | } 79 | else 80 | { 81 | System.Diagnostics.Debug.WriteLine($"Failed to understand '{value}' as a valid color."); 82 | } 83 | } 84 | } 85 | 86 | return null; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/ColorTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | [Export(typeof(ITaggerProvider))] 10 | [ContentType("CSharp")] 11 | [ContentType("Razor")] 12 | [ContentType("LegacyRazorCSharp")] 13 | [TagType(typeof(ColorTag))] 14 | internal sealed class ColorTaggerProvider : ITaggerProvider 15 | { 16 | public ITagger CreateTagger(ITextBuffer buffer) 17 | where T : ITag 18 | { 19 | if (buffer == null) 20 | { 21 | throw new ArgumentNullException(nameof(buffer)); 22 | } 23 | 24 | return buffer.Properties.GetOrCreateSingletonProperty(() => new ColorTagger(buffer)) as ITagger; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ConsoleColorDialog.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | using WpfColorHelper; 3 | 4 | namespace CsInlineColorViz; 5 | 6 | class ConsoleColorDialog : ColorSelectionDialog 7 | { 8 | public ConsoleColorDialog() : base() 9 | { 10 | var sp = new StackPanel(); 11 | 12 | foreach (var colorName in ColorListHelper.ConsoleColorsNamesAlphabetical()) 13 | { 14 | if (ColorHelper.TryGetColor(colorName, out System.Windows.Media.Color clr)) 15 | { 16 | sp.Children.Add(CreateButton(colorName, clr)); 17 | } 18 | } 19 | 20 | var sv = new ScrollViewer { VerticalScrollBarVisibility = ScrollBarVisibility.Auto, Content = sp }; 21 | 22 | this.Content = sv; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/CsInlineColorViz.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 5 | 6 | 7 | 8 | Debug 9 | AnyCPU 10 | 2.0 11 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 12 | {7E6E7299-F72B-4236-B833-B0F2BEB4BFFD} 13 | Library 14 | Properties 15 | CsInlineColorViz 16 | CsInlineColorViz 17 | v4.8.1 18 | true 19 | true 20 | true 21 | true 22 | false 23 | true 24 | true 25 | Program 26 | $(DevEnvDir)devenv.exe 27 | /rootsuffix Exp 28 | 29 | 30 | true 31 | full 32 | false 33 | bin\Debug\ 34 | DEBUG;TRACE 35 | prompt 36 | 4 37 | 38 | 39 | pdbonly 40 | true 41 | bin\Release\ 42 | TRACE 43 | prompt 44 | 4 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | True 98 | True 99 | source.extension.vsixmanifest 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | Designer 117 | VsixManifestGenerator 118 | source.extension.cs 119 | 120 | 121 | 122 | PreserveNewest 123 | true 124 | 125 | 126 | PreserveNewest 127 | true 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 3.0.300 142 | 143 | 144 | 2.22.0 145 | 146 | 147 | 6.1.0 148 | 149 | 150 | 4.7.36 151 | 152 | 153 | 17.8.37222 154 | 155 | 156 | 1.0.3 157 | 158 | 159 | 2.20.17 160 | 161 | 162 | 8.0.0 163 | 164 | 165 | 4.3.4 166 | 167 | 168 | 9.0.0 169 | 170 | 171 | 1.7.0 172 | runtime; build; native; contentfiles; analyzers; buildtransitive 173 | all 174 | 175 | 176 | 177 | 178 | 179 | 186 | -------------------------------------------------------------------------------- /src/CsInlineColorViz.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32104.313 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CsInlineColorViz", "CsInlineColorViz.csproj", "{7E6E7299-F72B-4236-B833-B0F2BEB4BFFD}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E89FACCB-B299-4A3E-A5C3-32531AC79B62}" 9 | ProjectSection(SolutionItems) = preProject 10 | .editorconfig = .editorconfig 11 | ..\Directory.Build.props = ..\Directory.Build.props 12 | ..\README.md = ..\README.md 13 | EndProjectSection 14 | EndProject 15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSInlineColorViz.Tests", "CSInlineColorViz.Tests\CSInlineColorViz.Tests.csproj", "{D3A4F760-435E-4399-81C0-8BADC9C725CD}" 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Release|Any CPU = Release|Any CPU 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {7E6E7299-F72B-4236-B833-B0F2BEB4BFFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {7E6E7299-F72B-4236-B833-B0F2BEB4BFFD}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {7E6E7299-F72B-4236-B833-B0F2BEB4BFFD}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {7E6E7299-F72B-4236-B833-B0F2BEB4BFFD}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {D3A4F760-435E-4399-81C0-8BADC9C725CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {D3A4F760-435E-4399-81C0-8BADC9C725CD}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {D3A4F760-435E-4399-81C0-8BADC9C725CD}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {D3A4F760-435E-4399-81C0-8BADC9C725CD}.Release|Any CPU.Build.0 = Release|Any CPU 31 | EndGlobalSection 32 | GlobalSection(SolutionProperties) = preSolution 33 | HideSolutionNode = FALSE 34 | EndGlobalSection 35 | GlobalSection(ExtensibilityGlobals) = postSolution 36 | SolutionGuid = {5BFB0AC9-9783-4BF6-A186-23028D9F1C47} 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /src/CsInlineColorVizPackage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Threading; 5 | using Microsoft.ApplicationInsights; 6 | using Microsoft.ApplicationInsights.Extensibility; 7 | using Microsoft.VisualStudio.Shell; 8 | using Microsoft.VisualStudio.Shell.Interop; 9 | using static Microsoft.VisualStudio.VSConstants; 10 | using Task = System.Threading.Tasks.Task; 11 | 12 | namespace CsInlineColorViz; 13 | 14 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] 15 | [Guid(CsInlineColorVizPackage.PackageGuidString)] 16 | [InstalledProductRegistration(Vsix.Name, Vsix.Description, Vsix.Version)] 17 | [ProvideAutoLoad(UICONTEXT.CSharpProject_string, PackageAutoLoadFlags.BackgroundLoad)] 18 | public sealed class CsInlineColorVizPackage : AsyncPackage 19 | { 20 | public const string PackageGuidString = "df7e1d03-27f6-44ba-875a-c9f732e6ad65"; 21 | 22 | public static AsyncPackage Instance { get; private set; } 23 | 24 | protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) 25 | { 26 | Instance = this; 27 | 28 | await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); 29 | 30 | await OutputPane.Instance.WriteAsync(Vsix.Name); 31 | await OutputPane.Instance.WriteAsync(Vsix.Version); 32 | 33 | await SponsorRequestHelper.CheckIfNeedToShowAsync(); 34 | 35 | await base.InitializeAsync(cancellationToken, progress); 36 | 37 | await TrackBasicUsageAnalyticsAsync(); 38 | } 39 | 40 | private static async Task TrackBasicUsageAnalyticsAsync() 41 | { 42 | try 43 | { 44 | #if !DEBUG 45 | if (string.IsNullOrWhiteSpace(AnalyticsConfig.TelemetryConnectionString)) 46 | { 47 | return; 48 | } 49 | 50 | var config = new TelemetryConfiguration 51 | { 52 | ConnectionString = AnalyticsConfig.TelemetryConnectionString, 53 | }; 54 | 55 | var client = new TelemetryClient(config); 56 | 57 | var properties = new Dictionary 58 | { 59 | { "VsixVersion", Vsix.Version }, 60 | { "VsVersion", Microsoft.VisualStudio.Telemetry.TelemetryService.DefaultSession?.GetSharedProperty("VS.Core.ExeVersion") }, 61 | { "Architecture", RuntimeInformation.ProcessArchitecture.ToString() }, 62 | { "MsInternal", Microsoft.VisualStudio.Telemetry.TelemetryService.DefaultSession?.IsUserMicrosoftInternal.ToString() }, 63 | }; 64 | 65 | client.TrackEvent(Vsix.Name, properties); 66 | #endif 67 | } 68 | catch (Exception exc) 69 | { 70 | System.Diagnostics.Debug.WriteLine(exc); 71 | await OutputPane.Instance.WriteAsync("Error tracking usage analytics: " + exc.Message); 72 | } 73 | } 74 | 75 | internal static async Task EnsureInstanceLoadedAsync() 76 | { 77 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 78 | 79 | if (CsInlineColorVizPackage.Instance == null) 80 | { 81 | // Try and force load the project if it hasn't already loaded 82 | // so can access the configured options. 83 | if (ServiceProvider.GlobalProvider.GetService(typeof(SVsShell)) is IVsShell shell) 84 | { 85 | Guid PackageToBeLoadedGuid = new Guid(CsInlineColorVizPackage.PackageGuidString); 86 | shell.LoadPackage(ref PackageToBeLoadedGuid, out _); 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/FunColorTagger.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text.RegularExpressions; 3 | using System.Windows.Media; 4 | using Microsoft.VisualStudio.Text; 5 | using WpfColorHelper; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | internal sealed class FunColorTagger : RegexTagger, ITestableRegexColorTagger 10 | { 11 | // Note this contains an empty group, so that the output groups match ColorTagger, 12 | // as the adornment expects values in specific groups when making changes with the dialog 13 | internal static Regex regularExpression = new(@"(FunColors.FunColor.|FunColor.)()([a-z,A-Z0-9]{5,})", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 14 | 15 | public Regex ColorExpression => regularExpression; 16 | 17 | internal FunColorTagger(ITextBuffer buffer) 18 | : base(buffer, [regularExpression]) 19 | { 20 | } 21 | 22 | internal override ColorTag TryCreateTagForMatch(Match match, int lineNumber, int lineStart, int spanStart, string lineText) 23 | { 24 | if (lineText.Contains(match.Value) && match.Groups.Count == 4) 25 | { 26 | var value = match.Groups[3].Value; 27 | var precedingChar = match.Index > 0 ? lineText[match.Index - 1] : ' '; 28 | 29 | // Do this check here rather than as part of the RegEx so don't have to adjust the insertion point for the adornment 30 | if (new[] { ' ', ',', '(' }.Contains(precedingChar)) 31 | { 32 | if (ColorHelper.TryGetColor(value, out Color clr)) 33 | { 34 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.FunColors); 35 | } 36 | else 37 | { 38 | System.Diagnostics.Debug.WriteLine($"Failed to understand '{value}' as a valid color name."); 39 | } 40 | } 41 | } 42 | 43 | return null; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/FunColorTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | [Export(typeof(ITaggerProvider))] 10 | [ContentType("CSharp")] 11 | [ContentType("Razor")] 12 | [ContentType("LegacyRazorCSharp")] 13 | [TagType(typeof(ColorTag))] 14 | internal sealed class FunColorTaggerProvider : ITaggerProvider 15 | { 16 | public ITagger CreateTagger(ITextBuffer buffer) 17 | where T : ITag 18 | { 19 | if (buffer == null) 20 | { 21 | throw new ArgumentNullException(nameof(buffer)); 22 | } 23 | 24 | return buffer.Properties.GetOrCreateSingletonProperty(() => new FunColorTagger(buffer)) as ITagger; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/FunColorsDialog.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace CsInlineColorViz; 4 | 5 | class FunColorsDialog : ColorSelectionDialog 6 | { 7 | public FunColorsDialog() : base() 8 | { 9 | var sp = new StackPanel(); 10 | 11 | foreach (var (name, color) in ColorListHelper.FunColors()) 12 | { 13 | sp.Children.Add(CreateButton(name, color)); 14 | } 15 | 16 | var sv = new ScrollViewer { VerticalScrollBarVisibility = ScrollBarVisibility.Auto, Content = sp }; 17 | 18 | this.Content = sv; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/HexIntTagger.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using System.Windows.Media; 3 | using Microsoft.VisualStudio.Text; 4 | using WpfColorHelper; 5 | 6 | namespace CsInlineColorViz; 7 | 8 | internal sealed class HexIntTagger : RegexTagger, ITestableRegexColorTagger 9 | { 10 | internal static Regex regularExpression = new("(0x)([0-9A-F]{3,8})", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 11 | 12 | public Regex ColorExpression => regularExpression; 13 | 14 | internal HexIntTagger(ITextBuffer buffer) 15 | : base(buffer, [regularExpression]) 16 | { 17 | } 18 | 19 | internal override ColorTag TryCreateTagForMatch(Match match, int lineNumber, int lineStart, int spanStart, string lineText) 20 | { 21 | if (lineText.Contains(match.Value) && match.Groups.Count == 3) 22 | { 23 | var value = match.Groups[2].Value; 24 | 25 | if (ColorHelper.TryGetHexColor($"#{value}", out Color clr)) 26 | { 27 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.None); 28 | } 29 | else 30 | { 31 | System.Diagnostics.Debug.WriteLine($"Failed to understand '{value}' as a valid color."); 32 | } 33 | } 34 | 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/HexIntTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | [Export(typeof(ITaggerProvider))] 10 | [ContentType("CSharp")] 11 | [ContentType("Razor")] 12 | [ContentType("LegacyRazorCSharp")] 13 | [TagType(typeof(ColorTag))] 14 | internal sealed class HexIntTaggerProvider : ITaggerProvider 15 | { 16 | public ITagger CreateTagger(ITextBuffer buffer) 17 | where T : ITag 18 | { 19 | if (buffer == null) 20 | { 21 | throw new ArgumentNullException(nameof(buffer)); 22 | } 23 | 24 | return buffer.Properties.GetOrCreateSingletonProperty(() => new HexIntTagger(buffer)) as ITagger; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/HexStringTagger.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using System.Windows.Media; 3 | using Microsoft.VisualStudio.Text; 4 | using WpfColorHelper; 5 | 6 | namespace CsInlineColorViz; 7 | 8 | internal sealed class HexStringTagger : RegexTagger, ITestableRegexColorTagger 9 | { 10 | internal static Regex regularExpression = new("(\"#)([0-9A-F]{3,8})(\")", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 11 | 12 | public Regex ColorExpression => regularExpression; 13 | 14 | internal HexStringTagger(ITextBuffer buffer) 15 | : base(buffer, [regularExpression]) 16 | { 17 | } 18 | 19 | internal override ColorTag TryCreateTagForMatch(Match match, int lineNumber, int lineStart, int spanStart, string lineText) 20 | { 21 | if (lineText.Contains(match.Value) && match.Groups.Count == 4) 22 | { 23 | var value = match.Groups[2].Value; 24 | 25 | if (ColorHelper.TryGetHexColor($"#{value}", out Color clr)) 26 | { 27 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.None); 28 | } 29 | else 30 | { 31 | System.Diagnostics.Debug.WriteLine($"Failed to understand '{value}' as a valid color."); 32 | } 33 | } 34 | 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/HexStringTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | [Export(typeof(ITaggerProvider))] 10 | [ContentType("CSharp")] 11 | [ContentType("Razor")] 12 | [ContentType("LegacyRazorCSharp")] 13 | [TagType(typeof(ColorTag))] 14 | internal sealed class HexStringTaggerProvider : ITaggerProvider 15 | { 16 | public ITagger CreateTagger(ITextBuffer buffer) 17 | where T : ITag 18 | { 19 | if (buffer == null) 20 | { 21 | throw new ArgumentNullException(nameof(buffer)); 22 | } 23 | 24 | return buffer.Properties.GetOrCreateSingletonProperty(() => new HexStringTagger(buffer)) as ITagger; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/ITestableRegexColorTagger.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | namespace CsInlineColorViz; 4 | 5 | public interface ITestableRegexColorTagger 6 | { 7 | public Regex ColorExpression { get; } 8 | } -------------------------------------------------------------------------------- /src/InternalSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CsInlineColorViz; 4 | 5 | /// 6 | /// These are internal settings that are not exposed to the user. 7 | /// Reusing the capabilities of VS Options to store values. 8 | /// 9 | internal class InternalSettings : BaseOptionModel 10 | { 11 | /// 12 | /// The first time we know the extension was used. 13 | /// 14 | public DateTime FirstUse { get; set; } = DateTime.MinValue; 15 | 16 | /// 17 | /// How many different document instances 18 | /// 19 | public int UseCount { get; set; } = 0; 20 | } 21 | -------------------------------------------------------------------------------- /src/IntraTextAdornmentTagger.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Windows; 6 | using Microsoft.VisualStudio.Shell; 7 | using Microsoft.VisualStudio.Text; 8 | using Microsoft.VisualStudio.Text.Editor; 9 | using Microsoft.VisualStudio.Text.Tagging; 10 | 11 | namespace CsInlineColorViz; 12 | 13 | /// 14 | /// Helper class for interspersing adornments into text. 15 | /// 16 | /// 17 | /// To avoid an issue around intra-text adornment support and its interaction with text buffer changes, 18 | /// this tagger reacts to text and link tag changes with a delay. It waits to send out its own TagsChanged 19 | /// event until the WPF Dispatcher is running again and it takes care to report adornments 20 | /// that are consistent with the latest sent TagsChanged event by storing that particular snapshot 21 | /// and using it to query for the data tags. 22 | /// 23 | /// The tag containing the data. 24 | /// The type of the adornment. 25 | internal abstract class IntraTextAdornmentTagger 26 | : ITagger 27 | where TAdornment : UIElement 28 | { 29 | protected readonly IWpfTextView view; 30 | 31 | private readonly List invalidatedSpans = new List(); 32 | 33 | private Dictionary adornmentCache = new Dictionary(); 34 | 35 | protected IntraTextAdornmentTagger(IWpfTextView view) 36 | { 37 | this.view = view; 38 | this.Snapshot = view.TextBuffer.CurrentSnapshot; 39 | 40 | this.view.LayoutChanged += this.HandleLayoutChanged; 41 | this.view.TextBuffer.Changed += this.HandleBufferChanged; 42 | 43 | this.view.Closed += (s, e) => 44 | { 45 | this.view.LayoutChanged -= this.HandleLayoutChanged; 46 | if (this.view.TextBuffer != null) 47 | { 48 | this.view.TextBuffer.Changed -= this.HandleBufferChanged; 49 | } 50 | }; 51 | } 52 | 53 | public event EventHandler TagsChanged; 54 | 55 | protected ITextSnapshot Snapshot { get; private set; } 56 | 57 | // Produces tags on the snapshot that the tag consumer asked for. 58 | public virtual IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) 59 | { 60 | if (spans == null || spans.Count == 0) 61 | { 62 | yield break; 63 | } 64 | 65 | // Translate the request to the snapshot that this tagger is current with. 66 | ITextSnapshot requestedSnapshot = spans[0].Snapshot; 67 | 68 | var translatedSpans = new NormalizedSnapshotSpanCollection(spans.Select(span => span.TranslateTo(this.Snapshot, SpanTrackingMode.EdgeExclusive))); 69 | 70 | // Grab the adornments. 71 | foreach (var tagSpan in this.GetAdornmentTagsOnSnapshot(translatedSpans)) 72 | { 73 | // Translate each adornment to the snapshot that the tagger was asked about. 74 | SnapshotSpan span = tagSpan.Span.TranslateTo(requestedSnapshot, SpanTrackingMode.EdgeExclusive); 75 | 76 | IntraTextAdornmentTag tag = new IntraTextAdornmentTag(tagSpan.Tag.Adornment, tagSpan.Tag.RemovalCallback, tagSpan.Tag.Affinity); 77 | yield return new TagSpan(span, tag); 78 | } 79 | 80 | yield break; 81 | } 82 | 83 | protected abstract TAdornment CreateAdornment(TData data, SnapshotSpan span); 84 | 85 | // Return True if the adornment was updated and should be kept. False to have the adornment removed from the view. 86 | protected abstract bool UpdateAdornment(TAdornment adornment, TData data); 87 | 88 | /// Get the adornments within the span collection 89 | /// Spans to provide adornment data for. These spans do not necessarily correspond to text lines. 90 | /// 91 | /// If adornments need to be updated, call or . 92 | /// This will, indirectly, cause to be called. 93 | /// 94 | /// 95 | /// A sequence of: 96 | /// * adornment data for each adornment to be displayed 97 | /// * the span of text that should be elided for that adornment (zero length spans are acceptable) 98 | /// * and affinity of the adornment (this should be null if and only if the elided span has a length greater than zero). 99 | /// 100 | protected abstract IEnumerable> GetAdornmentData(NormalizedSnapshotSpanCollection spans); 101 | 102 | /// 103 | /// Causes intra-text adornments to be updated asynchronously. 104 | /// 105 | protected void InvalidateSpans(IList spans) 106 | { 107 | lock (this.invalidatedSpans) 108 | { 109 | bool wasEmpty = this.invalidatedSpans.Count == 0; 110 | this.invalidatedSpans.AddRange(spans); 111 | 112 | if (wasEmpty && this.invalidatedSpans.Count > 0) 113 | { 114 | #pragma warning disable VSTHRD102 // Implement internal logic asynchronously 115 | ThreadHelper.JoinableTaskFactory.Run(async () => 116 | { 117 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 118 | this.AsyncUpdate(); 119 | }); 120 | #pragma warning restore VSTHRD102 // Implement internal logic asynchronously 121 | } 122 | } 123 | } 124 | 125 | /// 126 | /// Causes intra-text adornments to be updated synchronously. 127 | /// 128 | protected void RaiseTagsChanged(SnapshotSpan span) 129 | { 130 | this.TagsChanged?.Invoke(this, new SnapshotSpanEventArgs(span)); 131 | } 132 | 133 | private void HandleBufferChanged(object sender, TextContentChangedEventArgs args) 134 | { 135 | var editedSpans = args.Changes.Select(change => new SnapshotSpan(args.After, change.NewSpan)).ToList(); 136 | this.InvalidateSpans(editedSpans); 137 | } 138 | 139 | private void AsyncUpdate() 140 | { 141 | // Store the snapshot that we're now current with and send an event 142 | // for the text that has changed. 143 | if (this.Snapshot != this.view.TextBuffer.CurrentSnapshot) 144 | { 145 | this.Snapshot = this.view.TextBuffer.CurrentSnapshot; 146 | 147 | Dictionary translatedAdornmentCache = new Dictionary(); 148 | 149 | foreach (var keyValuePair in this.adornmentCache) 150 | { 151 | var adjustedKey = keyValuePair.Key.TranslateTo(this.Snapshot, SpanTrackingMode.EdgeExclusive); 152 | 153 | if (!translatedAdornmentCache.ContainsKey(adjustedKey)) 154 | { 155 | translatedAdornmentCache.Add(adjustedKey, keyValuePair.Value); 156 | } 157 | } 158 | 159 | this.adornmentCache = translatedAdornmentCache; 160 | } 161 | 162 | List translatedSpans; 163 | lock (this.invalidatedSpans) 164 | { 165 | translatedSpans = this.invalidatedSpans.Select(s => s.TranslateTo(this.Snapshot, SpanTrackingMode.EdgeInclusive)).ToList(); 166 | this.invalidatedSpans.Clear(); 167 | } 168 | 169 | if (translatedSpans.Count == 0) 170 | { 171 | return; 172 | } 173 | 174 | var start = translatedSpans.Select(span => span.Start).Min(); 175 | var end = translatedSpans.Select(span => span.End).Max(); 176 | 177 | this.RaiseTagsChanged(new SnapshotSpan(start, end)); 178 | } 179 | 180 | private void HandleLayoutChanged(object sender, TextViewLayoutChangedEventArgs e) 181 | { 182 | SnapshotSpan visibleSpan = this.view.TextViewLines.FormattedSpan; 183 | 184 | // Filter out the adornments that are no longer visible. 185 | List toRemove = new List( 186 | from keyValuePair 187 | in this.adornmentCache 188 | where !keyValuePair.Key.TranslateTo(visibleSpan.Snapshot, SpanTrackingMode.EdgeExclusive).IntersectsWith(visibleSpan) 189 | select keyValuePair.Key); 190 | 191 | foreach (var span in toRemove) 192 | { 193 | this.adornmentCache.Remove(span); 194 | } 195 | } 196 | 197 | // Produces tags on the snapshot that this tagger is current with. 198 | private IEnumerable> GetAdornmentTagsOnSnapshot(NormalizedSnapshotSpanCollection spans) 199 | { 200 | if (spans.Count == 0) 201 | { 202 | yield break; 203 | } 204 | 205 | ITextSnapshot snapshot = spans[0].Snapshot; 206 | 207 | System.Diagnostics.Debug.Assert(snapshot == this.Snapshot, "Snapshots out of sync."); 208 | 209 | // Since WPF UI objects have state (like mouse hover or animation) and are relatively expensive to create and lay out, 210 | // this code tries to reuse controls as much as possible. 211 | // The controls are stored in this.adornmentCache between the calls. 212 | 213 | // Mark which adornments fall inside the requested spans with Keep=false 214 | // so that they can be removed from the cache if they no longer correspond to data tags. 215 | HashSet toRemove = new HashSet(); 216 | foreach (var ar in this.adornmentCache) 217 | { 218 | if (spans.IntersectsWith(new NormalizedSnapshotSpanCollection(ar.Key))) 219 | { 220 | toRemove.Add(ar.Key); 221 | } 222 | } 223 | 224 | List ensureVisible = new List(); 225 | 226 | foreach (var spanDataPair in this.GetAdornmentData(spans).Distinct(new Comparer())) 227 | { 228 | // Look up the corresponding adornment or create one if it's new. 229 | SnapshotSpan adornmentLocation = spanDataPair.Item1; 230 | PositionAffinity? affinity = spanDataPair.Item2; 231 | TData adornmentData = spanDataPair.Item3; 232 | 233 | if (this.adornmentCache.TryGetValue(adornmentLocation, out TAdornment adornment)) 234 | { 235 | if (this.UpdateAdornment(adornment, adornmentData)) 236 | { 237 | toRemove.Remove(adornmentLocation); 238 | } 239 | } 240 | else 241 | { 242 | adornment = this.CreateAdornment(adornmentData, adornmentLocation); 243 | 244 | if (adornment == null) 245 | { 246 | continue; 247 | } 248 | 249 | // Get the adornment to measure itself. Its DesiredSize property is used to determine 250 | // how much space to leave between text for this adornment. 251 | // Note: If the size of the adornment changes, the line will be reformatted to accommodate it. 252 | // Note: Some adornments may change size when added to the view's visual tree due to inherited 253 | // dependency properties that affect layout. Such options can include SnapsToDevicePixels, 254 | // UseLayoutRounding, TextRenderingMode, TextHintingMode, and TextFormattingMode. Making sure 255 | // that these properties on the adornment match the view's values before calling Measure here 256 | // can help avoid the size change and the resulting unnecessary re-format. 257 | adornment.Measure(new System.Windows.Size(double.PositiveInfinity, double.PositiveInfinity)); 258 | 259 | this.adornmentCache.Add(adornmentLocation, adornment); 260 | 261 | // If the adornment is being added outside the changed area (span), keep a record of it's location 262 | // We need this to ensure it is shown. 263 | // It won't be made visible by default as it's not in a place the editor will automatically check for visual updates 264 | if (!spans.IntersectsWith(new NormalizedSnapshotSpanCollection(adornmentLocation))) 265 | { 266 | ensureVisible.Add(adornmentLocation); 267 | } 268 | } 269 | 270 | yield return new TagSpan(adornmentLocation, new IntraTextAdornmentTag(adornment, null, affinity)); 271 | } 272 | 273 | foreach (var snapshotSpan in toRemove) 274 | { 275 | this.adornmentCache.Remove(snapshotSpan); 276 | } 277 | 278 | // Ensure any adornments that were added outside the visible area are made visible 279 | foreach (var newAdornment in ensureVisible) 280 | { 281 | InvalidateSpans(new[] { newAdornment }); 282 | } 283 | 284 | // Ensure anything removed is reflected in the UI 285 | foreach (var knownAdornment in adornmentCache) 286 | { 287 | // Does the visible adornment have a corresponding data tag? 288 | if (!GetAdornmentData(new NormalizedSnapshotSpanCollection(knownAdornment.Key)).Any()) 289 | { 290 | // If not, force the UI to refresh the location of the displayed adornment (to remove it) 291 | InvalidateSpans(new[] { knownAdornment.Key }); 292 | } 293 | } 294 | 295 | yield break; 296 | } 297 | 298 | private class Comparer : IEqualityComparer> 299 | { 300 | public bool Equals(Tuple x, Tuple y) 301 | { 302 | if (x == null && y == null) 303 | { 304 | return true; 305 | } 306 | 307 | if (x == null || y == null) 308 | { 309 | return false; 310 | } 311 | 312 | return x.Item1.Equals(y.Item1); 313 | } 314 | 315 | public int GetHashCode(Tuple obj) 316 | { 317 | return obj.Item1.GetHashCode(); 318 | } 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /src/KnownColorDialog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Windows.Controls; 4 | using WpfColorHelper; 5 | 6 | namespace CsInlineColorViz; 7 | 8 | class KnownColorDialog : ColorSelectionDialog 9 | { 10 | public KnownColorDialog() : base() 11 | { 12 | var sp = new StackPanel(); 13 | 14 | // TODO: consider separating "colors" and interpreted system values 15 | foreach (var colorName in Enum.GetValues(typeof(System.Drawing.KnownColor)).Cast().ToList().OrderBy(o => o.ToString()).Select(o => o.ToString())) 16 | { 17 | if (ColorHelper.TryGetColor(colorName, out System.Windows.Media.Color clr)) 18 | { 19 | sp.Children.Add(CreateButton(colorName, clr)); 20 | } 21 | } 22 | 23 | var sv = new ScrollViewer { VerticalScrollBarVisibility = ScrollBarVisibility.Auto, Content = sp }; 24 | 25 | this.Content = sv; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021-2024 Matt Lacey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/MauiProjTagger.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using System.Windows.Media; 3 | using Microsoft.VisualStudio.Text; 4 | using WpfColorHelper; 5 | 6 | namespace CsInlineColorViz; 7 | 8 | internal sealed class MauiProjTagger : RegexTagger, ITestableRegexColorTagger 9 | { 10 | internal static Regex regularExpression = new("( Color=\"#)([0-9A-F]{3,8})(\")", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 11 | 12 | public Regex ColorExpression => regularExpression; 13 | 14 | internal MauiProjTagger(ITextBuffer buffer) 15 | : base(buffer, [regularExpression]) 16 | { 17 | } 18 | 19 | internal override ColorTag TryCreateTagForMatch(Match match, int lineNumber, int lineStart, int spanStart, string lineText) 20 | { 21 | if (lineText.Contains(match.Value) && match.Groups.Count == 4) 22 | { 23 | var value = match.Groups[2].Value; 24 | 25 | if (ColorHelper.TryGetHexColor($"#{value}", out Color clr)) 26 | { 27 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.None); 28 | } 29 | else 30 | { 31 | System.Diagnostics.Debug.WriteLine($"Failed to understand '{value}' as a valid color."); 32 | } 33 | } 34 | 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/MauiProjTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | // This works for any XML file but the intended application is within .csproj files (for .NET MAUI apps) 10 | [Export(typeof(ITaggerProvider))] 11 | [ContentType("XML")] 12 | [TagType(typeof(ColorTag))] 13 | internal sealed class MauiProjTaggerProvider : ITaggerProvider 14 | { 15 | public ITagger CreateTagger(ITextBuffer buffer) 16 | where T : ITag 17 | { 18 | if (buffer == null) 19 | { 20 | throw new ArgumentNullException(nameof(buffer)); 21 | } 22 | 23 | return buffer.Properties.GetOrCreateSingletonProperty(() => new MauiProjTagger(buffer)) as ITagger; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/NamedColorDialog.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | using WpfColorHelper; 3 | 4 | namespace CsInlineColorViz; 5 | 6 | class NamedColorDialog : ColorSelectionDialog 7 | { 8 | public NamedColorDialog() : base() 9 | { 10 | var tabs = new TabControl(); 11 | 12 | var tab1 = new TabItem { Header = " A-Z " }; 13 | 14 | var sp = new StackPanel(); 15 | 16 | foreach (var color in ColorListHelper.SystemDrawingColorsAlphabetical()) 17 | { 18 | if (ColorHelper.TryGetColor(color.Name, out System.Windows.Media.Color clr)) 19 | { 20 | sp.Children.Add(CreateButton(color.Name, clr)); 21 | } 22 | } 23 | 24 | var sv = new ScrollViewer { VerticalScrollBarVisibility = ScrollBarVisibility.Auto, Content = sp }; 25 | tab1.Content = sv; 26 | 27 | var tab2 = new TabItem { Header = " 🌈 " }; 28 | 29 | var sp2 = new StackPanel(); 30 | 31 | foreach (var color in ColorListHelper.SystemDrawingColorsSpectrum()) 32 | { 33 | if (ColorHelper.TryGetColor(color.Name, out System.Windows.Media.Color clr)) 34 | { 35 | sp2.Children.Add(CreateButton(color.Name, clr)); 36 | } 37 | } 38 | 39 | var sv2 = new ScrollViewer { VerticalScrollBarVisibility = ScrollBarVisibility.Auto, Content = sp2 }; 40 | tab2.Content = sv2; 41 | 42 | tabs.Items.Add(tab1); 43 | tabs.Items.Add(tab2); 44 | 45 | this.Content = tabs; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/OutlineTextControl.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Windows; 3 | using System.Windows.Media; 4 | 5 | namespace CsInlineColorViz; 6 | 7 | /// 8 | /// OutlineText custom control class derives layout, event, data binding, and rendering from derived FrameworkElement class. 9 | /// 10 | public class OutlineTextControl : FrameworkElement 11 | { 12 | private Geometry _textGeometry; 13 | private Geometry _textHighLightGeometry; 14 | 15 | /// 16 | /// Invoked when a dependency property has changed. Generate a new FormattedText object to display. 17 | /// 18 | /// OutlineText object whose property was updated. 19 | /// Event arguments for the dependency property. 20 | private static void OnOutlineTextInvalidated(DependencyObject d, DependencyPropertyChangedEventArgs e) 21 | { 22 | ((OutlineTextControl)d).CreateText(); 23 | } 24 | 25 | // 26 | /// 27 | /// OnRender override draws the geometry of the text and optional highlight. 28 | /// 29 | /// Drawing context of the OutlineText control. 30 | protected override void OnRender(DrawingContext drawingContext) 31 | { 32 | // Draw the outline based on the properties that are set. 33 | drawingContext.DrawGeometry(Fill, new System.Windows.Media.Pen(Stroke, StrokeThickness), _textGeometry); 34 | 35 | // Draw the text highlight based on the properties that are set. 36 | if (Highlight == true) 37 | { 38 | drawingContext.DrawGeometry(null, new System.Windows.Media.Pen(Stroke, StrokeThickness), _textHighLightGeometry); 39 | } 40 | } 41 | 42 | // 43 | // 44 | /// 45 | /// Create the outline geometry based on the formatted text. 46 | /// 47 | public void CreateText() 48 | { 49 | System.Windows.FontStyle fontStyle = FontStyles.Normal; 50 | FontWeight fontWeight = FontWeights.Medium; 51 | 52 | if (Bold == true) fontWeight = FontWeights.Bold; 53 | if (Italic == true) fontStyle = FontStyles.Italic; 54 | 55 | // Create the formatted text based on the properties set. 56 | FormattedText formattedText = new FormattedText( 57 | Text, 58 | CultureInfo.GetCultureInfo("en-us"), 59 | FlowDirection.LeftToRight, 60 | new Typeface( 61 | Font, 62 | fontStyle, 63 | fontWeight, 64 | FontStretches.Normal), 65 | FontSize, 66 | System.Windows.Media.Brushes.Black // This brush does not matter since we use the geometry of the text. 67 | ); 68 | 69 | // Build the geometry object that represents the text. 70 | _textGeometry = formattedText.BuildGeometry(new System.Windows.Point(0, 0)); 71 | 72 | // Build the geometry object that represents the text highlight. 73 | if (Highlight == true) 74 | { 75 | _textHighLightGeometry = formattedText.BuildHighlightGeometry(new System.Windows.Point(0, 0)); 76 | } 77 | } 78 | // 79 | 80 | /// 81 | /// Specifies whether the font should display Bold font weight. 82 | /// 83 | public bool Bold 84 | { 85 | get 86 | { 87 | return (bool)GetValue(BoldProperty); 88 | } 89 | 90 | set 91 | { 92 | SetValue(BoldProperty, value); 93 | } 94 | } 95 | 96 | /// 97 | /// Identifies the Bold dependency property. 98 | /// 99 | public static readonly DependencyProperty BoldProperty = DependencyProperty.Register( 100 | "Bold", 101 | typeof(bool), 102 | typeof(OutlineTextControl), 103 | new FrameworkPropertyMetadata( 104 | false, 105 | FrameworkPropertyMetadataOptions.AffectsRender, 106 | new PropertyChangedCallback(OnOutlineTextInvalidated), 107 | null 108 | ) 109 | ); 110 | 111 | /// 112 | /// Specifies the brush to use for the fill of the formatted text. 113 | /// 114 | public System.Windows.Media.Brush Fill 115 | { 116 | get 117 | { 118 | return (System.Windows.Media.Brush)GetValue(FillProperty); 119 | } 120 | 121 | set 122 | { 123 | SetValue(FillProperty, value); 124 | } 125 | } 126 | 127 | /// 128 | /// Identifies the Fill dependency property. 129 | /// 130 | public static readonly DependencyProperty FillProperty = DependencyProperty.Register( 131 | "Fill", 132 | typeof(System.Windows.Media.Brush), 133 | typeof(OutlineTextControl), 134 | new FrameworkPropertyMetadata( 135 | new SolidColorBrush(Colors.LightSteelBlue), 136 | FrameworkPropertyMetadataOptions.AffectsRender, 137 | new PropertyChangedCallback(OnOutlineTextInvalidated), 138 | null 139 | ) 140 | ); 141 | 142 | /// 143 | /// The font to use for the displayed formatted text. 144 | /// 145 | public System.Windows.Media.FontFamily Font 146 | { 147 | get 148 | { 149 | return (System.Windows.Media.FontFamily)GetValue(FontProperty); 150 | } 151 | 152 | set 153 | { 154 | SetValue(FontProperty, value); 155 | } 156 | } 157 | 158 | /// 159 | /// Identifies the Font dependency property. 160 | /// 161 | public static readonly DependencyProperty FontProperty = DependencyProperty.Register( 162 | "Font", 163 | typeof(System.Windows.Media.FontFamily), 164 | typeof(OutlineTextControl), 165 | new FrameworkPropertyMetadata( 166 | new System.Windows.Media.FontFamily("Arial"), 167 | FrameworkPropertyMetadataOptions.AffectsRender, 168 | new PropertyChangedCallback(OnOutlineTextInvalidated), 169 | null 170 | ) 171 | ); 172 | 173 | /// 174 | /// The current font size. 175 | /// 176 | public double FontSize 177 | { 178 | get 179 | { 180 | return (double)GetValue(FontSizeProperty); 181 | } 182 | 183 | set 184 | { 185 | SetValue(FontSizeProperty, value); 186 | } 187 | } 188 | 189 | /// 190 | /// Identifies the FontSize dependency property. 191 | /// 192 | public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register( 193 | "FontSize", 194 | typeof(double), 195 | typeof(OutlineTextControl), 196 | new FrameworkPropertyMetadata( 197 | (double)48.0, 198 | FrameworkPropertyMetadataOptions.AffectsRender, 199 | new PropertyChangedCallback(OnOutlineTextInvalidated), 200 | null 201 | ) 202 | ); 203 | 204 | /// 205 | /// Specifies whether to show the text highlight. 206 | /// 207 | public bool Highlight 208 | { 209 | get 210 | { 211 | return (bool)GetValue(HighlightProperty); 212 | } 213 | 214 | set 215 | { 216 | SetValue(HighlightProperty, value); 217 | } 218 | } 219 | 220 | /// 221 | /// Identifies the Highlight dependency property. 222 | /// 223 | public static readonly DependencyProperty HighlightProperty = DependencyProperty.Register( 224 | "Highlight", 225 | typeof(bool), 226 | typeof(OutlineTextControl), 227 | new FrameworkPropertyMetadata( 228 | false, 229 | FrameworkPropertyMetadataOptions.AffectsRender, 230 | new PropertyChangedCallback(OnOutlineTextInvalidated), 231 | null 232 | ) 233 | ); 234 | 235 | /// 236 | /// Specifies whether the font should display Italic font style. 237 | /// 238 | public bool Italic 239 | { 240 | get 241 | { 242 | return (bool)GetValue(ItalicProperty); 243 | } 244 | 245 | set 246 | { 247 | SetValue(ItalicProperty, value); 248 | } 249 | } 250 | 251 | /// 252 | /// Identifies the Italic dependency property. 253 | /// 254 | public static readonly DependencyProperty ItalicProperty = DependencyProperty.Register( 255 | "Italic", 256 | typeof(bool), 257 | typeof(OutlineTextControl), 258 | new FrameworkPropertyMetadata( 259 | false, 260 | FrameworkPropertyMetadataOptions.AffectsRender, 261 | new PropertyChangedCallback(OnOutlineTextInvalidated), 262 | null 263 | ) 264 | ); 265 | 266 | /// 267 | /// Specifies the brush to use for the stroke and optional highlight of the formatted text. 268 | /// 269 | public System.Windows.Media.Brush Stroke 270 | { 271 | get 272 | { 273 | return (System.Windows.Media.Brush)GetValue(StrokeProperty); 274 | } 275 | 276 | set 277 | { 278 | SetValue(StrokeProperty, value); 279 | } 280 | } 281 | 282 | /// 283 | /// Identifies the Stroke dependency property. 284 | /// 285 | public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register( 286 | "Stroke", 287 | typeof(System.Windows.Media.Brush), 288 | typeof(OutlineTextControl), 289 | new FrameworkPropertyMetadata( 290 | new SolidColorBrush(Colors.Teal), 291 | FrameworkPropertyMetadataOptions.AffectsRender, 292 | new PropertyChangedCallback(OnOutlineTextInvalidated), 293 | null 294 | ) 295 | ); 296 | 297 | /// 298 | /// The stroke thickness of the font. 299 | /// 300 | public ushort StrokeThickness 301 | { 302 | get 303 | { 304 | return (ushort)GetValue(StrokeThicknessProperty); 305 | } 306 | 307 | set 308 | { 309 | SetValue(StrokeThicknessProperty, value); 310 | } 311 | } 312 | 313 | /// 314 | /// Identifies the StrokeThickness dependency property. 315 | /// 316 | public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register( 317 | "StrokeThickness", 318 | typeof(ushort), 319 | typeof(OutlineTextControl), 320 | new FrameworkPropertyMetadata( 321 | (ushort)0, 322 | FrameworkPropertyMetadataOptions.AffectsRender, 323 | new PropertyChangedCallback(OnOutlineTextInvalidated), 324 | null 325 | ) 326 | ); 327 | 328 | /// 329 | /// Specifies the text string to display. 330 | /// 331 | public string Text 332 | { 333 | get 334 | { 335 | return (string)GetValue(TextProperty); 336 | } 337 | 338 | set 339 | { 340 | SetValue(TextProperty, value); 341 | } 342 | } 343 | 344 | /// 345 | /// Identifies the Text dependency property. 346 | /// 347 | public static readonly DependencyProperty TextProperty = DependencyProperty.Register( 348 | "Text", 349 | typeof(string), 350 | typeof(OutlineTextControl), 351 | new FrameworkPropertyMetadata( 352 | "", 353 | FrameworkPropertyMetadataOptions.AffectsRender, 354 | new PropertyChangedCallback(OnOutlineTextInvalidated), 355 | null 356 | ) 357 | ); 358 | } 359 | -------------------------------------------------------------------------------- /src/OutputPane.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Microsoft.VisualStudio; 4 | using Microsoft.VisualStudio.Shell; 5 | using Microsoft.VisualStudio.Shell.Interop; 6 | using Task = System.Threading.Tasks.Task; 7 | 8 | namespace CsInlineColorViz; 9 | 10 | public class OutputPane 11 | { 12 | private static Guid csicvPaneGuid = new Guid("D5024E0F-63F2-431D-A68B-924CE419B8D3"); 13 | 14 | private static OutputPane instance; 15 | 16 | private readonly IVsOutputWindowPane pane; 17 | 18 | private OutputPane() 19 | { 20 | ThreadHelper.ThrowIfNotOnUIThread(); 21 | 22 | if (ServiceProvider.GlobalProvider.GetService(typeof(SVsOutputWindow)) is IVsOutputWindow outWindow 23 | && (ErrorHandler.Failed(outWindow.GetPane(ref csicvPaneGuid, out pane)) || pane == null)) 24 | { 25 | if (ErrorHandler.Failed(outWindow.CreatePane(ref csicvPaneGuid, Vsix.Name, 1, 0))) 26 | { 27 | System.Diagnostics.Debug.WriteLine("Failed to create the Output window pane."); 28 | return; 29 | } 30 | 31 | if (ErrorHandler.Failed(outWindow.GetPane(ref csicvPaneGuid, out pane)) || (pane == null)) 32 | { 33 | System.Diagnostics.Debug.WriteLine("Failed to get access to the Output window pane."); 34 | } 35 | } 36 | } 37 | 38 | public static OutputPane Instance => instance ??= new OutputPane(); 39 | 40 | public async Task ActivateAsync() 41 | { 42 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(CancellationToken.None); 43 | 44 | pane?.Activate(); 45 | } 46 | 47 | public async Task WriteAsync(string message) 48 | { 49 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(CancellationToken.None); 50 | 51 | _ = (pane?.OutputStringThreadSafe($"{message}{Environment.NewLine}")); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/PopupType.cs: -------------------------------------------------------------------------------- 1 | namespace CsInlineColorViz; 2 | 3 | public enum PopupType 4 | { 5 | None, 6 | NamedColors, 7 | ConsoleColors, 8 | KnownColors, 9 | SystemColors, 10 | FunColors, 11 | UnityColors, 12 | // RGB 13 | // RGBA? 14 | // HSLA? 15 | // HSV? 16 | // HSVA? 17 | } 18 | -------------------------------------------------------------------------------- /src/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | using CsInlineColorViz; 5 | 6 | [assembly: AssemblyTitle(Vsix.Name)] 7 | [assembly: AssemblyDescription(Vsix.Description)] 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany(Vsix.Author)] 10 | [assembly: AssemblyProduct(Vsix.Name)] 11 | [assembly: AssemblyCopyright(Vsix.Author)] 12 | [assembly: AssemblyTrademark("")] 13 | [assembly: AssemblyCulture("")] 14 | 15 | [assembly: ComVisible(false)] 16 | 17 | [assembly: AssemblyVersion(Vsix.Version)] 18 | [assembly: AssemblyFileVersion(Vsix.Version)] 19 | 20 | [assembly: InternalsVisibleTo("CsInlineColorViz.Tests")] 21 | -------------------------------------------------------------------------------- /src/RegexTagger.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text.RegularExpressions; 6 | using Microsoft.VisualStudio.Text; 7 | using Microsoft.VisualStudio.Text.Tagging; 8 | 9 | namespace CsInlineColorViz; 10 | 11 | /// 12 | /// Helper base class for writing simple taggers based on regular expressions. 13 | /// 14 | /// 15 | /// Regular expressions are expected to be single-line. 16 | /// 17 | /// The type of tags that will be produced by this tagger. 18 | public abstract class RegexTagger : ITagger 19 | where T : ITag 20 | { 21 | private readonly IEnumerable matchExpressions; 22 | 23 | public RegexTagger(ITextBuffer buffer, IEnumerable matchExpressions) 24 | { 25 | if (matchExpressions.Any(re => (re.Options & RegexOptions.Multiline) == RegexOptions.Multiline)) 26 | { 27 | throw new ArgumentException("Multiline regular expressions are not supported."); 28 | } 29 | 30 | this.matchExpressions = matchExpressions; 31 | 32 | buffer.Changed += (sender, args) => this.HandleBufferChanged(args); 33 | } 34 | 35 | public event EventHandler TagsChanged; 36 | 37 | public virtual IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) 38 | { 39 | string snapshotText = null; 40 | 41 | // Here we grab whole lines so that matches that only partially fall inside the spans argument are detected. 42 | // Note that the spans argument can contain spans that are sub-spans of lines or intersect multiple lines. 43 | foreach (var (line, spanStart) in this.GetIntersectingLines(spans)) 44 | { 45 | string lineText = line.GetText(); 46 | 47 | foreach (var regex in this.matchExpressions) 48 | { 49 | foreach (var match in regex.Matches(lineText).Cast()) 50 | { 51 | snapshotText ??= spans[0].Snapshot.GetText(); 52 | 53 | T tag = this.TryCreateTagForMatch(match, line.LineNumber, line.Start.Position, spanStart, lineText); 54 | if (tag != null) 55 | { 56 | var afterEqualsPos = match.Value.IndexOf('=') + 1; 57 | var span = new SnapshotSpan(line.Start + match.Index + afterEqualsPos, match.Length); 58 | yield return new TagSpan(span, tag); 59 | } 60 | } 61 | } 62 | } 63 | 64 | yield break; 65 | } 66 | 67 | /// 68 | /// Overridden in the derived implementation to provide a tag for each regular expression match. 69 | /// If the return value is null, this match will be skipped. 70 | /// 71 | /// The match to create a tag for. 72 | /// The tag to return from , if non-null. 73 | internal abstract T TryCreateTagForMatch(Match match, int lineNumber, int lineStart, int spanStart, string lineText); 74 | 75 | /// 76 | /// Handle buffer changes. The default implementation expands changes to full lines and sends out 77 | /// a event for these lines. 78 | /// 79 | /// The buffer change arguments. 80 | protected virtual void HandleBufferChanged(TextContentChangedEventArgs args) 81 | { 82 | if (args.Changes.Count == 0) 83 | { 84 | return; 85 | } 86 | 87 | var temp = this.TagsChanged; 88 | if (temp == null) 89 | { 90 | return; 91 | } 92 | 93 | // Combine all changes into a single span so that 94 | // the ITagger<>.TagsChanged event can be raised just once for a compound edit 95 | // with many parts. 96 | ITextSnapshot snapshot = args.After; 97 | 98 | int start = args.Changes[0].NewPosition; 99 | int end = args.Changes[args.Changes.Count - 1].NewEnd; 100 | 101 | var totalAffectedSpan = new SnapshotSpan( 102 | snapshot.GetLineFromPosition(start).Start, 103 | snapshot.GetLineFromPosition(end).End); 104 | 105 | temp(this, new SnapshotSpanEventArgs(totalAffectedSpan)); 106 | } 107 | 108 | private IEnumerable<(ITextSnapshotLine, int)> GetIntersectingLines(NormalizedSnapshotSpanCollection spans) 109 | { 110 | if (spans.Count == 0) 111 | { 112 | yield break; 113 | } 114 | 115 | int lastVisitedLineNumber = -1; 116 | ITextSnapshot snapshot = spans[0].Snapshot; 117 | 118 | foreach (var span in spans) 119 | { 120 | int firstLine = snapshot.GetLineNumberFromPosition(span.Start); 121 | int lastLine = snapshot.GetLineNumberFromPosition(span.End); 122 | 123 | for (int i = Math.Max(lastVisitedLineNumber, firstLine); i <= lastLine; i++) 124 | { 125 | yield return (snapshot.GetLineFromLineNumber(i), span.Start); 126 | } 127 | 128 | lastVisitedLineNumber = lastLine; 129 | } 130 | 131 | yield break; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/Resources/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mrlacey/CSInlineColorViz/15142ec27948ef6135379acc7594ce77137a810d/src/Resources/Icon.png -------------------------------------------------------------------------------- /src/SponsorDetector.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace CsInlineColorViz 4 | { 5 | public class SponsorDetector 6 | { 7 | // This might be the code you see, but it's not what I compile into the extensions when built ;) 8 | public static async Task IsSponsorAsync() 9 | { 10 | return await Task.FromResult(false); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/SponsorRequestHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Task = System.Threading.Tasks.Task; 3 | 4 | namespace CsInlineColorViz 5 | { 6 | public class SponsorRequestHelper 7 | { 8 | public static async Task CheckIfNeedToShowAsync() 9 | { 10 | var settings = await InternalSettings.GetLiveInstanceAsync(); 11 | if (settings.FirstUse == DateTime.MinValue) 12 | { 13 | // Track first known use so don't prompt too soon 14 | settings.FirstUse = DateTime.UtcNow; 15 | await settings.SaveAsync(); 16 | } 17 | 18 | if (await SponsorDetector.IsSponsorAsync()) 19 | { 20 | if (new Random().Next(1, 10) == 2) 21 | { 22 | await ShowThanksForSponsorshipMessageAsync(); 23 | } 24 | } 25 | else 26 | { 27 | // Always create the OutputPane, even if don't activate it. 28 | await ShowPromptForSponsorshipAsync(); 29 | 30 | // Allow some time and usage so not prompting before had a chance to get value 31 | // At least 7 days since first use and loading adorners on at least 100 different docs 32 | if (settings.FirstUse < DateTime.UtcNow.AddDays(7) 33 | && settings.UseCount > 100) 34 | { 35 | await OutputPane.Instance.ActivateAsync(); 36 | } 37 | } 38 | } 39 | 40 | private static async Task ShowThanksForSponsorshipMessageAsync() 41 | { 42 | await OutputPane.Instance.WriteAsync("Thank you for your sponsorship. It really helps."); 43 | await OutputPane.Instance.WriteAsync("If you have ideas for new features or suggestions for new features"); 44 | await OutputPane.Instance.WriteAsync("please raise an issue at https://github.com/mrlacey/CSInlineColorViz/issues"); 45 | await OutputPane.Instance.WriteAsync(string.Empty); 46 | await OutputPane.Instance.WriteAsync("I have other extensions you might be intertested in at https://marketplace.visualstudio.com/publishers/MattLaceyLtd"); 47 | await OutputPane.Instance.WriteAsync(string.Empty); 48 | } 49 | 50 | private static async Task ShowPromptForSponsorshipAsync() 51 | { 52 | await OutputPane.Instance.WriteAsync("********************************************************************************************************"); 53 | await OutputPane.Instance.WriteAsync("This is a free extension that is made possible thanks to the kind and generous donations of:"); 54 | await OutputPane.Instance.WriteAsync(""); 55 | await OutputPane.Instance.WriteAsync("Daniel, James, Mike, Bill, unicorns39283, Martin, Richard, Alan, Howard, Mike, Dave, Joe, "); 56 | await OutputPane.Instance.WriteAsync("Alvin, Anders, Melvyn, Nik, Kevin, Richard, Orien, Shmueli, Gabriel, Martin, Neil, Daniel, "); 57 | await OutputPane.Instance.WriteAsync("Victor, Uno, Paula, Tom, Nick, Niki, chasingcode, luatnt, holeow, logarrhythmic, kokolorix, "); 58 | await OutputPane.Instance.WriteAsync("Guiorgy, Jessé, pharmacyhalo, MXM-7, atexinspect, João, hals1010, WTD-leachA, andermikael, "); 59 | await OutputPane.Instance.WriteAsync("spudwa, Cleroth, relentless-dev-purchases & 20+ more"); 60 | await OutputPane.Instance.WriteAsync(""); 61 | await OutputPane.Instance.WriteAsync("Join them to show you appreciation and ensure future maintenance and development by becoming a sponsor."); 62 | await OutputPane.Instance.WriteAsync(""); 63 | await OutputPane.Instance.WriteAsync("Go to https://github.com/sponsors/mrlacey"); 64 | await OutputPane.Instance.WriteAsync(""); 65 | await OutputPane.Instance.WriteAsync("Any amount, as either a one-off or on a monthly basis, is appreciated more than you can imagine."); 66 | await OutputPane.Instance.WriteAsync(""); 67 | await OutputPane.Instance.WriteAsync("I'll also tell you how to hide this message too. ;)"); 68 | await OutputPane.Instance.WriteAsync(""); 69 | await OutputPane.Instance.WriteAsync(""); 70 | await OutputPane.Instance.WriteAsync("If you can't afford to support financially, you can always"); 71 | await OutputPane.Instance.WriteAsync("leave a positive review at https://marketplace.visualstudio.com/items?itemName=MattLaceyLtd.CSInlineColorViz&ssr=false#review-details"); 72 | await OutputPane.Instance.WriteAsync(""); 73 | await OutputPane.Instance.WriteAsync("********************************************************************************************************"); 74 | 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/SvgTagger.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using System.Windows.Media; 3 | using Microsoft.VisualStudio.Text; 4 | using WpfColorHelper; 5 | 6 | namespace CsInlineColorViz; 7 | 8 | internal sealed class SvgTagger : RegexTagger, ITestableRegexColorTagger 9 | { 10 | internal static Regex regularExpression = new("((\\:|\")#)([0-9A-F]{3,8})(\"|;)", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 11 | 12 | public Regex ColorExpression => regularExpression; 13 | 14 | internal SvgTagger(ITextBuffer buffer) 15 | : base(buffer, [regularExpression]) 16 | { 17 | } 18 | 19 | internal override ColorTag TryCreateTagForMatch(Match match, int lineNumber, int lineStart, int spanStart, string lineText) 20 | { 21 | if (lineText.Contains(match.Value) && match.Groups.Count == 5) 22 | { 23 | var value = match.Groups[3].Value; 24 | 25 | if (ColorHelper.TryGetHexColor($"#{value}", out Color clr)) 26 | { 27 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.None); 28 | } 29 | else 30 | { 31 | System.Diagnostics.Debug.WriteLine($"Failed to understand '{value}' as a valid color."); 32 | } 33 | } 34 | 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/SvgTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | // different projects report SVG files with different content types 10 | [Export(typeof(ITaggerProvider))] 11 | [ContentType("HTML")] 12 | [ContentType("XML")] 13 | [TagType(typeof(ColorTag))] 14 | internal sealed class SvgTaggerProvider : ITaggerProvider 15 | { 16 | [Import] 17 | internal ITextDocumentFactoryService docFactory = null; 18 | 19 | public ITagger CreateTagger(ITextBuffer buffer) 20 | where T : ITag 21 | { 22 | if (buffer == null) 23 | { 24 | throw new ArgumentNullException(nameof(buffer)); 25 | } 26 | 27 | string fileName = null; 28 | 29 | if (docFactory.TryGetTextDocument(buffer, out ITextDocument document)) 30 | { 31 | fileName = document.FilePath; 32 | // You can now use the fileName variable as needed 33 | } 34 | 35 | if (string.IsNullOrWhiteSpace(fileName) || !fileName.EndsWith(".svg", StringComparison.OrdinalIgnoreCase)) 36 | { 37 | return null; 38 | } 39 | 40 | return buffer.Properties.GetOrCreateSingletonProperty(() => new SvgTagger(buffer)) as ITagger; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/SystemColorsDialog.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | using WpfColorHelper; 3 | 4 | namespace CsInlineColorViz; 5 | 6 | class SystemColorsDialog : ColorSelectionDialog 7 | { 8 | public SystemColorsDialog() : base() 9 | { 10 | var sp = new StackPanel(); 11 | 12 | foreach (var color in ColorListHelper.SystemColorsAlphabetically()) 13 | { 14 | if (ColorHelper.TryGetColor(color.Name, out System.Windows.Media.Color clr)) 15 | { 16 | sp.Children.Add(CreateButton(color.Name, clr)); 17 | } 18 | } 19 | 20 | var sv = new ScrollViewer { VerticalScrollBarVisibility = ScrollBarVisibility.Auto, Content = sp }; 21 | 22 | this.Content = sv; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/TextDocumentHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using EnvDTE; 6 | using Microsoft.VisualStudio.ComponentModelHost; 7 | using Microsoft.VisualStudio.Editor; 8 | using Microsoft.VisualStudio.Shell; 9 | using Microsoft.VisualStudio.Shell.Interop; 10 | using Microsoft.VisualStudio.Text; 11 | using Microsoft.VisualStudio.Text.Operations; 12 | using Microsoft.VisualStudio.TextManager.Interop; 13 | 14 | namespace CsInlineColorViz; 15 | 16 | internal static class TextDocumentHelper 17 | { 18 | /// 19 | /// The common set of options to be used for find and replace patterns. 20 | /// 21 | internal const FindOptions StandardFindOptions = FindOptions.MatchCase; 22 | 23 | /// 24 | /// Finds all matches of the specified pattern within the specified text document. 25 | /// 26 | /// The text document. 27 | /// The pattern string. 28 | /// The set of matches. 29 | internal static async Task> FindMatches(TextDocument textDocument, string patternString) 30 | { 31 | var matches = new List(); 32 | 33 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 34 | 35 | if (TryGetTextBufferAt(textDocument.Parent.FullName, out ITextBuffer textBuffer)) 36 | { 37 | IFinder finder = GetFinder(patternString, textBuffer); 38 | var findMatches = finder.FindAll(); 39 | foreach (var match in findMatches) 40 | { 41 | matches.Add(GetEditPointForSnapshotPosition(textDocument, textBuffer.CurrentSnapshot, match.Start)); 42 | } 43 | } 44 | 45 | return matches; 46 | } 47 | 48 | internal static async Task MakeReplacements(TextDocument textDocument, EditPoint startPoint, string patternString, string replacementString) 49 | { 50 | var result = false; 51 | 52 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); 53 | 54 | if (TryGetTextBufferAt(textDocument.Parent.FullName, out ITextBuffer textBuffer)) 55 | { 56 | IFinder finder = GetFinder(patternString, replacementString, textBuffer); 57 | result = ReplaceAll(textBuffer, finder.FindForReplaceAll(GetSnapshotSpanForExtent(textBuffer.CurrentSnapshot, startPoint, patternString.Length))); 58 | } 59 | 60 | return result; 61 | } 62 | 63 | private static IFindService GetFindService() 64 | { 65 | var componentModel = (IComponentModel)Package.GetGlobalService(typeof(SComponentModel)); 66 | var findService = componentModel.GetService(); 67 | return findService; 68 | } 69 | 70 | private static IFinder GetFinder(string findWhat, ITextBuffer textBuffer) 71 | { 72 | var finderFactory = GetFindService().CreateFinderFactory(findWhat, StandardFindOptions); 73 | return finderFactory.Create(textBuffer.CurrentSnapshot); 74 | } 75 | 76 | private static IFinder GetFinder(string findWhat, string replaceWith, ITextBuffer textBuffer) 77 | { 78 | var finderFactory = GetFindService().CreateFinderFactory(findWhat, replaceWith, StandardFindOptions); 79 | return finderFactory.Create(textBuffer.CurrentSnapshot); 80 | } 81 | 82 | private static EditPoint GetEditPointForSnapshotPosition(TextDocument textDocument, ITextSnapshot textSnapshot, int position) 83 | { 84 | ThreadHelper.ThrowIfNotOnUIThread(); 85 | 86 | var editPoint = textDocument.CreateEditPoint(); 87 | var textSnapshotLine = textSnapshot.GetLineFromPosition(position); 88 | editPoint.MoveToLineAndOffset(textSnapshotLine.LineNumber + 1, position - textSnapshotLine.Start.Position + 1); 89 | return editPoint; 90 | } 91 | 92 | private static Span GetSnapshotSpanForExtent(ITextSnapshot textSnapshot, EditPoint startPoint, int length) 93 | { 94 | ThreadHelper.ThrowIfNotOnUIThread(); 95 | 96 | var startPosition = GetSnapshotPositionForTextPoint(textSnapshot, startPoint); 97 | var endPosition = startPosition + length; 98 | 99 | if (startPosition <= endPosition) 100 | { 101 | return new Span(startPosition, endPosition - startPosition); 102 | } 103 | else 104 | { 105 | return new Span(endPosition, startPosition - endPosition); 106 | } 107 | } 108 | 109 | private static int GetSnapshotPositionForTextPoint(ITextSnapshot textSnapshot, TextPoint textPoint) 110 | { 111 | ThreadHelper.ThrowIfNotOnUIThread(); 112 | 113 | var textSnapshotLine = textSnapshot.GetLineFromLineNumber(textPoint.Line - 1); 114 | return textSnapshotLine.Start.Position + textPoint.LineCharOffset - 1; 115 | } 116 | 117 | 118 | private static bool ReplaceAll(ITextBuffer textBuffer, IEnumerable replacements) 119 | { 120 | var result = false; 121 | 122 | if (replacements.Any()) 123 | { 124 | using var edit = textBuffer.CreateEdit(); 125 | foreach (var match in replacements) 126 | { 127 | result = true; 128 | edit.Replace(match.Match, match.Replace); 129 | } 130 | 131 | edit.Apply(); 132 | } 133 | 134 | return result; 135 | } 136 | 137 | private static bool TryGetTextBufferAt(string filePath, out ITextBuffer textBuffer) 138 | { 139 | var componentModel = (IComponentModel)Package.GetGlobalService(typeof(SComponentModel)); 140 | 141 | if (CsInlineColorVizPackage.Instance is not null 142 | && VsShellUtilities.IsDocumentOpen( 143 | CsInlineColorVizPackage.Instance, 144 | filePath, 145 | Guid.Empty, 146 | out var _, 147 | out var _, 148 | out IVsWindowFrame windowFrame)) 149 | { 150 | IVsTextView view = VsShellUtilities.GetTextView(windowFrame); 151 | 152 | if (view.GetBuffer(out IVsTextLines lines) == 0) 153 | { 154 | if (lines is IVsTextBuffer buffer) 155 | { 156 | var editorAdapterFactoryService = componentModel.GetService(); 157 | textBuffer = editorAdapterFactoryService.GetDataBuffer(buffer); 158 | return true; 159 | } 160 | } 161 | } 162 | 163 | textBuffer = null; 164 | return false; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/UnityColorsDialog.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | using WpfColorHelper; 3 | 4 | namespace CsInlineColorViz; 5 | 6 | class UnityColorsDialog : ColorSelectionDialog 7 | { 8 | public UnityColorsDialog() : base() 9 | { 10 | var sp = new StackPanel(); 11 | 12 | foreach (var color in ColorListHelper.UnityColorsAlphabetical()) 13 | { 14 | if (ColorHelper.TryGetColor(color, out System.Windows.Media.Color clr)) 15 | { 16 | sp.Children.Add(CreateButton(color, clr)); 17 | } 18 | } 19 | 20 | var sv = new ScrollViewer { VerticalScrollBarVisibility = ScrollBarVisibility.Auto, Content = sp }; 21 | 22 | this.Content = sv; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/UnityTagger.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection.Metadata; 2 | using System.Text.RegularExpressions; 3 | using System.Windows.Media; 4 | using Microsoft.VisualStudio.Text; 5 | using WpfColorHelper; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | internal sealed class UnityTagger : RegexTagger, ITestableRegexColorTagger 10 | { 11 | internal static Regex regularExpression = new("()", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 12 | 13 | public Regex ColorExpression => regularExpression; 14 | 15 | public UnityTagger(ITextBuffer buffer) 16 | : base(buffer, [regularExpression]) 17 | { 18 | } 19 | 20 | internal override ColorTag TryCreateTagForMatch(Match match, int lineNumber, int lineStart, int spanStart, string lineText) 21 | { 22 | if (lineText.Contains(match.Value) && match.Groups.Count == 4) 23 | { 24 | var value = match.Groups[2].Value; 25 | 26 | if (ColorHelper.TryGetHexColor($"#{value}", out Color clr)) 27 | { 28 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.None); 29 | } 30 | else 31 | { 32 | System.Diagnostics.Debug.WriteLine($"Failed to understand '{value}' as a valid color."); 33 | } 34 | } 35 | 36 | return null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/UnityTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | [Export(typeof(ITaggerProvider))] 10 | [ContentType("CSharp")] 11 | [TagType(typeof(ColorTag))] 12 | internal sealed class UnityTaggerProvider : ITaggerProvider 13 | { 14 | public ITagger CreateTagger(ITextBuffer buffer) 15 | where T : ITag 16 | { 17 | if (buffer == null) 18 | { 19 | throw new ArgumentNullException(nameof(buffer)); 20 | } 21 | 22 | return buffer.Properties.GetOrCreateSingletonProperty(() => new UnityTagger(buffer)) as ITagger; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/UnityTextTagger.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using System.Windows.Media; 3 | using Microsoft.VisualStudio.Text; 4 | using WpfColorHelper; 5 | 6 | namespace CsInlineColorViz; 7 | 8 | internal sealed class UnityTextTagger : RegexTagger, ITestableRegexColorTagger 9 | { 10 | internal static Regex regularExpression = new(@"()", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 11 | 12 | public Regex ColorExpression => regularExpression; 13 | 14 | public UnityTextTagger(ITextBuffer buffer) 15 | : base(buffer, [regularExpression]) 16 | { 17 | } 18 | 19 | internal override ColorTag TryCreateTagForMatch(Match match, int lineNumber, int lineStart, int spanStart, string lineText) 20 | { 21 | if (lineText.Contains(match.Value) && match.Groups.Count == 4) 22 | { 23 | var value = match.Groups[3].Value; 24 | 25 | if (ColorHelper.TryGetColor($"{value}", out Color clr)) 26 | { 27 | return new ColorTag(clr, match, lineNumber, lineStart, PopupType.UnityColors); 28 | } 29 | else 30 | { 31 | System.Diagnostics.Debug.WriteLine($"Failed to understand '{value}' as a valid color."); 32 | } 33 | } 34 | 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/UnityTextTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Tagging; 5 | using Microsoft.VisualStudio.Utilities; 6 | 7 | namespace CsInlineColorViz; 8 | 9 | [Export(typeof(ITaggerProvider))] 10 | [ContentType("CSharp")] 11 | [TagType(typeof(ColorTag))] 12 | internal sealed class UnityTextTaggerProvider : ITaggerProvider 13 | { 14 | public ITagger CreateTagger(ITextBuffer buffer) 15 | where T : ITag 16 | { 17 | if (buffer == null) 18 | { 19 | throw new ArgumentNullException(nameof(buffer)); 20 | } 21 | 22 | return buffer.Properties.GetOrCreateSingletonProperty(() => new UnityTextTagger(buffer)) as ITagger; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/filestosign.txt: -------------------------------------------------------------------------------- 1 | CsInlineColorViz.dll -------------------------------------------------------------------------------- /src/signvsix.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/source.extension.cs: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // 3 | // This file was generated by VSIX Synchronizer 4 | // 5 | // ------------------------------------------------------------------------------ 6 | namespace CsInlineColorViz 7 | { 8 | internal sealed partial class Vsix 9 | { 10 | public const string Id = "CsInlineColorViz.6b864116-c092-4b7a-aad8-34239a7bbe70"; 11 | public const string Name = "C# Inline Color Vizualizer"; 12 | public const string Description = @"See samples of the colors you use within your C# code. "; 13 | public const string Language = "en-US"; 14 | public const string Version = "1.20.1"; 15 | public const string Author = "Matt Lacey"; 16 | public const string Tags = ""; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | C# Inline Color Vizualizer 6 | See samples of the colors you use within your C# code. 7 | https://github.com/mrlacey/CSInlineColorViz 8 | LICENSE.txt 9 | Resources\Icon.png 10 | Resources\Icon.png 11 | 12 | 13 | 14 | amd64 15 | 16 | 17 | arm64 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | --------------------------------------------------------------------------------