File: LineCommit\CommitViewManager.vb
Web Access
Project: src\src\EditorFeatures\VisualBasic\Microsoft.CodeAnalysis.VisualBasic.EditorFeatures.vbproj (Microsoft.CodeAnalysis.VisualBasic.EditorFeatures)
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
 
Imports System.Threading
Imports Microsoft.VisualStudio.Text
Imports Microsoft.VisualStudio.Text.Editor
Imports Microsoft.VisualStudio.Text.Operations
Imports Microsoft.VisualStudio.Utilities
 
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit
    ''' <summary>
    ''' This class watches for view-based events in relation to a specific subject buffer and passes along commit operations.
    ''' </summary>
    Friend Class CommitViewManager
        Private ReadOnly _view As ITextView
        Private ReadOnly _commitBufferManagerFactory As CommitBufferManagerFactory
        Private ReadOnly _textBufferAssociatedViewService As ITextBufferAssociatedViewService
        Private ReadOnly _textUndoHistoryRegistry As ITextUndoHistoryRegistry
        Private ReadOnly _uiThreadOperationExecutor As IUIThreadOperationExecutor
 
        Public Sub New(view As ITextView,
                       commitBufferManagerFactory As CommitBufferManagerFactory,
                       textBufferAssociatedViewService As ITextBufferAssociatedViewService,
                       textUndoHistoryRegistry As ITextUndoHistoryRegistry,
                       uiThreadOperationExecutor As IUIThreadOperationExecutor)
            _view = view
            _commitBufferManagerFactory = commitBufferManagerFactory
            _textBufferAssociatedViewService = textBufferAssociatedViewService
            _textUndoHistoryRegistry = textUndoHistoryRegistry
            _uiThreadOperationExecutor = uiThreadOperationExecutor
 
            AddHandler _view.Caret.PositionChanged, AddressOf OnCaretPositionChanged
            AddHandler _view.LostAggregateFocus, AddressOf OnLostAggregateFocus
            AddHandler _view.Closed, AddressOf OnViewClosed
        End Sub
 
        Public Sub Disconnect()
            RemoveHandler _view.Caret.PositionChanged, AddressOf OnCaretPositionChanged
            RemoveHandler _view.LostAggregateFocus, AddressOf OnLostAggregateFocus
            RemoveHandler _view.Closed, AddressOf OnViewClosed
        End Sub
 
        Private Sub OnCaretPositionChanged(sender As Object, e As CaretPositionChangedEventArgs)
            Dim oldSnapshotPoint = MapDownToPoint(e.OldPosition)
            Dim newSnapshotPoint = MapDownToPoint(e.NewPosition)
 
            If Not oldSnapshotPoint.HasValue Then
                Return
            End If
 
            Dim oldBuffer = oldSnapshotPoint.Value.Snapshot.TextBuffer
            Dim newBuffer = If(newSnapshotPoint.HasValue, newSnapshotPoint.Value.Snapshot.TextBuffer, Nothing)
 
            _uiThreadOperationExecutor.Execute(
                VBEditorResources.Line_commit,
                VBEditorResources.Committing_line,
                allowCancellation:=True,
                showProgress:=False,
                action:=
                Sub(context)
                    ' If our buffers changed, then we commit the old one
                    If oldBuffer IsNot newBuffer Then
                        CommitBufferForCaretMovement(oldBuffer, e, context.UserCancellationToken)
                        Return
                    End If
 
                    ' We're in the same snapshot. Are we on the same line?
                    Dim commitBufferManager = _commitBufferManagerFactory.CreateForBuffer(newBuffer)
                    If CommitBufferManager.IsMovementBetweenStatements(oldSnapshotPoint.Value, newSnapshotPoint.Value, context.UserCancellationToken) Then
                        CommitBufferForCaretMovement(oldBuffer, e, context.UserCancellationToken)
                    End If
                End Sub)
        End Sub
 
        Private Sub CommitBufferForCaretMovement(buffer As ITextBuffer,
                                                 e As CaretPositionChangedEventArgs,
                                                 cancellationToken As CancellationToken)
            Dim commitBufferManager = _commitBufferManagerFactory.CreateForBuffer(buffer)
 
            If commitBufferManager.HasDirtyRegion Then
                ' In projection buffer scenarios, the text undo history is associated with the surface buffer, so the
                ' following line's usage of the surface buffer is correct
                Using transaction = _textUndoHistoryRegistry.GetHistory(_view.TextBuffer).CreateTransaction(VBEditorResources.Visual_Basic_Pretty_List)
                    Dim beforeUndoPrimitive As New BeforeCommitCaretMoveUndoPrimitive(buffer, _textBufferAssociatedViewService, e.OldPosition)
                    transaction.AddUndo(beforeUndoPrimitive)
 
                    Dim beforeCommitVersion = buffer.CurrentSnapshot.Version
                    Dim subjectBufferCaretPosition = MapDownToPoint(e.OldPosition)
                    commitBufferManager.CommitDirty(isExplicitFormat:=False, cancellationToken:=cancellationToken)
 
                    If buffer.CurrentSnapshot.Version Is beforeCommitVersion Then
                        transaction.Cancel()
                        Return
                    End If
 
                    beforeUndoPrimitive.MarkAsActive()
                    transaction.AddUndo(New AfterCommitCaretMoveUndoPrimitive(buffer, _textBufferAssociatedViewService, _view.Caret.Position))
                    transaction.Complete()
                End Using
            End If
        End Sub
 
        Private Function MapDownToPoint(caretPosition As CaretPosition) As SnapshotPoint?
            Return _view.BufferGraph.MapDownToFirstMatch(caretPosition.BufferPosition,
                                                         PointTrackingMode.Positive,
                                                         Function(b) b.ContentType.IsOfType(ContentTypeNames.VisualBasicContentType),
                                                         caretPosition.Affinity)
        End Function
 
        Private Sub OnViewClosed(sender As Object, e As EventArgs)
            Disconnect()
        End Sub
 
        Private Sub OnLostAggregateFocus(sender As Object, e As EventArgs)
            _uiThreadOperationExecutor.Execute(
                "Commit",
                VBEditorResources.Committing_line,
                allowCancellation:=True,
                showProgress:=False,
                action:=
                Sub(context)
                    For Each buffer In _view.BufferGraph.GetTextBuffers(Function(b) b.ContentType.IsOfType(ContentTypeNames.VisualBasicContentType))
                        _commitBufferManagerFactory.CreateForBuffer(buffer).CommitDirty(isExplicitFormat:=False, cancellationToken:=context.UserCancellationToken)
                    Next
                End Sub)
        End Sub
    End Class
End Namespace