/// <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();
            }
        }
示例#6
0
        /// <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());
        }
示例#7
0
        /// <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());
         }
     }
 }
示例#11
0
        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();
        }
示例#15
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);
        }
        /// <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();
            }
        }