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

Commit ca6de27

Browse files
authored
Merge branch 'master' into fixes/1469-indent-heuristic
2 parents df677af + 306bd6b commit ca6de27

File tree

14 files changed

+194
-33
lines changed

14 files changed

+194
-33
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
@@ -45,10 +45,18 @@
4545
<PackageReference Include="LibGit2Sharp" Version="0.26.0-preview-0080" />
4646
<PackageReference Include="Madskristensen.VisualStudio.SDK" Version="14.3.75-pre" />
4747
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.6.1" />
48+
<PackageReference Include="Microsoft.VisualStudio.StaticReviews.Embeddable" Version="0.1.14-alpha" />
4849
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
4950
<PackageReference Include="Rothko" Version="0.0.3-ghfvs" />
5051
<PackageReference Include="Serilog" Version="2.5.0" />
5152
<PackageReference Include="SerilogAnalyzer" Version="0.12.0.0" />
5253
<PackageReference Include="Stateless" Version="2.5.56.0" targetFramework="net45" />
5354
</ItemGroup>
54-
</Project>
55+
<Target Name="LinkStaticReviewsEmbeddableAssemblies" AfterTargets="ResolveReferences" BeforeTargets="FindReferenceAssembliesForReferences">
56+
<ItemGroup>
57+
<ReferencePath Condition="'%(FileName)' == 'Microsoft.VisualStudio.StaticReviews.Embeddable'">
58+
<EmbedInteropTypes>true</EmbedInteropTypes>
59+
</ReferencePath>
60+
</ItemGroup>
61+
</Target>
62+
</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/Services/RepositoryCloneService.cs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.IO;
55
using System.Linq;
66
using System.Reactive.Linq;
7+
using System.Threading;
78
using System.Threading.Tasks;
89
using GitHub.Api;
910
using GitHub.Extensions;
@@ -69,13 +70,8 @@ public async Task<ViewerRepositoriesModel> ReadViewerRepositories(HostAddress ad
6970
{
7071
var order = new RepositoryOrder
7172
{
72-
Field = RepositoryOrderField.Name,
73-
Direction = OrderDirection.Asc
74-
};
75-
76-
var affiliation = new RepositoryAffiliation?[]
77-
{
78-
RepositoryAffiliation.Owner, RepositoryAffiliation.Collaborator
73+
Field = RepositoryOrderField.PushedAt,
74+
Direction = OrderDirection.Desc
7975
};
8076

8177
var repositorySelection = new Fragment<Repository, RepositoryListItemModel>(
@@ -94,14 +90,17 @@ public async Task<ViewerRepositoriesModel> ReadViewerRepositories(HostAddress ad
9490
.Select(viewer => new ViewerRepositoriesModel
9591
{
9692
Owner = viewer.Login,
97-
Repositories = viewer.Repositories(null, null, null, null, null, null, null, order, affiliation, null)
93+
Repositories = viewer.Repositories(null, null, null, null, null, null, null, order, null, null)
9894
.AllPages()
9995
.Select(repositorySelection).ToList(),
96+
ContributedToRepositories = viewer.RepositoriesContributedTo(100, null, null, null, null, null, null, order, null)
97+
.Nodes
98+
.Select(repositorySelection).ToList(),
10099
Organizations = viewer.Organizations(null, null, null, null).AllPages().Select(org => new
101100
{
102101
org.Login,
103-
Repositories = org.Repositories(null, null, null, null, null, null, null, order, null, null)
104-
.AllPages()
102+
Repositories = org.Repositories(100, null, null, null, null, null, null, order, null, null)
103+
.Nodes
105104
.Select(repositorySelection).ToList()
106105
}).ToDictionary(x => x.Login, x => (IReadOnlyList<RepositoryListItemModel>)x.Repositories),
107106
}).Compile();

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

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.ComponentModel;
44
using System.ComponentModel.Composition;
5+
using System.Globalization;
56
using System.Linq;
67
using System.Reactive.Linq;
78
using System.Threading.Tasks;
@@ -108,7 +109,8 @@ public async Task Activate()
108109

109110
try
110111
{
111-
var results = await service.ReadViewerRepositories(connection.HostAddress).ConfigureAwait(true);
112+
var results = await log.TimeAsync(nameof(service.ReadViewerRepositories),
113+
() => service.ReadViewerRepositories(connection.HostAddress));
112114

113115
var yourRepositories = results.Repositories
114116
.Where(r => r.Owner == results.Owner)
@@ -117,10 +119,17 @@ public async Task Activate()
117119
.Where(r => r.Owner != results.Owner)
118120
.OrderBy(r => r.Owner)
119121
.Select(x => new RepositoryItemViewModel(x, "Collaborator repositories"));
122+
var repositoriesContributedTo = results.ContributedToRepositories
123+
.Select(x => new RepositoryItemViewModel(x, "Contributed to repositories"));
120124
var orgRepositories = results.Organizations
121125
.OrderBy(x => x.Key)
122-
.SelectMany(x => x.Value.Select(y => new RepositoryItemViewModel(y, x.Key)));
123-
Items = yourRepositories.Concat(collaboratorRepositories).Concat(orgRepositories).ToList();
126+
.SelectMany(x => x.Value.Select(y => new RepositoryItemViewModel(y, GroupName(x, 100))));
127+
Items = yourRepositories
128+
.Concat(collaboratorRepositories)
129+
.Concat(repositoriesContributedTo)
130+
.Concat(orgRepositories)
131+
.ToList();
132+
log.Information("Read {Total} viewer repositories", Items.Count);
124133
ItemsView = CollectionViewSource.GetDefaultView(Items);
125134
ItemsView.GroupDescriptions.Add(new PropertyGroupDescription(nameof(RepositoryItemViewModel.Group)));
126135
ItemsView.Filter = FilterItem;
@@ -142,6 +151,17 @@ public async Task Activate()
142151
}
143152
}
144153

154+
static string GroupName(KeyValuePair<string, IReadOnlyList<RepositoryListItemModel>> group, int max)
155+
{
156+
var name = group.Key;
157+
if (group.Value.Count == max)
158+
{
159+
name += $" ({string.Format(CultureInfo.InvariantCulture, Resources.MostRecentlyPushed, max)})";
160+
}
161+
162+
return name;
163+
}
164+
145165
bool FilterItem(object obj)
146166
{
147167
if (obj is IRepositoryItemViewModel item && !string.IsNullOrWhiteSpace(Filter))

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.Exports/Models/ViewerRepositoriesModel.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public class ViewerRepositoriesModel
77
{
88
public string Owner { get; set; }
99
public IReadOnlyList<RepositoryListItemModel> Repositories { get; set; }
10+
public IReadOnlyList<RepositoryListItemModel> ContributedToRepositories { get; set; }
1011
public IDictionary<string, IReadOnlyList<RepositoryListItemModel>> Organizations { get; set; }
1112
}
1213
}

0 commit comments

Comments
 (0)