From b169b7e2771f92c726d641593b4ac50526dc67c2 Mon Sep 17 00:00:00 2001 From: Mitch Capper Date: Tue, 14 Mar 2023 00:07:47 -0700 Subject: [PATCH 1/8] vs2022 support and updated tooling --- .editorconfig | 170 ++++++++++++++++++ JustMyCodeToggle.sln | 29 +-- .../Tvl.VisualStudio.JustMyCodeToggle.csproj | 104 ++++++----- .../source.extension.vsixmanifest | 48 ++--- 4 files changed, 267 insertions(+), 84 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..cfd039e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,170 @@ +# NOTE: Requires **VS2019 16.3** or later + +# Rules for JustMyCodeToggle +# Description: Code analysis rules for JustMyCodeToggle. + +# Code files +[*.{cs,vb}] + + +dotnet_diagnostic.CA1001.severity = warning + +dotnet_diagnostic.CA1009.severity = warning + +dotnet_diagnostic.CA1016.severity = warning + +dotnet_diagnostic.CA1033.severity = warning + +dotnet_diagnostic.CA1049.severity = warning + +dotnet_diagnostic.CA1060.severity = warning + +dotnet_diagnostic.CA1061.severity = warning + +dotnet_diagnostic.CA1063.severity = warning + +dotnet_diagnostic.CA1065.severity = warning + +dotnet_diagnostic.CA1301.severity = warning + +dotnet_diagnostic.CA1400.severity = warning + +dotnet_diagnostic.CA1401.severity = warning + +dotnet_diagnostic.CA1403.severity = warning + +dotnet_diagnostic.CA1404.severity = warning + +dotnet_diagnostic.CA1405.severity = warning + +dotnet_diagnostic.CA1410.severity = warning + +dotnet_diagnostic.CA1415.severity = warning + +dotnet_diagnostic.CA1821.severity = warning + +dotnet_diagnostic.CA1900.severity = warning + +dotnet_diagnostic.CA1901.severity = warning + +dotnet_diagnostic.CA2002.severity = warning + +dotnet_diagnostic.CA2100.severity = warning + +dotnet_diagnostic.CA2101.severity = warning + +dotnet_diagnostic.CA2108.severity = warning + +dotnet_diagnostic.CA2111.severity = warning + +dotnet_diagnostic.CA2112.severity = warning + +dotnet_diagnostic.CA2114.severity = warning + +dotnet_diagnostic.CA2116.severity = warning + +dotnet_diagnostic.CA2117.severity = warning + +dotnet_diagnostic.CA2122.severity = warning + +dotnet_diagnostic.CA2123.severity = warning + +dotnet_diagnostic.CA2124.severity = warning + +dotnet_diagnostic.CA2126.severity = warning + +dotnet_diagnostic.CA2131.severity = warning + +dotnet_diagnostic.CA2132.severity = warning + +dotnet_diagnostic.CA2133.severity = warning + +dotnet_diagnostic.CA2134.severity = warning + +dotnet_diagnostic.CA2137.severity = warning + +dotnet_diagnostic.CA2138.severity = warning + +dotnet_diagnostic.CA2140.severity = warning + +dotnet_diagnostic.CA2141.severity = warning + +dotnet_diagnostic.CA2146.severity = warning + +dotnet_diagnostic.CA2147.severity = warning + +dotnet_diagnostic.CA2149.severity = warning + +dotnet_diagnostic.CA2200.severity = warning + +dotnet_diagnostic.CA2202.severity = warning + +dotnet_diagnostic.CA2207.severity = warning + +dotnet_diagnostic.CA2212.severity = warning + +dotnet_diagnostic.CA2213.severity = warning + +dotnet_diagnostic.CA2214.severity = warning + +dotnet_diagnostic.CA2216.severity = warning + +dotnet_diagnostic.CA2220.severity = warning + +dotnet_diagnostic.CA2229.severity = warning + +dotnet_diagnostic.CA2231.severity = warning + +dotnet_diagnostic.CA2232.severity = warning + +dotnet_diagnostic.CA2235.severity = warning + +dotnet_diagnostic.CA2236.severity = warning + +dotnet_diagnostic.CA2237.severity = warning + +dotnet_diagnostic.CA2238.severity = warning + +dotnet_diagnostic.CA2240.severity = warning + +dotnet_diagnostic.CA2241.severity = warning + +dotnet_diagnostic.CA2242.severity = warning + +dotnet_diagnostic.CS1573.severity = silent + +dotnet_diagnostic.CS1591.severity = silent + +dotnet_diagnostic.CS1712.severity = silent + +# IDE0003 duplicates functionality provided by SX1101 +dotnet_diagnostic.IDE0003.severity = none + +dotnet_diagnostic.SA1101.severity = none + +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2275 +dotnet_diagnostic.SA1139.severity = none + +dotnet_diagnostic.SA1202.severity = none + +dotnet_diagnostic.SA1204.severity = none + +dotnet_diagnostic.SA1309.severity = none + +dotnet_diagnostic.SA1412.severity = warning + +dotnet_diagnostic.SA1600.severity = none + +dotnet_diagnostic.SA1601.severity = none + +dotnet_diagnostic.SA1602.severity = none + +dotnet_diagnostic.SA1611.severity = none + +dotnet_diagnostic.SA1615.severity = none + +dotnet_diagnostic.SX1101.severity = warning + +dotnet_diagnostic.SX1309.severity = warning + +dotnet_diagnostic.SX1309S.severity = warning diff --git a/JustMyCodeToggle.sln b/JustMyCodeToggle.sln index 69854fb..1834bf0 100644 --- a/JustMyCodeToggle.sln +++ b/JustMyCodeToggle.sln @@ -1,29 +1,32 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.12 -MinimumVisualStudioVersion = 15.0 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{05CB4485-3F59-4189-96D8-F046221EC9CE}" - ProjectSection(SolutionItems) = preProject - .gitattributes = .gitattributes - .gitignore = .gitignore - appveyor.yml = appveyor.yml - LICENSE.txt = LICENSE.txt - README.md = README.md - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tvl.VisualStudio.JustMyCodeToggle", "Tvl.VisualStudio.JustMyCodeToggle\Tvl.VisualStudio.JustMyCodeToggle.csproj", "{10CAD392-E083-41EB-92C9-B5F3142FA9F8}" +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33417.168 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tvl.VisualStudio.JustMyCodeToggle", "Tvl.VisualStudio.JustMyCodeToggle\Tvl.VisualStudio.JustMyCodeToggle.csproj", "{10CAD392-E083-41EB-92C9-B5F3142FA9F8}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|arm64 = Debug|arm64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|arm64 = Release|arm64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Debug|arm64.ActiveCfg = Debug|arm64 + {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Debug|arm64.Build.0 = Debug|arm64 + {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Debug|x86.ActiveCfg = Debug|x86 + {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Debug|x86.Build.0 = Debug|x86 {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Release|Any CPU.ActiveCfg = Release|Any CPU {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Release|Any CPU.Build.0 = Release|Any CPU + {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Release|arm64.ActiveCfg = Release|arm64 + {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Release|arm64.Build.0 = Release|arm64 + {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Release|x86.ActiveCfg = Release|x86 + {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Tvl.VisualStudio.JustMyCodeToggle.csproj b/Tvl.VisualStudio.JustMyCodeToggle/Tvl.VisualStudio.JustMyCodeToggle.csproj index 729399a..5f50d23 100644 --- a/Tvl.VisualStudio.JustMyCodeToggle/Tvl.VisualStudio.JustMyCodeToggle.csproj +++ b/Tvl.VisualStudio.JustMyCodeToggle/Tvl.VisualStudio.JustMyCodeToggle.csproj @@ -1,94 +1,102 @@  - - - + - net45 - + 17.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + Debug + AnyCPU + 2.0 + {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + v4.7.2 + Properties Adds the Just My Code command button to Visual Studio. Tunnel Vision Laboratories, LLC Copyright © Sam Harwell 2017 - 1.3.0.0 - 1.3.0.0 - 1.3.0-dev - + 1.4.0.0 + 1.4.0.0 + 1.4.0-dev + {10CAD392-E083-41EB-92C9-B5F3142FA9F8} + Library true true true false false true - false - - true - SharedKey.snk + true - - - - False - - - true - - - - full + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 - - + pdbonly - true + true + bin\Release\ + TRACE + prompt + 4 - true - $(MSBuildThisFileDirectory)..\JustMyCodeToggle.ruleset + + + AnyCPU - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + - - - - - + + Designer + - - - + - LICENSE.txt true - 1000 Designer - VSPackage.resources true - - - - - - + + + + \ No newline at end of file diff --git a/Tvl.VisualStudio.JustMyCodeToggle/source.extension.vsixmanifest b/Tvl.VisualStudio.JustMyCodeToggle/source.extension.vsixmanifest index 80a4a5e..c124e8e 100644 --- a/Tvl.VisualStudio.JustMyCodeToggle/source.extension.vsixmanifest +++ b/Tvl.VisualStudio.JustMyCodeToggle/source.extension.vsixmanifest @@ -1,25 +1,27 @@ - + - - - Just My Code Toggle - Adds the Just My Code command button to Visual Studio. - https://github.com/tunnelvisionlabs/JustMyCodeToggle - LICENSE.txt - - - https://github.com/tunnelvisionlabs/JustMyCodeToggle/releases/latest - - - debugging, just my code - - - - - - - - - - + + + Just My Code Toggle + Adds the Just My Code command button to Visual Studio. + https://github.com/tunnelvisionlabs/JustMyCodeToggle + LICENSE.txt + + + https://github.com/tunnelvisionlabs/JustMyCodeToggle/releases/latest + + + debugging, just my code + + + + amd64 + + + + + + + + From 9256bf3e4a0e84a39a8e2abfb0f553b75b93a399 Mon Sep 17 00:00:00 2001 From: Mitch Capper Date: Thu, 16 Mar 2023 11:49:29 -0700 Subject: [PATCH 2/8] Updated to new csproj format --- JustMyCodeToggle.sln | 12 -- .../Properties/AssemblyInfo.cs | 12 -- .../Tvl.VisualStudio.JustMyCodeToggle.csproj | 172 ++++++++---------- 3 files changed, 71 insertions(+), 125 deletions(-) delete mode 100644 Tvl.VisualStudio.JustMyCodeToggle/Properties/AssemblyInfo.cs diff --git a/JustMyCodeToggle.sln b/JustMyCodeToggle.sln index 1834bf0..f4b2121 100644 --- a/JustMyCodeToggle.sln +++ b/JustMyCodeToggle.sln @@ -8,25 +8,13 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Debug|arm64 = Debug|arm64 - Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU - Release|arm64 = Release|arm64 - Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Debug|arm64.ActiveCfg = Debug|arm64 - {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Debug|arm64.Build.0 = Debug|arm64 - {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Debug|x86.ActiveCfg = Debug|x86 - {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Debug|x86.Build.0 = Debug|x86 {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Release|Any CPU.ActiveCfg = Release|Any CPU {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Release|Any CPU.Build.0 = Release|Any CPU - {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Release|arm64.ActiveCfg = Release|arm64 - {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Release|arm64.Build.0 = Release|arm64 - {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Release|x86.ActiveCfg = Release|x86 - {10CAD392-E083-41EB-92C9-B5F3142FA9F8}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Properties/AssemblyInfo.cs b/Tvl.VisualStudio.JustMyCodeToggle/Properties/AssemblyInfo.cs deleted file mode 100644 index e16d031..0000000 --- a/Tvl.VisualStudio.JustMyCodeToggle/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.txt in the project root for license information. - -using System; -using System.Runtime.InteropServices; - -[assembly: CLSCompliant(false)] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Tvl.VisualStudio.JustMyCodeToggle.csproj b/Tvl.VisualStudio.JustMyCodeToggle/Tvl.VisualStudio.JustMyCodeToggle.csproj index 5f50d23..6bdfd1c 100644 --- a/Tvl.VisualStudio.JustMyCodeToggle/Tvl.VisualStudio.JustMyCodeToggle.csproj +++ b/Tvl.VisualStudio.JustMyCodeToggle/Tvl.VisualStudio.JustMyCodeToggle.csproj @@ -1,102 +1,72 @@ - - - - 17.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - Debug - AnyCPU - 2.0 - {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - v4.7.2 - Properties - Adds the Just My Code command button to Visual Studio. - Tunnel Vision Laboratories, LLC - Copyright © Sam Harwell 2017 - 1.4.0.0 - 1.4.0.0 - 1.4.0-dev - {10CAD392-E083-41EB-92C9-B5F3142FA9F8} - Library - true - true - true - false - false - true - true - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - true - - - AnyCPU - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - - - Designer - - - - - - - - LICENSE.txt - true - - - - - 1000 - Designer - - - - - VSPackage.resources - true - - - - - + + + net472 + + Properties + Adds the Just My Code command button to Visual Studio. + Tunnel Vision Laboratories, LLC + Copyright © Sam Harwell 2017 + 1.4.0.0 + 1.4.0.0 + 1.4.0-dev + true + true + true + false + false + true + true + obj + obj/int + True + + + $(MSBuildThisFileDirectory)..\JustMyCodeToggle.ruleset + + + pdbonly + true + + + + False + + + true + + + + + + + + + + + + + + Designer + + + + + LICENSE.txt + true + + + + + 1000 + Designer + + + + + VSPackage.resources + true + + + + \ No newline at end of file From fa8a5398bce5a81549d345a92cb2eacc14038ef1 Mon Sep 17 00:00:00 2001 From: Mitch Capper Date: Wed, 29 Jan 2025 21:23:58 -0800 Subject: [PATCH 3/8] Several additional toggles added, major code refactoring and util expansion --- .editorconfig | 130 +++++++++++++++++- JustMyCodeToggle.sln | 5 + README.md | 15 +- .../Commands/FullSymbolLoadToggleCmd.cs | 11 ++ .../Commands/JitOptimizationsToggleCmd.cs | 79 +++++++++++ .../Commands/JustMyCodeToggleCmd.cs | 44 ++++++ .../Commands/NativeDebuggingToggleCmd.cs | 119 ++++++++++++++++ .../Commands/OurBaseSettingCmd.cs | 59 ++++++++ Tvl.VisualStudio.JustMyCodeToggle/Helpers.cs | 46 +++++++ .../JustMyCodeToggle.cs | 30 ++++ .../JustMyCodeToggle.vsct | 87 ++++++++++-- .../JustMyCodeToggleConstants.cs | 17 --- .../JustMyCodeTogglePackage.cs | 95 +++++-------- .../Managers/LaunchProfileManager.cs | 73 ++++++++++ .../Managers/ProjectEventHandler.cs | 63 +++++++++ .../Managers/SettingsManager.cs | 91 ++++++++++++ .../Managers/StartupProjectManager.cs | 62 +++++++++ .../Tvl.VisualStudio.JustMyCodeToggle.csproj | 55 +++++++- .../VSPackage.resx | 2 +- .../source.extension.cs | 18 +++ doc/toolbar.png | Bin 7563 -> 8027 bytes 21 files changed, 1002 insertions(+), 99 deletions(-) create mode 100644 Tvl.VisualStudio.JustMyCodeToggle/Commands/FullSymbolLoadToggleCmd.cs create mode 100644 Tvl.VisualStudio.JustMyCodeToggle/Commands/JitOptimizationsToggleCmd.cs create mode 100644 Tvl.VisualStudio.JustMyCodeToggle/Commands/JustMyCodeToggleCmd.cs create mode 100644 Tvl.VisualStudio.JustMyCodeToggle/Commands/NativeDebuggingToggleCmd.cs create mode 100644 Tvl.VisualStudio.JustMyCodeToggle/Commands/OurBaseSettingCmd.cs create mode 100644 Tvl.VisualStudio.JustMyCodeToggle/Helpers.cs create mode 100644 Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggle.cs delete mode 100644 Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggleConstants.cs create mode 100644 Tvl.VisualStudio.JustMyCodeToggle/Managers/LaunchProfileManager.cs create mode 100644 Tvl.VisualStudio.JustMyCodeToggle/Managers/ProjectEventHandler.cs create mode 100644 Tvl.VisualStudio.JustMyCodeToggle/Managers/SettingsManager.cs create mode 100644 Tvl.VisualStudio.JustMyCodeToggle/Managers/StartupProjectManager.cs create mode 100644 Tvl.VisualStudio.JustMyCodeToggle/source.extension.cs diff --git a/.editorconfig b/.editorconfig index cfd039e..0507db4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,11 +2,91 @@ # Rules for JustMyCodeToggle # Description: Code analysis rules for JustMyCodeToggle. - -# Code files -[*.{cs,vb}] - - +# top-most EditorConfig file +root = true + +[*] +charset = utf-8 +end_of_line = crlf +indent_style = space +indent_size = 4 +tab_width = 4 +insert_final_newline = true + +# C# files +[*.cs] +# Organize using directives +dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = false + +# Code style defaults +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true + +# Naming conventions +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_naming_style.camel_case_underscore.capitalization = camel_case +dotnet_naming_style.camel_case_underscore.required_prefix = _ + +dotnet_naming_rule.public_members_pascal_case.symbols = public_members +dotnet_naming_rule.public_members_pascal_case.style = pascal_case +dotnet_naming_rule.public_members_pascal_case.severity = suggestion + +dotnet_naming_rule.private_fields_camel_case_underscore.symbols = private_fields +dotnet_naming_rule.private_fields_camel_case_underscore.style = camel_case_underscore +dotnet_naming_rule.private_fields_camel_case_underscore.severity = suggestion +csharp_indent_labels = no_change +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:suggestion +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_prefer_system_threading_lock = true:suggestion +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_space_around_binary_operators = before_and_after +csharp_style_throw_expression = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_prefer_static_anonymous_function = true:suggestion +csharp_prefer_static_local_function = true:suggestion +csharp_style_prefer_readonly_struct_member = true:suggestion +csharp_style_prefer_readonly_struct = true:suggestion +csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent +csharp_style_conditional_delegate_call = true:suggestion +csharp_style_prefer_switch_expression = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_var_elsewhere = false:silent +csharp_style_var_when_type_is_apparent = false:silent +csharp_style_var_for_built_in_types = false:silent +csharp_style_prefer_extended_property_pattern = true:suggestion dotnet_diagnostic.CA1001.severity = warning dotnet_diagnostic.CA1009.severity = warning @@ -168,3 +248,43 @@ dotnet_diagnostic.SX1101.severity = warning dotnet_diagnostic.SX1309.severity = warning dotnet_diagnostic.SX1309S.severity = warning +# VSTHRD200: Use "Async" suffix for async methods +dotnet_diagnostic.VSTHRD200.severity = none +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_readonly_field = true:suggestion +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_predefined_type_for_member_access = true:silent +dotnet_style_allow_multiple_blank_lines_experimental = true:silent +dotnet_style_allow_statement_immediately_after_block_experimental = true:silent +dotnet_code_quality_unused_parameters = all:suggestion +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_qualification_for_event = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_field = false:silent + + +[*.vsct] +indent_style = space +indent_size = 2 + diff --git a/JustMyCodeToggle.sln b/JustMyCodeToggle.sln index f4b2121..e9c4792 100644 --- a/JustMyCodeToggle.sln +++ b/JustMyCodeToggle.sln @@ -5,6 +5,11 @@ VisualStudioVersion = 17.6.33417.168 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tvl.VisualStudio.JustMyCodeToggle", "Tvl.VisualStudio.JustMyCodeToggle\Tvl.VisualStudio.JustMyCodeToggle.csproj", "{10CAD392-E083-41EB-92C9-B5F3142FA9F8}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/README.md b/README.md index 1eb4bf5..616552d 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,18 @@ [![Build status](https://ci.appveyor.com/api/projects/status/dnxhxa3chg1dtpnt/branch/master?svg=true)](https://ci.appveyor.com/project/sharwell/justmycodetoggle/branch/master) -This extension adds the **Debug.JustMyCodeToggle** command to Visual Studio. By default, the command appears in the **Debug** toolbar as well as in the context menu for the **Call Stack** window. +This primary goal of this extension is to add the **Debug.JustMyCodeToggle** from the options menu to the Visual Studio main window. By default, the command appears in the **Debug** toolbar as well as in the context menu for the **Call Stack** window. It also adds a few other setting commands in the toolbar using a split-button to toggle: Native Code Debugging, All Symbols / Selective Symbol loading, Disable .NET and JIT code optimization for external code or release builds for better preservation of variables/stack items normally optimized away. + +JustMyCode toggle can be changed while debugging but the others will only take effect on the next debug session. ### Debug Toolbar ![Debug Toolbar](doc/toolbar.png) +The toolbar also features a few other toggles including: +- Native Code Debugging - The debug properties debug native code option (stored in the .csproj.user file) +- All Module Symbol Loading - Toggle between VS trying to only load symbols for needed files/modules or loading for all modules ( this is the Options->Debugging->Symbols "Automatic searching symbols" dropdown ) +- Disabling native / managed code optimization - Better debugging for native/3rd party code to preserve stack and variables (Only for new CPS style projects with launch profile support, as we need to set env vars). Env vars are stored in the launch profile but are deleted when you toggle this back off. This is similar to / slightly more robust than the Debugging-> use of precompiled images (which I couldn't find). + ### Call Stack Window @@ -14,4 +21,8 @@ This extension adds the **Debug.JustMyCodeToggle** command to Visual Studio. By ### Keyboard -By default, the new command is not bound to a keystroke. A key binding may be added manually by configuring a binding for the **Debug.JustMyCodeToggle** command. +By default, the new command is not bound to a keystroke. A key binding may be added manually by configuring a binding for any of the following commands: +- Debug.JustMyCodeToggle +- Debug.JMCSymbolLocalToggle +- Debug.JMCNativeCodeDebugging +- Debug.JMCDisableJitOptmizations diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Commands/FullSymbolLoadToggleCmd.cs b/Tvl.VisualStudio.JustMyCodeToggle/Commands/FullSymbolLoadToggleCmd.cs new file mode 100644 index 0000000..6315230 --- /dev/null +++ b/Tvl.VisualStudio.JustMyCodeToggle/Commands/FullSymbolLoadToggleCmd.cs @@ -0,0 +1,11 @@ +using Community.VisualStudio.Toolkit; + +namespace Tvl.VisualStudio.JustMyCodeToggle.Commands +{ + + //do we only do the exclude list but load all others or do we automatically decide what to load + [Command(PackageIds.JMCSymbolLoadBtn)] + internal class FullSymbolLoadToggleCmd() : ToggleSettingCmd("Debugger", "SymbolUseExcludeList", 1, 0) + { + } +} diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Commands/JitOptimizationsToggleCmd.cs b/Tvl.VisualStudio.JustMyCodeToggle/Commands/JitOptimizationsToggleCmd.cs new file mode 100644 index 0000000..0ee1caf --- /dev/null +++ b/Tvl.VisualStudio.JustMyCodeToggle/Commands/JitOptimizationsToggleCmd.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Community.VisualStudio.Toolkit; +using Microsoft.VisualStudio.Shell; +using Tvl.VisualStudio.JustMyCodeToggle.Managers; + +namespace Tvl.VisualStudio.JustMyCodeToggle.Commands +{ + [Command(PackageIds.JMCDisableJitOptmizationsBtn)] + internal class JitOptimizationsToggleCmd : BaseCommand + { + + private StartupProjectManager _startupProjectManager; + private LaunchProfileManager _launchProfileManager; + + public JitOptimizationsToggleCmd() + { + var baseVars = new Dictionary(){ {"ZapDisable","1" }, + {"ReadyToRun","0" }, + {"TieredCompilation","0" }, + {"JITMinOpts","1" }}; + var envs = new List>(); + string[] prefixes = ["DOTNET_", "COMPlus_"]; + foreach (var kvp in baseVars) + { + foreach (var prefix in prefixes) + envs.Add(new(prefix + kvp.Key, kvp.Value)); + } + ENVVars = envs.ToArray(); + } + private KeyValuePair[] ENVVars; + protected override async Task InitializeCompletedAsync() + { + await base.InitializeCompletedAsync(); + _startupProjectManager = Package.GetTypedService(); + _launchProfileManager = Package.GetTypedService(); + + await UpdateCheckedState(); + _startupProjectManager.StartupProjectChanged += (_, _) => UpdateCheckedState(); + + } + + protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + await SetValue(!Command.Checked); + await UpdateCheckedState(); + } + + private async Task SetValue(bool value) + { + if (!_startupProjectManager.ActiveProjectSupportsCPSProfiles) + return;//shouldn't get here + await _launchProfileManager.UpdateLaunchProfileENVVars(isDelete: !value, ENVVars.ToArray()); + + } + + private async Task UpdateCheckedState() + { + Command.Enabled = _startupProjectManager.HasStartupProject && _startupProjectManager.ActiveProjectSupportsCPSProfiles; + if (!Command.Enabled) + return; + var launchProfile = await _launchProfileManager.GetLaunchProfile(); + if (launchProfile == null) + { + Command.Enabled = false; + return; + } + + var envVars = launchProfile.EnvironmentVariables; + var zapDisable = launchProfile?.EnvironmentVariables.FirstOrDefault(a => a.Key == "COMPlus_ZapDisable"); + + Command.Checked = zapDisable?.Value == "1"; + + + } + } +} diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Commands/JustMyCodeToggleCmd.cs b/Tvl.VisualStudio.JustMyCodeToggle/Commands/JustMyCodeToggleCmd.cs new file mode 100644 index 0000000..f8db3ef --- /dev/null +++ b/Tvl.VisualStudio.JustMyCodeToggle/Commands/JustMyCodeToggleCmd.cs @@ -0,0 +1,44 @@ +using System.Threading.Tasks; +using Community.VisualStudio.Toolkit; +using EnvDTE; + + +namespace Tvl.VisualStudio.JustMyCodeToggle.Commands +{ + [Command(PackageIds.JMCMenuController)] + internal class MenuBtnCmd : BaseCommand + { + public MenuBtnCmd() + { + instance = this; + } + internal static MenuBtnCmd instance; + + } + + [Command(PackageIds.JMCJustMyCodeBtn)] + internal class JustMyCodeToggleCmd() : ToggleSettingCmd("Debugger", "JustMyCode", 1, 0) + { + protected override void SetCheckedToMatch(int cur) + { + base.SetCheckedToMatch(cur); + if (MenuBtnCmd.instance != null) + MenuBtnCmd.instance.Command.Checked = Command.Checked; //keep menu in sync with us + } + protected override Task InitializeCompletedAsync() + { + DelaySetChecked(); + return base.InitializeCompletedAsync(); + } + /// + /// to set the related command items not initalized before + /// + private async void DelaySetChecked() + { + await Task.Delay(1000); + //await JoinableTaskFactory.SwitchToMainThreadAsync(); + SetCheckedToMatch(Command.Checked ? 1 : 0); + + } + } +} diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Commands/NativeDebuggingToggleCmd.cs b/Tvl.VisualStudio.JustMyCodeToggle/Commands/NativeDebuggingToggleCmd.cs new file mode 100644 index 0000000..7ceda5a --- /dev/null +++ b/Tvl.VisualStudio.JustMyCodeToggle/Commands/NativeDebuggingToggleCmd.cs @@ -0,0 +1,119 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using Community.VisualStudio.Toolkit; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Tvl.VisualStudio.JustMyCodeToggle.Managers; +namespace Tvl.VisualStudio.JustMyCodeToggle.Commands +{ + [Command(PackageIds.JMCNativeCodeBtn)] + internal class NativeDebuggingToggleCmd : BaseCommand + { + protected const string cpsProfileStr = "nativeDebugging"; + protected const string nonCpsPropertyStr = "EnableUnmanagedDebugging"; + + private StartupProjectManager _startupProjectManager; + private LaunchProfileManager _launchProfileManager; + + public NativeDebuggingToggleCmd() + { + + } + + protected override async Task InitializeCompletedAsync() + { + _startupProjectManager = Package.GetTypedService(); + _launchProfileManager = Package.GetTypedService(); + await UpdateCheckedState(); + _startupProjectManager.StartupProjectChanged += (_, _) => UpdateCheckedState(); + await base.InitializeCompletedAsync(); + } + + protected async Task SetValue(bool value) + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + if (_startupProjectManager.ActiveProjectSupportsCPSProfiles) + { + await _launchProfileManager.UpdateLaunchProfileSetting(cpsProfileStr, value); + } + else + { + var startupProject = await _startupProjectManager.GetStartupProject(); + var configName = startupProject == null ? null : await startupProject.GetCurrentConfigurationName(); + Command.Enabled = startupProject != null && configName != null; + if (Command.Enabled) + { + + var buildStorage = startupProject as IVsBuildPropertyStorage; + var cmdres = buildStorage.SetPropertyValue(nonCpsPropertyStr, configName, (uint)_PersistStorageType.PST_USER_FILE, value ? "true" : "false"); + + await startupProject.SaveProject(force: true);//must use force or else it is as if the change wasn't detected. Also cannot figure out how to just save the user file I believe we must save both + //note while this changes our .user file it doesn't update the active native debugging plan for launch + + var dteProj = await startupProject.GetDTEProjectFromIvsProject(); + try + { + + var itm = dteProj.ConfigurationManager?.ActiveConfiguration?.Properties?.Item("EnableUnmanagedDebugging"); //this updates the actual launch project in memory object + if (itm != null) + itm.Value = value ? "true" : "false"; + + } + catch { } + + } + } + } + + private async Task UpdateCheckedState() + { + Command.Enabled = _startupProjectManager.HasStartupProject; + if (!Command.Enabled) + return; + + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + if (_startupProjectManager.ActiveProjectSupportsCPSProfiles) + { + var launchProfile = await _launchProfileManager.GetLaunchProfile(); + if (launchProfile == null) + { + Debug.WriteLine($"JMC NativeDebuggingToggleCmd: in theory project supports CPS profiles but no launch profile found??"); + Command.Enabled = false; + } + else + { + var curVal = launchProfile.OtherSettings.FirstOrDefault(a => a.Key.Equals(cpsProfileStr, StringComparison.CurrentCultureIgnoreCase)); + Command.Checked = curVal.Key != default && ((bool)curVal.Value == true); + } + } + else + { + var startupProject = await _startupProjectManager.GetStartupProject(); + var configName = await startupProject.GetCurrentConfigurationName(); + Command.Enabled = startupProject != null && configName != null; + if (Command.Enabled) + { + + var buildStorage = startupProject as IVsBuildPropertyStorage; + var cmdres = buildStorage.GetPropertyValue(nonCpsPropertyStr, configName, (uint)_PersistStorageType.PST_USER_FILE, out var existing); + if (cmdres != VSConstants.S_OK || String.IsNullOrWhiteSpace(existing)) + buildStorage.GetPropertyValue(nonCpsPropertyStr, configName, (uint)_PersistStorageType.PST_PROJECT_FILE, out existing);//user file will override project file but if user file is blank fallback to project + + + Command.Checked = existing == "true"; + + } + + } + } + protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) + { + await UpdateCheckedState(); + await SetValue(!Command.Checked); + await base.ExecuteAsync(e); + } + } +} diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Commands/OurBaseSettingCmd.cs b/Tvl.VisualStudio.JustMyCodeToggle/Commands/OurBaseSettingCmd.cs new file mode 100644 index 0000000..b009285 --- /dev/null +++ b/Tvl.VisualStudio.JustMyCodeToggle/Commands/OurBaseSettingCmd.cs @@ -0,0 +1,59 @@ +using System.Threading.Tasks; +using Community.VisualStudio.Toolkit; +using Microsoft.VisualStudio.Shell; +using Tvl.VisualStudio.JustMyCodeToggle.Managers; + + +namespace Tvl.VisualStudio.JustMyCodeToggle.Commands +{ + internal abstract class ToggleSettingCmd(string collectionPath, string propertyName, SETTING_TYPE enabled_val, SETTING_TYPE disabled_val) : OurSettingCmd(collectionPath, propertyName) where OUR_CLASS : class, new() + { + + protected override Task ExecuteAsync(OleMenuCmdEventArgs e) + { + var cur = _settingsManager.GetSetting(collectionPath, propertyName); + cur = cur.Equals(disabled_val) ? enabled_val : disabled_val; + SetValue(cur); + SetCheckedToMatch(cur); + + + return base.ExecuteAsync(e); + } + protected virtual void SetCheckedToMatch(SETTING_TYPE cur) + { + if (!cur.Equals(disabled_val)) + Command.Checked = true; + else + Command.Checked = false; + } + protected override async Task InitializeCompletedAsync() + { + await base.InitializeCompletedAsync(); + SetCheckedToMatch(GetValue()); + + } + } + internal abstract class OurSettingCmd : BaseCommand where OUR_CLASS : class, new() + { + protected readonly string collectionPath; + protected readonly string propertyName; + protected SettingsManager _settingsManager; + + public OurSettingCmd(string collectionPath, string propertyName) + { + + this.collectionPath = collectionPath; + this.propertyName = propertyName; + } + protected override async Task InitializeCompletedAsync() + { + await base.InitializeCompletedAsync(); + _settingsManager = Package.GetTypedService(); + + } + + protected SETTING_TYPE GetValue() => _settingsManager.GetSetting(collectionPath, propertyName); + protected void SetValue(SETTING_TYPE val) => _settingsManager.SetSetting(collectionPath, propertyName, val); + + } +} diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Helpers.cs b/Tvl.VisualStudio.JustMyCodeToggle/Helpers.cs new file mode 100644 index 0000000..9d9a1a2 --- /dev/null +++ b/Tvl.VisualStudio.JustMyCodeToggle/Helpers.cs @@ -0,0 +1,46 @@ +using System.Threading.Tasks; +using Community.VisualStudio.Toolkit; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.ProjectSystem; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Tvl.VisualStudio.JustMyCodeToggle +{ + + /// + /// Extension methods for Visual Studio project types + /// + public static class ProjectExtensions + { + public static T GetTypedService(this AsyncPackage package) where T : class => package.GetService(); + public static async Task GetDTEProjectFromIvsProject(this IVsProject proj) + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + ((IVsHierarchy)proj).GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ExtObject, out object prop); + return prop as EnvDTE.Project; + } + + public static async Task GetCurrentConfigurationName(this IVsProject project) + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + var hProject = project as IVsHierarchy; + hProject.GetGuidProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ProjectIDGuid, out var projGuid); + var bm = await VS.Services.GetSolutionBuildManagerAsync() as IVsSolutionBuildManager5; + bm.FindActiveProjectCfgName(ref projGuid, out var currentConfigName); + return currentConfigName; + } + + public static async Task SaveProject(this IVsProject project, bool force) + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + var solution = await VS.Services.GetSolutionAsync(); + solution.SaveSolutionElement( + force ? (uint)__VSSLNSAVEOPTIONS.SLNSAVEOPT_ForceSave : (uint)__VSSLNSAVEOPTIONS.SLNSAVEOPT_SaveIfDirty, + project as IVsHierarchy, + 0); + } + } + + +} diff --git a/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggle.cs b/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggle.cs new file mode 100644 index 0000000..4920211 --- /dev/null +++ b/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggle.cs @@ -0,0 +1,30 @@ +// ------------------------------------------------------------------------------ +// +// This file was generated by VSIX Synchronizer +// +// ------------------------------------------------------------------------------ +namespace Tvl.VisualStudio.JustMyCodeToggle +{ + using System; + + /// + /// Helper class that exposes all GUIDs used across VS Package. + /// + internal sealed partial class PackageGuids + { + public const string guidJustMyCodeTogglePackageCmdSetString = "4e7a4be2-f4dd-4cc1-a142-e23645e43ecd"; + public static Guid guidJustMyCodeTogglePackageCmdSet = new Guid(guidJustMyCodeTogglePackageCmdSetString); + } + /// + /// Helper class that encapsulates all CommandIDs uses across VS Package. + /// + internal sealed partial class PackageIds + { + public const int JMCToolbarGroup = 0x1101; + public const int JMCMenuController = 0x1102; + public const int JMCNativeCodeBtn = 0x1103; + public const int JMCJustMyCodeBtn = 0x1104; + public const int JMCSymbolLoadBtn = 0x1105; + public const int JMCDisableJitOptmizationsBtn = 0x1107; + } +} \ No newline at end of file diff --git a/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggle.vsct b/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggle.vsct index 38c4037..1664b0f 100644 --- a/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggle.vsct +++ b/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggle.vsct @@ -1,15 +1,62 @@ - + + - - + + + + + + + + + TextChanges + DontCache + TextIsAnchorCommand + + + + + - + + + + + - + - + - - - + + + + + + + diff --git a/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggleConstants.cs b/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggleConstants.cs deleted file mode 100644 index 34258d9..0000000 --- a/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggleConstants.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. -// Licensed under the MIT License. See LICENSE.txt in the project root for license information. - -namespace Tvl.VisualStudio.JustMyCodeToggle -{ - using Guid = System.Guid; - - internal static class JustMyCodeToggleConstants - { - public const string GuidJustMyCodeTogglePackageString = "73702199-D0C5-4863-8203-99D41B34DD2D"; - - public const string GuidJustMyCodeToggleCommandSetString = "B1317E30-CFDA-47CA-90A0-95E894B150A0"; - public static readonly Guid GuidJustMyCodeToggleCommandSet = new Guid("{" + GuidJustMyCodeToggleCommandSetString + "}"); - - public static readonly int CmdidJustMyCodeToggle = 0x0100; - } -} diff --git a/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeTogglePackage.cs b/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeTogglePackage.cs index 45c13ed..7feeff0 100644 --- a/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeTogglePackage.cs +++ b/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeTogglePackage.cs @@ -1,92 +1,67 @@ -// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. namespace Tvl.VisualStudio.JustMyCodeToggle { using System; - using System.ComponentModel.Design; + using System.Runtime.InteropServices; using System.Threading; - using Microsoft; + using Community.VisualStudio.Toolkit; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Shell; - using IMenuCommandService = System.ComponentModel.Design.IMenuCommandService; + using Microsoft.VisualStudio.Shell.Interop; + using Tvl.VisualStudio.JustMyCodeToggle.Managers; using Task = System.Threading.Tasks.Task; - [Guid(JustMyCodeToggleConstants.GuidJustMyCodeTogglePackageString)] + [Guid(PackageGuids.guidJustMyCodeTogglePackageCmdSetString)] [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] [ProvideMenuResource(1000, 1)] + [InstalledProductRegistration(Vsix.Name, Vsix.Description, Vsix.Version)] [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExistsAndFullyLoaded_string, PackageAutoLoadFlags.BackgroundLoad)] - internal class JustMyCodeTogglePackage : AsyncPackage + internal class JustMyCodeTogglePackage : ToolkitPackage { - private readonly OleMenuCommand _command; - public JustMyCodeTogglePackage() - { - var id = new CommandID(JustMyCodeToggleConstants.GuidJustMyCodeToggleCommandSet, JustMyCodeToggleConstants.CmdidJustMyCodeToggle); - EventHandler invokeHandler = HandleInvokeJustMyCodeToggle; - EventHandler changeHandler = HandleChangeJustMyCodeToggle; - EventHandler beforeQueryStatus = HandleBeforeQueryStatusJustMyCodeToggle; - _command = new OleMenuCommand(invokeHandler, changeHandler, beforeQueryStatus, id); - } + protected ProjectEventHandler ProjectEventHandler; - public EnvDTE.DTE ApplicationObject - { - get - { - return GetService(typeof(EnvDTE._DTE)) as EnvDTE.DTE; - } + private StartupProjectManager _startupProjectManager; + private LaunchProfileManager _launchProfileManager; + private SettingsManager _settingsManager; + + internal T RegisterService(T service){ + AddService(typeof(T),(_,_,_) => Task.FromResult(service),promote:true); + return service; } protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) { - await base.InitializeAsync(cancellationToken, progress); + //await Helpers.Init(); + // Initialize managers + _startupProjectManager = RegisterService(new StartupProjectManager()); + _launchProfileManager = RegisterService(new LaunchProfileManager(_startupProjectManager)); + _settingsManager = RegisterService(new SettingsManager((IVsSettingsManager)await VS.Services.GetSettingsManagerAsync())); + + await this.RegisterCommandsAsync(); + await base.InitializeAsync(cancellationToken, progress); await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + var globalSolutionSvc = await VS.GetServiceAsync(); + ProjectEventHandler = new(_startupProjectManager); + globalSolutionSvc.AdviseSolutionEvents(ProjectEventHandler, out _); + var monitor = await VS.GetServiceAsync(); + monitor.AdviseSelectionEvents(ProjectEventHandler, out _); + var bm = await VS.Services.GetSolutionBuildManagerAsync(); + bm.AdviseUpdateSolutionEvents(ProjectEventHandler, out _); - var mcs = (IMenuCommandService)await GetServiceAsync(typeof(IMenuCommandService)); - Assumes.Present(mcs); - mcs.AddCommand(_command); + AfterLoad(); } - private void HandleInvokeJustMyCodeToggle(object sender, EventArgs e) - { - try - { - EnvDTE.Property enableJustMyCode = ApplicationObject.get_Properties("Debugging", "General").Item("EnableJustMyCode"); - if (enableJustMyCode.Value is bool value) - { - enableJustMyCode.Value = !value; - } - } - catch (Exception ex) when (!ErrorHandler.IsCriticalException(ex)) - { - } - } - private void HandleChangeJustMyCodeToggle(object sender, EventArgs e) + private async void AfterLoad() { + await Task.Delay(2000); + _startupProjectManager.CheckStartupProjectChanged(true); } - private void HandleBeforeQueryStatusJustMyCodeToggle(object sender, EventArgs e) - { - try - { - _command.Supported = true; - - EnvDTE.Property enableJustMyCode = ApplicationObject.get_Properties("Debugging", "General").Item("EnableJustMyCode"); - if (enableJustMyCode.Value is bool value) - { - _command.Checked = value; - } - - _command.Enabled = true; - } - catch (Exception ex) when (!ErrorHandler.IsCriticalException(ex)) - { - _command.Supported = false; - _command.Enabled = false; - } - } } } diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Managers/LaunchProfileManager.cs b/Tvl.VisualStudio.JustMyCodeToggle/Managers/LaunchProfileManager.cs new file mode 100644 index 0000000..57226a9 --- /dev/null +++ b/Tvl.VisualStudio.JustMyCodeToggle/Managers/LaunchProfileManager.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.VisualStudio.ProjectSystem; +using Microsoft.VisualStudio.ProjectSystem.Debug; +using Microsoft.VisualStudio.ProjectSystem.VS; +using Microsoft.VisualStudio.Shell; + +namespace Tvl.VisualStudio.JustMyCodeToggle.Managers +{ + /// + /// Handles launch profile configuration and management + /// + public class LaunchProfileManager + { + private readonly StartupProjectManager _startupProjectManager; + + public LaunchProfileManager(StartupProjectManager startupProjectManager) + { + _startupProjectManager = startupProjectManager; + } + + public Task UpdateLaunchProfileENVVars(bool isDelete, params KeyValuePair[] vars) + { + if (!_startupProjectManager.ActiveProjectSupportsCPSProfiles) + return Task.CompletedTask; + + return TryModifyActiveProfile((profile) => + { + if (isDelete) + { + foreach (var var in vars) + profile.EnvironmentVariables.Remove(var.Key); + } + else + { + foreach (var var in vars) + profile.EnvironmentVariables[var.Key] = var.Value; + } + }); + } + + public Task UpdateLaunchProfileSetting(string key, object value) + { + return TryModifyActiveProfile((profile) => profile.OtherSettings[key] = value); + } + + private async Task TryModifyActiveProfile(Action onEditProfile) + { + var launchProvider = await GetLaunchProvider(); + var activeProfile = launchProvider.ActiveProfile?.Name; + if (activeProfile == null) + return; + await launchProvider.TryUpdateProfileAsync(activeProfile, onEditProfile); + } + + private async Task GetLaunchProvider() + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + var proj = await _startupProjectManager.GetStartupProject(); + var provider = proj.AsUnconfiguredProject()?.Services.ActiveConfiguredProjectProvider?.ActiveConfiguredProject?.Services.ExportProvider; + return provider?.GetService(); + } + + public async Task GetLaunchProfile() + { + var launchProvider = await GetLaunchProvider(); + return launchProvider?.ActiveProfile as ILaunchProfile2; + } + } + + +} diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Managers/ProjectEventHandler.cs b/Tvl.VisualStudio.JustMyCodeToggle/Managers/ProjectEventHandler.cs new file mode 100644 index 0000000..238d67d --- /dev/null +++ b/Tvl.VisualStudio.JustMyCodeToggle/Managers/ProjectEventHandler.cs @@ -0,0 +1,63 @@ +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Tvl.VisualStudio.JustMyCodeToggle.Managers +{ + internal class ProjectEventHandler(StartupProjectManager _startupProjectManager) : IVsUpdateSolutionEvents, IVsSolutionEvents, IVsSelectionEvents + { + + public int UpdateSolution_Begin(ref int pfCancelUpdate) => VSConstants.S_OK; + + public int UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand) => VSConstants.S_OK; + + + public int UpdateSolution_StartUpdate(ref int pfCancelUpdate) => VSConstants.S_OK; + + public int UpdateSolution_Cancel() => VSConstants.S_OK; + + public int OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy) + { + _startupProjectManager.CheckStartupProjectChanged(); + return VSConstants.S_OK; + } + + public int OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) => VSConstants.S_OK; + + public int OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel) => VSConstants.S_OK; + + public int OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved) => VSConstants.S_OK; + + public int OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) + { + _startupProjectManager.CheckStartupProjectChanged(true); + return VSConstants.S_OK; + } + + public int OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel) => VSConstants.S_OK; + + public int OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy) => VSConstants.S_OK; + + public int OnAfterOpenSolution(object pUnkReserved, int fNewSolution) + { + _startupProjectManager.CheckStartupProjectChanged(); return VSConstants.S_OK; + } + + public int OnQueryCloseSolution(object pUnkReserved, ref int pfCancel) => VSConstants.S_OK; + + + public int OnBeforeCloseSolution(object pUnkReserved) => VSConstants.S_OK; + + public int OnAfterCloseSolution(object pUnkReserved) => VSConstants.S_OK; + + public int OnSelectionChanged(IVsHierarchy pHierOld, uint itemidOld, IVsMultiItemSelect pMISOld, ISelectionContainer pSCOld, IVsHierarchy pHierNew, uint itemidNew, IVsMultiItemSelect pMISNew, ISelectionContainer pSCNew) => VSConstants.S_OK; + public int OnElementValueChanged(uint elementid, object varValueOld, object varValueNew) + { + if (elementid == (uint)VSConstants.VSSELELEMID.SEID_StartupProject) + _startupProjectManager.CheckStartupProjectChanged(true); + return VSConstants.S_OK; + } + + public int OnCmdUIContextChanged(uint dwCmdUICookie, int fActive) => VSConstants.S_OK; + } +} diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Managers/SettingsManager.cs b/Tvl.VisualStudio.JustMyCodeToggle/Managers/SettingsManager.cs new file mode 100644 index 0000000..80781f5 --- /dev/null +++ b/Tvl.VisualStudio.JustMyCodeToggle/Managers/SettingsManager.cs @@ -0,0 +1,91 @@ +using System; +using System.ComponentModel; +using Microsoft.VisualStudio.Settings; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Tvl.VisualStudio.JustMyCodeToggle.Managers +{ + /// + /// Manages Visual Studio settings storage and retrieval + /// + public class SettingsManager + { + private readonly IVsSettingsManager _settingsManager; + + public SettingsManager(IVsSettingsManager settingsManager) + { + _settingsManager = settingsManager; + } + + public T GetSetting(string collectionPath, string propertyName) + { + var store = GetSettingsStore(); + object ret = null; + int res = 0; + + if (typeof(T) == typeof(string)) + { + res = store.GetString(collectionPath, propertyName, out var val); + ret = val; + } + else if (typeof(T) == typeof(int)) + { + res = store.GetInt(collectionPath, propertyName, out var val); + ret = val; + } + else if (typeof(T) == typeof(bool)) + { + res = store.GetBool(collectionPath, propertyName, out var val); + ret = val; + } + else + throw new NotImplementedException($"Type {typeof(T)} is not supported for settings"); + + return res != 0 ? default : (T)ret; + } + + public void SetSetting(string collectionPath, string propertyName, T value) + { + var store = (IVsWritableSettingsStore)GetSettingsStore(true); + int res = 0; + + switch (value) + { + case int v: + res = store.SetInt(collectionPath, propertyName, v); + break; + case bool v: + res = store.SetBool(collectionPath, propertyName, v ? 1 : 0); + break; + case string v: + res = store.SetString(collectionPath, propertyName, v); + break; + default: + throw new NotImplementedException($"Type {typeof(T)} is not supported for settings"); + } + + if (res != 0) + throw new Win32Exception(); + } + + private IVsSettingsStore GetSettingsStore(bool writable = false) + { + IVsSettingsStore ret; + int result; + + if (!writable) + result = _settingsManager.GetReadOnlySettingsStore((uint)SettingsScope.UserSettings, out ret); + else + { + result = _settingsManager.GetWritableSettingsStore((uint)SettingsScope.UserSettings, out var wret); + ret = wret; + } + + if (result != 0) + throw new Win32Exception(); + return ret; + } + } + + +} diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Managers/StartupProjectManager.cs b/Tvl.VisualStudio.JustMyCodeToggle/Managers/StartupProjectManager.cs new file mode 100644 index 0000000..d102ae2 --- /dev/null +++ b/Tvl.VisualStudio.JustMyCodeToggle/Managers/StartupProjectManager.cs @@ -0,0 +1,62 @@ +using System; +using System.Threading.Tasks; +using Community.VisualStudio.Toolkit; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Tvl.VisualStudio.JustMyCodeToggle.Managers +{ + /// + /// Manages startup project state and notifications + /// + public class StartupProjectManager + { + public bool ActiveProjectSupportsCPSProfiles { get; private set; } + public event EventHandler StartupProjectChanged; + private string lastStartupProject; + public bool HasStartupProject => !string.IsNullOrWhiteSpace(lastStartupProject); + + public async Task GetStartupProject() + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + try + { + var bm = await VS.Services.GetSolutionBuildManagerAsync(); + if (bm?.get_StartupProject(out var hstartupProject) == VSConstants.S_OK && hstartupProject != null) + return (IVsProject)hstartupProject; + } + catch { } // Some project types can throw with get_startupproject + return null; + } + + public async void CheckStartupProjectChanged(bool forceEvent = false, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + try + { + await Task.Delay(50); // Make sure it has actually updated first + var project = await GetStartupProject(); + var hProject = project as IVsHierarchy; + + string startupProject = null; + if (project != null) + { + if (project.GetMkDocument((uint)VSConstants.VSITEMID.Root, out startupProject) != VSConstants.S_OK) + if (hProject.GetCanonicalName((uint)VSConstants.VSITEMID.Root, out startupProject) != VSConstants.S_OK) + startupProject = null; + } + + var oldVal = lastStartupProject; + lastStartupProject = startupProject; + ActiveProjectSupportsCPSProfiles = hProject?.IsCapabilityMatch("LaunchProfiles") == true; + + if (oldVal != lastStartupProject || forceEvent) + StartupProjectChanged?.Invoke(null, EventArgs.Empty); + } + catch { } + } + } + + +} diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Tvl.VisualStudio.JustMyCodeToggle.csproj b/Tvl.VisualStudio.JustMyCodeToggle/Tvl.VisualStudio.JustMyCodeToggle.csproj index 6bdfd1c..68c9286 100644 --- a/Tvl.VisualStudio.JustMyCodeToggle/Tvl.VisualStudio.JustMyCodeToggle.csproj +++ b/Tvl.VisualStudio.JustMyCodeToggle/Tvl.VisualStudio.JustMyCodeToggle.csproj @@ -1,12 +1,13 @@ - net472 + net480 Properties Adds the Just My Code command button to Visual Studio. Tunnel Vision Laboratories, LLC Copyright © Sam Harwell 2017 1.4.0.0 + preview 1.4.0.0 1.4.0-dev true @@ -36,17 +37,34 @@ + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + compile; build; native; contentfiles; analyzers; buildtransitive + + - - + + Designer + VsixManifestGenerator @@ -54,6 +72,14 @@ LICENSE.txt true + + VsctGenerator + JustMyCodeToggle.cs + + + VsixManifestGenerator + source.extension.cs + @@ -61,6 +87,19 @@ Designer + + + True + True + JustMyCodeToggle.vsct + + + True + True + source.extension.vsixmanifest + + + VSPackage.resources @@ -69,4 +108,12 @@ - \ No newline at end of file + + $(GetVsixSourceItemsDependsOn);IncludeNuGetResolvedAssets + + + + + + + diff --git a/Tvl.VisualStudio.JustMyCodeToggle/VSPackage.resx b/Tvl.VisualStudio.JustMyCodeToggle/VSPackage.resx index 41903cb..8967019 100644 --- a/Tvl.VisualStudio.JustMyCodeToggle/VSPackage.resx +++ b/Tvl.VisualStudio.JustMyCodeToggle/VSPackage.resx @@ -90,7 +90,7 @@ text/microsoft-resx - 1.3 + 1.4 System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 diff --git a/Tvl.VisualStudio.JustMyCodeToggle/source.extension.cs b/Tvl.VisualStudio.JustMyCodeToggle/source.extension.cs new file mode 100644 index 0000000..b8011b3 --- /dev/null +++ b/Tvl.VisualStudio.JustMyCodeToggle/source.extension.cs @@ -0,0 +1,18 @@ +// ------------------------------------------------------------------------------ +// +// This file was generated by VSIX Synchronizer +// +// ------------------------------------------------------------------------------ +namespace Tvl.VisualStudio.JustMyCodeToggle +{ + internal sealed partial class Vsix + { + public const string Id = "Tvl.VisualStudio.JustMyCodeToggle.0E2F0645-E87E-433C-B82B-C89EAE52A0FD"; + public const string Name = "Just My Code Toggle"; + public const string Description = @"Adds the Just My Code command button to Visual Studio."; + public const string Language = "en-US"; + public const string Version = "1.4.0"; + public const string Author = "Sam Harwell"; + public const string Tags = "debugging, just my code"; + } +} diff --git a/doc/toolbar.png b/doc/toolbar.png index 9dc6545f901d3c17aba96265013fb0b7dbc755ec..7bee6d0e5d60a3f8ab7ff1812ace989dcf403d03 100644 GIT binary patch literal 8027 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 PE0U6&s%*9N+mQbUQZqvt From d43107e779a0fc42c3d22f7c1caab36371facc77 Mon Sep 17 00:00:00 2001 From: Mitch Capper Date: Wed, 16 Apr 2025 10:20:29 -0700 Subject: [PATCH 4/8] Fixed Native debugging state to be correct --- .../Commands/NativeDebuggingToggleCmd.cs | 1 + .../Tvl.VisualStudio.JustMyCodeToggle.csproj | 6 +++--- Tvl.VisualStudio.JustMyCodeToggle/source.extension.cs | 3 ++- .../source.extension.vsixmanifest | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Commands/NativeDebuggingToggleCmd.cs b/Tvl.VisualStudio.JustMyCodeToggle/Commands/NativeDebuggingToggleCmd.cs index 7ceda5a..d5c0cbe 100644 --- a/Tvl.VisualStudio.JustMyCodeToggle/Commands/NativeDebuggingToggleCmd.cs +++ b/Tvl.VisualStudio.JustMyCodeToggle/Commands/NativeDebuggingToggleCmd.cs @@ -113,6 +113,7 @@ protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) { await UpdateCheckedState(); await SetValue(!Command.Checked); + await UpdateCheckedState(); await base.ExecuteAsync(e); } } diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Tvl.VisualStudio.JustMyCodeToggle.csproj b/Tvl.VisualStudio.JustMyCodeToggle/Tvl.VisualStudio.JustMyCodeToggle.csproj index 68c9286..eb979fa 100644 --- a/Tvl.VisualStudio.JustMyCodeToggle/Tvl.VisualStudio.JustMyCodeToggle.csproj +++ b/Tvl.VisualStudio.JustMyCodeToggle/Tvl.VisualStudio.JustMyCodeToggle.csproj @@ -6,10 +6,10 @@ Adds the Just My Code command button to Visual Studio. Tunnel Vision Laboratories, LLC Copyright © Sam Harwell 2017 - 1.4.0.0 + 1.4.1.0 preview - 1.4.0.0 - 1.4.0-dev + 1.4.1.0 + 1.4.1-dev true true true diff --git a/Tvl.VisualStudio.JustMyCodeToggle/source.extension.cs b/Tvl.VisualStudio.JustMyCodeToggle/source.extension.cs index b8011b3..2e5282c 100644 --- a/Tvl.VisualStudio.JustMyCodeToggle/source.extension.cs +++ b/Tvl.VisualStudio.JustMyCodeToggle/source.extension.cs @@ -11,8 +11,9 @@ internal sealed partial class Vsix public const string Name = "Just My Code Toggle"; public const string Description = @"Adds the Just My Code command button to Visual Studio."; public const string Language = "en-US"; - public const string Version = "1.4.0"; + public const string Version = "1.4.1"; public const string Author = "Sam Harwell"; public const string Tags = "debugging, just my code"; + public const bool IsPreview = false; } } diff --git a/Tvl.VisualStudio.JustMyCodeToggle/source.extension.vsixmanifest b/Tvl.VisualStudio.JustMyCodeToggle/source.extension.vsixmanifest index c124e8e..94d4434 100644 --- a/Tvl.VisualStudio.JustMyCodeToggle/source.extension.vsixmanifest +++ b/Tvl.VisualStudio.JustMyCodeToggle/source.extension.vsixmanifest @@ -1,7 +1,7 @@ - + Just My Code Toggle Adds the Just My Code command button to Visual Studio. https://github.com/tunnelvisionlabs/JustMyCodeToggle From a576898212fc284cea366e765981b60a56648150 Mon Sep 17 00:00:00 2001 From: Mitch Capper Date: Sat, 1 Nov 2025 15:22:30 -0700 Subject: [PATCH 5/8] Refactor to better support more complexity across VS versions and faster updates Work to support VS2026, full symbol load just launches the options->symbols page. --- .editorconfig | 4 + JustMyCodeToggle.sln | 4 +- .../Commands/CommandSetters.cs | 242 ++++++++++++++++++ .../Commands/FullSymbolLoadToggleCmd.cs | 115 ++++++++- .../Commands/JitOptimizationsToggleCmd.cs | 76 +++--- .../Commands/JustMyCodeToggleCmd.cs | 42 +-- .../Commands/NativeDebuggingToggleCmd.cs | 116 ++------- .../Commands/OurBaseSettingCmd.cs | 195 +++++++++++--- .../ExtensibilityExtensionEntrypoint.cs | 41 +++ Tvl.VisualStudio.JustMyCodeToggle/Helpers.cs | 27 ++ .../JustMyCodeToggle.cs | 11 +- .../JustMyCodeToggle.vsct | 14 +- .../JustMyCodeTogglePackage.cs | 30 ++- .../Managers/DebuggerServiceManager.cs | 60 +++++ .../Managers/DteManager.cs | 136 ++++++++++ .../Managers/ExtensibilitySettingManager.cs | 39 +++ .../Managers/LaunchProfileManager.cs | 9 +- ...ingsManager.cs => SettingsStoreManager.cs} | 12 +- .../Managers/StartupProjectManager.cs | 21 +- .../Tvl.VisualStudio.JustMyCodeToggle.csproj | 47 ++-- .../VSPackage.resx | 101 -------- .../source.extension.cs | 5 +- .../source.extension.vsixmanifest | 11 +- 23 files changed, 996 insertions(+), 362 deletions(-) create mode 100644 Tvl.VisualStudio.JustMyCodeToggle/Commands/CommandSetters.cs create mode 100644 Tvl.VisualStudio.JustMyCodeToggle/ExtensibilityExtensionEntrypoint.cs create mode 100644 Tvl.VisualStudio.JustMyCodeToggle/Managers/DebuggerServiceManager.cs create mode 100644 Tvl.VisualStudio.JustMyCodeToggle/Managers/DteManager.cs create mode 100644 Tvl.VisualStudio.JustMyCodeToggle/Managers/ExtensibilitySettingManager.cs rename Tvl.VisualStudio.JustMyCodeToggle/Managers/{SettingsManager.cs => SettingsStoreManager.cs} (91%) delete mode 100644 Tvl.VisualStudio.JustMyCodeToggle/VSPackage.resx diff --git a/.editorconfig b/.editorconfig index 0507db4..32566aa 100644 --- a/.editorconfig +++ b/.editorconfig @@ -18,6 +18,10 @@ insert_final_newline = true # Organize using directives dotnet_sort_system_directives_first = true dotnet_separate_import_directive_groups = false +# VSEXTPREVIEW_SETTINGS: Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. +dotnet_diagnostic.VSEXTPREVIEW_SETTINGS.severity = none + + # Code style defaults csharp_new_line_before_open_brace = all diff --git a/JustMyCodeToggle.sln b/JustMyCodeToggle.sln index e9c4792..3125f0c 100644 --- a/JustMyCodeToggle.sln +++ b/JustMyCodeToggle.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.6.33417.168 +# Visual Studio Version 18 +VisualStudioVersion = 18.0.11123.170 d18.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tvl.VisualStudio.JustMyCodeToggle", "Tvl.VisualStudio.JustMyCodeToggle\Tvl.VisualStudio.JustMyCodeToggle.csproj", "{10CAD392-E083-41EB-92C9-B5F3142FA9F8}" EndProject diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Commands/CommandSetters.cs b/Tvl.VisualStudio.JustMyCodeToggle/Commands/CommandSetters.cs new file mode 100644 index 0000000..e4e55ba --- /dev/null +++ b/Tvl.VisualStudio.JustMyCodeToggle/Commands/CommandSetters.cs @@ -0,0 +1,242 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using Tvl.VisualStudio.JustMyCodeToggle.Managers; +using kvp = System.Collections.Generic.KeyValuePair; +namespace Tvl.VisualStudio.JustMyCodeToggle.Commands +{ + internal class ExtensibilityBoolToValSettingSetter : ExtensibilitySettingSetter, ISetSettingInterface{ + private SETTING_TYPE trueVal; + private SETTING_TYPE falseVal; + + public ExtensibilityBoolToValSettingSetter(string propertyName, SETTING_TYPE trueVal, SETTING_TYPE falseVal) : base(propertyName) + { + this.trueVal = trueVal; + this.falseVal = falseVal; + } + Task ISetSettingInterface.SetSetting(bool val) + { + return this.SetSetting(val ? trueVal : falseVal); + } + + async Task ISetSettingInterface.GetSetting() + { + return (await this.GetSetting()).Equals(trueVal); + } + } + internal class ExtensibilitySettingSetter : ISetSettingInterface + { + public Task GetSetting() => _settingsManager.Value.GetSetting(propertyName); + public Task SetSetting(SETTING_TYPE val) + { + return _settingsManager.Value.SetSetting(propertyName, val); + } + protected readonly string propertyName; + protected Lazy _settingsManager; + public ExtensibilitySettingSetter(string propertyName) + { + this.propertyName = propertyName; + _settingsManager = new(() => JustMyCodeTogglePackage.instance.GetTypedService()); + } + } + /// + /// for bool values use strings true/false + /// + internal class BuildStoreSetter : ISetSettingInterface + { + public async Task GetSetting() + { + if (!await SyncToCurStartProj()) + return default; + + var buildStorage = curStartupProject as IVsBuildPropertyStorage; + + var cmdres = buildStorage.GetPropertyValue(propertyName, curConfigName, (uint)_PersistStorageType.PST_USER_FILE, out var existing); + if (cmdres != VSConstants.S_OK || String.IsNullOrWhiteSpace(existing)) + buildStorage.GetPropertyValue(propertyName, curConfigName, (uint)_PersistStorageType.PST_PROJECT_FILE, out existing);//user file will override project file but if user file is blank fallback to project + + + return strToVal(existing); + + + } + public async Task SyncToCurStartProj() + { + curStartupProject = await _settingsManager.Value.GetStartupProject(); + curConfigName = curStartupProject == null ? null : await curStartupProject.GetCurrentConfigurationName(); + return curStartupProject != null && curConfigName != null; + } + protected virtual string valToStr(SETTING_TYPE val) + { + if (val is bool bval) + return bval ? "true" : "false"; + else if (val is int ival) + return ival.ToString(); + return val.ToString(); + } + protected virtual SETTING_TYPE strToVal(string str) + { + if (typeof(SETTING_TYPE) == typeof(bool)) + { + if (Boolean.TryParse(str, out var bval)) + return (SETTING_TYPE)(object)bval; + else + return default; + + } + else if (typeof(SETTING_TYPE) == typeof(int)) + { + if (Int32.TryParse(str, out var ival)) + return (SETTING_TYPE)(object)ival; + else + return default; + } + return (SETTING_TYPE)(object)str; + } + public async Task SetSetting(SETTING_TYPE _val) + { + + if (!await SyncToCurStartProj()) + return; + var val = valToStr(_val); + + var buildStorage = curStartupProject as IVsBuildPropertyStorage; + var cmdres = buildStorage.SetPropertyValue(propertyName, curConfigName, (uint)_PersistStorageType.PST_USER_FILE, val); + + await curStartupProject.SaveProject(force: true);//must use force or else it is as if the change wasn't detected. Also cannot figure out how to just save the user file I believe we must save both + //note while this changes our .user file it doesn't update the active native debugging plan for launch + + var dteProj = await curStartupProject.GetDTEProjectFromIvsProject(); + try + { + + var itm = dteProj.ConfigurationManager?.ActiveConfiguration?.Properties?.Item(propertyName); //this updates the actual launch project in memory object + if (itm != null) + itm.Value = val; + + } + catch { } + + + + } + + + protected readonly string propertyName; + protected Lazy _settingsManager; + private IVsProject curStartupProject; + private string curConfigName; + + public BuildStoreSetter(string propertyName) + { + this.propertyName = propertyName; + _settingsManager = new(JustMyCodeTogglePackage.instance.GetTypedService); + } + } + internal class DteStoreSetter : ISetSettingInterface + { + public Task GetSetting() => Task.FromResult(_settingsManager.Value.GetDteSetting(category, page, propertyName)); + + public Task SetSetting(SETTING_TYPE val) + { + _settingsManager.Value.SetDteSetting(category, page, propertyName, val); + return Task.CompletedTask; + } + + protected readonly string category; + private readonly string page; + protected readonly string propertyName; + protected Lazy _settingsManager; + + public DteStoreSetter(string category, string page, string propertyName) + { + + this.category = category; + this.page = page; + this.propertyName = propertyName; + _settingsManager = new(JustMyCodeTogglePackage.instance.GetTypedService); + } + } + internal class StartupProfileEnvVarSetter : StartupProfileSetter + { + private string deleteOnVal; + override public async Task GetSetting() + { + return await _settingsManager.Value.GetProfileEnvVar(propertyName) ?? deleteOnVal; + } + + public virtual Task SetSetting(string val) => _settingsManager.Value.UpdateLaunchProfileENVVars(val == deleteOnVal, new kvp(propertyName, val)); + public virtual Task SetSettingAddl(string val, params kvp[] addl) => _settingsManager.Value.UpdateLaunchProfileENVVars(val == deleteOnVal, [new kvp(propertyName, val), .. addl]); + public StartupProfileEnvVarSetter(string propertyName, string deleteOnVal) : base(propertyName) + { + this.deleteOnVal = deleteOnVal; + } + } + internal class StartupProfileSetter : ISetSettingInterface + { + public virtual async Task GetSetting() + { + var launchProfile = await _settingsManager.Value.GetLaunchProfile(); + if (launchProfile == null) + { + Debug.WriteLine($"JMC: in theory project supports CPS profiles but no launch profile found??"); + return default; + } + else + { + var curVal = launchProfile.OtherSettings.FirstOrDefault(a => a.Key.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)); + return (SETTING_TYPE)curVal.Value; + } + } + + public virtual Task SetSetting(SETTING_TYPE val) => _settingsManager.Value.UpdateLaunchProfileSetting(propertyName, val); + + protected readonly string propertyName; + protected Lazy _settingsManager; + protected Lazy _startManager; + + public StartupProjectManager StartManager => _startManager.Value; + public bool SupportsLaunchProfiles => _settingsManager.Value.SupportsLaunchProfiles; + + public StartupProfileSetter(string propertyName) + { + this.propertyName = propertyName; + _settingsManager = new(JustMyCodeTogglePackage.instance.GetTypedService); + _startManager = new(JustMyCodeTogglePackage.instance.GetTypedService); + } + } + + internal class SettingsStoreSetter : ISetSettingInterface + { + public Task GetSetting() => Task.FromResult(_settingsManager.Value.GetSetting(collectionPath, propertyName)); + + public Task SetSetting(SETTING_TYPE val) + { + _settingsManager.Value.SetSetting(collectionPath, propertyName, val); + return Task.CompletedTask; + } + + protected readonly string collectionPath; + protected readonly string propertyName; + protected Lazy _settingsManager; + + public SettingsStoreSetter(string collectionPath, string propertyName) + { + + this.collectionPath = collectionPath; + this.propertyName = propertyName; + _settingsManager = new(JustMyCodeTogglePackage.instance.GetTypedService); + } + + } + internal interface ISetSettingInterface + { + Task SetSetting(SETTING_TYPE val); + Task GetSetting(); + } +} diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Commands/FullSymbolLoadToggleCmd.cs b/Tvl.VisualStudio.JustMyCodeToggle/Commands/FullSymbolLoadToggleCmd.cs index 6315230..4cc5db6 100644 --- a/Tvl.VisualStudio.JustMyCodeToggle/Commands/FullSymbolLoadToggleCmd.cs +++ b/Tvl.VisualStudio.JustMyCodeToggle/Commands/FullSymbolLoadToggleCmd.cs @@ -1,11 +1,120 @@ +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; using Community.VisualStudio.Toolkit; +using EnvDTE; +using Microsoft.VisualStudio.Extensibility; +using Microsoft.VisualStudio.Extensibility.Commands; +using Microsoft.VisualStudio.Shell; +using Tvl.VisualStudio.JustMyCodeToggle.Managers; namespace Tvl.VisualStudio.JustMyCodeToggle.Commands { + internal class DebuggerSymbolLoadSetter : ISetSettingInterface - //do we only do the exclude list but load all others or do we automatically decide what to load - [Command(PackageIds.JMCSymbolLoadBtn)] - internal class FullSymbolLoadToggleCmd() : ToggleSettingCmd("Debugger", "SymbolUseExcludeList", 1, 0) { + private DebuggerServiceManager _settingsManager; + + public DebuggerSymbolLoadSetter() + { + _settingsManager = JustMyCodeTogglePackage.instance.GetTypedService(); + } + public Task GetSetting() => Task.FromResult(_settingsManager.GetLoadAllModules()); + + public async Task SetSetting(bool val) + { + await _settingsManager.SetLoadAllModulesAsync(val); + } + } + [Command(PackageGuids.guidJustMyCodeTogglePackageCmdSetString,PackageIds.JMCSymbolLoadBtn)] + internal class FullSymbolLoadBtn : OurOLEButton + { + + protected override void BeforeQueryStatus(EventArgs e) + { + var useCmd = ! ProjectExtensions.IsVS2026; + useCmd = true; + + this.Command.Visible = useCmd; + + //this.Command.Visible = true; + + //this.Command.Enabled = useCmd; + //this.Command.Supported = useCmd; + + + //this.Command.Enabled = useCmd; + //this.Command.Supported = useCmd; + + base.BeforeQueryStatus(e); + } + protected override Task InitializeCompletedAsync() + { + + return base.InitializeCompletedAsync();; + } + + protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + var dte = JustMyCodeTogglePackage.GetGlobalService(typeof(DTE)) as DTE; + dte.ExecuteCommand("Tools.Options", "Debugging.Symbols"); + + + //return base.ExecuteAsync(e); + } } + + [VisualStudioContribution] + internal class FullSymbolLoadExtensibilityBtn : ToggleCommand // OurExtensibilityToggleButton + { + + public override Task InitializeAsync(CancellationToken cancellationToken) + { + //JustMyCodeTogglePackage.instance.RegisterService(new ExtensibilitySettingManager(Extensibility)); + return base.InitializeAsync(cancellationToken); + } + public override CommandConfiguration CommandConfiguration => new("Toggle Full Symbol Loading2") + { + // Use this object initializer to set optional parameters for the command. The required parameter, + // displayName, is set above. DisplayName is localized and references an entry in .vsextension\string-resources.json. + Icon = new(ImageMoniker.KnownValues.Extension, IconSettings.IconAndText), + Flags = CommandFlags.CanToggle, + + //VsctCommandMapping = new VsctId(new(PackageGuids.guidJustMyCodeTogglePackageCmdSetString),PackageIds.JMCSymbolLoadBtn), + Placements = [ + CommandPlacement.KnownPlacements.ExtensionsMenu, + //CommandPlacement.VsctParent(new(PackageGuids.guidJustMyCodeTogglePackageCmdSetString),PackageIds.JMCToolbarGroup, priority: 0x124), + + ], + }; + + public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken) + { + Debug.WriteLine("DONE WITH IT"); + } + } + + internal class FullSymbolLoadCmd : ToggleSettingDynamicSetterCmd + { + private SettingsStoreSetter settingStoreSetter; + private ExtensibilityBoolToValSettingSetter unifiedSetter; + + public FullSymbolLoadCmd() : base(true, false) + { + this.settingStoreSetter = new("Debugger", "SymbolUseExcludeList"); + this.unifiedSetter = new ExtensibilityBoolToValSettingSetter(@"debugging.symbols.load.moduleFilterMode", "loadAllButExcluded", "loadOnlyIncluded"); + + } + + protected override Task[]> GetSetters() + { + ISetSettingInterface setter = ProjectExtensions.IsVS2026 && ExtensibilitySettingManager.Inited ? unifiedSetter : settingStoreSetter; + return Task.FromResult[]>([setter]); + } + } + + } diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Commands/JitOptimizationsToggleCmd.cs b/Tvl.VisualStudio.JustMyCodeToggle/Commands/JitOptimizationsToggleCmd.cs index 0ee1caf..9786e41 100644 --- a/Tvl.VisualStudio.JustMyCodeToggle/Commands/JitOptimizationsToggleCmd.cs +++ b/Tvl.VisualStudio.JustMyCodeToggle/Commands/JitOptimizationsToggleCmd.cs @@ -1,79 +1,61 @@ using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Community.VisualStudio.Toolkit; -using Microsoft.VisualStudio.Shell; -using Tvl.VisualStudio.JustMyCodeToggle.Managers; namespace Tvl.VisualStudio.JustMyCodeToggle.Commands { - [Command(PackageIds.JMCDisableJitOptmizationsBtn)] - internal class JitOptimizationsToggleCmd : BaseCommand + [Command(PackageGuids.guidJustMyCodeTogglePackageCmdSetString,PackageIds.JMCDisableJitOptmizationsBtn)] + internal class JitOptimizationsBtn : OurOLEButton { + } - private StartupProjectManager _startupProjectManager; - private LaunchProfileManager _launchProfileManager; + internal class JitOptimizationsCommand : ToggleSettingCmd, ISetSettingInterface + { + protected StartupProfileEnvVarSetter profileSetter; - public JitOptimizationsToggleCmd() + private const string primaryCheckVar = "COMPlus_ZapDisable"; + public JitOptimizationsCommand() : base(null, true, false) { + setter = this; + profileSetter = new(primaryCheckVar, "0"); + var prefixes = new string[] { "DOTNET_", "COMPlus_" }; + var envs = new List>(); var baseVars = new Dictionary(){ {"ZapDisable","1" }, {"ReadyToRun","0" }, {"TieredCompilation","0" }, {"JITMinOpts","1" }}; - var envs = new List>(); - string[] prefixes = ["DOTNET_", "COMPlus_"]; foreach (var kvp in baseVars) { foreach (var prefix in prefixes) - envs.Add(new(prefix + kvp.Key, kvp.Value)); + { + var key = prefix + kvp.Key; + if (key != primaryCheckVar) + envs.Add(new(key, kvp.Value)); + } } - ENVVars = envs.ToArray(); + this.ENVVars = envs.ToArray(); + profileSetter.StartManager.StartupProjectChanged += (_,_) => this.SyncCheckedToCurVal(); + profileSetter.StartManager.ActiveProjectSupportsCPSProfilesChanged += (_, _) => SyncEnabled(); } - private KeyValuePair[] ENVVars; - protected override async Task InitializeCompletedAsync() - { - await base.InitializeCompletedAsync(); - _startupProjectManager = Package.GetTypedService(); - _launchProfileManager = Package.GetTypedService(); - await UpdateCheckedState(); - _startupProjectManager.StartupProjectChanged += (_, _) => UpdateCheckedState(); + + private void SyncEnabled() + { + Native.SetEnabled( profileSetter.StartManager.ActiveProjectSupportsCPSProfiles); } - protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) - { - await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - await SetValue(!Command.Checked); - await UpdateCheckedState(); - } + public KeyValuePair[] ENVVars { get; } - private async Task SetValue(bool value) + public async Task GetSetting() { - if (!_startupProjectManager.ActiveProjectSupportsCPSProfiles) - return;//shouldn't get here - await _launchProfileManager.UpdateLaunchProfileENVVars(isDelete: !value, ENVVars.ToArray()); - + return await profileSetter.GetSetting() == "1"; } - private async Task UpdateCheckedState() + public Task SetSetting(bool val) { - Command.Enabled = _startupProjectManager.HasStartupProject && _startupProjectManager.ActiveProjectSupportsCPSProfiles; - if (!Command.Enabled) - return; - var launchProfile = await _launchProfileManager.GetLaunchProfile(); - if (launchProfile == null) - { - Command.Enabled = false; - return; - } - - var envVars = launchProfile.EnvironmentVariables; - var zapDisable = launchProfile?.EnvironmentVariables.FirstOrDefault(a => a.Key == "COMPlus_ZapDisable"); - - Command.Checked = zapDisable?.Value == "1"; - + return profileSetter.SetSettingAddl(val ? "1" : "0", this.ENVVars); } } } diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Commands/JustMyCodeToggleCmd.cs b/Tvl.VisualStudio.JustMyCodeToggle/Commands/JustMyCodeToggleCmd.cs index f8db3ef..ee68f28 100644 --- a/Tvl.VisualStudio.JustMyCodeToggle/Commands/JustMyCodeToggleCmd.cs +++ b/Tvl.VisualStudio.JustMyCodeToggle/Commands/JustMyCodeToggleCmd.cs @@ -5,40 +5,40 @@ namespace Tvl.VisualStudio.JustMyCodeToggle.Commands { - [Command(PackageIds.JMCMenuController)] + [Command(PackageGuids.guidJustMyCodeTogglePackageCmdSetString,PackageIds.JMCMenuController)] internal class MenuBtnCmd : BaseCommand { - public MenuBtnCmd() - { - instance = this; - } + public MenuBtnCmd() => instance = this; internal static MenuBtnCmd instance; + } + [Command(PackageGuids.guidJustMyCodeTogglePackageCmdSetString,PackageIds.JMCJustMyCodeBtn)] + internal class JustMyCodeBtn : OurOLEButton + { } - [Command(PackageIds.JMCJustMyCodeBtn)] - internal class JustMyCodeToggleCmd() : ToggleSettingCmd("Debugger", "JustMyCode", 1, 0) + internal class JustMyCodeCmd : ToggleSettingDynamicSetterCmd { - protected override void SetCheckedToMatch(int cur) + private SettingsStoreSetter settingStoreSetter; + private DteStoreSetter dteSetter; + + protected override Task[]> GetSetters() + { + ISetSettingInterface setter = ProjectExtensions.IsVS2026 ? dteSetter : settingStoreSetter; + return Task.FromResult[]>([setter]); + } + override protected void SetCheckedToMatch(bool cur) { base.SetCheckedToMatch(cur); if (MenuBtnCmd.instance != null) - MenuBtnCmd.instance.Command.Checked = Command.Checked; //keep menu in sync with us + MenuBtnCmd.instance.Command.Checked = Native.IsChecked(); //keep menu in sync with us } - protected override Task InitializeCompletedAsync() + + public JustMyCodeCmd() : base(true, false) { - DelaySetChecked(); - return base.InitializeCompletedAsync(); + this.settingStoreSetter = new("Debugger", "JustMyCode"); + this.dteSetter = new("Debugging", "General", "EnableJustMyCode"); } - /// - /// to set the related command items not initalized before - /// - private async void DelaySetChecked() - { - await Task.Delay(1000); - //await JoinableTaskFactory.SwitchToMainThreadAsync(); - SetCheckedToMatch(Command.Checked ? 1 : 0); - } } } diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Commands/NativeDebuggingToggleCmd.cs b/Tvl.VisualStudio.JustMyCodeToggle/Commands/NativeDebuggingToggleCmd.cs index d5c0cbe..d0115c0 100644 --- a/Tvl.VisualStudio.JustMyCodeToggle/Commands/NativeDebuggingToggleCmd.cs +++ b/Tvl.VisualStudio.JustMyCodeToggle/Commands/NativeDebuggingToggleCmd.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading.Tasks; @@ -9,112 +10,29 @@ using Tvl.VisualStudio.JustMyCodeToggle.Managers; namespace Tvl.VisualStudio.JustMyCodeToggle.Commands { - [Command(PackageIds.JMCNativeCodeBtn)] - internal class NativeDebuggingToggleCmd : BaseCommand - { - protected const string cpsProfileStr = "nativeDebugging"; - protected const string nonCpsPropertyStr = "EnableUnmanagedDebugging"; - - private StartupProjectManager _startupProjectManager; - private LaunchProfileManager _launchProfileManager; - - public NativeDebuggingToggleCmd() - { - - } + [Command(PackageGuids.guidJustMyCodeTogglePackageCmdSetString,PackageIds.JMCNativeCodeBtn)] + internal class OleNativeDebuggingBtn : OurOLEButton { - protected override async Task InitializeCompletedAsync() + protected override Task ExecuteAsync(OleMenuCmdEventArgs e) { - _startupProjectManager = Package.GetTypedService(); - _launchProfileManager = Package.GetTypedService(); - await UpdateCheckedState(); - _startupProjectManager.StartupProjectChanged += (_, _) => UpdateCheckedState(); - await base.InitializeCompletedAsync(); - } - - protected async Task SetValue(bool value) - { - await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - if (_startupProjectManager.ActiveProjectSupportsCPSProfiles) - { - await _launchProfileManager.UpdateLaunchProfileSetting(cpsProfileStr, value); - } - else - { - var startupProject = await _startupProjectManager.GetStartupProject(); - var configName = startupProject == null ? null : await startupProject.GetCurrentConfigurationName(); - Command.Enabled = startupProject != null && configName != null; - if (Command.Enabled) - { - - var buildStorage = startupProject as IVsBuildPropertyStorage; - var cmdres = buildStorage.SetPropertyValue(nonCpsPropertyStr, configName, (uint)_PersistStorageType.PST_USER_FILE, value ? "true" : "false"); - - await startupProject.SaveProject(force: true);//must use force or else it is as if the change wasn't detected. Also cannot figure out how to just save the user file I believe we must save both - //note while this changes our .user file it doesn't update the active native debugging plan for launch - - var dteProj = await startupProject.GetDTEProjectFromIvsProject(); - try - { - - var itm = dteProj.ConfigurationManager?.ActiveConfiguration?.Properties?.Item("EnableUnmanagedDebugging"); //this updates the actual launch project in memory object - if (itm != null) - itm.Value = value ? "true" : "false"; - - } - catch { } - - } - } + return base.ExecuteAsync(e); } + } + internal class NativeDebuggingCmd : ToggleSettingDynamicSetterCmd + { + private StartupProfileSetter profileSetter; + private BuildStoreSetter storageSetter; - private async Task UpdateCheckedState() + public NativeDebuggingCmd() : base(true, false) { - Command.Enabled = _startupProjectManager.HasStartupProject; - if (!Command.Enabled) - return; - - await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - if (_startupProjectManager.ActiveProjectSupportsCPSProfiles) - { - var launchProfile = await _launchProfileManager.GetLaunchProfile(); - if (launchProfile == null) - { - Debug.WriteLine($"JMC NativeDebuggingToggleCmd: in theory project supports CPS profiles but no launch profile found??"); - Command.Enabled = false; - } - else - { - var curVal = launchProfile.OtherSettings.FirstOrDefault(a => a.Key.Equals(cpsProfileStr, StringComparison.CurrentCultureIgnoreCase)); - Command.Checked = curVal.Key != default && ((bool)curVal.Value == true); - } - } - else - { - var startupProject = await _startupProjectManager.GetStartupProject(); - var configName = await startupProject.GetCurrentConfigurationName(); - Command.Enabled = startupProject != null && configName != null; - if (Command.Enabled) - { - - var buildStorage = startupProject as IVsBuildPropertyStorage; - var cmdres = buildStorage.GetPropertyValue(nonCpsPropertyStr, configName, (uint)_PersistStorageType.PST_USER_FILE, out var existing); - if (cmdres != VSConstants.S_OK || String.IsNullOrWhiteSpace(existing)) - buildStorage.GetPropertyValue(nonCpsPropertyStr, configName, (uint)_PersistStorageType.PST_PROJECT_FILE, out existing);//user file will override project file but if user file is blank fallback to project - - - Command.Checked = existing == "true"; - - } - - } + this.profileSetter = new("nativeDebugging"); + this.storageSetter = new("EnableUnmanagedDebugging"); + profileSetter.StartManager.StartupProjectChanged += (_, _) => this.SyncCheckedToCurVal(); } - protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) + protected override Task[]> GetSetters() { - await UpdateCheckedState(); - await SetValue(!Command.Checked); - await UpdateCheckedState(); - await base.ExecuteAsync(e); + ISetSettingInterface setter = profileSetter.SupportsLaunchProfiles ? profileSetter : storageSetter; + return Task.FromResult[]>([setter]); } } } diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Commands/OurBaseSettingCmd.cs b/Tvl.VisualStudio.JustMyCodeToggle/Commands/OurBaseSettingCmd.cs index b009285..8653408 100644 --- a/Tvl.VisualStudio.JustMyCodeToggle/Commands/OurBaseSettingCmd.cs +++ b/Tvl.VisualStudio.JustMyCodeToggle/Commands/OurBaseSettingCmd.cs @@ -1,59 +1,194 @@ +using System; +using System.Threading; using System.Threading.Tasks; using Community.VisualStudio.Toolkit; +using Microsoft.VisualStudio.Extensibility; +using Microsoft.VisualStudio.Extensibility.Commands; using Microsoft.VisualStudio.Shell; using Tvl.VisualStudio.JustMyCodeToggle.Managers; namespace Tvl.VisualStudio.JustMyCodeToggle.Commands { - internal abstract class ToggleSettingCmd(string collectionPath, string propertyName, SETTING_TYPE enabled_val, SETTING_TYPE disabled_val) : OurSettingCmd(collectionPath, propertyName) where OUR_CLASS : class, new() + + + /// + /// Allows using multiple/dynamic setters IE depending on VS version, or if project supports CPS or not, if multiple setters are returned from GetSetters the first is used for GetSetting + /// + /// + /// + internal abstract class ToggleSettingDynamicSetterCmd : ToggleSettingCmd, ISetSettingInterface { - - protected override Task ExecuteAsync(OleMenuCmdEventArgs e) - { - var cur = _settingsManager.GetSetting(collectionPath, propertyName); - cur = cur.Equals(disabled_val) ? enabled_val : disabled_val; - SetValue(cur); - SetCheckedToMatch(cur); - return base.ExecuteAsync(e); + + protected delegate SETTING_TYPE TransformValDelegate(ISetSettingInterface setter, SETTING_TYPE val, bool forGetSetting); + protected ToggleSettingDynamicSetterCmd(SETTING_TYPE enabled_val, SETTING_TYPE disabled_val, Func, SETTING_TYPE, SETTING_TYPE> TransformVal = null) : base(null, enabled_val, disabled_val) + { + this.setter = this; + this.TransformVal = TransformVal; } - protected virtual void SetCheckedToMatch(SETTING_TYPE cur) + + protected abstract Task[]> GetSetters(); + public Func, SETTING_TYPE, SETTING_TYPE> TransformVal { get; } + + public async Task GetSetting() { - if (!cur.Equals(disabled_val)) - Command.Checked = true; - else - Command.Checked = false; + var setters = await GetSetters(); + var ret = await setters[0].GetSetting(); + if (TransformVal != null) + ret = TransformVal(setters[0], ret); + return ret; } - protected override async Task InitializeCompletedAsync() + + public async Task SetSetting(SETTING_TYPE val) { - await base.InitializeCompletedAsync(); - SetCheckedToMatch(GetValue()); - + var setters = await GetSetters(); + foreach (var setter in setters) + { + var toSet = val; + if (TransformVal != null) + toSet = TransformVal(setter, val); + await setter.SetSetting(toSet); + + } } } - internal abstract class OurSettingCmd : BaseCommand where OUR_CLASS : class, new() + public interface IOurCommand { - protected readonly string collectionPath; - protected readonly string propertyName; - protected SettingsManager _settingsManager; + Task Execute(); + Task Initialize(); + IButtonNative Native { set; } + } + public interface IButtonNative + { + IOurCommand OurCommand { set; } + bool IsChecked(); + void SetChecked(bool isChecked); + void SetEnabled(bool isEnabled); + } + internal abstract class OurExtensibilityToggleButton : ToggleCommand, IButtonNative where CMD_CLASS : class, IOurCommand, new() + { + public IOurCommand OurCommand { set; get; } + public OurExtensibilityToggleButton() + { + OurCommand = new CMD_CLASS(); + OurCommand.Native = this; + } - public OurSettingCmd(string collectionPath, string propertyName) + public override async Task InitializeAsync(CancellationToken cancellationToken) { + await OurCommand?.Initialize(); + await base.InitializeAsync(cancellationToken); + } + + public void SetChecked(bool isChecked) => this.IsChecked = isChecked; + + public void SetEnabled(bool isEnabled) => this.SetEnabledState(isEnabled); + + bool IButtonNative.IsChecked() => this.IsChecked; - this.collectionPath = collectionPath; - this.propertyName = propertyName; + override public async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken) + { + await (OurCommand?.Execute() ?? Task.CompletedTask); + // base is abstract for this } - protected override async Task InitializeCompletedAsync() + } + internal abstract class OurOLEButton : BaseCommand, IButtonNative where OUR_CLASS : class, new() + where CMD_CLASS : class, IOurCommand, new() { + public OurOLEButton() + { + OurCommand = new CMD_CLASS(); + OurCommand.Native = this; + } + public IOurCommand OurCommand { protected get; set; } + + protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) + { + await (OurCommand?.Execute() ?? Task.CompletedTask); + await base.ExecuteAsync(e); + } + public bool IsChecked() => this.Command.Checked; + public void SetChecked(bool isChecked) => this.Command.Checked = isChecked; + public void SetEnabled(bool isEnabled) => this.Command.Enabled = isEnabled; + override protected async Task InitializeCompletedAsync() + { + await (OurCommand?.Initialize() ?? Task.CompletedTask); await base.InitializeCompletedAsync(); - _settingsManager = Package.GetTypedService(); - + } } + internal abstract class ToggleSettingCmd : IOurCommand + { + + + protected virtual ISetSettingInterface setter { get; set; } + protected virtual SETTING_TYPE enabled_val { get; set; } + protected virtual SETTING_TYPE disabled_val { get; set; } + public IButtonNative Native + { + set + { + field = value; + //field.OurCommand = this; + } + protected get; + } + + protected virtual async Task SyncCheckedToCurVal() - protected SETTING_TYPE GetValue() => _settingsManager.GetSetting(collectionPath, propertyName); - protected void SetValue(SETTING_TYPE val) => _settingsManager.SetSetting(collectionPath, propertyName, val); + { + try + { + this.SetCheckedToMatch(await setter.GetSetting()); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"JMC ToggleSettingCmd {this.GetType()} StartProjChanged exception: {ex}"); + } + } + + public ToggleSettingCmd(ISetSettingInterface setter, SETTING_TYPE enabled_val, SETTING_TYPE disabled_val) + { + this.setter = setter; + this.enabled_val = enabled_val; + this.disabled_val = disabled_val; + } + public virtual async Task Execute() + { + try + { + + var cur = await setter.GetSetting(); + cur = cur.Equals(disabled_val) ? enabled_val : disabled_val; + await setter.SetSetting(cur); + SetCheckedToMatch(cur); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"JMC ToggleSettingCmd {this.GetType()} ExecuteAsync exception: {ex}"); + + } + + + } + protected virtual void SetCheckedToMatch(SETTING_TYPE cur) + { + Native.SetChecked(!cur.Equals(disabled_val)); + } + public async Task Initialize() + { + try + { + await SyncCheckedToCurVal(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"JMC ToggleSettingCmd InitializeCompletedAsync exception: {ex}"); + + } + + } } } diff --git a/Tvl.VisualStudio.JustMyCodeToggle/ExtensibilityExtensionEntrypoint.cs b/Tvl.VisualStudio.JustMyCodeToggle/ExtensibilityExtensionEntrypoint.cs new file mode 100644 index 0000000..aa6d02e --- /dev/null +++ b/Tvl.VisualStudio.JustMyCodeToggle/ExtensibilityExtensionEntrypoint.cs @@ -0,0 +1,41 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.Extensibility; +using System.Diagnostics; +using Tvl.VisualStudio.JustMyCodeToggle.Managers; + +namespace Tvl.VisualStudio.JustMyCodeToggle +{ + /// + /// Extension entrypoint for the VisualStudio.Extensibility extension. + /// + [VisualStudioContribution] + internal class ExtensibilityExtensionEntrypoint : Extension + { + //public ExtensibilityExtensionEntrypoint(){ + + // Debug.WriteLine("Inited here..."); + // } + + /// + public override ExtensionConfiguration ExtensionConfiguration => new() + { + RequiresInProcessHosting = true, + }; + + ///// + //protected override void InitializeServices(IServiceCollection serviceCollection) + //{ + + // Debug.WriteLine("ExtensionEntrypoint InitializeServices called!"); + + // base.InitializeServices(serviceCollection); + + // // Initialize the symbol load helper immediately during service initialization + // // This happens before any commands are invoked + + + + // // You can configure dependency injection here by adding services to the serviceCollection. + //} + } +} diff --git a/Tvl.VisualStudio.JustMyCodeToggle/Helpers.cs b/Tvl.VisualStudio.JustMyCodeToggle/Helpers.cs index 9d9a1a2..415b8ed 100644 --- a/Tvl.VisualStudio.JustMyCodeToggle/Helpers.cs +++ b/Tvl.VisualStudio.JustMyCodeToggle/Helpers.cs @@ -1,3 +1,4 @@ +using System; using System.Threading.Tasks; using Community.VisualStudio.Toolkit; using Microsoft.VisualStudio; @@ -13,6 +14,32 @@ namespace Tvl.VisualStudio.JustMyCodeToggle /// public static class ProjectExtensions { + public static bool IsVS2026 => _isVS2026.Value; + private static Lazy _isVS2026 = new ( () => + { + ThreadHelper.ThrowIfNotOnUIThread(); + try + { + var shell = (IVsShell)Package.GetGlobalService(typeof(SVsShell)); + if (shell != null) + { + shell.GetProperty((int)__VSSPROPID5.VSSPROPID_ReleaseVersion, out object version); + if (version is string versionString) + { + // VS 2026 is version 18.x + if (versionString.StartsWith("18.") || versionString.StartsWith("19.")) + { + return true; + } + } + } + } + catch + { + // If we can't detect version, assume older version + } + return false; + }); public static T GetTypedService(this AsyncPackage package) where T : class => package.GetService(); public static async Task GetDTEProjectFromIvsProject(this IVsProject proj) { diff --git a/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggle.cs b/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggle.cs index 4920211..dbaab61 100644 --- a/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggle.cs +++ b/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggle.cs @@ -1,20 +1,25 @@ // ------------------------------------------------------------------------------ // -// This file was generated by VSIX Synchronizer +// This file was generated by VSIX Synchronizer 1.0.45 +// Available from https://marketplace.visualstudio.com/items?itemName=MadsKristensen.VsixSynchronizer64 // // ------------------------------------------------------------------------------ namespace Tvl.VisualStudio.JustMyCodeToggle { using System; - + /// /// Helper class that exposes all GUIDs used across VS Package. /// internal sealed partial class PackageGuids { + public const string guidJustMyCodeTogglePackageString = "c86c0c68-5ff5-43af-8a60-54540cbea3e2"; + public static Guid guidJustMyCodeTogglePackage = new Guid(guidJustMyCodeTogglePackageString); + public const string guidJustMyCodeTogglePackageCmdSetString = "4e7a4be2-f4dd-4cc1-a142-e23645e43ecd"; public static Guid guidJustMyCodeTogglePackageCmdSet = new Guid(guidJustMyCodeTogglePackageCmdSetString); } + /// /// Helper class that encapsulates all CommandIDs uses across VS Package. /// @@ -27,4 +32,4 @@ internal sealed partial class PackageIds public const int JMCSymbolLoadBtn = 0x1105; public const int JMCDisableJitOptmizationsBtn = 0x1107; } -} \ No newline at end of file +} diff --git a/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggle.vsct b/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggle.vsct index 1664b0f..9368104 100644 --- a/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggle.vsct +++ b/Tvl.VisualStudio.JustMyCodeToggle/JustMyCodeToggle.vsct @@ -5,7 +5,7 @@ - + @@ -17,6 +17,7 @@ DontCache TextIsAnchorCommand + Debug.JMCTogglePackageMenu @@ -41,6 +42,8 @@ TogglePatternAvailable IconIsMoniker + DynamicVisibility + DefaultInvisible @@ -51,13 +54,13 @@ Toggle Native Code debugging for corrent launch option -