Example #1
0
        /// <summary>
        /// Invokes a custom ReadLine method that is similar to but more basic than PSReadLine.
        /// This method should be used when PSReadLine is disabled, either by user settings or
        /// unsupported PowerShell versions.
        /// </summary>
        /// <param name="isCommandLine">
        /// Indicates whether ReadLine should act like a command line.
        /// </param>
        /// <param name="cancellationToken">
        /// The cancellation token that will be checked prior to completing the returned task.
        /// </param>
        /// <returns>
        /// A task object representing the asynchronus operation. The Result property on
        /// the task object returns the user input string.
        /// </returns>
        internal async Task <string> InvokeLegacyReadLineAsync(bool isCommandLine, CancellationToken cancellationToken)
        {
            // TODO: Is inputBeforeCompletion used?
            string            inputBeforeCompletion = null;
            string            inputAfterCompletion  = null;
            CommandCompletion currentCompletion     = null;

            int historyIndex = -1;
            Collection <PSObject> currentHistory = null;

            StringBuilder inputLine = new StringBuilder();

            int initialCursorCol = await ConsoleProxy.GetCursorLeftAsync(cancellationToken).ConfigureAwait(false);

            int initialCursorRow = await ConsoleProxy.GetCursorTopAsync(cancellationToken).ConfigureAwait(false);

            // TODO: Are these used?
            int initialWindowLeft = Console.WindowLeft;
            int initialWindowTop  = Console.WindowTop;

            int currentCursorIndex = 0;

            Console.TreatControlCAsInput = true;

            try
            {
                while (!cancellationToken.IsCancellationRequested)
                {
                    ConsoleKeyInfo keyInfo = await ReadKeyAsync(cancellationToken).ConfigureAwait(false);

                    // Do final position calculation after the key has been pressed
                    // because the window could have been resized before then
                    int promptStartCol = initialCursorCol;
                    int promptStartRow = initialCursorRow;
                    int consoleWidth   = Console.WindowWidth;

                    if ((int)keyInfo.Key == 3 ||
                        keyInfo.Key == ConsoleKey.C && keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control))
                    {
                        throw new PipelineStoppedException();
                    }
                    else if (keyInfo.Key == ConsoleKey.Tab && isCommandLine)
                    {
                        if (currentCompletion == null)
                        {
                            inputBeforeCompletion = inputLine.ToString();
                            inputAfterCompletion  = null;

                            // TODO: This logic should be moved to AstOperations or similar!

                            if (this.powerShellContext.IsDebuggerStopped)
                            {
                                PSCommand command = new PSCommand();
                                command.AddCommand("TabExpansion2");
                                command.AddParameter("InputScript", inputBeforeCompletion);
                                command.AddParameter("CursorColumn", currentCursorIndex);
                                command.AddParameter("Options", null);

                                var results = await this.powerShellContext
                                              .ExecuteCommandAsync <CommandCompletion>(command, sendOutputToHost : false, sendErrorToHost : false)
                                              .ConfigureAwait(false);

                                currentCompletion = results.FirstOrDefault();
                            }
                            else
                            {
                                using (RunspaceHandle runspaceHandle = await this.powerShellContext.GetRunspaceHandleAsync().ConfigureAwait(false))
                                    using (PowerShell powerShell = PowerShell.Create())
                                    {
                                        powerShell.Runspace = runspaceHandle.Runspace;
                                        currentCompletion   =
                                            CommandCompletion.CompleteInput(
                                                inputBeforeCompletion,
                                                currentCursorIndex,
                                                null,
                                                powerShell);

                                        if (currentCompletion.CompletionMatches.Count > 0)
                                        {
                                            int replacementEndIndex =
                                                currentCompletion.ReplacementIndex +
                                                currentCompletion.ReplacementLength;

                                            inputAfterCompletion =
                                                inputLine.ToString(
                                                    replacementEndIndex,
                                                    inputLine.Length - replacementEndIndex);
                                        }
                                        else
                                        {
                                            currentCompletion = null;
                                        }
                                    }
                            }
                        }

                        CompletionResult completion =
                            currentCompletion?.GetNextResult(
                                !keyInfo.Modifiers.HasFlag(ConsoleModifiers.Shift));

                        if (completion != null)
                        {
                            currentCursorIndex =
                                this.InsertInput(
                                    inputLine,
                                    promptStartCol,
                                    promptStartRow,
                                    $"{completion.CompletionText}{inputAfterCompletion}",
                                    currentCursorIndex,
                                    insertIndex: currentCompletion.ReplacementIndex,
                                    replaceLength: inputLine.Length - currentCompletion.ReplacementIndex,
                                    finalCursorIndex: currentCompletion.ReplacementIndex + completion.CompletionText.Length);
                        }
                    }
                    else if (keyInfo.Key == ConsoleKey.LeftArrow)
                    {
                        currentCompletion = null;

                        if (currentCursorIndex > 0)
                        {
                            currentCursorIndex =
                                this.MoveCursorToIndex(
                                    promptStartCol,
                                    promptStartRow,
                                    consoleWidth,
                                    currentCursorIndex - 1);
                        }
                    }
                    else if (keyInfo.Key == ConsoleKey.Home)
                    {
                        currentCompletion = null;

                        currentCursorIndex =
                            this.MoveCursorToIndex(
                                promptStartCol,
                                promptStartRow,
                                consoleWidth,
                                0);
                    }
                    else if (keyInfo.Key == ConsoleKey.RightArrow)
                    {
                        currentCompletion = null;

                        if (currentCursorIndex < inputLine.Length)
                        {
                            currentCursorIndex =
                                this.MoveCursorToIndex(
                                    promptStartCol,
                                    promptStartRow,
                                    consoleWidth,
                                    currentCursorIndex + 1);
                        }
                    }
                    else if (keyInfo.Key == ConsoleKey.End)
                    {
                        currentCompletion = null;

                        currentCursorIndex =
                            this.MoveCursorToIndex(
                                promptStartCol,
                                promptStartRow,
                                consoleWidth,
                                inputLine.Length);
                    }
                    else if (keyInfo.Key == ConsoleKey.UpArrow && isCommandLine)
                    {
                        currentCompletion = null;

                        // TODO: Ctrl+Up should allow navigation in multi-line input

                        if (currentHistory == null)
                        {
                            historyIndex = -1;

                            PSCommand command = new PSCommand();
                            command.AddCommand("Get-History");

                            currentHistory = await this.powerShellContext.ExecuteCommandAsync <PSObject>(command, sendOutputToHost : false, sendErrorToHost : false)
                                             .ConfigureAwait(false)
                                             as Collection <PSObject>;

                            if (currentHistory != null)
                            {
                                historyIndex = currentHistory.Count;
                            }
                        }

                        if (currentHistory != null && currentHistory.Count > 0 && historyIndex > 0)
                        {
                            historyIndex--;

                            currentCursorIndex =
                                this.InsertInput(
                                    inputLine,
                                    promptStartCol,
                                    promptStartRow,
                                    (string)currentHistory[historyIndex].Properties["CommandLine"].Value,
                                    currentCursorIndex,
                                    insertIndex: 0,
                                    replaceLength: inputLine.Length);
                        }
                    }
                    else if (keyInfo.Key == ConsoleKey.DownArrow && isCommandLine)
                    {
                        currentCompletion = null;

                        // The down arrow shouldn't cause history to be loaded,
                        // it's only for navigating an active history array

                        if (historyIndex > -1 && historyIndex < currentHistory.Count &&
                            currentHistory != null && currentHistory.Count > 0)
                        {
                            historyIndex++;

                            if (historyIndex < currentHistory.Count)
                            {
                                currentCursorIndex =
                                    this.InsertInput(
                                        inputLine,
                                        promptStartCol,
                                        promptStartRow,
                                        (string)currentHistory[historyIndex].Properties["CommandLine"].Value,
                                        currentCursorIndex,
                                        insertIndex: 0,
                                        replaceLength: inputLine.Length);
                            }
                            else if (historyIndex == currentHistory.Count)
                            {
                                currentCursorIndex =
                                    this.InsertInput(
                                        inputLine,
                                        promptStartCol,
                                        promptStartRow,
                                        string.Empty,
                                        currentCursorIndex,
                                        insertIndex: 0,
                                        replaceLength: inputLine.Length);
                            }
                        }
                    }
                    else if (keyInfo.Key == ConsoleKey.Escape)
                    {
                        currentCompletion = null;
                        historyIndex      = currentHistory != null ? currentHistory.Count : -1;

                        currentCursorIndex =
                            this.InsertInput(
                                inputLine,
                                promptStartCol,
                                promptStartRow,
                                string.Empty,
                                currentCursorIndex,
                                insertIndex: 0,
                                replaceLength: inputLine.Length);
                    }
                    else if (keyInfo.Key == ConsoleKey.Backspace)
                    {
                        currentCompletion = null;

                        if (currentCursorIndex > 0)
                        {
                            currentCursorIndex =
                                this.InsertInput(
                                    inputLine,
                                    promptStartCol,
                                    promptStartRow,
                                    string.Empty,
                                    currentCursorIndex,
                                    insertIndex: currentCursorIndex - 1,
                                    replaceLength: 1,
                                    finalCursorIndex: currentCursorIndex - 1);
                        }
                    }
                    else if (keyInfo.Key == ConsoleKey.Delete)
                    {
                        currentCompletion = null;

                        if (currentCursorIndex < inputLine.Length)
                        {
                            currentCursorIndex =
                                this.InsertInput(
                                    inputLine,
                                    promptStartCol,
                                    promptStartRow,
                                    string.Empty,
                                    currentCursorIndex,
                                    replaceLength: 1,
                                    finalCursorIndex: currentCursorIndex);
                        }
                    }
                    else if (keyInfo.Key == ConsoleKey.Enter)
                    {
                        string completedInput = inputLine.ToString();
                        currentCompletion = null;
                        currentHistory    = null;

                        //if ((keyInfo.Modifiers & ConsoleModifiers.Shift) == ConsoleModifiers.Shift)
                        //{
                        //    // TODO: Start a new line!
                        //    continue;
                        //}

                        Parser.ParseInput(
                            completedInput,
                            out Token[] tokens,
                            out ParseError[] parseErrors);
Example #2
0
 private static Task <ConsoleKeyInfo> ReadKeyAsync(CancellationToken cancellationToken)
 {
     return(ConsoleProxy.ReadKeyAsync(intercept: true, cancellationToken));
 }
        private async Task StartReplLoopAsync(CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                string commandString     = null;
                int    originalCursorTop = 0;

                try
                {
                    await this.WritePromptStringToHostAsync(cancellationToken).ConfigureAwait(false);
                }
                catch (OperationCanceledException)
                {
                    break;
                }

                try
                {
                    originalCursorTop = await ConsoleProxy.GetCursorTopAsync(cancellationToken).ConfigureAwait(false);

                    commandString = await this.ReadCommandLineAsync(cancellationToken).ConfigureAwait(false);
                }
                catch (PipelineStoppedException)
                {
                    this.WriteOutput(
                        "^C",
                        true,
                        OutputType.Normal,
                        foregroundColor: ConsoleColor.Red);
                }
                // Do nothing here, the while loop condition will exit.
                catch (TaskCanceledException)
                { }
                catch (OperationCanceledException)
                { }
                catch (Exception e) // Narrow this if possible
                {
                    this.WriteOutput(
                        $"\n\nAn error occurred while reading input:\n\n{e.ToString()}\n",
                        true,
                        OutputType.Error);

                    Logger.LogException("Caught exception while reading command line", e);
                }
                finally
                {
                    // This supplies the newline in the Legacy ReadLine when executing code in the terminal via hitting the ENTER key.
                    // Without this, hitting ENTER with a no input looks like it does nothing (no new prompt is written)
                    // and also the output would show up on the same line as the code you wanted to execute (the prompt line).
                    // Since PSReadLine handles ENTER internally to itself, we only want to do this when using the Legacy ReadLine.
                    if (!_isPSReadLineEnabled &&
                        !cancellationToken.IsCancellationRequested &&
                        originalCursorTop == await ConsoleProxy.GetCursorTopAsync(cancellationToken).ConfigureAwait(false))
                    {
                        this.WriteLine();
                    }
                }

                if (!string.IsNullOrWhiteSpace(commandString))
                {
                    var unusedTask =
                        this.powerShellContext
                        .ExecuteScriptStringAsync(
                            commandString,
                            writeInputToHost: false,
                            writeOutputToHost: true,
                            addToHistory: true)
                        .ConfigureAwait(continueOnCapturedContext: false);

                    break;
                }
            }
        }
Example #4
0
        public async static Task <SecureString> ReadSecureLineAsync(CancellationToken cancellationToken)
        {
            SecureString secureString = new SecureString();

            // TODO: Are these values used?
            int initialPromptRow = await ConsoleProxy.GetCursorTopAsync(cancellationToken).ConfigureAwait(false);

            int initialPromptCol = await ConsoleProxy.GetCursorLeftAsync(cancellationToken).ConfigureAwait(false);

            int previousInputLength = 0;

            Console.TreatControlCAsInput = true;

            try
            {
                while (!cancellationToken.IsCancellationRequested)
                {
                    ConsoleKeyInfo keyInfo = await ReadKeyAsync(cancellationToken).ConfigureAwait(false);

                    if ((int)keyInfo.Key == 3 ||
                        keyInfo.Key == ConsoleKey.C && keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control))
                    {
                        throw new PipelineStoppedException();
                    }
                    if (keyInfo.Key == ConsoleKey.Enter)
                    {
                        // Break to return the completed string
                        break;
                    }
                    if (keyInfo.Key == ConsoleKey.Tab)
                    {
                        continue;
                    }
                    if (keyInfo.Key == ConsoleKey.Backspace)
                    {
                        if (secureString.Length > 0)
                        {
                            secureString.RemoveAt(secureString.Length - 1);
                        }
                    }
                    else if (keyInfo.KeyChar != 0 && !char.IsControl(keyInfo.KeyChar))
                    {
                        secureString.AppendChar(keyInfo.KeyChar);
                    }

                    // Re-render the secure string characters
                    int currentInputLength = secureString.Length;
                    int consoleWidth       = Console.WindowWidth;

                    if (currentInputLength > previousInputLength)
                    {
                        Console.Write('*');
                    }
                    else if (previousInputLength > 0 && currentInputLength < previousInputLength)
                    {
                        int row = await ConsoleProxy.GetCursorTopAsync(cancellationToken).ConfigureAwait(false);

                        int col = await ConsoleProxy.GetCursorLeftAsync(cancellationToken).ConfigureAwait(false);

                        // Back up the cursor before clearing the character
                        col--;
                        if (col < 0)
                        {
                            col = consoleWidth - 1;
                            row--;
                        }

                        Console.SetCursorPosition(col, row);
                        Console.Write(' ');
                        Console.SetCursorPosition(col, row);
                    }

                    previousInputLength = currentInputLength;
                }
            }
            finally
            {
                Console.TreatControlCAsInput = false;
            }

            return(secureString);
        }
Example #5
0
        private async Task StartReplLoopAsync(CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                string commandString     = null;
                int    originalCursorTop = 0;

                try
                {
                    await this.WritePromptStringToHostAsync(cancellationToken);
                }
                catch (OperationCanceledException)
                {
                    break;
                }

                try
                {
                    originalCursorTop = await ConsoleProxy.GetCursorTopAsync(cancellationToken);

                    commandString = await this.ReadCommandLineAsync(cancellationToken);
                }
                catch (PipelineStoppedException)
                {
                    this.WriteOutput(
                        "^C",
                        true,
                        OutputType.Normal,
                        foregroundColor: ConsoleColor.Red);
                }
                // Do nothing here, the while loop condition will exit.
                catch (TaskCanceledException)
                { }
                catch (OperationCanceledException)
                { }
                catch (Exception e) // Narrow this if possible
                {
                    this.WriteOutput(
                        $"\n\nAn error occurred while reading input:\n\n{e.ToString()}\n",
                        true,
                        OutputType.Error);

                    Logger.LogException("Caught exception while reading command line", e);
                }
                finally
                {
                    if (!cancellationToken.IsCancellationRequested &&
                        originalCursorTop == await ConsoleProxy.GetCursorTopAsync(cancellationToken))
                    {
                        this.WriteLine();
                    }
                }

                if (!string.IsNullOrWhiteSpace(commandString))
                {
                    var unusedTask =
                        this.powerShellContext
                        .ExecuteScriptStringAsync(
                            commandString,
                            writeInputToHost: false,
                            writeOutputToHost: true,
                            addToHistory: true)
                        .ConfigureAwait(continueOnCapturedContext: false);

                    break;
                }
            }
        }