// TODO - Consider the case where a "cursor" is implemented, allowing deletion / modification at various places in the string and having to keep track of placement public UserInteraction GetUserInteraction(string userInputString, bool isUpArrowPressed, bool isDownArrowPressed, TerminalState terminalState) { // Make sure we actually have a user interaction to deal with this frame - if not, short circuit and just return var userInteraction = new UserInteraction(); if (string.IsNullOrEmpty(userInputString) && !isUpArrowPressed && !isDownArrowPressed) { return(userInteraction); } // First, check to see if the user hit the up arrow key to scroll through their last inputs if (isUpArrowPressed) { var previousCommands = terminalState.GetPreviousTerminalCommands(); var currentSelectedCommandNumber = terminalState.GetTerminalCommandSelectedNumber(); var selectedCommand = GetPreviousCommandWithArrowKeys(previousCommands, currentSelectedCommandNumber, isUpArrow: true); // If, for whatever reason, we could not find the selected command for the user, do not try to change their input or do anything if (selectedCommand == null) { return(userInteraction); } // Clear out and modify the user's current input, they want this replaced with a previous submitted input terminalState.ClearCurrentInput(); var isSetInputSuccess = terminalState.TrySetCurrentInput(selectedCommand.TerminalCommandInput); if (isSetInputSuccess) { userInteraction.IsInputModified = true; userInteraction.ModifiedInput = selectedCommand.TerminalCommandInput; terminalState.SetTerminalCommandSelectedNumber(selectedCommand.TerminalCommandNumber); } else { Debug.Assert(isSetInputSuccess, $"Failed to set current input: {selectedCommand.TerminalCommandInput}"); } return(userInteraction); } // Next, check to see if the user hit the down arrow key to scroll through their last inputs if (isDownArrowPressed) { var previousCommands = terminalState.GetPreviousTerminalCommands(); var currentSelectedCommandNumber = terminalState.GetTerminalCommandSelectedNumber(); var selectedCommand = GetPreviousCommandWithArrowKeys(previousCommands, currentSelectedCommandNumber, isUpArrow: false); // If, for whatever reason, we could not find the selected command for the user, do not try to change their input or do anything if (selectedCommand == null) { return(userInteraction); } // Clear out and modify the user's current input, they want this replaced with a previous submitted input terminalState.ClearCurrentInput(); var isSetInputSuccess = terminalState.TrySetCurrentInput(selectedCommand.TerminalCommandInput); if (isSetInputSuccess) { userInteraction.IsInputModified = true; userInteraction.ModifiedInput = selectedCommand.TerminalCommandInput; terminalState.SetTerminalCommandSelectedNumber(selectedCommand.TerminalCommandNumber); } else { Debug.Assert(isSetInputSuccess, $"Failed to set current input: {selectedCommand.TerminalCommandInput}"); } return(userInteraction); } // If the user starts typing, we know they did not try to find a previous command (or are modifying a previous one) // At that point, remove any command that may have been selected and treat this as a brand new input case terminalState.ClearTerminalCommandSelectedNumber(); // Parse which characters the user's have pressed foreach (char userInputCharacter in userInputString) { // Case of enter or return being pressed to submit the user's input if (_submitCharacters.Contains(userInputCharacter)) { // Get the user's full input (not including enter or any prompt characters) userInteraction.IsInputSubmitted = true; userInteraction.SubmittedInput = terminalState.GetCurrentInput(); // Clear the user's input since it has been submitted, regardless of its validity terminalState.ClearCurrentInput(); userInteraction.IsInputModified = true; userInteraction.ModifiedInput = string.Empty; } // Case of backspace or delete bring pressed to delete some of user's input not yet submitted else if (_deleteCharacters.Contains(userInputCharacter)) { var currentInput = terminalState.GetCurrentInput(); // If the last character is being deleted, the state should be cleared differently if (currentInput.Length == 1) { terminalState.ClearCurrentInput(); userInteraction.IsInputModified = true; userInteraction.ModifiedInput = string.Empty; } // Otherwise if there is more than one character, delete the last character else if (currentInput.Length != 0) { // If we have characters to delete, remove them and reflect that change back to the user var updatedInput = currentInput.Remove(currentInput.Length - 1); var isSetInputSuccess = terminalState.TrySetCurrentInput(updatedInput); if (isSetInputSuccess) { userInteraction.IsInputModified = true; userInteraction.ModifiedInput = updatedInput; } Debug.Assert(isSetInputSuccess, $"Failed to set current input: {updatedInput}"); } } // Case of every other key, treated as the text of the key pressed else { var currentInput = terminalState.GetCurrentInput(); var updatedInput = currentInput + userInputCharacter; var isSetInputSuccess = terminalState.TrySetCurrentInput(updatedInput); if (isSetInputSuccess) { userInteraction.IsInputModified = true; userInteraction.ModifiedInput = updatedInput; } else { // We may be over the character limit, so check if the input is valid before asserting that it should have been set var isValidInput = terminalState.TryValidateInput(updatedInput, out var validInput); Debug.Assert(!isSetInputSuccess && !isValidInput, $"Failed to set current input with valid input: {validInput}"); } } } return(userInteraction); }