예제 #1
0
        /// <summary>
        /// Completion lists calculator.
        /// </summary>
        /// <param name="script">The active script.</param>
        /// <param name="caretPosition">The caret position.</param>
        /// <param name="runspace">Runspace for completion computing.</param>
        /// <returns></returns>
        public static CommandCompletion GetCommandCompletionList(string script, int caretPosition, Runspace runspace)
        {
            Ast ast;

            Token[]         tokens;
            IScriptPosition cursorPosition;

            GetCommandCompletionParameters(script, caretPosition, out ast, out tokens, out cursorPosition);
            if (ast == null)
            {
                return(null);
            }

            CommandCompletion commandCompletion = null;

            if (runspace.RunspaceAvailability == RunspaceAvailability.Available)
            {
                using (_currentPowerShell = PowerShell.Create())
                {
                    _currentPowerShell.Runspace = runspace;
                    commandCompletion           = CommandCompletion.CompleteInput(ast, tokens, cursorPosition, null, _currentPowerShell);
                }
            }

            return(commandCompletion);
        }
예제 #2
0
        private CommandCompletion GetCompletions()
        {
            if (_tabCommandCount == 0)
            {
                try
                {
                    _tabCompletions = null;

                    // Could use the overload that takes an AST as it's faster (we've already parsed the
                    // input for coloring) but that overload is a little more complicated in passing in the
                    // cursor position.
                    var ps = PowerShell.Create(RunspaceMode.CurrentRunspace);
                    _tabCompletions = CommandCompletion.CompleteInput(_buffer.ToString(), _current, null, ps);

                    if (_tabCompletions.CompletionMatches.Count == 0)
                    {
                        return(null);
                    }
                }
                catch (Exception)
                {
                }
            }

            return(_tabCompletions);
        }
예제 #3
0
        private void StartIntelliSense(int lineStartPosition, int caretPosition, string lineTextUpToCaret)
        {
            if (_intellisenseRunning)
            {
                return;
            }

            _intellisenseRunning = true;
            var statusBar = (IVsStatusbar)PowerShellToolsPackage.Instance.GetService(typeof(SVsStatusbar));

            statusBar.SetText("Running IntelliSense...");
            var sw = new Stopwatch();

            sw.Start();

            Ast ast;

            Token[]         tokens;
            IScriptPosition cursorPosition;

            GetCommandCompletionParameters(caretPosition, out ast, out tokens, out cursorPosition);
            if (ast == null)
            {
                return;
            }

            var ps = PowerShell.Create();

            ps.Runspace = PowerShellToolsPackage.Debugger.Runspace;

            var commandCompletion = CommandCompletion.CompleteInput(ast, tokens, cursorPosition, null, ps);

            var line        = _textView.Caret.Position.BufferPosition.GetContainingLine();
            var caretInLine = (caretPosition - line.Start);
            var text        = line.GetText().Substring(0, caretInLine);

            IList <CompletionResult> list = commandCompletion.CompletionMatches;

            if (string.Equals(lineTextUpToCaret, text, StringComparison.Ordinal) && list.Count != 0)
            {
                if (list.Count != 0)
                {
                    try
                    {
                        IntellisenseDone(commandCompletion.CompletionMatches, lineStartPosition,
                                         commandCompletion.ReplacementIndex + 0, commandCompletion.ReplacementLength, caretPosition);
                    }
                    catch (Exception ex)
                    {
                        Log.Debug("Failed to start IntelliSense.", ex);
                    }
                }
            }

            statusBar.SetText(String.Format("IntelliSense complete in {0:0.00} seconds...", sw.Elapsed.TotalSeconds));
            _intellisenseRunning = false;
        }
