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

Commit 584f70b

Browse files
authored
Merge pull request #940 from github/release/2.3
Release 2.3
2 parents dd0c5ea + 6982360 commit 584f70b

File tree

13 files changed

+148
-50
lines changed

13 files changed

+148
-50
lines changed

src/GitHub.App/SampleData/LocalRepositoryModelDesigner.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using GitHub.Primitives;
66
using GitHub.UI;
77
using NullGuard;
8+
using GitHub.Exports;
89

910
namespace GitHub.App.SampleData
1011
{
@@ -22,7 +23,7 @@ public class LocalRepositoryModelDesigner : ILocalRepositoryModel
2223
public event PropertyChangedEventHandler PropertyChanged;
2324
#pragma warning restore CS0067
2425

25-
public Task<UriString> GenerateUrl(string path = null, int startLine = -1, int endLine = -1)
26+
public Task<UriString> GenerateUrl(LinkType linkType, string path = null, int startLine = -1, int endLine = -1)
2627
{
2728
throw new NotImplementedException();
2829
}

src/GitHub.Exports/Exports/ExportMetadata.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ public enum MenuType
4444
OpenPullRequests
4545
}
4646

47+
/// <summary>
48+
/// Defines the type of repository link to navigate to
49+
/// </summary>
50+
public enum LinkType
51+
{
52+
Blob,
53+
Blame
54+
}
55+
4756
/// <summary>
4857
/// A MEF export attribute that defines an export of type <see cref="IViewModel"/> with
4958
/// <see cref="UIViewType"/> metadata.

src/GitHub.Exports/Models/ILocalRepositoryModel.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using GitHub.Primitives;
1+
using GitHub.Exports;
2+
using GitHub.Primitives;
23
using System.ComponentModel;
34
using System.Threading.Tasks;
45

@@ -32,6 +33,6 @@ public interface ILocalRepositoryModel : IRepositoryModel, INotifyPropertyChange
3233
/// <param name="startLine">A specific line, or (if specifying the <paramref name="endLine"/> as well) the start of a range</param>
3334
/// <param name="endLine">The end of a line range on the specified file.</param>
3435
/// <returns>An UriString with the generated url, or null if the repository has no remote server configured or if it can't be found locally</returns>
35-
Task<UriString> GenerateUrl(string path = null, int startLine = -1, int endLine = -1);
36+
Task<UriString> GenerateUrl(LinkType linkType, string path = null, int startLine = -1, int endLine = -1);
3637
}
3738
}

src/GitHub.Exports/Models/LocalRepositoryModel.cs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using GitHub.Services;
99
using GitHub.Extensions;
1010
using System.Threading.Tasks;
11+
using GitHub.Exports;
1112

