From 692d7be65f6872e3e0ebae6727d1000673f753df Mon Sep 17 00:00:00 2001 From: tmat Date: Wed, 24 Aug 2022 09:32:04 -0700 Subject: [PATCH 1/4] AutomaticLineEnder --- .../AutomaticCompletion/AutomaticLineEnderCommandHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs index 8a0fad77343ef..14b8ea2c5527a 100644 --- a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs +++ b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs @@ -327,7 +327,7 @@ protected override void ModifySelectedNode( CancellationToken cancellationToken) { var root = document.GetRequiredSyntaxRootSynchronously(cancellationToken); - var formattingOptions = document.GetSyntaxFormattingOptionsAsync(EditorOptionsService.GlobalOptions, cancellationToken).AsTask().WaitAndGetResult(cancellationToken); + var formattingOptions = args.SubjectBuffer.GetSyntaxFormattingOptions(EditorOptionsService, document.Project.Services, explicitFormat: false); // Add braces for the selected node if (addBrace) From 94b94b132a61df5915ee0dd22ccf68c2acaca167 Mon Sep 17 00:00:00 2001 From: tmat Date: Wed, 24 Aug 2022 10:11:34 -0700 Subject: [PATCH 2/4] Remove some Workspace.ApplyTextChanges calls --- .../AsyncCompletion/CompletionSource.cs | 13 ++++++------- .../Core/Shared/Extensions/WorkspaceExtensions.cs | 8 +------- .../EndConstructGeneration/ReplaceSpanResult.vb | 7 +------ .../AbstractLanguageService`2.IVsLanguageTextOps.cs | 4 +--- .../Remote/RemoteHostClientServiceFactoryTests.cs | 8 +++++++- .../Impl/Snippets/SnippetCommandHandler.vb | 5 +---- 6 files changed, 17 insertions(+), 28 deletions(-) diff --git a/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CompletionSource.cs b/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CompletionSource.cs index 4ac20ea3d2864..504e8ccad43cd 100644 --- a/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CompletionSource.cs +++ b/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CompletionSource.cs @@ -17,6 +17,7 @@ using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; @@ -153,7 +154,7 @@ private bool ShouldTriggerCompletion( SourceText sourceText, Document document, CompletionService completionService, - in CompletionOptions options) + CompletionOptions options) { // The trigger reason guarantees that user wants a completion. if (trigger.Reason is AsyncCompletionData.CompletionTriggerReason.Invoke or @@ -173,7 +174,7 @@ private bool ShouldTriggerCompletion( // Otherwise, tab should not be a completion trigger. if (trigger.Reason == AsyncCompletionData.CompletionTriggerReason.Insertion && trigger.Character == '\t') { - return TryInvokeSnippetCompletion(completionService, document, sourceText, triggerLocation.Position, options); + return TryInvokeSnippetCompletion(triggerLocation.Snapshot.TextBuffer, triggerLocation.Position, sourceText, document.Project.Services, completionService.GetRules(options)); } var roslynTrigger = Helpers.GetRoslynTrigger(trigger, triggerLocation); @@ -184,16 +185,15 @@ private bool ShouldTriggerCompletion( } private bool TryInvokeSnippetCompletion( - CompletionService completionService, Document document, SourceText text, int caretPoint, in CompletionOptions options) + ITextBuffer buffer, int caretPoint, SourceText text, LanguageServices services, CompletionRules rules) { - var rules = completionService.GetRules(options); // Do not invoke snippet if the corresponding rule is not set in options. if (rules.SnippetsRule != SnippetsRule.IncludeAfterTypingIdentifierQuestionTab) { return false; } - var syntaxFacts = document.GetLanguageService(); + var syntaxFacts = services.GetService(); // Snippets are included if the user types: // If at least one condition for snippets do not hold, bail out. if (syntaxFacts == null || @@ -206,8 +206,7 @@ private bool TryInvokeSnippetCompletion( // Because is actually a command to bring up snippets, // we delete the last that was typed. - var textChange = new TextChange(TextSpan.FromBounds(caretPoint - 2, caretPoint), string.Empty); - document.Project.Solution.Workspace.ApplyTextChanges(document.Id, textChange, CancellationToken.None); + buffer.ApplyChange(new TextChange(TextSpan.FromBounds(caretPoint - 2, caretPoint), string.Empty)); _snippetCompletionTriggeredIndirectly = true; return true; diff --git a/src/EditorFeatures/Core/Shared/Extensions/WorkspaceExtensions.cs b/src/EditorFeatures/Core/Shared/Extensions/WorkspaceExtensions.cs index 86e400e9f4fc6..48416005e0eaf 100644 --- a/src/EditorFeatures/Core/Shared/Extensions/WorkspaceExtensions.cs +++ b/src/EditorFeatures/Core/Shared/Extensions/WorkspaceExtensions.cs @@ -36,13 +36,7 @@ internal static void ApplyTextChanges(this Workspace workspace, DocumentId id, I workspace.TryApplyChanges(newSolution); } - /// - /// Update the solution so that the document with the Id has the text changes - /// - internal static void ApplyTextChanges(this Workspace workspace, DocumentId id, TextChange textChange, CancellationToken cancellationToken) - => workspace.ApplyTextChanges(id, SpecializedCollections.SingletonEnumerable(textChange), cancellationToken); - - internal static Solution UpdateDocument(this Solution solution, DocumentId id, IEnumerable textChanges, CancellationToken cancellationToken) + private static Solution UpdateDocument(this Solution solution, DocumentId id, IEnumerable textChanges, CancellationToken cancellationToken) { var oldDocument = solution.GetRequiredDocument(id); var oldText = oldDocument.GetTextSynchronously(cancellationToken); diff --git a/src/EditorFeatures/VisualBasic/EndConstructGeneration/ReplaceSpanResult.vb b/src/EditorFeatures/VisualBasic/EndConstructGeneration/ReplaceSpanResult.vb index ed1364118765b..909b925f7be53 100644 --- a/src/EditorFeatures/VisualBasic/EndConstructGeneration/ReplaceSpanResult.vb +++ b/src/EditorFeatures/VisualBasic/EndConstructGeneration/ReplaceSpanResult.vb @@ -37,12 +37,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.EndConstructGeneration Return End If - Dim oldSolution = document.Project.Solution.Workspace.CurrentSolution - Dim newSolution = oldSolution.UpdateDocument( - document.Id, SpecializedCollections.SingletonEnumerable( - New TextChange(_snapshotSpan.TranslateTo(current, SpanTrackingMode.EdgeExclusive).Span.ToTextSpan(), _replacementText)), CancellationToken.None) - - oldSolution.Workspace.TryApplyChanges(newSolution) + subjectBuffer.ApplyChange(New TextChange(_snapshotSpan.TranslateTo(current, SpanTrackingMode.EdgeExclusive).Span.ToTextSpan(), _replacementText)) Dim startPoint = _snapshotSpan.Start.TranslateTo(subjectBuffer.CurrentSnapshot, PointTrackingMode.Negative) If _newCaretPosition IsNot Nothing Then diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs index 7625d240385c5..d238e691387a5 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs @@ -80,9 +80,7 @@ private int FormatWorker(IVsTextLayer textLayer, TextSpan[] selections, Cancella return VSConstants.S_OK; } - // create new formatted document - var formattedDocument = document.WithText(text.WithChanges(formattedChanges)); - Workspace.ApplyDocumentChanges(formattedDocument, cancellationToken); + textBuffer.ApplyChanges(formattedChanges); return VSConstants.S_OK; } diff --git a/src/VisualStudio/Core/Test.Next/Remote/RemoteHostClientServiceFactoryTests.cs b/src/VisualStudio/Core/Test.Next/Remote/RemoteHostClientServiceFactoryTests.cs index 91468ab5924c1..d13762f016f80 100644 --- a/src/VisualStudio/Core/Test.Next/Remote/RemoteHostClientServiceFactoryTests.cs +++ b/src/VisualStudio/Core/Test.Next/Remote/RemoteHostClientServiceFactoryTests.cs @@ -10,6 +10,7 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; +using EnvDTE; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; @@ -50,7 +51,12 @@ public async Task UpdaterService() workspace.AddSolution(SolutionInfo.Create(SolutionId.CreateNewId(), VersionStamp.Default)); var project = workspace.AddProject("proj", LanguageNames.CSharp); var document = workspace.AddDocument(project.Id, "doc.cs", SourceText.From("code")); - workspace.ApplyTextChanges(document.Id, new[] { new TextChange(new TextSpan(0, 1), "abc") }, CancellationToken.None); + + var oldText = document.GetTextSynchronously(CancellationToken.None); + var newText = oldText.WithChanges(new[] { new TextChange(new TextSpan(0, 1), "abc") }); + var newSolution = document.Project.Solution.WithDocumentText(document.Id, newText, PreservationMode.PreserveIdentity); + + workspace.TryApplyChanges(newSolution); // wait for listener var workspaceListener = listenerProvider.GetWaiter(FeatureAttribute.Workspace); diff --git a/src/VisualStudio/VisualBasic/Impl/Snippets/SnippetCommandHandler.vb b/src/VisualStudio/VisualBasic/Impl/Snippets/SnippetCommandHandler.vb index b11388bc1b4e8..362c5d40db877 100644 --- a/src/VisualStudio/VisualBasic/Impl/Snippets/SnippetCommandHandler.vb +++ b/src/VisualStudio/VisualBasic/Impl/Snippets/SnippetCommandHandler.vb @@ -117,10 +117,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Snippets Dim currentSnapshot = subjectBuffer.CurrentSnapshot Dim document = currentSnapshot.GetOpenDocumentInCurrentContextWithChanges() If document IsNot Nothing Then - Dim editorWorkspace = document.Project.Solution.Workspace - Dim text = currentSnapshot.AsText() - Dim change = New TextChange(Microsoft.CodeAnalysis.Text.TextSpan.FromBounds(caretPosition - 1, caretPosition), String.Empty) - editorWorkspace.ApplyTextChanges(document.Id, change, CancellationToken.None) + subjectBuffer.ApplyChange(New TextChange(Microsoft.CodeAnalysis.Text.TextSpan.FromBounds(caretPosition - 1, caretPosition), String.Empty)) End If End Sub End Class From 23dab09f100cf15ad6e29f997904210fae27fa56 Mon Sep 17 00:00:00 2001 From: tmat Date: Wed, 24 Aug 2022 10:34:57 -0700 Subject: [PATCH 3/4] Extract method --- .../ExtractMethod/ExtractMethodCommandHandler.cs | 12 +++++++----- .../Organizing/OrganizeDocumentCommandHandler.cs | 13 +++++++------ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/EditorFeatures/Core/ExtractMethod/ExtractMethodCommandHandler.cs b/src/EditorFeatures/Core/ExtractMethod/ExtractMethodCommandHandler.cs index c2b2ef77f0b98..65e32f123563a 100644 --- a/src/EditorFeatures/Core/ExtractMethod/ExtractMethodCommandHandler.cs +++ b/src/EditorFeatures/Core/ExtractMethod/ExtractMethodCommandHandler.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections; +using System.Collections.Generic; using System.ComponentModel.Composition; using System.Linq; using System.Reflection.Metadata; @@ -22,6 +24,7 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Commanding; +using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; @@ -189,10 +192,11 @@ private async Task ExecuteWorkerAsync( var cleanupOptions = await document.GetCodeCleanupOptionsAsync(_globalOptions, cancellationToken).ConfigureAwait(false); var (formattedDocument, methodNameAtInvocation) = await result.GetFormattedDocumentAsync(cleanupOptions, cancellationToken).ConfigureAwait(false); + var changes = await formattedDocument.GetTextChangesAsync(document, cancellationToken).ConfigureAwait(false); await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - ApplyChange_OnUIThread(formattedDocument, textBuffer, waitContext); + ApplyChange_OnUIThread(textBuffer, changes, waitContext); // start inline rename to allow the user to change the name if they want. var textSnapshot = textBuffer.CurrentSnapshot; @@ -206,17 +210,15 @@ private async Task ExecuteWorkerAsync( } private void ApplyChange_OnUIThread( - Document formattedDocument, ITextBuffer textBuffer, IBackgroundWorkIndicatorContext waitContext) + ITextBuffer textBuffer, IEnumerable changes, IBackgroundWorkIndicatorContext waitContext) { _threadingContext.ThrowIfNotOnUIThread(); - var cancellationToken = waitContext.UserCancellationToken; - using var undoTransaction = _undoManager.GetTextBufferUndoManager(textBuffer).TextBufferUndoHistory.CreateTransaction("Extract Method"); // We're about to make an edit ourselves. so disable the cancellation that happens on editing. waitContext.CancelOnEdit = false; - formattedDocument.Project.Solution.Workspace.ApplyDocumentChanges(formattedDocument, cancellationToken); + textBuffer.ApplyChanges(changes); // apply changes undoTransaction.Complete(); diff --git a/src/EditorFeatures/Core/Organizing/OrganizeDocumentCommandHandler.cs b/src/EditorFeatures/Core/Organizing/OrganizeDocumentCommandHandler.cs index 270a3496c720a..a0cf2fe26b0dd 100644 --- a/src/EditorFeatures/Core/Organizing/OrganizeDocumentCommandHandler.cs +++ b/src/EditorFeatures/Core/Organizing/OrganizeDocumentCommandHandler.cs @@ -8,6 +8,7 @@ using System.ComponentModel.Composition; using System.Diagnostics.CodeAnalysis; using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Commanding.Commands; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; @@ -64,7 +65,8 @@ public bool ExecuteCommand(OrganizeDocumentCommandArgs args, CommandExecutionCon var newDocument = OrganizingService.OrganizeAsync(document, cancellationToken: cancellationToken).WaitAndGetResult(cancellationToken); if (document != newDocument) { - ApplyTextChange(document, newDocument); + var changes = newDocument.GetTextChangesAsync(document, cancellationToken).WaitAndGetResult(cancellationToken); + args.SubjectBuffer.ApplyChanges(changes); } } } @@ -144,7 +146,8 @@ private void SortImports(ITextBuffer subjectBuffer, IUIThreadOperationContext op var newDocument = organizeImportsService.OrganizeImportsAsync(document, options, cancellationToken).WaitAndGetResult(cancellationToken); if (document != newDocument) { - ApplyTextChange(document, newDocument); + var changes = newDocument.GetTextChangesAsync(document, cancellationToken).WaitAndGetResult(cancellationToken); + subjectBuffer.ApplyChanges(changes); } } } @@ -163,12 +166,10 @@ private void SortAndRemoveUnusedImports(ITextBuffer subjectBuffer, IUIThreadOper newDocument = organizeImportsService.OrganizeImportsAsync(newDocument, options, cancellationToken).WaitAndGetResult(cancellationToken); if (document != newDocument) { - ApplyTextChange(document, newDocument); + var changes = newDocument.GetTextChangesAsync(document, cancellationToken).WaitAndGetResult(cancellationToken); + subjectBuffer.ApplyChanges(changes); } } } - - protected static void ApplyTextChange(Document oldDocument, Document newDocument) - => oldDocument.Project.Solution.Workspace.ApplyDocumentChanges(newDocument, CancellationToken.None); } } From 8d212f000e9db0f688dcd28061055a278fb9b281 Mon Sep 17 00:00:00 2001 From: tmat Date: Wed, 24 Aug 2022 11:06:44 -0700 Subject: [PATCH 4/4] Eliminate more calls to ApplyDocumentChanges --- .../EndConstructGeneration/EndConstructCommandHandler.vb | 4 ++-- src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb | 5 +++-- ...bstractImplementAbstractClassOrInterfaceCommandHandler.vb | 3 ++- .../CaseCorrecting/CaseCorrectionServiceTests.vb | 5 +---- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/EditorFeatures/VisualBasic/EndConstructGeneration/EndConstructCommandHandler.vb b/src/EditorFeatures/VisualBasic/EndConstructGeneration/EndConstructCommandHandler.vb index 0c01dad43f02d..eda6cf180b9ac 100644 --- a/src/EditorFeatures/VisualBasic/EndConstructGeneration/EndConstructCommandHandler.vb +++ b/src/EditorFeatures/VisualBasic/EndConstructGeneration/EndConstructCommandHandler.vb @@ -141,11 +141,11 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.EndConstructGeneration Dim options = document.GetCodeCleanupOptionsAsync(_globalOptions, cancellationToken).AsTask().WaitAndGetResult(cancellationToken) Dim cleanDocument = CodeCleaner.CleanupAsync(document, GetSpanToCleanup(statement), Options, codeCleanups, cancellationToken:=cancellationToken).WaitAndGetResult(cancellationToken) + Dim changes = cleanDocument.GetTextChangesAsync(document, cancellationToken).WaitAndGetResult(cancellationToken) Using transaction = New CaretPreservingEditTransaction(VBEditorResources.End_Construct, view, _undoHistoryRegistry, _editorOperationsFactoryService) transaction.MergePolicy = AutomaticCodeChangeMergePolicy.Instance - - cleanDocument.Project.Solution.Workspace.ApplyDocumentChanges(cleanDocument, cancellationToken) + buffer.ApplyChanges(changes) transaction.Complete() End Using End Sub diff --git a/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb b/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb index 08331fd3dedf7..cf753e036fbdc 100644 --- a/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb +++ b/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb @@ -85,7 +85,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit baseSnapshot, baseTree, dirtyRegion, - document.GetSyntaxTreeSynchronously(cancellationToken), + tree, cancellationToken) Dim codeCleanups = CodeCleaner.GetDefaultProviders(document). @@ -126,7 +126,8 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit End If End If - finalDocument.Project.Solution.Workspace.ApplyDocumentChanges(finalDocument, cancellationToken) + Dim changes = finalDocument.GetTextChangesAsync(document, cancellationToken).WaitAndGetResult(cancellationToken) + buffer.ApplyChanges(changes) End Using End Sub diff --git a/src/EditorFeatures/VisualBasic/Utilities/CommandHandlers/AbstractImplementAbstractClassOrInterfaceCommandHandler.vb b/src/EditorFeatures/VisualBasic/Utilities/CommandHandlers/AbstractImplementAbstractClassOrInterfaceCommandHandler.vb index cad0b54ca50e3..8cb816511fdaa 100644 --- a/src/EditorFeatures/VisualBasic/Utilities/CommandHandlers/AbstractImplementAbstractClassOrInterfaceCommandHandler.vb +++ b/src/EditorFeatures/VisualBasic/Utilities/CommandHandlers/AbstractImplementAbstractClassOrInterfaceCommandHandler.vb @@ -175,7 +175,8 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Utilities.CommandHandlers newDocument = Simplifier.ReduceAsync(newDocument, Simplifier.Annotation, cleanupOptions.SimplifierOptions, cancellationToken).WaitAndGetResult(cancellationToken) newDocument = Formatter.FormatAsync(newDocument, Formatter.Annotation, cleanupOptions.FormattingOptions, cancellationToken).WaitAndGetResult(cancellationToken) - newDocument.Project.Solution.Workspace.ApplyDocumentChanges(newDocument, cancellationToken) + Dim changes = newDocument.GetTextChangesAsync(document, cancellationToken).WaitAndGetResult(cancellationToken) + args.SubjectBuffer.ApplyChanges(changes) ' Place the cursor back to where it logically was after this token = newDocument.GetSyntaxRootSynchronously(cancellationToken). diff --git a/src/EditorFeatures/VisualBasicTest/CaseCorrecting/CaseCorrectionServiceTests.vb b/src/EditorFeatures/VisualBasicTest/CaseCorrecting/CaseCorrectionServiceTests.vb index 91cb517348475..6a7770a524df2 100644 --- a/src/EditorFeatures/VisualBasicTest/CaseCorrecting/CaseCorrectionServiceTests.vb +++ b/src/EditorFeatures/VisualBasicTest/CaseCorrecting/CaseCorrectionServiceTests.vb @@ -30,7 +30,6 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CaseCorrecting Private Shared Async Function TestAsync(expected As String, workspace As TestWorkspace) As Task Dim hostDocument = workspace.Documents.First() - Dim buffer = hostDocument.GetTextBuffer() Dim document = workspace.CurrentSolution.GetDocument(hostDocument.Id) Dim span = (Await document.GetSyntaxRootAsync()).FullSpan @@ -42,9 +41,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CaseCorrecting ImmutableArray.Create(Of ICodeCleanupProvider)(New CaseCorrectionCodeCleanupProvider()), CancellationToken.None) - newDocument.Project.Solution.Workspace.ApplyDocumentChanges(newDocument, CancellationToken.None) - - Dim actual = buffer.CurrentSnapshot.GetText() + Dim actual = newDocument.GetTextSynchronously(CancellationToken.None).ToString() Assert.Equal(expected, actual) End Function