예제 #4
0
 public string[] GetTabCompletion(string commandLine)
 {
     string[] strArrays;
     if (commandLine != null)
     {
         lock (this.clientRequestLock)
         {
             if (this.State == PowwaSession.SessionState.Available)
             {
                 this.State = PowwaSession.SessionState.ExecutingCommand;
                 System.Management.Automation.PowerShell powerShell = null;
                 try
                 {
                     try
                     {
                         powerShell          = System.Management.Automation.PowerShell.Create();
                         powerShell.Runspace = this.Runspace;
                         CommandCompletion commandCompletion = CommandCompletion.CompleteInput(commandLine, commandLine.Length, null, powerShell);
                         string            str        = commandLine.Substring(0, commandCompletion.ReplacementIndex);
                         string[]          strArrays1 = new string[commandCompletion.CompletionMatches.Count];
                         for (int i = 0; i < commandCompletion.CompletionMatches.Count; i++)
                         {
                             strArrays1[i] = string.Concat(str, commandCompletion.CompletionMatches[i].CompletionText);
                         }
                         strArrays = strArrays1;
                     }
                     catch
                     {
                         strArrays = new string[0];
                     }
                 }
                 finally
                 {
                     if (powerShell != null)
                     {
                         powerShell.Dispose();
                     }
                     this.State = PowwaSession.SessionState.Available;
                 }
             }
             else
             {
                 PowwaEvents.PowwaEVENT_DEBUG_LOG1("GetTabCompletion(): Invalid Session State", "SessionState", this.State.ToString());
                 throw new InvalidOperationException("The session is not available");
             }
         }
         return(strArrays);
     }
     else
     {
         throw new ArgumentNullException("commandLine");
     }
 }
예제 #5
0
        /// <summary>
        /// Gets completions for the symbol found in the Ast at
        /// the given file offset.
        /// </summary>
        /// <param name="scriptAst">
        /// The Ast which will be traversed to find a completable symbol.
        /// </param>
        /// <param name="currentTokens">
        /// The array of tokens corresponding to the scriptAst parameter.
        /// </param>
        /// <param name="fileOffset">
        /// The 1-based file offset at which a symbol will be located.
        /// </param>
        /// <param name="runspace">
        /// The Runspace to use for gathering completions.
        /// </param>
        /// <returns>
        /// A CommandCompletion instance that contains completions for the
        /// symbol at the given offset.
        /// </returns>
        static public CommandCompletion GetCompletions(
            Ast scriptAst,
            Token[] currentTokens,
            int fileOffset,
            Runspace runspace)
        {
            var type   = scriptAst.Extent.StartScriptPosition.GetType();
            var method =
#if NanoServer
                type.GetMethod(
                    "CloneWithNewOffset",
                    BindingFlags.Instance | BindingFlags.NonPublic);
#else
                type.GetMethod(
                    "CloneWithNewOffset",
                    BindingFlags.Instance | BindingFlags.NonPublic,
                    null,
                    new[] { typeof(int) }, null);
#endif

            IScriptPosition cursorPosition =
                (IScriptPosition)method.Invoke(
                    scriptAst.Extent.StartScriptPosition,
                    new object[] { fileOffset });

            Logger.Write(
                LogLevel.Verbose,
                string.Format(
                    "Getting completions at offset {0} (line: {1}, column: {2})",
                    fileOffset,
                    cursorPosition.LineNumber,
                    cursorPosition.ColumnNumber));

            CommandCompletion commandCompletion = null;
            if (runspace.RunspaceAvailability == RunspaceAvailability.Available)
            {
                using (System.Management.Automation.PowerShell powerShell =
                           System.Management.Automation.PowerShell.Create())
                {
                    powerShell.Runspace = runspace;

                    commandCompletion =
                        CommandCompletion.CompleteInput(
                            scriptAst,
                            currentTokens,
                            cursorPosition,
                            null,
                            powerShell);
                }
            }

            return(commandCompletion);
        }
예제 #6
0
 public CommandCompletion CompleteInput(string input, int cursorIndex)
 {
     System.Management.Automation.PowerShell ps;
     if (!IsRemote)
     {
         ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace);
     }
     else
     {
         ps          = System.Management.Automation.PowerShell.Create();
         ps.Runspace = _runSpace;
     }
     return(CommandCompletion.CompleteInput(input, cursorIndex, null, ps));
 }
예제 #7
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);
        }
