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

Commit d9ce6f5

Browse files
committed
Add separate GitHub.OpenFromClipboard command
This is a mirror of `GitHub.CopyLink`.
1 parent d06ba40 commit d9ce6f5

File tree

7 files changed

+223
-4
lines changed

7 files changed

+223
-4
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace GitHub.Commands
2+
{
3+
public interface IOpenFromClipboardCommand : IVsCommand<string>
4+
{
5+
}
6+
}

src/GitHub.Exports/GitHub.Exports.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@
146146
<ItemGroup>
147147
<Compile Include="Commands\ICopyLinkCommand.cs" />
148148
<Compile Include="Commands\IBlameLinkCommand.cs" />
149+
<Compile Include="Commands\IOpenFromClipboardCommand.cs" />
149150
<Compile Include="Commands\IOpenFromUrlCommand.cs" />
150151
<Compile Include="Commands\IToggleInlineCommentMarginCommand.cs" />
151152
<Compile Include="Commands\IOpenLinkCommand.cs" />

src/GitHub.Exports/Settings/PkgCmdID.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public static class PkgCmdIDList
1212
public const int showCurrentPullRequestCommand = 0x202;
1313
public const int syncSubmodulesCommand = 0x203;
1414
public const int openFromUrlCommand = 0x204;
15+
public const int openFromClipboardCommand = 0x205;
1516

