diff --git a/src/EditorFeatures/CSharp/SplitStringLiteral/SplitStringLiteralCommandHandler.cs b/src/EditorFeatures/CSharp/SplitStringLiteral/SplitStringLiteralCommandHandler.cs index 23403b2c9398e..583c3a9f2e59a 100644 --- a/src/EditorFeatures/CSharp/SplitStringLiteral/SplitStringLiteralCommandHandler.cs +++ b/src/EditorFeatures/CSharp/SplitStringLiteral/SplitStringLiteralCommandHandler.cs @@ -92,7 +92,14 @@ public bool ExecuteCommandWorker(ReturnKeyCommandArgs args) } } - IndentationOptions? lazyOptions = null; + var document = subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); + if (document == null) + { + return false; + } + + var parsedDocument = ParsedDocument.CreateSynchronously(document, CancellationToken.None); + var options = subjectBuffer.GetIndentationOptions(_editorOptionsService, parsedDocument.LanguageServices, explicitFormat: false); // We now go through the verified string literals and split each of them. // The list of spans is traversed in reverse order so we do not have to @@ -100,53 +107,35 @@ public bool ExecuteCommandWorker(ReturnKeyCommandArgs args) // from splitting at earlier caret positions. foreach (var span in spans.Reverse()) { - if (!SplitString(textView, subjectBuffer, span.Start.Position, ref lazyOptions, CancellationToken.None)) + using var transaction = CaretPreservingEditTransaction.TryCreate( + CSharpEditorResources.Split_string, textView, _undoHistoryRegistry, _editorOperationsFactoryService); + + var splitter = StringSplitter.TryCreate(parsedDocument, span.Start.Position, options, CancellationToken.None); + if (splitter?.TrySplit(out var newRoot, out var newPosition) != true) { return false; } - } - - return true; - } - - private bool SplitString(ITextView textView, ITextBuffer subjectBuffer, int position, ref IndentationOptions? lazyOptions, CancellationToken cancellationToken) - { - var document = subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); - if (document == null) - { - return false; - } - lazyOptions ??= subjectBuffer.GetIndentationOptions(_editorOptionsService, document.Project.Services, explicitFormat: false); + // apply the change: + var newDocument = parsedDocument.WithChangedRoot(newRoot!, CancellationToken.None); + subjectBuffer.ApplyChanges(newDocument.GetChanges(parsedDocument)); + parsedDocument = newDocument; - using var transaction = CaretPreservingEditTransaction.TryCreate( - CSharpEditorResources.Split_string, textView, _undoHistoryRegistry, _editorOperationsFactoryService); + // move caret: + var snapshotPoint = new SnapshotPoint( + subjectBuffer.CurrentSnapshot, newPosition); + var newCaretPoint = textView.BufferGraph.MapUpToBuffer( + snapshotPoint, PointTrackingMode.Negative, PositionAffinity.Predecessor, + textView.TextBuffer); - var parsedDocument = ParsedDocument.CreateSynchronously(document, CancellationToken.None); - var splitter = StringSplitter.TryCreate(parsedDocument, position, lazyOptions.Value, cancellationToken); - if (splitter?.TrySplit(out var newRoot, out var newPosition) != true) - { - return false; - } - - // apply the change: - var newDocument = document.WithSyntaxRoot(newRoot!); - var workspace = newDocument.Project.Solution.Workspace; - workspace.TryApplyChanges(newDocument.Project.Solution); - - // move caret: - var snapshotPoint = new SnapshotPoint( - subjectBuffer.CurrentSnapshot, newPosition); - var newCaretPoint = textView.BufferGraph.MapUpToBuffer( - snapshotPoint, PointTrackingMode.Negative, PositionAffinity.Predecessor, - textView.TextBuffer); + if (newCaretPoint != null) + { + textView.Caret.MoveTo(newCaretPoint.Value); + } - if (newCaretPoint != null) - { - textView.Caret.MoveTo(newCaretPoint.Value); + transaction?.Complete(); } - transaction?.Complete(); return true; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Utilities/ParsedDocument.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Utilities/ParsedDocument.cs index 61a3336f9b59e..c72048048d0c5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Utilities/ParsedDocument.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Utilities/ParsedDocument.cs @@ -3,11 +3,16 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; +using static System.Net.Mime.MediaTypeNames; +using static Humanizer.In; namespace Microsoft.CodeAnalysis; @@ -55,4 +60,27 @@ public ParsedDocument WithChangedRoot(SyntaxNode root, CancellationToken cancell var text = root.SyntaxTree.GetText(cancellationToken); return new ParsedDocument(Id, text, root, HostLanguageServices); } + + /// + /// Equivalent semantics to + /// + public IEnumerable GetChanges(in ParsedDocument oldDocument) + { + Contract.ThrowIfFalse(Id == oldDocument.Id); + + if (Text == oldDocument.Text || SyntaxTree == oldDocument.SyntaxTree) + { + return SpecializedCollections.EmptyEnumerable(); + } + + var textChanges = Text.GetTextChanges(oldDocument.Text); + + // if changes are significant (not the whole document being replaced) then use these changes + if (textChanges.Count > 1 || textChanges.Count == 1 && textChanges[0].Span != new TextSpan(0, oldDocument.Text.Length)) + { + return textChanges; + } + + return SyntaxTree.GetChanges(oldDocument.SyntaxTree); + } }