PowerShell Editor Services (PSES)

repository·main·Indexed 20 days ago

https://github.com/powershell/powershelleditorservices

A PowerShell module providing the backend logic for PowerShell development in editors. It implements the Language Server Protocol (LSP) and Debug Adapter Protocol (DAP) to enable IntelliSense, debugging, and terminal integration. PSES includes services for code navigation, semantic analysis via PowerShell Script Analyzer, and a scripting API via $psEditor. It supports PowerShell 7+ and provides best-effort support for Windows PowerShell 5.1.

Tokens
28.4K
Snippets
80
Records
108
Agent score
71%

What's inside PowerShell Editor Services

  1. Overview of PowerShell Editor Services

    main

    PowerShell Editor Services (PSES) is a PowerShell module designed to provide a consistent and robust development experience across various editors and IDEs. It provides core services including:

    • Language Service: Enables code navigation (find references, go to definition), statement completions (IntelliSense), and real-time semantic analysis via PowerShell Script Analyzer.
    • Debugging Service: Simplifies interaction with the PowerShell debugger, supporting breakpoints, variable inspection, and call stack management.
    • $psEditor API: Allows for scripting the host editor.
    • Extension Terminal: Provides a full terminal experience for interactive development.

    PSES runs as a PowerShell module in currently supported versions of PowerShell 7+. Windows PowerShell 5.1 is supported on a best-effort basis.

  2. Use the PowerShellEditorServices.Commands module

    main
    The PowerShellEditorServices.Commands module provides cmdlets to facilitate the manipulation of script files and editor features within PowerShell Editor Services. It includes tools for working with ScriptExtent objects (which represent positions in a file), interacting with the Abstract Syntax Tree (AST), and managing editor commands.
  3. Find and retrieve ASTs and tokens

    main

    To interact with the structure of a script, use these cmdlets:

    • Find-Ast: Searches a script file to find a specific Abstract Syntax Tree (AST). It searches all ASTs following the initial starting AST, including those not part of the same tree.
    • Get-Token: Retrieves tokens from the current editor context or from a specific ScriptExtent object. Once retrieved, you can use ScriptExtent functions to manipulate the text at those locations.
  4. Understand the role of PowerShellContext

    main
    The Microsoft.PowerShell.EditorServices.PowerShellContext class is the core component underlying all other services. It is responsible for managing a session's runspace and handling the execution of scripts and commands, ensuring execution is handled correctly regardless of the current runspace state.
  5. How the PowerShell Editor Services extensibility model works

    main

    PowerShell Editor Services provides a generalized extensibility model that allows you to write PowerShell code to automate or extend any host editor (like VS Code) that uses PowerShell Editor Services.

    The core of this model is the $psEditor object, which is of type Microsoft.PowerShell.EditorServices.Services.PowerShellContext.EditorObject. This object is the entry point for accessing high-level services in the current editing session, such as the editor's workspace. It is conceptually similar to the $psISE object used in the legacy PowerShell ISE, but designed to be editor-agnostic.

    # No direct code snippet for the concept itself, but usage follows:
    $psEditor.Workspace.OpenFile($path)
  6. Use Debugging with PowerShell Editor Services

    main

    Debugging is handled within the same process as the Language Server Protocol (LSP). To use debugging, your client must support the Debug Adapter Protocol (DAP).

    To connect the debugger, the client should use the path to the specific debug named pipe found in the session.json file generated during startup.

    Supported extensions include:

    • PowerShell for Visual Studio Code
    • nvim-dap-powershell for Neovim
    • powershell.nvim for Neovim
    • intellij-powershell
  7. How PSES handles `Console.ReadKey()` cancellation via LSP

    main

    To overcome the inability to cancel the synchronous Console.ReadKey() API, PowerShell Editor Services uses a workaround involving the Language Server Protocol (LSP) and the client's terminal API.

    The Mechanism

    Instead of attempting to force-cancel the thread, PSES uses a cancellation token to trigger an LSP notification to the client (e.g., VS Code).

    1. Trigger: When a task requiring REPL interruption is canceled, the ReadKey implementation detects the cancellation token.
    2. Notification: PSES sends an LSP notification named sendKeyPress to the client.
    3. Client Action: The client (such as VS Code) receives this notification and uses its terminal API (e.g., SendText) to inject a dummy character (such as p) into the terminal's stdin.
    4. Resolution: The injected character causes the synchronous Console.ReadKey() call to return.
    5. Cleanup: Because PSES knows the cancellation was intentional, it ignores the dummy character and proceeds as if the API had been natively canceled.

    Requirements for Implementation

    This solution relies on:

    • A client that supports writing arbitrary text to the process's stdin (like VS Code's SendText API).
    • An LSP implementation that can handle the sendKeyPress notification.
    • A threading model that avoids race conditions when processing this injected input.
  8. Tag functions as editor commands using EditorCommandAttribute

    main

    To make a PowerShell function available as an editor command within PowerShell Editor Services, you must decorate it with the [Microsoft.PowerShell.EditorServices.Services.PowerShellContext.EditorCommand] attribute. This attribute is applied similarly to [CmdletBinding()].

    The attribute accepts the following named parameters:

    • Name: The internal name of the command.
    • DisplayName: The user-facing name displayed in the editor.
    • SuppressOutput: A switch parameter that, when present, prevents the command's output from being sent back to the editor's output stream.
    function Invoke-MyEditorCommand {
        [CmdletBinding()]
        [Microsoft.PowerShell.EditorServices.Services.PowerShellContext.EditorCommand(DisplayName='My Command', SuppressOutput)]
        param()
        end {
            # Command logic here
        }
    }
  9. Understand the limitations of PowerShell Symbol Renaming

    main

    Because PowerShell is not a statically typed language, symbol renaming (functions, parameters, etc.) is performed on a best-effort basis and is primarily intended for quick updates to variables or functions within a single, self-contained script file.

    Unsupported Scenarios

    • Cross-file renaming: Renaming symbols across multiple files is not supported.
    • Dynamic elements: Dynamic Parameters and dynamically constructed splat parameters are not supported.
    • Scoped variables: Scoped variables (e.g., $SCRIPT:test) are not supported.
    • Isolated Scriptblocks: Variables inside scriptblocks used in unscoped operations (like Foreach-Parallel or Start-Job) may not be correctly identified if they aren't defined within the block.
    • Command-based access: Get-Variable and Set-Variable are not searched for renames.
    • Positional logic: Renaming may fail if the logic relies on specific block ordering (e.g., defining a variable in a begin block but placing it after a process block).
  10. Manipulate script positions with ScriptExtent cmdlets

    main

    The module uses ScriptExtent objects to represent specific ranges or positions within a script. You can use the following cmdlets to manage these objects:

    • ConvertTo-ScriptExtent: Creates a ScriptExtent object from an object with position-related properties or by specifying parameters directly.
    • ConvertFrom-ScriptExtent: Converts ScriptExtent objects into types compatible with $psEditor API methods.
    • Join-ScriptExtent: Combines multiple ScriptExtent objects piped into it into a single, unified extent. This is useful for grouping multiple ASTs or tokens.
    • Test-ScriptExtent: Determines the spatial relationship between two ScriptExtent objects (e.g., whether one is before, after, or inside another).
    • Set-ScriptExtent: Inserts or replaces text at a specific position in a file currently open in PowerShell Editor Services.
  11. Enable the PowerShell Extension Terminal

    main

    The PowerShell Extension Terminal uses the host process's stdio streams for console input and output.

    Important Requirements:

    1. Mutual Exclusivity: Using the Extension Terminal is mutually exclusive from using stdio for Language Server Protocol (LSP) messages. If you want the terminal, you must use Named Pipes/Unix Domain Sockets for LSP.
    2. Activation: You must include the -EnableConsoleRepl switch when calling Start-EditorServices.ps1 to enable this feature.

    This feature is used by Visual Studio Code, Vim, and IntelliJ extensions.

    # Example activation via named pipes
    pwsh -NoLogo -NoProfile -Command "./PowerShellEditorServices/Start-EditorServices.ps1 -SessionDetailsPath ./session.json -EnableConsoleRepl"