Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
041cfcf
Fixes #4986. Navigating with Viewport.Y greater than zero will cause …
BDisp Apr 17, 2026
ce2ce48
Fixes #4990. Navigating with Viewport.X greater than zero will cause …
BDisp Apr 17, 2026
6ca8e82
Merge branch 'develop' into textview-remaining-issues-fix
tig Apr 17, 2026
e9f3cd8
Merge branch 'develop' into textview-remaining-issues-fix
tig Apr 17, 2026
0f27375
Fixes #4994 - Navigating left and right while holding down the Ctrl k…
BDisp Apr 17, 2026
8074d0f
Merge branch 'develop' into textview-remaining-issues-fix
BDisp Apr 17, 2026
769edb0
Merge branch 'develop' into textview-remaining-issues-fix
BDisp Apr 17, 2026
ce4645e
Merge branch 'develop' into textview-remaining-issues-fix
tig Apr 18, 2026
14afefc
Fixes #4998. TextView.UpdateContentSize isn't working correctly on in…
BDisp Apr 18, 2026
3ca7567
Fixes #4999. TextView with hidden cursor due scrolling pressing any C…
BDisp Apr 18, 2026
2496629
Update Tests/UnitTestsParallelizable/Views/TextView.NavigationTests.cs
tig Apr 19, 2026
f6f0164
Merge branch 'develop' into textview-remaining-issues-fix
BDisp Apr 19, 2026
40d4147
Merge branch 'develop' into textview-remaining-issues-fix
BDisp Apr 19, 2026
c336758
Fixes #4891. DoDrawComplete should ignore scrolled Viewport.Location …
BDisp Apr 19, 2026
79c079c
Merge branch 'develop' into textview-remaining-issues-fix
tig Apr 19, 2026
2e3a3d9
Merge branch 'develop' into textview-remaining-issues-fix
BDisp Apr 20, 2026
5dc6b6b
Clarify comment related to deleted
BDisp Apr 19, 2026
97d63ff
Fix MoveUp() and add more unit tests
BDisp Apr 19, 2026
2d304d1
Test that proves despite does not change viewport position but does s…
BDisp Apr 19, 2026
daba04a
Fix MoveLeft method and add a test
BDisp Apr 19, 2026
3b57826
Fix MoveWordLeft and add unit test
BDisp Apr 19, 2026
ae4e7c7
Fix MoveWordRight and add unit test
BDisp Apr 19, 2026
0dbccbc
Simplify MoveRight code
BDisp Apr 19, 2026
b552436
Fix DeleteCharLeft invoke ContentChanged twice
BDisp Apr 20, 2026
767020d
Fix ShouldInvalidateMaxWidthCache to use full size
BDisp Apr 20, 2026
ac3dbc4
Remove unnecessary LINQ in _cachedMaxWidthPerLine
BDisp Apr 20, 2026
ab0ece7
Move ShouldInvalidateMaxWidthCache tests
BDisp Apr 20, 2026
cecd5b6
Fix ArgumentOutOfRangeException on DeleteTextLeft
BDisp Apr 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion Terminal.Gui/Views/TextInput/TextView/TextView.Movement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ private bool MoveRight ()
{
CurrentColumn++;

if (CurrentColumn >= currentLine.Count || TextModel.CursorColumn (TextModel.CellsToStringList (currentLine), CurrentColumn, TabWidth, out _, out _) >= Viewport.Width)
if (CurrentColumn >= currentLine.Count || TextModel.CursorColumn (TextModel.CellsToStringList (currentLine), CurrentColumn, TabWidth, out _, out _) >= Viewport.X + Viewport.Width)
{
SetNeedsDraw ();
}
Comment on lines 301 to 308

Copilot AI Apr 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MoveRight() adds a visibility check (Viewport.X > 0 && CurrentColumn < Viewport.X), but Viewport.X is a display-column offset while CurrentColumn is a cell index. This will break with tabs/wide graphemes and can mark the cursor as "left of viewport" when it isn't. Use the cursor's display column (via TextModel.CursorColumn(...)) for comparisons against Viewport.X.

Copilot uses AI. Check for mistakes.
Expand Down Expand Up @@ -393,6 +393,11 @@ private bool MoveWordLeft ()
CurrentRow = newPos.Value.row;
}

if (CurrentRow < Viewport.Y || CurrentColumn < Viewport.X || CurrentColumn >= Viewport.X + Viewport.Width)
{
SetNeedsDraw ();
}

Comment thread
BDisp marked this conversation as resolved.
Outdated
DoNeededAction ();

return true;
Expand All @@ -408,6 +413,11 @@ private bool MoveWordRight ()
CurrentRow = newPos.Value.row;
}

