async Task SessionChanged(IPullRequestSession session) { this.session = session; fileSubscription?.Dispose(); if (session == null) { Thread = null; threadSubscription?.Dispose(); return; } var relativePath = session.GetRelativePath(fullPath); file = await session.GetFile(relativePath); fileSubscription = file.WhenAnyValue(x => x.InlineCommentThreads).Subscribe(_ => UpdateThread().Forget()); }
/// <summary> /// Initializes a new instance of the <see cref="PullRequestReviewViewModel"/> class. /// </summary> /// <param name="editorService">The pull request editor service.</param> /// <param name="session">The pull request session.</param> /// <param name="model">The pull request review model.</param> public PullRequestReviewViewModel( IPullRequestEditorService editorService, IPullRequestSession session, PullRequestReviewModel model) { Guard.ArgumentNotNull(editorService, nameof(editorService)); Guard.ArgumentNotNull(session, nameof(session)); Guard.ArgumentNotNull(model, nameof(model)); Model = model; Body = string.IsNullOrWhiteSpace(Model.Body) ? null : Model.Body; StateDisplay = ToString(Model.State); var comments = new List <IPullRequestReviewFileCommentViewModel>(); var outdated = new List <IPullRequestReviewFileCommentViewModel>(); foreach (var comment in model.Comments) { if (comment.Thread != null) { var vm = new PullRequestReviewCommentViewModel( editorService, session, comment.Thread.Path, comment); if (comment.Thread.Position != null) { comments.Add(vm); } else { outdated.Add(vm); } } } FileComments = comments; OutdatedFileComments = outdated; HasDetails = Body != null || FileComments.Count > 0 || OutdatedFileComments.Count > 0; }
async Task StatusChanged() { try { var session = CurrentSession; var pr = await service.GetPullRequestForCurrentBranch(repository).FirstOrDefaultAsync(); if (pr != null) { var changePR = pr.Item1 != (session?.PullRequest.Base.RepositoryCloneUrl.Owner) || pr.Item2 != (session?.PullRequest.Number); if (changePR) { var modelService = await connectionManager.GetModelService(repository, modelServiceFactory); var pullRequest = await modelService?.GetPullRequest(pr.Item1, repository.Name, pr.Item2); if (pullRequest != null) { var newSession = await GetSessionInternal(pullRequest); if (newSession != null) { newSession.IsCheckedOut = true; } session = newSession; } } } else { session = null; } CurrentSession = session; } catch (Exception e) { log.Error(e, "Error changing repository"); } }
public async Task Initialize( IPullRequestSession session, IApiClient apiClient) { Guard.ArgumentNotNull(session, nameof(session)); Guard.ArgumentNotNull(apiClient, nameof(apiClient)); if (this.session != null) { return; } this.session = session; var viewModel = new PullRequestCommentsViewModel(apiClient, session); await viewModel.Initialize(); view.DataContext = viewModel; }
/// <summary> /// Initializes a new instance of the <see cref="PullRequestReviewCommentViewModel"/> class. /// </summary> /// <param name="session">The pull request session.</param> /// <param name="thread">The thread that the comment is a part of.</param> /// <param name="currentUser">The current user.</param> /// <param name="model">The comment model.</param> public PullRequestReviewCommentViewModel( IPullRequestSession session, ICommentThreadViewModel thread, IActorViewModel currentUser, PullRequestReviewModel review, PullRequestReviewCommentModel model) : this( session, thread, currentUser, model.Id, model.Body, CommentEditState.None, new ActorViewModel(model.Author), model.CreatedAt, review.State == PullRequestReviewState.Pending, model.Url != null ? new Uri(model.Url) : null) { }
/// <summary> /// Creates a placeholder comment which can be used to add a new comment to a thread. /// </summary> /// <param name="session">The pull request session.</param> /// <param name="thread">The comment thread.</param> /// <param name="currentUser">The current user.</param> /// <returns>THe placeholder comment.</returns> public static CommentViewModel CreatePlaceholder( IPullRequestSession session, ICommentThreadViewModel thread, IActorViewModel currentUser) { return(new PullRequestReviewCommentViewModel( session, thread, currentUser, 0, null, 0, string.Empty, CommentEditState.Placeholder, currentUser, DateTimeOffset.MinValue, false, null)); }
async Task RefreshCurrentSession(LocalRepositoryModel repository, IPullRequestSession session) { if (repository != null && repository.HasRemotesButNoOrigin) { NoRemoteOriginCallout(); } var showStatus = await IsDotComOrEnterpriseRepository(repository); if (!showStatus) { ShowStatus(null); return; } var viewModel = CreatePullRequestStatusViewModel(repository, session); ShowStatus(viewModel); }
/// <inheritdoc/> public async Task InitializeAsync( IPullRequestSession session, Func <IInlineCommentThreadModel, bool> filter = null) { Guard.ArgumentNotNull(session, nameof(session)); subscriptions?.Dispose(); this.pullRequestSession = session; this.commentFilter = filter; subscriptions = new CompositeDisposable(); subscriptions.Add(session.WhenAnyValue(x => x.IsCheckedOut).Subscribe(isBranchCheckedOut)); var dirs = new Dictionary <string, PullRequestDirectoryNode> { { string.Empty, new PullRequestDirectoryNode(string.Empty) } }; using (var changes = await service.GetTreeChanges(session.LocalRepository, session.PullRequest)) { foreach (var changedFile in session.PullRequest.ChangedFiles) { var node = new PullRequestFileNode( session.LocalRepository.LocalPath, changedFile.FileName, changedFile.Sha, changedFile.Status, GetOldFileName(changedFile, changes)); var file = await session.GetFile(changedFile.FileName); if (file != null) { subscriptions.Add(file.WhenAnyValue(x => x.InlineCommentThreads) .Subscribe(x => node.CommentCount = CountComments(x, filter))); } var dir = GetDirectory(Path.GetDirectoryName(node.RelativePath), dirs); dir.Files.Add(node); } } ChangedFilesCount = session.PullRequest.ChangedFiles.Count; Items = dirs[string.Empty].Children.ToList(); }
public PullRequestCommentsViewModel( IPullRequestSession session) { this.session = session; Repository = session.LocalRepository; Number = session.PullRequest.Number; Title = session.PullRequest.Title; Conversation = new IssueCommentThreadViewModel(Repository, Number, session.User); foreach (var comment in session.PullRequest.Comments) { Conversation.Comments.Add(new CommentViewModel( Conversation, session.User, comment)); } }
async Task RefreshCurrentSession(ILocalRepositoryModel repository, IPullRequestSession session) { try { var showStatus = await IsDotComOrEnterpriseRepository(repository); if (!showStatus) { ShowStatus(null); return; } var viewModel = CreatePullRequestStatusViewModel(session); ShowStatus(viewModel); } catch (Exception e) { log.Error(e, nameof(RefreshCurrentSession)); } }
/// <summary> /// Initializes a new instance of the <see cref="InlineCommentThreadViewModel"/> class. /// </summary> /// <param name="apiClient">The API client to use to post/update comments.</param> /// <param name="session">The current PR review session.</param> public InlineCommentThreadViewModel( IPullRequestSession session, IEnumerable <IPullRequestReviewCommentModel> comments) : base(session.User) { Guard.ArgumentNotNull(session, nameof(session)); Session = session; PostComment = ReactiveCommand.CreateAsyncTask( Observable.Return(true), DoPostComment); foreach (var comment in comments) { Comments.Add(new CommentViewModel(this, CurrentUser, comment)); } Comments.Add(CommentViewModel.CreatePlaceholder(this, CurrentUser)); }
void AddBufferTag( ITextBuffer buffer, IPullRequestSession session, string path, string commitSha, DiffSide?side) { buffer.Properties.GetOrCreateSingletonProperty( typeof(PullRequestTextBufferInfo), () => new PullRequestTextBufferInfo(session, path, commitSha, side)); var projection = buffer as IProjectionBuffer; if (projection != null) { foreach (var source in projection.SourceBuffers) { AddBufferTag(source, session, path, commitSha, side); } } }
/// <summary> /// Initializes a new instance of the <see cref="PullRequestReviewCommentViewModel"/> class. /// </summary> /// <param name="session">The pull request session.</param> /// <param name="commentService">The comment service</param> /// <param name="thread">The thread that the comment is a part of.</param> /// <param name="currentUser">The current user.</param> /// <param name="pullRequestId">The pull request id of the comment.</param> /// <param name="commentId">The GraphQL ID of the comment.</param> /// <param name="databaseId">The database id of the comment.</param> /// <param name="body">The comment body.</param> /// <param name="state">The comment edit state.</param> /// <param name="author">The author of the comment.</param> /// <param name="updatedAt">The modified date of the comment.</param> /// <param name="isPending">Whether this is a pending comment.</param> /// <param name="webUrl"></param> public PullRequestReviewCommentViewModel( IPullRequestSession session, ICommentService commentService, ICommentThreadViewModel thread, IActorViewModel currentUser, int pullRequestId, string commentId, int databaseId, string body, CommentEditState state, IActorViewModel author, DateTimeOffset updatedAt, bool isPending, Uri webUrl) : base(commentService, thread, currentUser, pullRequestId, commentId, databaseId, body, state, author, updatedAt, webUrl) { Guard.ArgumentNotNull(session, nameof(session)); this.session = session; IsPending = isPending; var pendingReviewAndIdObservable = Observable.CombineLatest( session.WhenAnyValue(x => x.HasPendingReview, x => !x), this.WhenAnyValue(model => model.Id).Select(i => i == null), (hasPendingReview, isNewComment) => new { hasPendingReview, isNewComment }); canStartReview = pendingReviewAndIdObservable .Select(arg => arg.hasPendingReview && arg.isNewComment) .ToProperty(this, x => x.CanStartReview); commitCaption = pendingReviewAndIdObservable .Select(arg => !arg.isNewComment ? Resources.UpdateComment : arg.hasPendingReview ? Resources.AddSingleComment : Resources.AddReviewComment) .ToProperty(this, x => x.CommitCaption); StartReview = ReactiveCommand.CreateAsyncTask( CommitEdit.CanExecuteObservable, DoStartReview); AddErrorHandler(StartReview); }
async Task <PullRequestReviewCommentThreadViewModel> CreateTarget( IViewViewModelFactory factory = null, IPullRequestSession session = null, IPullRequestSessionFile file = null, PullRequestReviewModel review = null, IEnumerable <InlineCommentModel> comments = null) { factory = factory ?? CreateFactory(); session = session ?? CreateSession(); file = file ?? Substitute.For <IPullRequestSessionFile>(); review = review ?? new PullRequestReviewModel(); comments = comments ?? CreateComments(); var thread = Substitute.For <IInlineCommentThreadModel>(); thread.Comments.Returns(comments.ToList()); var result = new PullRequestReviewCommentThreadViewModel(factory); await result.InitializeAsync(session, file, review, thread, true); return(result); }
static async Task <PullRequestReviewCommentViewModel> CreateTarget( IPullRequestSession session = null, ICommentService commentService = null, ICommentThreadViewModel thread = null, ActorModel currentUser = null, PullRequestReviewModel review = null, PullRequestReviewCommentModel comment = null) { session = session ?? CreateSession(); commentService = commentService ?? Substitute.For <ICommentService>(); thread = thread ?? CreateThread(); currentUser = currentUser ?? new ActorModel { Login = "******" }; comment = comment ?? new PullRequestReviewCommentModel(); review = review ?? CreateReview(PullRequestReviewState.Approved, comment); var result = new PullRequestReviewCommentViewModel(commentService); await result.InitializeAsync(session, thread, review, comment, CommentEditState.None); return(result); }
static PullRequestReviewCommentViewModel CreateTarget( IPullRequestSession session = null, ICommentThreadViewModel thread = null, ActorModel currentUser = null, PullRequestReviewModel review = null, PullRequestReviewCommentModel comment = null) { session = session ?? CreateSession(); thread = thread ?? CreateThread(); currentUser = currentUser ?? new ActorModel { Login = "******" }; comment = comment ?? new PullRequestReviewCommentModel(); review = review ?? CreateReview(comment); return(new PullRequestReviewCommentViewModel( session, thread, new ActorViewModel(currentUser), review, comment)); }
void Initialize() { document = buffer.Properties.GetProperty <ITextDocument>(typeof(ITextDocument)); if (document == null) { return; } var bufferInfo = sessionManager.GetTextBufferInfo(buffer); IPullRequestSession session = null; if (bufferInfo != null) { fullPath = bufferInfo.FilePath; leftHandSide = bufferInfo.IsLeftComparisonBuffer; if (!bufferInfo.Session.IsCheckedOut) { session = bufferInfo.Session; } } else { fullPath = document.FilePath; } if (session == null) { managerSubscription = sessionManager.WhenAnyValue(x => x.CurrentSession).Subscribe(SessionChanged); } else { SessionChanged(session); } initialized = true; }
void Initialize() { document = TryGetDocument(buffer); if (document == null) { return; } var bufferInfo = sessionManager.GetTextBufferInfo(buffer); IPullRequestSession session = null; if (bufferInfo != null) { fullPath = bufferInfo.FilePath; leftHandSide = bufferInfo.IsLeftComparisonBuffer; if (!bufferInfo.Session.IsCheckedOut) { session = bufferInfo.Session; } } else { fullPath = document.FilePath; } if (session == null) { managerSubscription = sessionManager.WhenAnyValue(x => x.CurrentSession).Subscribe(x => ForgetWithLogging(SessionChanged(x))); } else { ForgetWithLogging(SessionChanged(session)); } initialized = true; }
IDifferenceViewer FocusExistingDiffViewer( IPullRequestSession session, string mergeBase, string rightPath) { IVsUIHierarchy uiHierarchy; uint itemID; IVsWindowFrame windowFrame; // Diff documents are indexed by the path on the right hand side of the comparison. if (VsShellUtilities.IsDocumentOpen( serviceProvider, rightPath, Guid.Empty, out uiHierarchy, out itemID, out windowFrame)) { var diffViewer = GetDiffViewer(windowFrame); if (diffViewer != null) { PullRequestTextBufferInfo leftBufferInfo; if (diffViewer.LeftView.TextBuffer.Properties.TryGetProperty( typeof(PullRequestTextBufferInfo), out leftBufferInfo) && leftBufferInfo.Session.PullRequest.Number == session.PullRequest.Number && leftBufferInfo.CommitSha == mergeBase) { ErrorHandler.ThrowOnFailure(windowFrame.Show()); return(diffViewer); } } } return(null); }
/// <inheritdoc/> public async Task <IDifferenceViewer> OpenDiff( IPullRequestSession session, string relativePath, IInlineCommentThreadModel thread) { Guard.ArgumentNotNull(session, nameof(session)); Guard.ArgumentNotEmptyString(relativePath, nameof(relativePath)); Guard.ArgumentNotNull(thread, nameof(thread)); var diffViewer = await OpenDiff(session, relativePath, thread.CommitSha, scrollToFirstDiff : false); var param = (object)new InlineCommentNavigationParams { FromLine = thread.LineNumber - 1, }; // HACK: We need to wait here for the inline comment tags to initialize so we can find the next inline comment. // There must be a better way of doing this. await Task.Delay(1500); RaiseWhenAvailable(Guids.CommandSetString, PkgCmdIDList.NextInlineCommentId, param); return(diffViewer); }
static void RaisePullRequestChanged(IPullRequestSession session, PullRequestDetailModel newPullRequest) { ((ISubject <PullRequestDetailModel>)session.PullRequestChanged).OnNext(newPullRequest); }
/// <inheritdoc/> public async Task <ITextView> OpenFile( IPullRequestSession session, string relativePath, bool workingDirectory) { Guard.ArgumentNotNull(session, nameof(session)); Guard.ArgumentNotEmptyString(relativePath, nameof(relativePath)); try { var fullPath = GetAbsolutePath(session.LocalRepository, relativePath); string fileName; string commitSha; if (workingDirectory) { fileName = fullPath; commitSha = null; } else { var file = await session.GetFile(relativePath); fileName = await pullRequestService.ExtractToTempFile( session.LocalRepository, session.PullRequest, file.RelativePath, file.CommitSha, pullRequestService.GetEncoding(session.LocalRepository, file.RelativePath)); commitSha = file.CommitSha; } IVsTextView textView; IWpfTextView wpfTextView; using (workingDirectory ? null : OpenInProvisionalTab()) { var readOnly = !workingDirectory; textView = OpenDocument(fileName, readOnly, out wpfTextView); if (!workingDirectory) { AddBufferTag(wpfTextView.TextBuffer, session, fullPath, commitSha, null); EnableNavigateToEditor(textView, session); } } if (workingDirectory) { await usageTracker.IncrementCounter(x => x.NumberOfPRDetailsOpenFileInSolution); } else { await usageTracker.IncrementCounter(x => x.NumberOfPRDetailsViewFile); } return(wpfTextView); } catch (Exception e) { ShowErrorInStatusBar("Error opening file", e); return(null); } }
void EnableNavigateToEditor(ITextView textView, IPullRequestSession session) { var vsTextView = vsEditorAdaptersFactory.GetViewAdapter(textView); EnableNavigateToEditor(vsTextView, session); }
/// <summary> /// Gets the head (target) branch label for a pull request, stripping the owner if the pull /// request is not from a fork. /// </summary> /// <param name="session">The pull request session.</param> /// <returns>The head branch label</returns> public static string GetBaseBranchDisplay(this IPullRequestSession session) { Guard.ArgumentNotNull(session, nameof(session)); return(GetBranchDisplay(session.IsPullRequestFromFork(), session.PullRequest?.Base?.Label)); }
/// <inheritdoc/> public async Task <IDifferenceViewer> OpenDiff(IPullRequestSession session, string relativePath, string headSha, bool scrollToFirstDiff) { Guard.ArgumentNotNull(session, nameof(session)); Guard.ArgumentNotEmptyString(relativePath, nameof(relativePath)); try { var workingDirectory = headSha == null; var file = await session.GetFile(relativePath, headSha ?? "HEAD"); var mergeBase = await pullRequestService.GetMergeBase(session.LocalRepository, session.PullRequest); var encoding = pullRequestService.GetEncoding(session.LocalRepository, file.RelativePath); var rightFile = workingDirectory ? Path.Combine(session.LocalRepository.LocalPath, relativePath) : await pullRequestService.ExtractToTempFile( session.LocalRepository, session.PullRequest, relativePath, file.CommitSha, encoding); var diffViewer = FocusExistingDiffViewer(session, mergeBase, rightFile); if (diffViewer != null) { return(diffViewer); } var leftFile = await pullRequestService.ExtractToTempFile( session.LocalRepository, session.PullRequest, relativePath, mergeBase, encoding); var leftPath = await GetBaseFileName(session, file); var rightPath = file.RelativePath; var leftLabel = $"{leftPath};{session.GetBaseBranchDisplay()}"; var rightLabel = workingDirectory ? rightPath : $"{rightPath};PR {session.PullRequest.Number}"; var caption = $"Diff - {Path.GetFileName(file.RelativePath)}"; var options = __VSDIFFSERVICEOPTIONS.VSDIFFOPT_DetectBinaryFiles | __VSDIFFSERVICEOPTIONS.VSDIFFOPT_LeftFileIsTemporary; if (!workingDirectory) { options |= __VSDIFFSERVICEOPTIONS.VSDIFFOPT_RightFileIsTemporary; } IVsWindowFrame frame; using (OpenWithOption(DifferenceViewerOptions.ScrollToFirstDiffName, scrollToFirstDiff)) using (OpenInProvisionalTab()) { var tooltip = $"{leftLabel}\nvs.\n{rightLabel}"; // Diff window will open in provisional (right hand) tab until document is touched. frame = VisualStudio.Services.DifferenceService.OpenComparisonWindow2( leftFile, rightFile, caption, tooltip, leftLabel, rightLabel, string.Empty, string.Empty, (uint)options); } diffViewer = GetDiffViewer(frame); var leftText = diffViewer.LeftView.TextBuffer.CurrentSnapshot.GetText(); var rightText = diffViewer.RightView.TextBuffer.CurrentSnapshot.GetText(); if (leftText == string.Empty) { // Don't show LeftView when empty. diffViewer.ViewMode = DifferenceViewMode.RightViewOnly; } else if (rightText == string.Empty) { // Don't show RightView when empty. diffViewer.ViewMode = DifferenceViewMode.LeftViewOnly; } else if (leftText == rightText) { // Don't show LeftView when no changes. diffViewer.ViewMode = DifferenceViewMode.RightViewOnly; } AddBufferTag(diffViewer.LeftView.TextBuffer, session, leftPath, mergeBase, DiffSide.Left); if (!workingDirectory) { AddBufferTag(diffViewer.RightView.TextBuffer, session, rightPath, file.CommitSha, DiffSide.Right); EnableNavigateToEditor(diffViewer.LeftView, session); EnableNavigateToEditor(diffViewer.RightView, session); EnableNavigateToEditor(diffViewer.InlineView, session); } if (workingDirectory) { await usageTracker.IncrementCounter(x => x.NumberOfPRDetailsCompareWithSolution); } else { await usageTracker.IncrementCounter(x => x.NumberOfPRDetailsViewChanges); } return(diffViewer); } catch (Exception e) { ShowErrorInStatusBar("Error opening file", e); return(null); } }
/// <summary> /// Initializes a new instance of the <see cref="ShowInlineCommentTag"/> class. /// </summary> /// <param name="session">The pull request session.</param> /// <param name="lineNumber">0-based index of the inline tag</param> /// <param name="diffLineType">The diff type for the inline comment</param> public ShowInlineCommentTag(IPullRequestSession session, int lineNumber, DiffChangeType diffLineType) : base(session, lineNumber, diffLineType) { }
void AddCompareBufferTag(ITextBuffer buffer, IPullRequestSession session, string path, bool isLeftBuffer) { buffer.Properties.GetOrCreateSingletonProperty( typeof(PullRequestTextBufferInfo), () => new PullRequestTextBufferInfo(session, path, isLeftBuffer)); }
/// <inheritdoc/> public async Task OpenFile( IPullRequestSession session, string relativePath, bool workingDirectory) { Guard.ArgumentNotNull(session, nameof(session)); Guard.ArgumentNotEmptyString(relativePath, nameof(relativePath)); try { var fullPath = Path.Combine(session.LocalRepository.LocalPath, relativePath); string fileName; string commitSha; if (workingDirectory) { fileName = fullPath; commitSha = null; } else { var file = await session.GetFile(relativePath); fileName = await pullRequestService.ExtractToTempFile( session.LocalRepository, session.PullRequest, file.RelativePath, file.CommitSha, pullRequestService.GetEncoding(session.LocalRepository, file.RelativePath)); commitSha = file.CommitSha; } using (workingDirectory ? null : OpenInProvisionalTab()) { var window = VisualStudio.Services.Dte.ItemOperations.OpenFile(fileName); window.Document.ReadOnly = !workingDirectory; var buffer = GetBufferAt(fileName); if (!workingDirectory) { AddBufferTag(buffer, session, fullPath, commitSha, null); var textView = FindActiveView(); var file = await session.GetFile(relativePath); EnableNavigateToEditor(textView, session, file); } } if (workingDirectory) { await usageTracker.IncrementCounter(x => x.NumberOfPRDetailsOpenFileInSolution); } else { await usageTracker.IncrementCounter(x => x.NumberOfPRDetailsViewFile); } } catch (Exception e) { ShowErrorInStatusBar("Error opening file", e); } }
public Task InitializeAsync( IPullRequestSession session, Func<IInlineCommentThreadModel, bool> commentFilter = null) { return Task.CompletedTask; }
/// <inheritdoc/> public async Task <IDifferenceViewer> OpenDiff(IPullRequestSession session, string relativePath, string headSha, bool scrollToFirstDraftOrDiff) { Guard.ArgumentNotNull(session, nameof(session)); Guard.ArgumentNotEmptyString(relativePath, nameof(relativePath)); try { var workingDirectory = headSha == null; var file = await session.GetFile(relativePath, headSha ?? "HEAD"); var mergeBase = await pullRequestService.GetMergeBase(session.LocalRepository, session.PullRequest); var encoding = pullRequestService.GetEncoding(session.LocalRepository, file.RelativePath); var rightFile = workingDirectory ? Path.Combine(session.LocalRepository.LocalPath, relativePath) : await pullRequestService.ExtractToTempFile( session.LocalRepository, session.PullRequest, relativePath, file.CommitSha, encoding); var diffViewer = FocusExistingDiffViewer(session, mergeBase, rightFile); if (diffViewer != null) { return(diffViewer); } var leftFile = await pullRequestService.ExtractToTempFile( session.LocalRepository, session.PullRequest, relativePath, mergeBase, encoding); var leftPath = await GetBaseFileName(session, file); var rightPath = file.RelativePath; var leftLabel = $"{leftPath};{session.GetBaseBranchDisplay()}"; var rightLabel = workingDirectory ? rightPath : $"{rightPath};PR {session.PullRequest.Number}"; var caption = $"Diff - {Path.GetFileName(file.RelativePath)}"; var options = __VSDIFFSERVICEOPTIONS.VSDIFFOPT_DetectBinaryFiles | __VSDIFFSERVICEOPTIONS.VSDIFFOPT_LeftFileIsTemporary; var openThread = (line : -1, side : DiffSide.Left); var scrollToFirstDiff = false; if (!workingDirectory) { options |= __VSDIFFSERVICEOPTIONS.VSDIFFOPT_RightFileIsTemporary; } if (scrollToFirstDraftOrDiff) { var(key, _) = PullRequestReviewCommentThreadViewModel.GetDraftKeys( session.LocalRepository.CloneUrl.WithOwner(session.RepositoryOwner), session.PullRequest.Number, relativePath, 0); var drafts = (await draftStore.GetDrafts <PullRequestReviewCommentDraft>(key) .ConfigureAwait(true)) .OrderByDescending(x => x.data.UpdatedAt) .ToList(); if (drafts.Count > 0 && int.TryParse(drafts[0].secondaryKey, out var line)) { openThread = (line, drafts[0].data.Side);