@@ -583,7 +583,7 @@ internal Task LoadHostProfilesAsync(CancellationToken cancellationToken)
583
583
584
584
private Task EnableShellIntegrationAsync ( CancellationToken cancellationToken )
585
585
{
586
- // Imported on 01/03/23 from
586
+ // Imported on 01/03/24 from
587
587
// https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1
588
588
// with quotes escaped, `__VSCodeOriginalPSConsoleHostReadLine` removed (as it's done
589
589
// in our own ReadLine function), and `[Console]::Write` replaced with `Write-Host`.
@@ -602,42 +602,74 @@ private Task EnableShellIntegrationAsync(CancellationToken cancellationToken)
602
602
603
603
$Global:__LastHistoryId = -1
604
604
605
+ # Store the nonce in script scope and unset the global
606
+ $Nonce = $env:VSCODE_NONCE
607
+ $env:VSCODE_NONCE = $null
608
+
609
+ if ($env:VSCODE_ENV_REPLACE) {
610
+ $Split = $env:VSCODE_ENV_REPLACE.Split("":"")
611
+ foreach ($Item in $Split) {
612
+ $Inner = $Item.Split('=')
613
+ [Environment]::SetEnvironmentVariable($Inner[0], $Inner[1].Replace('\x3a', ':'))
614
+ }
615
+ $env:VSCODE_ENV_REPLACE = $null
616
+ }
617
+ if ($env:VSCODE_ENV_PREPEND) {
618
+ $Split = $env:VSCODE_ENV_PREPEND.Split("":"")
619
+ foreach ($Item in $Split) {
620
+ $Inner = $Item.Split('=')
621
+ [Environment]::SetEnvironmentVariable($Inner[0], $Inner[1].Replace('\x3a', ':') + [Environment]::GetEnvironmentVariable($Inner[0]))
622
+ }
623
+ $env:VSCODE_ENV_PREPEND = $null
624
+ }
625
+ if ($env:VSCODE_ENV_APPEND) {
626
+ $Split = $env:VSCODE_ENV_APPEND.Split("":"")
627
+ foreach ($Item in $Split) {
628
+ $Inner = $Item.Split('=')
629
+ [Environment]::SetEnvironmentVariable($Inner[0], [Environment]::GetEnvironmentVariable($Inner[0]) + $Inner[1].Replace('\x3a', ':'))
630
+ }
631
+ $env:VSCODE_ENV_APPEND = $null
632
+ }
633
+
605
634
function Global:__VSCode-Escape-Value([string]$value) {
606
635
# NOTE: In PowerShell v6.1+, this can be written `$value -replace '…', { … }` instead of `[regex]::Replace`.
607
636
# Replace any non-alphanumeric characters.
608
637
[regex]::Replace($value, '[\\\n;]', { param($match)
609
- # Encode the (ascii) matches as `\x<hex>`
610
- -Join (
611
- [System.Text.Encoding]::UTF8.GetBytes($match.Value) | ForEach-Object { '\x{0:x2}' -f $_ }
612
- )
613
- })
638
+ # Encode the (ascii) matches as `\x<hex>`
639
+ -Join (
640
+ [System.Text.Encoding]::UTF8.GetBytes($match.Value) | ForEach-Object { '\x{0:x2}' -f $_ }
641
+ )
642
+ })
614
643
}
615
644
616
645
function Global:Prompt() {
646
+ $FakeCode = [int]!$global:?
617
647
# NOTE: We disable strict mode for the scope of this function because it unhelpfully throws an
618
648
# error when $LastHistoryEntry is null, and is not otherwise useful.
619
649
Set-StrictMode -Off
620
- $FakeCode = [int]!$global:?
621
650
$LastHistoryEntry = Get-History -Count 1
622
651
# Skip finishing the command if the first command has not yet started
623
652
if ($Global:__LastHistoryId -ne -1) {
624
653
if ($LastHistoryEntry.Id -eq $Global:__LastHistoryId) {
625
654
# Don't provide a command line or exit code if there was no history entry (eg. ctrl+c, enter on no command)
626
- $Result = ""$([char]0x1b)]633;E`a""
655
+ $Result = ""$([char]0x1b)]633;E`a""
627
656
$Result += ""$([char]0x1b)]633;D`a""
628
- } else {
657
+ }
658
+ else {
629
659
# Command finished command line
630
- # OSC 633 ; A ; <CommandLine?> ST
631
- $Result = ""$([char]0x1b)]633;E;""
660
+ # OSC 633 ; E ; <CommandLine?> ; <Nonce ?> ST
661
+ $Result = ""$([char]0x1b)]633;E;""
632
662
# Sanitize the command line to ensure it can get transferred to the terminal and can be parsed
633
663
# correctly. This isn't entirely safe but good for most cases, it's important for the Pt parameter
634
664
# to only be composed of _printable_ characters as per the spec.
635
665
if ($LastHistoryEntry.CommandLine) {
636
666
$CommandLine = $LastHistoryEntry.CommandLine
637
- } else {
667
+ }
668
+ else {
638
669
$CommandLine = """"
639
670
}
640
671
$Result += $(__VSCode-Escape-Value $CommandLine)
672
+ $Result += "";$Nonce""
641
673
$Result += ""`a""
642
674
# Command finished exit code
643
675
# OSC 633 ; D [; <ExitCode>] ST
@@ -649,7 +681,7 @@ private Task EnableShellIntegrationAsync(CancellationToken cancellationToken)
649
681
$Result += ""$([char]0x1b)]633;A`a""
650
682
# Current working directory
651
683
# OSC 633 ; <Property>=<Value> ST
652
- $Result += if($pwd.Provider.Name -eq 'FileSystem'){ ""$([char]0x1b)]633;P;Cwd=$(__VSCode-Escape-Value $pwd.ProviderPath)`a""}
684
+ $Result += if ($pwd.Provider.Name -eq 'FileSystem') { ""$([char]0x1b)]633;P;Cwd=$(__VSCode-Escape-Value $pwd.ProviderPath)`a"" }
653
685
# Before running the original prompt, put $? back to what it was:
654
686
if ($FakeCode -ne 0) {
655
687
Write-Error ""failure"" -ea ignore
@@ -664,28 +696,91 @@ private Task EnableShellIntegrationAsync(CancellationToken cancellationToken)
664
696
665
697
# Set IsWindows property
666
698
if ($PSVersionTable.PSVersion -lt ""6.0"") {
667
- [Console]::Write(""$([char]0x1b)]633;P;IsWindows=$true`a"")
668
- } else {
669
- [Console]::Write(""$([char]0x1b)]633;P;IsWindows=$IsWindows`a"")
699
+ # Windows PowerShell is only available on Windows
700
+ Write-Host -NoNewLine ""$([char]0x1b)]633;P;IsWindows=$true`a""
701
+ }
702
+ else {
703
+ Write-Host -NoNewLine ""$([char]0x1b)]633;P;IsWindows=$IsWindows`a""
670
704
}
671
705
672
706
# Set always on key handlers which map to default VS Code keybindings
673
707
function Set-MappedKeyHandler {
674
708
param ([string[]] $Chord, [string[]]$Sequence)
675
- $Handler = $(Get-PSReadLineKeyHandler -Chord $Chord | Select-Object -First 1)
709
+ try {
710
+ $Handler = Get-PSReadLineKeyHandler -Chord $Chord | Select-Object -First 1
711
+ }
712
+ catch [System.Management.Automation.ParameterBindingException] {
713
+ # PowerShell 5.1 ships with PSReadLine 2.0.0 which does not have -Chord,
714
+ # so we check what's bound and filter it.
715
+ $Handler = Get-PSReadLineKeyHandler -Bound | Where-Object -FilterScript { $_.Key -eq $Chord } | Select-Object -First 1
716
+ }
676
717
if ($Handler) {
677
718
Set-PSReadLineKeyHandler -Chord $Sequence -Function $Handler.Function
678
719
}
679
720
}
680
721
722
+ $Global:__VSCodeHaltCompletions = $false
681
723
function Set-MappedKeyHandlers {
682
724
Set-MappedKeyHandler -Chord Ctrl+Spacebar -Sequence 'F12,a'
683
725
Set-MappedKeyHandler -Chord Alt+Spacebar -Sequence 'F12,b'
684
726
Set-MappedKeyHandler -Chord Shift+Enter -Sequence 'F12,c'
685
727
Set-MappedKeyHandler -Chord Shift+End -Sequence 'F12,d'
728
+
729
+ # Conditionally enable suggestions
730
+ if ($env:VSCODE_SUGGEST -eq '1') {
731
+ Remove-Item Env:VSCODE_SUGGEST
732
+
733
+ # VS Code send completions request (may override Ctrl+Spacebar)
734
+ Set-PSReadLineKeyHandler -Chord 'F12,e' -ScriptBlock {
735
+ Send-Completions
736
+ }
737
+
738
+ # Suggest trigger characters
739
+ Set-PSReadLineKeyHandler -Chord ""-"" -ScriptBlock {
740
+ [Microsoft.PowerShell.PSConsoleReadLine]::Insert(""-"")
741
+ if (!$Global:__VSCodeHaltCompletions) {
742
+ Send-Completions
743
+ }
744
+ }
745
+
746
+ Set-PSReadLineKeyHandler -Chord 'F12,y' -ScriptBlock {
747
+ $Global:__VSCodeHaltCompletions = $true
748
+ }
749
+
750
+ Set-PSReadLineKeyHandler -Chord 'F12,z' -ScriptBlock {
751
+ $Global:__VSCodeHaltCompletions = $false
752
+ }
753
+ }
754
+ }
755
+
756
+ function Send-Completions {
757
+ $commandLine = """"
758
+ $cursorIndex = 0
759
+ # TODO: Since fuzzy matching exists, should completions be provided only for character after the
760
+ # last space and then filter on the client side? That would let you trigger ctrl+space
761
+ # anywhere on a word and have full completions available
762
+ [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$commandLine, [ref]$cursorIndex)
763
+ $completionPrefix = $commandLine
764
+
765
+ # Get completions
766
+ $result = ""`e]633;Completions""
767
+ if ($completionPrefix.Length -gt 0) {
768
+ # Get and send completions
769
+ $completions = TabExpansion2 -inputScript $completionPrefix -cursorColumn $cursorIndex
770
+ if ($null -ne $completions.CompletionMatches) {
771
+ $result += "";$($completions.ReplacementIndex);$($completions.ReplacementLength);$($cursorIndex);""
772
+ $result += $completions.CompletionMatches | ConvertTo-Json -Compress
773
+ }
774
+ }
775
+ $result += ""`a""
776
+
777
+ Write-Host -NoNewLine $result
686
778
}
687
779
688
- Set-MappedKeyHandlers
780
+ # Register key handlers if PSReadLine is available
781
+ if (Get-Module -Name PSReadLine) {
782
+ Set-MappedKeyHandlers
783
+ }
689
784
" ;
690
785
691
786
return ExecutePSCommandAsync ( new PSCommand ( ) . AddScript ( shellIntegrationScript ) , cancellationToken ) ;
0 commit comments