/// <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); }
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); }
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; }
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"); } }
/// <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); }
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)); }
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> /// 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); }
/// <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); }
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); }
// 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);
CommandCompletion IPSConsoleReadLineMockableMethods.CompleteInput(string input, int cursorIndex, Hashtable options, System.Management.Automation.PowerShell powershell) { return(CallPossibleExternalApplication( () => CommandCompletion.CompleteInput(input, cursorIndex, options, powershell))); }
CommandCompletion IPSConsoleReadLineMockableMethods.CompleteInput(string input, int cursorIndex, Hashtable options, System.Management.Automation.PowerShell powershell) { return(CalloutUsingDefaultConsoleMode( () => CommandCompletion.CompleteInput(input, cursorIndex, options, powershell))); }
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); }
public CommandCompletion InvokeCommandCompletion(CompleteRequest req) { using var ps = PowerShell.Create(); ps.Runspace = this.psRunspace; return(CommandCompletion.CompleteInput(req.Code, req.CursorPosition, null, ps)); }
// Code completion private void DoCodeCompletion(CodeCompletionRequest request) { var result = CommandCompletion.CompleteInput(request.Input, request.CaretOffset, null, _powerShell); request.CompletionResult = result; }
/// <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); }