77using System . Collections . Immutable ;
88using System . Composition ;
99using System . Linq ;
10+ using System . Reflection ;
1011using System . Runtime . CompilerServices ;
1112using System . Threading ;
1213using System . Threading . Tasks ;
1314using Microsoft . CodeAnalysis . CodeActions ;
15+ using Microsoft . CodeAnalysis . Diagnostics ;
1416using Microsoft . CodeAnalysis . Extensions ;
1517using Microsoft . CodeAnalysis . Host . Mef ;
1618using Microsoft . CodeAnalysis . Internal . Log ;
@@ -24,21 +26,29 @@ namespace Microsoft.CodeAnalysis.CodeRefactorings
2426 [ Export ( typeof ( ICodeRefactoringService ) ) , Shared ]
2527 internal class CodeRefactoringService : ICodeRefactoringService
2628 {
27- private readonly Lazy < ImmutableDictionary < string , Lazy < IEnumerable < CodeRefactoringProvider > > > > _lazyLanguageToProvidersMap ;
29+ private readonly Lazy < ImmutableDictionary < string , Lazy < ImmutableArray < CodeRefactoringProvider > > > > _lazyLanguageToProvidersMap ;
30+ private readonly ConditionalWeakTable < IReadOnlyList < AnalyzerReference > , StrongBox < ImmutableArray < CodeRefactoringProvider > > > _projectRefactoringsMap
31+ = new ConditionalWeakTable < IReadOnlyList < AnalyzerReference > , StrongBox < ImmutableArray < CodeRefactoringProvider > > > ( ) ;
32+
33+ private readonly ConditionalWeakTable < AnalyzerReference , ProjectCodeRefactoringProvider > _analyzerReferenceToRefactoringsMap
34+ = new ConditionalWeakTable < AnalyzerReference , ProjectCodeRefactoringProvider > ( ) ;
35+ private readonly ConditionalWeakTable < AnalyzerReference , ProjectCodeRefactoringProvider > . CreateValueCallback _createProjectCodeRefactoringsProvider
36+ = new ConditionalWeakTable < AnalyzerReference , ProjectCodeRefactoringProvider > . CreateValueCallback ( r => new ProjectCodeRefactoringProvider ( r ) ) ;
2837
2938 [ ImportingConstructor ]
39+ [ Obsolete ( MefConstruction . ImportingConstructorMessage , error : true ) ]
3040 public CodeRefactoringService (
3141 [ ImportMany ] IEnumerable < Lazy < CodeRefactoringProvider , CodeChangeProviderMetadata > > providers )
3242 {
3343 // convert set of all code refactoring providers into a map from language to a lazy initialized list of ordered providers.
34- _lazyLanguageToProvidersMap = new Lazy < ImmutableDictionary < string , Lazy < IEnumerable < CodeRefactoringProvider > > > > (
44+ _lazyLanguageToProvidersMap = new Lazy < ImmutableDictionary < string , Lazy < ImmutableArray < CodeRefactoringProvider > > > > (
3545 ( ) =>
3646 ImmutableDictionary . CreateRange (
3747 DistributeLanguages ( providers )
3848 . GroupBy ( lz => lz . Metadata . Language )
39- . Select ( grp => new KeyValuePair < string , Lazy < IEnumerable < CodeRefactoringProvider > > > (
49+ . Select ( grp => new KeyValuePair < string , Lazy < ImmutableArray < CodeRefactoringProvider > > > (
4050 grp . Key ,
41- new Lazy < IEnumerable < CodeRefactoringProvider > > ( ( ) => ExtensionOrderer . Order ( grp ) . Select ( lz => lz . Value ) ) ) ) ) ) ;
51+ new Lazy < ImmutableArray < CodeRefactoringProvider > > ( ( ) => ExtensionOrderer . Order ( grp ) . Select ( lz => lz . Value ) . ToImmutableArray ( ) ) ) ) ) ) ;
4252 }
4353
4454 private IEnumerable < Lazy < CodeRefactoringProvider , OrderableLanguageMetadata > > DistributeLanguages ( IEnumerable < Lazy < CodeRefactoringProvider , CodeChangeProviderMetadata > > providers )
@@ -54,19 +64,18 @@ private IEnumerable<Lazy<CodeRefactoringProvider, OrderableLanguageMetadata>> Di
5464 }
5565 }
5666
57- private ImmutableDictionary < string , Lazy < IEnumerable < CodeRefactoringProvider > > > LanguageToProvidersMap
67+ private ImmutableDictionary < string , Lazy < ImmutableArray < CodeRefactoringProvider > > > LanguageToProvidersMap
5868 => _lazyLanguageToProvidersMap . Value ;
5969
60- private IEnumerable < CodeRefactoringProvider > GetProviders ( Document document )
70+ private ConcatImmutableArray < CodeRefactoringProvider > GetProviders ( Document document )
6171 {
72+ var allRefactorings = ImmutableArray < CodeRefactoringProvider > . Empty ;
6273 if ( LanguageToProvidersMap . TryGetValue ( document . Project . Language , out var lazyProviders ) )
6374 {
64- return lazyProviders . Value ;
65- }
66- else
67- {
68- return SpecializedCollections . EmptyEnumerable < CodeRefactoringProvider > ( ) ;
75+ allRefactorings = lazyProviders . Value ;
6976 }
77+
78+ return allRefactorings . ConcatFast ( GetProjectRefactorings ( document . Project ) ) ;
7079 }
7180
7281 public async Task < bool > HasRefactoringsAsync (
@@ -192,5 +201,112 @@ public async Task<ImmutableArray<CodeRefactoring>> GetRefactoringsAsync(
192201
193202 return null ;
194203 }
204+
205+ private ImmutableArray < CodeRefactoringProvider > GetProjectRefactorings ( Project project )
206+ {
207+ // TODO (https://github.com/dotnet/roslyn/issues/4932): Don't restrict refactorings in Interactive
208+ if ( project . Solution . Workspace . Kind == WorkspaceKind . Interactive )
209+ {
210+ return ImmutableArray < CodeRefactoringProvider > . Empty ;
211+ }
212+
213+ if ( _projectRefactoringsMap . TryGetValue ( project . AnalyzerReferences , out var refactorings ) )
214+ {
215+ return refactorings . Value ;
216+ }
217+
218+ return GetProjectRefactoringsSlow ( project ) ;
219+
220+ // Local functions
221+ ImmutableArray < CodeRefactoringProvider > GetProjectRefactoringsSlow ( Project project )
222+ {
223+ return _projectRefactoringsMap . GetValue ( project . AnalyzerReferences , pId => new StrongBox < ImmutableArray < CodeRefactoringProvider > > ( ComputeProjectRefactorings ( project ) ) ) . Value ;
224+ }
225+
226+ ImmutableArray < CodeRefactoringProvider > ComputeProjectRefactorings ( Project project )
227+ {
228+ var builder = ArrayBuilder < CodeRefactoringProvider > . GetInstance ( ) ;
229+ foreach ( var reference in project . AnalyzerReferences )
230+ {
231+ var projectCodeRefactoringProvider = _analyzerReferenceToRefactoringsMap . GetValue ( reference , _createProjectCodeRefactoringsProvider ) ;
232+ foreach ( var refactoring in projectCodeRefactoringProvider . GetRefactorings ( project . Language ) )
233+ {
234+ builder . Add ( refactoring ) ;
235+ }
236+ }
237+
238+ return builder . ToImmutableAndFree ( ) ;
239+ }
240+ }
241+
242+ private class ProjectCodeRefactoringProvider
243+ {
244+ private readonly AnalyzerReference _reference ;
245+ private ImmutableDictionary < string , ImmutableArray < CodeRefactoringProvider > > _refactoringsPerLanguage ;
246+
247+ public ProjectCodeRefactoringProvider ( AnalyzerReference reference )
248+ {
249+ _reference = reference ;
250+ _refactoringsPerLanguage = ImmutableDictionary < string , ImmutableArray < CodeRefactoringProvider > > . Empty ;
251+ }
252+
253+ public ImmutableArray < CodeRefactoringProvider > GetRefactorings ( string language )
254+ {
255+ return ImmutableInterlocked . GetOrAdd ( ref _refactoringsPerLanguage , language , ( language , provider ) => provider . CreateRefactorings ( language ) , this ) ;
256+ }
257+
258+ private ImmutableArray < CodeRefactoringProvider > CreateRefactorings ( string language )
259+ {
260+ // check whether the analyzer reference knows how to return fixers directly.
261+ if ( _reference is ICodeRefactoringProviderFactory codeRefactoringProviderFactory )
262+ {
263+ return codeRefactoringProviderFactory . GetRefactorings ( ) ;
264+ }
265+
266+ // otherwise, see whether we can pick it up from reference itself
267+ if ( ! ( _reference is AnalyzerFileReference analyzerFileReference ) )
268+ {
269+ return ImmutableArray < CodeRefactoringProvider > . Empty ;
270+ }
271+
272+ var builder = ArrayBuilder < CodeRefactoringProvider > . GetInstance ( ) ;
273+
274+ try
275+ {
276+ var analyzerAssembly = analyzerFileReference . GetAssembly ( ) ;
277+ var typeInfos = analyzerAssembly . DefinedTypes ;
278+
279+ foreach ( var typeInfo in typeInfos )
280+ {
281+ if ( typeInfo . IsSubclassOf ( typeof ( CodeRefactoringProvider ) ) )
282+ {
283+ try
284+ {
285+ var attribute = typeInfo . GetCustomAttribute < ExportCodeRefactoringProviderAttribute > ( ) ;
286+ if ( attribute != null )
287+ {
288+ if ( attribute . Languages == null ||
289+ attribute . Languages . Length == 0 ||
290+ attribute . Languages . Contains ( language ) )
291+ {
292+ builder . Add ( ( CodeRefactoringProvider ) Activator . CreateInstance ( typeInfo . AsType ( ) ) ) ;
293+ }
294+ }
295+ }
296+ catch
297+ {
298+ }
299+ }
300+ }
301+ }
302+ catch
303+ {
304+ // REVIEW: is the below message right?
305+ // NOTE: We could report "unable to load analyzer" exception here but it should have been already reported by DiagnosticService.
306+ }
307+
308+ return builder . ToImmutableAndFree ( ) ;
309+ }
310+ }
195311 }
196312}
0 commit comments