Skip to content

Commit

Permalink
Fixes #1963. Only remove one character on backspace when wordwrap is …
Browse files Browse the repository at this point in the history
…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]>
  • Loading branch information
3 people authored Sep 5, 2022
1 parent c6af956 commit bbbacf4
Show file tree
Hide file tree
Showing 2 changed files with 234 additions and 13 deletions.
21 changes: 8 additions & 13 deletions Terminal.Gui/Views/TextView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3178,14 +3178,14 @@ void KillWordBackward ()
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;
Expand Down Expand Up @@ -3235,7 +3235,7 @@ void KillWordForward ()
restCount = currentLine.Count - currentColumn;
currentLine.RemoveRange (currentColumn, restCount);
}
if (wordWrap && wrapManager.RemoveRange (currentRow, currentColumn, restCount)) {
if (wordWrap) {
wrapNeeded = true;
}

Expand Down Expand Up @@ -3717,7 +3717,7 @@ bool DeleteTextForwards ()
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) {
Expand All @@ -3734,7 +3734,7 @@ bool DeleteTextForwards ()
historyText.Add (new List<List<Rune>> () { new List<Rune> (currentLine) }, CursorPosition,
HistoryText.LineStatus.Replaced);

if (wordWrap && wrapManager.RemoveAt (currentRow, currentColumn)) {
if (wordWrap) {
wrapNeeded = true;
}

Expand Down Expand Up @@ -3762,7 +3762,7 @@ bool DeleteTextBackwards ()
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--;
Expand Down Expand Up @@ -3794,20 +3794,15 @@ bool DeleteTextBackwards ()
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--;

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 ();
}

Expand Down
226 changes: 226 additions & 0 deletions UnitTests/TextViewTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6044,5 +6044,231 @@ This is the second line.
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);
}
}
}

0 comments on commit bbbacf4

Please sign in to comment.