예제 #8
0
        /// <summary>
        /// Gets completions for the symbol found in the Ast at
        /// the given file offset.
        /// </summary>
        /// <param name="scriptAst">
        /// The Ast which will be traversed to find a completable symbol.
        /// </param>
        /// <param name="currentTokens">
        /// The array of tokens corresponding to the scriptAst parameter.
        /// </param>
        /// <param name="fileOffset">
        /// The 1-based file offset at which a symbol will be located.
        /// </param>
        /// <param name="executionService">
        /// The PowerShellContext to use for gathering completions.
        /// </param>
        /// <param name="logger">An ILogger implementation used for writing log messages.</param>
        /// <param name="cancellationToken">
        /// A CancellationToken to cancel completion requests.
        /// </param>
        /// <returns>
        /// A CommandCompletion instance that contains completions for the
        /// symbol at the given offset.
        /// </returns>
        public static async Task <CommandCompletion> GetCompletionsAsync(
            Ast scriptAst,
            Token[] currentTokens,
            int fileOffset,
            IInternalPowerShellExecutionService executionService,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            IScriptPosition cursorPosition = s_clonePositionWithNewOffset(scriptAst.Extent.StartScriptPosition, fileOffset);

            logger.LogTrace(
                string.Format(
                    "Getting completions at offset {0} (line: {1}, column: {2})",
                    fileOffset,
                    cursorPosition.LineNumber,
                    cursorPosition.ColumnNumber));

            Stopwatch stopwatch = new();

            CommandCompletion commandCompletion = null;
            await executionService.ExecuteDelegateAsync(
                representation : "CompleteInput",
                new ExecutionOptions { Priority = ExecutionPriority.Next },
                (pwsh, _) =>
            {
                stopwatch.Start();
                commandCompletion = CommandCompletion.CompleteInput(
                    scriptAst,
                    currentTokens,
                    cursorPosition,
                    options: null,
                    powershell: pwsh);
            },
                cancellationToken)
            .ConfigureAwait(false);

            stopwatch.Stop();
            logger.LogTrace($"IntelliSense completed in {stopwatch.ElapsedMilliseconds}ms.");

            return(commandCompletion);
        }
예제 #9
0
        /// <summary>
        /// Gets completions for the symbol found in the Ast at
        /// the given file offset.
        /// </summary>
        /// <param name="scriptAst">
        /// The Ast which will be traversed to find a completable symbol.
        /// </param>
        /// <param name="currentTokens">
        /// The array of tokens corresponding to the scriptAst parameter.
        /// </param>
        /// <param name="fileOffset">
        /// The 1-based file offset at which a symbol will be located.
        /// </param>
        /// <param name="runspace">
        /// The Runspace to use for gathering completions.
        /// </param>
        /// <returns>
        /// A CommandCompletion instance that contains completions for the
        /// symbol at the given offset.
        /// </returns>
        static public CommandCompletion GetCompletions(
            Ast scriptAst,
            Token[] currentTokens,
            int fileOffset,
            Runspace runspace)
        {
            var type   = scriptAst.Extent.StartScriptPosition.GetType();
            var method =
                type.GetMethod(
                    "CloneWithNewOffset",
                    BindingFlags.Instance | BindingFlags.NonPublic,
                    null,
                    new[] { typeof(int) }, null);

            IScriptPosition cursorPosition =
                (IScriptPosition)method.Invoke(
                    scriptAst.Extent.StartScriptPosition,
                    new object[] { fileOffset });

            CommandCompletion commandCompletion = null;

            if (runspace.RunspaceAvailability == RunspaceAvailability.Available)
            {
                using (System.Management.Automation.PowerShell powerShell =
                           System.Management.Automation.PowerShell.Create())
                {
                    powerShell.Runspace = runspace;

                    commandCompletion =
                        CommandCompletion.CompleteInput(
                            scriptAst,
                            currentTokens,
                            cursorPosition,
                            null,
                            powerShell);
                }
            }

            return(commandCompletion);
        }
