/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/EditorFeatures/VisualBasic/LineCommit/CommitCommandHandler.vb
288 строк
16 KB
Cyrus Najmabadi
remove dupe string
29 ноя 2024, 23:08
29 ноя 2024, 23:08
b26e15c
Код
Авторство
О чём код?
' 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.ComponentModel.Composition Imports System.Diagnostics.CodeAnalysis Imports System.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Editor.Shared.Utilities Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Formatting.Rules Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.Text.Shared.Extensions Imports Microsoft.VisualStudio.Commanding Imports Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion Imports Microsoft.VisualStudio.Text Imports Microsoft.VisualStudio.Text.Editor Imports Microsoft.VisualStudio.Text.Editor.Commanding.Commands Imports Microsoft.VisualStudio.Text.Operations Imports Microsoft.VisualStudio.Utilities Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit ''' <summary> ''' Watches for the enter key being pressed, and triggers a commit in response. ''' </summary> ''' <remarks>This particular command filter acts as a "wrapper" around any other command, as it ''' wishes to invoke the commit after whatever processed the enter is done doing what it's ''' doing.</remarks> <Export(GetType(ICommandHandler))> <ContentType(ContentTypeNames.VisualBasicContentType)> <Name(PredefinedCommandHandlerNames.Commit)> <Order(Before:=PredefinedCommandHandlerNames.EndConstruct)> <Order(Before:=PredefinedCompletionNames.CompletionCommandHandler)> Friend Class CommitCommandHandler Implements IChainedCommandHandler(Of ReturnKeyCommandArgs) Implements IChainedCommandHandler(Of PasteCommandArgs) Implements IChainedCommandHandler(Of SaveCommandArgs) Implements IChainedCommandHandler(Of FormatDocumentCommandArgs) Implements IChainedCommandHandler(Of FormatSelectionCommandArgs) Private ReadOnly _bufferManagerFactory As CommitBufferManagerFactory Private ReadOnly _editorOperationsFactoryService As IEditorOperationsFactoryService Private ReadOnly _smartIndentationService As ISmartIndentationService Private ReadOnly _textUndoHistoryRegistry As ITextUndoHistoryRegistry Private ReadOnly _globalOptions As IGlobalOptionService Public ReadOnly Property DisplayName As String Implements INamed.DisplayName Get Return VBEditorResources.Line_commit End Get End Property <ImportingConstructor()> <SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification:="Used in test code: https://github.com/dotnet/roslyn/issues/42814")> Public Sub New( bufferManagerFactory As CommitBufferManagerFactory, editorOperationsFactoryService As IEditorOperationsFactoryService, smartIndentationService As ISmartIndentationService, textUndoHistoryRegistry As ITextUndoHistoryRegistry, globalOptions As IGlobalOptionService) _bufferManagerFactory = bufferManagerFactory _editorOperationsFactoryService = editorOperationsFactoryService _smartIndentationService = smartIndentationService _textUndoHistoryRegistry = textUndoHistoryRegistry _globalOptions = globalOptions End Sub Public Sub ExecuteCommand(args As FormatDocumentCommandArgs, nextHandler As Action, context As CommandExecutionContext) Implements IChainedCommandHandler(Of FormatDocumentCommandArgs).ExecuteCommand If Not args.SubjectBuffer.CanApplyChangeDocumentToWorkspace() Then nextHandler() Return End If Using context.OperationContext.AddScope(allowCancellation:=True, VBEditorResources.Formatting_Document) Dim buffer = args.SubjectBuffer Dim snapshot = buffer.CurrentSnapshot Dim wholeFile = snapshot.GetFullSpan() Dim commitBufferManager = _bufferManagerFactory.CreateForBuffer(buffer) commitBufferManager.ExpandDirtyRegion(wholeFile) commitBufferManager.CommitDirty(isExplicitFormat:=True, cancellationToken:=context.OperationContext.UserCancellationToken) End Using End Sub Public Function GetCommandState(args As FormatDocumentCommandArgs, nextHandler As Func(Of CommandState)) As CommandState Implements IChainedCommandHandler(Of FormatDocumentCommandArgs).GetCommandState Return nextHandler() End Function Public Sub ExecuteCommand(args As FormatSelectionCommandArgs, nextHandler As Action, context As CommandExecutionContext) Implements IChainedCommandHandler(Of FormatSelectionCommandArgs).ExecuteCommand If Not args.SubjectBuffer.CanApplyChangeDocumentToWorkspace() Then nextHandler() Return End If Using context.OperationContext.AddScope(allowCancellation:=True, VBEditorResources.Formatting_Document) Dim buffer = args.SubjectBuffer Dim selections = args.TextView.Selection.GetSnapshotSpansOnBuffer(buffer) If selections.Count < 1 Then nextHandler() Return End If Dim snapshot = buffer.CurrentSnapshot For index As Integer = 0 To selections.Count - 1 Dim textspan = CommonFormattingHelpers.GetFormattingSpan(snapshot, selections(0)) Dim selectedSpan = New SnapshotSpan(snapshot, textspan.Start, textspan.Length) Dim commitBufferManager = _bufferManagerFactory.CreateForBuffer(buffer) commitBufferManager.ExpandDirtyRegion(selectedSpan) commitBufferManager.CommitDirty(isExplicitFormat:=True, cancellationToken:=context.OperationContext.UserCancellationToken) Next End Using 'We don't call nextHandler, since we have handled this command. End Sub Public Function GetCommandState(args As FormatSelectionCommandArgs, nextHandler As Func(Of CommandState)) As CommandState Implements IChainedCommandHandler(Of FormatSelectionCommandArgs).GetCommandState Return nextHandler() End Function Public Sub ExecuteCommand(args As ReturnKeyCommandArgs, nextHandler As Action, context As CommandExecutionContext) Implements IChainedCommandHandler(Of ReturnKeyCommandArgs).ExecuteCommand If Not _globalOptions.GetOption(LineCommitOptionsStorage.PrettyListing, LanguageNames.VisualBasic) Then nextHandler() Return End If ' Commit is not cancellable. Dim _cancellationToken = CancellationToken.None Dim bufferManager = _bufferManagerFactory.CreateForBuffer(args.SubjectBuffer) Dim oldCaretPoint = args.TextView.GetCaretPoint(args.SubjectBuffer) ' When we call nextHandler(), it's possible that some other feature might try to move the caret ' or something similar. We want this "outer" commit to be the real one, so start suppressing any ' re-entrant commits. Dim suppressionHandle = bufferManager.BeginSuppressingCommits() Try ' Evil: we really want a enter in VB to be always grouped as a single undo transaction, and so make sure all ' things from here on out are grouped as one. Using transaction = _textUndoHistoryRegistry.GetHistory(args.TextView.TextBuffer).CreateTransaction(EditorFeaturesResources.Insert_new_line) transaction.MergePolicy = AutomaticCodeChangeMergePolicy.Instance nextHandler() If ShouldCommitFromEnter(oldCaretPoint, args.TextView, args.SubjectBuffer, _cancellationToken) Then ' We are done suppressing at this point suppressionHandle.Dispose() suppressionHandle = Nothing bufferManager.CommitDirty(isExplicitFormat:=False, cancellationToken:=_cancellationToken) ' We may have re-indented the surrounding block, so let's recompute ' where we should end up Dim newCaretPosition = args.TextView.GetCaretPoint(args.SubjectBuffer) If newCaretPosition.HasValue Then Dim currentLine = newCaretPosition.Value.GetContainingLine() Dim desiredIndentation = args.TextView.GetDesiredIndentation(_smartIndentationService, currentLine) If currentLine.Length = 0 AndAlso desiredIndentation.HasValue Then args.TextView.TryMoveCaretToAndEnsureVisible(New VirtualSnapshotPoint(currentLine.Start, desiredIndentation.Value)) _editorOperationsFactoryService.GetEditorOperations(args.TextView).AddAfterTextBufferChangePrimitive() End If End If End If transaction.Complete() End Using Finally ' Resume committing If suppressionHandle IsNot Nothing Then suppressionHandle.Dispose() End If End Try End Sub Private Function ShouldCommitFromEnter( oldCaretPositionInOldSnapshot As SnapshotPoint?, textView As ITextView, subjectBuffer As ITextBuffer, cancellationToken As CancellationToken) As Boolean Dim newCaretPosition = textView.GetCaretPoint(subjectBuffer) If Not oldCaretPositionInOldSnapshot.HasValue OrElse Not newCaretPosition.HasValue Then ' We somehow moved in or out of our subject buffer during this commit. Not sure how, ' but that counts as losing focus and thus is a commit Return True End If ' Map oldCaretPosition forward to the current snapshot Dim oldCaretPositionInCurrentSnapshot = oldCaretPositionInOldSnapshot.Value.CreateTrackingPoint(PointTrackingMode.Negative).GetPoint(subjectBuffer.CurrentSnapshot) Dim bufferManager = _bufferManagerFactory.CreateForBuffer(subjectBuffer) If CommitBufferManager.IsMovementBetweenStatements(oldCaretPositionInCurrentSnapshot, newCaretPosition.Value, cancellationToken) Then Dim document = subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges() If document Is Nothing Then Return False End If Dim tree = document.GetSyntaxTreeSynchronously(cancellationToken) Dim token = tree.FindTokenOnLeftOfPosition(oldCaretPositionInOldSnapshot.Value, cancellationToken) If token.IsKind(SyntaxKind.StringLiteralToken) AndAlso token.FullSpan.Contains(oldCaretPositionInOldSnapshot.Value) Then Return False End If ' Get the statement which we pressed enter for Dim oldStatement = ContainingStatementInfo.GetInfo(oldCaretPositionInCurrentSnapshot, tree, cancellationToken) Return oldStatement Is Nothing OrElse Not oldStatement.IsIncomplete End If Return False End Function Public Function GetCommandState(args As ReturnKeyCommandArgs, nextHandler As Func(Of CommandState)) As CommandState Implements IChainedCommandHandler(Of ReturnKeyCommandArgs).GetCommandState ' We don't make any decision if the enter key is allowed; we just forward onto the next handler Return nextHandler() End Function Public Sub ExecuteCommand(args As PasteCommandArgs, nextHandler As Action, context As CommandExecutionContext) Implements IChainedCommandHandler(Of PasteCommandArgs).ExecuteCommand Using context.OperationContext.AddScope(allowCancellation:=True, EditorFeaturesResources.Formatting_pasted_text) CommitOnPaste(args, nextHandler, context.OperationContext.UserCancellationToken) End Using End Sub Private Sub CommitOnPaste(args As PasteCommandArgs, nextHandler As Action, cancellationToken As CancellationToken) Using transaction = _textUndoHistoryRegistry.GetHistory(args.TextView.TextBuffer).CreateTransaction(EditorFeaturesResources.Paste) Dim oldVersion = args.SubjectBuffer.CurrentSnapshot.Version ' Do the paste in the same transaction as the commit/format nextHandler() If Not _globalOptions.GetOption(FormattingOptionsStorage.FormatOnPaste, LanguageNames.VisualBasic) Then transaction.Complete() Return End If Dim document = args.SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges() If document IsNot Nothing Then Dim formattingRuleService = document.Project.Solution.Services.GetService(Of IHostDependentFormattingRuleFactoryService)() If formattingRuleService.ShouldNotFormatOrCommitOnPaste(document.Id) Then transaction.Complete() Return End If End If ' Did we paste content that changed the number of lines? If oldVersion.Changes IsNot Nothing AndAlso oldVersion.Changes.IncludesLineChanges Then Try _bufferManagerFactory.CreateForBuffer(args.SubjectBuffer).CommitDirty(isExplicitFormat:=False, cancellationToken:=cancellationToken) Catch ex As OperationCanceledException ' If the commit was cancelled, we still want the paste to go through End Try End If transaction.Complete() End Using End Sub Public Function GetCommandState(args As PasteCommandArgs, nextHandler As Func(Of CommandState)) As CommandState Implements IChainedCommandHandler(Of PasteCommandArgs).GetCommandState Return nextHandler() End Function Public Sub ExecuteCommand(args As SaveCommandArgs, nextHandler As Action, context As CommandExecutionContext) Implements IChainedCommandHandler(Of SaveCommandArgs).ExecuteCommand If _globalOptions.GetOption(FormattingOptionsStorage.FormatOnSave) Then Using context.OperationContext.AddScope(allowCancellation:=True, VBEditorResources.Formatting_Document) Using transaction = _textUndoHistoryRegistry.GetHistory(args.TextView.TextBuffer).CreateTransaction(VBEditorResources.Format_on_Save) _bufferManagerFactory.CreateForBuffer(args.SubjectBuffer).CommitDirty(isExplicitFormat:=False, cancellationToken:=context.OperationContext.UserCancellationToken) ' We should only create the transaction if anything actually happened If transaction.UndoPrimitives.Any() Then transaction.Complete() End If End Using End Using End If nextHandler() End Sub Public Function GetCommandState(args As SaveCommandArgs, nextHandler As Func(Of CommandState)) As CommandState Implements IChainedCommandHandler(Of SaveCommandArgs).GetCommandState Return nextHandler() End Function End Class End Namespace