Esempio n. 1
0
        async Task Initialize()
        {
            needsInitialize = false;

            var bufferInfo = sessionManager.GetTextBufferInfo(buffer);

            if (bufferInfo != null)
            {
                session      = bufferInfo.Session;
                relativePath = bufferInfo.RelativePath;
                file         = await session.GetFile(relativePath);

                fileSubscription = file.LinesChanged.Subscribe(LinesChanged);
                side             = bufferInfo.Side ?? DiffSide.Right;
                NotifyTagsChanged();
            }
            else
            {
                await InitializeLiveFile();

                sessionManagerSubscription = sessionManager
                                             .WhenAnyValue(x => x.CurrentSession)
                                             .Skip(1)
                                             .Subscribe(_ => ForgetWithLogging(InitializeLiveFile()));
            }
        }
        async Task Initialize()
        {
            needsInitialize = false;

            var bufferInfo = sessionManager.GetTextBufferInfo(buffer);

            if (bufferInfo != null)
            {
                session      = bufferInfo.Session;
                relativePath = bufferInfo.RelativePath;
                file         = await session.GetFile(relativePath);

                leftHandSide = bufferInfo.IsLeftComparisonBuffer;
                NotifyTagsChanged();
            }
            else
            {
                await InitializeLiveFile();

                sessionManagerSubscription = sessionManager
                                             .WhenAnyValue(x => x.CurrentSession)
                                             .Skip(1)
                                             .Subscribe(_ => ForgetWithLogging(InitializeLiveFile()));
            }
        }
        /// <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)));

                        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);
                    dir.Files.Add(node);
                }
            }

            ChangedFilesCount = session.PullRequest.ChangedFiles.Count;
            Items             = dirs[string.Empty].Children.ToList();
        }
        async Task SessionChanged(IPullRequestSession session)
        {
            sessionSubscription?.Dispose();
            this.session = session;

            if (file != null)
            {
                file = null;
                NotifyTagsChanged();
            }

            if (session == null)
            {
                return;
            }

            relativePath = session.GetRelativePath(fullPath);

            if (relativePath == null)
            {
                return;
            }

            var snapshot = buffer.CurrentSnapshot;

            if (leftHandSide)
            {
                // If we're tagging the LHS of a diff, then the snapshot will be the base commit
                // (as you'd expect) but that means that the diff will be empty, so get the RHS
                // snapshot from the view for the comparison.
                var projection = view.TextSnapshot as IProjectionSnapshot;
                snapshot = projection?.SourceSnapshots.Count == 2 ? projection.SourceSnapshots[1] : null;
            }

            if (snapshot == null)
            {
                return;
            }

            var repository      = gitService.GetRepository(session.LocalRepository.LocalPath);
            var isContentSource = !leftHandSide && !(buffer is IProjectionBuffer);

            file = await session.GetFile(relativePath, isContentSource?this : null);

            if (file == null)
            {
                return;
            }

            sessionSubscription = file.WhenAnyValue(x => x.InlineCommentThreads)
                                  .Subscribe(_ => NotifyTagsChanged());

            NotifyTagsChanged();
        }