예제 #10
0
        public static void InvokeCompletionWindow(TextEditor textEditor)
        {
            completionWindow = new CompletionWindow(textEditor.TextArea);

            completionWindow.Closed += delegate
            {
                completionWindow = null;
            };

            var text   = textEditor.Text;
            var offset = textEditor.TextArea.Caret.Offset;

            var completedInput = CommandCompletion.CompleteInput(text, offset, null, PSConsolePowerShell.PowerShellInstance);

            // var r = CommandCompletion.MapStringInputToParsedInput(text, offset);

            "InvokeCompletedInput".ExecuteScriptEntryPoint(completedInput.CompletionMatches);

            if (completedInput.CompletionMatches.Count > 0)
            {
                completedInput.CompletionMatches.ToList()
                .ForEach(record =>
                {
                    completionWindow.CompletionList.CompletionData.Add(
                        new CompletionData
                    {
                        CompletionText    = record.CompletionText,
                        ToolTip           = record.ToolTip,
                        Resultype         = record.ResultType,
                        ReplacementLength = completedInput.ReplacementLength
                    });
                });

                completionWindow.Show();
            }
        }
        private IList <ICompletionData> GetPowershellCompletion(string completionWord, string content, int caretOffset)
        {
            var completionData = new List <ICompletionData>();

            // Get built in PS completion
            try {
                Debug.WriteLine("GetPowershellCompletion(" + completionWord + ", [content truncated], " + caretOffset + ")");

                /*var result =
                 *  CommandCompletion.CompleteInput(
                 *      content, caretOffset, null, _powershell).CompletionMatches;
                 *
                 */
                Ast             ast;
                Token[]         tokens;
                IScriptPosition cursorPosition;

                GetCommandCompletionParameters(content, caretOffset, out ast, out tokens, out cursorPosition);

                if (ast == null)
                {
                    return(null);
                }

                CommandCompletion commandCompletion = null;

                /*if (runspace.RunspaceAvailability == RunspaceAvailability.Available)
                 * {
                 *  using (_currentPowerShell = PowerShell.Create())
                 *  {
                 *      _currentPowerShell.Runspace = runspace;
                 *      commandCompletion = CommandCompletion.CompleteInput(ast, tokens, cursorPosition, null, _currentPowerShell);
                 *  }
                 * }*/
                commandCompletion = CommandCompletion.CompleteInput(ast, tokens, cursorPosition, null, _powershell);

                foreach (var item in commandCompletion.CompletionMatches)
                {
                    switch (item.ResultType)
                    {
                    case CompletionResultType.Type:
                    case CompletionResultType.Keyword:
                        completionData.Add(new KeywordCompletionData(item.CompletionText, Glyph.Keyword, item.ToolTip));
                        break;

                    case CompletionResultType.Command:
                        completionData.Add(new KeywordCompletionData(item.CompletionText, Glyph.MethodPublic, item.ToolTip));
                        break;

                    case CompletionResultType.ParameterName:
                        completionData.Add(new ParameterCompletionData(item.CompletionText, string.Empty, item.ToolTip));
                        break;

                    case CompletionResultType.ParameterValue:
                        completionData.Add(new ParameterValueCompletionData(item.CompletionText, item.ToolTip));
                        break;

                    case CompletionResultType.Property:
                        completionData.Add(new ParameterCompletionData(item.CompletionText, string.Empty, item.ToolTip, false));
                        break;

                    case CompletionResultType.Variable:
                        completionData.Add(new VariableCompletionData(item.CompletionText, string.Empty));
                        break;
                    }
                }
            }
            catch (PSInvalidOperationException ex)
            {
                Logger.Debug("Error when trying to code complete.", ex);
            }

            return(completionData);
        }
