66using System . ComponentModel . Composition ;
77using System . Linq ;
88using System . Threading ;
9+ using Microsoft . CodeAnalysis . Collections ;
910using Microsoft . CodeAnalysis . CSharp . SplitStringLiteral ;
1011using Microsoft . CodeAnalysis . Editor . Shared . Extensions ;
1112using Microsoft . CodeAnalysis . Editor . Shared . Utilities ;
@@ -86,6 +87,13 @@ public bool ExecuteCommandWorker(ReturnKeyCommandArgs args, CancellationToken ca
8687 // The list of spans is traversed in reverse order so we do not have to
8788 // deal with updating later caret positions to account for the added space
8889 // from splitting at earlier caret positions.
90+ //
91+ // However, we need to track the new caret positions in the original forward order
92+ // for setting the multi-selection at the end. We use tracking points to automatically
93+ // adjust positions as the buffer changes.
94+ using var _ = PooledObjects . ArrayBuilder < ITrackingPoint > . GetInstance ( spans . Count , fillWithValue : null ! , out var trackingPoints ) ;
95+
96+ var currentIndex = spans . Count - 1 ;
8997 foreach ( var span in spans . Reverse ( ) )
9098 {
9199 using var transaction = CaretPreservingEditTransaction . TryCreate (
@@ -103,23 +111,35 @@ public bool ExecuteCommandWorker(ReturnKeyCommandArgs args, CancellationToken ca
103111 var newSnapshot = subjectBuffer . ApplyChanges ( newDocument . GetChanges ( parsedDocument ) ) ;
104112 parsedDocument = newDocument ;
105113
106- // The buffer edit may have adjusted to position of the current caret but we might need a different location.
107- // Only adjust caret if it is the only one (no multi-caret support: https://github.com/dotnet/roslyn/issues/64812).
108- if ( spans . Count == 1 )
109- {
110- var newCaretPoint = textView . BufferGraph . MapUpToBuffer (
111- new SnapshotPoint ( newSnapshot , newPosition ) ,
112- PointTrackingMode . Negative ,
113- PositionAffinity . Predecessor ,
114- textView . TextBuffer ) ;
115-
116- if ( newCaretPoint != null )
117- textView . Caret . MoveTo ( newCaretPoint . Value ) ;
118- }
114+ // Create a tracking point for the new caret position.
115+ // Use Negative tracking mode so the caret stays before any text inserted at this position.
116+ trackingPoints [ currentIndex ] = newSnapshot . CreateTrackingPoint ( newPosition , PointTrackingMode . Negative ) ;
119117
120118 transaction ? . Complete ( ) ;
119+ currentIndex -- ;
120+ }
121+
122+ // Now set all the caret positions using the multi-selection broker.
123+ // Get the position of each tracking point in the final snapshot.
124+ var finalSnapshot = subjectBuffer . CurrentSnapshot ;
125+ using var finalCaretSpans = TemporaryArray < SnapshotSpan > . Empty ;
126+
127+ foreach ( var trackingPoint in trackingPoints )
128+ {
129+ var position = trackingPoint . GetPosition ( finalSnapshot ) ;
130+ var newCaretPoint = textView . BufferGraph . MapUpToBuffer (
131+ new SnapshotPoint ( finalSnapshot , position ) ,
132+ PointTrackingMode . Negative ,
133+ PositionAffinity . Predecessor ,
134+ textView . TextBuffer ) ;
135+
136+ if ( newCaretPoint != null )
137+ finalCaretSpans . AsRef ( ) . Add ( new SnapshotSpan ( newCaretPoint . Value , 0 ) ) ;
121138 }
122139
140+ if ( finalCaretSpans . Count > 0 )
141+ textView . SetMultiSelection ( finalCaretSpans . ToImmutableAndClear ( ) ) ;
142+
123143 return true ;
124144
125145 static bool LineContainsQuote ( ITextSnapshotLine line , int caretPosition )
0 commit comments