public void InvalidInputEmptyTest()
        {
            var terminalState    = new TerminalState();
            var isInputLengthSet = terminalState.TrySetTerminalInputLengthLimit(1);
            var isInputValid     = terminalState.TryValidateInput(string.Empty, out var validInput);

            Assert.IsTrue(isInputLengthSet);
            Assert.IsFalse(isInputValid);
            Assert.IsNull(validInput);
        }
        public void ValidInputTest()
        {
            var terminalState    = new TerminalState();
            var expected         = "test";
            var isInputLengthSet = terminalState.TrySetTerminalInputLengthLimit(10);
            var isInputValid     = terminalState.TryValidateInput(expected, out var validInput);

            Assert.IsTrue(isInputLengthSet);
            Assert.IsTrue(isInputValid);
            Assert.AreEqual(expected, validInput);
        }
Exemplo n.º 3
0
        // 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);
        }
Exemplo n.º 4
0
        // 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);
            }
        }