1617
public const int backCommand = 0x300;
1718
public const int forwardCommand = 0x301;
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
using System;
2+
using System.IO;
3+
using System.Windows;
4+
using System.Threading.Tasks;
5+
using System.ComponentModel.Composition;
6+
using GitHub.Commands;
7+
using GitHub.Services;
8+
using GitHub.App.Services;
9+
using GitHub.Services.Vssdk.Commands;
10+
using EnvDTE;
11+
using Microsoft.VisualStudio;
12+
using Microsoft.VisualStudio.Shell;
13+
using Microsoft.VisualStudio.Shell.Interop;
14+
using Task = System.Threading.Tasks.Task;
15+
using SVsServiceProvider = Microsoft.VisualStudio.Shell.SVsServiceProvider;
16+
17+
namespace GitHub.VisualStudio.Commands
18+
{
19+
[Export(typeof(IOpenFromClipboardCommand))]
20+
public class OpenFromClipboardCommand : VsCommand<string>, IOpenFromClipboardCommand
21+
{
22+
readonly Lazy<GitHubContextService> gitHubContextService;
23+
readonly Lazy<IRepositoryCloneService> repositoryCloneService;
24+
readonly Lazy<IPullRequestEditorService> pullRequestEditorService;
25+
readonly Lazy<ITeamExplorerContext> teamExplorerContext;
26+
readonly Lazy<IGitHubToolWindowManager> gitHubToolWindowManager;
27+
readonly Lazy<DTE> dte;
28+
readonly IServiceProvider serviceProvider;
29+
30+
/// <summary>
31+
/// Gets the GUID of the group the command belongs to.
32+
/// </summary>
33+
public static readonly Guid CommandSet = Guids.guidGitHubCmdSet;
34+
35+
/// <summary>
36+
/// Gets the numeric identifier of the command.
37+
/// </summary>
38+
public const int CommandId = PkgCmdIDList.openFromClipboardCommand;
39+
40+
[ImportingConstructor]
41+
public OpenFromClipboardCommand(
42+
Lazy<GitHubContextService> gitHubContextService,
43+
Lazy<IRepositoryCloneService> repositoryCloneService,
44+
Lazy<IPullRequestEditorService> pullRequestEditorService,
45+
Lazy<ITeamExplorerContext> teamExplorerContext,
46+
[Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider) :
47+
base(CommandSet, CommandId)
48+
{
49+
this.gitHubContextService = gitHubContextService;
50+
this.repositoryCloneService = repositoryCloneService;
51+
this.pullRequestEditorService = pullRequestEditorService;
52+
this.teamExplorerContext = teamExplorerContext;
53+
this.serviceProvider = serviceProvider;
54+
dte = new Lazy<DTE>(() => (DTE)serviceProvider.GetService(typeof(DTE)));
55+
gitHubToolWindowManager = new Lazy<IGitHubToolWindowManager>(
56+
() => (IGitHubToolWindowManager)serviceProvider.GetService(typeof(IGitHubToolWindowManager)));
57+
58+
// See https://code.msdn.microsoft.com/windowsdesktop/AllowParams-2005-9442298f
59+
ParametersDescription = "u"; // accept a single url
60+
}
61+
62+
public override async Task Execute(string url)
63+
{
64+
if (string.IsNullOrEmpty(url))
65+
{
66+
url = Clipboard.GetText(TextDataFormat.Text);
67+
}
68+
69+
var context = gitHubContextService.Value.FindContextFromUrl(url);
70+
context = context ?? gitHubContextService.Value.FindContextFromBrowser();
71+
72+
if (context == null)
73+
{
74+
return;
75+
}
76+
77+
var activeDir = teamExplorerContext.Value.ActiveRepository?.LocalPath;
78+
if (activeDir != null)
79+
{
80+
// Try opening file in current context
81+
if (gitHubContextService.Value.TryOpenFile(activeDir, context))
82+
{
83+
return;
84+
}
85+
}
86+
87+
// Keep repos in unique dir while testing
88+
var defaultSubPath = "GitHubCache";
89+
90+
var cloneUrl = gitHubContextService.Value.ToRepositoryUrl(context).ToString();
91+
var targetDir = Path.Combine(repositoryCloneService.Value.DefaultClonePath, defaultSubPath, context.Owner);
92+
var repositoryDirName = context.RepositoryName;
93+
var repositoryDir = Path.Combine(targetDir, repositoryDirName);
94+
95+
if (!Directory.Exists(repositoryDir))
96+
{
97+
var result = ShowInfoMessage($"Clone {cloneUrl} to '{repositoryDir}'?");
98+
switch (result)
99+
{
100+
case VSConstants.MessageBoxResult.IDYES:
101+
await repositoryCloneService.Value.CloneRepository(cloneUrl, repositoryDirName, targetDir);
102+
// Open the cloned repository
103+
dte.Value.ExecuteCommand("File.OpenFolder", repositoryDir);
104+
dte.Value.ExecuteCommand("View.TfsTeamExplorer");
105+
break;
106+
case VSConstants.MessageBoxResult.IDNO:
107+
// Target the current solution
108+
repositoryDir = FindSolutionDirectory(dte.Value.Solution);
109+
if (repositoryDir == null)
110+
{
111+
// No current solution to use
112+
return;
113+
}
114+
115+
break;
116+
case VSConstants.MessageBoxResult.IDCANCEL:
117+
return;
118+
}
119+
}
120+
121+
var solutionDir = FindSolutionDirectory(dte.Value.Solution);
122+
if (solutionDir == null || !ContainsDirectory(repositoryDir, solutionDir))
123+
{
124+
var result = ShowInfoMessage($"Open repository at '{repositoryDir}'?");
125+
switch (result)
126+
{
127+
case VSConstants.MessageBoxResult.IDYES:
128+
// Open if current solution isn't in repository directory
129+
dte.Value.ExecuteCommand("File.OpenFolder", repositoryDir);
130+
dte.Value.ExecuteCommand("View.TfsTeamExplorer");
131+
break;
132+
case VSConstants.MessageBoxResult.IDNO:
133+
break;
134+
case VSConstants.MessageBoxResult.IDCANCEL:
135+
return;
136+
}
137+
}
138+
139+
await TryOpenPullRequest(context);
140+
gitHubContextService.Value.TryOpenFile(repositoryDir, context);
141+
}
142+
143+
VSConstants.MessageBoxResult ShowInfoMessage(string message)
144+
{
145+
return (VSConstants.MessageBoxResult)VsShellUtilities.ShowMessageBox(serviceProvider, message, null,
146+
OLEMSGICON.OLEMSGICON_QUERY, OLEMSGBUTTON.OLEMSGBUTTON_YESNOCANCEL, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
147+
}
148+
149+
static bool ContainsDirectory(string repositoryDir, string solutionDir)
150+
{
151+
if (solutionDir.Equals(repositoryDir, StringComparison.OrdinalIgnoreCase))
152+
{
153+
return true;
154+
}
155+
156+
if (solutionDir.StartsWith(repositoryDir + '\\', StringComparison.OrdinalIgnoreCase))
157+
{
158+
return true;
159+
}
160+
161+
return false;
162+
}
163+
164+
static string FindSolutionDirectory(Solution solution)
165+
{
166+
var solutionPath = solution.FileName;
167+
if (File.Exists(solutionPath))
168+
{
169+
return Path.GetDirectoryName(solutionPath);
170+
}
171+
172+
if (Directory.Exists(solutionPath))
173+
{
174+
return solutionPath;
175+
}
176+
177+
return null;
178+
}
179+
180+
async Task<bool> TryOpenPullRequest(GitHubContext context)
181+
{
182+
var pullRequest = context.PullRequest;
183+
if (pullRequest == null)
184+
{
185+
return false;
186+
}
187+
188+
var host = await gitHubToolWindowManager.Value.ShowGitHubPane();
189+
await host.ShowPullRequest(context.Owner, context.RepositoryName, pullRequest.Value);
190+
return true;
191+
}
192+
}
193+
}

src/GitHub.VisualStudio/GitHub.VisualStudio.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@
328328
<Compile Include="AssemblyResolverPackage.cs" />
329329
<Compile Include="Commands\BlameLinkCommand.cs" />
330330
<Compile Include="Commands\CreateGistCommand.cs" />
331+
<Compile Include="Commands\OpenFromClipboardCommand.cs" />
331332
<Compile Include="Commands\OpenFromUrlCommand.cs" />
332333
<Compile Include="Commands\ShowMessageBoxCommand.cs" />
333334
<Compile Include="Commands\LinkCommandBase.cs" />

src/GitHub.VisualStudio/GitHub.VisualStudio.vsct

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,17 @@
123123
</Strings>
124124
</Button>
125125

126+
<Button guid="guidGitHubCmdSet" id="openFromClipboardCommand" type="Button">
127+
<Icon guid="guidImages" id="logo" />
128+
<CommandFlag>IconIsMoniker</CommandFlag>
129+
<CommandFlag>AllowParams</CommandFlag>
130+
<Strings>
131+
<ButtonText>Open from clipboard</ButtonText>
132+
<CanonicalName>.GitHub.OpenFromClipboard</CanonicalName>
133+
<LocCanonicalName>.GitHub.OpenFromClipboard</LocCanonicalName>
134+
</Strings>
135+
</Button>
136+
126137
<!--- Toolbar buttons -->
127138
<Button guid="guidGitHubToolbarCmdSet" id="backCommand" type="Button">
128139
<Icon guid="guidImages" id="arrow_left" />
@@ -282,11 +293,15 @@
282293
<Parent guid="guidContextMenuSet" id="idGitHubContextSubMenuGroup"/>
283294
</CommandPlacement>
284295

285-
<CommandPlacement guid="guidContextMenuSet" id="idCreateGistCommand" priority="0x102">
296+
<CommandPlacement guid="guidGitHubCmdSet" id="openFromClipboardCommand" priority="0x0102">
297+
<Parent guid="guidContextMenuSet" id="idGitHubContextSubMenuGroup"/>
298+
</CommandPlacement>
299+
300+
<CommandPlacement guid="guidContextMenuSet" id="idCreateGistCommand" priority="0x103">
286301
<Parent guid="guidContextMenuSet" id="idGitHubContextSubMenuGroup"/>
287302
</CommandPlacement>
288303

289-
<CommandPlacement guid="guidContextMenuSet" id="idBlameCommand" priority="0x103">
304+
<CommandPlacement guid="guidContextMenuSet" id="idBlameCommand" priority="0x104">
290305
<Parent guid="guidContextMenuSet" id="idGitHubContextSubMenuGroup"/>
291306
</CommandPlacement>
292307

@@ -349,7 +364,8 @@
349364
<IDSymbol name="showGitHubPaneCommand" value="0x200"/>
350365
<IDSymbol name="showCurrentPullRequestCommand" value="0x202"/>
351366
<IDSymbol name="syncSubmodulesCommand" value="0x0203" />
352-
<IDSymbol name="openFromUrlCommand" value="0x0204" />
367+
<IDSymbol name="openFromUrlCommand" value="0x0204" />
368+
<IDSymbol name="openFromClipboardCommand" value="0x0205" />
353369
</GuidSymbol>
354370

355371
<!-- This is the Manage Connections menu -->

src/GitHub.VisualStudio/GitHubPackage.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ async Task InitializeMenus()
9191
exports.GetExportedValue<IShowGitHubPaneCommand>(),
9292
exports.GetExportedValue<IGoToSolutionOrPullRequestFileCommand>(),
9393
exports.GetExportedValue<ISyncSubmodulesCommand>(),
94-
exports.GetExportedValue<IOpenFromUrlCommand>()
94+
exports.GetExportedValue<IOpenFromUrlCommand>(),
95+
exports.GetExportedValue<IOpenFromClipboardCommand>()
9596
};
9697
}
9798
else

0 commit comments

Comments
 (0)