public async Task CanQueueParallelRunspaceRequests()
        {
            // Concurrently initiate 4 requests in the session
            this.powerShellContext.ExecuteScriptString("$x = 100");
            Task <RunspaceHandle> handleTask = this.powerShellContext.GetRunspaceHandle();

            this.powerShellContext.ExecuteScriptString("$x += 200");
            this.powerShellContext.ExecuteScriptString("$x = $x / 100");

            PSCommand psCommand = new PSCommand();

            psCommand.AddScript("$x");
            Task <IEnumerable <int> > resultTask = this.powerShellContext.ExecuteCommand <int>(psCommand);

            // Wait for the requested runspace handle and then dispose it
            RunspaceHandle handle = await handleTask;

            handle.Dispose();

            // At this point, the remaining command executions should execute and complete
            int result = (await resultTask).FirstOrDefault();

            // 100 + 200 = 300, then divided by 100 is 3.  We are ensuring that
            // the commands were executed in the sequence they were called.
            Assert.Equal(3, result);
        }
        /// <summary>
        /// Initializes this ExtensionService using the provided IEditorOperations
        /// implementation for future interaction with the host editor.
        /// </summary>
        /// <param name="editorOperations">An IEditorOperations implementation.</param>
        /// <returns>A Task that can be awaited for completion.</returns>
        internal async Task InitializeAsync(
            IServiceProvider serviceProvider,
            IEditorOperations editorOperations)
        {
            // Attach to ExtensionService events
            this.CommandAdded   += ExtensionService_ExtensionAddedAsync;
            this.CommandUpdated += ExtensionService_ExtensionUpdatedAsync;
            this.CommandRemoved += ExtensionService_ExtensionRemovedAsync;

            this.EditorObject =
                new EditorObject(
                    serviceProvider,
                    this,
                    editorOperations);

            // Assign the new EditorObject to be the static instance available to binary APIs
            this.EditorObject.SetAsStaticInstance();

            // Register the editor object in the runspace
            PSCommand variableCommand = new PSCommand();

            using (RunspaceHandle handle = await this.PowerShellContext.GetRunspaceHandleAsync().ConfigureAwait(false))
            {
                handle.Runspace.SessionStateProxy.PSVariable.Set(
                    "psEditor",
                    this.EditorObject);
            }
        }
        /// <summary>
        /// Initializes this ExtensionService using the provided IEditorOperations
        /// implementation for future interaction with the host editor.
        /// </summary>
        /// <param name="editorOperations">An IEditorOperations implementation.</param>
        /// <returns>A Task that can be awaited for completion.</returns>
        public async Task InitializeAsync(
            IServiceProvider serviceProvider,
            IEditorOperations editorOperations)
        {
            // Attach to ExtensionService events
            this.CommandAdded   += ExtensionService_ExtensionAddedAsync;
            this.CommandUpdated += ExtensionService_ExtensionUpdatedAsync;
            this.CommandRemoved += ExtensionService_ExtensionRemovedAsync;

            this.EditorObject =
                new EditorObject(
                    serviceProvider,
                    this,
                    editorOperations);

            // Register the editor object in the runspace
            PSCommand variableCommand = new PSCommand();

            using (RunspaceHandle handle = await this.PowerShellContext.GetRunspaceHandleAsync())
            {
                handle.Runspace.SessionStateProxy.PSVariable.Set(
                    "psEditor",
                    this.EditorObject);
            }
        }
        protected async Task HandleCompletionResolveRequest(
            CompletionItem completionItem,
            RequestContext <CompletionItem> requestContext)
        {
            if (completionItem.Kind == CompletionItemKind.Function)
            {
                RunspaceHandle runspaceHandle =
                    await editorSession.PowerShellContext.GetRunspaceHandle();

                // Get the documentation for the function
                CommandInfo commandInfo =
                    CommandHelpers.GetCommandInfo(
                        completionItem.Label,
                        runspaceHandle.Runspace);

                completionItem.Documentation =
                    CommandHelpers.GetCommandSynopsis(
                        commandInfo,
                        runspaceHandle.Runspace);

                runspaceHandle.Dispose();
            }

            // Send back the updated CompletionItem
            await requestContext.SendResult(completionItem);
        }
