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

Commit 2243891

Browse files
committed
Add ResolvePath to resolve Treeish + BlobName
1 parent cb7816c commit 2243891

File tree

3 files changed

+123
-70
lines changed

3 files changed

+123
-70
lines changed

src/GitHub.App/Services/GitHubContext.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ public class GitHubContext
88
public string RepositoryName { get; set; }
99
public string Host { get; set; }
1010
public string BranchName { get; set; }
11-
public string CommitSha { get; set; }
11+
public string Treeish { get; set; }
12+
public string BlobName { get; set; }
1213
public int? PullRequest { get; set; }
1314
public int? Issue { get; set; }
14-
public string Path { get; set; }
1515
public int? Line { get; set; }
1616
public int? LineEnd { get; set; }
1717
}

src/GitHub.App/Services/GitHubContextService.cs

Lines changed: 73 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,22 @@ public class GitHubContextService
3333

3434
const string issue = "(?<issue>[0-9]+)";
3535

36-
static readonly string path = $"^{repo}/(?<path>[^ ]+)";
36+
static readonly string tree = $"^{repo}/(?<tree>[^ ]+)";
37+
static readonly string blobName = $"^{repo}/(?<blobName>[^ /]+)";
3738

3839
static readonly Regex windowTitleRepositoryRegex = new Regex($"^(GitHub - )?{owner}/{repo}(: .*)? - ", RegexOptions.Compiled);
3940
static readonly Regex windowTitleBranchRegex = new Regex($"^(GitHub - )?{owner}/{repo} at {branch} ", RegexOptions.Compiled);
4041
static readonly Regex windowTitlePullRequestRegex = new Regex($" · Pull Request #{pull} · {owner}/{repo}( · GitHub)? - ", RegexOptions.Compiled);
4142
static readonly Regex windowTitleIssueRegex = new Regex($" · Issue #{issue} · {owner}/{repo}( · GitHub)? - ", RegexOptions.Compiled);
42-
static readonly Regex windowTitlePathRegex = new Regex($"{path} at {branch} · {owner}/{repo}( · GitHub)? - ", RegexOptions.Compiled);
43+
static readonly Regex windowTitleBlobRegex = new Regex($"{blobName} at {branch} · {owner}/{repo}( · GitHub)? - ", RegexOptions.Compiled);
44+
static readonly Regex windowTitleTreeRegex = new Regex($"{tree} at {branch} · {owner}/{repo}( · GitHub)? - ", RegexOptions.Compiled);
4345
static readonly Regex windowTitleBranchesRegex = new Regex($"Branches · {owner}/{repo}( · GitHub)? - ", RegexOptions.Compiled);
4446

4547
static readonly Regex urlLineRegex = new Regex($"#L(?<line>[0-9]+)(-L(?<lineEnd>[0-9]+))?$", RegexOptions.Compiled);
46-
static readonly Regex urlBlobCommitRegex = new Regex($"blob/(?<commit>[a-z0-9]{{40}})/(?<path>[^#]*)", RegexOptions.Compiled);
47-
static readonly Regex urlBlobBranchRegex = new Regex($"blob/(?<branch>master)/(?<path>[^#]*)", RegexOptions.Compiled);
48+
static readonly Regex urlBlobRegex = new Regex($"blob/(?<treeish>[^/]+(/[^/]+)*)/(?<blobName>[^/#]+)", RegexOptions.Compiled);
49+
50+
static readonly Regex treeishCommitRegex = new Regex($"(?<commit>[a-z0-9]{{40}})(/(?<tree>.+))?", RegexOptions.Compiled);
51+
static readonly Regex treeishBranchRegex = new Regex($"(?<branch>master)(/(?<tree>.+))?", RegexOptions.Compiled);
4852