1213
namespace GitHub.Models
1314
{
@@ -57,11 +58,12 @@ public void Refresh()
5758
/// Generates a http(s) url to the repository in the remote server, optionally
5859
/// pointing to a specific file and specific line range in it.
5960
/// </summary>
61+
/// <param name="linkType">Type of link to generate</param>
6062
/// <param name="path">The file to generate an url to. Optional.</param>
6163
/// <param name="startLine">A specific line, or (if specifying the <paramref name="endLine"/> as well) the start of a range</param>
6264
/// <param name="endLine">The end of a line range on the specified file.</param>
6365
/// <returns>An UriString with the generated url, or null if the repository has no remote server configured or if it can't be found locally</returns>
64-
public async Task<UriString> GenerateUrl(string path = null, int startLine = -1, int endLine = -1)
66+
public async Task<UriString> GenerateUrl(LinkType linkType, string path = null, int startLine = -1, int endLine = -1)
6567
{
6668
if (CloneUrl == null)
6769
return null;
@@ -97,22 +99,24 @@ public async Task<UriString> GenerateUrl(string path = null, int startLine = -1,
9799
endLine = -1;
98100
}
99101

100-
return new UriString(GenerateUrl(CloneUrl.ToRepositoryUrl().AbsoluteUri, sha, path, startLine, endLine));
102+
return new UriString(GenerateUrl(linkType, CloneUrl.ToRepositoryUrl().AbsoluteUri, sha, path, startLine, endLine));
101103
}
102104

103105
const string CommitFormat = "{0}/commit/{1}";
104106
const string BlobFormat = "{0}/blob/{1}/{2}";
107+
const string BlameFormat = "{0}/blame/{1}/{2}";
105108
const string StartLineFormat = "{0}#L{1}";
106109
const string EndLineFormat = "{0}-L{1}";
107-
static string GenerateUrl(string basePath, string sha, string path, int startLine = -1, int endLine = -1)
110+
static string GenerateUrl(LinkType linkType, string basePath, string sha, string path, int startLine = -1, int endLine = -1)
108111
{
109112
if (sha == null)
110113
return basePath;
111114

112115
if (String.IsNullOrEmpty(path))
113116
return String.Format(CultureInfo.InvariantCulture, CommitFormat, basePath, sha);
114117

115-
var ret = String.Format(CultureInfo.InvariantCulture, BlobFormat, basePath, sha, path.Replace(@"\", "/"));
118+
var ret = String.Format(CultureInfo.InvariantCulture, GetLinkFormat(linkType), basePath, sha, path.Replace(@"\", "/"));
119+
116120
if (startLine < 0)
117121
return ret;
118122
ret = String.Format(CultureInfo.InvariantCulture, StartLineFormat, ret, startLine);
@@ -121,6 +125,26 @@ static string GenerateUrl(string basePath, string sha, string path, int startLin
121125
return String.Format(CultureInfo.InvariantCulture, EndLineFormat, ret, endLine);
122126
}
123127

128+
/// <summary>
129+
/// Selects the proper format for the link type, defaults to the blob url when link type is not selected.
130+
/// </summary>
131+
/// <param name="linkType">Type of link to generate</param>
132+
/// <returns>The string format of the selected link type</returns>
133+
static string GetLinkFormat(LinkType linkType)
134+
{
135+
switch (linkType)
136+
{
137+
case LinkType.Blame:
138+
return BlameFormat;
139+
140+
case LinkType.Blob:
141+
return BlobFormat;
142+
143+
default:
144+
return BlobFormat;
145+
}
146+
}
147+
124148
/// <summary>
125149
/// Gets the local path of the repository.
126150
/// </summary>

src/GitHub.VisualStudio/GitHub.VisualStudio.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@
303303
<Compile Include="AssemblyResolverPackage.cs" />
304304
<Compile Include="Helpers\VisualTreeExtensions.cs" />
305305
<Compile Include="IServiceProviderPackage.cs" />
306+
<Compile Include="Menus\BlameLink.cs" />
306307
<Compile Include="Menus\MenuBase.cs" />
307308
<Compile Include="Menus\CreateGist.cs" />
308309
<Compile Include="Helpers\SharedDictionaryManager.cs" />

src/GitHub.VisualStudio/GitHub.VisualStudio.vsct

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,17 @@
159159
<ButtonText>Copy link to clipboard</ButtonText>
160160
</Strings>
161161
</Button>
162+
163+
<Button guid="guidContextMenuSet" id="idBlameCommand" type="Button">
164+
<Icon guid="guidImages" id="link_external" />
165+
<CommandFlag>IconIsMoniker</CommandFlag>
166+
<CommandFlag>DefaultInvisible</CommandFlag>
167+
<CommandFlag>DynamicVisibility</CommandFlag>
168+
<Strings>
169+
<ButtonText>Blame</ButtonText>
170+
</Strings>
171+
</Button>
172+
162173
</Buttons>
163174

164175
</Commands>
@@ -195,6 +206,11 @@
195206
<Parent guid="guidContextMenuSet" id="idGitHubContextSubMenuGroup"/>
196207
</CommandPlacement>
197208

209+
<CommandPlacement guid="guidContextMenuSet" id="idBlameCommand" priority="0x103">
210+
<Parent guid="guidContextMenuSet" id="idGitHubContextSubMenuGroup"/>
211+
</CommandPlacement>
212+
213+
198214
<!-- Standard toolbar commands -->
199215
<CommandPlacement guid="guidGitHubToolbarCmdSet" id="backCommand" priority="0x100">
200216
<Parent guid="guidGitHubToolbarCmdSet" id="idGitHubToolbarMenuGroup"/>
@@ -275,6 +291,8 @@
275291
<IDSymbol name="openLinkCommand" value="0x100" />
276292
<IDSymbol name="copyLinkCommand" value="0x101"/>
277293
<IDSymbol name="idCreateGistCommand" value="0x0400" />
294+
<IDSymbol name="idBlameCommand" value="0x0500" />
295+
278296
</GuidSymbol>
279297

280298
<GuidSymbol name="GUID_XAML_EDITOR" value="{4C87B692-1202-46AA-B64C-EF01FAEC53DA}">
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using GitHub.Exports;
2+
using GitHub.Services;
3+
using GitHub.VisualStudio.UI;
4+
using NullGuard;
5+
using System;
6+
using System.Windows;
7+
8+
namespace GitHub.VisualStudio.Menus
9+
{
10+
public class BlameLink : LinkMenuBase, IDynamicMenuHandler
11+
{
12+
public BlameLink(IGitHubServiceProvider serviceProvider)
13+
: base(serviceProvider)
14+
{
15+
}
16+
17+
public Guid Guid => GuidList.guidContextMenuSet;
18+
public int CmdId => PkgCmdIDList.blameCommand;
19+
20+
public async void Activate([AllowNull]object data = null)
21+
{
22+
var isgithub = await IsGitHubRepo();
23+
if (!isgithub)
24+
return;
25+
26+
var link = await GenerateLink(LinkType.Blame);
27+
if (link == null)
28+
return;
29+
var browser = ServiceProvider.TryGetService<IVisualStudioBrowser>();
30+
browser?.OpenUrl(link.ToUri());
31+
32+
await UsageTracker.IncrementOpenInGitHubCount();
33+
}
34+
}
35+
}

src/GitHub.VisualStudio/Menus/CopyLink.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using GitHub.Services;
1+
using GitHub.Exports;
2+
using GitHub.Services;
23
using GitHub.VisualStudio.UI;
34
using NullGuard;
45
using System;
@@ -23,7 +24,7 @@ public async void Activate([AllowNull]object data = null)
2324
if (!isgithub)
2425
return;
2526

26-
var link = await GenerateLink();
27+
var link = await GenerateLink(LinkType.Blob);
2728
if (link == null)
2829
return;
2930
try
@@ -39,11 +40,5 @@ public async void Activate([AllowNull]object data = null)
3940
ns?.ShowMessage(Resources.Error_FailedToCopyToClipboard);
4041
}
4142
}
42-
43-
public bool CanShow()
44-
{
45-
var githubRepoCheckTask = IsCurrentFileInGitHubRepository();
46-
return githubRepoCheckTask.Wait(250) ? githubRepoCheckTask.Result : false;
47-
}
4843
}
4944
}

src/GitHub.VisualStudio/Menus/LinkMenuBase.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using GitHub.Primitives;
1+
using GitHub.Exports;
2+
using GitHub.Primitives;
23
using GitHub.Services;
34
using System;
45
using System.Threading.Tasks;
@@ -30,13 +31,19 @@ protected async Task<bool> IsCurrentFileInGitHubRepository()
3031
IsFileDescendantOfDirectory(activeDocument.Name, ActiveRepo.LocalPath);
3132
}
3233

33-
protected Task<UriString> GenerateLink()
34+
protected Task<UriString> GenerateLink(LinkType linkType)
3435
{
3536
var repo = ActiveRepo;
3637
var activeDocument = ServiceProvider.TryGetService<IActiveDocumentSnapshot>();
3738
if (activeDocument == null)
3839
return null;
39-
return repo.GenerateUrl(activeDocument.Name, activeDocument.StartLine, activeDocument.EndLine);
40+
return repo.GenerateUrl(linkType, activeDocument.Name, activeDocument.StartLine, activeDocument.EndLine);
41+
}
42+
43+
public bool CanShow()
44+
{
45+
var githubRepoCheckTask = IsCurrentFileInGitHubRepository();
46+
return githubRepoCheckTask.Wait(250) ? githubRepoCheckTask.Result : false;
4047
}
4148

4249
// Taken from http://stackoverflow.com/a/26012991/6448

src/GitHub.VisualStudio/Menus/MenuProvider.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ public MenuProvider(IGitHubServiceProvider serviceProvider)
5050
{
5151
new CopyLink(serviceProvider),
5252
new CreateGist(serviceProvider),
53-
new OpenLink(serviceProvider)
53+
new OpenLink(serviceProvider),
54+
new BlameLink(serviceProvider)
5455
};
5556
}
5657
}

0 commit comments

Comments
 (0)