@@ -49,6 +49,9 @@ public partial class SharpIdeCodeEdit : CodeEdit
4949 private bool _fileChangingSuppressBreakpointToggleEvent ;
5050 private bool _settingWholeDocumentTextSuppressLineEditsEvent ; // A dodgy workaround - setting the whole document doesn't guarantee that the line count stayed the same etc. We are still going to have broken highlighting. TODO: Investigate getting minimal text change ranges, and change those ranges only
5151 private bool _fileDeleted ;
52+ // Captured in _GuiInput *before* a line-modifying keystroke is processed, so that OnLinesEditedFrom
53+ // can determine the correct LineEditOrigin from pre-edit state rather than post-edit state.
54+ private ( int line , int col , string lineText ) ? _pendingLineEditOrigin ;
5255 private IDisposable ? _projectDiagnosticsObserveDisposable ;
5356
5457 [ Inject ] private readonly IdeOpenTabsFileManager _openTabsFileManager = null ! ;
@@ -140,43 +143,42 @@ private async Task OnSolutionAltered()
140143 public enum LineEditOrigin
141144 {
142145 StartOfLine ,
146+ MidLine ,
143147 EndOfLine ,
144148 Unknown
145149 }
146150 // Line removed - fromLine 55, toLine 54
147151 // Line added - fromLine 54, toLine 55
148152 // Multi cursor gets a single line event for each
149- // problem is 10 to 11 gets returned for 'Enter' at the start of line 10, as well as 'Enter' at the end of line 10
150- // This means that the line that moves down needs to be based on whether the new line was from the start or end of the line
151153 private void OnLinesEditedFrom ( long fromLine , long toLine )
152154 {
153155 if ( fromLine == toLine ) return ;
154156 if ( _settingWholeDocumentTextSuppressLineEditsEvent ) return ;
155- var fromLineText = GetLine ( ( int ) fromLine ) ;
156- var caretPosition = this . GetCaretPosition ( ) ;
157- var caretPositionEnum = LineEditOrigin . Unknown ;
158157
159- if ( caretPosition . col > fromLineText . Length )
160- {
161- _syntaxHighlighter . LinesChanged ( fromLine , toLine , caretPositionEnum ) ;
162- return ;
163- }
158+ // Consume the pre-edit snapshot captured in _GuiInput (if any).
159+ // Because the snapshot was taken *before* the edit, the caret position and line text
160+ // are exactly what they were at the moment the key was pressed — no post-edit guesswork.
161+ var snapshot = _pendingLineEditOrigin ;
162+ _pendingLineEditOrigin = null ;
164163
165- var textFrom0ToCaret = fromLineText . AsSpan ( ) [ ..caretPosition . col ] ;
166- if ( textFrom0ToCaret . IsEmpty || textFrom0ToCaret . IsWhiteSpace ( ) )
167- {
168- caretPositionEnum = LineEditOrigin . StartOfLine ;
169- }
170- else
164+ var origin = LineEditOrigin . Unknown ;
165+ if ( snapshot is not null )
171166 {
172- var textfromCaretToEnd = fromLineText . AsSpan ( ) [ caretPosition . col ..] ;
173- if ( textfromCaretToEnd . IsEmpty || textfromCaretToEnd . IsWhiteSpace ( ) )
174- {
175- caretPositionEnum = LineEditOrigin . EndOfLine ;
176- }
167+ var ( _, snapCol , snapText ) = snapshot . Value ;
168+ var clampedCol = Math . Min ( snapCol , snapText . Length ) ;
169+ var textBeforeCaret = snapText . AsSpan ( ) [ ..clampedCol ] ;
170+ var textAfterCaret = snapText . AsSpan ( ) [ clampedCol ..] ;
171+
172+ if ( textBeforeCaret . IsEmpty || textBeforeCaret . IsWhiteSpace ( ) )
173+ origin = LineEditOrigin . StartOfLine ;
174+ else if ( textAfterCaret . IsEmpty || textAfterCaret . IsWhiteSpace ( ) )
175+ origin = LineEditOrigin . EndOfLine ;
176+ else
177+ origin = LineEditOrigin . MidLine ;
177178 }
178- //GD.Print($"Lines edited from {fromLine} to {toLine}, origin: {caretPositionEnum}, current caret position: {caretPosition}");
179- _syntaxHighlighter . LinesChanged ( fromLine , toLine , caretPositionEnum ) ;
179+
180+ //GD.Print($"Lines edited from {fromLine} to {toLine}, origin: {origin}");
181+ _syntaxHighlighter . LinesChanged ( fromLine , toLine , origin ) ;
180182 }
181183
182184 public override void _ExitTree ( )
@@ -428,6 +430,30 @@ public override void _Draw()
428430 public override void _GuiInput ( InputEvent @event )
429431 {
430432 if ( @event is InputEventMouseMotion ) return ;
433+
434+ // Capture pre-edit caret state for line-modifying keystrokes, so that OnLinesEditedFrom
435+ // can determine LineEditOrigin from the state *before* the edit happened.
436+ // We only do this for single-caret edits; multi-caret falls back to Unknown.
437+ if ( @event is InputEventKey { Pressed : true } keyEvent && GetCaretCount ( ) == 1 )
438+ {
439+ var ( caretLine , caretCol ) = GetCaretPosition ( ) ;
440+ switch ( keyEvent . Keycode )
441+ {
442+ // Enter / numpad Enter — line(s) added
443+ case Key . Enter or Key . KpEnter :
444+ _pendingLineEditOrigin = ( caretLine , caretCol , GetLine ( caretLine ) ) ;
445+ break ;
446+ // Forward-delete at end of line merges the next line up — line removed
447+ case Key . Delete when ! HasSelection ( ) :
448+ {
449+ var lineText = GetLine ( caretLine ) ;
450+ if ( caretCol == lineText . Length && caretLine < GetLineCount ( ) - 1 )
451+ _pendingLineEditOrigin = ( caretLine , caretCol , lineText ) ;
452+ break ;
453+ }
454+ }
455+ }
456+
431457 if ( @event . IsActionPressed ( InputStringNames . Backspace , true ) && HasSelection ( ) is false )
432458 {
433459 var ( caretLine , caretCol ) = GetCaretPosition ( ) ;
@@ -437,6 +463,8 @@ public override void _GuiInput(InputEvent @event)
437463 var textBeforeCaret = lineText . AsSpan ( ) [ ..caretCol ] ;
438464 if ( textBeforeCaret . IsEmpty || textBeforeCaret . IsWhiteSpace ( ) )
439465 {
466+ // Capture pre-edit state before RemoveText triggers LinesEditedFrom
467+ if ( GetCaretCount ( ) == 1 ) _pendingLineEditOrigin = ( caretLine , caretCol , lineText ) ;
440468 BeginComplexOperation ( ) ;
441469 var prevLine = caretLine - 1 ;
442470 var prevLineLength = GetLine ( prevLine ) . Length ;
0 commit comments