Пример #1
0
        public CommandCompletion GetCompletions(string inputText, int currentIndex, bool?forward)
        {
            if (_lastCompletion is null ||
                inputText != _lastInputText)
            {
                _lastInputText  = inputText;
                _lastCompletion = CommandCompletion.CompleteInput(inputText, currentIndex, new(), _powershell);
            }

            if (forward.HasValue)
            {
                _lastCompletion.GetNextResult(forward.Value);
            }

            return(_lastCompletion);
        }
        /// <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)
        {
            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);

            int initialCursorRow = await ConsoleProxy.GetCursorTopAsync(cancellationToken);

            int initialWindowLeft = Console.WindowLeft;
            int initialWindowTop  = Console.WindowTop;

            int currentCursorIndex = 0;

            Console.TreatControlCAsInput = true;

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

                    // 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, false, false);

                                currentCompletion = results.FirstOrDefault();
                            }
                            else
                            {
                                using (RunspaceHandle runspaceHandle = await this.powerShellContext.GetRunspaceHandleAsync())
                                    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,
                                    false,
                                    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);
Пример #3
0
        public override string ReadLine(CancellationToken cancellationToken)
        {
            string            inputBeforeCompletion = null;
            string            inputAfterCompletion  = null;
            CommandCompletion currentCompletion     = null;

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

            StringBuilder inputLine = new();

            int initialCursorCol = Console.CursorLeft;
            int initialCursorRow = Console.CursorTop;

            int currentCursorIndex = 0;

            Console.TreatControlCAsInput = true;

            try
            {
                while (!cancellationToken.IsCancellationRequested)
                {
                    ConsoleKeyInfo keyInfo = ReadKey(cancellationToken);

                    // 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;

                    switch (keyInfo.Key)
                    {
                    case ConsoleKey.Tab:
                        if (currentCompletion is null)
                        {
                            inputBeforeCompletion = inputLine.ToString();
                            inputAfterCompletion  = null;

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

                            if (_psesHost.DebugContext.IsStopped)
                            {
                                PSCommand command = new PSCommand()
                                                    .AddCommand("TabExpansion2")
                                                    .AddParameter("InputScript", inputBeforeCompletion)
                                                    .AddParameter("CursorColumn", currentCursorIndex)
                                                    .AddParameter("Options", null);

                                currentCompletion = _psesHost.InvokePSCommand <CommandCompletion>(command, executionOptions: null, cancellationToken).FirstOrDefault();
                            }
                            else
                            {
                                currentCompletion = _psesHost.InvokePSDelegate(
                                    "Legacy readline inline command completion",
                                    executionOptions: null,
                                    (pwsh, _) => CommandCompletion.CompleteInput(inputAfterCompletion, currentCursorIndex, options: null, pwsh),
                                    cancellationToken);

                                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 is not null)
                        {
                            currentCursorIndex =
                                InsertInput(
                                    inputLine,
                                    promptStartCol,
                                    promptStartRow,
                                    completion.CompletionText + inputAfterCompletion,
                                    currentCursorIndex,
                                    insertIndex: currentCompletion.ReplacementIndex,
                                    replaceLength: inputLine.Length - currentCompletion.ReplacementIndex,
                                    finalCursorIndex: currentCompletion.ReplacementIndex + completion.CompletionText.Length);
                        }

                        continue;

                    case ConsoleKey.LeftArrow:
                        currentCompletion = null;

                        if (currentCursorIndex > 0)
                        {
                            currentCursorIndex =
                                MoveCursorToIndex(
                                    promptStartCol,
                                    promptStartRow,
                                    consoleWidth,
                                    currentCursorIndex - 1);
                        }

                        continue;

                    case ConsoleKey.Home:
                        currentCompletion = null;

                        currentCursorIndex =
                            MoveCursorToIndex(
                                promptStartCol,
                                promptStartRow,
                                consoleWidth,
                                0);

                        continue;

                    case ConsoleKey.RightArrow:
                        currentCompletion = null;

                        if (currentCursorIndex < inputLine.Length)
                        {
                            currentCursorIndex =
                                MoveCursorToIndex(
                                    promptStartCol,
                                    promptStartRow,
                                    consoleWidth,
                                    currentCursorIndex + 1);
                        }

                        continue;

                    case ConsoleKey.End:
                        currentCompletion = null;

                        currentCursorIndex =
                            MoveCursorToIndex(
                                promptStartCol,
                                promptStartRow,
                                consoleWidth,
                                inputLine.Length);

                        continue;

                    case ConsoleKey.UpArrow:
                        currentCompletion = null;

                        // TODO: Ctrl+Up should allow navigation in multi-line input
                        if (currentHistory is null)
                        {
                            historyIndex = -1;

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

                            currentHistory = _psesHost.InvokePSCommand <PSObject>(command, executionOptions: null, cancellationToken);

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

                        if (currentHistory?.Count > 0 && historyIndex > 0)
                        {
                            historyIndex--;

                            currentCursorIndex =
                                InsertInput(
                                    inputLine,
                                    promptStartCol,
                                    promptStartRow,
                                    (string)currentHistory[historyIndex].Properties["CommandLine"].Value,
                                    currentCursorIndex,
                                    insertIndex: 0,
                                    replaceLength: inputLine.Length);
                        }

                        continue;

                    case ConsoleKey.DownArrow:
                        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?.Count > 0)
                        {
                            historyIndex++;

                            if (historyIndex < currentHistory.Count)
                            {
                                currentCursorIndex =
                                    InsertInput(
                                        inputLine,
                                        promptStartCol,
                                        promptStartRow,
                                        (string)currentHistory[historyIndex].Properties["CommandLine"].Value,
                                        currentCursorIndex,
                                        insertIndex: 0,
                                        replaceLength: inputLine.Length);
                            }
                            else if (historyIndex == currentHistory.Count)
                            {
                                currentCursorIndex =
                                    InsertInput(
                                        inputLine,
                                        promptStartCol,
                                        promptStartRow,
                                        string.Empty,
                                        currentCursorIndex,
                                        insertIndex: 0,
                                        replaceLength: inputLine.Length);
                            }
                        }

                        continue;

                    case ConsoleKey.Escape:
                        currentCompletion = null;
                        historyIndex      = currentHistory != null ? currentHistory.Count : -1;

                        currentCursorIndex =
                            InsertInput(
                                inputLine,
                                promptStartCol,
                                promptStartRow,
                                string.Empty,
                                currentCursorIndex,
                                insertIndex: 0,
                                replaceLength: inputLine.Length);

                        continue;

                    case ConsoleKey.Backspace:
                        currentCompletion = null;

                        if (currentCursorIndex > 0)
                        {
                            currentCursorIndex =
                                InsertInput(
                                    inputLine,
                                    promptStartCol,
                                    promptStartRow,
                                    string.Empty,
                                    currentCursorIndex,
                                    insertIndex: currentCursorIndex - 1,
                                    replaceLength: 1,
                                    finalCursorIndex: currentCursorIndex - 1);
                        }

                        continue;

                    case ConsoleKey.Delete:
                        currentCompletion = null;

                        if (currentCursorIndex < inputLine.Length)
                        {
                            currentCursorIndex =
                                InsertInput(
                                    inputLine,
                                    promptStartCol,
                                    promptStartRow,
                                    string.Empty,
                                    currentCursorIndex,
                                    replaceLength: 1,
                                    finalCursorIndex: currentCursorIndex);
                        }

                        continue;

                    case ConsoleKey.Enter:
                        string completedInput = inputLine.ToString();
                        currentCompletion = null;
                        currentHistory    = null;

                        // TODO: Add line continuation support:
                        // - When shift+enter is pressed, or
                        // - When the parse indicates incomplete input

                        //if ((keyInfo.Modifiers & ConsoleModifiers.Shift) == ConsoleModifiers.Shift)
                        //{
                        //    // TODO: Start a new line!
                        //    continue;
                        //}
                        //Parser.ParseInput(
                        //    completedInput,
                        //    out Token[] tokens,
                        //    out ParseError[] parseErrors);
                        //if (parseErrors.Any(e => e.IncompleteInput))
                        //{
                        //    // TODO: Start a new line!
                        //    continue;
                        //}

                        return(completedInput);

                    default:
                        if (keyInfo.IsCtrlC())
                        {
                            throw new PipelineStoppedException();
                        }

                        // Normal character input
                        if (keyInfo.KeyChar != 0 && !char.IsControl(keyInfo.KeyChar))
                        {
                            currentCompletion = null;

                            currentCursorIndex =
                                InsertInput(
                                    inputLine,
                                    promptStartCol,
                                    promptStartRow,
                                    keyInfo.KeyChar.ToString(),     // TODO: Determine whether this should take culture into account
                                    currentCursorIndex,
                                    finalCursorIndex: currentCursorIndex + 1);
                        }

                        continue;
                    }
                }
            }
            catch (OperationCanceledException)
            {
                // We've broken out of the loop
            }
            finally
            {
                Console.TreatControlCAsInput = false;
            }

            // If we break out of the loop without returning (because of the Enter key)
            // then the readline has been aborted in some way and we should return nothing
            return(null);
        }