Esempio n. 5
0
        async Task <IInlineCommentThreadModel> GetFirstCommentThread(IPullRequestFileNode file)
        {
            var sessionFile = await pullRequestSession.GetFile(file.RelativePath);

            var threads = sessionFile.InlineCommentThreads.AsEnumerable();

            if (commentFilter != null)
            {
                threads = threads.Where(commentFilter);
            }

            return(threads.FirstOrDefault());
        }
        async Task DoOpen()
        {
            try
            {
                if (thread == null)
                {
                    if (model.Thread.IsOutdated)
                    {
                        var file = await session.GetFile(RelativePath, model.Thread.OriginalCommitSha);

                        thread = file.InlineCommentThreads.FirstOrDefault(t => t.Comments.Any(c => c.Comment.Id == model.Id));
                    }
                    else
                    {
                        var file = await session.GetFile(RelativePath, model.Thread.CommitSha);

                        thread = file.InlineCommentThreads.FirstOrDefault(t => t.Comments.Any(c => c.Comment.Id == model.Id));

                        if (thread?.LineNumber == -1)
                        {
                            log.Warning("Couldn't find line number for comment on {RelativePath} @ {CommitSha}", RelativePath, model.Thread.CommitSha);
                            // Fall back to opening outdated file if we can't find a line number for the comment
                            file = await session.GetFile(RelativePath, model.Thread.OriginalCommitSha);

                            thread = file.InlineCommentThreads.FirstOrDefault(t => t.Comments.Any(c => c.Comment.Id == model.Id));
                        }
                    }
                }

                if (thread != null && thread.LineNumber != -1)
                {
                    await editorService.OpenDiff(session, RelativePath, thread);
                }
            }
            catch (Exception e)
            {
                log.Error(e, nameof(DoOpen));
            }
        }
Esempio n. 7
0
        IPullRequestSessionManager CreateSessionManager(
            string commitSha            = "COMMIT",
            string relativePath         = RelativePath,
            IPullRequestSession session = null,
            PullRequestTextBufferInfo textBufferInfo = null)
        {
            var thread = CreateThread(10, "Existing comment");

            var diff = new DiffChunk
            {
                DiffLine      = 10,
                OldLineNumber = 1,
                NewLineNumber = 1,
            };

            for (var i = 0; i < 10; ++i)
            {
                diff.Lines.Add(new DiffLine
                {
                    NewLineNumber  = i,
                    DiffLineNumber = i + 10,
                    Type           = i < 5 ? DiffChangeType.Delete : DiffChangeType.Add,
                });
            }

            var file = Substitute.For <IPullRequestSessionFile>();

            file.CommitSha.Returns(commitSha);
            file.Diff.Returns(new[] { diff });
            file.InlineCommentThreads.Returns(new[] { thread });
            file.LinesChanged.Returns(new Subject <IReadOnlyList <Tuple <int, DiffSide> > >());

            session = session ?? CreateSession();

            if (textBufferInfo != null)
            {
                session.GetFile(textBufferInfo.RelativePath, textBufferInfo.CommitSha).Returns(file);
            }

            var result = Substitute.For <IPullRequestSessionManager>();

            result.CurrentSession.Returns(session);
            result.GetLiveFile(relativePath, Arg.Any <ITextView>(), Arg.Any <ITextBuffer>()).Returns(file);
            result.GetRelativePath(Arg.Any <ITextBuffer>()).Returns(relativePath);
            result.GetTextBufferInfo(Arg.Any <ITextBuffer>()).Returns(textBufferInfo);

            return(result);
        }
        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());
        }
        async Task DoOpen()
        {
            try
            {
                if (thread == null)
                {
                    var file = await session.GetFile(RelativePath, model.Thread.CommitSha);

                    thread = file.InlineCommentThreads.FirstOrDefault(t => t.Comments.Any(c => c.Comment.Id == model.Id));
                }

                if (thread != null && thread.LineNumber != -1)
                {
                    await editorService.OpenDiff(session, RelativePath, thread);
                }
            }
            catch (Exception)
            {
                // TODO: Show error.
            }
        }
Esempio n. 10
0
        async Task DoOpen(object o)
        {
            try
            {
                if (thread == null)
                {
                    var commit = model.Position.HasValue ? model.CommitId : model.OriginalCommitId;
                    var file   = await session.GetFile(RelativePath, commit);

                    thread = file.InlineCommentThreads.FirstOrDefault(t => t.Comments.Any(c => c.Id == model.Id));
                }

                if (thread != null && thread.LineNumber != -1)
                {
                    await editorService.OpenDiff(session, RelativePath, thread);
                }
            }
            catch (Exception)
            {
                // TODO: Show error.
            }
        }
Esempio n. 11
0
        /// <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);
            }
        }
Esempio n. 12
0
        /// <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);
            }
        }
        /// <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);
        /// <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);
            }
        }