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

Commit 7207afb

Browse files
Merge pull request #1 from github/master
Update master
2 parents 5722b18 + 8629f72 commit 7207afb

File tree

9 files changed

+151
-19
lines changed

9 files changed

+151
-19
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ If you have an existing clone, make sure to run `git submodule sync` to update y
1212

1313
## About
1414

15-
The GitHub Extension for Visual Studio provides GitHub integration in Visual Studio 2015.
15+
The GitHub Extension for Visual Studio provides GitHub integration in Visual Studio 2015 and newer.
1616
Most of the extension UI lives in the Team Explorer pane, which is available from the View menu.
1717

1818
Official builds of this extension are available at [the official website](https://visualstudio.github.com).

src/GitHub.App/GitHub.App.csproj

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,18 @@
4646
<PackageReference Include="LibGit2Sharp.NativeBinaries" Version="1.0.164" />
4747
<PackageReference Include="Madskristensen.VisualStudio.SDK" Version="14.3.75-pre" />
4848
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.6.1" />
49+
<PackageReference Include="Microsoft.VisualStudio.StaticReviews.Embeddable" Version="0.1.14-alpha" />
4950
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
5051
<PackageReference Include="Rothko" Version="0.0.3-ghfvs" />
5152
<PackageReference Include="Serilog" Version="2.5.0" />
5253
<PackageReference Include="SerilogAnalyzer" Version="0.12.0.0" />
5354
<PackageReference Include="Stateless" Version="2.5.56.0" targetFramework="net45" />
5455
</ItemGroup>
55-
</Project>
56+
<Target Name="LinkStaticReviewsEmbeddableAssemblies" AfterTargets="ResolveReferences" BeforeTargets="FindReferenceAssembliesForReferences">
57+
<ItemGroup>
58+
<ReferencePath Condition="'%(FileName)' == 'Microsoft.VisualStudio.StaticReviews.Embeddable'">
59+
<EmbedInteropTypes>true</EmbedInteropTypes>
60+
</ReferencePath>
61+
</ItemGroup>
62+
</Target>
63+
</Project>

src/GitHub.App/Services/PullRequestService.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
using System.Reactive;
99
using System.Reactive.Linq;
1010
using System.Reactive.Threading.Tasks;
11+
using System.Runtime.InteropServices;
1112
using System.Text;
1213
using System.Text.RegularExpressions;
14+
using System.Threading;
1315
using System.Threading.Tasks;
1416
using System.Windows.Forms;
1517
using GitHub.Api;
@@ -19,6 +21,7 @@
1921
using GitHub.Models;
2022
using GitHub.Primitives;
2123
using LibGit2Sharp;
24+
using Microsoft.VisualStudio.StaticReviews.Contracts;
2225
using Octokit.GraphQL;
2326
using Octokit.GraphQL.Model;
2427
using Rothko;
@@ -32,7 +35,7 @@ namespace GitHub.Services
3235
{
3336
[Export(typeof(IPullRequestService))]
3437
[PartCreationPolicy(CreationPolicy.Shared)]
35-
public class PullRequestService : IPullRequestService
38+
public class PullRequestService : IPullRequestService, IStaticReviewFileMap
3639
{
3740
const string SettingCreatedByGHfVS = "created-by-ghfvs";
3841
const string SettingGHfVSPullRequest = "ghfvs-pr-owner-number";
@@ -58,6 +61,8 @@ public class PullRequestService : IPullRequestService
5861
readonly IOperatingSystem os;
5962
readonly IUsageTracker usageTracker;
6063

64+
readonly IDictionary<string, (string commitId, string repoPath)> tempFileMappings;
65+
6166
[ImportingConstructor]
6267
public PullRequestService(
6368
IGitClient gitClient,
@@ -73,6 +78,7 @@ public PullRequestService(
7378
this.graphqlFactory = graphqlFactory;
7479
this.os = os;
7580
this.usageTracker = usageTracker;
81+
this.tempFileMappings = new Dictionary<string, (string commitId, string repoPath)>(StringComparer.OrdinalIgnoreCase);
7682
}
7783

7884
public async Task<Page<PullRequestListItemModel>> ReadPullRequests(
@@ -755,6 +761,12 @@ public async Task<string> ExtractToTempFile(
755761
}
756762
}
757763

764+
lock (this.tempFileMappings)
765+
{
766+
string gitRelativePath = relativePath.TrimStart('/').Replace('\\', '/');
767+
this.tempFileMappings[CanonicalizeLocalFilePath(tempFilePath)] = (commitSha, gitRelativePath);
768+
}
769+
758770
return tempFilePath;
759771
}
760772

@@ -827,6 +839,28 @@ public bool ConfirmCancelPendingReview()
827839
MessageBoxIcon.Question) == DialogResult.Yes;
828840
}
829841

842+
/// <inheritdoc />
843+
public Task<string> GetObjectishFromLocalPathAsync(string localPath, CancellationToken cancellationToken)
844+
{
845+
lock (this.tempFileMappings)
846+
{
847+
var canonicalizedPath = CanonicalizeLocalFilePath(localPath);
848+
if (this.tempFileMappings.TryGetValue(canonicalizedPath, out (string commitId, string repoPath) result))
849+
{
850+
return Task.FromResult($"{result.commitId}:{result.repoPath}");
851+
}
852+
}
853+
854+
return Task.FromResult<string>(null);
855+
}
856+
857+
/// <inheritdoc />
858+
public Task<string> GetLocalPathFromObjectishAsync(string objectish, CancellationToken cancellationToken)
859+
{
860+
throw new NotImplementedException();
861+
}
862+
863+
830864
async Task<string> CreateRemote(IRepository repo, UriString cloneUri)
831865
{
832866
foreach (var remote in repo.Network.Remotes)
@@ -1006,6 +1040,12 @@ static string BuildGHfVSConfigKeyValue(string owner, int number)
10061040
return default;
10071041
}
10081042

1043+
static string CanonicalizeLocalFilePath(string localPath)
1044+
{
1045+
localPath = localPath.Replace("\\\\", "\\");
1046+
return Path.GetFullPath(localPath);
1047+
}
1048+
10091049
class ListItemAdapter : PullRequestListItemModel
10101050
{
10111051
public IList<ReviewAdapter> Reviews { get; set; }

src/GitHub.App/ViewModels/Dialog/Clone/RepositorySelectViewModel.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ public async Task Activate()
108108

109109
try
110110
{
111-
var results = await service.ReadViewerRepositories(connection.HostAddress).ConfigureAwait(true);
111+
var results = await log.TimeAsync(nameof(service.ReadViewerRepositories),
112+
() => service.ReadViewerRepositories(connection.HostAddress));
112113

113114
var yourRepositories = results.Repositories
114115
.Where(r => r.Owner == results.Owner)
@@ -121,6 +122,7 @@ public async Task Activate()
121122
.OrderBy(x => x.Key)
122123
.SelectMany(x => x.Value.Select(y => new RepositoryItemViewModel(y, x.Key)));
123124
Items = yourRepositories.Concat(collaboratorRepositories).Concat(orgRepositories).ToList();
125+
log.Information("Read {Total} viewer repositories", Items.Count);
124126
ItemsView = CollectionViewSource.GetDefaultView(Items);
125127
ItemsView.GroupDescriptions.Add(new PropertyGroupDescription(nameof(RepositoryItemViewModel.Group)));
126128
ItemsView.Filter = FilterItem;

src/GitHub.App/ViewModels/GitHubPane/PullRequestDetailViewModel.cs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Reactive;
88
using System.Reactive.Linq;
99
using System.Reactive.Threading.Tasks;
10+
using System.Threading;
1011
using System.Threading.Tasks;
1112
using GitHub.App;
1213
using GitHub.Commands;
@@ -17,6 +18,7 @@
1718
using GitHub.Models;
1819
using GitHub.Services;
1920
using LibGit2Sharp;
21+
using Microsoft.VisualStudio.StaticReviews.Contracts;
2022
using ReactiveUI;
2123
using ReactiveUI.Legacy;
2224
using Serilog;
@@ -28,7 +30,7 @@ namespace GitHub.ViewModels.GitHubPane
2830
/// <inheritdoc cref="IPullRequestDetailViewModel"/>
2931
[Export(typeof(IPullRequestDetailViewModel))]
3032
[PartCreationPolicy(CreationPolicy.NonShared)]
31-
public sealed class PullRequestDetailViewModel : PanePageViewModelBase, IPullRequestDetailViewModel
33+
public sealed class PullRequestDetailViewModel : PanePageViewModelBase, IPullRequestDetailViewModel, IStaticReviewFileMap
3234
{
3335
static readonly ILogger log = LogManager.ForContext<PullRequestDetailViewModel>();
3436

@@ -40,6 +42,7 @@ public sealed class PullRequestDetailViewModel : PanePageViewModelBase, IPullReq
4042
readonly ISyncSubmodulesCommand syncSubmodulesCommand;
4143
readonly IViewViewModelFactory viewViewModelFactory;
4244
readonly IGitService gitService;
45+
4346
IModelService modelService;
4447
PullRequestDetailModel model;
4548
IActorViewModel author;
@@ -98,6 +101,7 @@ public PullRequestDetailViewModel(
98101
this.syncSubmodulesCommand = syncSubmodulesCommand;
99102
this.viewViewModelFactory = viewViewModelFactory;
100103
this.gitService = gitService;
104+
101105
Files = files;
102106

103107
Checkout = ReactiveCommand.CreateFromObservable(
@@ -137,6 +141,9 @@ public PullRequestDetailViewModel(
137141
ShowAnnotations = ReactiveCommand.Create<IPullRequestCheckViewModel>(DoShowAnnotations);
138142
}
139143

144+
[Import(AllowDefault = true)]
145+
private IStaticReviewFileMapManager StaticReviewFileMapManager { get; set; }
146+
140147
private void DoOpenDetailsUrl()
141148
{
142149
usageTracker.IncrementCounter(measuresModel => measuresModel.NumberOfPRDetailsOpenInGitHub).Forget();
@@ -490,6 +497,7 @@ public string GetLocalFilePath(IPullRequestFileNode file)
490497
public override void Activated()
491498
{
492499
active = true;
500+
this.StaticReviewFileMapManager?.RegisterStaticReviewFileMap(this);
493501

494502
if (refreshOnActivate)
495503
{
@@ -499,7 +507,43 @@ public override void Activated()
499507
}
500508

501509
/// <inheritdoc/>
502-
public override void Deactivated() => active = false;
510+
public override void Deactivated()
511+
{
512+
this.StaticReviewFileMapManager?.UnregisterStaticReviewFileMap(this);
513+
active = false;
514+
}
515+
516+
/// <inheritdoc/>
517+
public Task<string> GetLocalPathFromObjectishAsync(string objectish, CancellationToken cancellationToken)
518+
{
519+
if (this.pullRequestsService != null)
520+
{
521+
string commitId = objectish.Substring(0, objectish.IndexOf(':'));
522+
string relativePath = objectish.Substring(objectish.IndexOf(':')+1).TrimStart('/');
523+
524+
return this.pullRequestsService.ExtractToTempFile(
525+
this.Session.LocalRepository,
526+
this.Session.PullRequest,
527+
relativePath,
528+
commitId,
529+
this.pullRequestsService.GetEncoding(this.Session.LocalRepository, relativePath));
530+
}
531+
532+
return Task.FromResult<string>(null);
533+
}
534+
535+
/// <inheritdoc/>
536+
public Task<string> GetObjectishFromLocalPathAsync(string localPath, CancellationToken cancellationToken)
537+
{
538+
// We rely on pull request service's global map here instead of trying to get it from IPullRequestSessionManager via ITextBuffer
539+
// because it is possible that the file queried wasn't opened by GitHub extension and instead was opened by LSP
540+
if (this.pullRequestsService is IStaticReviewFileMap staticReviewFileMap)
541+
{
542+
return staticReviewFileMap.GetObjectishFromLocalPathAsync(localPath, cancellationToken);
543+
}
544+
545+
return Task.FromResult<string>(null);
546+
}
503547

504548
/// <inheritdoc/>
505549
protected override void Dispose(bool disposing)

src/GitHub.InlineReviews/Services/PullRequestSessionService.cs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public IReadOnlyList<InlineAnnotationModel> BuildAnnotations(
9797
relativePath = relativePath.Replace("\\", "/");
9898

9999
return pullRequest.CheckSuites
100-
?.SelectMany(checkSuite => checkSuite.CheckRuns.Select(checkRun => new { checkSuite, checkRun}))
100+
?.SelectMany(checkSuite => checkSuite.CheckRuns.Select(checkRun => new { checkSuite, checkRun }))
101101
.SelectMany(arg =>
102102
arg.checkRun.Annotations
103103
.Where(annotation => annotation.Path == relativePath)
@@ -361,17 +361,21 @@ public virtual async Task<PullRequestDetailModel> ReadPullRequestDetail(HostAddr
361361
var result = await connection.Run(readPullRequest, vars);
362362

363363
var apiClient = await apiClientFactory.Create(address);
364-
var files = await apiClient.GetPullRequestFiles(owner, name, number).ToList();
365-
var lastCommitModel = await GetPullRequestLastCommitAdapter(address, owner, name, number);
366364

367-
result.Statuses = (IReadOnlyList<StatusModel>) lastCommitModel.Statuses ?? Array.Empty<StatusModel>();
365+
var files = await log.TimeAsync(nameof(apiClient.GetPullRequestFiles),
366+
async () => await apiClient.GetPullRequestFiles(owner, name, number).ToList());
367+
368+
var lastCommitModel = await log.TimeAsync(nameof(GetPullRequestLastCommitAdapter),
369+
() => GetPullRequestLastCommitAdapter(address, owner, name, number));
370+
371+
result.Statuses = (IReadOnlyList<StatusModel>)lastCommitModel.Statuses ?? Array.Empty<StatusModel>();
368372

369373
if (lastCommitModel.CheckSuites == null)
370374
{
371375
result.CheckSuites = Array.Empty<CheckSuiteModel>();
372376
}
373377
else
374-
{
378+
{
375379
result.CheckSuites = lastCommitModel.CheckSuites;
376380
foreach (var checkSuite in result.CheckSuites)
377381
{
@@ -838,8 +842,8 @@ async Task<LastCommitAdapter> GetPullRequestLastCommitAdapter(HostAddress addres
838842
commit => new LastCommitAdapter
839843
{
840844
Statuses = commit.Commit.Status == null ? null : commit.Commit.Status
841-
.Select(context => context == null
842-
? null
845+
.Select(context => context == null
846+
? null
843847
: context.Contexts
844848
.Select(statusContext => new StatusModel
845849
{
@@ -871,7 +875,7 @@ async Task<LastCommitAdapter> GetPullRequestLastCommitAdapter(HostAddress addres
871875
static void BuildPullRequestThreads(PullRequestDetailModel model)
872876
{
873877
var commentsByReplyId = new Dictionary<string, List<CommentAdapter>>();
874-
878+
875879
// Get all comments that are not replies.
876880
foreach (CommentAdapter comment in model.Reviews.SelectMany(x => x.Comments))
877881
{
@@ -961,5 +965,5 @@ class LastCommitAdapter
961965

962966
public string HeadSha { get; set; }
963967
}
964-
}
968+
}
965969
}

src/GitHub.Logging/Logging/ILoggerExtensions.cs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
using System;
2+
using System.Globalization;
3+
using System.Threading.Tasks;
14
using Serilog;
25

36
namespace GitHub.Logging
@@ -25,5 +28,37 @@ public static void Assert(this ILogger logger, bool condition, string messageTem
2528
#pragma warning restore Serilog004
2629
}
2730
}
31+
32+
public static void Time(this ILogger logger, string name, Action method)
33+
{
34+
var startTime = DateTime.Now;
35+
method();
36+
logger.Verbose("{Name} took {Seconds} seconds", name, FormatSeconds(DateTime.Now - startTime));
37+
}
38+
39+
public static T Time<T>(this ILogger logger, string name, Func<T> method)
40+
{
41+
var startTime = DateTime.Now;
42+
var value = method();
43+
logger.Verbose("{Name} took {Seconds} seconds", name, FormatSeconds(DateTime.Now - startTime));
44+
return value;
45+
}
46+
47+
public static async Task TimeAsync(this ILogger logger, string name, Func<Task> methodAsync)
48+
{
49+
var startTime = DateTime.Now;
50+
await methodAsync().ConfigureAwait(false);
51+
logger.Verbose("{Name} took {Seconds} seconds", name, FormatSeconds(DateTime.Now - startTime));
52+
}
53+
54+
public static async Task<T> TimeAsync<T>(this ILogger logger, string name, Func<Task<T>> methodAsync)
55+
{
56+
var startTime = DateTime.Now;
57+
var value = await methodAsync().ConfigureAwait(false);
58+
logger.Verbose("{Name} took {Seconds} seconds", name, FormatSeconds(DateTime.Now - startTime));
59+
return value;
60+
}
61+
62+
static string FormatSeconds(TimeSpan timeSpan) => timeSpan.TotalSeconds.ToString("0.##", CultureInfo.InvariantCulture);
2863
}
29-
}
64+
}

src/GitHub.UI/GitHub.UI.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<PropertyGroup>
33
<TargetFramework>net46</TargetFramework>
44
<ExtrasEnableWpfProjectSetup>true</ExtrasEnableWpfProjectSetup>
5+
<ExtrasEnableDefaultResourceItems>false</ExtrasEnableDefaultResourceItems>
56
<DebugType>full</DebugType>
67
<DebugSymbols>true</DebugSymbols>
78
</PropertyGroup>

test/GitHub.App.UnitTests/Models/ModelServiceTests.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -374,9 +374,7 @@ public async Task VaccumsTheCacheAsync()
374374

375375
static ModelService CreateTarget(
376376
IApiClient apiClient = null,
377-
Octokit.IConnection graphql = null,
378-
IBlobCache hostCache = null,
379-
IAvatarProvider avatarProvider = null)
377+
IBlobCache hostCache = null)
380378
{
381379
return new ModelService(
382380
apiClient ?? Substitute.For<IApiClient>(),

0 commit comments

Comments
 (0)