Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit 0eadf70

Browse files
authored
Merge pull request #1513 from github/refactor/simplify-VSGitExt
Simplify the VSGitExt service
2 parents fc06da7 + ee9ab4a commit 0eadf70

File tree

4 files changed

+63
-104
lines changed

4 files changed

+63
-104
lines changed

src/GitHub.Exports/Services/IVSUIContextFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ public VSUIContextChangedEventArgs(bool activated)
2020
public interface IVSUIContext
2121
{
2222
bool IsActive { get; }
23-
event EventHandler<VSUIContextChangedEventArgs> UIContextChanged;
23+
void WhenActivated(Action action);
2424
}
2525
}

src/GitHub.TeamFoundation.14/Services/VSGitExt.cs

Lines changed: 23 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ public class VSGitExt : IVSGitExt
2424
static readonly ILogger log = LogManager.ForContext<VSGitExt>();
2525

2626
readonly Func<Type, Task<object>> getServiceAsync;
27-
readonly IVSUIContext context;
2827
readonly ILocalRepositoryModelFactory repositoryFactory;
28+
readonly object refreshLock = new object();
2929

3030
IGitExt gitService;
3131
IReadOnlyList<ILocalRepositoryModel> activeRepositories;
@@ -41,94 +41,50 @@ public VSGitExt(Func<Type, Task<object>> getServiceAsync, IVSUIContextFactory fa
4141
this.getServiceAsync = getServiceAsync;
4242
this.repositoryFactory = repositoryFactory;
4343

44-
// The IGitExt service isn't available when a TFS based solution is opened directly.
45-
// It will become available when moving to a Git based solution (and cause a UIContext event to fire).
46-
context = factory.GetUIContext(new Guid(Guids.GitSccProviderId));
47-
4844
// Start with empty array until we have a chance to initialize.
4945
ActiveRepositories = Array.Empty<ILocalRepositoryModel>();
5046

51-
PendingTasks = InitializeAsync();
52-
}
53-
54-
async Task InitializeAsync()
55-
{
56-
try
57-
{
58-
if (!context.IsActive || !await TryInitialize())
59-
{
60-
// If we're not in the UIContext or TryInitialize fails, have another go when the UIContext changes.
61-
context.UIContextChanged += ContextChanged;
62-
log.Debug("VSGitExt will be initialized later");
63-
}
64-
}
65-
catch (Exception e)
66-
{
67-
log.Error(e, "Initializing");
68-
}
69-
}
70-
71-
void ContextChanged(object sender, VSUIContextChangedEventArgs e)
72-
{
73-
if (e.Activated)
74-
{
75-
PendingTasks = ContextChangedAsync();
76-
}
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());
7751
}
7852

79-
async Task ContextChangedAsync()
53+
void Initialize()
8054
{
81-
try
55+
PendingTasks = getServiceAsync(typeof(IGitExt)).ContinueWith(t =>
8256
{
83-
// If we're in the UIContext and TryInitialize succeeds, we can stop listening for events.
84-
// NOTE: this event can fire with UIContext=true in a TFS solution (not just Git).
85-
if (await TryInitialize())
57+
gitService = (IGitExt)t.Result;
58+
if (gitService == null)
8659
{
87-
context.UIContextChanged -= ContextChanged;
88-
log.Debug("Initialized VSGitExt on UIContextChanged");
60+
log.Error("Couldn't find IGitExt service");
61+
return;
8962
}
90-
}
91-
catch (Exception e)
92-
{
93-
log.Error(e, "UIContextChanged");
94-
}
95-
}
9663

97-
async Task<bool> TryInitialize()
98-
{
99-
gitService = (IGitExt)await getServiceAsync(typeof(IGitExt));
100-
if (gitService != null)
101-
{
64+
RefreshActiveRepositories();
10265
gitService.PropertyChanged += (s, e) =>
10366
{
10467
if (e.PropertyName == nameof(gitService.ActiveRepositories))
10568
{
106-
// Execute tasks in sequence using thread pool (TaskScheduler.Default).
107-
PendingTasks = PendingTasks.ContinueWith(_ => RefreshActiveRepositories(), TaskScheduler.Default);
69+
RefreshActiveRepositories();
10870
}
10971
};
110-
111-
// Do this after we start listening so we don't miss an event.
112-
await Task.Run(() => RefreshActiveRepositories());
113-
114-
log.Debug("Found IGitExt service and initialized VSGitExt");
115-
return true;
116-
}
117-
118-
log.Error("Couldn't find IGitExt service");
119-
return false;
72+
}, TaskScheduler.Default);
12073
}
12174

12275
void RefreshActiveRepositories()
12376
{
12477
try
12578
{
126-
log.Debug(
127-
"IGitExt.ActiveRepositories (#{Id}) returned {Repositories}",
128-
gitService.GetHashCode(),
129-
gitService?.ActiveRepositories.Select(x => x.RepositoryPath));
79+
lock (refreshLock)
80+
{
81+
log.Debug(
82+
"IGitExt.ActiveRepositories (#{Id}) returned {Repositories}",
83+
gitService.GetHashCode(),
84+
gitService.ActiveRepositories.Select(x => x.RepositoryPath));
13085

131-
ActiveRepositories = gitService?.ActiveRepositories.Select(x => repositoryFactory.Create(x.RepositoryPath)).ToList();
86+
ActiveRepositories = gitService?.ActiveRepositories.Select(x => repositoryFactory.Create(x.RepositoryPath)).ToList();
87+
}
13288
}
13389
catch (Exception e)
13490
{
@@ -160,6 +116,6 @@ private set
160116
/// <summary>
161117
/// Tasks that are pending execution on the thread pool.
162118
/// </summary>
163-
public Task PendingTasks { get; private set; }
119+
public Task PendingTasks { get; private set; } = Task.CompletedTask;
164120
}
165121
}
Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.ComponentModel.Composition;
42
using Microsoft.VisualStudio.Shell;
53
using GitHub.Services;
64

