Skip to content

Commit

Permalink
Fix console issues with deleting characters (#471)
Browse files Browse the repository at this point in the history
  • Loading branch information
tlmii authored Jan 8, 2021
1 parent c28ef49 commit 5c20cf9
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 6 deletions.
24 changes: 20 additions & 4 deletions src/Microsoft.Repl/Input/InputManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ public class InputManager : IInputManager
private readonly Dictionary<ConsoleKey, Dictionary<ConsoleModifiers, AsyncKeyPressHandler>> _handlers = new Dictionary<ConsoleKey, Dictionary<ConsoleModifiers, AsyncKeyPressHandler>>();
private readonly List<char> _inputBuffer = new List<char>();

public InputManager() { }

/// <summary>
/// For testing purposes only
/// </summary>
internal InputManager(string initialInput, int initialPosition)
{
_inputBuffer.AddRange(initialInput);
CaretPosition = initialPosition;
}


public bool IsOverwriteMode { get; set; }

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

if (CaretPosition == _inputBuffer.Count)
int caret = CaretPosition;
if (caret == _inputBuffer.Count)
{
return;
}

List<char> update = _inputBuffer.ToList();
update.RemoveAt(CaretPosition);
update.RemoveAt(caret);
state.ConsoleManager.IsCaretVisible = false;
SetInput(state, update);
state.MoveCarets(caret - CaretPosition);
state.ConsoleManager.IsCaretVisible = true;
}

public void RemovePreviousCharacter(IShellState state)
{
state = state ?? throw new ArgumentNullException(nameof(state));

if (CaretPosition == 0)
int caret = CaretPosition;
if (caret == 0)
{
return;
}

List<char> update = _inputBuffer.ToList();
update.RemoveAt(CaretPosition - 1);
update.RemoveAt(caret - 1);
state.ConsoleManager.IsCaretVisible = false;
SetInput(state, update);
state.MoveCarets(caret - CaretPosition - 1);
state.ConsoleManager.IsCaretVisible = true;
}

Expand Down
4 changes: 4 additions & 0 deletions src/Microsoft.Repl/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Microsoft.Repl.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
[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.")]

4 changes: 2 additions & 2 deletions test/Microsoft.HttpRepl.Fakes/MockedShellState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class MockedShellState : IShellState
{
private readonly ShellState _shellState;
private readonly StringBuilder _output = new StringBuilder();
public MockedShellState()
public MockedShellState(IInputManager inputManager = null)
{
DefaultCommandDispatcher<object> defaultCommandDispatcher = DefaultCommandDispatcher.Create(x => { }, new object());
Mock<IConsoleManager> mockedConsoleManager = new Mock<IConsoleManager>();
Expand All @@ -28,7 +28,7 @@ public MockedShellState()
mockedConsoleManager.Setup(x => x.Write(It.IsAny<string>())).Callback((string s) => _output.Append(s));
mockedConsoleManager.Setup(x => x.WriteLine(It.IsAny<string>())).Callback((string s) => _output.AppendLine(s));

_shellState = new ShellState(defaultCommandDispatcher, consoleManager: mockedConsoleManager.Object);
_shellState = new ShellState(defaultCommandDispatcher, inputManager: inputManager, consoleManager: mockedConsoleManager.Object);
}

public string ErrorMessage { get; private set; }
Expand Down
119 changes: 119 additions & 0 deletions test/Microsoft.Repl.Tests/InputManagerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using Microsoft.HttpRepl.Fakes;
using Microsoft.Repl.Input;
using Xunit;

namespace Microsoft.Repl.Tests
{
public class InputManagerTests
{
[Fact]
public void RemovePreviousCharacter_AtBeginning_DoesNothing()
{
// Arrange
string initialInput = "echo on";
int initialPosition = 0;
InputManager inputManager = new(initialInput, initialPosition);
MockedShellState mockedShellState = new(inputManager);

// Act
inputManager.RemovePreviousCharacter(mockedShellState);

// Assert
Assert.Equal(initialPosition, inputManager.CaretPosition);
Assert.Equal(initialInput, inputManager.GetCurrentBuffer());
}

[Fact]
public void RemovePreviousCharacter_AtEnd_RemovesLastCharacter()
{
// Arrange
string initialInput = "echo on";
int initialPosition = 7;
string expectedInput = "echo o";
int expectedPosition = 6;
InputManager inputManager = new(initialInput, initialPosition);
MockedShellState mockedShellState = new(inputManager);

// Act
inputManager.RemovePreviousCharacter(mockedShellState);

// Assert
Assert.Equal(expectedPosition, inputManager.CaretPosition);
Assert.Equal(expectedInput, inputManager.GetCurrentBuffer());
}

[Fact]
public void RemovePreviousCharacter_InMiddle_RemovesProperCharacter()
{
// Arrange
string initialInput = "echo on";
int initialPosition = 4;
string expectedInput = "ech on";
int expectedPosition = 3;
InputManager inputManager = new(initialInput, initialPosition);
MockedShellState mockedShellState = new(inputManager);

// Act
inputManager.RemovePreviousCharacter(mockedShellState);

// Assert
Assert.Equal(expectedPosition, inputManager.CaretPosition);
Assert.Equal(expectedInput, inputManager.GetCurrentBuffer());
}

[Fact]
public void RemoveCurrentCharacter_AtEnd_DoesNothing()
{
// Arrange
string initialInput = "echo on";
int initialPosition = 7;
InputManager inputManager = new(initialInput, initialPosition);
MockedShellState mockedShellState = new(inputManager);

// Act
inputManager.RemoveCurrentCharacter(mockedShellState);

// Assert
Assert.Equal(initialPosition, inputManager.CaretPosition);
Assert.Equal(initialInput, inputManager.GetCurrentBuffer());
}

[Fact]
public void RemoveCurrentCharacter_AtBeginning_RemovesFirstCharacter()
{
// Arrange
string initialInput = "echo on";
int initialPosition = 0;
string expectedInput = "cho on";
int expectedPosition = 0;
InputManager inputManager = new(initialInput, initialPosition);
MockedShellState mockedShellState = new(inputManager);

// Act
inputManager.RemoveCurrentCharacter(mockedShellState);

// Assert
Assert.Equal(expectedPosition, inputManager.CaretPosition);
Assert.Equal(expectedInput, inputManager.GetCurrentBuffer());
}

[Fact]
public void RemoveCurrentCharacter_InMiddle_RemovesProperCharacter()
{
// Arrange
string initialInput = "echo on";
int initialPosition = 4;
string expectedInput = "echoon";
int expectedPosition = 4;
InputManager inputManager = new(initialInput, initialPosition);
MockedShellState mockedShellState = new(inputManager);

// Act
inputManager.RemoveCurrentCharacter(mockedShellState);

// Assert
Assert.Equal(expectedPosition, inputManager.CaretPosition);
Assert.Equal(expectedInput, inputManager.GetCurrentBuffer());
}
}
}

0 comments on commit 5c20cf9

Please sign in to comment.