if (CurrentRow >= Viewport.Y + Viewport.Height || CurrentColumn >= Viewport.X + Viewport.Width || CurrentColumn < Viewport.X)
{
SetNeedsDraw ();
}

Comment thread
BDisp marked this conversation as resolved.
Outdated
DoNeededAction ();

return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ private void AdjustViewport ()
}
need = true;
}
else if (!_wordWrap && (CurrentColumn - Viewport.X + 1 > Viewport.Width || dSize.size + 1 >= Viewport.Width))
else if (!_wordWrap && (CurrentColumn - Viewport.X + 1 > Viewport.Width || dSize.size - Viewport.X + 1 >= Viewport.Width))
{
Viewport = Viewport with { X = TextModel.CalculateLeftColumn (line, Viewport.X, CurrentColumn, Viewport.Width, TabWidth) };
need = true;
Expand All @@ -140,11 +140,6 @@ private void AdjustViewport ()
Viewport = Viewport with { Y = Math.Min (Math.Max (CurrentRow - Viewport.Height + 1, 0), CurrentRow) };
need = true;
}
else if (!WordWrap && Viewport.Y > 0 && CurrentRow - Viewport.Height + 1 < Viewport.Y)
{
Viewport = Viewport with { Y = Math.Max (Viewport.Y - 1, 0) };
need = true;
}

if (need)
{
Expand Down
161 changes: 161 additions & 0 deletions Tests/UnitTestsParallelizable/Views/TextView.NavigationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -318,4 +318,165 @@ public void CtrlB_Moves_Backward_One_Character ()
Assert.Equal (Point.Empty, tv.InsertionPoint);
Assert.False (tv.IsSelecting);
}

[Fact]
public void CursorRight_At_NearTheEndOfLine_With_ViewportY_Greater_Than_Zero_Does_Not_Scroll_Up ()
{
// Test that pressing CursorRight at near the end of line does not scroll up if Viewport.Y > 0
TextView tv = new ()
{
Width = 10,
Height = 3,
Text = "Line1.\nLine2.\nLine3.\nLine4.\nLine5."
};
tv.BeginInit ();
tv.EndInit ();

// Scroll to second line and set insertion point at near the end of line
tv.Viewport = tv.Viewport with { Y = 1 };
tv.InsertionPoint = new Point (5, 1);
Assert.Equal (new Point (0, 1), tv.Viewport.Location);
Assert.Equal (new Point (5, 1), tv.InsertionPoint);
Assert.False (tv.WordWrap);

// Press CursorRight - should not scroll up since we aren't already at first line
Assert.True (tv.NewKeyDownEvent (Key.CursorRight));
Assert.Equal (new Point (0, 1), tv.Viewport.Location);
Assert.Equal (new Point (6, 1), tv.InsertionPoint);
}

[Fact]
public void CursorRight_At_BeforeNearTheEndOfLine_With_ViewportX_Greater_Than_Zero_Does_Not_Scroll_Left ()
{
// Test that pressing CursorRight at neat the end of line does not scroll left if Viewport.X > 0
Comment thread
tig marked this conversation as resolved.
Outdated
TextView tv = new ()
{
Width = 10, Height = 3,
Text = "Line1 with more long text.\nLine2.\nLine3.\nLine4."
};
tv.BeginInit ();
tv.EndInit ();

// Scroll to the column 10 and set insertion point at before near the end of line
tv.Viewport = tv.Viewport with { X = 10 };
tv.InsertionPoint = new Point (17, 0);
Assert.Equal (new Point (10, 0), tv.Viewport.Location);
Assert.Equal (new Point (17, 0), tv.InsertionPoint);
Assert.False (tv.WordWrap);
Assert.True (tv.NeedsDraw);

// Clear NeedsDraw to isolate the effect of CursorRight key press
tv.ClearNeedsDraw ();
Assert.False (tv.NeedsDraw);

// Press CursorRight - should not scroll left since we aren't already at the end of the line
Assert.True (tv.NewKeyDownEvent (Key.CursorRight));
Assert.Equal (new Point (10, 0), tv.Viewport.Location);
Assert.Equal (new Point (18, 0), tv.InsertionPoint);
Assert.False (tv.NeedsDraw);
}

