2
0
Эх сурвалжийг харах

Fixes #1963. Only remove one character on backspace when wordwrap is on (#1964)

* Fixed double delete when wordwrap is enabled

* Fixes #1963. Deletes multiple characters editing a TextView with WordWrap enabled.

Co-authored-by: BDisp <[email protected]>
Co-authored-by: Tig Kindel <[email protected]>
Jeff Greene 2 жил өмнө
parent
commit
bbbacf4514

+ 8 - 13
Terminal.Gui/Views/TextView.cs

@@ -3178,14 +3178,14 @@ namespace Terminal.Gui {
 			if (newPos.HasValue && currentRow == newPos.Value.row) {
 				var restCount = currentColumn - newPos.Value.col;
 				currentLine.RemoveRange (newPos.Value.col, restCount);
-				if (wordWrap && wrapManager.RemoveRange (currentRow, newPos.Value.col, restCount)) {
+				if (wordWrap) {
 					wrapNeeded = true;
 				}
 				currentColumn = newPos.Value.col;
 			} else if (newPos.HasValue) {
 				var restCount = currentLine.Count - currentColumn;
 				currentLine.RemoveRange (currentColumn, restCount);
-				if (wordWrap && wrapManager.RemoveRange (currentRow, currentColumn, restCount)) {
+				if (wordWrap) {
 					wrapNeeded = true;
 				}
 				currentColumn = newPos.Value.col;
@@ -3235,7 +3235,7 @@ namespace Terminal.Gui {
 				restCount = currentLine.Count - currentColumn;
 				currentLine.RemoveRange (currentColumn, restCount);
 			}
-			if (wordWrap && wrapManager.RemoveRange (currentRow, currentColumn, restCount)) {
+			if (wordWrap) {
 				wrapNeeded = true;
 			}
 
@@ -3717,7 +3717,7 @@ namespace Terminal.Gui {
 				historyText.Add (new List<List<Rune>> () { new List<Rune> (currentLine) }, CursorPosition,
 					HistoryText.LineStatus.Replaced);
 
-				if (wordWrap && wrapManager.RemoveLine (currentRow, currentColumn, out _)) {
+				if (wordWrap) {
 					wrapNeeded = true;
 				}
 				if (wrapNeeded) {
@@ -3734,7 +3734,7 @@ namespace Terminal.Gui {
 				historyText.Add (new List<List<Rune>> () { new List<Rune> (currentLine) }, CursorPosition,
 					HistoryText.LineStatus.Replaced);
 
-				if (wordWrap && wrapManager.RemoveAt (currentRow, currentColumn)) {
+				if (wordWrap) {
 					wrapNeeded = true;
 				}
 
@@ -3762,7 +3762,7 @@ namespace Terminal.Gui {
 				historyText.Add (new List<List<Rune>> () { new List<Rune> (currentLine) }, CursorPosition);
 
 				currentLine.RemoveAt (currentColumn - 1);
-				if (wordWrap && wrapManager.RemoveAt (currentRow, currentColumn - 1)) {
+				if (wordWrap) {
 					wrapNeeded = true;
 				}
 				currentColumn--;
@@ -3794,8 +3794,7 @@ namespace Terminal.Gui {
 				var prevCount = prevRow.Count;
 				model.GetLine (prowIdx).AddRange (GetCurrentLine ());
 				model.RemoveLine (currentRow);
-				bool lineRemoved = false;
-				if (wordWrap && wrapManager.RemoveLine (currentRow, currentColumn, out lineRemoved, false)) {
+				if (wordWrap) {
 					wrapNeeded = true;
 				}
 				currentRow--;
@@ -3803,11 +3802,7 @@ namespace Terminal.Gui {
 				historyText.Add (new List<List<Rune>> () { GetCurrentLine () }, new Point (currentColumn, prowIdx),
 					HistoryText.LineStatus.Replaced);
 
-				if (wrapNeeded && !lineRemoved) {
-					currentColumn = Math.Max (prevCount - 1, 0);
-				} else {
-					currentColumn = prevCount;
-				}
+				currentColumn = prevCount;
 				SetNeedsDisplay ();
 			}
 

+ 226 - 0
UnitTests/TextViewTests.cs

@@ -6044,5 +6044,231 @@ d
 line.
 ", output);
 		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void DeleteTextBackwards_WordWrap_False_Return_Undo ()
+		{
+			const string text = "This is the first line.\nThis is the second line.\n";
+			var tv = new TextView () {
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				Text = text
+			};
+			var envText = tv.Text;
+			Application.Top.Add (tv);
+			Application.Begin (Application.Top);
+
+			Assert.False (tv.WordWrap);
+			Assert.Equal (Point.Empty, tv.CursorPosition);
+			GraphViewTests.AssertDriverContentsWithFrameAre (@"
+This is the first line. 
+This is the second line.
+", output);
+
+			tv.CursorPosition = new Point (3, 0);
+			Assert.Equal (new Point (3, 0), tv.CursorPosition);
+			Assert.True (tv.ProcessKey (new KeyEvent (Key.Backspace, new KeyModifiers ())));
+			tv.Redraw (tv.Bounds);
+			Assert.Equal (new Point (2, 0), tv.CursorPosition);
+			GraphViewTests.AssertDriverContentsWithFrameAre (@"
+Ths is the first line.  
+This is the second line.
+", output);
+
+			tv.CursorPosition = new Point (0, 1);
+			Assert.Equal (new Point (0, 1), tv.CursorPosition);
+			Assert.True (tv.ProcessKey (new KeyEvent (Key.Backspace, new KeyModifiers ())));
+			tv.Redraw (tv.Bounds);
+			Assert.Equal (new Point (22, 0), tv.CursorPosition);
+			GraphViewTests.AssertDriverContentsWithFrameAre (@"
+Ths is the first line.This is the second line.
+", output);
+
+			Assert.True (tv.ProcessKey (new KeyEvent (Key.Enter, new KeyModifiers ())));
+			tv.Redraw (tv.Bounds);
+			Assert.Equal (new Point (0, 1), tv.CursorPosition);
+			GraphViewTests.AssertDriverContentsWithFrameAre (@"
+Ths is the first line.  
+This is the second line.
+", output);
+
+			while (tv.Text != envText) {
+				Assert.True (tv.ProcessKey (new KeyEvent (Key.Z | Key.CtrlMask, new KeyModifiers ())));
+			}
+			Assert.Equal (envText, tv.Text);
+			Assert.Equal (new Point (3, 0), tv.CursorPosition);
+			Assert.False (tv.IsDirty);
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void DeleteTextBackwards_WordWrap_True_Return_Undo ()
+		{
+			const string text = "This is the first line.\nThis is the second line.\n";
+			var tv = new TextView () {
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				Text = text,
+				WordWrap = true
+			};
+			var envText = tv.Text;
+			Application.Top.Add (tv);
+			Application.Begin (Application.Top);
+
+			Assert.True (tv.WordWrap);
+			Assert.Equal (Point.Empty, tv.CursorPosition);
+			GraphViewTests.AssertDriverContentsWithFrameAre (@"
+This is the first line. 
+This is the second line.
+", output);
+
+			tv.CursorPosition = new Point (3, 0);
+			Assert.Equal (new Point (3, 0), tv.CursorPosition);
+			Assert.True (tv.ProcessKey (new KeyEvent (Key.Backspace, new KeyModifiers ())));
+			tv.Redraw (tv.Bounds);
+			Assert.Equal (new Point (2, 0), tv.CursorPosition);
+			GraphViewTests.AssertDriverContentsWithFrameAre (@"
+Ths is the first line.  
+This is the second line.
+", output);
+
+			tv.CursorPosition = new Point (0, 1);
+			Assert.Equal (new Point (0, 1), tv.CursorPosition);
+			Assert.True (tv.ProcessKey (new KeyEvent (Key.Backspace, new KeyModifiers ())));
+			tv.Redraw (tv.Bounds);
+			Assert.Equal (new Point (22, 0), tv.CursorPosition);
+			GraphViewTests.AssertDriverContentsWithFrameAre (@"
+Ths is the first line.This is the second line.
+", output);
+
+			Assert.True (tv.ProcessKey (new KeyEvent (Key.Enter, new KeyModifiers ())));
+			tv.Redraw (tv.Bounds);
+			Assert.Equal (new Point (0, 1), tv.CursorPosition);
+			GraphViewTests.AssertDriverContentsWithFrameAre (@"
+Ths is the first line.  
+This is the second line.
+", output);
+
+			while (tv.Text != envText) {
+				Assert.True (tv.ProcessKey (new KeyEvent (Key.Z | Key.CtrlMask, new KeyModifiers ())));
+			}
+			Assert.Equal (envText, tv.Text);
+			Assert.Equal (new Point (3, 0), tv.CursorPosition);
+			Assert.False (tv.IsDirty);
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void DeleteTextForwards_WordWrap_False_Return_Undo ()
+		{
+			const string text = "This is the first line.\nThis is the second line.\n";
+			var tv = new TextView () {
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				Text = text
+			};
+			var envText = tv.Text;
+			Application.Top.Add (tv);
+			Application.Begin (Application.Top);
+
+			Assert.False (tv.WordWrap);
+			Assert.Equal (Point.Empty, tv.CursorPosition);
+			GraphViewTests.AssertDriverContentsWithFrameAre (@"
+This is the first line. 
+This is the second line.
+", output);
+
+			tv.CursorPosition = new Point (2, 0);
+			Assert.Equal (new Point (2, 0), tv.CursorPosition);
+			Assert.True (tv.ProcessKey (new KeyEvent (Key.DeleteChar, new KeyModifiers ())));
+			tv.Redraw (tv.Bounds);
+			Assert.Equal (new Point (2, 0), tv.CursorPosition);
+			GraphViewTests.AssertDriverContentsWithFrameAre (@"
+Ths is the first line.  
+This is the second line.
+", output);
+
+			tv.CursorPosition = new Point (22, 0);
+			Assert.Equal (new Point (22, 0), tv.CursorPosition);
+			Assert.True (tv.ProcessKey (new KeyEvent (Key.DeleteChar, new KeyModifiers ())));
+			tv.Redraw (tv.Bounds);
+			Assert.Equal (new Point (22, 0), tv.CursorPosition);
+			GraphViewTests.AssertDriverContentsWithFrameAre (@"
+Ths is the first line.This is the second line.
+", output);
+
+			Assert.True (tv.ProcessKey (new KeyEvent (Key.Enter, new KeyModifiers ())));
+			tv.Redraw (tv.Bounds);
+			Assert.Equal (new Point (0, 1), tv.CursorPosition);
+			GraphViewTests.AssertDriverContentsWithFrameAre (@"
+Ths is the first line.  
+This is the second line.
+", output);
+
+			while (tv.Text != envText) {
+				Assert.True (tv.ProcessKey (new KeyEvent (Key.Z | Key.CtrlMask, new KeyModifiers ())));
+			}
+			Assert.Equal (envText, tv.Text);
+			Assert.Equal (new Point (2, 0), tv.CursorPosition);
+			Assert.False (tv.IsDirty);
+		}
+
+		[Fact]
+		[AutoInitShutdown]
+		public void DeleteTextForwards_WordWrap_True_Return_Undo ()
+		{
+			const string text = "This is the first line.\nThis is the second line.\n";
+			var tv = new TextView () {
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				Text = text,
+				WordWrap = true
+			};
+			var envText = tv.Text;
+			Application.Top.Add (tv);
+			Application.Begin (Application.Top);
+
+			Assert.True (tv.WordWrap);
+			Assert.Equal (Point.Empty, tv.CursorPosition);
+			GraphViewTests.AssertDriverContentsWithFrameAre (@"
+This is the first line. 
+This is the second line.
+", output);
+
+			tv.CursorPosition = new Point (2, 0);
+			Assert.Equal (new Point (2, 0), tv.CursorPosition);
+			Assert.True (tv.ProcessKey (new KeyEvent (Key.DeleteChar, new KeyModifiers ())));
+			tv.Redraw (tv.Bounds);
+			Assert.Equal (new Point (2, 0), tv.CursorPosition);
+			GraphViewTests.AssertDriverContentsWithFrameAre (@"
+Ths is the first line.  
+This is the second line.
+", output);
+
+			tv.CursorPosition = new Point (22, 0);
+			Assert.Equal (new Point (22, 0), tv.CursorPosition);
+			Assert.True (tv.ProcessKey (new KeyEvent (Key.DeleteChar, new KeyModifiers ())));
+			tv.Redraw (tv.Bounds);
+			Assert.Equal (new Point (22, 0), tv.CursorPosition);
+			GraphViewTests.AssertDriverContentsWithFrameAre (@"
+Ths is the first line.This is the second line.
+", output);
+
+			Assert.True (tv.ProcessKey (new KeyEvent (Key.Enter, new KeyModifiers ())));
+			tv.Redraw (tv.Bounds);
+			Assert.Equal (new Point (0, 1), tv.CursorPosition);
+			GraphViewTests.AssertDriverContentsWithFrameAre (@"
+Ths is the first line.  
+This is the second line.
+", output);
+
+			while (tv.Text != envText) {
+				Assert.True (tv.ProcessKey (new KeyEvent (Key.Z | Key.CtrlMask, new KeyModifiers ())));
+			}
+			Assert.Equal (envText, tv.Text);
+			Assert.Equal (new Point (2, 0), tv.CursorPosition);
+			Assert.False (tv.IsDirty);
+		}
 	}
 }