示例#5
0
        /// <summary>
        /// Initializes this ExtensionService using the provided IEditorOperations
        /// implementation for future interaction with the host editor.
        /// </summary>
        /// <param name="editorOperations">An IEditorOperations implementation.</param>
        /// <returns>A Task that can be awaited for completion.</returns>
        public async Task Initialize(IEditorOperations editorOperations)
        {
            this.EditorObject = new EditorObject(this, editorOperations);

            // Register the editor object in the runspace
            PSCommand variableCommand = new PSCommand();

            using (RunspaceHandle handle = await this.PowerShellContext.GetRunspaceHandle())
            {
                handle.Runspace.SessionStateProxy.PSVariable.Set(
                    "psEditor",
                    this.EditorObject);
            }

            // Load the cmdlet interface
            Type   thisType       = this.GetType();
            Stream resourceStream =
                thisType.Assembly.GetManifestResourceStream(
                    thisType.Namespace + ".CmdletInterface.ps1");

            using (StreamReader reader = new StreamReader(resourceStream))
            {
                // Create a temporary folder path
                string randomFileNamePart =
                    Path.GetFileNameWithoutExtension(
                        Path.GetRandomFileName());

                string tempScriptPath =
                    Path.Combine(
                        Path.GetTempPath(),
                        "PSES_ExtensionCmdlets_" + randomFileNamePart + ".ps1");

                Logger.Write(
                    LogLevel.Verbose,
                    "Executing extension API cmdlet script at path: " + tempScriptPath);

                // Read the cmdlet interface script and write it to a temporary
                // file so that we don't have to execute the full file contents
                // directly.  This keeps the script execution from creating a
                // lot of noise in the verbose logs.
                string cmdletInterfaceScript = reader.ReadToEnd();
                File.WriteAllText(
                    tempScriptPath,
                    cmdletInterfaceScript);

                await this.PowerShellContext.ExecuteScriptString(
                    ". " + tempScriptPath,
                    writeInputToHost : false,
                    writeOutputToHost : false);

                // Delete the temporary file
                File.Delete(tempScriptPath);
            }
        }
        /// <summary>
        /// Releases control of the runspace aquired via the <see cref="RunspaceHandle" />.
        /// </summary>
        /// <param name="runspaceHandle">
        /// The <see cref="RunspaceHandle" /> representing the control to release.
        /// </param>
        internal void ReleaseRunspaceHandle(RunspaceHandle runspaceHandle)
        {
            if (_isDisposed)
            {
                return;
            }

            ReleaseRunspaceHandleImpl(runspaceHandle.IsReadLine);
            if (runspaceHandle.IsReadLine && !_powerShellContext.IsCurrentRunspaceOutOfProcess())
            {
                ReleaseRunspaceHandleImpl(isReadLine: false);
            }
        }
        /// <summary>
        /// Initializes this ExtensionService using the provided IEditorOperations
        /// implementation for future interaction with the host editor.
        /// </summary>
        /// <param name="editorOperations">An IEditorOperations implementation.</param>
        /// <returns>A Task that can be awaited for completion.</returns>
        public async Task Initialize(IEditorOperations editorOperations)
        {
            this.EditorObject = new EditorObject(this, editorOperations);

            // Register the editor object in the runspace
            PSCommand variableCommand = new PSCommand();

            using (RunspaceHandle handle = await this.PowerShellContext.GetRunspaceHandle())
            {
                handle.Runspace.SessionStateProxy.PSVariable.Set(
                    "psEditor",
                    this.EditorObject);
            }
        }
        /// <summary>
        /// Releases control of the runspace aquired via the <see cref="RunspaceHandle" />.
        /// </summary>
        /// <param name="runspaceHandle">
        /// The <see cref="RunspaceHandle" /> representing the control to release.
        /// </param>
        /// <returns>
        /// A <see cref="Task" /> object representing the release of the
        /// <see cref="RunspaceHandle" />.
        /// </returns>
        internal async Task ReleaseRunspaceHandleAsync(RunspaceHandle runspaceHandle)
        {
            if (_isDisposed)
            {
                return;
            }

            await ReleaseRunspaceHandleImplAsync(runspaceHandle.IsReadLine);

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

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

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

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

                var stopwatch = new Stopwatch();

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

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

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

                return(commandCompletion);
            }
            finally
            {
                s_completionHandle.Release();
            }
        }
        /// <summary>
        /// Invokes a custom ReadLine method that is similar to but more basic than PSReadLine.
        /// This method should be used when PSReadLine is disabled, either by user settings or
        /// unsupported PowerShell versions.
        /// </summary>
        /// <param name="isCommandLine">
        /// Indicates whether ReadLine should act like a command line.
        /// </param>
        /// <param name="cancellationToken">
        /// The cancellation token that will be checked prior to completing the returned task.
        /// </param>
        /// <returns>
        /// A task object representing the asynchronus operation. The Result property on
        /// the task object returns the user input string.
        /// </returns>
        internal async Task <string> InvokeLegacyReadLineAsync(bool isCommandLine, CancellationToken cancellationToken)
        {
            string            inputBeforeCompletion = null;
            string            inputAfterCompletion  = null;
            CommandCompletion currentCompletion     = null;

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

            StringBuilder inputLine = new StringBuilder();

            int initialCursorCol = await ConsoleProxy.GetCursorLeftAsync(cancellationToken);

            int initialCursorRow = await ConsoleProxy.GetCursorTopAsync(cancellationToken);

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

            int currentCursorIndex = 0;

            Console.TreatControlCAsInput = true;

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

                    // Do final position calculation after the key has been pressed
                    // because the window could have been resized before then
                    int promptStartCol = initialCursorCol;
                    int promptStartRow = initialCursorRow;
                    int consoleWidth   = Console.WindowWidth;

                    if ((int)keyInfo.Key == 3 ||
                        keyInfo.Key == ConsoleKey.C && keyInfo.Modifiers.HasFlag(ConsoleModifiers.Control))
                    {
                        throw new PipelineStoppedException();
                    }
                    else if (keyInfo.Key == ConsoleKey.Tab && isCommandLine)
                    {
                        if (currentCompletion == null)
                        {
                            inputBeforeCompletion = inputLine.ToString();
                            inputAfterCompletion  = null;

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

                            if (this.powerShellContext.IsDebuggerStopped)
                            {
                                PSCommand command = new PSCommand();
                                command.AddCommand("TabExpansion2");
                                command.AddParameter("InputScript", inputBeforeCompletion);
                                command.AddParameter("CursorColumn", currentCursorIndex);
                                command.AddParameter("Options", null);

                                var results =
                                    await this.powerShellContext.ExecuteCommandAsync <CommandCompletion>(command, false, false);

                                currentCompletion = results.FirstOrDefault();
                            }
                            else
                            {
                                using (RunspaceHandle runspaceHandle = await this.powerShellContext.GetRunspaceHandleAsync())
                                    using (PowerShell powerShell = PowerShell.Create())
                                    {
                                        powerShell.Runspace = runspaceHandle.Runspace;
                                        currentCompletion   =
                                            CommandCompletion.CompleteInput(
                                                inputBeforeCompletion,
                                                currentCursorIndex,
                                                null,
                                                powerShell);

                                        if (currentCompletion.CompletionMatches.Count > 0)
                                        {
                                            int replacementEndIndex =
                                                currentCompletion.ReplacementIndex +
                                                currentCompletion.ReplacementLength;

                                            inputAfterCompletion =
                                                inputLine.ToString(
                                                    replacementEndIndex,
                                                    inputLine.Length - replacementEndIndex);
                                        }
                                        else
                                        {
                                            currentCompletion = null;
                                        }
                                    }
                            }
                        }

                        CompletionResult completion =
                            currentCompletion?.GetNextResult(
                                !keyInfo.Modifiers.HasFlag(ConsoleModifiers.Shift));

                        if (completion != null)
                        {
                            currentCursorIndex =
                                this.InsertInput(
                                    inputLine,
                                    promptStartCol,
                                    promptStartRow,
                                    $"{completion.CompletionText}{inputAfterCompletion}",
                                    currentCursorIndex,
                                    insertIndex: currentCompletion.ReplacementIndex,
                                    replaceLength: inputLine.Length - currentCompletion.ReplacementIndex,
                                    finalCursorIndex: currentCompletion.ReplacementIndex + completion.CompletionText.Length);
                        }
                    }
                    else if (keyInfo.Key == ConsoleKey.LeftArrow)
                    {
                        currentCompletion = null;

                        if (currentCursorIndex > 0)
                        {
                            currentCursorIndex =
                                this.MoveCursorToIndex(
                                    promptStartCol,
                                    promptStartRow,
                                    consoleWidth,
                                    currentCursorIndex - 1);
                        }
                    }
                    else if (keyInfo.Key == ConsoleKey.Home)
                    {
                        currentCompletion = null;

                        currentCursorIndex =
                            this.MoveCursorToIndex(
                                promptStartCol,
                                promptStartRow,
                                consoleWidth,
                                0);
                    }
                    else if (keyInfo.Key == ConsoleKey.RightArrow)
                    {
                        currentCompletion = null;

                        if (currentCursorIndex < inputLine.Length)
                        {
                            currentCursorIndex =
                                this.MoveCursorToIndex(
                                    promptStartCol,
                                    promptStartRow,
                                    consoleWidth,
                                    currentCursorIndex + 1);
                        }
                    }
                    else if (keyInfo.Key == ConsoleKey.End)
                    {
                        currentCompletion = null;

                        currentCursorIndex =
                            this.MoveCursorToIndex(
                                promptStartCol,
                                promptStartRow,
                                consoleWidth,
                                inputLine.Length);
                    }
                    else if (keyInfo.Key == ConsoleKey.UpArrow && isCommandLine)
                    {
                        currentCompletion = null;

                        // TODO: Ctrl+Up should allow navigation in multi-line input

                        if (currentHistory == null)
                        {
                            historyIndex = -1;

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

                            currentHistory =
                                await this.powerShellContext.ExecuteCommandAsync <PSObject>(
                                    command,
                                    false,
                                    false) as Collection <PSObject>;

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

                        if (currentHistory != null && currentHistory.Count > 0 && historyIndex > 0)
                        {
                            historyIndex--;

                            currentCursorIndex =
                                this.InsertInput(
                                    inputLine,
                                    promptStartCol,
                                    promptStartRow,
                                    (string)currentHistory[historyIndex].Properties["CommandLine"].Value,
                                    currentCursorIndex,
                                    insertIndex: 0,
                                    replaceLength: inputLine.Length);
                        }
                    }
                    else if (keyInfo.Key == ConsoleKey.DownArrow && isCommandLine)
                    {
                        currentCompletion = null;

                        // The down arrow shouldn't cause history to be loaded,
                        // it's only for navigating an active history array

                        if (historyIndex > -1 && historyIndex < currentHistory.Count &&
                            currentHistory != null && currentHistory.Count > 0)
                        {
                            historyIndex++;

                            if (historyIndex < currentHistory.Count)
                            {
                                currentCursorIndex =
                                    this.InsertInput(
                                        inputLine,
                                        promptStartCol,
                                        promptStartRow,
                                        (string)currentHistory[historyIndex].Properties["CommandLine"].Value,
                                        currentCursorIndex,
                                        insertIndex: 0,
                                        replaceLength: inputLine.Length);
                            }
                            else if (historyIndex == currentHistory.Count)
                            {
                                currentCursorIndex =
                                    this.InsertInput(
                                        inputLine,
                                        promptStartCol,
                                        promptStartRow,
                                        string.Empty,
                                        currentCursorIndex,
                                        insertIndex: 0,
                                        replaceLength: inputLine.Length);
                            }
                        }
                    }
                    else if (keyInfo.Key == ConsoleKey.Escape)
                    {
                        currentCompletion = null;
                        historyIndex      = currentHistory != null ? currentHistory.Count : -1;

                        currentCursorIndex =
                            this.InsertInput(
                                inputLine,
                                promptStartCol,
                                promptStartRow,
                                string.Empty,
                                currentCursorIndex,
                                insertIndex: 0,
                                replaceLength: inputLine.Length);
                    }
                    else if (keyInfo.Key == ConsoleKey.Backspace)
                    {
                        currentCompletion = null;

                        if (currentCursorIndex > 0)
                        {
                            currentCursorIndex =
                                this.InsertInput(
                                    inputLine,
                                    promptStartCol,
                                    promptStartRow,
                                    string.Empty,
                                    currentCursorIndex,
                                    insertIndex: currentCursorIndex - 1,
                                    replaceLength: 1,
                                    finalCursorIndex: currentCursorIndex - 1);
                        }
                    }
                    else if (keyInfo.Key == ConsoleKey.Delete)
                    {
                        currentCompletion = null;

                        if (currentCursorIndex < inputLine.Length)
                        {
                            currentCursorIndex =
                                this.InsertInput(
                                    inputLine,
                                    promptStartCol,
                                    promptStartRow,
                                    string.Empty,
                                    currentCursorIndex,
                                    replaceLength: 1,
                                    finalCursorIndex: currentCursorIndex);
                        }
                    }
                    else if (keyInfo.Key == ConsoleKey.Enter)
                    {
                        string completedInput = inputLine.ToString();
                        currentCompletion = null;
                        currentHistory    = null;

                        //if ((keyInfo.Modifiers & ConsoleModifiers.Shift) == ConsoleModifiers.Shift)
                        //{
                        //    // TODO: Start a new line!
                        //    continue;
                        //}

                        Parser.ParseInput(
                            completedInput,
                            out Token[] tokens,
                            out ParseError[] parseErrors);
        public async Task CanResolveAndLoadProfilesForHostId()
        {
            string testProfilePath =
                Path.GetFullPath(
                    @"..\..\..\PowerShellEditorServices.Test.Shared\Profile\Profile.ps1");

            string profileName =
                string.Format(
                    "{0}_{1}",
                    TestHostDetails.ProfileId,
                    ProfilePaths.AllHostsProfileName);

            string currentUserPath =
                Path.Combine(
                    Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
                    "WindowsPowerShell");
            string allUsersPath = null; // To be set later

            using (RunspaceHandle runspaceHandle = await this.powerShellContext.GetRunspaceHandle())
            {
                allUsersPath =
                    (string)runspaceHandle
                    .Runspace
                    .SessionStateProxy
                    .PSVariable
                    .Get("PsHome")
                    .Value;
            }

            string[] expectedProfilePaths =
                new string[]
            {
                Path.Combine(allUsersPath, ProfilePaths.AllHostsProfileName),
                Path.Combine(allUsersPath, profileName),
                Path.Combine(currentUserPath, ProfilePaths.AllHostsProfileName),
                Path.Combine(currentUserPath, profileName)
            };

            // Copy the test profile to the current user's host profile path
            File.Copy(testProfilePath, expectedProfilePaths[3], true);

            // Load the profiles for the test host name
            await this.powerShellContext.LoadHostProfiles();

            // Delete the test profile before any assert failures
            // cause the function to exit
            File.Delete(expectedProfilePaths[3]);

            // Ensure that all the paths are set in the correct variables
            // and that the current user's host profile got loaded
            PSCommand psCommand = new PSCommand();

            psCommand.AddScript(
                "\"$($profile.AllUsersAllHosts) " +
                "$($profile.AllUsersCurrentHost) " +
                "$($profile.CurrentUserAllHosts) " +
                "$($profile.CurrentUserCurrentHost) " +
                "$(Assert-ProfileLoaded)\"");

            var result =
                await this.powerShellContext.ExecuteCommand <string>(
                    psCommand);

            string expectedString =
                string.Format(
                    "{0} True",
                    string.Join(
                        " ",
                        expectedProfilePaths));

            Assert.Equal(expectedString, result.FirstOrDefault(), true);
        }