Public/Enter-RSSession.ps1

<#
.SYNOPSIS
Enters a new restartable PowerShell session.
 
.DESCRIPTION
Enters a new restartable PowerShell session. Throws a non-terminating error when the caller is already in a restartable session or the host is not a ConsoleHost.
The PowerShell executable that called this function is used to create a new session.
 
.PARAMETER OnStart
ScriptBlock that is called at the start of the restartable session.
 
.PARAMETER ArgumentList
An array of arguments that is passed to the OnStart script block.
 
.PARAMETER ShowProcessId
Switch to specify if the process ID of the restartable session is shown in the prompt.
 
.INPUTS
None.
 
.OUTPUTS
None.
 
.EXAMPLE
Enter-RSSession -OnStart {'Hi'}
 
.EXAMPLE
# Automatically restart a session and reload the module on file save.
$onStart = {
    param($modulePath)
    Import-Module $modulePath
    Start-RSRestartFileWatcher -Path $modulePath -IncludeSubdirectories
}
Enter-RSSession -OnStart $onStart -ArgumentList D:\ScriptModuleTest
 
#>

function Enter-RSSession
{
    [CmdletBinding(DefaultParameterSetName='NoOnStart')]
    param
    (
        [Parameter(ParameterSetName='NoOnStart', Mandatory=$false, ValueFromPipelineByPropertyName=$true)]
        [Parameter(ParameterSetName='OnStart', Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
        [ScriptBlock]$OnStart,

        [Parameter(ParameterSetName='OnStart', ValueFromPipelineByPropertyName=$true)]
        [Object[]]$ArgumentList,

        [Parameter(ValueFromPipelineByPropertyName=$true)]
        [Switch]$ShowProcessId
    )

    process
    {
        if ([RestartableSession.GlobalVariable]::IsInRestartableSession())
        {
            Write-Error -Message 'Already in a RS Session.' -Category InvalidOperation
            return
        }

        if ($host.Name -ne 'ConsoleHost')
        {
            Write-Error -Message 'Only ConsoleHost is supported.' -Category InvalidOperation
            return
        }

        $powershellExe = (Get-Process -Id $PID).Path
        $command = {
            # This is executed in the created new session scope.
            # Don't use any temporary variables as they are visible to users.

            Import-Module RestartableSession

            if ($args[1])
            {
                # ShowProcessId
                [RestartableSession.GlobalVariable]::PromptPrefix += 'RS({0})[{1}] ' -f $args[0], $PID
            }
            else
            {
                [RestartableSession.GlobalVariable]::PromptPrefix = 'RS({0}) ' -f $args[0]
            }
            [RestartableSession.GlobalVariable]::OriginalPromptFunction = (Get-Command Prompt).ScriptBlock

            function Prompt
            {
                [RestartableSession.GlobalVariable]::PromptPrefix + [RestartableSession.GlobalVariable]::OriginalPromptFunction.Invoke()
            }

            if ($args[2])
            {
                if ($args.Length -gt 3)
                {
                    Invoke-Command -ScriptBlock ([ScriptBlock]::Create($args[2])) -NoNewScope -ArgumentList $args[3..($args.Length-1)]
                }
                else
                {
                    Invoke-Command -ScriptBlock ([ScriptBlock]::Create($args[2])) -NoNewScope
                }
            }
        }

        $restartCount = 1
        while ($true)
        {
            $arguments = @($restartCount, ($ShowProcessId -eq $true), $OnStart) + $ArgumentList

            & $powershellExe -NoExit -Command $command -Args $arguments

            if ($LASTEXITCODE -eq [RestartableSession.GlobalVariable]::kExitCodeToBreak)
            {
                break
            }
            ++$restartCount
        }
    }
}