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

Commit d4a5b93

Browse files
committed
Automatic switching between in-box and installed extension
1 parent 70cec0d commit d4a5b93

File tree

10 files changed

+188
-64
lines changed

10 files changed

+188
-64
lines changed

src/GitHub.VisualStudio.16/CompositionServices.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,13 @@
1111
using GitHub.Factories;
1212
using GitHub.Models;
1313
using GitHub.Services;
14-
using GitHub.VisualStudio;
1514
using GitHub.VisualStudio.Views;
1615
using GitHub.VisualStudio.Views.Dialog.Clone;
1716
using Microsoft.VisualStudio.Shell;
1817
using Rothko;
1918
using Task = System.Threading.Tasks.Task;
2019

21-
namespace GitHubCore
20+
namespace GitHub.VisualStudio
2221
{
2322
public class CompositionServices
2423
{
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using Microsoft;
3+
using Microsoft.VisualStudio.Shell;
4+
using Microsoft.VisualStudio.Shell.Interop;
5+
6+
namespace GitHub.VisualStudio
7+
{
8+
public class ExtensionServicesFactory
9+
{
10+
readonly IServiceProvider serviceProvider;
11+
12+
public ExtensionServicesFactory(IServiceProvider serviceProvider)
13+
{
14+
this.serviceProvider = serviceProvider;
15+
}
16+
17+
public IExtensionServices Create()
18+
{
19+
var package = FindGitHubPackage();
20+
if(package != null)
21+
{
22+
return new InstalledExtensionServices(package);
23+
}
24+
else
25+
{
26+
return new InBoxExtensionServices();
27+
}
28+
}
29+
30+
IVsPackage FindGitHubPackage()
31+
{
32+
ThreadHelper.ThrowIfNotOnUIThread();
33+
34+
var shell = serviceProvider.GetService(typeof(SVsShell)) as IVsShell;
35+
Assumes.Present(shell);
36+
shell.LoadPackage(new Guid(Guids.PackageId), out var package);
37+
return package;
38+
}
39+
}
40+
}

src/GitHub.VisualStudio.16/GitHub.VisualStudio.16.csproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<ProjectGuid>{4D63EA3B-6896-42B5-B182-AA54D9F5CFD6}</ProjectGuid>
1515
<OutputType>Library</OutputType>
1616
<AppDesignerFolder>Properties</AppDesignerFolder>
17-
<RootNamespace>GitHubCore</RootNamespace>
17+
<RootNamespace>GitHub.VisualStudio</RootNamespace>
1818
<AssemblyName>GitHub.VisualStudio.16</AssemblyName>
1919
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
2020
<GeneratePkgDefFile>true</GeneratePkgDefFile>
@@ -47,7 +47,11 @@
4747
</PropertyGroup>
4848
<ItemGroup>
4949
<Compile Include="CompositionServices.cs" />
50+
<Compile Include="ExtensionServicesFactory.cs" />
51+
<Compile Include="IExtensionServices.cs" />
5052
<Compile Include="Images.imagemanifest.cs" />
53+
<Compile Include="InBoxExtensionServices.cs" />
54+
<Compile Include="InstalledExtensionServices.cs" />
5155
<Compile Include="Properties\AssemblyInfo.cs" />
5256
<Compile Include="Resources.Designer.cs">
5357
<AutoGen>True</AutoGen>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using Microsoft.VisualStudio.Shell.CodeContainerManagement;
2+
3+
namespace GitHub.VisualStudio
4+
{
5+
public interface IExtensionServices
6+
{
7+
ICodeContainerProvider GetGitHubContainerProvider();
8+
}
9+
}

src/GitHub.VisualStudio.16/Images.imagemanifest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace GitHubCore
1+
namespace GitHub.VisualStudio
22
{
33
internal class Images
44
{
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using GitHub.Models;
5+
using GitHub.Services;
6+
using Microsoft;
7+
using Microsoft.VisualStudio.Shell;
8+
using Microsoft.VisualStudio.Shell.CodeContainerManagement;
9+
using Microsoft.VisualStudio.ComponentModelHost;
10+
using CodeContainer = Microsoft.VisualStudio.Shell.CodeContainerManagement.CodeContainer;
11+
using ICodeContainerProvider = Microsoft.VisualStudio.Shell.CodeContainerManagement.ICodeContainerProvider;
12+
13+
namespace GitHub.VisualStudio
14+
{
15+
public class InBoxExtensionServices : IExtensionServices
16+
{
17+
public ICodeContainerProvider GetGitHubContainerProvider()
18+
{
19+
return new InBoxGitHubContainerProvider();
20+
}
21+
22+
class InBoxGitHubContainerProvider : ICodeContainerProvider
23+
{
24+
public async Task<CodeContainer> AcquireCodeContainerAsync(IProgress<ServiceProgressData> downloadProgress, CancellationToken cancellationToken)
25+
{
26+
return await RunAcquisitionAsync(downloadProgress, cancellationToken, null);
27+
}
28+
29+
public async Task<CodeContainer> AcquireCodeContainerAsync(RemoteCodeContainer onlineCodeContainer, IProgress<ServiceProgressData> downloadProgress, CancellationToken cancellationToken)
30+
{
31+
var url = onlineCodeContainer.DisplayUrl.ToString();
32+
return await RunAcquisitionAsync(downloadProgress, cancellationToken, url);
33+
}
34+
35+
async Task<CodeContainer> RunAcquisitionAsync(IProgress<ServiceProgressData> downloadProgress, CancellationToken cancellationToken, string url = null)
36+
{
37+
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
38+
39+
var componentModel = await ServiceProvider.GetGlobalServiceAsync<SComponentModel, IComponentModel>();
40+
Assumes.Present(componentModel);
41+
42+
var result = await ShowCloneDialogAsync(componentModel, downloadProgress, cancellationToken, url);
43+
if (result == null)
44+
{
45+
return null;
46+
}
47+
48+
var repositoryName = result.Url.RepositoryName;
49+
var repositoryRootFullPath = result.Path;
50+
var sccProvider = new Guid(Guids.GitSccProviderId);
51+
var codeContainerProvider = new Guid(Guids.CodeContainerProviderId);
52+
var displayUrl = result.Url.ToRepositoryUrl();
53+
var browseOnlineUrl = new Uri(displayUrl.ToString().TrimSuffix(".git"));
54+
var lastAccessed = DateTimeOffset.UtcNow;
55+
56+
var codeContainer = new CodeContainer(
57+
localProperties: new CodeContainerLocalProperties(repositoryRootFullPath, CodeContainerType.Folder,
58+
new CodeContainerSourceControlProperties(repositoryName, repositoryRootFullPath, sccProvider)),
59+
remote: new RemoteCodeContainer(repositoryName, codeContainerProvider, displayUrl, browseOnlineUrl, lastAccessed),
60+
isFavorite: false,
61+
lastAccessed: lastAccessed);
62+
63+
// Report all steps complete before returning a CodeContainer
64+
downloadProgress.Report(new ServiceProgressData(string.Empty, string.Empty, 1, 1));
65+
66+
return codeContainer;
67+
}
68+
69+
static async Task<CloneDialogResult> ShowCloneDialogAsync(IComponentModel componentModel,
70+
IProgress<ServiceProgressData> downloadProgress, CancellationToken cancellationToken, string url = null)
71+
{
72+
var compositionServices = new CompositionServices();
73+
var compositionContainer = compositionServices.CreateCompositionContainer(componentModel.DefaultExportProvider);
74+
75+
var dialogService = compositionContainer.GetExportedValue<IDialogService>();
76+
var cloneDialogResult = await dialogService.ShowCloneDialog(null, url);
77+
if (cloneDialogResult != null)
78+
{
79+
var repositoryCloneService = compositionContainer.GetExportedValue<IRepositoryCloneService>();
80+
await repositoryCloneService.CloneOrOpenRepository(cloneDialogResult, downloadProgress, cancellationToken);
81+
return cloneDialogResult;
82+
}
83+
84+
return null;
85+
}
86+
}
87+
}
88+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using System.IO;
3+
using System.Reflection;
4+
using Microsoft.VisualStudio.Shell.Interop;
5+
using ICodeContainerProvider = Microsoft.VisualStudio.Shell.CodeContainerManagement.ICodeContainerProvider;
6+
7+
namespace GitHub.VisualStudio
8+
{
9+
public class InstalledExtensionServices : IExtensionServices
10+
{
11+
readonly IVsPackage package;
12+
13+
public InstalledExtensionServices(IVsPackage package)
14+
{
15+
this.package = package;
16+
}
17+
18+
public ICodeContainerProvider GetGitHubContainerProvider()
19+
{
20+
var baseDirectory = Path.GetDirectoryName(package.GetType().Assembly.Location);
21+
var assemblyFile = Path.Combine(baseDirectory, "GitHub.StartPage.dll");
22+
var assembly = Assembly.LoadFrom(assemblyFile);
23+
var type = assembly.GetType("GitHub.StartPage.GitHubContainerProvider", true);
24+
return (ICodeContainerProvider)Activator.CreateInstance(type);
25+
}
26+
}
27+
}

src/GitHub.VisualStudio.16/Resources.Designer.cs

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 15 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
using System;
2-
using System.Runtime.InteropServices;
32
using System.Threading;
43
using System.Threading.Tasks;
5-
using GitHub.Models;
6-
using GitHub.Services;
4+
using System.Runtime.InteropServices;
75
using GitHub.VisualStudio;
8-
using GitHubCore;
9-
using Microsoft;
106
using Microsoft.VisualStudio.Shell;
117
using Microsoft.VisualStudio.Shell.CodeContainerManagement;
12-
using Microsoft.VisualStudio.ComponentModelHost;
138
using CodeContainer = Microsoft.VisualStudio.Shell.CodeContainerManagement.CodeContainer;
149
using ICodeContainerProvider = Microsoft.VisualStudio.Shell.CodeContainerManagement.ICodeContainerProvider;
1510

@@ -25,67 +20,30 @@ public sealed class StartPagePackage : ExtensionPointPackage
2520
[Guid(Guids.CodeContainerProviderId)]
2621
public class GitHubContainerProvider : ICodeContainerProvider
2722
{
28-
public async Task<CodeContainer> AcquireCodeContainerAsync(IProgress<ServiceProgressData> downloadProgress, CancellationToken cancellationToken)
29-
{
30-
return await RunAcquisitionAsync(downloadProgress, cancellationToken, null);
31-
}
23+
readonly ICodeContainerProvider provider;
3224

33-
public async Task<CodeContainer> AcquireCodeContainerAsync(RemoteCodeContainer onlineCodeContainer, IProgress<ServiceProgressData> downloadProgress, CancellationToken cancellationToken)
25+
public GitHubContainerProvider() : this(null)
3426
{
35-
var url = onlineCodeContainer.DisplayUrl.ToString();
36-
return await RunAcquisitionAsync(downloadProgress, cancellationToken, url);
3727
}
3828

39-
async Task<CodeContainer> RunAcquisitionAsync(IProgress<ServiceProgressData> downloadProgress, CancellationToken cancellationToken, string url = null)
29+
public GitHubContainerProvider(IServiceProvider serviceProvider)
4030
{
41-
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
42-
43-
var componentModel = await ServiceProvider.GetGlobalServiceAsync<SComponentModel, IComponentModel>();
44-
Assumes.Present(componentModel);
45-
46-
var result = await ShowCloneDialogAsync(componentModel, downloadProgress, cancellationToken, url);
47-
if (result == null)
48-
{
49-
return null;
50-
}
31+
ThreadHelper.ThrowIfNotOnUIThread();
5132

52-
var repositoryName = result.Url.RepositoryName;
53-
var repositoryRootFullPath = result.Path;
54-
var sccProvider = new Guid(Guids.GitSccProviderId);
55-
var codeContainerProvider = new Guid(Guids.CodeContainerProviderId);
56-
var displayUrl = result.Url.ToRepositoryUrl();
57-
var browseOnlineUrl = new Uri(displayUrl.ToString().TrimSuffix(".git"));
58-
var lastAccessed = DateTimeOffset.UtcNow;
59-
60-
var codeContainer = new CodeContainer(
61-
localProperties: new CodeContainerLocalProperties(repositoryRootFullPath, CodeContainerType.Folder,
62-
new CodeContainerSourceControlProperties(repositoryName, repositoryRootFullPath, sccProvider)),
63-
remote: new RemoteCodeContainer(repositoryName, codeContainerProvider, displayUrl, browseOnlineUrl, lastAccessed),
64-
isFavorite: false,
65-
lastAccessed: lastAccessed);
66-
67-
// Report all steps complete before returning a CodeContainer
68-
downloadProgress.Report(new ServiceProgressData(string.Empty, string.Empty, 1, 1));
69-
70-
return codeContainer;
33+
serviceProvider = serviceProvider ?? ServiceProvider.GlobalProvider;
34+
var factory = new ExtensionServicesFactory(serviceProvider);
35+
var services = factory.Create();
36+
provider = services.GetGitHubContainerProvider();
7137
}
7238

73-
static async Task<CloneDialogResult> ShowCloneDialogAsync(IComponentModel componentModel,
74-
IProgress<ServiceProgressData> downloadProgress, CancellationToken cancellationToken, string url = null)
39+
public Task<CodeContainer> AcquireCodeContainerAsync(IProgress<ServiceProgressData> downloadProgress, CancellationToken cancellationToken)
7540
{
76-
var compositionServices = new CompositionServices();
77-
var compositionContainer = compositionServices.CreateCompositionContainer(componentModel.DefaultExportProvider);
78-
79-
var dialogService = compositionContainer.GetExportedValue<IDialogService>();
80-
var cloneDialogResult = await dialogService.ShowCloneDialog(null, url);
81-
if (cloneDialogResult != null)
82-
{
83-
var repositoryCloneService = compositionContainer.GetExportedValue<IRepositoryCloneService>();
84-
await repositoryCloneService.CloneOrOpenRepository(cloneDialogResult, downloadProgress, cancellationToken);
85-
return cloneDialogResult;
86-
}
41+
return provider.AcquireCodeContainerAsync(downloadProgress, cancellationToken);
42+
}
8743

88-
return null;
44+
public Task<CodeContainer> AcquireCodeContainerAsync(RemoteCodeContainer onlineCodeContainer, IProgress<ServiceProgressData> downloadProgress, CancellationToken cancellationToken)
45+
{
46+
return provider.AcquireCodeContainerAsync(onlineCodeContainer, downloadProgress, cancellationToken);
8947
}
9048
}
9149
}

src/GitHub.VisualStudio.TestApp/MainWindow.xaml.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System.ComponentModel.Composition.Hosting;
55
using GitHub.Services;
66
using GitHub.VisualStudio.UI.Services;
7-
using GitHubCore;
87
using Microsoft.VisualStudio.Shell;
98
using Task = System.Threading.Tasks.Task;
109

0 commit comments

Comments
 (0)