예제 #12
0
        // TODO: BRING THIS BACK
        /// <summary>
        /// Gets completions for the symbol found in the Ast at
        /// the given file offset.
        /// </summary>
        /// <param name="scriptAst">
        /// The Ast which will be traversed to find a completable symbol.
        /// </param>
        /// <param name="currentTokens">
        /// The array of tokens corresponding to the scriptAst parameter.
        /// </param>
        /// <param name="fileOffset">
        /// The 1-based file offset at which a symbol will be located.
        /// </param>
        /// <param name="powerShellContext">
        /// The PowerShellContext to use for gathering completions.
        /// </param>
        /// <param name="logger">An ILogger implementation used for writing log messages.</param>
        /// <param name="cancellationToken">
        /// A CancellationToken to cancel completion requests.
        /// </param>
        /// <returns>
        /// A CommandCompletion instance that contains completions for the
        /// symbol at the given offset.
        /// </returns>
        public static async Task <CommandCompletion> GetCompletionsAsync(
            Ast scriptAst,
            Token[] currentTokens,
            int fileOffset,
            PowerShellContextService powerShellContext,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            if (!s_completionHandle.Wait(0))
            {
                return(null);
            }

            try
            {
                IScriptPosition cursorPosition = (IScriptPosition)s_extentCloneWithNewOffset.Invoke(
                    scriptAst.Extent.StartScriptPosition,
                    new object[] { fileOffset });

                logger.LogTrace(
                    string.Format(
                        "Getting completions at offset {0} (line: {1}, column: {2})",
                        fileOffset,
                        cursorPosition.LineNumber,
                        cursorPosition.ColumnNumber));

                if (!powerShellContext.IsAvailable)
                {
                    return(null);
                }

                var stopwatch = new Stopwatch();

                // If the current runspace is out of process we can use
                // CommandCompletion.CompleteInput because PSReadLine won't be taking up the
                // main runspace.
                if (powerShellContext.IsCurrentRunspaceOutOfProcess())
                {
                    using (RunspaceHandle runspaceHandle = await powerShellContext.GetRunspaceHandleAsync(cancellationToken))
                        using (System.Management.Automation.PowerShell powerShell = System.Management.Automation.PowerShell.Create())
                        {
                            powerShell.Runspace = runspaceHandle.Runspace;
                            stopwatch.Start();
                            try
                            {
                                return(CommandCompletion.CompleteInput(
                                           scriptAst,
                                           currentTokens,
                                           cursorPosition,
                                           options: null,
                                           powershell: powerShell));
                            }
                            finally
                            {
                                stopwatch.Stop();
                                logger.LogTrace($"IntelliSense completed in {stopwatch.ElapsedMilliseconds}ms.");
                            }
                        }
                }

                CommandCompletion commandCompletion = null;
                await powerShellContext.InvokeOnPipelineThreadAsync(
                    pwsh =>
                {
                    stopwatch.Start();
                    commandCompletion = CommandCompletion.CompleteInput(
                        scriptAst,
                        currentTokens,
                        cursorPosition,
                        options: null,
                        powershell: pwsh);
                });

                stopwatch.Stop();
                logger.LogTrace($"IntelliSense completed in {stopwatch.ElapsedMilliseconds}ms.");

                return(commandCompletion);
            }
            finally
            {
                s_completionHandle.Release();
            }
        }
        /// <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);
예제 #14
0
 CommandCompletion IPSConsoleReadLineMockableMethods.CompleteInput(string input, int cursorIndex, Hashtable options, System.Management.Automation.PowerShell powershell)
 {
     return(CallPossibleExternalApplication(
                () => CommandCompletion.CompleteInput(input, cursorIndex, options, powershell)));
 }
예제 #15
0
 CommandCompletion IPSConsoleReadLineMockableMethods.CompleteInput(string input, int cursorIndex, Hashtable options, System.Management.Automation.PowerShell powershell)
 {
     return(CalloutUsingDefaultConsoleMode(
                () => CommandCompletion.CompleteInput(input, cursorIndex, options, powershell)));
 }
예제 #16
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);
        }
예제 #17
0
 public CommandCompletion InvokeCommandCompletion(CompleteRequest req)
 {
     using var ps = PowerShell.Create();
     ps.Runspace  = this.psRunspace;
     return(CommandCompletion.CompleteInput(req.Code, req.CursorPosition, null, ps));
 }
