public void GetDefaultHistoricalCommandsTest() { var terminalState = new TerminalState(); var previousCommands = terminalState.GetPreviousTerminalCommands(); Assert.IsNotNull(previousCommands); Assert.IsEmpty(previousCommands); }
public void HistoricalCommandIsVisibleByDefaultTest() { var terminalState = new TerminalState(); var terminalCommand = new TerminalCommand { TerminalCommandInput = "testInput", TerminalCommandOutput = "testOutput" }; var isHistoryLimitSet = terminalState.TrySetCommandHistoryLimit(10); var isAddSuccess = terminalState.TryAddHistoricalCommand(terminalCommand); var previousCommands = terminalState.GetPreviousTerminalCommands(); var previousCommand = previousCommands.FirstOrDefault(); Assert.IsTrue(isHistoryLimitSet); Assert.IsTrue(isAddSuccess); Assert.IsTrue(previousCommand.IsVisibleInTerminal); }
public void AddHistoricalCommandTest() { var terminalState = new TerminalState(); var terminalCommand = new TerminalCommand { TerminalCommandInput = "testInput", TerminalCommandOutput = "testOutput" }; var isHistoryLimitSet = terminalState.TrySetCommandHistoryLimit(10); var isAddHistoricalCommandSuccess = terminalState.TryAddHistoricalCommand(terminalCommand); var previousCommands = terminalState.GetPreviousTerminalCommands(); Assert.IsTrue(isHistoryLimitSet); Assert.IsTrue(isAddHistoricalCommandSuccess); Assert.IsNotNull(previousCommands); Assert.IsNotEmpty(previousCommands); Assert.IsTrue(previousCommands.Contains(terminalCommand)); }
public void DoNotRemoveHistoricalCommandWhenUnderLimitTest() { var terminalState = new TerminalState(); var terminalCommand = new TerminalCommand { TerminalCommandInput = "testInput", TerminalCommandOutput = "testOutput" }; var isHistoryLimitSet = terminalState.TrySetCommandHistoryLimit(5); var isAddSuccess = terminalState.TryAddHistoricalCommand(terminalCommand); var isRemoveSuccess = terminalState.TryRemoveOldestHistoricalCommand(); var previousCommands = terminalState.GetPreviousTerminalCommands(); Assert.IsTrue(isHistoryLimitSet); Assert.IsTrue(isAddSuccess); Assert.IsFalse(isRemoveSuccess); Assert.IsTrue(previousCommands.Contains(terminalCommand)); Assert.IsNotEmpty(previousCommands); }
public void AddHistoricalCommandWhenAtLimitTest() { var terminalState = new TerminalState(); var underLimitTerminalCommand = new TerminalCommand { TerminalCommandInput = "underLimitInput", TerminalCommandOutput = "underLimitOutput" }; var overLimitTerminalCommand = new TerminalCommand { TerminalCommandInput = "overLimitInput", TerminalCommandOutput = "overLimitOutput" }; var isHistoryLimitSet = terminalState.TrySetCommandHistoryLimit(1); var isUnderLimitAddHistoricalCommandSuccess = terminalState.TryAddHistoricalCommand(underLimitTerminalCommand); var isOverLimitAddHistoricalCommandSuccess = terminalState.TryAddHistoricalCommand(overLimitTerminalCommand); var previousCommands = terminalState.GetPreviousTerminalCommands(); Assert.IsTrue(isHistoryLimitSet); Assert.IsTrue(isUnderLimitAddHistoricalCommandSuccess); Assert.IsFalse(isOverLimitAddHistoricalCommandSuccess); Assert.IsTrue(previousCommands.Contains(underLimitTerminalCommand)); Assert.IsFalse(previousCommands.Contains(overLimitTerminalCommand)); }
// 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); }
// Game Loop - Executed Once Per Frame public void Update() { // First, figure out if the user has done anything to modify the input var isUpArrowPressed = Input.GetKeyDown(KeyCode.UpArrow); var isDownArrowPressed = Input.GetKeyDown(KeyCode.DownArrow); var userInputString = Input.inputString; var userInteraction = _userInterfaceController.GetUserInteraction(userInputString, isUpArrowPressed, isDownArrowPressed, _terminalState); // Next, if the user submitted input as part of their interactions, attempt to validate and execute what they submitted if (userInteraction.IsInputSubmitted) { // Need to get the current directory before we execute the command since it could change the current directory var currentDirectory = _fileSystemState.GetCurrentDirectory(); var userInteractionResponse = new StringBuilder(); // Since the user submitted input, we now need to parse that input Debug.Log($"User input submitted: `{userInteraction.SubmittedInput}`"); var isParseInputSuccess = _userInputParser.TryParseUserInput(userInteraction.SubmittedInput, out var parsedUserSubmittedInput); if (!isParseInputSuccess) { Debug.Log($"Failed to parse user input: `{userInteraction.SubmittedInput}`"); } // Extract the arguments into a parameterized array var args = parsedUserSubmittedInput.Arguments?.ToArray(); // Check to see that the we can retrieve the command the user wants to execute from the parsed input var isCommandRetrievedSuccess = _commandController.TryGetCommand(_commandState, parsedUserSubmittedInput.CommandName, out var command); if (!isCommandRetrievedSuccess) { userInteractionResponse.AppendLine($"Command `{parsedUserSubmittedInput.CommandName}` not found."); userInteractionResponse.AppendLine($"Run `{_helpCommandName}` for a list of available commands"); } // Execute the command if we successfully retrieved it // Note - Each command is in charge of its own validation and if / how it executes after succeeding or failing validation else { var commandResponse = command.ExecuteCommand(args); userInteractionResponse.AppendLine(commandResponse); } // Mark that the user's output will change based on this latest terminal command userInteraction.IsOutputModified = true; var terminalCommand = new TerminalCommand { TerminalCommandNumber = _terminalState.GetTerminalCommandSubmissionNumber(), TerminalCommandPath = _directoryController.GetDirectoryPath(currentDirectory), TerminalCommandInput = userInteraction.SubmittedInput, TerminalCommandOutput = userInteractionResponse.ToString(), // If the command was a valid `clear` command, we do not want to show output for it, otherwise we do want output visible IsVisibleInTerminal = command == null || command.GetType() != typeof(ClearCommand) || !command.TryValidateArguments(out _, args) }; _terminalState.IncrementTerminalCommandSubmissionNumber(); // Add the input to the list of historical inputs if it is a valid input (not empty, null, or over the character limit) if (_terminalState.TryValidateInput(userInteraction.SubmittedInput, out var validSubmittedInput)) { var isAddHistoricalInputSuccess = _terminalState.TryAddHistoricalCommand(terminalCommand); if (!isAddHistoricalInputSuccess && _terminalState.TryRemoveOldestHistoricalCommand()) { isAddHistoricalInputSuccess = _terminalState.TryAddHistoricalCommand(terminalCommand); } Debug.Assert(isAddHistoricalInputSuccess, $"Failed to add valid historical input: {validSubmittedInput} with output: {userInteractionResponse}"); } } // Next, if the user has modified input, make sure that is reflected back in the UI if (userInteraction.IsInputModified) { // Grab the current directory after the command has executed, because the command could have changed the current directory var currentDirectory = _fileSystemState.GetCurrentDirectory(); var currentDirectoryPath = _directoryController.GetDirectoryPath(currentDirectory); _userInterfaceController.SetUserInterfaceTextWithInputPrompt(InputTextObject, userInteraction.ModifiedInput, currentDirectoryPath); } // Finally, if the user's input requires a corresponding change in output, reflect that in the UI if (userInteraction.IsOutputModified) { // If a command was submitted, it has already been added to the previous commands with relevant output // We can construct full output to the user with the list of previous commands var previousTerminalCommands = _terminalState.GetPreviousTerminalCommands(); userInteraction.ModifiedOutput = _userInterfaceController.BuildUserInterfaceText(previousTerminalCommands); _userInterfaceController.SetUserInterfaceText(OutputTextObject, userInteraction.ModifiedOutput); } }