4953
[ImportingConstructor]
5054
public GitHubContextService([Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider)
@@ -84,19 +88,11 @@ public GitHubContext FindContextFromUrl(string url)
8488

8589
context.PullRequest = FindPullRequest(url);
8690

87-
var match = urlBlobCommitRegex.Match(subpath);
88-
if (match.Success)
89-
{
90-
context.CommitSha = match.Groups["commit"].Value;
91-
context.Path = match.Groups["path"].Value;
92-
return context;
93-
}
94-
95-
match = urlBlobBranchRegex.Match(subpath);
91+
var match = urlBlobRegex.Match(subpath);
9692
if (match.Success)
9793
{
98-
context.BranchName = match.Groups["branch"].Value;
99-
context.Path = match.Groups["path"].Value;
94+
context.Treeish = match.Groups["treeish"].Value;
95+
context.BlobName = match.Groups["blobName"].Value;
10096
return context;
10197
}
10298

@@ -139,15 +135,27 @@ public Uri ToRepositoryUrl(GitHubContext context)
139135

140136
public GitHubContext FindContextFromWindowTitle(string windowTitle)
141137
{
142-
var match = windowTitlePathRegex.Match(windowTitle);
138+
var match = windowTitleBlobRegex.Match(windowTitle);
143139
if (match.Success)
144140
{
145141
return new GitHubContext
146142
{
147143
Owner = match.Groups["owner"].Value,
148144
RepositoryName = match.Groups["repo"].Value,
149145
BranchName = match.Groups["branch"].Value,
150-
Path = match.Groups["path"].Value
146+
BlobName = match.Groups["blobName"].Value
147+
};
148+
}
149+
150+
match = windowTitleTreeRegex.Match(windowTitle);
151+
if (match.Success)
152+
{
153+
return new GitHubContext
154+
{
155+
Owner = match.Groups["owner"].Value,
156+
RepositoryName = match.Groups["repo"].Value,
157+
BranchName = match.Groups["branch"].Value,
158+
Treeish = $"{match.Groups["branch"].Value}/{match.Groups["tree"].Value}"
151159
};
152160
}
153161

@@ -213,24 +221,30 @@ public GitHubContext FindContextFromWindowTitle(string windowTitle)
213221

214222
public bool TryOpenFile(GitHubContext context, string repositoryDir)
215223
{
216-
var path = context.Path;
217-
if (path == null)
224+
var fileName = context.BlobName;
225+
if (fileName == null)
218226
{
219227
return false;
220228
}
221229

222-
var windowsPath = path.Replace('/', '\\');
223-
var fullPath = Path.Combine(repositoryDir, windowsPath);
224-
if (!File.Exists(fullPath))
230+
string fullPath;
231+
var resolvedPath = ResolvePath(context);
232+
if (resolvedPath != null)
225233
{
226-
// Search by filename only
227-
var fileName = Path.GetFileName(path);
228-
fullPath = Directory.EnumerateFiles(repositoryDir, fileName, SearchOption.AllDirectories).FirstOrDefault();
234+
fullPath = Path.Combine(repositoryDir, resolvedPath);
235+
if (!File.Exists(fullPath))
236+
{
237+
return false;
238+
}
229239
}
230-
231-
if (fullPath == null)
240+
else
232241
{
233-
return false;
242+
// Search by filename only
243+
fullPath = Directory.EnumerateFiles(repositoryDir, fileName, SearchOption.AllDirectories).FirstOrDefault();
244+
if (fullPath == null)
245+
{
246+
return false;
247+
}
234248
}
235249

236250
var textView = OpenDocument(fullPath);
@@ -250,6 +264,37 @@ public bool TryOpenFile(GitHubContext context, string repositoryDir)
250264
return true;
251265
}
252266

267+
public string ResolvePath(GitHubContext context)
268+
{
269+
var treeish = context.Treeish;
270+
if (treeish == null)
271+
{
272+
return null;
273+
}
274+
275+
var blobName = context.BlobName;
276+
if (blobName == null)
277+
{
278+
return null;
279+
}
280+
281+
var match = treeishCommitRegex.Match(treeish);
282+
if (match.Success)
283+
{
284+
var tree = match.Groups["tree"].Value.Replace('/', '\\');
285+
return Path.Combine(tree, blobName);
286+
}
287+
288+
match = treeishBranchRegex.Match(treeish);
289+
if (match.Success)
290+
{
291+
var tree = match.Groups["tree"].Value.Replace('/', '\\');
292+
return Path.Combine(tree, blobName);
293+
}
294+
295+
return null;
296+
}
297+
253298
IVsTextView OpenDocument(string fullPath)
254299
{
255300
var logicalView = VSConstants.LOGVIEWID.TextView_guid;

test/GitHub.App.UnitTests/Services/GitHubContextServiceTests.cs

Lines changed: 48 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -63,46 +63,20 @@ public void PullRequest(string url, int? expectPullRequest)
6363
Assert.That(context?.PullRequest, Is.EqualTo(expectPullRequest));
6464
}
6565

66-
[TestCase("https://github.com/github/VisualStudio/blob/master", null, null, null)]
67-
[TestCase("https://github.com/github/VisualStudio/blob/master/foo.cs", "master", null, "foo.cs")]
68-
[TestCase("https://github.com/github/VisualStudio/blob/master/path/foo.cs", "master", null, "path/foo.cs")]
69-
[TestCase("https://github.com/github/VisualStudio/blob/ee863ce265fc6217f589e66766125fed1b5b8256/path/foo.cs", null, "ee863ce265fc6217f589e66766125fed1b5b8256", "path/foo.cs")]
70-
[TestCase("https://github.com/github/VisualStudio/blob/not_master/foo.cs", null, null, null, Description = "We currently only match SHA and master")]
71-
public void Blob(string url, string expectBranch, string expectCommitSha, string expectPath)
66+
[TestCase("https://github.com/github/VisualStudio/blob/master", null, null)]
67+
[TestCase("https://github.com/github/VisualStudio/blob/master/foo.cs", "master", "foo.cs")]
68+
[TestCase("https://github.com/github/VisualStudio/blob/master/path/foo.cs", "master/path", "foo.cs")]
69+
[TestCase("https://github.com/github/VisualStudio/blob/ee863ce265fc6217f589e66766125fed1b5b8256/foo.cs", "ee863ce265fc6217f589e66766125fed1b5b8256", "foo.cs")]
70+
[TestCase("https://github.com/github/VisualStudio/blob/ee863ce265fc6217f589e66766125fed1b5b8256/path/foo.cs", "ee863ce265fc6217f589e66766125fed1b5b8256/path", "foo.cs")]
71+
[TestCase("https://github.com/github/VisualStudio/blob/master/bar.cs#stuff", "master", "bar.cs")]
72+
public void Blob(string url, string expectTreeish, string expectBlobName)
7273
{
7374
var target = CreateGitHubContextService();
7475

7576
var context = target.FindContextFromUrl(url);
7677

77-
Assert.That(context.BranchName, Is.EqualTo(expectBranch));
78-
Assert.That(context.CommitSha, Is.EqualTo(expectCommitSha));
79-
Assert.That(context.Path, Is.EqualTo(expectPath));
80-
}
81-
82-
[TestCase("https://github.com", null)]
83-
[TestCase("https://github.com/github", null)]
84-
[TestCase("https://github.com/github/VisualStudio", null)]
85-
[TestCase("https://github.com/github/VisualStudio/blob/master/README.md", "README.md")]
86-
[TestCase("https://github.com/github/VisualStudio/blob/master/README.md#notices", "README.md")]
87-
[TestCase("https://github.com/github/VisualStudio/blob/0d264d50c57d701fa62d202f481075a6c6dbdce8/src/Code.cs#L86", "src/Code.cs")]
88-
public void Path(string url, string expectPath)
89-
{
90-
var target = CreateGitHubContextService();
91-
92-
var context = target.FindContextFromUrl(url);
93-
94-
Assert.That(context.Path, Is.EqualTo(expectPath));
95-
}
96-
97-
// HACK: We're assuming that branches don't contain a '/' (sic)
98-
[TestCase("https://github.com/github/VisualStudio/blob/fixes/branch/buggy.cs", "branch/buggy.cs")]
99-
public void ProblemPath(string url, string expectPath)
100-
{
101-
var target = CreateGitHubContextService();
102-
103-
var context = target.FindContextFromUrl(url);
104-
105-
Assert.That(context.Path, Is.EqualTo(expectPath));
78+
Assert.That(context.Treeish, Is.EqualTo(expectTreeish));
79+
Assert.That(context.BlobName, Is.EqualTo(expectBlobName));
10680
}
10781

10882
[TestCase("https://github.com", null)]
@@ -307,16 +281,29 @@ public void Issue(string windowTitle, int expectIssue)
307281
Assert.That(context.Issue, Is.EqualTo(expectIssue));
308282
}
309283

310-
[TestCase("VisualStudio/mark_github.xaml at master · github/VisualStudio - Google Chrome", "mark_github.xaml")]
311-
[TestCase("VisualStudio/src/GitHub.VisualStudio/Resources/icons at master · github/VisualStudio - Google Chrome", "src/GitHub.VisualStudio/Resources/icons")]
312-
[TestCase("VisualStudio/README.md at master · jcansdale/VisualStudio · GitHub - Mozilla Firefox", "README.md", Description = "Firefox")]
313-
public void Path(string windowTitle, string expectPath)
284+
[TestCase("VisualStudio/mark_github.xaml at master · github/VisualStudio - Google Chrome", "mark_github.xaml", "master")]
285+
[TestCase("VisualStudio/src/GitHub.VisualStudio/Resources/icons at master · github/VisualStudio - Google Chrome", null, "master")]
286+
[TestCase("VisualStudio/src at master · github/VisualStudio - Google Chrome", "src", "master", Description = "Can't differentiate between single level tree and blob")]
287+
[TestCase("VisualStudio/README.md at master · jcansdale/VisualStudio · GitHub - Mozilla Firefox", "README.md", "master", Description = "Firefox")]
288+
public void Blob(string windowTitle, string expectBlobName, string expectBranchName)
314289
{
315290
var target = CreateGitHubContextService();
316291

317292
var context = target.FindContextFromWindowTitle(windowTitle);
318293

319-
Assert.That(context?.Path, Is.EqualTo(expectPath));
294+
Assert.That(context?.BlobName, Is.EqualTo(expectBlobName));
295+
Assert.That(context?.BranchName, Is.EqualTo(expectBranchName));
296+
}
297+
298+
[TestCase("VisualStudio/src/GitHub.VisualStudio/Resources/icons at master · github/VisualStudio - Google Chrome", "master/src/GitHub.VisualStudio/Resources/icons", "master")]
299+
public void Tree(string windowTitle, string expectTreeish, string expectBranch)
300+
{
301+
var target = CreateGitHubContextService();
302+
303+
var context = target.FindContextFromWindowTitle(windowTitle);
304+
305+
Assert.That(context?.Treeish, Is.EqualTo(expectTreeish));
306+
Assert.That(context?.BranchName, Is.EqualTo(expectBranch));
320307
}
321308

322309
[TestCase("jcansdale/VisualStudio: GitHub Extension for Visual Studio - Google Chrome", "jcansdale", "VisualStudio", Description = "Chrome")]
@@ -334,6 +321,27 @@ public void RepositoryHome(string windowTitle, string expectOwner, string expect
334321
}
335322
}
336323