예제 #18
0
        // Code completion

        private void DoCodeCompletion(CodeCompletionRequest request)
        {
            var result = CommandCompletion.CompleteInput(request.Input, request.CaretOffset, null, _powerShell);

            request.CompletionResult = result;
        }
예제 #19
0
        /// <summary>
        /// Gets completions for the symbol found in the Ast at
        /// the given file offset.
        /// </summary>
        /// <param name="scriptAst">
        /// The Ast which will be traversed to find a completable symbol.
        /// </param>
        /// <param name="currentTokens">
        /// The array of tokens corresponding to the scriptAst parameter.
        /// </param>
        /// <param name="fileOffset">
        /// The 1-based file offset at which a symbol will be located.
        /// </param>
        /// <param name="powerShellContext">
        /// The PowerShellContext to use for gathering completions.
        /// </param>
        /// <param name="logger">An ILogger implementation used for writing log messages.</param>
        /// <param name="cancellationToken">
        /// A CancellationToken to cancel completion requests.
        /// </param>
        /// <returns>
        /// A CommandCompletion instance that contains completions for the
        /// symbol at the given offset.
        /// </returns>
        static public async Task <CommandCompletion> GetCompletions(
            Ast scriptAst,
            Token[] currentTokens,
            int fileOffset,
            PowerShellContext powerShellContext,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            var type   = scriptAst.Extent.StartScriptPosition.GetType();
            var method =
#if CoreCLR
                type.GetMethod(
                    "CloneWithNewOffset",
                    BindingFlags.Instance | BindingFlags.NonPublic);
#else
                type.GetMethod(
                    "CloneWithNewOffset",
                    BindingFlags.Instance | BindingFlags.NonPublic,
                    null,
                    new[] { typeof(int) }, null);
#endif

            IScriptPosition cursorPosition =
                (IScriptPosition)method.Invoke(
                    scriptAst.Extent.StartScriptPosition,
                    new object[] { fileOffset });

            logger.Write(
                LogLevel.Verbose,
                string.Format(
                    "Getting completions at offset {0} (line: {1}, column: {2})",
                    fileOffset,
                    cursorPosition.LineNumber,
                    cursorPosition.ColumnNumber));

            CommandCompletion commandCompletion = null;
            if (powerShellContext.IsDebuggerStopped)
            {
                PSCommand command = new PSCommand();
                command.AddCommand("TabExpansion2");
                command.AddParameter("Ast", scriptAst);
                command.AddParameter("Tokens", currentTokens);
                command.AddParameter("PositionOfCursor", cursorPosition);
                command.AddParameter("Options", null);

                PSObject outputObject =
                    (await powerShellContext.ExecuteCommand <PSObject>(command, false, false))
                    .FirstOrDefault();

                if (outputObject != null)
                {
                    ErrorRecord errorRecord = outputObject.BaseObject as ErrorRecord;
                    if (errorRecord != null)
                    {
                        logger.WriteException(
                            "Encountered an error while invoking TabExpansion2 in the debugger",
                            errorRecord.Exception);
                    }
                    else
                    {
                        commandCompletion = outputObject.BaseObject as CommandCompletion;
                    }
                }
            }
            else if (powerShellContext.CurrentRunspace.Runspace.RunspaceAvailability ==
                     RunspaceAvailability.Available)
            {
                using (RunspaceHandle runspaceHandle = await powerShellContext.GetRunspaceHandle(cancellationToken))
                    using (PowerShell powerShell = PowerShell.Create())
                    {
                        powerShell.Runspace = runspaceHandle.Runspace;

                        Stopwatch stopwatch = new Stopwatch();
                        stopwatch.Start();

                        commandCompletion =
                            CommandCompletion.CompleteInput(
                                scriptAst,
                                currentTokens,
                                cursorPosition,
                                null,
                                powerShell);

                        stopwatch.Stop();

                        logger.Write(LogLevel.Verbose, $"IntelliSense completed in {stopwatch.ElapsedMilliseconds}ms.");
                    }
            }

            return(commandCompletion);
        }