Skip to content
This repository was archived by the owner on Jan 21, 2026. It is now read-only.

Commit 5c20cf9

Browse files
authored
Fix console issues with deleting characters (#471)
1 parent c28ef49 commit 5c20cf9

File tree

4 files changed

+145
-6
lines changed

4 files changed

+145
-6
lines changed

src/Microsoft.Repl/Input/InputManager.cs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ public class InputManager : IInputManager
1616
private readonly Dictionary<ConsoleKey, Dictionary<ConsoleModifiers, AsyncKeyPressHandler>> _handlers = new Dictionary<ConsoleKey, Dictionary<ConsoleModifiers, AsyncKeyPressHandler>>();
1717
private readonly List<char> _inputBuffer = new List<char>();
1818

19+
public InputManager() { }
20+
21+
/// <summary>
22+
/// For testing purposes only
23+
/// </summary>
24+
internal InputManager(string initialInput, int initialPosition)
25+
{
26+
_inputBuffer.AddRange(initialInput);
27+
CaretPosition = initialPosition;
28+
}
29+
30+
1931
public bool IsOverwriteMode { get; set; }
2032

2133
public int CaretPosition { get; private set; }
@@ -88,31 +100,35 @@ public void RemoveCurrentCharacter(IShellState state)
88100
{
89101
state = state ?? throw new ArgumentNullException(nameof(state));
90102

91-
if (CaretPosition == _inputBuffer.Count)
103+
int caret = CaretPosition;
104+
if (caret == _inputBuffer.Count)
92105
{
93106
return;
94107
}
95108

96109
List<char> update = _inputBuffer.ToList();
97-
update.RemoveAt(CaretPosition);
110+
update.RemoveAt(caret);
98111
state.ConsoleManager.IsCaretVisible = false;
99112
SetInput(state, update);
113+
state.MoveCarets(caret - CaretPosition);
100114
state.ConsoleManager.IsCaretVisible = true;
101115
}
102116

103117
public void RemovePreviousCharacter(IShellState state)
104118
{
105119
state = state ?? throw new ArgumentNullException(nameof(state));
106120

107-
if (CaretPosition == 0)
121+
int caret = CaretPosition;
122+
if (caret == 0)
108123
{
109124
return;
110125
}
111126

112127
List<char> update = _inputBuffer.ToList();
113-
update.RemoveAt(CaretPosition - 1);
128+
update.RemoveAt(caret - 1);
114129
state.ConsoleManager.IsCaretVisible = false;
115130
SetInput(state, update);
131+
state.MoveCarets(caret - CaretPosition - 1);
116132
state.ConsoleManager.IsCaretVisible = true;
117133
}
118134

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System.Runtime.CompilerServices;
5+
6+
[assembly: InternalsVisibleTo("Microsoft.Repl.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
47
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "This is done commonly throughout the codebase to catch unexpected errors.")]
8+

test/Microsoft.HttpRepl.Fakes/MockedShellState.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public class MockedShellState : IShellState
1818
{
1919
private readonly ShellState _shellState;
2020
private readonly StringBuilder _output = new StringBuilder();
21-
public MockedShellState()
21+
public MockedShellState(IInputManager inputManager = null)
2222
{
2323
DefaultCommandDispatcher<object> defaultCommandDispatcher = DefaultCommandDispatcher.Create(x => { }, new object());
2424
Mock<IConsoleManager> mockedConsoleManager = new Mock<IConsoleManager>();
@@ -28,7 +28,7 @@ public MockedShellState()
2828
mockedConsoleManager.Setup(x => x.Write(It.IsAny<string>())).Callback((string s) => _output.Append(s));
2929
mockedConsoleManager.Setup(x => x.WriteLine(It.IsAny<string>())).Callback((string s) => _output.AppendLine(s));
3030

31-
_shellState = new ShellState(defaultCommandDispatcher, consoleManager: mockedConsoleManager.Object);
31+
_shellState = new ShellState(defaultCommandDispatcher, inputManager: inputManager, consoleManager: mockedConsoleManager.Object);
3232
}
3333

3434
public string ErrorMessage { get; private set; }
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using Microsoft.HttpRepl.Fakes;
2+
using Microsoft.Repl.Input;
3+
using Xunit;
4+
5+
namespace Microsoft.Repl.Tests
6+
{
7+
public class InputManagerTests
8+
{
9+
[Fact]
10+
public void RemovePreviousCharacter_AtBeginning_DoesNothing()
11+
{
12+
// Arrange
13+
string initialInput = "echo on";
14+
int initialPosition = 0;
15+
InputManager inputManager = new(initialInput, initialPosition);
16+
MockedShellState mockedShellState = new(inputManager);
17+
18+
// Act
19+
inputManager.RemovePreviousCharacter(mockedShellState);
20+
21+
// Assert
22+
Assert.Equal(initialPosition, inputManager.CaretPosition);
23+
Assert.Equal(initialInput, inputManager.GetCurrentBuffer());
24+
}
25+
26+
[Fact]
27+
public void RemovePreviousCharacter_AtEnd_RemovesLastCharacter()
28+
{
29+
// Arrange
30+
string initialInput = "echo on";
31+
int initialPosition = 7;
32+
string expectedInput = "echo o";
33+
int expectedPosition = 6;
34+
InputManager inputManager = new(initialInput, initialPosition);
35+
MockedShellState mockedShellState = new(inputManager);
36+
37+
// Act
38+
inputManager.RemovePreviousCharacter(mockedShellState);
39+
40+
// Assert
41+
Assert.Equal(expectedPosition, inputManager.CaretPosition);
42+
Assert.Equal(expectedInput, inputManager.GetCurrentBuffer());
43+
}
44+
45+
[Fact]
46+
public void RemovePreviousCharacter_InMiddle_RemovesProperCharacter()
47+
{
48+
// Arrange
49+
string initialInput = "echo on";
50+
int initialPosition = 4;
51+
string expectedInput = "ech on";
52+
int expectedPosition = 3;
53+
InputManager inputManager = new(initialInput, initialPosition);
54+
MockedShellState mockedShellState = new(inputManager);
55+
56+
// Act
57+
inputManager.RemovePreviousCharacter(mockedShellState);
58+
59+
// Assert
60+
Assert.Equal(expectedPosition, inputManager.CaretPosition);
61+
Assert.Equal(expectedInput, inputManager.GetCurrentBuffer());
62+
}
63+
64+
[Fact]
65+
public void RemoveCurrentCharacter_AtEnd_DoesNothing()
66+
{
67+
// Arrange
68+
string initialInput = "echo on";
69+
int initialPosition = 7;
70+
InputManager inputManager = new(initialInput, initialPosition);
71+
MockedShellState mockedShellState = new(inputManager);
72+
73+
// Act
74+
inputManager.RemoveCurrentCharacter(mockedShellState);
75+
76+
// Assert
77+
Assert.Equal(initialPosition, inputManager.CaretPosition);
78+
Assert.Equal(initialInput, inputManager.GetCurrentBuffer());
79+
}
80+
81+
[Fact]
82+
public void RemoveCurrentCharacter_AtBeginning_RemovesFirstCharacter()
83+
{
84+
// Arrange
85+
string initialInput = "echo on";
86+
int initialPosition = 0;
87+
string expectedInput = "cho on";
88+
int expectedPosition = 0;
89+
InputManager inputManager = new(initialInput, initialPosition);
90+
MockedShellState mockedShellState = new(inputManager);
91+
92+
// Act
93+
inputManager.RemoveCurrentCharacter(mockedShellState);
94+
95+
// Assert
96+
Assert.Equal(expectedPosition, inputManager.CaretPosition);
97+
Assert.Equal(expectedInput, inputManager.GetCurrentBuffer());
98+
}
99+
100+
[Fact]
101+
public void RemoveCurrentCharacter_InMiddle_RemovesProperCharacter()
102+
{
103+
// Arrange
104+
string initialInput = "echo on";
105+
int initialPosition = 4;
106+
string expectedInput = "echoon";
107+
int expectedPosition = 4;
108+
InputManager inputManager = new(initialInput, initialPosition);
109+
MockedShellState mockedShellState = new(inputManager);
110+
111+
// Act
112+
inputManager.RemoveCurrentCharacter(mockedShellState);
113+
114+
// Assert
115+
Assert.Equal(expectedPosition, inputManager.CaretPosition);
116+
Assert.Equal(expectedInput, inputManager.GetCurrentBuffer());
117+
}
118+
}
119+
}

0 commit comments

Comments
 (0)