# Where PowerShell Command Completion Code Lives: Inside the TabExpansion2 Engine

> Discover where PowerShell command completion code lives within the TabExpansion2 engine. Explore the PowerShell/PowerShell repository to find suggestion logic.

- Repository: [PowerShell Team/PowerShell](https://github.com/PowerShell/PowerShell)
- Tags: internals
- Published: 2026-04-24

---

**PowerShell's command completion logic resides in the `src/System.Management.Automation/engine/CommandCompletion/` directory, where the `TabExpansion2` function serves as the entry point that delegates to the `CommandCompletion` class and `CompletionCompleters` for generating suggestions.**

The PowerShell command completion system powers the interactive tab-completion experience you use every time you press **Tab** in the terminal. Unlike monolithic shell implementations, PowerShell separates its completion engine into a dedicated subsystem within the open-source PowerShell/PowerShell repository, exposing both internal APIs and extensibility points for custom completers.

## The Command Completion Pipeline Architecture

The completion flow follows a strict pipeline from user input to suggestion display:

1. **User presses Tab** → The host invokes the built-in function **`TabExpansion2`**
2. **`CommandCompletion.CompleteInput`** creates a completion context and initiates analysis
3. **`CompletionAnalysis`** parses the input into AST nodes and tokens, building a **`CompletionContext`**
4. **`CompletionCompleters`** dispatches to specialized completers based on token type (commands, parameters, members, variables)
5. **`CompletionHelpers`** handles quote normalization and wildcard matching
6. **`CompletionResult`** objects return the final suggestions to the UI

## Entry Point: The TabExpansion2 Function

The `TabExpansion2` function is defined as a hidden script function in [`src/System.Management.Automation/engine/InitialSessionState.cs`](https://github.com/PowerShell/PowerShell/blob/main/src/System.Management.Automation/engine/InitialSessionState.cs). During engine startup, the following script is registered via `SessionStateFunctionEntry.GetDelayParsedFunctionEntry("TabExpansion2", …)`:

```csharp
private const string s_tabExpansionFunctionText = @"
    param($commandName, $wordToComplete, $cursorPosition, $options)
    # Calls the CommandCompletion engine

    [System.Management.Automation.CommandCompletion]::CompleteInput($null,$commandName,$wordToComplete,$cursorPosition,$options,$null,$null)
";

```

This function acts as the bridge between the host application (PowerShell console, VS Code, etc.) and the .NET completion engine.

## Core Engine: CommandCompletion and CompletionAnalysis

### CommandCompletion.cs

The primary API surface lives in [`src/System.Management.Automation/engine/CommandCompletion/CommandCompletion.cs`](https://github.com/PowerShell/PowerShell/blob/main/src/System.Management.Automation/engine/CommandCompletion/CommandCompletion.cs). The static **`CompleteInput`** method serves as the external entry point:

```csharp
public static List<CompletionResult> CompleteInput(
    PowerShell powershell,
    string input,
    int cursor,
    Hashtable options,
    out int replacementIndex,
    out int replacementLength)
{
    // …parse the input into AST & tokens…
    var analysis = new CompletionAnalysis(ast, tokens, cursorPos, options);
    return analysis.GetResults(powershell, out replacementIndex, out replacementLength);
}

```

### CompletionAnalysis.cs

The `CompletionAnalysis` class in [`src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs`](https://github.com/PowerShell/PowerShell/blob/main/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs) inspects the AST (Abstract Syntax Tree) to determine the completion context. The **`ExtractAstContext`** method identifies the token under the cursor and creates a `CompletionContext` object containing:

- The **AST node** at the cursor position
- **Token stream** from the parser
- **Word to complete** (partial string)
- **Execution context** for invoking sub-commands

## Concrete Completers: CompletionCompleters

All domain-specific completion logic lives in [`src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs`](https://github.com/PowerShell/PowerShell/blob/main/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs). This file contains the dispatch logic that routes to specialized completers based on token kind:

```csharp
switch (tokenAtCursor.Kind)
{
    case TokenKind.Identifier:
        result = GetResultForIdentifier(completionContext, ref replacementIndex, ref replacementLength);
        break;

    case TokenKind.Parameter:
        result = CompletionCompleters.CompleteCommandParameter(completionContext);
        break;

    case TokenKind.Dot:
    case TokenKind.ColonColon:
        result = CompletionCompleters.CompleteMember(completionContext,
                     @static: tokenAtCursor.Kind == TokenKind.ColonColon,
                     ref replacementLength);
        break;
    // …many other cases (variables, strings, operators, etc.)…
}

```

The `CompletionCompleters` class implements static methods like **`CompleteCommandParameter`**, **`CompleteMember`**, and **`CompleteVariable`**, each handling a specific completion scenario.

## Helper Utilities and Result Types

### CompletionHelpers.cs

Utility functions in [`src/System.Management.Automation/engine/CommandCompletion/CompletionHelpers.cs`](https://github.com/PowerShell/PowerShell/blob/main/src/System.Management.Automation/engine/CommandCompletion/CompletionHelpers.cs) handle cross-cutting concerns:

- **`HandleDoubleAndSingleQuote`** and **`QuoteCompletionText`** manage quote insertion and escaping
- **`LiteralMatchOrdinalIgnoreCase`** and **`WildcardPatternMatchIgnoreCase`** provide matching logic for filename completion
- Helper methods create properly formatted `CompletionResult` instances

### CompletionResult.cs

The `CompletionResult` class defined in [`src/System.Management.Automation/engine/CommandCompletion/CompletionResult.cs`](https://github.com/PowerShell/PowerShell/blob/main/src/System.Management.Automation/engine/CommandCompletion/CompletionResult.cs) encapsulates the final suggestion data:

- **`CompletionText`**: The actual text inserted into the command line
- **`ListItemText`**: The display text shown in dropdown menus
- **`ToolTip`**: Descriptive text (parameter help, type info)
- **`ResultType`**: Enum indicating Command, Parameter, Property, Method, etc.

## Programmatic Access Examples

### Simulating Tab Completion from PowerShell

You can invoke the completion engine directly without pressing Tab:

```powershell

# Emulate pressing Tab after typing "Get-Ser"

$input = 'Get-Ser'
$cursor = $input.Length          # cursor at end of line

$repIdx = 0
$repLen = 0
[System.Management.Automation.CommandCompletion]::CompleteInput(
    $null, $input, $cursor, @{}, [ref]$repIdx, [ref]$repLen)

```

This returns a collection of `CompletionResult` objects containing suggestions like `Get-Service`.

### Registering a Custom Argument Completer

Custom completers integrate with the engine via `Register-ArgumentCompleter`:

```powershell

# Register a script block that provides completions for a fake command

Register-ArgumentCompleter -CommandName 'Invoke-MyCmd' -ParameterName 'Path' -ScriptBlock {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)

    # Return any .txt files in the current directory that start with the word typed

    Get-ChildItem -Path . -Filter "$wordToComplete*.txt" |
        ForEach-Object {
            [System.Management.Automation.CompletionResult]::new(
                $_.Name, $_.Name, 'ParameterValue', $_.FullName)
        }
}

```

The engine stores these script blocks in the `CustomArgumentCompleters` dictionary within `CompletionContext` and invokes them when completing the specified command parameters.

### Accessing Completion from C#

For tools and editors embedding PowerShell, access completions via the public API:

```csharp
using System.Management.Automation;
using System.Management.Automation.Language;

// Build a PowerShell instance
PowerShell ps = PowerShell.Create(RunspaceMode.CurrentRunspace);

// Request completions for "Get-P*" at cursor position 7
var results = CommandCompletion.CompleteInput(
                ps,
                "Get-P*",
                7,
                new Hashtable(),
                out int replIdx,
                out int replLen);

foreach (var r in results)
{
    Console.WriteLine($"{r.CompletionText}   // {r.ToolTip}");
}

```

## Summary

- **Entry Point**: The `TabExpansion2` function in [`InitialSessionState.cs`](https://github.com/PowerShell/PowerShell/blob/main/InitialSessionState.cs) forwards Tab key presses to the completion engine.
- **Core Logic**: `CommandCompletion.CompleteInput` in [`CommandCompletion.cs`](https://github.com/PowerShell/PowerShell/blob/main/CommandCompletion.cs) orchestrates the analysis and result generation.
- **Context Building**: `CompletionAnalysis` parses the command line AST to determine what type of completion is needed.
- **Specialized Completers**: `CompletionCompleters` contains the concrete implementations for commands, parameters, members, and variables.
- **Utilities**: `CompletionHelpers` handles quote processing and wildcard matching, while `CompletionResult` defines the data contract for suggestions.
- **Extensibility**: The `Register-ArgumentCompleter` command stores custom completers in `CompletionContext.CustomArgumentCompleters` for invocation during completion.

## Frequently Asked Questions

### What is the difference between TabExpansion and TabExpansion2?

**TabExpansion2 is the modern completion entry point that replaced the legacy TabExpansion function.** According to the PowerShell source code in [`InitialSessionState.cs`](https://github.com/PowerShell/PowerShell/blob/main/InitialSessionState.cs), `TabExpansion2` calls the managed `[System.Management.Automation.CommandCompletion]::CompleteInput` API, whereas the older `TabExpansion` relied primarily on script-based completion logic. All modern PowerShell hosts use `TabExpansion2` to leverage the full AST-based completion engine.

### How do I create a custom argument completer in PowerShell?

**Use the `Register-ArgumentCompleter` cmdlet to bind a script block to specific commands or parameters.** Your script block receives parameters including `$wordToComplete`, `$commandAst`, and `$fakeBoundParameter`, and must return `CompletionResult` objects. The engine caches these completers in the `CustomArgumentCompleters` dictionary and invokes them automatically when the user requests completion for registered commands.

### Where are the built-in completers for commands and parameters defined?

**Built-in completers live in the `CompletionCompleters` class within [`src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs`](https://github.com/PowerShell/PowerShell/blob/main/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs).** This file contains static methods like `CompleteCommandParameter` for parameter names, `CompleteMember` for properties and methods, and specialized handlers for variables and filenames. The class uses a switch statement on `TokenKind` to dispatch to the appropriate completer based on the cursor context.

### Can I invoke PowerShell's completion engine from C# without running a script?

**Yes, by calling `CommandCompletion.CompleteInput` statically from the `System.Management.Automation` namespace.** Pass a `PowerShell` instance, the input string, cursor position, and optional completion options. The method returns a `List<CompletionResult>` containing the suggestions and outputs the `replacementIndex` and `replacementLength` to indicate which portion of the input should be replaced.