324+
public class TheResolvePathMethod
325+
{
326+
[TestCase("https://github.com/github/VisualStudio/blob/ee863ce265fc6217f589e66766125fed1b5b8256/foo.cs", "foo.cs")]
327+
[TestCase("https://github.com/github/VisualStudio/blob/ee863ce265fc6217f589e66766125fed1b5b8256/dir/foo.cs", @"dir\foo.cs")]
328+
[TestCase("https://github.com/github/VisualStudio/blob/ee863ce265fc6217f589e66766125fed1b5b8256/dir/subdir/foo.cs", @"dir\subdir\foo.cs")]
329+
[TestCase("https://github.com/github/VisualStudio/blob/master/foo.cs", "foo.cs")]
330+
[TestCase("https://github.com/github/VisualStudio/blob/master/dir/foo.cs", @"dir\foo.cs")]
331+
[TestCase("https://github.com/github/VisualStudio/blob/master/dir/subdir/foo.cs", @"dir\subdir\foo.cs")]
332+
[TestCase("https://github.com/github/VisualStudio/blob/unknown-branch/dir/subdir/foo.cs", null)]
333+
[TestCase("https://github.com/github/VisualStudio/blob/unknown/branch/dir/subdir/foo.cs", null)]
334+
public void ResolvePath(string url, string expectPath)
335+
{
336+
var target = CreateGitHubContextService();
337+
var context = target.FindContextFromUrl(url);
338+
339+
var path = target.ResolvePath(context);
340+
341+
Assert.That(path, Is.EqualTo(expectPath));
342+
}
343+
}
344+
337345
static GitHubContextService CreateGitHubContextService()
338346
{
339347
var sp = Substitute.For<IServiceProvider>();

0 commit comments

Comments
 (0)