From ec0822401aec9971ebc72fb1bf982cdeceee17ce Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 22 Jul 2022 01:07:08 +0100 Subject: [PATCH] Fixes #1867. Use Undo and Redo commands with WordWrap enabled. (#1877) --- Terminal.Gui/Views/TextView.cs | 154 +++++++++++++++++++++++++++------ UnitTests/TextViewTests.cs | 6 +- 2 files changed, 129 insertions(+), 31 deletions(-) diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index bfaad7f38e..1196e81b90 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -28,6 +28,7 @@ using System.Globalization; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Threading; using NStack; @@ -1371,6 +1372,8 @@ private void Model_LinesLoaded () private void HistoryText_ChangeText (HistoryText.HistoryTextItem obj) { + SetWrapModel (); + var startLine = obj.CursorPosition.Y; if (obj.RemovedOnAdded != null) { @@ -1402,6 +1405,9 @@ private void HistoryText_ChangeText (HistoryText.HistoryTextItem obj) } CursorPosition = obj.FinalCursorPosition; + + UpdateWrapModel (); + Adjust (); } @@ -2021,6 +2027,8 @@ ustring GetRegion () // void ClearRegion () { + SetWrapModel (); + long start, end; long currentEncoded = ((long)(uint)currentRow << 32) | (uint)currentColumn; GetEncodedRegionBounds (out start, out end); @@ -2047,6 +2055,8 @@ void ClearRegion () historyText.Add (new List> (removedLines), CursorPosition, HistoryText.LineStatus.Removed); + UpdateWrapModel (); + return; } @@ -2069,6 +2079,8 @@ void ClearRegion () historyText.Add (new List> (removedLines), CursorPosition, HistoryText.LineStatus.Removed); + UpdateWrapModel (); + SetNeedsDisplay (); } @@ -2211,12 +2223,19 @@ void ResetContinuousFind () } } + string currentCaller; + /// /// Restore from original model. /// - void SetWrapModel () + void SetWrapModel ([CallerMemberName] string caller = null) { + if (currentCaller != null) + return; + if (wordWrap) { + currentCaller = caller; + currentColumn = wrapManager.GetModelColFromWrappedLines (currentRow, currentColumn); currentRow = wrapManager.GetModelLineFromWrappedLines (currentRow); selectionStartColumn = wrapManager.GetModelColFromWrappedLines (selectionStartRow, selectionStartColumn); @@ -2228,9 +2247,14 @@ void SetWrapModel () /// /// Update the original model. /// - void UpdateWrapModel () + void UpdateWrapModel ([CallerMemberName] string caller = null) { + if (currentCaller != null && currentCaller != caller) + return; + if (wordWrap) { + currentCaller = null; + wrapManager.UpdateModel (model, out int nRow, out int nCol, out int nStartRow, out int nStartCol, currentRow, currentColumn, @@ -2241,6 +2265,8 @@ void UpdateWrapModel () selectionStartColumn = nStartCol; wrapNeeded = true; } + if (currentCaller != null) + throw new InvalidOperationException ($"WordWrap settings was changed after the {currentCaller} call."); } /// @@ -2371,17 +2397,6 @@ void Insert (Rune rune) } line.Insert (Math.Min (currentColumn, line.Count), rune); } - if (wordWrap) { - if (Used) { - wrapNeeded = wrapManager.Insert (currentRow, currentColumn, rune); - } else { - wrapNeeded = wrapManager.RemoveAt (currentRow, currentColumn); - wrapNeeded = wrapManager.Insert (currentRow, currentColumn, rune); - } - if (wrapNeeded) { - SetNeedsDisplay (); - } - } var prow = currentRow - topRow; if (!wrapNeeded) { SetNeedsDisplay (new Rect (0, prow, Math.Max (Frame.Width, 0), Math.Max (prow + 1, 0))); @@ -2424,6 +2439,8 @@ void InsertText (ustring text) return; } + SetWrapModel (); + var line = GetCurrentLine (); historyText.Add (new List> () { new List (line) }, CursorPosition); @@ -2444,6 +2461,9 @@ void InsertText (ustring text) } else { SetNeedsDisplay (new Rect (0, currentRow - topRow, Frame.Width, Math.Max (currentRow - topRow + 1, 0))); } + + UpdateWrapModel (); + return; } @@ -2486,6 +2506,8 @@ void InsertText (ustring text) historyText.Add (new List> () { new List (line) }, CursorPosition, HistoryText.LineStatus.Replaced); + + UpdateWrapModel (); } // The column we are tracking, or -1 if we are not tracking any column @@ -2614,7 +2636,7 @@ public override bool ProcessKey (KeyEvent kb) void RedoChanges () { - if (ReadOnly || wordWrap) + if (ReadOnly) return; historyText.Redo (); @@ -2622,7 +2644,7 @@ void RedoChanges () void UndoChanges () { - if (ReadOnly || wordWrap) + if (ReadOnly) return; historyText.Undo (); @@ -2940,6 +2962,8 @@ bool ProcessBackTab () return ProcessMovePreviousView (); } if (currentColumn > 0) { + SetWrapModel (); + var currentLine = GetCurrentLine (); if (currentLine.Count > 0 && currentLine [currentColumn - 1] == '\t') { @@ -2951,6 +2975,8 @@ bool ProcessBackTab () historyText.Add (new List> () { new List (GetCurrentLine ()) }, CursorPosition, HistoryText.LineStatus.Replaced); } + + UpdateWrapModel (); } DoNeededAction (); return true; @@ -2983,6 +3009,8 @@ bool ProcessReturn () return false; } + SetWrapModel (); + var currentLine = GetCurrentLine (); historyText.Add (new List> () { new List (currentLine) }, CursorPosition); @@ -3003,10 +3031,6 @@ bool ProcessReturn () historyText.Add (addedLines, CursorPosition, HistoryText.LineStatus.Added); - if (wordWrap) { - wrapManager.AddLine (currentRow, currentColumn); - wrapNeeded = true; - } currentRow++; bool fullNeedsDisplay = false; @@ -3029,6 +3053,8 @@ bool ProcessReturn () else SetNeedsDisplay (new Rect (0, currentRow - topRow, 2, Frame.Height)); + UpdateWrapModel (); + DoNeededAction (); return true; } @@ -3037,6 +3063,9 @@ void KillWordBackward () { if (isReadOnly) return; + + SetWrapModel (); + var currentLine = GetCurrentLine (); historyText.Add (new List> () { new List (GetCurrentLine ()) }, CursorPosition); @@ -3047,6 +3076,8 @@ void KillWordBackward () historyText.ReplaceLast (new List> () { new List (GetCurrentLine ()) }, CursorPosition, HistoryText.LineStatus.Replaced); + UpdateWrapModel (); + return; } var newPos = WordBackward (currentColumn, currentRow); @@ -3070,6 +3101,8 @@ void KillWordBackward () historyText.Add (new List> () { new List (GetCurrentLine ()) }, CursorPosition, HistoryText.LineStatus.Replaced); + UpdateWrapModel (); + if (wrapNeeded) { SetNeedsDisplay (); } else { @@ -3082,6 +3115,9 @@ void KillWordForward () { if (isReadOnly) return; + + SetWrapModel (); + var currentLine = GetCurrentLine (); historyText.Add (new List> () { new List (GetCurrentLine ()) }, CursorPosition); @@ -3092,6 +3128,8 @@ void KillWordForward () historyText.ReplaceLast (new List> () { new List (GetCurrentLine ()) }, CursorPosition, HistoryText.LineStatus.Replaced); + UpdateWrapModel (); + return; } var newPos = WordForward (currentColumn, currentRow); @@ -3110,6 +3148,8 @@ void KillWordForward () historyText.Add (new List> () { new List (GetCurrentLine ()) }, CursorPosition, HistoryText.LineStatus.Replaced); + UpdateWrapModel (); + if (wrapNeeded) { SetNeedsDisplay (); } else { @@ -3149,9 +3189,13 @@ void KillToStartOfLine () return; } + SetWrapModel (); + var currentLine = GetCurrentLine (); var setLastWasKill = true; if (currentLine.Count > 0 && currentColumn == 0) { + UpdateWrapModel (); + DeleteTextBackwards (); return; } @@ -3203,7 +3247,12 @@ void KillToStartOfLine () historyText.Add (new List> () { new List (GetCurrentLine ()) }, CursorPosition, HistoryText.LineStatus.Replaced); - SetNeedsDisplay (new Rect (0, currentRow - topRow, Frame.Width, Frame.Height)); + UpdateWrapModel (); + + if (wrapNeeded) + SetNeedsDisplay (); + else + SetNeedsDisplay (new Rect (0, currentRow - topRow, Frame.Width, Frame.Height)); lastWasKill = setLastWasKill; DoNeededAction (); } @@ -3217,9 +3266,13 @@ void KillToEndOfLine () return; } + SetWrapModel (); + var currentLine = GetCurrentLine (); var setLastWasKill = true; if (currentLine.Count > 0 && currentColumn == currentLine.Count) { + UpdateWrapModel (); + DeleteTextForwards (); return; } @@ -3265,7 +3318,12 @@ void KillToEndOfLine () historyText.Add (new List> () { new List (GetCurrentLine ()) }, CursorPosition, HistoryText.LineStatus.Replaced); - SetNeedsDisplay (new Rect (0, currentRow - topRow, Frame.Width, Frame.Height)); + UpdateWrapModel (); + + if (wrapNeeded) + SetNeedsDisplay (); + else + SetNeedsDisplay (new Rect (0, currentRow - topRow, Frame.Width, Frame.Height)); lastWasKill = setLastWasKill; DoNeededAction (); } @@ -3293,6 +3351,9 @@ public void DeleteCharRight () { if (isReadOnly) return; + + SetWrapModel (); + if (selecting) { historyText.Add (new List> () { new List (GetCurrentLine ()) }, CursorPosition, HistoryText.LineStatus.Original); @@ -3304,11 +3365,18 @@ public void DeleteCharRight () historyText.Add (new List> () { new List (currentLine) }, CursorPosition, HistoryText.LineStatus.Replaced); + UpdateWrapModel (); + return; } if (DeleteTextForwards ()) { + UpdateWrapModel (); + return; } + + UpdateWrapModel (); + DoNeededAction (); } @@ -3319,6 +3387,9 @@ public void DeleteCharLeft () { if (isReadOnly) return; + + SetWrapModel (); + if (selecting) { historyText.Add (new List> () { new List (GetCurrentLine ()) }, CursorPosition, HistoryText.LineStatus.Original); @@ -3330,11 +3401,18 @@ public void DeleteCharLeft () historyText.Add (new List> () { new List (currentLine) }, CursorPosition, HistoryText.LineStatus.Replaced); + UpdateWrapModel (); + return; } if (DeleteTextBackwards ()) { + UpdateWrapModel (); + return; } + + UpdateWrapModel (); + DoNeededAction (); } @@ -3443,7 +3521,7 @@ bool InsertText (KeyEvent kb) if (isReadOnly) return true; - var curPos = CursorPosition; + SetWrapModel (); historyText.Add (new List> () { new List (GetCurrentLine ()) }, CursorPosition); @@ -3465,6 +3543,8 @@ bool InsertText (KeyEvent kb) historyText.Add (new List> () { new List (GetCurrentLine ()) }, CursorPosition, HistoryText.LineStatus.Replaced); + UpdateWrapModel (); + return true; } @@ -3517,10 +3597,15 @@ void DoNeededAction () bool DeleteTextForwards () { + SetWrapModel (); + var currentLine = GetCurrentLine (); if (currentColumn == currentLine.Count) { - if (currentRow + 1 == model.Count) + if (currentRow + 1 == model.Count) { + UpdateWrapModel (); + return true; + } historyText.Add (new List> () { new List (currentLine) }, CursorPosition); @@ -3541,8 +3626,12 @@ bool DeleteTextForwards () if (wordWrap && wrapManager.RemoveLine (currentRow, currentColumn, out _)) { wrapNeeded = true; } - var sr = currentRow - topRow; - SetNeedsDisplay (new Rect (0, sr, Frame.Width, sr + 1)); + if (wrapNeeded) { + SetNeedsDisplay (); + } else { + var sr = currentRow - topRow; + SetNeedsDisplay (new Rect (0, sr, Frame.Width, sr + 1)); + } } else { historyText.Add (new List> () { new List (currentLine) }, CursorPosition); @@ -3554,15 +3643,24 @@ bool DeleteTextForwards () if (wordWrap && wrapManager.RemoveAt (currentRow, currentColumn)) { wrapNeeded = true; } - var r = currentRow - topRow; - SetNeedsDisplay (new Rect (currentColumn - leftColumn, r, Frame.Width, r + 1)); + + if (wrapNeeded) { + SetNeedsDisplay (); + } else { + var r = currentRow - topRow; + SetNeedsDisplay (new Rect (currentColumn - leftColumn, r, Frame.Width, r + 1)); + } } + UpdateWrapModel (); + return false; } bool DeleteTextBackwards () { + SetWrapModel (); + if (currentColumn > 0) { // Delete backwards var currentLine = GetCurrentLine (); @@ -3619,6 +3717,8 @@ bool DeleteTextBackwards () SetNeedsDisplay (); } + UpdateWrapModel (); + return false; } diff --git a/UnitTests/TextViewTests.cs b/UnitTests/TextViewTests.cs index 5584e66bb5..e5ab10cf55 100644 --- a/UnitTests/TextViewTests.cs +++ b/UnitTests/TextViewTests.cs @@ -5561,14 +5561,12 @@ public void HistoryText_Undo_Redo_Disabled_On_WordWrap () Assert.Equal (3, tv.Lines); Assert.Equal (new Point (1, 1), tv.CursorPosition); - // Undo is disabled Assert.True (tv.ProcessKey (new KeyEvent (Key.Z | Key.CtrlMask, new KeyModifiers ()))); - Assert.Equal ($"This is the {Environment.NewLine}athird line.{Environment.NewLine}", tv.Text); + Assert.Equal ($"This is the {Environment.NewLine}third line.{Environment.NewLine}", tv.Text); Assert.Equal (3, tv.Lines); - Assert.Equal (new Point (1, 1), tv.CursorPosition); + Assert.Equal (new Point (0, 1), tv.CursorPosition); Assert.True (tv.IsDirty); - // Redo is disabled Assert.True (tv.ProcessKey (new KeyEvent (Key.R | Key.CtrlMask, new KeyModifiers ()))); Assert.Equal ($"This is the {Environment.NewLine}athird line.{Environment.NewLine}", tv.Text); Assert.Equal (3, tv.Lines);