11using System ;
22using System . Linq ;
3+ using System . Threading . Tasks ;
34using System . Collections . Generic ;
45using System . ComponentModel . Composition ;
56using GitHub . Models ;
89using GitHub . TeamFoundation . Services ;
910using Serilog ;
1011using Microsoft . VisualStudio . TeamFoundation . Git . Extensibility ;
11- using Task = System . Threading . Tasks . Task ;
1212
1313namespace GitHub . VisualStudio . Base
1414{
1515 /// <summary>
1616 /// This service acts as an always available version of <see cref="IGitExt"/>.
1717 /// </summary>
18- [ Export ( typeof ( IVSGitExt ) ) ]
19- [ PartCreationPolicy ( CreationPolicy . Shared ) ]
18+ /// <remarks>
19+ /// Initialization for this service will be done asynchronously and the <see cref="IGitExt" /> service will be
20+ /// retrieved using <see cref="GetServiceAsync" />. This means the service can be constructed and subscribed to from a background thread.
21+ /// </remarks>
2022 public class VSGitExt : IVSGitExt
2123 {
2224 static readonly ILogger log = LogManager . ForContext < VSGitExt > ( ) ;
2325
24- readonly IGitHubServiceProvider serviceProvider ;
25- readonly IVSUIContext context ;
26+ readonly Func < Type , Task < object > > getServiceAsync ;
2627 readonly ILocalRepositoryModelFactory repositoryFactory ;
2728 readonly object refreshLock = new object ( ) ;
2829
2930 IGitExt gitService ;
3031 IReadOnlyList < ILocalRepositoryModel > activeRepositories ;
3132
3233 [ ImportingConstructor ]
33- public VSGitExt ( IGitHubServiceProvider serviceProvider )
34- : this ( serviceProvider , new VSUIContextFactory ( ) , new LocalRepositoryModelFactory ( ) )
34+ public VSGitExt ( Func < Type , Task < object > > getServiceAsync )
35+ : this ( getServiceAsync , new VSUIContextFactory ( ) , new LocalRepositoryModelFactory ( ) )
3536 {
3637 }
3738
38- public VSGitExt ( IGitHubServiceProvider serviceProvider , IVSUIContextFactory factory , ILocalRepositoryModelFactory repositoryFactory )
39+ public VSGitExt ( Func < Type , Task < object > > getServiceAsync , IVSUIContextFactory factory , ILocalRepositoryModelFactory repositoryFactory )
3940 {
40- this . serviceProvider = serviceProvider ;
41+ this . getServiceAsync = getServiceAsync ;
4142 this . repositoryFactory = repositoryFactory ;
4243
43- // The IGitExt service isn't available when a TFS based solution is opened directly.
44- // It will become available when moving to a Git based solution (cause a UIContext event to fire).
45- context = factory . GetUIContext ( new Guid ( Guids . GitSccProviderId ) ) ;
46-
47- // Start with empty array until we have a change to initialize.
44+ // Start with empty array until we have a chance to initialize.
4845 ActiveRepositories = Array . Empty < ILocalRepositoryModel > ( ) ;
4946
50- if ( context . IsActive && TryInitialize ( ) )
51- {
52- // Refresh ActiveRepositories on background thread so we don't delay startup.
53- InitializeTask = Task . Run ( ( ) => RefreshActiveRepositories ( ) ) ;
54- }
55- else
56- {
57- // If we're not in the UIContext or TryInitialize fails, have another go when the UIContext changes.
58- context . UIContextChanged += ContextChanged ;
59- log . Debug ( "VSGitExt will be initialized later" ) ;
60- InitializeTask = Task . CompletedTask ;
61- }
47+ // The IGitExt service isn't available when a TFS based solution is opened directly.
48+ // It will become available when moving to a Git based solution (and cause a UIContext event to fire).
49+ var context = factory . GetUIContext ( new Guid ( Guids . GitSccProviderId ) ) ;
50+ context . WhenActivated ( ( ) => Initialize ( ) ) ;
6251 }
6352
64- void ContextChanged ( object sender , VSUIContextChangedEventArgs e )
53+ void Initialize ( )
6554 {
66- // If we're in the UIContext and TryInitialize succeeds, we can stop listening for events.
67- // NOTE: this event can fire with UIContext=true in a TFS solution (not just Git).
68- if ( e . Activated && TryInitialize ( ) )
55+ PendingTasks = getServiceAsync ( typeof ( IGitExt ) ) . ContinueWith ( t =>
6956 {
70- // Refresh ActiveRepositories on background thread so we don't delay UI context change.
71- InitializeTask = Task . Run ( ( ) => RefreshActiveRepositories ( ) ) ;
72- context . UIContextChanged -= ContextChanged ;
73- log . Debug ( "Initialized VSGitExt on UIContextChanged ") ;
74- }
75- }
57+ gitService = ( IGitExt ) t . Result ;
58+ if ( gitService == null )
59+ {
60+ log . Error ( "Couldn't find IGitExt service ") ;
61+ return ;
62+ }
7663
77- bool TryInitialize ( )
78- {
79- gitService = serviceProvider . GetService < IGitExt > ( ) ;
80- if ( gitService != null )
81- {
64+ RefreshActiveRepositories ( ) ;
8265 gitService . PropertyChanged += ( s , e ) =>
8366 {
8467 if ( e . PropertyName == nameof ( gitService . ActiveRepositories ) )
8568 {
8669 RefreshActiveRepositories ( ) ;
8770 }
8871 } ;
89-
90- log . Debug ( "Found IGitExt service and initialized VSGitExt" ) ;
91- return true ;
92- }
93-
94- log . Error ( "Couldn't find IGitExt service" ) ;
95- return false ;
72+ } , TaskScheduler . Default ) ;
9673 }
9774
9875 void RefreshActiveRepositories ( )
@@ -104,7 +81,7 @@ void RefreshActiveRepositories()
10481 log . Debug (
10582 "IGitExt.ActiveRepositories (#{Id}) returned {Repositories}" ,
10683 gitService . GetHashCode ( ) ,
107- gitService ? . ActiveRepositories . Select ( x => x . RepositoryPath ) ) ;
84+ gitService . ActiveRepositories . Select ( x => x . RepositoryPath ) ) ;
10885
10986 ActiveRepositories = gitService ? . ActiveRepositories . Select ( x => repositoryFactory . Create ( x . RepositoryPath ) ) . ToList ( ) ;
11087 }
@@ -136,6 +113,9 @@ private set
136113
137114 public event Action ActiveRepositoriesChanged ;
138115
139- public Task InitializeTask { get ; private set ; }
116+ /// <summary>
117+ /// Tasks that are pending execution on the thread pool.
118+ /// </summary>
119+ public Task PendingTasks { get ; private set ; } = Task . CompletedTask ;
140120 }
141121}
0 commit comments