22// The .NET Foundation licenses this file to you under the MIT license.
33// See the LICENSE file in the project root for more information.
44
5- #nullable disable
6-
75using System ;
86using System . Collections . Generic ;
97using System . Composition ;
1311using Microsoft . CodeAnalysis . Host . Mef ;
1412using Microsoft . Internal . VisualStudio . Shell . Interop ;
1513using Microsoft . VisualStudio . Shell ;
14+ using VSExperimentation = Microsoft . VisualStudio . Experimentation ;
1615
1716namespace Microsoft . VisualStudio . LanguageServices . Experimentation
1817{
1918 [ Export ( typeof ( VisualStudioExperimentationService ) ) ]
2019 [ ExportWorkspaceService ( typeof ( IExperimentationService ) , ServiceLayer . Host ) , Shared ]
2120 internal class VisualStudioExperimentationService : ForegroundThreadAffinitizedObject , IExperimentationService
2221 {
23- private readonly object _experimentationServiceOpt ;
24- private readonly MethodInfo _isCachedFlightEnabledInfo ;
25- private readonly IVsFeatureFlags _featureFlags ;
22+ private readonly object ? _experimentationServiceOpt ;
23+ private readonly MethodInfo ? _isCachedFlightEnabledInfo ;
24+ private readonly IVsFeatureFlags ? _featureFlags ;
2625
2726 /// <summary>
2827 /// Cache of values we've queried from the underlying VS service. These values are expected to last for the
@@ -36,15 +35,15 @@ internal class VisualStudioExperimentationService : ForegroundThreadAffinitizedO
3635 public VisualStudioExperimentationService ( IThreadingContext threadingContext , SVsServiceProvider serviceProvider )
3736 : base ( threadingContext )
3837 {
39- object experimentationServiceOpt = null ;
40- MethodInfo isCachedFlightEnabledInfo = null ;
41- IVsFeatureFlags featureFlags = null ;
38+ object ? experimentationServiceOpt = null ;
39+ MethodInfo ? isCachedFlightEnabledInfo = null ;
40+ IVsFeatureFlags ? featureFlags = null ;
4241
4342 threadingContext . JoinableTaskFactory . Run ( async ( ) =>
4443 {
4544 try
4645 {
47- featureFlags = ( IVsFeatureFlags ) await ( ( IAsyncServiceProvider ) serviceProvider ) . GetServiceAsync ( typeof ( SVsFeatureFlags ) ) . ConfigureAwait ( false ) ;
46+ featureFlags = ( IVsFeatureFlags ? ) await ( ( IAsyncServiceProvider ) serviceProvider ) . GetServiceAsync ( typeof ( SVsFeatureFlags ) ) . ConfigureAwait ( false ) ;
4847 experimentationServiceOpt = await ( ( IAsyncServiceProvider ) serviceProvider ) . GetServiceAsync ( typeof ( SVsExperimentationService ) ) . ConfigureAwait ( false ) ;
4948 if ( experimentationServiceOpt != null )
5049 {
@@ -71,8 +70,8 @@ public void EnableExperiment(string experimentName, bool value)
7170 _experimentEnabledMap . Remove ( experimentName ) ;
7271 }
7372
74- var featureFlags2 = ( IVsFeatureFlags2 ) _featureFlags ;
75- featureFlags2 . EnableFeatureFlag ( experimentName , value ) ;
73+ var featureFlags2 = ( IVsFeatureFlags2 ? ) _featureFlags ;
74+ featureFlags2 ? . EnableFeatureFlag ( experimentName , value ) ;
7675 }
7776
7877 public bool IsExperimentEnabled ( string experimentName )
@@ -101,36 +100,49 @@ public bool IsExperimentEnabled(string experimentName)
101100 private bool IsExperimentEnabledWorker ( string experimentName )
102101 {
103102 ThisCanBeCalledOnAnyThread ( ) ;
104- if ( _isCachedFlightEnabledInfo != null )
103+
104+ // First check feature flags.
105+ try
105106 {
106- try
107+ // check whether "." exist in the experimentName since it is requirement for featureflag service.
108+ // we do this since RPS complains about resource file being loaded for invalid name exception
109+ // we are not testing all rules but just simple "." check
110+ if ( experimentName . IndexOf ( "." ) > 0 && _featureFlags != null && _featureFlags . IsFeatureEnabled ( experimentName , defaultValue : false ) )
111+ return true ;
112+ }
113+ catch
114+ {
115+ // featureFlags can throw if given name is in incorrect format which can happen for us
116+ // since we use this for experimentation service as well
117+ }
118+
119+ // Then the legacy 'targetted notification system'.
120+ try
121+ {
122+ if ( _isCachedFlightEnabledInfo != null && ( bool ) _isCachedFlightEnabledInfo . Invoke ( _experimentationServiceOpt , new object [ ] { experimentName } ) )
123+ return true ;
124+ }
125+ catch
126+ {
127+ }
128+
129+ // Finally, the modern 'treatment variable' system.
130+ try
131+ {
132+ if ( VSExperimentation . ExperimentationService . Default is VSExperimentation . IExperimentationService3 experimentationService )
107133 {
108- // check whether "." exist in the experimentName since it is requirement for featureflag service.
109- // we do this since RPS complains about resource file being loaded for invalid name exception
110- // we are not testing all rules but just simple "." check
111- if ( experimentName . IndexOf ( "." ) > 0 )
134+ // Get from the well known 'VisualStudio' set of treatment variables.
135+ var treatmentVariables = experimentationService . GetCachedTreatmentVariables ( "VisualStudio" ) ;
136+ if ( treatmentVariables != null &&
137+ treatmentVariables . TryGetValue ( experimentName , out var value ) &&
138+ value is true )
112139 {
113- var enabled = _featureFlags . IsFeatureEnabled ( experimentName , defaultValue : false ) ;
114- if ( enabled )
115- {
116- return enabled ;
117- }
140+ return true ;
118141 }
119142 }
120- catch
121- {
122- // featureFlags can throw if given name is in incorrect format which can happen for us
123- // since we use this for experimentation service as well
124- }
125-
126- try
127- {
128- return ( bool ) _isCachedFlightEnabledInfo . Invoke ( _experimentationServiceOpt , new object [ ] { experimentName } ) ;
129- }
130- catch
131- {
132-
133- }
143+ }
144+ catch
145+ {
134146 }
135147
136148 return false ;
0 commit comments