1059 lines
46 KiB
PowerShell
1059 lines
46 KiB
PowerShell
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseApprovedVerbs', '', Scope = 'Function', Target = 'Warp-*', Justification = 'Warp-* functions are ours')]
|
|
param()
|
|
|
|
# Wrap things in a module to avoid cluttering the global scope. We assign it to '$null' to suppress
|
|
# the console output from creating the module.
|
|
# NOTE: If you do need a function to be global and also have access to variables in this scope, add
|
|
# the function name to the 'Export-ModuleMember' call at the end.
|
|
$null = New-Module -Name Warp-Module -ScriptBlock {
|
|
# Byte sequence used to signal the start of an OSC for Warp JSON messages.
|
|
$oscStart = "$([char]0x1b)]9278;"
|
|
|
|
# Appended to $oscStart to signal that the following message is JSON-encoded.
|
|
$oscJsonMarker = 'd'
|
|
|
|
$oscParamSeparator = ';'
|
|
|
|
# Byte used to signal the end of an OSC for Warp JSON messages.
|
|
$oscEnd = "$([char]0x07)"
|
|
|
|
# Writes a hex-encoded JSON message to the PTY.
|
|
function Warp-Send-JsonMessage([System.Collections.Hashtable]$table) {
|
|
$json = ConvertTo-Json -InputObject $table -Compress
|
|
# Sends a message to the controlling terminal as an OSC control sequence.
|
|
# TODO(CORE-2718): Determine if we need to hex encode the payload.
|
|
# Note that because the JSON string may contain characters that we don't control (including
|
|
# unicode), we encode it as hexadecimal string to avoid prematurely calling unhook if
|
|
# one of the bytes in JSON is 9c (ST) or other (CAN, SUB, ESC).
|
|
$encodedMessage = Warp-Encode-HexString $json
|
|
Write-Host -NoNewline "$oscStart$oscJsonMarker$oscParamSeparator$encodedMessage$oscEnd"
|
|
}
|
|
|
|
# This script block contains commands and constants that are needed in background threads.
|
|
# If you want to be able to use it in a background thread, stick it in this block
|
|
$warpCommon = {
|
|
# OSC used to mark the start of in-band command output.
|
|
#
|
|
# Printable characters received this OSC and oscEndGeneratorOutput are parsed and handled as
|
|
# output for an in-band command.
|
|
$oscStartGeneratorOutput = "$([char]0x1b)]9277;A$oscEnd"
|
|
|
|
# OSC used to mark the end of in-band command output.
|
|
#
|
|
# Printable characters received between oscStartGeneratorOutput and this are parsed and
|
|
# handled as output for an in-band command.
|
|
$oscEndGeneratorOutput = "$([char]0x1b)]9277;B$oscEnd"
|
|
|
|
$oscResetGrid = "$([char]0x1b)]9279$oscEnd"
|
|
|
|
function Warp-Send-ResetGridOSC() {
|
|
Write-Host -NoNewline $oscResetGrid
|
|
}
|
|
|
|
# Safely attempt to get Node.js version if available. Avoid literal 'node' invocation
|
|
# to satisfy PSUseCompatibleCommands across target platforms.
|
|
function Warp-TryGet-NodeVersion {
|
|
try {
|
|
$cmd = Get-Command -CommandType Application node 2>$null
|
|
if ($null -eq $cmd) { return '' }
|
|
$nv = & $cmd.Source --version 2>$null
|
|
if ($null -ne $nv -and "$nv" -ne '') {
|
|
return $nv
|
|
}
|
|
} catch {
|
|
# Log at verbose level so normal users are not spammed, but the catch is not empty.
|
|
Write-Verbose "node --version failed: $($_.Exception.Message)"
|
|
}
|
|
return ''
|
|
}
|
|
|
|
# Encode a string as hex-encoded UTF-8.
|
|
function Warp-Encode-HexString([string]$str) {
|
|
[BitConverter]::ToString([System.Text.Encoding]::UTF8.GetBytes($str)).Replace('-', '')
|
|
}
|
|
|
|
# Hex-encodes the given argument and writes it to the PTY, wrapped in the OSC
|
|
# sequences for generator output.
|
|
#
|
|
# The payload of the OSC is "<content_length>;<hex-encoded content>".
|
|
function Warp-Send-GeneratorOutputOsc {
|
|
param([string]$message)
|
|
|
|
$hexEncodedMessage = Warp-Encode-HexString $message
|
|
$byteCount = [System.Text.Encoding]::ASCII.GetByteCount($hexEncodedMessage)
|
|
|
|
Write-Host -NoNewline "$oscStartGeneratorOutput$byteCount;$hexEncodedMessage$oscEndGeneratorOutput"
|
|
Warp-Send-ResetGridOSC
|
|
}
|
|
|
|
# Do not run this in the main thread. It mucks around with some env vars
|
|
function Warp-Run-InBandGenerator {
|
|
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingInvokeExpression', '', Justification = 'We actually need it')]
|
|
param([string]$commandId, [string]$command)
|
|
|
|
try {
|
|
# We do not have a good way to simultaneously capture
|
|
# the command status $? and the command output of our command.
|
|
# this is because Invoke-Expression will always set $? to true.
|
|
# To get around this, we append a small bit of code to the original
|
|
# command that makes Invoke-Expression throw if the last command
|
|
# did not succeed.
|
|
$modifiedCommand = "$command" + '; if (-Not $?) { throw }'
|
|
|
|
# We set this immediately before running Invoke-Expression,
|
|
# that way it will default to 0
|
|
$LASTEXITCODE = 0
|
|
|
|
# Note: parens are important here. Without them
|
|
# parsing order gets messed up on the 2>&1
|
|
$rawOutput = Invoke-Expression -Command "$modifiedCommand" 2>&1
|
|
$exitCode = $LASTEXITCODE
|
|
|
|
# If the generator command returns multi-line output,
|
|
# we make sure to join the lines together with a newline, so
|
|
# they are properly parsed by warp
|
|
$stringifiedOutput = $rawOutput -join "$([char]0x0a)"
|
|
|
|
# This is a best-effort attempt to get an error code.
|
|
# We cannot duplicate our error code logic from Warp-Precmd
|
|
# b/c Invoke-Expression will swallow the value of $? and always
|
|
# return true. So we do our best to return a legit error code
|
|
Write-Output "$commandId;$stringifiedOutput;$exitCode"
|
|
} catch {
|
|
# This catches a terminating error (ex: entering a command that does not exist)
|
|
# In this case, we return an error code of 1
|
|
Write-Output "$commandId;1;"
|
|
}
|
|
}
|
|
}
|
|
|
|
# Load the Warp Common functions in the current session
|
|
. $warpCommon
|
|
|
|
function Get-EpochTime {
|
|
[decimal]([DateTime]::UtcNow - [DateTime]::new(1970, 1, 1, 0, 0, 0, 0)).Ticks / 1e7
|
|
}
|
|
|
|
function Warp-Bootstrapped {
|
|
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'WARP_BOOTSTRAPPED', Justification = 'False positive as we are assigning to global')]
|
|
param([decimal]$rcStartTime, [decimal]$rcEndTime)
|
|
|
|
$envVarNames = (Get-ChildItem env: | Select-Object -ExpandProperty Name | ForEach-Object { 'env:' + $_ }) + `
|
|
(Get-Variable | Select-Object -ExpandProperty Name) -join ' '
|
|
$aliasesRaw = Get-Command -CommandType Alias | Select-Object -ExpandProperty DisplayName
|
|
$aliases = $aliasesRaw -join [Environment]::NewLine
|
|
# Only query modules that ship with PowerShell itself. This keeps bootstrap fast by
|
|
# avoiding Windows system modules and third-party modules from the Gallery. The rest are
|
|
# loaded asynchronously later.
|
|
$corePsModules = @(
|
|
'Microsoft.PowerShell.*', # All built-in PS modules (cross-platform)
|
|
'Microsoft.WSMan.*', # WS-Management (Windows PS)
|
|
'CimCmdlets', # CIM/WMI (Windows)
|
|
'PackageManagement', # Package management
|
|
'PowerShellGet', # Package get
|
|
'PSReadLine', # Line editor bundled with PS
|
|
'ThreadJob', # Thread jobs (PS 7)
|
|
'PSDiagnostics', # PS diagnostics
|
|
'PSDesiredStateConfiguration', # DSC (Windows PS)
|
|
'PSWorkflow', # Legacy workflow (Windows PS 5)
|
|
'PSWorkflowUtility' # Legacy workflow utility (Windows PS 5)
|
|
)
|
|
$functionNamesRaw = Get-Command -CommandType Function -Module $corePsModules |
|
|
Where-Object { -not $_.Name.StartsWith('Warp') } |
|
|
Select-Object -ExpandProperty Name
|
|
$functionNames = $functionNamesRaw -join [Environment]::NewLine
|
|
$builtinsRaw = Get-Command -CommandType Cmdlet -Module $corePsModules | Select-Object -ExpandProperty Name
|
|
$builtins = $builtinsRaw -join [Environment]::NewLine
|
|
$shellVersion = $PSVersionTable.PSVersion.ToString()
|
|
# PowerShell wasn't cross-platform until version 6. Anything before that is definitely on Windows.
|
|
$osCategory = if ($PSVersionTable.PSVersion.Major -le 5) {
|
|
'Windows'
|
|
} elseif ($IsLinux) {
|
|
'Linux'
|
|
} elseif ($IsMacOS) {
|
|
'MacOS'
|
|
} elseif ($IsWindows) {
|
|
'Windows'
|
|
} else {
|
|
''
|
|
}
|
|
|
|
# We do not have an equivalent to 'compgen -k' here, so we are dropping
|
|
# in a hardcoded list. List is take from
|
|
# https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_reserved_words?view=powershell-7.4
|
|
$PSKeywords = @(
|
|
'begin', 'break', 'catch', 'class', 'continue', 'data', 'define',
|
|
'do', 'dynamicparam', 'else', 'elseif', 'end', 'enum', 'exit',
|
|
'filter', 'finally', 'for', 'foreach', 'from', 'function', 'hidden',
|
|
'if', 'in', 'param', 'process', 'return', 'static', 'switch', 'throw',
|
|
'trap', 'try', 'until', 'using', 'var', 'while', 'inlinescript',
|
|
'parallel', 'sequence', 'workflow'
|
|
) -join [environment]::NewLine
|
|
|
|
$linuxDistribution = $null
|
|
if ($osCategory -eq 'Linux') {
|
|
$osReleaseFile = if (Test-Path -Path '/etc/os-release') {
|
|
'/etc/os-release'
|
|
} elseif (Test-Path -Path '/usr/lib/os-release') {
|
|
'/usr/lib/os-release'
|
|
} else {
|
|
$null
|
|
}
|
|
if ($null -ne $osReleaseFile) {
|
|
# This is meant to be the equivalent to the bash command
|
|
# cat $os_release_file | sed -nE 's/^NAME="(.*)"$/\1/p'. We filter
|
|
# specifically for the Name= line of the osRelease file, and then
|
|
# pull out the OS name
|
|
$linuxDistribution = switch -Regex -File $osReleaseFile {
|
|
'^\s*NAME="(.*)"' {
|
|
$Matches[1]
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# TODO(PLAT-681) - finish the information here
|
|
# for keywords, see 'Get-Help about_Language_Keywords'
|
|
$bootstrappedMsg = @{
|
|
hook = 'Bootstrapped'
|
|
value = @{
|
|
session_id = $global:_warpSessionId
|
|
histfile = $(Get-PSReadLineOption).HistorySavePath
|
|
shell = 'pwsh'
|
|
home_dir = "$HOME"
|
|
path = $env:PATH
|
|
editor = "$env:EDITOR"
|
|
env_var_names = $envVarNames
|
|
abbreviations = ''
|
|
aliases = $aliases
|
|
function_names = $functionNames
|
|
builtins = $builtins
|
|
keywords = "$PSKeywords"
|
|
shell_version = $shellVersion
|
|
shell_options = ''
|
|
rcfiles_start_time = "$rcStartTime"
|
|
rcfiles_end_time = "$rcEndTime"
|
|
shell_plugins = ''
|
|
os_category = $osCategory
|
|
linux_distribution = "$linuxDistribution"
|
|
shell_path = (Get-Process -Id $PID).Path
|
|
}
|
|
}
|
|
Warp-Send-JsonMessage $bootstrappedMsg
|
|
$global:WARP_BOOTSTRAPPED = 1
|
|
}
|
|
|
|
function Warp-Preexec([string]$command) {
|
|
$HOST.UI.RawUI.WindowTitle = $command
|
|
$preexecMsg = @{
|
|
hook = 'Preexec'
|
|
value = @{
|
|
session_id = $global:_warpSessionId
|
|
command = $command
|
|
}
|
|
}
|
|
Warp-Send-JsonMessage $preexecMsg
|
|
Warp-Send-ResetGridOSC
|
|
|
|
# If this preexec is called for user command, kill ongoing generator command jobs and clean
|
|
# up the bookkeeping temp files used to bookkeep.
|
|
if (-not "$command" -match '^Warp-Run-GeneratorCommand') {
|
|
Warp-Stop-ActiveThread
|
|
}
|
|
|
|
# Clean up any completed warp jobs so they do not show up on the user's 'get-job'
|
|
# commands
|
|
Warp-Clean-CompletedThread
|
|
|
|
# Remove any instance of the 'Warp-Run-GeneratorCommand' call from the user's history
|
|
Clear-History -CommandLine 'Warp-Run-GeneratorCommand*'
|
|
}
|
|
|
|
function Warp-Finish-Update([string]$updateId) {
|
|
$updateMsg = @{
|
|
hook = 'FinishUpdate'
|
|
value = @{
|
|
session_id = $global:_warpSessionId
|
|
update_id = $updateId
|
|
}
|
|
}
|
|
Warp-Send-JsonMessage $updateMsg
|
|
}
|
|
|
|
function Warp-Handle-DistUpgrade {
|
|
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingInvokeExpression', '', Justification = 'We actually need it')]
|
|
param([string]$sourceFileName)
|
|
|
|
$aptConfig = Get-Command -Type Application apt-config | Select-Object -First 1
|
|
& $aptConfig shell '$aptSourcesDir' 'Dir::Etc::sourceparts/d' | Invoke-Expression
|
|
|
|
$sourceFilePath = "${aptSourcesDir}${sourceFileName}"
|
|
|
|
if (
|
|
-not (Test-Path "${sourceFilePath}.list") -and
|
|
-not (Test-Path "${sourceFilePath}.sources") -and
|
|
(Test-Path "${sourceFilePath}.list.distUpgrade")
|
|
) {
|
|
# DO NOT DO THIS. We should never run a command for user with 'sudo'. The only reason this
|
|
# is safe here is because we insert this function into the input for the user to determine
|
|
# if they want to execute (we never run it on their behalf without their permission).
|
|
sudo cp "${sourceFilePath}.list.distUpgrade" "${sourceFilePath}.list"
|
|
}
|
|
}
|
|
|
|
# We need this for a few reasons
|
|
# 1. We need to make sure the environment variable GIT_OPTIONAL_LOCKS=0.
|
|
# See https://stackoverflow.com/questions/71836872/git-environment-variables-on-powershell-on-windows
|
|
# for why this is complicated
|
|
# 2. We need to make sure that we are calling the Application git, and not
|
|
# an alias or cmdlet named Git
|
|
#
|
|
# NOTE: Inlining this call in the function has a weird side effect of outputting
|
|
# an escape sequence '^[i'. Since it made it more convenient to have a wrapper
|
|
# function anyway, I have not investigated this, but in case someone is working
|
|
# on this in the future, beware attempting to inline this function.
|
|
function Warp-Git {
|
|
$GIT_OPTIONAL_LOCKS = $env:GIT_OPTIONAL_LOCKS
|
|
$env:GIT_OPTIONAL_LOCKS = 0
|
|
try {
|
|
&(Get-Command -CommandType Application git | Select-Object -First 1) $args
|
|
} finally {
|
|
$env:GIT_OPTIONAL_LOCKS = $GIT_OPTIONAL_LOCKS
|
|
}
|
|
}
|
|
|
|
# Helper function that resets the values of '$?' and
|
|
# $LASTEXITCODE. Note that it cannot force '$?' to $true
|
|
# if it is currently $false
|
|
#
|
|
# Make sure when you call this you call it with -ErrorAction SilentlyContinue
|
|
# or it will print out error information when it is invoked.
|
|
function Warp-Restore-ErrorStatus {
|
|
[CmdletBinding()]
|
|
param([boolean]$status, [int]$code)
|
|
|
|
$global:LASTEXITCODE = $code
|
|
if ($status -eq $false) {
|
|
$PSCmdlet.WriteError([System.Management.Automation.ErrorRecord]::new(
|
|
[Exception]::new("$([char]0x00)"),
|
|
'warp-reset-error',
|
|
[System.Management.Automation.ErrorCategory]::NotSpecified,
|
|
$null
|
|
))
|
|
}
|
|
}
|
|
|
|
# Tracks whether or not powershell is unable to find a command.
|
|
# See the $ExecutionContext.InvokeCommand.CommandNotFoundAction where it is set to $true,
|
|
# and both $ExecutionContext.InvokeCommand.PostCommandLookupAction and Warp-Precmd where
|
|
# it is set to $false.
|
|
$script:commandNotFound = $false
|
|
|
|
function Warp-Configure-PSReadLine {
|
|
# Set-PSReadLineKeyHandler is the PowerShell equivalent of zsh's bindkey.
|
|
Set-PSReadLineKeyHandler -Chord 'Alt+2' -Function BackwardDeleteLine
|
|
|
|
# Input reporting. Note that ESC-1 is used instead of ESC-i as for all other shells. This
|
|
# is because PowerShell on Windows does some virtual key code translation which depends on
|
|
# the selected input language. On languages without an "i" on any key, this translation
|
|
# fails and the binding gets dropped.
|
|
Set-PSReadLineKeyHandler -Chord 'Alt+1' -ScriptBlock {
|
|
$inputBuffer = $null
|
|
$cursorPosition = $null
|
|
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$inputBuffer, [ref]$cursorPosition)
|
|
$inputBufferMsg = @{
|
|
hook = 'InputBuffer'
|
|
value = @{
|
|
session_id = $global:_warpSessionId
|
|
buffer = $inputBuffer
|
|
}
|
|
}
|
|
Warp-Send-JsonMessage $inputBufferMsg
|
|
[Microsoft.PowerShell.PSConsoleReadLine]::BackwardDeleteLine()
|
|
# This is triggered after precmd, so output here goes to the "early output" handler,
|
|
# i.e. the background block. This clears the line the cursor is on. We clear it out b/c
|
|
# at this point, the only stuff in the early output handler is typeahead, and that
|
|
# shouldn't be displayed in a background block at all. It should be in the input
|
|
# editor. Most shells will automatically emit the correct ANSI escape codes to delete
|
|
# the contents of the early output handler when we kill the line editor's buffer.
|
|
# However, PowerShell doesn't do this correctly due to cursor position mismatch. So,
|
|
# we do it manually here instead.
|
|
Write-Host -NoNewline "$([char]0x1b)[2K"
|
|
}
|
|
|
|
# Sets the prompt mode to custom prompt (PS1)
|
|
# Is the equivalent of warp_change_prompt_modes_to_ps1 in other shells
|
|
Set-PSReadLineKeyHandler -Chord 'Alt+p' -ScriptBlock {
|
|
$env:WARP_HONOR_PS1 = '1'
|
|
Warp-Redraw-Prompt
|
|
}
|
|
|
|
# Sets the prompt mode to warp prompt
|
|
# Is the equivalent of warp_change_prompt_modes_to_warp_prompt in other shells
|
|
Set-PSReadLineKeyHandler -Chord 'Alt+w' -ScriptBlock {
|
|
$env:WARP_HONOR_PS1 = '0'
|
|
Warp-Redraw-Prompt
|
|
}
|
|
|
|
Set-PSReadLineOption -AddToHistoryHandler {
|
|
param([string]$line)
|
|
|
|
if ($line -match '^Warp-Run-GeneratorCommand') {
|
|
return $false
|
|
}
|
|
return $true
|
|
}
|
|
|
|
Warp-Disable-PSPrediction
|
|
}
|
|
|
|
# Force use of the Inline PredictionViewStyle. The ListView style can occasionally cause some
|
|
# flickering when using Warp and it doesn't matter what the value of this setting is because
|
|
# Warp has its own input editor.
|
|
function Warp-Disable-PSPrediction {
|
|
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseCompatibleCommands', '', Justification = 'Errors are ignored')]
|
|
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingEmptyCatchBlock', '', Justification = 'Errors expected')]
|
|
param()
|
|
try {
|
|
Set-PSReadLineOption -PredictionSource None
|
|
Set-PSReadLineOption -PredictionViewStyle InlineView
|
|
} catch {
|
|
}
|
|
}
|
|
|
|
function Warp-Precmd {
|
|
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPositionalParameters', '', Justification = 'Warp-Git should use positionals')]
|
|
param([bool]$status, [int]$code)
|
|
# Our logic here is:
|
|
#
|
|
# if $status == True, always set $exitCode to 0
|
|
# if $status == False and $script:commandNotFound is true
|
|
# (meaning we triggered the CommandNotFoundHandler), set $exitCode to 127
|
|
# if $status == False and $LASTEXITCODE is zero, set $exitCode to 1
|
|
# else set $exitCode to $LASTEXITCODE
|
|
#
|
|
# Note that this is not going to be 100% accurate, as some cmdlets will fail
|
|
# without setting a $LASTEXITCODE, meaning the $LASTEXITCODE will be stale.
|
|
$warpCommandNotFound = $script:commandNotFound
|
|
$script:commandNotFound = $false
|
|
|
|
$exitCode = if ($status) {
|
|
0
|
|
} elseif ($warpCommandNotFound) {
|
|
127
|
|
} elseif ($code -eq 0) {
|
|
1
|
|
} else {
|
|
$code
|
|
}
|
|
|
|
$newTitle = (Get-Location).Path
|
|
# Replace the literal home dir with a tilde.
|
|
if ($newTitle.StartsWith($HOME)) {
|
|
$newTitle = '~' + $newTitle.Substring($HOME.length)
|
|
}
|
|
$HOST.UI.RawUI.WindowTitle = $newTitle
|
|
|
|
$blockId = $script:nextBlockId++
|
|
$nextBlockId = "precmd-${global:_warpSessionId}-$blockId"
|
|
$commandFinishedMsg = @{
|
|
hook = 'CommandFinished'
|
|
value = @{
|
|
session_id = $global:_warpSessionId
|
|
exit_code = $exitCode
|
|
next_block_id = $nextBlockId
|
|
}
|
|
}
|
|
Warp-Send-JsonMessage $commandFinishedMsg
|
|
Warp-Send-ResetGridOSC
|
|
|
|
Warp-Configure-PSReadLine
|
|
|
|
# If this is being called for a generator command, short circuit and send an unpopulated
|
|
# precmd payload (except for pwd), since we don't re-render the prompt after generator commands
|
|
# are run.
|
|
if ($script:generatorCommand -eq $true) {
|
|
# TODO(CORE-2639): handle user PreCmds here
|
|
|
|
$script:generatorCommand = $false
|
|
|
|
$precmdMsg = @{
|
|
hook = 'Precmd'
|
|
value = @{
|
|
exit_code = $exitCode
|
|
next_block_id = $nextBlockId
|
|
pwd = ''
|
|
ps1 = ''
|
|
git_head = ''
|
|
git_branch = ''
|
|
virtual_env = ''
|
|
conda_env = ''
|
|
session_id = $global:_warpSessionId
|
|
is_after_in_band_command = $true
|
|
}
|
|
}
|
|
Warp-Send-JsonMessage $precmdMsg
|
|
} else {
|
|
# TODO(CORE-2678): Figure out resetting bindkeys here
|
|
|
|
$virtualEnv = ''
|
|
$condaEnv = ''
|
|
$kubeConfig = ''
|
|
$gitHead = ''
|
|
$gitBranch = ''
|
|
$nodeVersion = ''
|
|
|
|
# Only fill these fields once we've finished bootstrapping, as the
|
|
# blocks created during the bootstrap process don't have visible
|
|
# prompts, and we don't want to invoke 'git' before we've sourced the
|
|
# user's rcfiles and have a fully-populated PATH.
|
|
if ($global:WARP_BOOTSTRAPPED -eq 1) {
|
|
if (Test-Path env:VIRTUAL_ENV) {
|
|
$virtualEnv = $env:VIRTUAL_ENV
|
|
}
|
|
if (Test-Path env:CONDA_DEFAULT_ENV) {
|
|
$condaEnv = $env:CONDA_DEFAULT_ENV
|
|
}
|
|
if (Test-Path env:KUBECONFIG) {
|
|
$kubeConfig = $env:KUBECONFIG
|
|
}
|
|
|
|
# Compute the Node.js version only when the Node.js Version chip is enabled
|
|
# (WARP_PROMPT_NODE_VERSION_ENABLED is '0' when the chip is not shown; default
|
|
# enabled when unset) and node is available. Cache the result keyed on the
|
|
# current location + PATH so we only spawn node when the directory or PATH
|
|
# changes (PATH changes on version-manager switches like `nvm use`).
|
|
$nodeChipEnabled = "$env:WARP_PROMPT_NODE_VERSION_ENABLED" -ne '0'
|
|
$hasNodeCommand = if ($nodeChipEnabled) { Get-Command -CommandType Application node 2>$null } else { $null }
|
|
if ($hasNodeCommand) {
|
|
try {
|
|
$nodeCacheKey = "$((Get-Location).Path)|$env:PATH"
|
|
if ($nodeCacheKey -eq $script:warpNodeVersionCacheKey) {
|
|
$nodeVersion = $script:warpNodeVersionCacheValue
|
|
} else {
|
|
# Walk up from the current directory to find a package.json
|
|
$dir = Get-Item -LiteralPath (Get-Location).Path
|
|
$foundPackageJson = $false
|
|
$packageJsonDir = $null
|
|
while ($null -ne $dir) {
|
|
$candidate = Join-Path $dir.FullName 'package.json'
|
|
if (Test-Path -LiteralPath $candidate) {
|
|
$foundPackageJson = $true
|
|
$packageJsonDir = $dir.FullName
|
|
break
|
|
}
|
|
$dir = $dir.Parent
|
|
}
|
|
|
|
if ($foundPackageJson) {
|
|
# Verify package.json resides within a Git repository by walking up to find a .git directory
|
|
$probe = Get-Item -LiteralPath $packageJsonDir
|
|
$inGitRepo = $false
|
|
while ($null -ne $probe) {
|
|
if (Test-Path -LiteralPath (Join-Path $probe.FullName '.git')) {
|
|
$inGitRepo = $true
|
|
break
|
|
}
|
|
$probe = $probe.Parent
|
|
}
|
|
|
|
if ($inGitRepo) {
|
|
$nodeVersion = Warp-TryGet-NodeVersion
|
|
}
|
|
}
|
|
|
|
$script:warpNodeVersionCacheKey = $nodeCacheKey
|
|
$script:warpNodeVersionCacheValue = $nodeVersion
|
|
}
|
|
} catch {
|
|
# Log at verbose level so the catch block is not empty and diagnostics are available when needed.
|
|
Write-Verbose "Failed to compute Node.js context: $($_.Exception.Message)"
|
|
}
|
|
}
|
|
|
|
# We do not inline $hasGitCommand b/c the linter does not like seeing '>'
|
|
# in an if statement; it thinks we are trying to do -gt incorrectly.
|
|
# Since this is a good warning and we do not want to turn off this lint rule,
|
|
# we do a little indirection here
|
|
$hasGitCommand = Get-Command -CommandType Application git 2>$null
|
|
if ($hasGitCommand) {
|
|
# This is deliberately not using || b/c || only works in Powershell >=7
|
|
$gitBranchTmp = Warp-Git symbolic-ref --short HEAD 2>$null
|
|
if ($null -ne $gitBranchTmp) {
|
|
$gitBranch = $gitBranchTmp
|
|
$gitHead = $gitBranchTmp
|
|
} else {
|
|
$gitHeadTmp = Warp-Git rev-parse --short HEAD 2>$null
|
|
if ($null -ne $gitHeadTmp) {
|
|
$gitHead = $gitHeadTmp
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$honor_ps1 = "$env:WARP_HONOR_PS1" -eq '1'
|
|
|
|
$precmdMsg = @{
|
|
hook = 'Precmd'
|
|
value = @{
|
|
exit_code = $exitCode
|
|
next_block_id = $nextBlockId
|
|
pwd = (Get-Location).Path
|
|
# TODO(PLAT-687) - honor the PS1
|
|
ps1 = ''
|
|
honor_ps1 = $honor_ps1
|
|
# TODO(PLAT-687) - pwsh does not by default support rprompt, but
|
|
# oh-my-posh does. If there is a way to easily extract the oh-my-posh
|
|
# rprompt, we might want to use it here
|
|
rprompt = ''
|
|
git_head = $gitHead
|
|
git_branch = $gitBranch
|
|
virtual_env = $virtualEnv
|
|
conda_env = $condaEnv
|
|
node_version = $nodeVersion
|
|
session_id = $global:_warpSessionId
|
|
kube_config = $kubeConfig
|
|
}
|
|
}
|
|
Warp-Send-JsonMessage $precmdMsg
|
|
}
|
|
}
|
|
|
|
$script:inBandCommandCount = 0
|
|
$script:threadInner = @{}
|
|
$script:threadOuter = @{}
|
|
|
|
# The inner runspace pool maintains a pool of runspaces that can execute
|
|
# arbitrary commands against the user's current environment without
|
|
# writing to the screen. Initialize to minimum of 10 runspaces
|
|
# to handle double the number of context chips we currently have
|
|
# that use in-band commands
|
|
$script:innerRunspacePool = [runspacefactory]::CreateRunspacePool(10, 20)
|
|
$script:innerRunspacePool.ApartmentState = [System.Threading.ApartmentState]::STA
|
|
$script:innerRunspacePool.ThreadOptions = 'ReuseThread'
|
|
$script:innerRunspacePool.Open() | Out-Null
|
|
|
|
# The outer runspace pool maintains a pool of runspaces that
|
|
# share the same host as the user's session. This allows them
|
|
# to send OSC commands via Write-Host. These outer runspaces
|
|
# handle receiving results from the inner runspaces and formatting
|
|
# those results into OSCs.
|
|
# Initialized to minimum of 5 runspaces since we currently do not
|
|
# run more than one outer command at a time.
|
|
$script:outerRunspacePool = [runspacefactory]::CreateRunspacePool(5, 10, $Host)
|
|
$script:outerRunspacePool.ApartmentState = [System.Threading.ApartmentState]::STA
|
|
$script:outerRunspacePool.ThreadOptions = 'ReuseThread'
|
|
$script:outerRunspacePool.Open() | Out-Null
|
|
|
|
class WarpGeneratorCommand {
|
|
[string]$CommandId
|
|
[string]$Command
|
|
}
|
|
|
|
function Warp-Run-GeneratorCommandImpl {
|
|
param(
|
|
[WarpGeneratorCommand[]]$commands
|
|
)
|
|
|
|
$jobNumber = $script:inBandCommandCount++
|
|
|
|
$batchNumber = 0
|
|
$jobs = $commands | ForEach-Object {
|
|
$commandId = $_.CommandId
|
|
$command = $_.Command
|
|
|
|
# Creates a powershell instance on one of our inner runspaces
|
|
# that first loads all the warp common functions, and then
|
|
# executes the in-band generator in the current directory
|
|
$ps = [powershell]::Create()
|
|
$ps.RunspacePool = $script:innerRunspacePool
|
|
$ps.AddScript($warpCommon) | Out-Null
|
|
$ps.AddScript({
|
|
param([string]$loc, [string]$commandId, [string]$command)
|
|
Set-Location $loc
|
|
Warp-Run-InBandGenerator -commandId $commandId -command "$command"
|
|
}).AddParameters(@($PWD.Path, $commandId, "$command")) | Out-Null
|
|
|
|
$script:threadInner["Warp-Inner-$jobNumber-$batchNumber"] = $psInner
|
|
$batchNumber++
|
|
|
|
@{
|
|
commandId = $commandId
|
|
ps = $ps
|
|
}
|
|
}
|
|
|
|
# Creates the outer job, which waits on all the inner jobs
|
|
# and then sends the results back to Warp via OSC
|
|
$psOuter = [powershell]::Create()
|
|
$psOuter.RunspacePool = $script:outerRunspacePool
|
|
$psOuter.AddScript($warpCommon) | Out-Null
|
|
$psOuter.AddScript({
|
|
param([object[]]$jobs)
|
|
|
|
$invocations = $jobs | ForEach-Object {
|
|
@{
|
|
commandId = $_.commandId
|
|
ps = $_.ps
|
|
async = $_.ps.BeginInvoke()
|
|
}
|
|
}
|
|
|
|
$invocations | ForEach-Object {
|
|
$commandId = $_.commandId
|
|
$ps = $_.ps
|
|
$async = $_.async
|
|
|
|
$output = "$commandId;1;"
|
|
|
|
try {
|
|
$output = $ps.EndInvoke($async)
|
|
} catch {
|
|
$output = "$commandId;1;"
|
|
}
|
|
Warp-Send-GeneratorOutputOsc $output
|
|
}
|
|
}).AddParameters(@($jobs)) | Out-Null
|
|
|
|
# Note: we are beginning the invocation, but are explicitly
|
|
# not stopping it as we do not want to block the main thread.
|
|
$async = $psOuter.BeginInvoke()
|
|
|
|
$script:threadOuter["Warp-Outer-$jobNumber"] = $psOuter
|
|
}
|
|
|
|
function Warp-Stop-ActiveThread {
|
|
$script:threadInner.values | ForEach-Object {
|
|
$_.Stop()
|
|
}
|
|
}
|
|
|
|
function Warp-Clean-CompletedThread {
|
|
# Powershell instances states > 2 are terminal.
|
|
# See https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.psinvocationstate
|
|
if ($script:threadInner.Count -gt 0) {
|
|
$script:threadInner.Keys.Clone() | ForEach-Object {
|
|
$thread = $script:threadInner[$_]
|
|
$state = [int]$thread.InvocationStateInfo.State
|
|
if ($state -gt 2) {
|
|
$thread.Dispose()
|
|
$script:threadInner.Remove($_)
|
|
}
|
|
}
|
|
}
|
|
if ($script:threadOuter.Count -gt 0) {
|
|
$script:threadOuter.Keys.Clone() | ForEach-Object {
|
|
$thread = $script:threadOuter[$_]
|
|
$state = [int]$thread.InvocationStateInfo.State
|
|
if ($state -gt 2) {
|
|
$thread.Dispose()
|
|
$script:threadOuter.Remove($_)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function Warp-Run-GeneratorCommand {
|
|
[CmdletBinding()]
|
|
param(
|
|
[parameter(ValueFromRemainingArguments = $true)][string[]]$passedArgs
|
|
)
|
|
|
|
$status = $?
|
|
$code = $global:LASTEXITCODE
|
|
|
|
# Setting this environment variable prevents warp_precmd from emitting the
|
|
# 'Block started' hook to the Rust app.
|
|
$script:generatorCommand = $true
|
|
|
|
# TODO(CORE-2639) If we ever start supporting user precmd or preexec
|
|
# (which doesn't really exist in powershell, but :shrug:), we need
|
|
# to properly handle them here like we do in bashzshfish
|
|
|
|
# Converts the passed in args to WarpGeneratorCommand objects to group them together
|
|
# note that if an odd number of arguments is passed in, the last arg will be silently ignored
|
|
[WarpGeneratorCommand[]] $jobs = @()
|
|
for ($i = 0; $i -lt $passedArgs.Length; $i += 2) {
|
|
$commandId = $passedArgs[$i]
|
|
$command = $passedArgs[$i + 1]
|
|
|
|
if ($null -ne $command) {
|
|
$jobs += [WarpGeneratorCommand]@{
|
|
commandId = $commandId
|
|
command = $command
|
|
}
|
|
}
|
|
}
|
|
|
|
try {
|
|
Warp-Run-GeneratorCommandImpl -commands $jobs
|
|
} finally {
|
|
# NOTE: for some reason the Warp-Restore-ErrorStatus does not work
|
|
# for this function, so we are inlining it in here.
|
|
$global:LASTEXITCODE = $code
|
|
if ($status -eq $false) {
|
|
$PSCmdlet.WriteError([System.Management.Automation.ErrorRecord]::new(
|
|
[Exception]::new("$([char]0x00)"),
|
|
'warp-reset-error',
|
|
[System.Management.Automation.ErrorCategory]::NotSpecified,
|
|
$null
|
|
))
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
function Warp-Render-Prompt {
|
|
param([bool]$status, [int]$code, [bool]$isGeneratorCommand)
|
|
|
|
# If this is a generator command, we do not want to recompute
|
|
# the prompt, and instead want to return the original prompt.
|
|
if ($isGeneratorCommand) {
|
|
return $script:lastRenderedPrompt
|
|
}
|
|
|
|
# Reset error code for computing prompt
|
|
$global:LASTEXITCODE = $code
|
|
if (-not $status) {
|
|
# Set's $? to false for the next function call,
|
|
# so it can be used for computing the prompt
|
|
Write-Error '' -ErrorAction Ignore
|
|
}
|
|
|
|
# Compute prompt and cache it as the last rendered prompt
|
|
$basePrompt = & $global:_warpOriginalPrompt
|
|
$script:lastRenderedPrompt = $basePrompt
|
|
|
|
return $basePrompt
|
|
}
|
|
|
|
function Warp-Decorate-Prompt {
|
|
param([string]$basePrompt)
|
|
|
|
$e = "$([char]0x1b)"
|
|
|
|
# Wrap prompt in Prompt Marker OSCs
|
|
$startPromptMarker = "$e]133;A$oscEnd"
|
|
$startRPromptMarker = "$e]133;P;k=r$oscEnd"
|
|
if ("$env:WARP_HONOR_PS1" -eq '0') {
|
|
$endPromptMarker = "$e]133;B$oscEnd$oscResetGrid"
|
|
} else {
|
|
$endPromptMarker = "$e]133;B$oscEnd"
|
|
}
|
|
$decoratedPrompt = "$basePrompt"
|
|
|
|
# We only redecorate the prompt if it is not already decorated
|
|
if (-not ($basePrompt -match '^\x1b]133;A')) {
|
|
$decoratedPrompt = "$startPromptMarker$basePrompt$endPromptMarker"
|
|
# Special case for ohmyposh that prints an rprompt. If it matches the format of ohmyposh
|
|
# rprompt, we properly parse it into lprompt and rprompt
|
|
if ($basePrompt -match '(?<lprompt>.*)[\x1b]7\s*(?<rprompt>\S.*)[\x1b]8') {
|
|
$lprompt = $Matches.lprompt
|
|
$rprompt = $Matches.rprompt
|
|
$decoratedPrompt = "$startPromptMarker$lprompt$endPromptMarker${e}7$startRPromptMarker$rprompt$endPromptMarker${e}8"
|
|
}
|
|
}
|
|
|
|
return $decoratedPrompt
|
|
}
|
|
|
|
$script:dontRunPrecmdForPrompt = $false
|
|
# Redraws the prompt. Since our prompt also triggers the precmd hook
|
|
# we need to signal that we do not want that to happen
|
|
function Warp-Redraw-Prompt {
|
|
param()
|
|
|
|
$y = $Host.UI.RawUI.CursorPosition.Y
|
|
$script:dontRunPrecmdForPrompt = $true
|
|
try {
|
|
[Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt($null, $y)
|
|
} finally {
|
|
$script:dontRunPrecmdForPrompt = $false
|
|
}
|
|
}
|
|
|
|
function Warp-Prompt {
|
|
param()
|
|
|
|
# We need to capture all the data related to exit codes and such
|
|
# as soon as possible for a few reasons
|
|
# 1. We need to make sure that these values are as fresh as possible
|
|
# and are not impacted by our Warp- functions
|
|
# 2. After we finish running Warp-Precmd and Warp-Render-Prompt, we want to set these values
|
|
# back to what they were originally
|
|
$status = $?
|
|
$code = $LASTEXITCODE
|
|
$isGeneratorCommand = [bool]($script:generatorCommand -eq $true)
|
|
|
|
if ($script:dontRunPrecmdForPrompt -ne $true) {
|
|
Warp-Precmd -status $status -code $code
|
|
}
|
|
|
|
$script:preexecHandled = $false
|
|
|
|
$renderedPrompt = Warp-Render-Prompt -status $status -code $code -isGeneratorCommand $isGeneratorCommand
|
|
$decoratedPrompt = Warp-Decorate-Prompt -basePrompt $renderedPrompt
|
|
$extraLines = ($decoratedPrompt -split "$([char]0x0a)").Length - 1
|
|
Set-PSReadLineOption -ExtraPromptLineCount $extraLines
|
|
|
|
# NOTE: Because we are in the prompt, we do not need to reset
|
|
# the $? automatic variable (apparently $prompt does not impact it).
|
|
# However, we do need to reset the LASTEXITCODE. If we ever refactor
|
|
# this to not use the prompt, then watch out for $?
|
|
$global:LASTEXITCODE = $code
|
|
|
|
return $decoratedPrompt
|
|
}
|
|
|
|
if ((Test-Path env:WARP_INITIAL_WORKING_DIR) -and -not [String]::IsNullOrEmpty($env:WARP_INITIAL_WORKING_DIR)) {
|
|
Set-Location $env:WARP_INITIAL_WORKING_DIR 2> $null
|
|
Remove-Item -Path env:WARP_INITIAL_WORKING_DIR
|
|
}
|
|
|
|
# In some cases, the Clear-Host command will not interface properly with the blocklist.
|
|
# Clear-Host defers to whatever the 'clear' command is defined, and if that command
|
|
# is not set up to work with Warp (or has funky other behaviors) it can cause problems.
|
|
#
|
|
# Specific examples:
|
|
# - The default /usr/bin/clear on mac creates a giant, empty block to clear content
|
|
# off of the screen.
|
|
# - if miniconda is installed on an osx system, the miniconda 'clear' command will be
|
|
# invoked for 'Clear-Host', which does not play with Warp and winds up doing nothing.
|
|
|
|
# Because of the above, we explicitly override both 'Clear-Host' and 'clear' to
|
|
# instead send a DCS command to Warp instructing it to clear the blocklist.
|
|
# We are explicitly NOT calling the underlying clear implementation:
|
|
# 1. B/c traditional clear sends an escape sequence that ends up creating an
|
|
# empty block that is the full height of the screen.
|
|
# 1. B/c our other bootstrap scripts (bash, zsh, fish) do not.
|
|
|
|
# If we ever want to call the underlying clear command, we could do so by:
|
|
# 1. Capturing it with '$_warp_original_clear = (Get-Command Clear-Host).Definition'
|
|
# 2. Invoking it with 'Invoke-Expression $_warp_original_clear'
|
|
|
|
# TODO(PLAT-781): On windows, these two functions should both clear the visible screen
|
|
# AND the scrollback
|
|
function Clear-Host() {
|
|
$inputBufferMsg = @{
|
|
hook = 'Clear'
|
|
value = @{
|
|
session_id = $global:_warpSessionId
|
|
}
|
|
}
|
|
Warp-Send-JsonMessage $inputBufferMsg
|
|
}
|
|
|
|
function clear() {
|
|
$inputBufferMsg = @{
|
|
hook = 'Clear'
|
|
value = @{
|
|
session_id = $global:_warpSessionId
|
|
}
|
|
}
|
|
Warp-Send-JsonMessage $inputBufferMsg
|
|
}
|
|
|
|
function Warp-Finish-Bootstrap {
|
|
param([decimal]$rcStartTime, [decimal]$rcEndTime)
|
|
# This is the closest we can get in PowerShell to a proper preexec hook. We wrap the
|
|
# invocation of PSConsoleHostReadline, and call our preexec hook before returning the
|
|
# returned value. This allows us to preserve the any custom implementations of
|
|
# PSConsoleHostReadLine.
|
|
$script:oldPSConsoleHostReadLine = $function:global:PSConsoleHostReadLine
|
|
$function:global:PSConsoleHostReadLine = {
|
|
$line = & $script:oldPSConsoleHostReadLine
|
|
|
|
Warp-Preexec "$line"
|
|
|
|
$line
|
|
}
|
|
|
|
# This handles the case when a command is not found (ex "ehco foo"). As long as it is a
|
|
# user-executed command, we set the $script:commandNotFound variable to $true, so we know
|
|
# that the command failed b/c of a command lookup failure.
|
|
$ExecutionContext.InvokeCommand.CommandNotFoundAction = {
|
|
$commandLine = $MyInvocation.Line
|
|
# Only trigger the preexec hook for user-submitted commands
|
|
# $EventArgs.CommandOrigin is either 'Runspace' or 'Internal'. Internal commands are run
|
|
# automatically by PowerShell internals. Runspace is for user-submitted/configured stuff.
|
|
# However, Runspace still includes stuff like the prompt function, PostCommandLookupAction,
|
|
# and the stuff we set during this bootstrap. So, add a condition to prevent preexec from
|
|
# triggering in those cases. Note that we prefix our own functions with the "Warp-" prefix
|
|
# so that we can ignore them here.
|
|
if ($EventArgs.CommandOrigin -ne 'Runspace' -or ($commandLine -match '^prompt$|^Warp-')) {
|
|
return
|
|
}
|
|
$script:commandNotFound = $true
|
|
}
|
|
|
|
# This sets up our wrapper around $function:prompt, which runs the precmd hook
|
|
# and computes the user's custom prompt.
|
|
$function:global:prompt = (Get-Command Warp-Prompt).ScriptBlock
|
|
Warp-Bootstrapped -rcStartTime $rcStartTime -rcEndTime $rcEndTime
|
|
}
|
|
|
|
###########################################################
|
|
# NOTE: NO non-bootstrap / non-user calls below this line #
|
|
###########################################################
|
|
|
|
# Send a precmd message to the terminal to differentiate between the warp
|
|
# bootstrap logic pasted into the PTY and the output of shell startup files.
|
|
Warp-Precmd -status $global:? -code $global:LASTEXITCODE
|
|
|
|
Export-ModuleMember -Function clear, Clear-Host, Get-EpochTime, Warp-Finish-Update, Warp-Handle-DistUpgrade, Warp-Run-GeneratorCommand, Warp-Finish-Bootstrap
|
|
}
|
|
|
|
# Finally, get ready to source the user's RC files. This must be done in the global scope (not
|
|
# inside Warp-Module) in order to obey the expected scoping in PowerShell's typical startup process.
|
|
. {
|
|
$rcStartTime = Get-EpochTime
|
|
# Source the user's RC files
|
|
# https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles?view=powershell-7.4#profile-types-and-locations
|
|
# https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles?view=powershell-7.4#the-profile-variable
|
|
foreach ($file in @($PROFILE.AllUsersAllHosts, $PROFILE.AllUsersCurrentHost, $PROFILE.CurrentUserAllHosts, $PROFILE.CurrentUserCurrentHost)) {
|
|
if ([System.IO.File]::Exists($file)) {
|
|
try {
|
|
. $file
|
|
} catch {
|
|
Write-Host -ForegroundColor Red $_.InvocationInfo.PositionMessage
|
|
Write-Host -ForegroundColor DarkRed $_.Exception
|
|
}
|
|
}
|
|
}
|
|
|
|
# Append additional PATH entries if provided via WARP_PATH_APPEND.
|
|
# This happens after we source RC files in case they reset PATH.
|
|
if (-not [String]::IsNullOrEmpty($env:WARP_PATH_APPEND)) {
|
|
$env:PATH = '{0}{1}{2}' -f $env:PATH, [IO.Path]::PathSeparator, $env:WARP_PATH_APPEND
|
|
Remove-Item -Path env:WARP_PATH_APPEND
|
|
}
|
|
|
|
# This is a workaround for oh-my-posh's "transient prompt" feature. When enabled, it causes the
|
|
# whole screen to clear on every command execution. It is implemented by overwriting the Enter
|
|
# and ctrl-c key handlers. Resetting those back to default effectively disables it.
|
|
# TODO(CORE-3234) - Find a workaround which allows transient prompt to work.
|
|
$enterHandler = Get-PSReadLineKeyHandler | Where-Object -Property Key -EQ -Value 'Enter'
|
|
if ($enterHandler -ne $null -and $enterHandler.Function -eq 'OhMyPoshEnterKeyHandler') {
|
|
Set-PSReadLineKeyHandler -Chord Enter -Function AcceptLine
|
|
}
|
|
$ctrlcHandler = Get-PSReadLineKeyHandler | Where-Object -Property Key -EQ -Value 'Control+c'
|
|
if ($ctrlcHandler -ne $null -and $ctrlcHandler.Function -eq 'OhMyPoshCtrlCKeyHandler') {
|
|
Set-PSReadLineKeyHandler -Chord 'Control+c' -Function CopyOrCancelLine
|
|
}
|
|
|
|
$rcEndTime = Get-EpochTime
|
|
|
|
# Capture the current prompt (potentially modified by a profile),
|
|
# and then reset the prompt to our current noop prompt.
|
|
$global:_warpOriginalPrompt = $function:global:prompt
|
|
|
|
Warp-Finish-Bootstrap -rcStartTime $rcStartTime -rcEndTime $rcEndTime
|
|
Remove-Variable -Name enterHandler, ctrlcHandler, rcStartTime, rcEndTime -Scope global -ErrorAction Ignore
|
|
|
|
# Restore the process's original execution policy now that the user's RC files have been loaded.
|
|
if ($global:_warp_PSProcessExecPolicy -ne $null) {
|
|
Set-ExecutionPolicy -Scope Process -ExecutionPolicy $global:_warp_PSProcessExecPolicy
|
|
}
|
|
}
|