Skip to content

Powershell – Oh-My-Posh Profile

# =========================================
# PowerShell Profile
# =========================================

using namespace System.Management.Automation
using namespace System.Management.Automation.Language

# ------------------------------
# Modules
# ------------------------------
if ($host.Name -eq 'ConsoleHost') {
    Import-Module PSReadLine
}

# Terminal-Icons
if (-not (Get-Module -Name Terminal-Icons)) {
    Import-Module Terminal-Icons -ErrorAction SilentlyContinue
}

# ------------------------------
# Oh-My-Posh Setup
# ------------------------------
$ThemeDir  = "$HOME\Documents\PowerShell\oh-my-posh-themes"
$ThemeFile = "$ThemeDir\jandedobbeleer.omp.json"

if (-not (Test-Path $ThemeDir)) {
    New-Item -ItemType Directory -Path $ThemeDir -Force | Out-Null
}

if (-not (Test-Path $ThemeFile)) {
    Write-Host "Downloading Oh-My-Posh theme..." -ForegroundColor Yellow
    Invoke-WebRequest -Uri "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/jandedobbeleer.omp.json" `
        -OutFile $ThemeFile
}

oh-my-posh init pwsh --config $ThemeFile | Invoke-Expression

# ------------------------------
# Aliases
# ------------------------------
Set-Alias ll Get-ChildItem
Set-Alias desktop "Desktop.ps1"

# ------------------------------
# Argument Completion
# ------------------------------
Register-ArgumentCompleter -Native -CommandName winget -ScriptBlock {
    param($wordToComplete, $commandAst, $cursorPosition)
    [Console]::InputEncoding = [Console]::OutputEncoding = $OutputEncoding = [System.Text.Utf8Encoding]::new()
    $Local:word = $wordToComplete.Replace('"','""')
    $Local:ast  = $commandAst.ToString().Replace('"','""')
    winget complete --word="$Local:word" --commandline "$Local:ast" --position $cursorPosition |
        ForEach-Object { [Microsoft.PowerShell.Automation.CompletionResult]::new($_,$_, 'ParameterValue', $_) }
}

Register-ArgumentCompleter -Native -CommandName dotnet -ScriptBlock {
    param($commandName, $wordToComplete, $cursorPosition)
    dotnet complete --position $cursorPosition "$wordToComplete" |
        ForEach-Object { [Microsoft.PowerShell.Automation.CompletionResult]::new($_,$_, 'ParameterValue', $_) }
}

# ------------------------------
# PSReadLine Key Handlers
# ------------------------------
Set-PSReadLineKeyHandler -Key UpArrow   -Function HistorySearchBackward
Set-PSReadLineKeyHandler -Key DownArrow -Function HistorySearchForward

Set-PSReadLineKeyHandler -Key F7 -BriefDescription History -LongDescription 'Show command history' -ScriptBlock {
    $pattern = $null
    [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$pattern,[ref]$null)
    if ($pattern) { $pattern = [regex]::Escape($pattern) }
    $history = Get-Content (Get-PSReadLineOption).HistorySavePath | Where-Object { -not $pattern -or ($_ -match $pattern) }
    $command = $history | Out-GridView -Title History -PassThru
    if ($command) {
        [Microsoft.PowerShell.PSConsoleReadLine]::RevertLine()
        [Microsoft.PowerShell.PSConsoleReadLine]::Insert($command)
    }
}

# Movement & deletion
Set-PSReadLineKeyHandler -Key Alt+d -Function ShellKillWord
Set-PSReadLineKeyHandler -Key Alt+Backspace -Function ShellBackwardKillWord
Set-PSReadLineKeyHandler -Key Alt+b -Function ShellBackwardWord
Set-PSReadLineKeyHandler -Key Alt+f -Function ShellForwardWord
Set-PSReadLineKeyHandler -Key Alt+B -Function SelectShellBackwardWord
Set-PSReadLineKeyHandler -Key Alt+F -Function SelectShellForwardWord

# ------------------------------
# Smart Insert/Delete Quotes & Braces
# ------------------------------
function Set-SmartQuotesAndBracesHandlers {
    $paired = @{
        '"' = '"'
        "'" = "'"
        '(' = ')'
        '{' = '}'
        '[' = ']'
    }

    foreach ($key in $paired.Keys) {
        Set-PSReadLineKeyHandler -Key $key -ScriptBlock {
            param($key,$arg)
            $closeChar = $paired[$key.KeyChar]

            $selectionStart = $null
            $selectionLength = $null
            [Microsoft.PowerShell.PSConsoleReadLine]::GetSelectionState([ref]$selectionStart,[ref]$selectionLength)

            $line = $null
            $cursor = $null
            [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line,[ref]$cursor)

            if ($selectionStart -ne -1) {
                [Microsoft.PowerShell.PSConsoleReadLine]::Replace($selectionStart,$selectionLength, $key.KeyChar + $line.SubString($selectionStart,$selectionLength) + $closeChar)
                [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($selectionStart + $selectionLength + 2)
            } else {
                [Microsoft.PowerShell.PSConsoleReadLine]::Insert("$($key.KeyChar)$closeChar")
                [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor + 1)
            }
        }
    }

    # Backspace smart deletion
    Set-PSReadLineKeyHandler -Key Backspace -ScriptBlock {
        param($key,$arg)
        $line = $null
        $cursor = $null
        [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line,[ref]$cursor)

        if ($cursor -gt 0 -and $cursor -lt $line.Length) {
            $prev = $line[$cursor-1]
            $next = $line[$cursor]
            if (($prev,$next) -in @([char[]]('"','"'),[char[]]("'", "'"),[char[]]('(',')'),[char[]]('{','}'),[char[]]('[',']'))) {
                [Microsoft.PowerShell.PSConsoleReadLine]::Delete($cursor-1,2)
                return
            }
        }
        [Microsoft.PowerShell.PSConsoleReadLine]::BackwardDeleteChar($key,$arg)
    }
}

Set-SmartQuotesAndBracesHandlers

# ------------------------------
# PSReadLine Options
# ------------------------------
Set-PSReadLineOption -PredictionSource History
Set-PSReadLineOption -PredictionViewStyle ListView
Set-PSReadLineOption -EditMode Windows

# ------------------------------
# Macros
# ------------------------------
Set-PSReadLineKeyHandler -Key Ctrl+Shift+b -ScriptBlock {
    [Microsoft.PowerShell.PSConsoleReadLine]::RevertLine()
    [Microsoft.PowerShell.PSConsoleReadLine]::Insert("dotnet build")
    [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine()
}

Set-PSReadLineKeyHandler -Key Ctrl+Shift+t -ScriptBlock {
    [Microsoft.PowerShell.PSConsoleReadLine]::RevertLine()
    [Microsoft.PowerShell.PSConsoleReadLine]::Insert("dotnet test")
    [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine()
}

# ------------------------------
# Directory Marks
# ------------------------------
$global:PSReadLineMarks = @{}

Set-PSReadLineKeyHandler -Key Ctrl+J -ScriptBlock {
    $key = [Console]::ReadKey($true)
    $global:PSReadLineMarks[$key.KeyChar] = $pwd
}

Set-PSReadLineKeyHandler -Key Ctrl+j -ScriptBlock {
    $key = [Console]::ReadKey()
    $dir = $global:PSReadLineMarks[$key.KeyChar]
    if ($dir) { cd $dir; [Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt() }
}

Set-PSReadLineKeyHandler -Key Alt+j -ScriptBlock {
    $global:PSReadLineMarks.GetEnumerator() | ForEach-Object { [PSCustomObject]@{Key=$_.Key; Dir=$_.Value} } |
        Format-Table -AutoSize | Out-Host
    [Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt()
}