[Fact]
public void CursorRight_With_CtrlKey_Pressed_At_BeforeNearTheEndOfLine_Scrolls_Right_Next_Word ()
{
// Test that pressing Ctrl+CursorRight at neat the end of line scrolls right to next word
TextView tv = new () { Width = 10, Height = 3, Text = "Line1 with more long text.\nLine2.\nLine3.\nLine4." };
tv.BeginInit ();
tv.EndInit ();

// Scroll to the column 10 and set insertion point at before near the end of line
tv.Viewport = tv.Viewport with { X = 10 };
tv.InsertionPoint = new Point (17, 0);
Assert.Equal (new Point (10, 0), tv.Viewport.Location);
Assert.Equal (new Point (17, 0), tv.InsertionPoint);
Assert.True (tv.NeedsDraw);

// Clear NeedsDraw to isolate the effect of Ctrl+CursorRight key press
tv.ClearNeedsDraw ();
Assert.False (tv.NeedsDraw);

// Press Ctrl+CursorRight - should scroll right to next word
Assert.True (tv.NewKeyDownEvent (Key.CursorRight.WithCtrl));
Assert.Equal (new Point (12, 0), tv.Viewport.Location);
Assert.Equal (new Point (21, 0), tv.InsertionPoint);
Assert.True (tv.NeedsDraw);
}

[Fact]
public void CursorRight_With_CtrlKey_Pressed_At_TheEndOfLine_Scrolls_Left_To_StartOfNextLine ()
{
// Test that pressing Ctrl+CursorRight at the end of line scrolls right to start of next line
TextView tv = new () { Width = 10, Height = 3, Text = "Line1 with more long text.\nLine2.\nLine3.\nLine4." };
tv.BeginInit ();
tv.EndInit ();

// Scroll to the column 17 and set insertion point at the end of line
tv.Viewport = tv.Viewport with { X = 17 };
tv.InsertionPoint = new Point (26, 0);
Assert.Equal (new Point (17, 0), tv.Viewport.Location);
Assert.Equal (new Point (26, 0), tv.InsertionPoint);
Assert.True (tv.NeedsDraw);

// Clear NeedsDraw to isolate the effect of Ctrl+CursorRight key press
tv.ClearNeedsDraw ();
Assert.False (tv.NeedsDraw);

// Press Ctrl+CursorRight - should scroll right to start of next line
Assert.True (tv.NewKeyDownEvent (Key.CursorRight.WithCtrl));
Assert.Equal (new Point (0, 0), tv.Viewport.Location);
Assert.Equal (new Point (0, 1), tv.InsertionPoint);
Assert.True (tv.NeedsDraw);
}

[Fact]
public void CursorLeft_With_CtrlKey_Pressed_At_BeforeNearTheStartOfLine_Scrolls_Left_Previous_Word ()
{
// Test that pressing Ctrl+CursorLeft at neat the start of line scrolls left to previous word
TextView tv = new () { Width = 10, Height = 3, Text = "Line1 with more long text.\nLine2.\nLine3.\nLine4." };
tv.BeginInit ();
tv.EndInit ();

// Scroll to the column 2 and set insertion point at before near the start of line
tv.Viewport = tv.Viewport with { X = 2 };
tv.InsertionPoint = new Point (4, 0);
Assert.Equal (new Point (2, 0), tv.Viewport.Location);
Assert.Equal (new Point (4, 0), tv.InsertionPoint);
Assert.True (tv.NeedsDraw);

// Clear NeedsDraw to isolate the effect of Ctrl+CursorLeft key press
tv.ClearNeedsDraw ();
Assert.False (tv.NeedsDraw);

// Press Ctrl+CursorLeft - should scroll left to previous word
Assert.True (tv.NewKeyDownEvent (Key.CursorLeft.WithCtrl));
Assert.Equal (new Point (0, 0), tv.Viewport.Location);
Assert.Equal (new Point (0, 0), tv.InsertionPoint);
Assert.True (tv.NeedsDraw);
}

[Fact]
public void CursorLeft_With_CtrlKey_Pressed_At_TheStartOfLine_Scrolls_Right_To_EndOfPreviousLine ()
{
// Test that pressing Ctrl+CursorLeft at the start of line scrolls left to end of previous line
TextView tv = new () { Width = 10, Height = 3, Text = "Line1 with more long text.\nLine2.\nLine3.\nLine4." };
tv.BeginInit ();
tv.EndInit ();

// Scroll to the column 0 and set insertion point at the start of line
tv.Viewport = tv.Viewport with { X = 0, Y = 1 };
tv.InsertionPoint = new Point (0, 1);
Assert.Equal (new Point (0, 1), tv.Viewport.Location);
Assert.Equal (new Point (0, 1), tv.InsertionPoint);
Assert.True (tv.NeedsDraw);

// Clear NeedsDraw to isolate the effect of Ctrl+CursorLeft key press
tv.ClearNeedsDraw ();
Assert.False (tv.NeedsDraw);

// Press Ctrl+CursorLeft - should scroll left to end of previous line
Assert.True (tv.NewKeyDownEvent (Key.CursorLeft.WithCtrl));
Assert.Equal (new Point (17, 0), tv.Viewport.Location);
Assert.Equal (new Point (26, 0), tv.InsertionPoint);
Assert.True (tv.NeedsDraw);
}
}
Loading