@@ -17,7 +17,6 @@ struct IMETextView: NSViewRepresentable {
1717 var textColor : NSColor
1818 var placeholder : String = " "
1919 var onReturn : ( ) -> Void
20- var onShiftReturn : ( ) -> Void
2120 var onUpArrow : ( ) -> Bool
2221 var onDownArrow : ( ) -> Bool
2322 var onTab : ( ) -> Bool
@@ -104,7 +103,6 @@ struct IMETextView: NSViewRepresentable {
104103
105104 private func applyCallbacks( to textView: _IMETextView ) {
106105 textView. onReturn = onReturn
107- textView. onShiftReturn = onShiftReturn
108106 textView. onUpArrow = onUpArrow
109107 textView. onDownArrow = onDownArrow
110108 textView. onTab = onTab
@@ -217,7 +215,6 @@ fileprivate final class ChipLayoutManager: NSLayoutManager, @unchecked Sendable
217215
218216fileprivate final class _IMETextView : NSTextView {
219217 var onReturn : ( ) -> Void = { }
220- var onShiftReturn : ( ) -> Void = { }
221218 var onUpArrow : ( ) -> Bool = { false }
222219 var onDownArrow : ( ) -> Bool = { false }
223220 var onTab : ( ) -> Bool = { false }
@@ -274,10 +271,11 @@ fileprivate final class _IMETextView: NSTextView {
274271
275272 override func keyDown( with event: NSEvent ) {
276273 // Shift+Enter: NSTextView doesn't bind this to a doCommand by default. Force-commit any
277- // composing IME text, then fire the newline callback (which appends "\n" via the binding).
274+ // composing IME text, then insert the newline through NSTextView so AppKit updates the
275+ // insertion point and scroll position together.
278276 if event. keyCode == 36 , event. modifierFlags. contains ( . shift) {
279277 commitMarkedTextIfNeeded ( )
280- onShiftReturn ( )
278+ insertShiftReturnNewline ( )
281279 return
282280 }
283281 // Shift+Tab: NSTextView routes this to insertBacktab: which we intercept here so it
@@ -407,5 +405,23 @@ fileprivate final class _IMETextView: NSTextView {
407405 let composing = ( storage. string as NSString ) . substring ( with: range)
408406 insertText ( composing, replacementRange: range)
409407 }
408+
409+ private func insertShiftReturnNewline( ) {
410+ insertText ( " \n " , replacementRange: selectedRange ( ) )
411+ revealInsertionPoint ( )
412+ }
413+
414+ private func revealInsertionPoint( ) {
415+ let textLength = ( string as NSString ) . length
416+ let location = min ( max ( selectedRange ( ) . location, 0 ) , textLength)
417+ let caretRange = NSRange ( location: location, length: 0 )
418+ scrollRangeToVisible ( caretRange)
419+ DispatchQueue . main. async { [ weak self] in
420+ guard let self else { return }
421+ let textLength = ( self . string as NSString ) . length
422+ let location = min ( max ( self . selectedRange ( ) . location, 0 ) , textLength)
423+ self . scrollRangeToVisible ( NSRange ( location: location, length: 0 ) )
424+ }
425+ }
410426}
411427#endif
0 commit comments