/// <summary> /// Gets completions for a statement contained in the given /// script file at the specified line and column position. /// </summary> /// <param name="scriptFile"> /// The script file in which completions will be gathered. /// </param> /// <param name="lineNumber"> /// The 1-based line number at which completions will be gathered. /// </param> /// <param name="columnNumber"> /// The 1-based column number at which completions will be gathered. /// </param> /// <returns> /// A CommandCompletion instance completions for the identified statement. /// </returns> public async Task <CompletionResults> GetCompletionsInFile( ScriptFile scriptFile, int lineNumber, int columnNumber) { Validate.IsNotNull("scriptFile", scriptFile); // Get the offset at the specified position. This method // will also validate the given position. int fileOffset = scriptFile.GetOffsetAtPosition( lineNumber, columnNumber); RunspaceHandle runspaceHandle = await this.powerShellContext.GetRunspaceHandle(); CompletionResults completionResults = AstOperations.GetCompletions( scriptFile.ScriptAst, scriptFile.ScriptTokens, fileOffset, runspaceHandle.Runspace); runspaceHandle.Dispose(); // save state of most recent completion mostRecentCompletions = completionResults; mostRecentRequestFile = scriptFile.Id; mostRecentRequestLine = lineNumber; mostRecentRequestOffest = columnNumber; return(completionResults); }
/// <summary> /// Finds the details of the symbol at the given script file location. /// </summary> /// <param name="scriptFile">The ScriptFile in which the symbol can be located.</param> /// <param name="lineNumber">The line number at which the symbol can be located.</param> /// <param name="columnNumber">The column number at which the symbol can be located.</param> /// <returns></returns> public async Task <SymbolDetails> FindSymbolDetailsAtLocation( ScriptFile scriptFile, int lineNumber, int columnNumber) { SymbolDetails symbolDetails = null; SymbolReference symbolReference = AstOperations.FindSymbolAtPosition( scriptFile.ScriptAst, lineNumber, columnNumber); if (symbolReference != null) { RunspaceHandle runspaceHandle = await this.powerShellContext.GetRunspaceHandle(); symbolReference.FilePath = scriptFile.FilePath; symbolDetails = new SymbolDetails(symbolReference, runspaceHandle.Runspace); runspaceHandle.Dispose(); } else { // TODO #21: Return Result<T> return(null); } return(symbolDetails); }
//public SymbolDetails GetSymbolDetails() #endregion #region Private Fields /// <summary> /// Gets all aliases found in the runspace /// </summary> private async Task GetAliases() { if (!this.areAliasesLoaded) { RunspaceHandle runspaceHandle = await this.powerShellContext.GetRunspaceHandle(); CommandInvocationIntrinsics invokeCommand = runspaceHandle.Runspace.SessionStateProxy.InvokeCommand; IEnumerable <CommandInfo> aliases = invokeCommand.GetCommands("*", CommandTypes.Alias, true); runspaceHandle.Dispose(); foreach (AliasInfo aliasInfo in aliases) { if (!CmdletToAliasDictionary.ContainsKey(aliasInfo.Definition)) { CmdletToAliasDictionary.Add(aliasInfo.Definition, new List <String>() { aliasInfo.Name }); } else { CmdletToAliasDictionary[aliasInfo.Definition].Add(aliasInfo.Name); } AliasToCmdletDictionary.Add(aliasInfo.Name, aliasInfo.Definition); } this.areAliasesLoaded = true; } }
/// <summary> /// Finds the details of the symbol at the given script file location. /// </summary> /// <param name="scriptFile">The ScriptFile in which the symbol can be located.</param> /// <param name="lineNumber">The line number at which the symbol can be located.</param> /// <param name="columnNumber">The column number at which the symbol can be located.</param> /// <returns></returns> public async Task <SymbolDetails> FindSymbolDetailsAtLocation( ScriptFile scriptFile, int lineNumber, int columnNumber) { SymbolDetails symbolDetails = null; SymbolReference symbolReference = AstOperations.FindSymbolAtPosition( scriptFile.ScriptAst, lineNumber, columnNumber); if (symbolReference != null) { // Request a runspace handle with a short timeout RunspaceHandle runspaceHandle = await this.powerShellContext.GetRunspaceHandle( new CancellationTokenSource(DefaultWaitTimeoutMilliseconds).Token); symbolReference.FilePath = scriptFile.FilePath; symbolDetails = new SymbolDetails(symbolReference, runspaceHandle.Runspace); runspaceHandle.Dispose(); } else { // TODO #21: Return Result<T> return(null); } return(symbolDetails); }
internal void ReleaseRunspaceHandle(RunspaceHandle runspaceHandle) { Validate.IsNotNull("runspaceHandle", runspaceHandle); IDisposable dequeuedTask = null; lock (this.runspaceMutex) { if (runspaceHandle != this.currentRunspaceHandle) { throw new InvalidOperationException("Released runspace handle was not the current handle."); } this.currentRunspaceHandle = null; if (!this.runspaceWaitQueue.IsEmpty) { this.currentRunspaceHandle = new RunspaceHandle(this.currentRunspace, this); dequeuedTask = this.runspaceWaitQueue.Dequeue( this.currentRunspaceHandle); } } // If a queued task was dequeued, call Dispose to cause it to be executed. if (dequeuedTask != null) { dequeuedTask.Dispose(); } }
/// <summary> /// Gets completions for a statement contained in the given /// script file at the specified line and column position. /// </summary> /// <param name="scriptFile"> /// The script file in which completions will be gathered. /// </param> /// <param name="lineNumber"> /// The 1-based line number at which completions will be gathered. /// </param> /// <param name="columnNumber"> /// The 1-based column number at which completions will be gathered. /// </param> /// <returns> /// A CommandCompletion instance completions for the identified statement. /// </returns> public async Task <CompletionResults> GetCompletionsInFile( ScriptFile scriptFile, int lineNumber, int columnNumber) { Validate.IsNotNull("scriptFile", scriptFile); // Get the offset at the specified position. This method // will also validate the given position. int fileOffset = scriptFile.GetOffsetAtPosition( lineNumber, columnNumber); RunspaceHandle runspaceHandle = await this.powerShellContext.GetRunspaceHandle( new CancellationTokenSource(DefaultWaitTimeoutMilliseconds).Token); CommandCompletion commandCompletion = AstOperations.GetCompletions( scriptFile.ScriptAst, scriptFile.ScriptTokens, fileOffset, runspaceHandle.Runspace); runspaceHandle.Dispose(); if (commandCompletion != null) { try { CompletionResults completionResults = CompletionResults.Create( scriptFile, commandCompletion); // save state of most recent completion mostRecentCompletions = completionResults; mostRecentRequestFile = scriptFile.Id; mostRecentRequestLine = lineNumber; mostRecentRequestOffest = columnNumber; return(completionResults); } catch (ArgumentException e) { // Bad completion results could return an invalid // replacement range, catch that here Logger.Write( LogLevel.Error, $"Caught exception while trying to create CompletionResults:\n\n{e.ToString()}"); } } // If all else fails, return empty results return(new CompletionResults()); }
/// <summary> /// Gets all aliases found in the runspace /// </summary> private async Task GetAliases() { if (_areAliasesLoaded) { return; } try { RunspaceHandle runspaceHandle = await _powerShellContext.GetRunspaceHandle( new CancellationTokenSource(DefaultWaitTimeoutMilliseconds).Token); CommandInvocationIntrinsics invokeCommand = runspaceHandle.Runspace.SessionStateProxy.InvokeCommand; IEnumerable <CommandInfo> aliases = invokeCommand.GetCommands("*", CommandTypes.Alias, true); runspaceHandle.Dispose(); foreach (AliasInfo aliasInfo in aliases) { if (!_cmdletToAliasDictionary.ContainsKey(aliasInfo.Definition)) { _cmdletToAliasDictionary.Add(aliasInfo.Definition, new List <String> { aliasInfo.Name }); } else { _cmdletToAliasDictionary[aliasInfo.Definition].Add(aliasInfo.Name); } _aliasToCmdletDictionary.Add(aliasInfo.Name, aliasInfo.Definition); } _areAliasesLoaded = true; } catch (PSNotSupportedException e) { _logger.Write( LogLevel.Warning, $"Caught PSNotSupportedException while attempting to get aliases from remote session:\n\n{e.ToString()}"); // Prevent the aliases from being fetched again - no point if the remote doesn't support InvokeCommand. _areAliasesLoaded = true; } catch (TaskCanceledException) { // The wait for a RunspaceHandle has timed out, skip aliases for now } }
/// <summary> /// Gets completions for a statement contained in the given /// script file at the specified line and column position. /// </summary> /// <param name="scriptFile"> /// The script file in which completions will be gathered. /// </param> /// <param name="lineNumber"> /// The 1-based line number at which completions will be gathered. /// </param> /// <param name="columnNumber"> /// The 1-based column number at which completions will be gathered. /// </param> /// <returns> /// A CommandCompletion instance completions for the identified statement. /// </returns> public async Task <CompletionResults> GetCompletionsInFile( ScriptFile scriptFile, int lineNumber, int columnNumber) { Validate.IsNotNull("scriptFile", scriptFile); // Get the offset at the specified position. This method // will also validate the given position. int fileOffset = scriptFile.GetOffsetAtPosition( lineNumber, columnNumber); RunspaceHandle runspaceHandle = await this.powerShellContext.GetRunspaceHandle( new CancellationTokenSource(DefaultWaitTimeoutMilliseconds).Token); CommandCompletion commandCompletion = AstOperations.GetCompletions( scriptFile.ScriptAst, scriptFile.ScriptTokens, fileOffset, runspaceHandle.Runspace); runspaceHandle.Dispose(); if (commandCompletion != null) { CompletionResults completionResults = CompletionResults.Create( scriptFile, commandCompletion); // save state of most recent completion mostRecentCompletions = completionResults; mostRecentRequestFile = scriptFile.Id; mostRecentRequestLine = lineNumber; mostRecentRequestOffest = columnNumber; return(completionResults); } else { return(new CompletionResults()); } }
internal void ReleaseRunspaceHandle(RunspaceHandle runspaceHandle) { Validate.IsNotNull("runspaceHandle", runspaceHandle); if (this.runspaceWaitQueue.IsEmpty) { var newRunspaceHandle = new RunspaceHandle(this.currentRunspace, this); this.runspaceWaitQueue.EnqueueAsync(newRunspaceHandle).Wait(); } else { // Write the situation to the log since this shouldn't happen Logger.Write( LogLevel.Error, "The PowerShellContext.runspaceWaitQueue has more than one item"); } }
/// <summary> /// Gets a RunspaceHandle for the session's runspace. This /// handle is used to gain temporary ownership of the runspace /// so that commands can be executed against it directly. /// </summary> /// <returns>A RunspaceHandle instance that gives access to the session's runspace.</returns> public Task <RunspaceHandle> GetRunspaceHandle() { lock (this.runspaceMutex) { if (this.currentRunspaceHandle == null) { this.currentRunspaceHandle = new RunspaceHandle(this.currentRunspace, this); TaskCompletionSource <RunspaceHandle> tcs = new TaskCompletionSource <RunspaceHandle>(); tcs.SetResult(this.currentRunspaceHandle); return(tcs.Task); } else { // TODO: Use CancellationToken? return(this.runspaceWaitQueue.Enqueue()); } } }
private void Initialize(Runspace initialRunspace) { Validate.IsNotNull("initialRunspace", initialRunspace); this.SessionState = PowerShellContextState.NotStarted; this.initialRunspace = initialRunspace; this.currentRunspace = initialRunspace; this.currentRunspace.Debugger.BreakpointUpdated += OnBreakpointUpdated; this.currentRunspace.Debugger.DebuggerStop += OnDebuggerStop; this.powerShell = PowerShell.Create(); this.powerShell.InvocationStateChanged += powerShell_InvocationStateChanged; this.powerShell.Runspace = this.currentRunspace; // TODO: Should this be configurable? this.SetExecutionPolicy(ExecutionPolicy.RemoteSigned); // Get the PowerShell runtime version this.PowerShellVersion = GetPowerShellVersion(); // Write out the PowerShell version for tracking purposes Logger.Write( LogLevel.Normal, string.Format( "PowerShell runtime version: {0}", this.PowerShellVersion)); #if !PowerShellv3 if (PowerShellVersion > new Version(3, 0)) { this.currentRunspace.Debugger.SetDebugMode(DebugModes.LocalScript | DebugModes.RemoteScript); } #endif this.SessionState = PowerShellContextState.Ready; // Now that the runspace is ready, enqueue it for first use RunspaceHandle runspaceHandle = new RunspaceHandle(this.currentRunspace, this); this.runspaceWaitQueue.EnqueueAsync(runspaceHandle).Wait(); }
/// <summary> /// Gets all aliases found in the runspace /// </summary> private async Task GetAliases() { if (!this.areAliasesLoaded) { try { RunspaceHandle runspaceHandle = await this.powerShellContext.GetRunspaceHandle( new CancellationTokenSource(DefaultWaitTimeoutMilliseconds).Token); CommandInvocationIntrinsics invokeCommand = runspaceHandle.Runspace.SessionStateProxy.InvokeCommand; IEnumerable <CommandInfo> aliases = invokeCommand.GetCommands("*", CommandTypes.Alias, true); runspaceHandle.Dispose(); foreach (AliasInfo aliasInfo in aliases) { if (!CmdletToAliasDictionary.ContainsKey(aliasInfo.Definition)) { CmdletToAliasDictionary.Add(aliasInfo.Definition, new List <String>() { aliasInfo.Name }); } else { CmdletToAliasDictionary[aliasInfo.Definition].Add(aliasInfo.Name); } AliasToCmdletDictionary.Add(aliasInfo.Name, aliasInfo.Definition); } this.areAliasesLoaded = true; } catch (TaskCanceledException) { // The wait for a RunspaceHandle has timed out, skip aliases for now } } }
/// <summary> /// Executes a PSCommand against the session's runspace and returns /// a collection of results of the expected type. /// </summary> /// <typeparam name="TResult">The expected result type.</typeparam> /// <param name="psCommand">The PSCommand to be executed.</param> /// <param name="sendOutputToHost"> /// If true, causes any output written during command execution to be written to the host. /// </param> /// <param name="sendErrorToHost"> /// If true, causes any errors encountered during command execution to be written to the host. /// </param> /// <returns> /// An awaitable Task which will provide results once the command /// execution completes. /// </returns> public async Task <IEnumerable <TResult> > ExecuteCommand <TResult>( PSCommand psCommand, bool sendOutputToHost = false, bool sendErrorToHost = true) { RunspaceHandle runspaceHandle = null; IEnumerable <TResult> executionResult = null; // If the debugger is active and the caller isn't on the pipeline // thread, send the command over to that thread to be executed. if (Thread.CurrentThread.ManagedThreadId != this.pipelineThreadId && this.pipelineExecutionTask != null) { Logger.Write(LogLevel.Verbose, "Passing command execution to pipeline thread."); PipelineExecutionRequest <TResult> executionRequest = new PipelineExecutionRequest <TResult>( this, psCommand, sendOutputToHost); // Send the pipeline execution request to the pipeline thread this.pipelineResultTask = new TaskCompletionSource <IPipelineExecutionRequest>(); this.pipelineExecutionTask.SetResult(executionRequest); await this.pipelineResultTask.Task; return(executionRequest.Results); } else { try { // Instruct PowerShell to send output and errors to the host if (sendOutputToHost) { psCommand.Commands[0].MergeMyResults( PipelineResultTypes.Error, PipelineResultTypes.Output); psCommand.Commands.Add( this.GetOutputCommand( endOfStatement: false)); } if (this.currentRunspace.RunspaceAvailability == RunspaceAvailability.AvailableForNestedCommand || this.debuggerStoppedTask != null) { Logger.Write( LogLevel.Verbose, string.Format( "Attempting to execute nested pipeline command(s):\r\n\r\n{0}", GetStringForPSCommand(psCommand))); executionResult = this.ExecuteCommandInDebugger <TResult>( psCommand, sendOutputToHost); } else { Logger.Write( LogLevel.Verbose, string.Format( "Attempting to execute command(s):\r\n\r\n{0}", GetStringForPSCommand(psCommand))); // Set the runspace runspaceHandle = await this.GetRunspaceHandle(); if (runspaceHandle.Runspace.RunspaceAvailability != RunspaceAvailability.AvailableForNestedCommand) { this.powerShell.Runspace = runspaceHandle.Runspace; } // Invoke the pipeline on a background thread // TODO: Use built-in async invocation! executionResult = await Task.Factory.StartNew <IEnumerable <TResult> >( () => { this.powerShell.Commands = psCommand; Collection <TResult> result = this.powerShell.Invoke <TResult>(); return(result); }, CancellationToken.None, // Might need a cancellation token TaskCreationOptions.None, TaskScheduler.Default ); if (this.powerShell.HadErrors) { string errorMessage = "Execution completed with errors:\r\n\r\n"; foreach (var error in this.powerShell.Streams.Error) { errorMessage += error.ToString() + "\r\n"; } Logger.Write(LogLevel.Error, errorMessage); } else { Logger.Write( LogLevel.Verbose, "Execution completed successfully."); } return(executionResult); } } catch (RuntimeException e) { Logger.Write( LogLevel.Error, "Runtime exception occurred while executing command:\r\n\r\n" + e.ToString()); if (sendErrorToHost) { // Write the error to the host this.WriteExceptionToHost(e); } } finally { // Get the new prompt before releasing the runspace handle if (sendOutputToHost) { // Write the prompt if (runspaceHandle != null) { this.WritePromptWithRunspace(runspaceHandle.Runspace); } else { this.WritePromptWithNestedPipeline(); } } // Dispose of the execution context if (runspaceHandle != null) { runspaceHandle.Dispose(); } } } return(executionResult); }
private void Initialize(HostDetails hostDetails, Runspace initialRunspace) { Validate.IsNotNull("initialRunspace", initialRunspace); this.SessionState = PowerShellContextState.NotStarted; this.initialRunspace = initialRunspace; this.currentRunspace = initialRunspace; this.currentRunspace.Debugger.BreakpointUpdated += OnBreakpointUpdated; this.currentRunspace.Debugger.DebuggerStop += OnDebuggerStop; this.powerShell = PowerShell.Create(); this.powerShell.Runspace = this.currentRunspace; // TODO: Should this be configurable? this.SetExecutionPolicy(ExecutionPolicy.RemoteSigned); // Get the PowerShell runtime version this.PowerShellVersion = GetPowerShellVersion(); // Write out the PowerShell version for tracking purposes Logger.Write( LogLevel.Normal, string.Format( "PowerShell runtime version: {0}", this.PowerShellVersion)); if (PowerShellVersion >= new Version(5, 0)) { this.versionSpecificOperations = new PowerShell5Operations(); } else if (PowerShellVersion.Major == 4) { this.versionSpecificOperations = new PowerShell4Operations(); } else if (PowerShellVersion.Major == 3) { this.versionSpecificOperations = new PowerShell3Operations(); } else { throw new NotSupportedException( "This computer has an unsupported version of PowerShell installed: " + PowerShellVersion.ToString()); } // Configure the runspace's debugger this.versionSpecificOperations.ConfigureDebugger( this.currentRunspace); // Set the $profile variable in the runspace this.profilePaths = this.SetProfileVariableInCurrentRunspace( hostDetails); // Now that initialization is complete we can watch for InvocationStateChanged this.powerShell.InvocationStateChanged += powerShell_InvocationStateChanged; this.SessionState = PowerShellContextState.Ready; // Now that the runspace is ready, enqueue it for first use RunspaceHandle runspaceHandle = new RunspaceHandle(this.currentRunspace, this); this.runspaceWaitQueue.EnqueueAsync(runspaceHandle).Wait(); }
/// <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); }
/// <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> GetCompletionsAsync( Ast scriptAst, Token[] currentTokens, int fileOffset, PowerShellContext 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.Write( LogLevel.Verbose, 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 (PowerShell powerShell = PowerShell.Create()) { powerShell.Runspace = runspaceHandle.Runspace; stopwatch.Start(); try { return(CommandCompletion.CompleteInput( scriptAst, currentTokens, cursorPosition, options: null, powershell: powerShell)); } finally { stopwatch.Stop(); logger.Write(LogLevel.Verbose, $"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.Write(LogLevel.Verbose, $"IntelliSense completed in {stopwatch.ElapsedMilliseconds}ms."); return(commandCompletion); } finally { s_completionHandle.Release(); } }