@@ -17,36 +15,14 @@ public IVSUIContext GetUIContext(Guid contextGuid)
1715
class VSUIContext : IVSUIContext
1816
{
1917
readonly UIContext context;
20-
readonly Dictionary<EventHandler<VSUIContextChangedEventArgs>, EventHandler<UIContextChangedEventArgs>> handlers =
21-
new Dictionary<EventHandler<VSUIContextChangedEventArgs>, EventHandler<UIContextChangedEventArgs>>();
18+
2219
public VSUIContext(UIContext context)
2320
{
2421
this.context = context;
2522
}
2623

2724
public bool IsActive { get { return context.IsActive; } }
2825

29-
public event EventHandler<VSUIContextChangedEventArgs> UIContextChanged
30-
{
31-
add
32-
{
33-
EventHandler<UIContextChangedEventArgs> handler = null;
34-
if (!handlers.TryGetValue(value, out handler))
35-
{
36-
handler = (s, e) => value.Invoke(s, new VSUIContextChangedEventArgs(e.Activated));
37-
handlers.Add(value, handler);
38-
}
39-
context.UIContextChanged += handler;
40-
}
41-
remove
42-
{
43-
EventHandler<UIContextChangedEventArgs> handler = null;
44-
if (handlers.TryGetValue(value, out handler))
45-
{
46-
handlers.Remove(value);
47-
context.UIContextChanged -= handler;
48-
}
49-
}
50-
}
26+
public void WhenActivated(Action action) => context.WhenActivated(action);
5127
}
5228
}

test/UnitTests/GitHub.TeamFoundation/VSGitExtTests.cs

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ public void GetServiceIGitExt_WhenUIContextChanged(bool activated, int expectCal
3636
var sp = Substitute.For<IAsyncServiceProvider>();
3737
var target = CreateVSGitExt(context, sp: sp);
3838

39-
var eventArgs = new VSUIContextChangedEventArgs(activated);
40-
context.UIContextChanged += Raise.Event<EventHandler<VSUIContextChangedEventArgs>>(context, eventArgs);
39+
context.IsActive = activated;
4140

41+
target.PendingTasks.Wait();
4242
sp.Received(expectCalls).GetServiceAsync(typeof(IGitExt));
4343
}
4444

@@ -106,8 +106,7 @@ public void WhenUIContextChanged_ActiveRepositoriesChangedIsFired()
106106
bool wasFired = false;
107107
target.ActiveRepositoriesChanged += () => wasFired = true;
108108

109-
var eventArgs = new VSUIContextChangedEventArgs(true);
110-
context.UIContextChanged += Raise.Event<EventHandler<VSUIContextChangedEventArgs>>(context, eventArgs);
109+
context.IsActive = true;
111110
target.PendingTasks.Wait();
112111

113112
Assert.That(wasFired, Is.True);
@@ -123,8 +122,7 @@ public void WhenUIContextChanged_FiredUsingThreadPoolThread()
123122
bool threadPool = false;
124123
target.ActiveRepositoriesChanged += () => threadPool = Thread.CurrentThread.IsThreadPoolThread;
125124

126-
var eventArgs = new VSUIContextChangedEventArgs(true);
127-
context.UIContextChanged += Raise.Event<EventHandler<VSUIContextChangedEventArgs>>(context, eventArgs);
125+
context.IsActive = true;
128126
target.PendingTasks.Wait();
129127

130128
Assert.That(threadPool, Is.True);
@@ -236,11 +234,40 @@ static IGitExt CreateGitExt(params string[] repositoryPaths)
236234
return gitExt;
237235
}
238236

239-
static IVSUIContext CreateVSUIContext(bool isActive)
237+
static MockVSUIContext CreateVSUIContext(bool isActive)
240238
{
241-
var context = Substitute.For<IVSUIContext>();
242-
context.IsActive.Returns(isActive);
243-
return context;
239+
return new MockVSUIContext { IsActive = isActive };
240+
}
241+
242+
class MockVSUIContext : IVSUIContext
243+
{
244+
bool isActive;
245+
Action action;
246+
247+
public bool IsActive
248+
{
249+
get { return isActive; }
250+
set
251+
{
252+
isActive = value;
253+
if (isActive && action != null)
254+
{
255+
action.Invoke();
256+
action = null;
257+
}
258+
}
259+
}
260+
261+
public void WhenActivated(Action action)
262+
{
263+
if (isActive)
264+
{
265+
action.Invoke();
266+
return;
267+
}
268+
269+
this.action = action;
270+
}
244271
}
245272

246273
class MockGitExt : IGitExt

0 commit comments

Comments
 (0)