diff --git a/lib/Octokit.GraphQL.0.1.1-beta.nupkg b/lib/Octokit.GraphQL.0.1.1-beta.nupkg
deleted file mode 100644
index ac26783672..0000000000
Binary files a/lib/Octokit.GraphQL.0.1.1-beta.nupkg and /dev/null differ
diff --git a/src/GitHub.Api/GitHub.Api.csproj b/src/GitHub.Api/GitHub.Api.csproj
index 6ff35a4ff0..dabd0989c6 100644
--- a/src/GitHub.Api/GitHub.Api.csproj
+++ b/src/GitHub.Api/GitHub.Api.csproj
@@ -30,6 +30,6 @@
-
+
diff --git a/src/GitHub.App/GitHub.App.csproj b/src/GitHub.App/GitHub.App.csproj
index a6f5be28c8..5a147b26c3 100644
--- a/src/GitHub.App/GitHub.App.csproj
+++ b/src/GitHub.App/GitHub.App.csproj
@@ -49,10 +49,10 @@
-
+
-
\ No newline at end of file
+
diff --git a/src/GitHub.App/SampleData/CommentThreadViewModelDesigner.cs b/src/GitHub.App/SampleData/CommentThreadViewModelDesigner.cs
index cd282d81c7..aaf13bafef 100644
--- a/src/GitHub.App/SampleData/CommentThreadViewModelDesigner.cs
+++ b/src/GitHub.App/SampleData/CommentThreadViewModelDesigner.cs
@@ -1,5 +1,7 @@
-using System.Diagnostics.CodeAnalysis;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
+using GitHub.Models;
using GitHub.ViewModels;
using ReactiveUI;
@@ -8,6 +10,16 @@ namespace GitHub.SampleData
[SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")]
public class CommentThreadViewModelDesigner : ViewModelBase, ICommentThreadViewModel
{
+ public CommentThreadViewModelDesigner()
+ {
+ Comments = new ReactiveList(){new CommentViewModelDesigner()
+ {
+ Author = new ActorViewModel{ Login = "shana"},
+ Body = "You can use a `CompositeDisposable` type here, it's designed to handle disposables in an optimal way (you can just call `Dispose()` on it and it will handle disposing everything it holds)."
+ }};
+
+ }
+
public IReadOnlyReactiveList Comments { get; }
= new ReactiveList();
diff --git a/src/GitHub.App/SampleData/InlineAnnotationViewModelDesigner.cs b/src/GitHub.App/SampleData/InlineAnnotationViewModelDesigner.cs
new file mode 100644
index 0000000000..4aea77efb7
--- /dev/null
+++ b/src/GitHub.App/SampleData/InlineAnnotationViewModelDesigner.cs
@@ -0,0 +1,40 @@
+using System.Collections.Generic;
+using GitHub.Models;
+using GitHub.ViewModels;
+
+namespace GitHub.SampleData
+{
+ public class InlineAnnotationViewModelDesigner : IInlineAnnotationViewModel
+ {
+ public InlineAnnotationViewModelDesigner()
+ {
+ var checkRunAnnotationModel = new CheckRunAnnotationModel
+ {
+ AnnotationLevel = CheckAnnotationLevel.Failure,
+ Path = "SomeFile.cs",
+ EndLine = 12,
+ StartLine = 12,
+ Message = "Some Error Message",
+ Title = "CS12345"
+ };
+
+ var checkRunModel =
+ new CheckRunModel
+ {
+ Annotations = new List {checkRunAnnotationModel},
+ Name = "Fake Check Run"
+ };
+
+ var checkSuiteModel = new CheckSuiteModel()
+ {
+ ApplicationName = "Fake Check Suite",
+ HeadSha = "ed6198c37b13638e902716252b0a17d54bd59e4a",
+ CheckRuns = new List { checkRunModel}
+ };
+
+ Model= new InlineAnnotationModel(checkSuiteModel, checkRunModel, checkRunAnnotationModel);
+ }
+
+ public InlineAnnotationModel Model { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/GitHub.App/SampleData/PullRequestAnnotationItemViewModelDesigner.cs b/src/GitHub.App/SampleData/PullRequestAnnotationItemViewModelDesigner.cs
new file mode 100644
index 0000000000..03f4e7d056
--- /dev/null
+++ b/src/GitHub.App/SampleData/PullRequestAnnotationItemViewModelDesigner.cs
@@ -0,0 +1,18 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Reactive;
+using GitHub.Models;
+using GitHub.ViewModels.GitHubPane;
+using ReactiveUI;
+
+namespace GitHub.SampleData
+{
+ [ExcludeFromCodeCoverage]
+ public sealed class PullRequestAnnotationItemViewModelDesigner : IPullRequestAnnotationItemViewModel
+ {
+ public CheckRunAnnotationModel Annotation { get; set; }
+ public bool IsExpanded { get; set; }
+ public string LineDescription => $"{Annotation.StartLine}:{Annotation.EndLine}";
+ public bool IsFileInPullRequest { get; set; }
+ public ReactiveCommand OpenAnnotation { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/GitHub.App/SampleData/PullRequestAnnotationsViewModelDesigner.cs b/src/GitHub.App/SampleData/PullRequestAnnotationsViewModelDesigner.cs
new file mode 100644
index 0000000000..8e728ee8f3
--- /dev/null
+++ b/src/GitHub.App/SampleData/PullRequestAnnotationsViewModelDesigner.cs
@@ -0,0 +1,91 @@
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Reactive;
+using System.Threading.Tasks;
+using GitHub.Models;
+using GitHub.ViewModels.GitHubPane;
+using ReactiveUI;
+
+namespace GitHub.SampleData
+{
+ [ExcludeFromCodeCoverage]
+ public sealed class PullRequestAnnotationsViewModelDesigner : PanePageViewModelBase, IPullRequestAnnotationsViewModel
+ {
+ public LocalRepositoryModel LocalRepository { get; set; }
+ public string RemoteRepositoryOwner { get; set; }
+ public int PullRequestNumber { get; set; } = 123;
+ public string CheckRunId { get; set; }
+ public ReactiveCommand NavigateToPullRequest { get; }
+ public string PullRequestTitle { get; } = "Fixing stuff in this PR";
+ public string CheckSuiteName { get; } = "Awesome Check Suite";
+ public string CheckRunSummary { get; } = "Awesome Check Run Summary";
+ public string CheckRunText { get; } = "Awesome Check Run Text";
+
+ public IReadOnlyDictionary AnnotationsDictionary { get; }
+ = new Dictionary
+ {
+ {
+ "asdf/asdf.cs",
+ new IPullRequestAnnotationItemViewModel[]
+ {
+ new PullRequestAnnotationItemViewModelDesigner
+ {
+ Annotation = new CheckRunAnnotationModel
+ {
+ AnnotationLevel = CheckAnnotationLevel.Warning,
+ StartLine = 3,
+ EndLine = 4,
+ Path = "asdf/asdf.cs",
+ Message = "; is expected",
+ Title = "CS 12345"
+ },
+ IsExpanded = true,
+ IsFileInPullRequest = true
+ },
+ new PullRequestAnnotationItemViewModelDesigner
+ {
+ Annotation = new CheckRunAnnotationModel
+ {
+ AnnotationLevel = CheckAnnotationLevel.Failure,
+ StartLine = 3,
+ EndLine = 4,
+ Path = "asdf/asdf.cs",
+ Message = "; is expected",
+ Title = "CS 12345"
+ },
+ IsExpanded = true,
+ IsFileInPullRequest = true
+ },
+ }
+ },
+ {
+ "blah.cs",
+ new IPullRequestAnnotationItemViewModel[]
+ {
+ new PullRequestAnnotationItemViewModelDesigner
+ {
+ Annotation = new CheckRunAnnotationModel
+ {
+ AnnotationLevel = CheckAnnotationLevel.Notice,
+ StartLine = 3,
+ EndLine = 4,
+ Path = "blah.cs",
+ Message = "; is expected",
+ Title = "CS 12345"
+ },
+ IsExpanded = true,
+ }
+ }
+ },
+ };
+
+ public string CheckRunName { get; } = "Psuedo Check Run";
+
+ public Task InitializeAsync(LocalRepositoryModel localRepository, IConnection connection, string owner,
+ string repo,
+ int pullRequestNumber, string checkRunId)
+ {
+ return Task.CompletedTask;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/GitHub.App/SampleData/PullRequestCheckViewModelDesigner.cs b/src/GitHub.App/SampleData/PullRequestCheckViewModelDesigner.cs
index 3011e581c1..ec75bf27be 100644
--- a/src/GitHub.App/SampleData/PullRequestCheckViewModelDesigner.cs
+++ b/src/GitHub.App/SampleData/PullRequestCheckViewModelDesigner.cs
@@ -1,6 +1,6 @@
using System;
using System.Reactive;
-using System.Windows.Media.Imaging;
+using GitHub.Models;
using GitHub.ViewModels;
using GitHub.ViewModels.GitHubPane;
using ReactiveUI;
@@ -18,5 +18,11 @@ public sealed class PullRequestCheckViewModelDesigner : ViewModelBase, IPullRequ
public Uri DetailsUrl { get; set; } = new Uri("http://github.com");
public ReactiveCommand OpenDetailsUrl { get; set; } = null;
+
+ public PullRequestCheckType CheckType { get; set; } = PullRequestCheckType.ChecksApi;
+
+ public string CheckRunId { get; set; }
+
+ public bool HasAnnotations { get; } = true;
}
}
\ No newline at end of file
diff --git a/src/GitHub.App/SampleData/PullRequestDetailViewModelDesigner.cs b/src/GitHub.App/SampleData/PullRequestDetailViewModelDesigner.cs
index 0bb87d5d37..9f1ad0262a 100644
--- a/src/GitHub.App/SampleData/PullRequestDetailViewModelDesigner.cs
+++ b/src/GitHub.App/SampleData/PullRequestDetailViewModelDesigner.cs
@@ -9,6 +9,7 @@
using System.Reactive;
using System.Threading.Tasks;
using GitHub.SampleData;
+using ReactiveUI.Legacy;
namespace GitHub.SampleData
{
@@ -122,8 +123,10 @@ public PullRequestDetailViewModelDesigner()
public ReactiveCommand Checkout { get; }
public ReactiveCommand Pull { get; }
public ReactiveCommand Push { get; }
+ public ReactiveCommand SyncSubmodules { get; }
public ReactiveCommand OpenOnGitHub { get; }
public ReactiveCommand ShowReview { get; }
+ public ReactiveCommand ShowAnnotations { get; }
public IReadOnlyList Checks { get; }
diff --git a/src/GitHub.App/SampleData/PullRequestFilesViewModelDesigner.cs b/src/GitHub.App/SampleData/PullRequestFilesViewModelDesigner.cs
index b60b43bc03..5dda31c86d 100644
--- a/src/GitHub.App/SampleData/PullRequestFilesViewModelDesigner.cs
+++ b/src/GitHub.App/SampleData/PullRequestFilesViewModelDesigner.cs
@@ -36,6 +36,9 @@ public PullRequestFilesViewModelDesigner()
public ReactiveCommand DiffFileWithWorkingDirectory { get; }
public ReactiveCommand OpenFileInWorkingDirectory { get; }
public ReactiveCommand OpenFirstComment { get; }
+ public ReactiveCommand OpenFirstAnnotationNotice { get; }
+ public ReactiveCommand OpenFirstAnnotationWarning { get; }
+ public ReactiveCommand OpenFirstAnnotationFailure { get; }
public Task InitializeAsync(
IPullRequestSession session,
diff --git a/src/GitHub.App/Services/FromGraphQlExtensions.cs b/src/GitHub.App/Services/FromGraphQlExtensions.cs
index d56a63ae93..d9b79bcece 100644
--- a/src/GitHub.App/Services/FromGraphQlExtensions.cs
+++ b/src/GitHub.App/Services/FromGraphQlExtensions.cs
@@ -1,6 +1,7 @@
using System;
using GitHub.Models;
using Octokit.GraphQL.Model;
+using CheckAnnotationLevel = GitHub.Models.CheckAnnotationLevel;
using CheckConclusionState = GitHub.Models.CheckConclusionState;
using CheckStatusState = GitHub.Models.CheckStatusState;
using PullRequestReviewState = GitHub.Models.PullRequestReviewState;
@@ -84,7 +85,7 @@ public static CheckStatusState FromGraphQl(this Octokit.GraphQL.Model.CheckStatu
}
}
- public static GitHub.Models.PullRequestReviewState FromGraphQl(this Octokit.GraphQL.Model.PullRequestReviewState value)
+ public static PullRequestReviewState FromGraphQl(this Octokit.GraphQL.Model.PullRequestReviewState value)
{
switch (value) {
case Octokit.GraphQL.Model.PullRequestReviewState.Pending:
@@ -101,5 +102,20 @@ public static GitHub.Models.PullRequestReviewState FromGraphQl(this Octokit.Grap
throw new ArgumentOutOfRangeException(nameof(value), value, null);
}
}
+
+ public static CheckAnnotationLevel FromGraphQl(this Octokit.GraphQL.Model.CheckAnnotationLevel value)
+ {
+ switch (value)
+ {
+ case Octokit.GraphQL.Model.CheckAnnotationLevel.Failure:
+ return CheckAnnotationLevel.Failure;
+ case Octokit.GraphQL.Model.CheckAnnotationLevel.Notice:
+ return CheckAnnotationLevel.Notice;
+ case Octokit.GraphQL.Model.CheckAnnotationLevel.Warning:
+ return CheckAnnotationLevel.Warning;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(value), value, null);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/GitHub.App/Services/PullRequestEditorService.cs b/src/GitHub.App/Services/PullRequestEditorService.cs
index aaf44c50ad..db4889e936 100644
--- a/src/GitHub.App/Services/PullRequestEditorService.cs
+++ b/src/GitHub.App/Services/PullRequestEditorService.cs
@@ -285,7 +285,7 @@ await pullRequestService.ExtractToTempFile(
}
///
- public async Task OpenDiff(
+ public Task OpenDiff(
IPullRequestSession session,
string relativePath,
IInlineCommentThreadModel thread)
@@ -294,11 +294,17 @@ public async Task OpenDiff(
Guard.ArgumentNotEmptyString(relativePath, nameof(relativePath));
Guard.ArgumentNotNull(thread, nameof(thread));
- var diffViewer = await OpenDiff(session, relativePath, thread.CommitSha, scrollToFirstDraftOrDiff: false);
+ return OpenDiff(session, relativePath, thread.CommitSha, thread.LineNumber - 1);
+ }
+
+ ///
+ public async Task OpenDiff(IPullRequestSession session, string relativePath, string headSha, int fromLine)
+ {
+ var diffViewer = await OpenDiff(session, relativePath, headSha, scrollToFirstDraftOrDiff: false);
- var param = (object)new InlineCommentNavigationParams
+ var param = (object) new InlineCommentNavigationParams
{
- FromLine = thread.LineNumber - 1,
+ FromLine = fromLine,
};
// HACK: We need to wait here for the inline comment tags to initialize so we can find the next inline comment.
diff --git a/src/GitHub.App/Services/PullRequestService.cs b/src/GitHub.App/Services/PullRequestService.cs
index 9e141857fa..55a85b7d98 100644
--- a/src/GitHub.App/Services/PullRequestService.cs
+++ b/src/GitHub.App/Services/PullRequestService.cs
@@ -115,7 +115,7 @@ public async Task> ReadPullRequests(
{
Conclusion = run.Conclusion.FromGraphQl(),
Status = run.Status.FromGraphQl()
- }).ToList()
+ }).ToList(),
}).ToList(),
Statuses = commit.Commit.Status
.Select(context =>
diff --git a/src/GitHub.App/ViewModels/GitHubPane/GitHubPaneViewModel.cs b/src/GitHub.App/ViewModels/GitHubPane/GitHubPaneViewModel.cs
index 05f70c788c..5e13fe5a4a 100644
--- a/src/GitHub.App/ViewModels/GitHubPane/GitHubPaneViewModel.cs
+++ b/src/GitHub.App/ViewModels/GitHubPane/GitHubPaneViewModel.cs
@@ -35,6 +35,7 @@ public sealed class GitHubPaneViewModel : ViewModelBase, IGitHubPaneViewModel, I
static readonly Regex pullUri = CreateRoute("/:owner/:repo/pull/:number");
static readonly Regex pullNewReviewUri = CreateRoute("/:owner/:repo/pull/:number/review/new");
static readonly Regex pullUserReviewsUri = CreateRoute("/:owner/:repo/pull/:number/reviews/:login");
+ static readonly Regex pullCheckRunsUri = CreateRoute("/:owner/:repo/pull/:number/checkruns/:id");
readonly IViewViewModelFactory viewModelFactory;
readonly ISimpleApiClientFactory apiClientFactory;
@@ -266,6 +267,15 @@ public async Task NavigateTo(Uri uri)
var login = match.Groups["login"].Value;
await ShowPullRequestReviews(owner, repo, number, login);
}
+ else if ((match = pullCheckRunsUri.Match(uri.AbsolutePath))?.Success == true)
+ {
+ var owner = match.Groups["owner"].Value;
+ var repo = match.Groups["repo"].Value;
+ var number = int.Parse(match.Groups["number"].Value);
+ var id = match.Groups["id"].Value;
+
+ await ShowPullRequestCheckRun(owner, repo, number, id);
+ }
else
{
throw new NotSupportedException("Unrecognised GitHub pane URL: " + uri.AbsolutePath);
@@ -319,6 +329,20 @@ public Task ShowPullRequestReviews(string owner, string repo, int number, string
x.User.Login == login);
}
+ ///
+ public Task ShowPullRequestCheckRun(string owner, string repo, int number, string checkRunId)
+ {
+ Guard.ArgumentNotNull(owner, nameof(owner));
+ Guard.ArgumentNotNull(repo, nameof(repo));
+
+ return NavigateTo(
+ x => x.InitializeAsync(LocalRepository, Connection, owner, repo, number, checkRunId),
+ x => x.RemoteRepositoryOwner == owner &&
+ x.LocalRepository.Name == repo &&
+ x.PullRequestNumber == number &&
+ x.CheckRunId == checkRunId);
+ }
+
///
public Task ShowPullRequestReviewAuthoring(string owner, string repo, int number)
{
@@ -489,8 +513,8 @@ static async Task IsValidRepository(ISimpleApiClient client)
static Regex CreateRoute(string route)
{
- // Build RegEx from route (:foo to named group (?[\w_.-]+)).
- var routeFormat = "^" + new Regex("(:([a-z]+))\\b").Replace(route, @"(?<$2>[\w_.-]+)") + "$";
+ // Build RegEx from route (:foo to named group (?[\w_.\-=]+)).
+ var routeFormat = "^" + new Regex("(:([a-z]+))\\b").Replace(route, @"(?<$2>[\w_.\-=]+)") + "$";
return new Regex(routeFormat, RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase);
}
}
diff --git a/src/GitHub.App/ViewModels/GitHubPane/PullRequestAnnotationItemViewModel.cs b/src/GitHub.App/ViewModels/GitHubPane/PullRequestAnnotationItemViewModel.cs
new file mode 100644
index 0000000000..d07c806f89
--- /dev/null
+++ b/src/GitHub.App/ViewModels/GitHubPane/PullRequestAnnotationItemViewModel.cs
@@ -0,0 +1,56 @@
+using System.Reactive;
+using System.Reactive.Linq;
+using GitHub.Models;
+using GitHub.Services;
+using ReactiveUI;
+
+namespace GitHub.ViewModels.GitHubPane
+{
+ ///
+ public class PullRequestAnnotationItemViewModel : ViewModelBase, IPullRequestAnnotationItemViewModel
+ {
+ bool isExpanded;
+
+ ///
+ /// Initializes the .
+ ///
+ /// The check run annotation model.
+ /// A flag that denotes if the annotation is part of the pull request's changes.
+ /// The check suite model.
+ /// The pull request session.
+ /// The pull request editor service.
+ public PullRequestAnnotationItemViewModel(
+ CheckRunAnnotationModel annotation,
+ bool isFileInPullRequest,
+ CheckSuiteModel checkSuite,
+ IPullRequestSession session,
+ IPullRequestEditorService editorService)
+ {
+ Annotation = annotation;
+ IsFileInPullRequest = isFileInPullRequest;
+
+ OpenAnnotation = ReactiveCommand.CreateFromTask(
+ async _ => await editorService.OpenDiff(session, annotation.Path, checkSuite.HeadSha, annotation.EndLine - 1),
+ Observable.Return(IsFileInPullRequest));
+ }
+
+ ///
+ public bool IsFileInPullRequest { get; }
+
+ ///
+ public CheckRunAnnotationModel Annotation { get; }
+
+ ///
+ public string LineDescription => $"{Annotation.StartLine}:{Annotation.EndLine}";
+
+ ///
+ public ReactiveCommand OpenAnnotation { get; }
+
+ ///
+ public bool IsExpanded
+ {
+ get { return isExpanded; }
+ set { this.RaiseAndSetIfChanged(ref isExpanded, value); }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/GitHub.App/ViewModels/GitHubPane/PullRequestAnnotationsViewModel.cs b/src/GitHub.App/ViewModels/GitHubPane/PullRequestAnnotationsViewModel.cs
new file mode 100644
index 0000000000..8b74b94ca5
--- /dev/null
+++ b/src/GitHub.App/ViewModels/GitHubPane/PullRequestAnnotationsViewModel.cs
@@ -0,0 +1,168 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Linq;
+using System.Reactive;
+using System.Threading.Tasks;
+using GitHub.Models;
+using GitHub.Services;
+using ReactiveUI;
+
+namespace GitHub.ViewModels.GitHubPane
+{
+ ///
+ [Export(typeof(IPullRequestAnnotationsViewModel))]
+ [PartCreationPolicy(CreationPolicy.NonShared)]
+ public class PullRequestAnnotationsViewModel : PanePageViewModelBase, IPullRequestAnnotationsViewModel
+ {
+ readonly IPullRequestSessionManager sessionManager;
+ readonly IPullRequestEditorService pullRequestEditorService;
+
+ IPullRequestSession session;
+ string title;
+ string checkSuiteName;
+ string checkRunName;
+ IReadOnlyDictionary annotationsDictionary;
+ string checkRunSummary;
+ string checkRunText;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The pull request session manager.
+ /// The pull request editor service.
+ [ImportingConstructor]
+ public PullRequestAnnotationsViewModel(IPullRequestSessionManager sessionManager, IPullRequestEditorService pullRequestEditorService)
+ {
+ this.sessionManager = sessionManager;
+ this.pullRequestEditorService = pullRequestEditorService;
+ NavigateToPullRequest = ReactiveCommand.Create(() => {
+ NavigateTo(FormattableString.Invariant(
+ $"{LocalRepository.Owner}/{LocalRepository.Name}/pull/{PullRequestNumber}"));
+ });
+ }
+
+ ///
+ public async Task InitializeAsync(LocalRepositoryModel localRepository, IConnection connection, string owner,
+ string repo, int pullRequestNumber, string checkRunId)
+ {
+ if (repo != localRepository.Name)
+ {
+ throw new NotSupportedException();
+ }
+
+ IsLoading = true;
+
+ try
+ {
+ LocalRepository = localRepository;
+ RemoteRepositoryOwner = owner;
+ PullRequestNumber = pullRequestNumber;
+ CheckRunId = checkRunId;
+ session = await sessionManager.GetSession(owner, repo, pullRequestNumber);
+ Load(session.PullRequest);
+ }
+ finally
+ {
+ IsLoading = false;
+ }
+ }
+
+ ///
+ public LocalRepositoryModel LocalRepository { get; private set; }
+
+ ///
+ public string RemoteRepositoryOwner { get; private set; }
+
+ ///
+ public int PullRequestNumber { get; private set; }
+
+ ///
+ public string CheckRunId { get; private set; }
+
+ ///
+ public ReactiveCommand NavigateToPullRequest { get; private set; }
+
+ ///
+ public string PullRequestTitle
+ {
+ get { return title; }
+ private set { this.RaiseAndSetIfChanged(ref title, value); }
+ }
+
+ ///
+ public string CheckSuiteName
+ {
+ get { return checkSuiteName; }
+ private set { this.RaiseAndSetIfChanged(ref checkSuiteName, value); }
+ }
+
+ ///
+ public string CheckRunName
+ {
+ get { return checkRunName; }
+ private set { this.RaiseAndSetIfChanged(ref checkRunName, value); }
+ }
+
+ ///
+ public string CheckRunSummary
+ {
+ get { return checkRunSummary; }
+ private set { this.RaiseAndSetIfChanged(ref checkRunSummary, value); }
+ }
+
+ ///
+ public string CheckRunText
+ {
+ get { return checkRunText; }
+ private set { this.RaiseAndSetIfChanged(ref checkRunText, value); }
+ }
+
+ ///
+ public IReadOnlyDictionary AnnotationsDictionary
+ {
+ get { return annotationsDictionary; }
+ private set { this.RaiseAndSetIfChanged(ref annotationsDictionary, value); }
+ }
+
+ void Load(PullRequestDetailModel pullRequest)
+ {
+ IsBusy = true;
+
+ try
+ {
+ PullRequestTitle = pullRequest.Title;
+
+ var checkSuiteRun = pullRequest
+ .CheckSuites.SelectMany(checkSuite => checkSuite.CheckRuns
+ .Select(checkRun => new{checkSuite, checkRun}))
+ .First(arg => arg.checkRun.Id == CheckRunId);
+
+ CheckSuiteName = checkSuiteRun.checkSuite.ApplicationName;
+ CheckRunName = checkSuiteRun.checkRun.Name;
+ CheckRunSummary = checkSuiteRun.checkRun.Summary;
+ CheckRunText = checkSuiteRun.checkRun.Text;
+
+ var changedFiles = new HashSet(session.PullRequest.ChangedFiles.Select(model => model.FileName));
+
+ var annotationsLookup = checkSuiteRun.checkRun.Annotations
+ .ToLookup(annotation => annotation.Path);
+
+ AnnotationsDictionary = annotationsLookup
+ .Select(models => models.Key)
+ .OrderBy(s => s)
+ .ToDictionary(
+ path => path,
+ path => annotationsLookup[path]
+ .Select(annotation => new PullRequestAnnotationItemViewModel(annotation, changedFiles.Contains(path), checkSuiteRun.checkSuite, session, pullRequestEditorService))
+ .Cast()
+ .ToArray()
+ );
+ }
+ finally
+ {
+ IsBusy = false;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/GitHub.App/ViewModels/GitHubPane/PullRequestCheckViewModel.cs b/src/GitHub.App/ViewModels/GitHubPane/PullRequestCheckViewModel.cs
index f95bd95fd1..0f0ab9224c 100644
--- a/src/GitHub.App/ViewModels/GitHubPane/PullRequestCheckViewModel.cs
+++ b/src/GitHub.App/ViewModels/GitHubPane/PullRequestCheckViewModel.cs
@@ -4,29 +4,35 @@
using System.Linq;
using System.Linq.Expressions;
using System.Reactive;
-using System.Reactive.Linq;
-using System.Windows.Media.Imaging;
using GitHub.Extensions;
using GitHub.Factories;
using GitHub.Models;
+using GitHub.Primitives;
using GitHub.Services;
using ReactiveUI;
namespace GitHub.ViewModels.GitHubPane
{
+ ///
[Export(typeof(IPullRequestCheckViewModel))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class PullRequestCheckViewModel: ViewModelBase, IPullRequestCheckViewModel
{
- private readonly IUsageTracker usageTracker;
const string DefaultAvatar = "pack://application:,,,/GitHub.App;component/Images/default_user_avatar.png";
+ private readonly IUsageTracker usageTracker;
+
+ ///
+ /// Factory method to create a .
+ ///
+ /// A viewviewmodel factory.
+ /// The pull request.
public static IEnumerable Build(IViewViewModelFactory viewViewModelFactory, PullRequestDetailModel pullRequest)
{
- var statuses = pullRequest.Statuses?.Select(model =>
+ var statuses = pullRequest.Statuses?.Select(statusModel =>
{
PullRequestCheckStatus checkStatus;
- switch (model.State)
+ switch (statusModel.State)
{
case StatusState.Expected:
case StatusState.Error:
@@ -45,19 +51,22 @@ public static IEnumerable Build(IViewViewModelFactor
var pullRequestCheckViewModel = (PullRequestCheckViewModel) viewViewModelFactory.CreateViewModel();
pullRequestCheckViewModel.CheckType = PullRequestCheckType.StatusApi;
- pullRequestCheckViewModel.Title = model.Context;
- pullRequestCheckViewModel.Description = model.Description;
+ pullRequestCheckViewModel.Title = statusModel.Context;
+ pullRequestCheckViewModel.Description = statusModel.Description;
pullRequestCheckViewModel.Status = checkStatus;
- pullRequestCheckViewModel.DetailsUrl = !string.IsNullOrEmpty(model.TargetUrl) ? new Uri(model.TargetUrl) : null;
+ pullRequestCheckViewModel.DetailsUrl = !string.IsNullOrEmpty(statusModel.TargetUrl) ? new Uri(statusModel.TargetUrl) : null;
return pullRequestCheckViewModel;
}) ?? Array.Empty();
- var checks = pullRequest.CheckSuites?.SelectMany(model => model.CheckRuns)
- .Select(model =>
+ var checks =
+ pullRequest.CheckSuites?
+ .SelectMany(checkSuite => checkSuite.CheckRuns
+ .Select(checkRun => new { checkSuiteModel = checkSuite, checkRun}))
+ .Select(arg =>
{
PullRequestCheckStatus checkStatus;
- switch (model.Status)
+ switch (arg.checkRun.Status)
{
case CheckStatusState.Requested:
case CheckStatusState.Queued:
@@ -66,7 +75,7 @@ public static IEnumerable Build(IViewViewModelFactor
break;
case CheckStatusState.Completed:
- switch (model.Conclusion)
+ switch (arg.checkRun.Conclusion)
{
case CheckConclusionState.Success:
checkStatus = PullRequestCheckStatus.Success;
@@ -91,17 +100,22 @@ public static IEnumerable Build(IViewViewModelFactor
var pullRequestCheckViewModel = (PullRequestCheckViewModel)viewViewModelFactory.CreateViewModel();
pullRequestCheckViewModel.CheckType = PullRequestCheckType.ChecksApi;
- pullRequestCheckViewModel.Title = model.Name;
- pullRequestCheckViewModel.Description = model.Summary;
+ pullRequestCheckViewModel.CheckRunId = arg.checkRun.Id;
+ pullRequestCheckViewModel.HasAnnotations = arg.checkRun.Annotations?.Any() ?? false;
+ pullRequestCheckViewModel.Title = arg.checkRun.Name;
+ pullRequestCheckViewModel.Description = arg.checkRun.Summary;
pullRequestCheckViewModel.Status = checkStatus;
- pullRequestCheckViewModel.DetailsUrl = new Uri(model.DetailsUrl);
-
+ pullRequestCheckViewModel.DetailsUrl = new Uri(arg.checkRun.DetailsUrl);
return pullRequestCheckViewModel;
}) ?? Array.Empty();
return statuses.Concat(checks).OrderBy(model => model.Title);
}
+ ///
+ /// Initializes a new instance of .
+ ///
+ /// The usage tracker.
[ImportingConstructor]
public PullRequestCheckViewModel(IUsageTracker usageTracker)
{
@@ -124,16 +138,28 @@ private void DoOpenDetailsUrl()
usageTracker.IncrementCounter(expression).Forget();
}
+ ///
public string Title { get; private set; }
+ ///
public string Description { get; private set; }
+ ///
public PullRequestCheckType CheckType { get; private set; }
+ ///
+ public string CheckRunId { get; private set; }
+
+ ///
+ public bool HasAnnotations { get; private set; }
+
+ ///
public PullRequestCheckStatus Status{ get; private set; }
+ ///
public Uri DetailsUrl { get; private set; }
+ ///
public ReactiveCommand OpenDetailsUrl { get; }
}
}
diff --git a/src/GitHub.App/ViewModels/GitHubPane/PullRequestDetailViewModel.cs b/src/GitHub.App/ViewModels/GitHubPane/PullRequestDetailViewModel.cs
index f968b391c3..ef5d29d7ff 100644
--- a/src/GitHub.App/ViewModels/GitHubPane/PullRequestDetailViewModel.cs
+++ b/src/GitHub.App/ViewModels/GitHubPane/PullRequestDetailViewModel.cs
@@ -18,14 +18,14 @@
using GitHub.Services;
using LibGit2Sharp;
using ReactiveUI;
+using ReactiveUI.Legacy;
using Serilog;
using static System.FormattableString;
+using ReactiveCommand = ReactiveUI.ReactiveCommand;
namespace GitHub.ViewModels.GitHubPane
{
- ///
- /// A view model which displays the details of a pull request.
- ///
+ ///
[Export(typeof(IPullRequestDetailViewModel))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public sealed class PullRequestDetailViewModel : PanePageViewModelBase, IPullRequestDetailViewModel
@@ -131,7 +131,10 @@ public PullRequestDetailViewModel(
SubscribeOperationError(SyncSubmodules);
OpenOnGitHub = ReactiveCommand.Create(DoOpenDetailsUrl);
+
ShowReview = ReactiveCommand.Create(DoShowReview);
+
+ ShowAnnotations = ReactiveCommand.Create(DoShowAnnotations);
}
private void DoOpenDetailsUrl()
@@ -139,9 +142,7 @@ private void DoOpenDetailsUrl()
usageTracker.IncrementCounter(measuresModel => measuresModel.NumberOfPRDetailsOpenInGitHub).Forget();
}
- ///
- /// Gets the underlying pull request model.
- ///
+ ///
public PullRequestDetailModel Model
{
get { return model; }
@@ -159,124 +160,89 @@ private set
}
}
- ///
- /// Gets the local repository.
- ///
+ ///
public LocalRepositoryModel LocalRepository { get; private set; }
- ///
- /// Gets the owner of the remote repository that contains the pull request.
- ///
- ///
- /// The remote repository may be different from the local repository if the local
- /// repository is a fork and the user is viewing pull requests from the parent repository.
- ///
+ ///
public string RemoteRepositoryOwner { get; private set; }
- ///
- /// Gets the Pull Request number.
- ///
+ ///
public int Number { get; private set; }
- ///
- /// Gets the Pull Request author.
- ///
+ ///
public IActorViewModel Author
{
get { return author; }
private set { this.RaiseAndSetIfChanged(ref author, value); }
}
- ///
- /// Gets the session for the pull request.
- ///
+ ///
public IPullRequestSession Session { get; private set; }
- ///
- /// Gets a string describing how to display the pull request's source branch.
- ///
+ ///
public string SourceBranchDisplayName
{
get { return sourceBranchDisplayName; }
private set { this.RaiseAndSetIfChanged(ref sourceBranchDisplayName, value); }
}
- ///
- /// Gets a string describing how to display the pull request's target branch.
- ///
+ ///
public string TargetBranchDisplayName
{
get { return targetBranchDisplayName; }
private set { this.RaiseAndSetIfChanged(ref targetBranchDisplayName, value); }
}
- ///
- /// Gets a value indicating whether the pull request branch is checked out.
- ///
+ ///
public bool IsCheckedOut
{
get { return isCheckedOut; }
private set { this.RaiseAndSetIfChanged(ref isCheckedOut, value); }
}
- ///
- /// Gets a value indicating whether the pull request comes from a fork.
- ///
+ ///
public bool IsFromFork
{
get { return isFromFork; }
private set { this.RaiseAndSetIfChanged(ref isFromFork, value); }
}
- ///
- /// Gets the pull request body.
- ///
+ ///
public string Body
{
get { return body; }
private set { this.RaiseAndSetIfChanged(ref body, value); }
}
- ///
- /// Gets the state associated with the command.
- ///
+ ///
public IPullRequestCheckoutState CheckoutState
{
get { return checkoutState; }
private set { this.RaiseAndSetIfChanged(ref checkoutState, value); }
}
- ///
- /// Gets the state associated with the and commands.
- ///
+ ///
public IPullRequestUpdateState UpdateState
{
get { return updateState; }
private set { this.RaiseAndSetIfChanged(ref updateState, value); }
}
- ///
- /// Gets the error message to be displayed in the action area as a result of an error in a
- /// git operation.
- ///
+ ///
public string OperationError
{
get { return operationError; }
private set { this.RaiseAndSetIfChanged(ref operationError, value); }
}
- ///
- /// Gets the latest pull request review for each user.
- ///
+ ///
public IReadOnlyList Reviews
{
get { return reviews; }
private set { this.RaiseAndSetIfChanged(ref reviews, value); }
}
- ///
- /// Gets the pull request's changed files.
- ///
+ ///
public IPullRequestFilesViewModel Files { get; }
///
@@ -288,50 +254,35 @@ public Uri WebUrl
private set { this.RaiseAndSetIfChanged(ref webUrl, value); }
}
- ///
- /// Gets a command that checks out the pull request locally.
- ///
+ ///
public ReactiveCommand Checkout { get; }
- ///
- /// Gets a command that pulls changes to the current branch.
- ///
+ ///
public ReactiveCommand Pull { get; }
- ///
- /// Gets a command that pushes changes from the current branch.
- ///
+ ///
public ReactiveCommand Push { get; }
- ///
- /// Sync submodules for PR branch.
- ///
+ ///
public ReactiveCommand SyncSubmodules { get; }
- ///
- /// Gets a command that opens the pull request on GitHub.
- ///
+ ///
public ReactiveCommand OpenOnGitHub { get; }
- ///
- /// Gets a command that navigates to a pull request review.
- ///
+ ///
public ReactiveCommand ShowReview { get; }
+ ///
+ public ReactiveCommand ShowAnnotations { get; }
+
+ ///
public IReadOnlyList Checks
{
get { return checks; }
private set { this.RaiseAndSetIfChanged(ref checks, value); }
}
- ///
- /// Initializes the view model.
- ///
- /// The local repository.
- /// The connection to the repository host.
- /// The pull request's repository owner.
- /// The pull request's repository name.
- /// The pull request number.
+ ///
public async Task InitializeAsync(
LocalRepositoryModel localRepository,
IConnection connection,
@@ -521,11 +472,7 @@ public override async Task Refresh()
}
}
- ///
- /// Gets the full path to a file in the working directory.
- ///
- /// The file.
- /// The full path to the file in the working directory.
+ ///
public string GetLocalFilePath(IPullRequestFileNode file)
{
return Path.Combine(LocalRepository.LocalPath, file.RelativePath);
@@ -651,10 +598,8 @@ async Task DoSyncSubmodules()
}
}
- void DoShowReview(IPullRequestReviewSummaryViewModel item)
+ void DoShowReview(IPullRequestReviewSummaryViewModel review)
{
- var review = item;
-
if (review.State == PullRequestReviewState.Pending)
{
NavigateTo(Invariant($"{RemoteRepositoryOwner}/{LocalRepository.Name}/pull/{Number}/review/new"));
@@ -665,6 +610,11 @@ void DoShowReview(IPullRequestReviewSummaryViewModel item)
}
}
+ void DoShowAnnotations(IPullRequestCheckViewModel checkView)
+ {
+ NavigateTo(Invariant($"{RemoteRepositoryOwner}/{LocalRepository.Name}/pull/{Number}/checkruns/{checkView.CheckRunId}"));
+ }
+
class CheckoutCommandState : IPullRequestCheckoutState
{
public CheckoutCommandState(string caption, string disabledMessage)
diff --git a/src/GitHub.App/ViewModels/GitHubPane/PullRequestFileNode.cs b/src/GitHub.App/ViewModels/GitHubPane/PullRequestFileNode.cs
index f9e1e4e164..374bd131ef 100644
--- a/src/GitHub.App/ViewModels/GitHubPane/PullRequestFileNode.cs
+++ b/src/GitHub.App/ViewModels/GitHubPane/PullRequestFileNode.cs
@@ -13,6 +13,9 @@ namespace GitHub.ViewModels.GitHubPane
public class PullRequestFileNode : ReactiveObject, IPullRequestFileNode
{
int commentCount;
+ int annotationNoticeCount;
+ int annotationWarningCount;
+ int _annotationFailureCount;
///
/// Initializes a new instance of the class.
@@ -99,5 +102,32 @@ public int CommentCount
get { return commentCount; }
set { this.RaiseAndSetIfChanged(ref commentCount, value); }
}
+
+ ///
+ /// Gets or sets the number of annotation notices on the file.
+ ///
+ public int AnnotationNoticeCount
+ {
+ get { return annotationNoticeCount; }
+ set { this.RaiseAndSetIfChanged(ref annotationNoticeCount, value); }
+ }
+
+ ///
+ /// Gets or sets the number of annotation errors on the file.
+ ///
+ public int AnnotationWarningCount
+ {
+ get { return annotationWarningCount; }
+ set { this.RaiseAndSetIfChanged(ref annotationWarningCount, value); }
+ }
+
+ ///
+ /// Gets or sets the number of annotation failures on the file.
+ ///
+ public int AnnotationFailureCount
+ {
+ get { return _annotationFailureCount; }
+ set { this.RaiseAndSetIfChanged(ref _annotationFailureCount, value); }
+ }
}
}
diff --git a/src/GitHub.App/ViewModels/GitHubPane/PullRequestFilesViewModel.cs b/src/GitHub.App/ViewModels/GitHubPane/PullRequestFilesViewModel.cs
index f1c51bdf51..6f7ef97e54 100644
--- a/src/GitHub.App/ViewModels/GitHubPane/PullRequestFilesViewModel.cs
+++ b/src/GitHub.App/ViewModels/GitHubPane/PullRequestFilesViewModel.cs
@@ -65,6 +65,26 @@ public PullRequestFilesViewModel(
await editorService.OpenDiff(pullRequestSession, file.RelativePath, thread);
}
});
+
+ OpenFirstAnnotationNotice = ReactiveCommand.CreateFromTask(
+ async file => await OpenFirstAnnotation(editorService, file, CheckAnnotationLevel.Notice));
+
+ OpenFirstAnnotationWarning = ReactiveCommand.CreateFromTask(
+ async file => await OpenFirstAnnotation(editorService, file, CheckAnnotationLevel.Warning));
+
+ OpenFirstAnnotationFailure = ReactiveCommand.CreateFromTask(
+ async file => await OpenFirstAnnotation(editorService, file, CheckAnnotationLevel.Failure));
+ }
+
+ private async Task OpenFirstAnnotation(IPullRequestEditorService editorService, IPullRequestFileNode file,
+ CheckAnnotationLevel checkAnnotationLevel)
+ {
+ var annotationModel = await GetFirstAnnotation(file, checkAnnotationLevel);
+
+ if (annotationModel != null)
+ {
+ await editorService.OpenDiff(pullRequestSession, file.RelativePath, annotationModel.HeadSha, annotationModel.EndLine);
+ }
}
///
@@ -122,6 +142,18 @@ public async Task InitializeAsync(
{
subscriptions.Add(file.WhenAnyValue(x => x.InlineCommentThreads)
.Subscribe(x => node.CommentCount = CountComments(x, filter)));
+
+ subscriptions.Add(file.WhenAnyValue(x => x.InlineAnnotations)
+ .Subscribe(x =>
+ {
+ var noticeCount = x.Count(model => model.AnnotationLevel == CheckAnnotationLevel.Notice);
+ var warningCount = x.Count(model => model.AnnotationLevel == CheckAnnotationLevel.Warning);
+ var failureCount = x.Count(model => model.AnnotationLevel == CheckAnnotationLevel.Failure);
+
+ node.AnnotationNoticeCount = noticeCount;
+ node.AnnotationWarningCount = warningCount;
+ node.AnnotationFailureCount = failureCount;
+ }));
}
var dir = GetDirectory(Path.GetDirectoryName(node.RelativePath), dirs);
@@ -148,6 +180,15 @@ public async Task InitializeAsync(
///
public ReactiveCommand OpenFirstComment { get; }
+ ///
+ public ReactiveCommand OpenFirstAnnotationNotice { get; }
+
+ ///
+ public ReactiveCommand OpenFirstAnnotationWarning { get; }
+
+ ///
+ public ReactiveCommand OpenFirstAnnotationFailure { get; }
+
static int CountComments(
IEnumerable thread,
Func commentFilter)
@@ -200,6 +241,15 @@ async Task GetFirstCommentThread(IPullRequestFileNode
return threads.FirstOrDefault();
}
+ async Task GetFirstAnnotation(IPullRequestFileNode file,
+ CheckAnnotationLevel annotationLevel)
+ {
+ var sessionFile = await pullRequestSession.GetFile(file.RelativePath);
+ var annotations = sessionFile.InlineAnnotations;
+
+ return annotations.FirstOrDefault(model => model.AnnotationLevel == annotationLevel);
+ }
+
///
/// Implements the command.
///
diff --git a/src/GitHub.App/ViewModels/InlineAnnotationViewModel.cs b/src/GitHub.App/ViewModels/InlineAnnotationViewModel.cs
new file mode 100644
index 0000000000..c395a9abef
--- /dev/null
+++ b/src/GitHub.App/ViewModels/InlineAnnotationViewModel.cs
@@ -0,0 +1,21 @@
+using GitHub.Models;
+using GitHub.ViewModels;
+
+namespace GitHub.ViewModels
+{
+ ///
+ public class InlineAnnotationViewModel: IInlineAnnotationViewModel
+ {
+ ///
+ public InlineAnnotationModel Model { get; }
+
+ ///
+ /// Initializes a .
+ ///
+ /// The inline annotation model.
+ public InlineAnnotationViewModel(InlineAnnotationModel model)
+ {
+ Model = model;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/GitHub.App/ViewModels/PullRequestReviewCommentThreadViewModel.cs b/src/GitHub.App/ViewModels/PullRequestReviewCommentThreadViewModel.cs
index 43b0d299ed..c821754f8b 100644
--- a/src/GitHub.App/ViewModels/PullRequestReviewCommentThreadViewModel.cs
+++ b/src/GitHub.App/ViewModels/PullRequestReviewCommentThreadViewModel.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Globalization;
using System.Linq;
@@ -75,8 +76,7 @@ public bool IsNewThread
public bool NeedsPush => needsPush.Value;
///
- public async Task InitializeAsync(
- IPullRequestSession session,
+ public async Task InitializeAsync(IPullRequestSession session,
IPullRequestSessionFile file,
IInlineCommentThreadModel thread,
bool addPlaceholder)
diff --git a/src/GitHub.Exports.Reactive/Models/IPullRequestSessionFile.cs b/src/GitHub.Exports.Reactive/Models/IPullRequestSessionFile.cs
index e13eb31a6c..0cd2a87cf0 100644
--- a/src/GitHub.Exports.Reactive/Models/IPullRequestSessionFile.cs
+++ b/src/GitHub.Exports.Reactive/Models/IPullRequestSessionFile.cs
@@ -54,6 +54,11 @@ public interface IPullRequestSessionFile : INotifyPropertyChanged
///
IReadOnlyList InlineCommentThreads { get; }
+ ///
+ /// Gets the inline annotations for the file.
+ ///
+ IReadOnlyList InlineAnnotations { get; }
+
///
/// Gets an observable that is raised with a collection of 0-based line numbers when the
/// review comments on the file are changed.
diff --git a/src/GitHub.Exports.Reactive/Models/InlineAnnotationModel.cs b/src/GitHub.Exports.Reactive/Models/InlineAnnotationModel.cs
new file mode 100644
index 0000000000..c102614014
--- /dev/null
+++ b/src/GitHub.Exports.Reactive/Models/InlineAnnotationModel.cs
@@ -0,0 +1,82 @@
+using GitHub.Extensions;
+
+namespace GitHub.Models
+{
+ ///
+ /// Represents an inline annotation on an .
+ ///
+ public class InlineAnnotationModel
+ {
+ readonly CheckSuiteModel checkSuite;
+ readonly CheckRunModel checkRun;
+ readonly CheckRunAnnotationModel annotation;
+
+ ///
+ /// Initializes the .
+ ///
+ /// The check suite model.
+ /// The check run model.
+ /// The annotation model.
+ public InlineAnnotationModel(CheckSuiteModel checkSuite, CheckRunModel checkRun,
+ CheckRunAnnotationModel annotation)
+ {
+ Guard.ArgumentNotNull(checkRun, nameof(checkRun));
+ Guard.ArgumentNotNull(annotation, nameof(annotation));
+ Guard.ArgumentNotNull(annotation.AnnotationLevel, nameof(annotation.AnnotationLevel));
+
+ this.checkSuite = checkSuite;
+ this.checkRun = checkRun;
+ this.annotation = annotation;
+ }
+
+ ///
+ /// Gets the annotation path.
+ ///
+ public string Path => annotation.Path;
+
+ ///
+ /// Gets the start line of the annotation.
+ ///
+ public int StartLine => annotation.StartLine;
+
+ ///
+ /// Gets the end line of the annotation.
+ ///
+ public int EndLine => annotation.EndLine;
+
+ ///
+ /// Gets the annotation level.
+ ///
+ public CheckAnnotationLevel AnnotationLevel => annotation.AnnotationLevel;
+
+ ///
+ /// Gets the name of the check suite.
+ ///
+ public string CheckSuiteName => checkSuite.ApplicationName;
+
+ ///
+ /// Gets the name of the check run.
+ ///
+ public string CheckRunName => checkRun.Name;
+
+ ///
+ /// Gets the annotation title.
+ ///
+ public string Title => annotation.Title;
+
+ ///
+ /// Gets the annotation message.
+ ///
+ public string Message => annotation.Message;
+
+ ///
+ /// Gets the sha the check run was created on.
+ ///
+ public string HeadSha => checkSuite.HeadSha;
+
+ ///
+ /// Gets the a descriptor for the line(s) reported.
+ ///
+ public string LineDescription => $"{StartLine}:{EndLine}";
+ }
+}
\ No newline at end of file
diff --git a/src/GitHub.Exports.Reactive/Services/IPullRequestEditorService.cs b/src/GitHub.Exports.Reactive/Services/IPullRequestEditorService.cs
index ec5e274422..954f0a6e34 100644
--- a/src/GitHub.Exports.Reactive/Services/IPullRequestEditorService.cs
+++ b/src/GitHub.Exports.Reactive/Services/IPullRequestEditorService.cs
@@ -37,8 +37,8 @@ public interface IPullRequestEditorService
Task OpenDiff(IPullRequestSession session, string relativePath, string headSha = null, bool scrollToFirstDiff = true);
///
- /// Opens an diff viewer for a file in a pull request with the specified inline comment
- /// thread open.
+ /// Opens an diff viewer for a file in a pull request with the specified inline review
+ /// comment thread open.
///
/// The pull request session.
/// The path to the file, relative to the repository.
@@ -46,6 +46,19 @@ public interface IPullRequestEditorService
/// The opened diff viewer.
Task OpenDiff(IPullRequestSession session, string relativePath, IInlineCommentThreadModel thread);
+ ///
+ /// Opens an diff viewer for a file in a pull request with the specified inline review line open.
+ ///
+ /// The pull request session.
+ /// The path to the file, relative to the repository.
+ ///
+ /// The commit SHA of the right hand side of the diff. Pass null to compare with the
+ /// working directory, or "HEAD" to compare with the HEAD commit of the pull request.
+ ///
+ /// The line number to open
+ /// The opened diff viewer.
+ Task OpenDiff(IPullRequestSession session, string relativePath, string headSha, int fromLine);
+
///
/// Find the active text view.
///
diff --git a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestAnnotationItemViewModel.cs b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestAnnotationItemViewModel.cs
new file mode 100644
index 0000000000..98e60121c4
--- /dev/null
+++ b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestAnnotationItemViewModel.cs
@@ -0,0 +1,37 @@
+using System.Reactive;
+using GitHub.Models;
+using ReactiveUI;
+
+namespace GitHub.ViewModels.GitHubPane
+{
+ ///
+ /// The viewmodel for a single annotation item in a list
+ ///
+ public interface IPullRequestAnnotationItemViewModel
+ {
+ ///
+ /// Gets the annotation model.
+ ///
+ CheckRunAnnotationModel Annotation { get; }
+
+ ///
+ /// Gets a formatted descriptor of the line(s) the annotation is about.
+ ///
+ string LineDescription { get; }
+
+ ///
+ /// Gets or sets a flag to control the expanded state.
+ ///
+ bool IsExpanded { get; set; }
+
+ ///
+ /// Gets a flag which indicates this annotation item is from a file changed in this pull request.
+ ///
+ bool IsFileInPullRequest { get; }
+
+ ///
+ /// Gets a command which opens the annotation in the diff view.
+ ///
+ ReactiveCommand OpenAnnotation { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestAnnotationsViewModel.cs b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestAnnotationsViewModel.cs
new file mode 100644
index 0000000000..9f4ca12e45
--- /dev/null
+++ b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestAnnotationsViewModel.cs
@@ -0,0 +1,91 @@
+using System.Collections.Generic;
+using System.Reactive;
+using System.Threading.Tasks;
+using GitHub.Models;
+using ReactiveUI;
+using ReactiveUI.Legacy;
+
+namespace GitHub.ViewModels.GitHubPane
+{
+ ///
+ /// A viewmodel which displays a list of annotations for a pull request's check run.
+ ///
+ public interface IPullRequestAnnotationsViewModel : IPanePageViewModel
+ {
+ ///
+ /// Gets the local repository.
+ ///
+ LocalRepositoryModel LocalRepository { get; }
+
+ ///
+ /// Gets the owner of the remote repository that contains the pull request.
+ ///
+ ///
+ /// The remote repository may be different from the local repository if the local
+ /// repository is a fork and the user is viewing pull requests from the parent repository.
+ ///
+ string RemoteRepositoryOwner { get; }
+
+ ///
+ /// Gets the number of the pull request.
+ ///
+ int PullRequestNumber { get; }
+
+ ///
+ /// Gets the title of the pull request.
+ ///
+ string PullRequestTitle { get; }
+
+ ///
+ /// Gets the id of the check run.
+ ///
+ string CheckRunId { get; }
+
+ ///
+ /// Gets the name of the check run.
+ ///
+ string CheckRunName { get; }
+
+ ///
+ /// Gets a command which navigates to the parent pull request.
+ ///
+ ReactiveCommand NavigateToPullRequest { get; }
+
+ ///
+ /// Name of the Check Suite.
+ ///
+ string CheckSuiteName { get; }
+
+ ///
+ /// Summary of the Check Run
+ ///
+ string CheckRunSummary { get; }
+
+ ///
+ /// Text of the Check Run
+ ///
+ string CheckRunText { get; }
+
+ ///
+ /// Gets a dictionary of annotations by file path.
+ ///
+ IReadOnlyDictionary AnnotationsDictionary { get; }
+
+ ///
+ /// Initializes the view model.
+ ///
+ /// The local repository.
+ /// The connection to the repository host.
+ /// The pull request's repository owner.
+ /// The pull request's repository name.
+ /// The pull request's number.
+ /// The pull request's check run id.
+ Task InitializeAsync(
+ LocalRepositoryModel localRepository,
+ IConnection connection,
+ string owner,
+ string repo,
+ int pullRequestNumber,
+ string checkRunId);
+ }
+}
\ No newline at end of file
diff --git a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestCheckViewModel.cs b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestCheckViewModel.cs
index 1a9d658a39..d24a9c7212 100644
--- a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestCheckViewModel.cs
+++ b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestCheckViewModel.cs
@@ -1,6 +1,5 @@
using System;
using System.Reactive;
-using System.Windows.Media.Imaging;
using GitHub.Models;
using ReactiveUI;
@@ -12,30 +11,44 @@ namespace GitHub.ViewModels.GitHubPane
public interface IPullRequestCheckViewModel: IViewModel
{
///
- /// The title of the Status/Check
+ /// The title of the Status/Check.
///
string Title { get; }
///
- /// The description of the Status/Check
+ /// The description of the Status/Check.
///
string Description { get; }
///
- /// The status of the Status/Check
+ /// The status of the Status/Check.
///
PullRequestCheckStatus Status { get; }
///
- /// The url where more information about the Status/Check can be found
+ /// The url where more information about the Status/Check can be found.
///
Uri DetailsUrl { get; }
///
- /// A command that opens the DetailsUrl in a browser
+ /// A command that opens the DetailsUrl in a browser.
///
-
ReactiveCommand OpenDetailsUrl { get; }
+
+ ///
+ /// Gets the type of check run, Status/Check.
+ ///
+ PullRequestCheckType CheckType { get; }
+
+ ///
+ /// Gets the id of the check run.
+ ///
+ string CheckRunId { get; }
+
+ ///
+ /// Gets a flag to show this check run has annotations.
+ ///
+ bool HasAnnotations { get; }
}
public enum PullRequestCheckStatus
diff --git a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestDetailViewModel.cs b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestDetailViewModel.cs
index c10c6b2376..52384df726 100644
--- a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestDetailViewModel.cs
+++ b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestDetailViewModel.cs
@@ -5,6 +5,7 @@
using GitHub.Models;
using GitHub.Services;
using ReactiveUI;
+using ReactiveUI.Legacy;
namespace GitHub.ViewModels.GitHubPane
{
@@ -62,7 +63,7 @@ public interface IPullRequestUpdateState
}
///
- /// Represents a view model for displaying details of a pull request.
+ /// A view model which displays the details of a pull request.
///
public interface IPullRequestDetailViewModel : IPanePageViewModel, IOpenInBrowser
{
@@ -165,6 +166,11 @@ public interface IPullRequestDetailViewModel : IPanePageViewModel, IOpenInBrowse
///
ReactiveCommand Push { get; }
+ ///
+ /// Sync submodules for PR branch.
+ ///
+ ReactiveCommand SyncSubmodules { get; }
+
///
/// Gets a command that opens the pull request on GitHub.
///
@@ -176,7 +182,12 @@ public interface IPullRequestDetailViewModel : IPanePageViewModel, IOpenInBrowse
ReactiveCommand ShowReview { get; }
///
- /// Gets the latest pull request Checks & Statuses
+ /// Gets a command that navigates to a pull request's check run annotation list.
+ ///
+ ReactiveCommand ShowAnnotations { get; }
+
+ ///
+ /// Gets the latest pull request checks & statuses.
///
IReadOnlyList Checks { get; }
diff --git a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestFileNode.cs b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestFileNode.cs
index 2eaac8c82b..c9d1b55b8e 100644
--- a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestFileNode.cs
+++ b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestFileNode.cs
@@ -33,5 +33,20 @@ public interface IPullRequestFileNode : IPullRequestChangeNode
/// Gets the number of review comments on the file.
///
int CommentCount { get; }
+
+ ///
+ /// Gets or sets the number of annotation notices on the file.
+ ///
+ int AnnotationNoticeCount { get; }
+
+ ///
+ /// Gets or sets the number of annotation errors on the file.
+ ///
+ int AnnotationWarningCount { get; }
+
+ ///
+ /// Gets or sets the number of annotation failures on the file.
+ ///
+ int AnnotationFailureCount { get; }
}
}
\ No newline at end of file
diff --git a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestFilesViewModel.cs b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestFilesViewModel.cs
index 7f2eda69c8..ebef31bf39 100644
--- a/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestFilesViewModel.cs
+++ b/src/GitHub.Exports.Reactive/ViewModels/GitHubPane/IPullRequestFilesViewModel.cs
@@ -51,6 +51,24 @@ public interface IPullRequestFilesViewModel : IViewModel, IDisposable
///
ReactiveCommand OpenFirstComment { get; }
+ ///
+ /// Gets a command that opens the first annotation notice for a in
+ /// the diff viewer.
+ ///
+ ReactiveCommand OpenFirstAnnotationNotice { get; }
+
+ ///
+ /// Gets a command that opens the first annotation warning for a in
+ /// the diff viewer.
+ ///
+ ReactiveCommand OpenFirstAnnotationWarning { get; }
+
+ ///
+ /// Gets a command that opens the first annotation failure for a in
+ /// the diff viewer.
+ ///
+ ReactiveCommand OpenFirstAnnotationFailure { get; }
+
///
/// Initializes the view model.
///
diff --git a/src/GitHub.Exports.Reactive/ViewModels/IInlineAnnotationViewModel.cs b/src/GitHub.Exports.Reactive/ViewModels/IInlineAnnotationViewModel.cs
new file mode 100644
index 0000000000..1ed533c306
--- /dev/null
+++ b/src/GitHub.Exports.Reactive/ViewModels/IInlineAnnotationViewModel.cs
@@ -0,0 +1,15 @@
+using GitHub.Models;
+
+namespace GitHub.ViewModels
+{
+ ///
+ /// A view model that represents a single inline annotation.
+ ///
+ public interface IInlineAnnotationViewModel
+ {
+ ///
+ /// Gets the inline annotation model.
+ ///
+ InlineAnnotationModel Model { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/GitHub.Exports.Reactive/ViewModels/IPullRequestReviewCommentThreadViewModel.cs b/src/GitHub.Exports.Reactive/ViewModels/IPullRequestReviewCommentThreadViewModel.cs
index 0a74e78b56..0afeb7ba12 100644
--- a/src/GitHub.Exports.Reactive/ViewModels/IPullRequestReviewCommentThreadViewModel.cs
+++ b/src/GitHub.Exports.Reactive/ViewModels/IPullRequestReviewCommentThreadViewModel.cs
@@ -1,4 +1,5 @@
-using System.Threading.Tasks;
+using System.Collections.Generic;
+using System.Threading.Tasks;
using GitHub.Models;
using GitHub.Services;
@@ -48,10 +49,9 @@ public interface IPullRequestReviewCommentThreadViewModel : ICommentThreadViewMo
/// The file that the comment is on.
/// The thread.
///
- /// Whether to add a placeholder comment at the end of the thread.
+ /// Whether to add a placeholder comment at the end of the thread.
///
- Task InitializeAsync(
- IPullRequestSession session,
+ Task InitializeAsync(IPullRequestSession session,
IPullRequestSessionFile file,
IInlineCommentThreadModel thread,
bool addPlaceholder);
@@ -64,8 +64,7 @@ Task InitializeAsync(
/// The 0-based line number of the thread.
/// The side of the diff.
/// Whether to start the placeholder in edit state.
- Task InitializeNewAsync(
- IPullRequestSession session,
+ Task InitializeNewAsync(IPullRequestSession session,
IPullRequestSessionFile file,
int lineNumber,
DiffSide side,
diff --git a/src/GitHub.Exports/Models/AnnotationModel.cs b/src/GitHub.Exports/Models/AnnotationModel.cs
index c986fc8aa3..3f8dc694b6 100644
--- a/src/GitHub.Exports/Models/AnnotationModel.cs
+++ b/src/GitHub.Exports/Models/AnnotationModel.cs
@@ -5,11 +5,6 @@
///
public class CheckRunAnnotationModel
{
- ///
- /// The path to the file that this annotation was made on.
- ///
- public string BlobUrl { get; set; }
-
///
/// The starting line number (1 indexed).
///
@@ -23,7 +18,7 @@ public class CheckRunAnnotationModel
///
/// The path that this annotation was made on.
///
- public string Filename { get; set; }
+ public string Path { get; set; }
///
/// The annotation's message.
@@ -38,11 +33,6 @@ public class CheckRunAnnotationModel
///
/// The annotation's severity level.
///
- public CheckAnnotationLevel? AnnotationLevel { get; set; }
-
- ///
- /// Additional information about the annotation.
- ///
- public string RawDetails { get; set; }
+ public CheckAnnotationLevel AnnotationLevel { get; set; }
}
}
\ No newline at end of file
diff --git a/src/GitHub.Exports/Models/CheckRunModel.cs b/src/GitHub.Exports/Models/CheckRunModel.cs
index a25335c8bb..de6ec46d9d 100644
--- a/src/GitHub.Exports/Models/CheckRunModel.cs
+++ b/src/GitHub.Exports/Models/CheckRunModel.cs
@@ -8,7 +8,14 @@ namespace GitHub.Models
///
public class CheckRunModel
{
- /// The conclusion of the check run.
+ ///
+ /// The id of a Check Run.
+ ///
+ public string Id { get; set; }
+
+ ///
+ /// The conclusion of the check run.
+ ///
public CheckConclusionState? Conclusion { get; set; }
///
@@ -21,7 +28,14 @@ public class CheckRunModel
///
public DateTimeOffset? CompletedAt { get; set; }
- /// The name of the check for this check run.
+ ///
+ /// The check run's annotations.
+ ///
+ public List Annotations { get; set; }
+
+ ///
+ /// The name of the check for this check run.
+ ///
public string Name { get; set; }
///
@@ -33,5 +47,10 @@ public class CheckRunModel
/// The summary of a Check Run.
///
public string Summary { get; set; }
+
+ ///
+ /// The detail of a Check Run.
+ ///
+ public string Text { get; set; }
}
}
\ No newline at end of file
diff --git a/src/GitHub.Exports/Models/CheckSuiteModel.cs b/src/GitHub.Exports/Models/CheckSuiteModel.cs
index c6e82cc1be..43ad354910 100644
--- a/src/GitHub.Exports/Models/CheckSuiteModel.cs
+++ b/src/GitHub.Exports/Models/CheckSuiteModel.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
namespace GitHub.Models
@@ -8,9 +8,16 @@ namespace GitHub.Models
///
public class CheckSuiteModel
{
+ ///
+ /// The head sha of a Check Suite.
+ ///
+ public string HeadSha { get; set; }
+
///
/// The check runs associated with a check suite.
///
public List CheckRuns { get; set; }
+
+ public string ApplicationName { get; set; }
}
}
\ No newline at end of file
diff --git a/src/GitHub.App/ViewModels/GitHubPane/PullRequestCheckType.cs b/src/GitHub.Exports/Models/PullRequestCheckType.cs
similarity index 67%
rename from src/GitHub.App/ViewModels/GitHubPane/PullRequestCheckType.cs
rename to src/GitHub.Exports/Models/PullRequestCheckType.cs
index f5cc9dbc20..d088320b53 100644
--- a/src/GitHub.App/ViewModels/GitHubPane/PullRequestCheckType.cs
+++ b/src/GitHub.Exports/Models/PullRequestCheckType.cs
@@ -1,4 +1,4 @@
-namespace GitHub.ViewModels.GitHubPane
+namespace GitHub.Models
{
public enum PullRequestCheckType
{
diff --git a/src/GitHub.Exports/Settings/generated/IPackageSettings.cs b/src/GitHub.Exports/Settings/generated/IPackageSettings.cs
index fa8d50b008..aec212bed9 100644
--- a/src/GitHub.Exports/Settings/generated/IPackageSettings.cs
+++ b/src/GitHub.Exports/Settings/generated/IPackageSettings.cs
@@ -1,4 +1,4 @@
-// This is an automatically generated file, based on settings.json and PackageSettingsGen.tt
+// This is an automatically generated file, based on settings.json and PackageSettingsGen.tt
/* settings.json content:
{
"settings": [
@@ -51,4 +51,4 @@ public interface IPackageSettings : INotifyPropertyChanged
bool HideTeamExplorerWelcomeMessage { get; set; }
bool EnableTraceLogging { get; set; }
}
-}
+}
\ No newline at end of file
diff --git a/src/GitHub.Exports/ViewModels/GitHubPane/IGitHubPaneViewModel.cs b/src/GitHub.Exports/ViewModels/GitHubPane/IGitHubPaneViewModel.cs
index ecab58b39d..69eb5c1c2f 100644
--- a/src/GitHub.Exports/ViewModels/GitHubPane/IGitHubPaneViewModel.cs
+++ b/src/GitHub.Exports/ViewModels/GitHubPane/IGitHubPaneViewModel.cs
@@ -91,6 +91,15 @@ public interface IGitHubPaneViewModel : IViewModel
/// The pull rqeuest number.
Task ShowPullRequest(string owner, string repo, int number);
+ ///
+ /// Shows the details for a pull request's check run in the GitHub pane.
+ ///
+ /// The repository owner.
+ /// The repository name.
+ /// The pull rqeuest number.
+ /// The check run id.
+ Task ShowPullRequestCheckRun(string owner, string repo, int number, string checkRunId);
+
///
/// Shows the pull requests reviews authored by a user.
///
diff --git a/src/GitHub.InlineReviews/Commands/NextInlineCommentCommand.cs b/src/GitHub.InlineReviews/Commands/NextInlineCommentCommand.cs
index 42ea92d69a..e6b4c35f50 100644
--- a/src/GitHub.InlineReviews/Commands/NextInlineCommentCommand.cs
+++ b/src/GitHub.InlineReviews/Commands/NextInlineCommentCommand.cs
@@ -53,7 +53,7 @@ public override Task Execute(InlineCommentNavigationParams parameter)
if (tags.Count > 0)
{
var cursorPoint = GetCursorPoint(textViews[0], parameter);
- var next = tags.FirstOrDefault(x => x.Point > cursorPoint) ?? tags.First();
+ var next = tags.FirstOrDefault(x => x.Point >= cursorPoint) ?? tags.First();
ShowPeekComments(parameter, next.TextView, next.Tag, textViews);
}
diff --git a/src/GitHub.InlineReviews/GitHub.InlineReviews.csproj b/src/GitHub.InlineReviews/GitHub.InlineReviews.csproj
index ef3c381721..6938de120a 100644
--- a/src/GitHub.InlineReviews/GitHub.InlineReviews.csproj
+++ b/src/GitHub.InlineReviews/GitHub.InlineReviews.csproj
@@ -354,11 +354,11 @@
..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll
-
- ..\..\packages\Octokit.GraphQL.0.1.1-beta\lib\netstandard1.1\Octokit.GraphQL.dll
+
+ ..\..\packages\Octokit.GraphQL.0.1.2-beta\lib\netstandard1.1\Octokit.GraphQL.dll
-
- ..\..\packages\Octokit.GraphQL.0.1.1-beta\lib\netstandard1.1\Octokit.GraphQL.Core.dll
+
+ ..\..\packages\Octokit.GraphQL.0.1.2-beta\lib\netstandard1.1\Octokit.GraphQL.Core.dll
@@ -441,9 +441,7 @@
PreserveNewest
-
-
-
+
diff --git a/src/GitHub.InlineReviews/Models/PullRequestSessionFile.cs b/src/GitHub.InlineReviews/Models/PullRequestSessionFile.cs
index a38b0d22c7..8cfa924372 100644
--- a/src/GitHub.InlineReviews/Models/PullRequestSessionFile.cs
+++ b/src/GitHub.InlineReviews/Models/PullRequestSessionFile.cs
@@ -25,6 +25,7 @@ public class PullRequestSessionFile : ReactiveObject, IPullRequestSessionFile
IReadOnlyList diff;
string commitSha;
IReadOnlyList inlineCommentThreads;
+ IReadOnlyList inlineAnnotations;
///
/// Initializes a new instance of the class.
@@ -99,6 +100,29 @@ public IReadOnlyList InlineCommentThreads
///
public IObservable>> LinesChanged => linesChanged;
+ ///
+ public IReadOnlyList InlineAnnotations
+ {
+ get
+ {
+ return inlineAnnotations;
+ }
+ set
+ {
+ var lines = (inlineAnnotations ?? Enumerable.Empty())?
+ .Concat(value ?? Enumerable.Empty())
+ .Select(x => Tuple.Create(x.StartLine, DiffSide.Right))
+ .Where(x => x.Item1 >= 0)
+ .Distinct()
+ .ToList();
+
+ this.RaisePropertyChanging();
+ inlineAnnotations = value;
+ this.RaisePropertyChanged();
+ NotifyLinesChanged(lines);
+ }
+ }
+
///
/// Raises the signal.
///
diff --git a/src/GitHub.InlineReviews/Services/IPullRequestSessionService.cs b/src/GitHub.InlineReviews/Services/IPullRequestSessionService.cs
index 7a3d9c845d..88d11c3866 100644
--- a/src/GitHub.InlineReviews/Services/IPullRequestSessionService.cs
+++ b/src/GitHub.InlineReviews/Services/IPullRequestSessionService.cs
@@ -44,6 +44,20 @@ Task> Diff(
string relativePath,
byte[] contents);
+
+
+ ///
+ /// Builds a set of annotation models for a file based on a pull request model
+ ///
+ /// The pull request session.
+ /// The relative path to the file.
+ ///
+ /// A collection of objects.
+ ///
+ IReadOnlyList BuildAnnotations(
+ PullRequestDetailModel pullRequest,
+ string relativePath);
+
///
/// Builds a set of comment thread models for a file based on a pull request model and a diff.
///
diff --git a/src/GitHub.InlineReviews/Services/PullRequestSession.cs b/src/GitHub.InlineReviews/Services/PullRequestSession.cs
index 88f2995824..29f232edc2 100644
--- a/src/GitHub.InlineReviews/Services/PullRequestSession.cs
+++ b/src/GitHub.InlineReviews/Services/PullRequestSession.cs
@@ -310,6 +310,7 @@ async Task UpdateFile(PullRequestSessionFile file)
file.CommitSha = file.IsTrackingHead ? PullRequest.HeadRefSha : file.CommitSha;
file.Diff = await service.Diff(LocalRepository, mergeBaseSha, file.CommitSha, file.RelativePath);
file.InlineCommentThreads = service.BuildCommentThreads(PullRequest, file.RelativePath, file.Diff, file.CommitSha);
+ file.InlineAnnotations = service.BuildAnnotations(PullRequest, file.RelativePath);
}
void UpdatePendingReview()
diff --git a/src/GitHub.InlineReviews/Services/PullRequestSessionService.cs b/src/GitHub.InlineReviews/Services/PullRequestSessionService.cs
index 8f8aa08f41..5043b96d8c 100644
--- a/src/GitHub.InlineReviews/Services/PullRequestSessionService.cs
+++ b/src/GitHub.InlineReviews/Services/PullRequestSessionService.cs
@@ -8,7 +8,6 @@
using System.Text;
using System.Threading.Tasks;
using GitHub.Api;
-using GitHub.App.Services;
using GitHub.Factories;
using GitHub.InlineReviews.Models;
using GitHub.Models;
@@ -18,22 +17,15 @@
using LibGit2Sharp;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Projection;
-using Octokit;
using Octokit.GraphQL;
-using Octokit.GraphQL.Core;
using Octokit.GraphQL.Model;
using ReactiveUI;
using Serilog;
using PullRequestReviewEvent = Octokit.PullRequestReviewEvent;
using static Octokit.GraphQL.Variable;
-using CheckAnnotationLevel = GitHub.Models.CheckAnnotationLevel;
-using CheckConclusionState = GitHub.Models.CheckConclusionState;
-using CheckStatusState = GitHub.Models.CheckStatusState;
using DraftPullRequestReviewComment = Octokit.GraphQL.Model.DraftPullRequestReviewComment;
using FileMode = System.IO.FileMode;
using NotFoundException = LibGit2Sharp.NotFoundException;
-using PullRequestReviewState = Octokit.GraphQL.Model.PullRequestReviewState;
-using StatusState = GitHub.Models.StatusState;
// GraphQL DatabaseId field are marked as deprecated, but we need them for interop with REST.
#pragma warning disable CS0618
@@ -97,6 +89,23 @@ public virtual async Task> Diff(LocalRepositoryModel re
}
}
+ ///
+ public IReadOnlyList BuildAnnotations(
+ PullRequestDetailModel pullRequest,
+ string relativePath)
+ {
+ relativePath = relativePath.Replace("\\", "/");
+
+ return pullRequest.CheckSuites
+ ?.SelectMany(checkSuite => checkSuite.CheckRuns.Select(checkRun => new { checkSuite, checkRun}))
+ .SelectMany(arg =>
+ arg.checkRun.Annotations
+ .Where(annotation => annotation.Path == relativePath)
+ .Select(annotation => new InlineAnnotationModel(arg.checkSuite, arg.checkRun, annotation)))
+ .OrderBy(tuple => tuple.StartLine)
+ .ToArray();
+ }
+
///
public IReadOnlyList BuildCommentThreads(
PullRequestDetailModel pullRequest,
@@ -357,6 +366,10 @@ public virtual async Task ReadPullRequestDetail(HostAddr
result.Statuses = lastCommitModel.Statuses;
result.CheckSuites = lastCommitModel.CheckSuites;
+ foreach (var checkSuite in result.CheckSuites)
+ {
+ checkSuite.HeadSha = lastCommitModel.HeadSha;
+ }
result.ChangedFiles = files.Select(file => new PullRequestFileModel
{
@@ -764,18 +777,32 @@ async Task GetPullRequestLastCommitAdapter(HostAddress addres
.PullRequest(Var(nameof(number))).Commits(last: 1).Nodes.Select(
commit => new LastCommitAdapter
{
+ HeadSha = commit.Commit.Oid,
CheckSuites = commit.Commit.CheckSuites(null, null, null, null, null).AllPages(10)
.Select(suite => new CheckSuiteModel
{
CheckRuns = suite.CheckRuns(null, null, null, null, null).AllPages(10)
.Select(run => new CheckRunModel
{
+ Id = run.Id.Value,
Conclusion = run.Conclusion.FromGraphQl(),
Status = run.Status.FromGraphQl(),
Name = run.Name,
DetailsUrl = run.Permalink,
Summary = run.Summary,
- }).ToList()
+ Text = run.Text,
+ Annotations = run.Annotations(null, null, null, null).AllPages()
+ .Select(annotation => new CheckRunAnnotationModel
+ {
+ Title = annotation.Title,
+ Message = annotation.Message,
+ Path = annotation.Path,
+ AnnotationLevel = annotation.AnnotationLevel.Value.FromGraphQl(),
+ StartLine = annotation.Location.Start.Line,
+ EndLine = annotation.Location.End.Line,
+ }).ToList()
+ }).ToList(),
+ ApplicationName = suite.App != null ? suite.App.Name : "Private App"
}).ToList(),
Statuses = commit.Commit.Status
.Select(context =>
@@ -784,7 +811,7 @@ async Task GetPullRequestLastCommitAdapter(HostAddress addres
State = statusContext.State.FromGraphQl(),
Context = statusContext.Context,
TargetUrl = statusContext.TargetUrl,
- Description = statusContext.Description,
+ Description = statusContext.Description
}).ToList()
).SingleOrDefault()
}
@@ -921,6 +948,8 @@ class LastCommitAdapter
public List CheckSuites { get; set; }
public List Statuses { get; set; }
+
+ public string HeadSha { get; set; }
}
}
}
diff --git a/src/GitHub.InlineReviews/Tags/InlineCommentGlyphFactory.cs b/src/GitHub.InlineReviews/Tags/InlineCommentGlyphFactory.cs
index 6d5c4ac613..f43c3265ea 100644
--- a/src/GitHub.InlineReviews/Tags/InlineCommentGlyphFactory.cs
+++ b/src/GitHub.InlineReviews/Tags/InlineCommentGlyphFactory.cs
@@ -56,12 +56,10 @@ static UserControl CreateGlyph(InlineCommentTag tag)
{
return new AddInlineCommentGlyph();
}
- else if (showTag != null)
+
+ if (showTag != null)
{
- return new ShowInlineCommentGlyph()
- {
- Opacity = showTag.Thread.IsStale ? 0.5 : 1,
- };
+ return new ShowInlineCommentGlyph();
}
throw new ArgumentException($"Unknown 'InlineCommentTag' type '{tag}'");
diff --git a/src/GitHub.InlineReviews/Tags/InlineCommentTagger.cs b/src/GitHub.InlineReviews/Tags/InlineCommentTagger.cs
index 3891003d63..d3e5d651b3 100644
--- a/src/GitHub.InlineReviews/Tags/InlineCommentTagger.cs
+++ b/src/GitHub.InlineReviews/Tags/InlineCommentTagger.cs
@@ -84,24 +84,58 @@ public IEnumerable> GetTags(NormalizedSnapshotSpanCol
{
var startLine = span.Start.GetContainingLine().LineNumber;
var endLine = span.End.GetContainingLine().LineNumber;
- var linesWithComments = new BitArray((endLine - startLine) + 1);
+ var linesWithTags = new BitArray((endLine - startLine) + 1);
var spanThreads = file.InlineCommentThreads.Where(x =>
x.LineNumber >= startLine &&
- x.LineNumber <= endLine);
+ x.LineNumber <= endLine)
+ .ToArray();
- foreach (var thread in spanThreads)
+ var spanThreadsByLine = spanThreads.ToDictionary(model => model.LineNumber);
+
+ Dictionary spanAnnotationsByLine = null;
+ if (side == DiffSide.Right)
+ {
+ var spanAnnotations = file.InlineAnnotations.Where(x =>
+ x.EndLine - 1 >= startLine &&
+ x.EndLine - 1 <= endLine);
+
+ spanAnnotationsByLine = spanAnnotations
+ .GroupBy(model => model.EndLine)
+ .ToDictionary(models => models.Key - 1, models => models.ToArray());
+ }
+
+ var lines = spanThreadsByLine.Keys.Union(spanAnnotationsByLine?.Keys ?? Enumerable.Empty());
+ foreach (var line in lines)
{
var snapshot = span.Snapshot;
- var line = snapshot.GetLineFromLineNumber(thread.LineNumber);
+ var snapshotLine = snapshot.GetLineFromLineNumber(line);
- if ((side == DiffSide.Left && thread.DiffLineType == DiffChangeType.Delete) ||
- (side == DiffSide.Right && thread.DiffLineType != DiffChangeType.Delete))
+ if (spanThreadsByLine.TryGetValue(line, out var thread))
{
- linesWithComments[thread.LineNumber - startLine] = true;
+ var isThreadDeleteSide = thread.DiffLineType == DiffChangeType.Delete;
+ var sidesMatch = side == DiffSide.Left && isThreadDeleteSide || side == DiffSide.Right && !isThreadDeleteSide;
+ if (!sidesMatch)
+ {
+ thread = null;
+ }
+ }
+
+ InlineAnnotationModel[] annotations = null;
+ spanAnnotationsByLine?.TryGetValue(line, out annotations);
+
+ if (thread != null || annotations != null)
+ {
+ linesWithTags[line - startLine] = true;
+
+ var showInlineTag = new ShowInlineCommentTag(currentSession, line, thread?.DiffLineType ?? DiffChangeType.Add)
+ {
+ Thread = thread,
+ Annotations = annotations
+ };
result.Add(new TagSpan(
- new SnapshotSpan(line.Start, line.End),
- new ShowInlineCommentTag(currentSession, thread)));
+ new SnapshotSpan(snapshotLine.Start, snapshotLine.End),
+ showInlineTag));
}
}
@@ -113,7 +147,7 @@ public IEnumerable> GetTags(NormalizedSnapshotSpanCol
if (lineNumber >= startLine &&
lineNumber <= endLine &&
- !linesWithComments[lineNumber - startLine]
+ !linesWithTags[lineNumber - startLine]
&& (side == DiffSide.Right || line.Type == DiffChangeType.Delete))
{
var snapshotLine = span.Snapshot.GetLineFromLineNumber(lineNumber);
diff --git a/src/GitHub.InlineReviews/Tags/ShowInlineCommentGlyph.xaml b/src/GitHub.InlineReviews/Tags/ShowInlineCommentGlyph.xaml
index 77e7386777..1e0add5448 100644
--- a/src/GitHub.InlineReviews/Tags/ShowInlineCommentGlyph.xaml
+++ b/src/GitHub.InlineReviews/Tags/ShowInlineCommentGlyph.xaml
@@ -9,16 +9,25 @@
-
diff --git a/src/GitHub.InlineReviews/Tags/ShowInlineCommentTag.cs b/src/GitHub.InlineReviews/Tags/ShowInlineCommentTag.cs
index b1071754cf..ab72d05db3 100644
--- a/src/GitHub.InlineReviews/Tags/ShowInlineCommentTag.cs
+++ b/src/GitHub.InlineReviews/Tags/ShowInlineCommentTag.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using GitHub.Extensions;
using GitHub.Models;
using GitHub.Services;
@@ -14,20 +15,21 @@ public class ShowInlineCommentTag : InlineCommentTag
/// Initializes a new instance of the class.
///
/// The pull request session.
- /// A model holding the details of the thread.
- public ShowInlineCommentTag(
- IPullRequestSession session,
- IInlineCommentThreadModel thread)
- : base(session, thread.LineNumber, thread.DiffLineType)
+ /// 0-based index of the inline tag
+ /// The diff type for the inline comment
+ public ShowInlineCommentTag(IPullRequestSession session, int lineNumber, DiffChangeType diffLineType)
+ : base(session, lineNumber, diffLineType)
{
- Guard.ArgumentNotNull(thread, nameof(thread));
-
- Thread = thread;
}
///
/// Gets a model holding details of the thread at the tagged line.
///
- public IInlineCommentThreadModel Thread { get; }
+ public IInlineCommentThreadModel Thread { get; set; }
+
+ ///
+ /// Gets a list of models holding details of the annotations at the tagged line.
+ ///
+ public IReadOnlyList Annotations { get; set; }
}
}
diff --git a/src/GitHub.InlineReviews/Tags/ShowInlineGlyph.xaml b/src/GitHub.InlineReviews/Tags/ShowInlineGlyph.xaml
new file mode 100644
index 0000000000..cf5726b99d
--- /dev/null
+++ b/src/GitHub.InlineReviews/Tags/ShowInlineGlyph.xaml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/GitHub.InlineReviews/Tags/ShowInlineGlyph.xaml.cs b/src/GitHub.InlineReviews/Tags/ShowInlineGlyph.xaml.cs
new file mode 100644
index 0000000000..e8b5ac4a68
--- /dev/null
+++ b/src/GitHub.InlineReviews/Tags/ShowInlineGlyph.xaml.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Windows.Controls;
+
+namespace GitHub.InlineReviews.Tags
+{
+ public partial class ShowInlineGlyph : UserControl
+ {
+ public ShowInlineGlyph()
+ {
+ InitializeComponent();
+ }
+
+ }
+}
diff --git a/src/GitHub.InlineReviews/ViewModels/InlineCommentPeekViewModel.cs b/src/GitHub.InlineReviews/ViewModels/InlineCommentPeekViewModel.cs
index efaf81971d..50431ece39 100644
--- a/src/GitHub.InlineReviews/ViewModels/InlineCommentPeekViewModel.cs
+++ b/src/GitHub.InlineReviews/ViewModels/InlineCommentPeekViewModel.cs
@@ -33,12 +33,14 @@ public sealed class InlineCommentPeekViewModel : ReactiveObject, IDisposable
IPullRequestSession session;
IPullRequestSessionFile file;
IPullRequestReviewCommentThreadViewModel thread;
+ IReadOnlyList annotations;
IDisposable fileSubscription;
IDisposable sessionSubscription;
IDisposable threadSubscription;
ITrackingPoint triggerPoint;
string relativePath;
DiffSide side;
+ bool availableForComment;
///
/// Initializes a new instance of the class.
@@ -86,6 +88,21 @@ public InlineCommentPeekViewModel(IInlineCommentPeekService peekService,
Observable.Return(previousCommentCommand.Enabled));
}
+ public bool AvailableForComment
+ {
+ get { return availableForComment; }
+ private set { this.RaiseAndSetIfChanged(ref availableForComment, value); }
+ }
+
+ ///
+ /// Gets the annotations displayed.
+ ///
+ public IReadOnlyList Annotations
+ {
+ get { return annotations; }
+ private set { this.RaiseAndSetIfChanged(ref annotations, value); }
+ }
+
///
/// Gets the thread of comments to display.
///
@@ -168,27 +185,39 @@ async Task UpdateThread()
Thread = null;
threadSubscription?.Dispose();
+ Annotations = null;
+
if (file == null)
return;
var lineAndLeftBuffer = peekService.GetLineNumber(peekSession, triggerPoint);
var lineNumber = lineAndLeftBuffer.Item1;
var leftBuffer = lineAndLeftBuffer.Item2;
+
+ AvailableForComment =
+ file.Diff.Any(chunk => chunk.Lines
+ .Any(line => line.NewLineNumber == lineNumber));
+
var thread = file.InlineCommentThreads?.FirstOrDefault(x =>
x.LineNumber == lineNumber &&
((leftBuffer && x.DiffLineType == DiffChangeType.Delete) || (!leftBuffer && x.DiffLineType != DiffChangeType.Delete)));
- var vm = factory.CreateViewModel();
+
+ Annotations = file.InlineAnnotations?.Where(model => model.EndLine - 1 == lineNumber)
+ .Select(model => new InlineAnnotationViewModel(model))
+ .ToArray();
+
+ var threadModel = factory.CreateViewModel();
if (thread?.Comments.Count > 0)
{
- await vm.InitializeAsync(session, file, thread, true);
+ await threadModel.InitializeAsync(session, file, thread, true);
}
else
{
- await vm.InitializeNewAsync(session, file, lineNumber, side, true);
+ await threadModel.InitializeNewAsync(session, file, lineNumber, side, true);
}
- Thread = vm;
+ Thread = threadModel;
}
async Task SessionChanged(IPullRequestSession pullRequestSession)
diff --git a/src/GitHub.InlineReviews/Views/InlineCommentPeekView.xaml b/src/GitHub.InlineReviews/Views/InlineCommentPeekView.xaml
index e8a7a4269a..55cb7fb4af 100644
--- a/src/GitHub.InlineReviews/Views/InlineCommentPeekView.xaml
+++ b/src/GitHub.InlineReviews/Views/InlineCommentPeekView.xaml
@@ -3,10 +3,10 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:ghfvs="https://github.com/github/VisualStudio"
xmlns:local="clr-namespace:GitHub.InlineReviews.Views"
xmlns:cache="clr-namespace:GitHub.UI.Helpers;assembly=GitHub.UI"
xmlns:ui="clr-namespace:GitHub.UI;assembly=GitHub.UI"
+ xmlns:ghfvs="https://github.com/github/VisualStudio"
mc:Ignorable="d"
d:DesignHeight="200" d:DesignWidth="500">
@@ -104,9 +104,19 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/GitHub.InlineReviews/Views/InlineCommentPeekView.xaml.cs b/src/GitHub.InlineReviews/Views/InlineCommentPeekView.xaml.cs
index 77e1511a6d..10cc2c5e06 100644
--- a/src/GitHub.InlineReviews/Views/InlineCommentPeekView.xaml.cs
+++ b/src/GitHub.InlineReviews/Views/InlineCommentPeekView.xaml.cs
@@ -15,17 +15,19 @@ public InlineCommentPeekView()
InitializeComponent();
desiredHeight = new Subject();
- threadView.LayoutUpdated += ThreadViewLayoutUpdated;
+ threadView.LayoutUpdated += ChildLayoutUpdated;
+ annotationsView.LayoutUpdated += ChildLayoutUpdated;
threadScroller.PreviewMouseWheel += ScrollViewerUtilities.FixMouseWheelScroll;
}
public IObservable DesiredHeight => desiredHeight;
- void ThreadViewLayoutUpdated(object sender, EventArgs e)
+ void ChildLayoutUpdated(object sender, EventArgs e)
{
var otherControlsHeight = ActualHeight - threadScroller.ActualHeight;
var threadViewHeight = threadView.DesiredSize.Height + threadView.Margin.Top + threadView.Margin.Bottom;
- desiredHeight.OnNext(threadViewHeight + otherControlsHeight);
+ var annotationsViewHeight = annotationsView.DesiredSize.Height + annotationsView.Margin.Top + annotationsView.Margin.Bottom;
+ desiredHeight.OnNext(threadViewHeight + annotationsViewHeight + otherControlsHeight);
}
}
}
diff --git a/src/GitHub.InlineReviews/packages.config b/src/GitHub.InlineReviews/packages.config
index 68889b5182..5b8e94fa19 100644
--- a/src/GitHub.InlineReviews/packages.config
+++ b/src/GitHub.InlineReviews/packages.config
@@ -36,7 +36,7 @@
-
+
diff --git a/src/GitHub.VisualStudio.UI/Views/CommentThreadView.xaml b/src/GitHub.VisualStudio.UI/Views/CommentThreadView.xaml
index 86d1616bb9..ec7257d64b 100644
--- a/src/GitHub.VisualStudio.UI/Views/CommentThreadView.xaml
+++ b/src/GitHub.VisualStudio.UI/Views/CommentThreadView.xaml
@@ -2,39 +2,20 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:sampleData="https://github.com/github/VisualStudio"
xmlns:local="clr-namespace:GitHub.VisualStudio.Views"
- xmlns:ghfvs="https://github.com/github/VisualStudio"
- mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300">
+ mc:Ignorable="d" d:DesignHeight="400" d:DesignWidth="500">
-
-
-
-
-
+
-
+
+
diff --git a/src/GitHub.VisualStudio.UI/Views/CommentThreadView.xaml.cs b/src/GitHub.VisualStudio.UI/Views/CommentThreadView.xaml.cs
index 4fb4fc78d5..f92c491604 100644
--- a/src/GitHub.VisualStudio.UI/Views/CommentThreadView.xaml.cs
+++ b/src/GitHub.VisualStudio.UI/Views/CommentThreadView.xaml.cs
@@ -9,7 +9,6 @@ public partial class CommentThreadView : UserControl
public CommentThreadView()
{
InitializeComponent();
- PreviewMouseWheel += ScrollViewerUtilities.FixMouseWheelScroll;
}
}
}
diff --git a/src/GitHub.VisualStudio.UI/Views/CommentView.xaml b/src/GitHub.VisualStudio.UI/Views/CommentView.xaml
index 527b36c50f..5368bb67f8 100644
--- a/src/GitHub.VisualStudio.UI/Views/CommentView.xaml
+++ b/src/GitHub.VisualStudio.UI/Views/CommentView.xaml
@@ -37,80 +37,97 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -149,7 +166,7 @@
AcceptsReturn="True"
AcceptsTab="True"
IsReadOnly="{Binding IsReadOnly}"
- Margin="4 0"
+ Margin="4 0 4 4"
Text="{Binding Body, UpdateSourceTrigger=PropertyChanged}"
TextWrapping="Wrap"
VerticalAlignment="Center"
diff --git a/src/GitHub.VisualStudio.UI/Views/GitHubPane/PullRequestAnnotationsView.xaml b/src/GitHub.VisualStudio.UI/Views/GitHubPane/PullRequestAnnotationsView.xaml
new file mode 100644
index 0000000000..f31c2bb315
--- /dev/null
+++ b/src/GitHub.VisualStudio.UI/Views/GitHubPane/PullRequestAnnotationsView.xaml
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ for
+
+
+ #
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/GitHub.VisualStudio.UI/Views/GitHubPane/PullRequestAnnotationsView.xaml.cs b/src/GitHub.VisualStudio.UI/Views/GitHubPane/PullRequestAnnotationsView.xaml.cs
new file mode 100644
index 0000000000..222b268468
--- /dev/null
+++ b/src/GitHub.VisualStudio.UI/Views/GitHubPane/PullRequestAnnotationsView.xaml.cs
@@ -0,0 +1,22 @@
+using System;
+using System.ComponentModel.Composition;
+using System.Windows.Forms;
+using GitHub.Exports;
+using GitHub.Services;
+using GitHub.UI;
+using GitHub.ViewModels.GitHubPane;
+using ReactiveUI;
+using UserControl = System.Windows.Controls.UserControl;
+
+namespace GitHub.VisualStudio.Views.GitHubPane
+{
+ [ExportViewFor(typeof(IPullRequestAnnotationsViewModel))]
+ [PartCreationPolicy(CreationPolicy.NonShared)]
+ public partial class PullRequestAnnotationsView : UserControl
+ {
+ public PullRequestAnnotationsView()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/src/GitHub.VisualStudio.UI/Views/GitHubPane/PullRequestCheckView.xaml b/src/GitHub.VisualStudio.UI/Views/GitHubPane/PullRequestCheckView.xaml
index d04daeeda1..d67839c532 100644
--- a/src/GitHub.VisualStudio.UI/Views/GitHubPane/PullRequestCheckView.xaml
+++ b/src/GitHub.VisualStudio.UI/Views/GitHubPane/PullRequestCheckView.xaml
@@ -24,11 +24,10 @@
-
-
-
+
-
+
+
@@ -38,16 +37,21 @@
-
-
-
-
diff --git a/src/GitHub.VisualStudio.UI/Views/GitHubPane/PullRequestDetailView.xaml b/src/GitHub.VisualStudio.UI/Views/GitHubPane/PullRequestDetailView.xaml
index 50119e213a..cdfdae874e 100644
--- a/src/GitHub.VisualStudio.UI/Views/GitHubPane/PullRequestDetailView.xaml
+++ b/src/GitHub.VisualStudio.UI/Views/GitHubPane/PullRequestDetailView.xaml
@@ -252,7 +252,7 @@
-
+
diff --git a/src/GitHub.VisualStudio.UI/Views/GitHubPane/PullRequestFilesView.xaml b/src/GitHub.VisualStudio.UI/Views/GitHubPane/PullRequestFilesView.xaml
index 23b8e8499b..806e58d2a1 100644
--- a/src/GitHub.VisualStudio.UI/Views/GitHubPane/PullRequestFilesView.xaml
+++ b/src/GitHub.VisualStudio.UI/Views/GitHubPane/PullRequestFilesView.xaml
@@ -6,6 +6,7 @@
xmlns:ghfvs="https://github.com/github/VisualStudio"
xmlns:local="clr-namespace:GitHub.VisualStudio.Views.GitHubPane"
xmlns:imaging="clr-namespace:Microsoft.VisualStudio.Imaging;assembly=Microsoft.VisualStudio.Imaging"
+ xmlns:catalog="clr-namespace:Microsoft.VisualStudio.Imaging;assembly=Microsoft.VisualStudio.ImageCatalog"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"
Name="root">
@@ -98,6 +99,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+