From fed92a836df56ab8a235fae5a08655008a140360 Mon Sep 17 00:00:00 2001
From: Willem-J-an <51120533+Willem-J-an@users.noreply.github.com>
Date: Wed, 27 Mar 2024 18:59:44 +0100
Subject: [PATCH 1/5] Update clients that support debugging (#2151)

---
 README.md | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index bb672b7f9..614f81096 100644
--- a/README.md
+++ b/README.md
@@ -126,7 +126,10 @@ If you want to take advantage of debugging,
 your client must support the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/).
 Your client should use the path to the debug named pipe found in the `session.json` file talked about above.
 
-Currently, only the Visual Studio Code extension supports debugging.
+The debugging functionality in PowerShell Editor Services is available in the following editor extensions:
+
+- [PowerShell for Visual Studio Code](https://github.com/PowerShell/vscode-powershell)
+- [nvim-dap-powershell for Neovim](https://github.com/Willem-J-an/nvim-dap-powershell)
 
 ## API Usage
 

From db3d83329045875120f17986a385caa439c33615 Mon Sep 17 00:00:00 2001
From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>
Date: Wed, 27 Mar 2024 11:55:03 -0700
Subject: [PATCH 2/5] Fix Dependabot

---
 .github/dependabot.yml | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index e3a2fb57f..d4c49ec2d 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -1,7 +1,15 @@
 version: 2
+registries:
+  nuget-azure:
+    type: nuget-feed
+    url: https://pkgs.dev.azure.com/mscodehub/PowerShellCore/_packaging/PowerShellCore_PublicPackages/nuget/v3/index.json
+    username: powershell
+    password: ${{ secrets.AZURE_NUGET_TOKEN }}
 updates:
 - package-ecosystem: nuget
   directory: "/"
+  registries:
+    - nuget-azure
   labels: [ ]
   schedule:
     interval: weekly

From 20f90a9d28bb181acc3137e4eeba8625399f4500 Mon Sep 17 00:00:00 2001
From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>
Date: Mon, 1 Apr 2024 15:39:16 -0700
Subject: [PATCH 3/5] Support 'preview' in version and always publish (a draft)

Removed the manual validation step because it is unnecesary (and annoying)
to gate this pipeline since it opens an unpublished, draft release.
The downstream triggered pipeline will gate the release to the VS Code
marketplace.
---
 .github/release.yml                           |  2 +-
 .../PowerShellEditorServices-Official.yml     | 50 +++++++------------
 tools/updateVersion.ps1                       | 26 +++++-----
 3 files changed, 32 insertions(+), 46 deletions(-)

diff --git a/.github/release.yml b/.github/release.yml
index bb7a942a4..cc5a1aa80 100644
--- a/.github/release.yml
+++ b/.github/release.yml
@@ -3,7 +3,7 @@ changelog:
     labels:
       - Ignore
     authors:
-      - dependabot[bot]
+      - dependabot
   categories:
     - title: Enhancements & Features ✨
       labels:
diff --git a/.pipelines/PowerShellEditorServices-Official.yml b/.pipelines/PowerShellEditorServices-Official.yml
index f1d47c08c..e22560e72 100644
--- a/.pipelines/PowerShellEditorServices-Official.yml
+++ b/.pipelines/PowerShellEditorServices-Official.yml
@@ -52,7 +52,10 @@ extends:
           - pwsh: |
               [xml]$xml = Get-Content PowerShellEditorServices.Common.props
               $version = $xml.Project.PropertyGroup.VersionPrefix
+              $prerelease = $xml.Project.PropertyGroup.VersionSuffix
+              if ($prerelease) { $version += "-$prerelease" }
               Write-Output "##vso[task.setvariable variable=version;isOutput=true]$version"
+              Write-Output "##vso[task.setvariable variable=prerelease;isOutput=true]$(-not [string]::IsNullOrEmpty($prerelease))"
             name: package
             displayName: Get version from project properties
           - task: onebranch.pipeline.version@1
@@ -61,17 +64,17 @@ extends:
               system: Custom
               customVersion: $(package.version)
           - task: UseDotNet@2
-            displayName: Install .NET 8.x SDK
+            displayName: Use .NET 8.x SDK
             inputs:
               packageType: sdk
               version: 8.x
           - task: UseDotNet@2
-            displayName: Install .NET 7.x runtime
+            displayName: Use .NET 7.x runtime (for tests)
             inputs:
               packageType: runtime
               version: 7.x
           - task: UseDotNet@2
-            displayName: Install .NET 6.x runtime
+            displayName: Use .NET 6.x runtime (for tests)
             inputs:
               packageType: runtime
               version: 6.x
@@ -80,29 +83,22 @@ extends:
             inputs:
               pwsh: true
               filePath: tools/installPSResources.ps1
-          - task: PowerShell@2
+          - pwsh: Invoke-Build TestFull -Configuration $(BuildConfiguration)
             displayName: Build and test
-            inputs:
-              targetType: inline
-              pwsh: true
-              script: Invoke-Build TestFull -Configuration $(BuildConfiguration)
           - task: PublishTestResults@2
             displayName: Publish test results
             inputs:
               testRunner: VSTest
               testResultsFiles: '**/*.trx'
               failTaskOnFailedTests: true
-          - task: PowerShell@2
+          - pwsh: |
+              $assembly = [Reflection.Assembly]::LoadFile("$(Build.SourcesDirectory)/module/PowerShellEditorServices/bin/Core/Microsoft.PowerShell.EditorServices.Hosting.dll")
+              if ($assembly.GetCustomAttributes([System.Diagnostics.DebuggableAttribute], $true).IsJITOptimizerDisabled) {
+                Write-Host "##vso[task.LogIssue type=error;]Was not built in release configuration!"
+                exit 1
+              }
             displayName: Assert release configuration
-            inputs:
-              targetType: inline
-              pwsh: true
-              script: |
-                $assembly = [Reflection.Assembly]::LoadFile("$(Build.SourcesDirectory)/module/PowerShellEditorServices/bin/Core/Microsoft.PowerShell.EditorServices.Hosting.dll")
-                if ($assembly.GetCustomAttributes([System.Diagnostics.DebuggableAttribute], $true).IsJITOptimizerDisabled) {
-                  Write-Host "##vso[task.LogIssue type=error;]Was not built in release configuration!"
-                  exit 1
-                }
+            continueOnError: true
           - task: onebranch.pipeline.signing@1
             displayName: Sign 1st-party files
             inputs:
@@ -136,22 +132,10 @@ extends:
       dependsOn: build
       variables:
         version: $[ stageDependencies.build.main.outputs['package.version'] ]
+        prerelease: $[ stageDependencies.build.main.outputs['package.prerelease'] ]
         drop: $(Pipeline.Workspace)/drop_build_main
       jobs:
-      - job: validation
-        displayName: Manual validation
-        pool:
-          type: agentless
-        timeoutInMinutes: 1440
-        steps:
-        - task: ManualValidation@0
-          displayName: Wait 24 hours for validation
-          inputs:
-            notifyUsers: $(Build.RequestedForEmail)
-            instructions: Please validate the release
-            timeoutInMinutes: 1440
       - job: github
-        dependsOn: validation
         displayName: Publish draft to GitHub
         pool:
           type: windows
@@ -176,7 +160,7 @@ extends:
             tagSource: userSpecifiedTag
             tag: v$(version)
             isDraft: true
+            isPreRelease: $(prerelease)
             addChangeLog: false
             releaseNotesSource: inline
-            releaseNotesInline: |
-              # TODO: Generate release notes on GitHub!
+            releaseNotesInline: "<!-- TODO: Generate release notes on GitHub! -->"
diff --git a/tools/updateVersion.ps1 b/tools/updateVersion.ps1
index e5b94280c..85e75dfcb 100644
--- a/tools/updateVersion.ps1
+++ b/tools/updateVersion.ps1
@@ -16,30 +16,32 @@ if ($LASTEXITCODE -ne 0) {
 
 $v = "$($Version.Major).$($Version.Minor).$($Version.Patch)"
 
-$path = "PowerShellEditorServices.Common.props"
-$f = Get-Content -Path $path
+$Path = "PowerShellEditorServices.Common.props"
+$f = Get-Content -Path $Path
 $f = $f -replace '^(?<prefix>\s+<VersionPrefix>)(.+)(?<suffix></VersionPrefix>)$', "`${prefix}${v}`${suffix}"
 $f = $f -replace '^(?<prefix>\s+<VersionSuffix>)(.*)(?<suffix></VersionSuffix>)$', "`${prefix}$($Version.PreReleaseLabel)`${suffix}"
-$f | Set-Content -Path $path
-git add $path
+$f | Set-Content -Path $Path
+git add $Path
 
-$path = "module/PowerShellEditorServices/PowerShellEditorServices.psd1"
-$f = Get-Content -Path $path
+$Path = "module/PowerShellEditorServices/PowerShellEditorServices.psd1"
+$f = Get-Content -Path $Path
 $f = $f -replace "^(?<prefix>ModuleVersion = ')(.+)(?<suffix>')`$", "`${prefix}${v}`${suffix}"
-$f | Set-Content -Path $path
-git add $path
+$f | Set-Content -Path $Path
+git add $Path
 
-$path = "CHANGELOG.md"
-$Changelog = Get-Content -Path $path
+$Path = "CHANGELOG.md"
+$Changelog = Get-Content -Path $Path
 @(
     $Changelog[0..1]
     "## v$Version"
     "### $([datetime]::Now.ToString('dddd, MMMM dd, yyyy'))"
     ""
+    "See more details at the GitHub Release for [v$Version](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v$Version)."
+    ""
     $Changes
     ""
     $Changelog[2..$Changelog.Length]
-) | Set-Content -Encoding utf8NoBOM -Path $path
-git add $path
+) | Set-Content -Encoding utf8NoBOM -Path $Path
+git add $Path
 
 git commit --edit --message "v$($Version): $Changes"

From 5b30599e52623cf401a9aa21f54bf7ab70fd96e1 Mon Sep 17 00:00:00 2001
From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>
Date: Tue, 2 Apr 2024 13:09:26 -0700
Subject: [PATCH 4/5] Use a provided shell integration script directly

Instead of having to maintain an edited copy (which was really annoying)
I stubbed out `PSConsoleHostReadLine` to do what's expected.
So now we can just use the existing shell integration script directly!

Since we can't reliably find the script using
`code --locate-shell-integration-path pwsh` we now rely on it being sent
by the client on initialization. Its presence implies the feature is on.

This is pretty VS Code specific, but not necessarily so.

Apply suggestions from code review

Thanks Patrick!

Co-authored-by: Patrick Meinecke <SeeminglyScience@users.noreply.github.com>
---
 .../Server/PsesLanguageServer.cs              |   5 +-
 .../PowerShell/Host/HostStartOptions.cs       |   2 +-
 .../PowerShell/Host/PsesInternalHost.cs       | 256 +++---------------
 3 files changed, 49 insertions(+), 214 deletions(-)

diff --git a/src/PowerShellEditorServices/Server/PsesLanguageServer.cs b/src/PowerShellEditorServices/Server/PsesLanguageServer.cs
index 74319e828..c2e25f64b 100644
--- a/src/PowerShellEditorServices/Server/PsesLanguageServer.cs
+++ b/src/PowerShellEditorServices/Server/PsesLanguageServer.cs
@@ -153,8 +153,9 @@ public async Task StartAsync()
                                 InitialWorkingDirectory = initializationOptions?.GetValue("initialWorkingDirectory")?.Value<string>()
                                     ?? workspaceService.WorkspaceFolders.FirstOrDefault()?.Uri.GetFileSystemPath()
                                     ?? Directory.GetCurrentDirectory(),
-                                ShellIntegrationEnabled = initializationOptions?.GetValue("shellIntegrationEnabled")?.Value<bool>()
-                                    ?? false
+                                // If a shell integration script path is provided, that implies the feature is enabled.
+                                ShellIntegrationScript = initializationOptions?.GetValue("shellIntegrationScript")?.Value<string>()
+                                    ?? "",
                             };
 
                             workspaceService.InitialWorkingDirectory = hostStartOptions.InitialWorkingDirectory;
diff --git a/src/PowerShellEditorServices/Services/PowerShell/Host/HostStartOptions.cs b/src/PowerShellEditorServices/Services/PowerShell/Host/HostStartOptions.cs
index f483b76b4..7de16fddf 100644
--- a/src/PowerShellEditorServices/Services/PowerShell/Host/HostStartOptions.cs
+++ b/src/PowerShellEditorServices/Services/PowerShell/Host/HostStartOptions.cs
@@ -9,6 +9,6 @@ internal struct HostStartOptions
 
         public string InitialWorkingDirectory { get; set; }
 
-        public bool ShellIntegrationEnabled { get; set; }
+        public string ShellIntegrationScript { get; set; }
 }
 }
diff --git a/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs b/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs
index c921de81b..6c77daa82 100644
--- a/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs
+++ b/src/PowerShellEditorServices/Services/PowerShell/Host/PsesInternalHost.cs
@@ -315,16 +315,28 @@ public async Task<bool> TryStartAsync(HostStartOptions startOptions, Cancellatio
                 _logger.LogDebug("Profiles loaded!");
             }
 
-            if (startOptions.ShellIntegrationEnabled)
+            if (!string.IsNullOrEmpty(startOptions.ShellIntegrationScript))
             {
-                _logger.LogDebug("Enabling shell integration...");
+                _logger.LogDebug("Enabling Terminal Shell Integration...");
                 _shellIntegrationEnabled = true;
-                await EnableShellIntegrationAsync(cancellationToken).ConfigureAwait(false);
+                // TODO: Make the __psEditorServices prefix shared (it's used elsewhere too).
+                string setupShellIntegration = $$"""
+                    # Setup Terminal Shell Integration.
+
+                    # Define a fake PSConsoleHostReadLine so the integration script's wrapper
+                    # can execute it to get the user's input.
+                    $global:__psEditorServices_userInput = "";
+                    function global:PSConsoleHostReadLine { $global:__psEditorServices_userInput }
+
+                    # Execute the provided shell integration script.
+                    try { . '{{startOptions.ShellIntegrationScript}}' } catch {}
+                    """;
+                await EnableShellIntegrationAsync(setupShellIntegration, cancellationToken).ConfigureAwait(false);
                 _logger.LogDebug("Shell integration enabled!");
             }
             else
             {
-                _logger.LogDebug("Shell integration not enabled!");
+                _logger.LogDebug("Terminal Shell Integration not enabled!");
             }
 
             await _started.Task.ConfigureAwait(false);
@@ -495,6 +507,7 @@ public Task ExecuteDelegateAsync(
                 new SynchronousDelegateTask(_logger, representation, executionOptions, action, cancellationToken));
         }
 
+        // TODO: One day fix these so the cancellation token is last.
         public Task<IReadOnlyList<TResult>> ExecutePSCommandAsync<TResult>(
             PSCommand psCommand,
             CancellationToken cancellationToken,
@@ -581,209 +594,12 @@ internal Task LoadHostProfilesAsync(CancellationToken cancellationToken)
                 cancellationToken);
         }
 
-        private Task EnableShellIntegrationAsync(CancellationToken cancellationToken)
+        private Task EnableShellIntegrationAsync(string shellIntegrationScript, CancellationToken cancellationToken)
         {
-            // Imported on 01/03/24 from
-            // https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1
-            // with quotes escaped, `__VSCodeOriginalPSConsoleHostReadLine` removed (as it's done
-            // in our own ReadLine function), and `[Console]::Write` replaced with `Write-Host`.
-            const string shellIntegrationScript = @"
-# Prevent installing more than once per session
-if (Test-Path variable:global:__VSCodeOriginalPrompt) {
-	return;
-}
-
-# Disable shell integration when the language mode is restricted
-if ($ExecutionContext.SessionState.LanguageMode -ne ""FullLanguage"") {
-	return;
-}
-
-$Global:__VSCodeOriginalPrompt = $function:Prompt
-
-$Global:__LastHistoryId = -1
-
-# Store the nonce in script scope and unset the global
-$Nonce = $env:VSCODE_NONCE
-$env:VSCODE_NONCE = $null
-
-if ($env:VSCODE_ENV_REPLACE) {
-	$Split = $env:VSCODE_ENV_REPLACE.Split("":"")
-	foreach ($Item in $Split) {
-		$Inner = $Item.Split('=')
-		[Environment]::SetEnvironmentVariable($Inner[0], $Inner[1].Replace('\x3a', ':'))
-	}
-	$env:VSCODE_ENV_REPLACE = $null
-}
-if ($env:VSCODE_ENV_PREPEND) {
-	$Split = $env:VSCODE_ENV_PREPEND.Split("":"")
-	foreach ($Item in $Split) {
-		$Inner = $Item.Split('=')
-		[Environment]::SetEnvironmentVariable($Inner[0], $Inner[1].Replace('\x3a', ':') + [Environment]::GetEnvironmentVariable($Inner[0]))
-	}
-	$env:VSCODE_ENV_PREPEND = $null
-}
-if ($env:VSCODE_ENV_APPEND) {
-	$Split = $env:VSCODE_ENV_APPEND.Split("":"")
-	foreach ($Item in $Split) {
-		$Inner = $Item.Split('=')
-		[Environment]::SetEnvironmentVariable($Inner[0], [Environment]::GetEnvironmentVariable($Inner[0]) + $Inner[1].Replace('\x3a', ':'))
-	}
-	$env:VSCODE_ENV_APPEND = $null
-}
-
-function Global:__VSCode-Escape-Value([string]$value) {
-	# NOTE: In PowerShell v6.1+, this can be written `$value -replace '…', { … }` instead of `[regex]::Replace`.
-	# Replace any non-alphanumeric characters.
-	[regex]::Replace($value, '[\\\n;]', { param($match)
-			# Encode the (ascii) matches as `\x<hex>`
-			-Join (
-				[System.Text.Encoding]::UTF8.GetBytes($match.Value) | ForEach-Object { '\x{0:x2}' -f $_ }
-			)
-		})
-}
-
-function Global:Prompt() {
-	$FakeCode = [int]!$global:?
-	# NOTE: We disable strict mode for the scope of this function because it unhelpfully throws an
-	# error when $LastHistoryEntry is null, and is not otherwise useful.
-	Set-StrictMode -Off
-	$LastHistoryEntry = Get-History -Count 1
-	# Skip finishing the command if the first command has not yet started
-	if ($Global:__LastHistoryId -ne -1) {
-		if ($LastHistoryEntry.Id -eq $Global:__LastHistoryId) {
-			# Don't provide a command line or exit code if there was no history entry (eg. ctrl+c, enter on no command)
-			$Result = ""$([char]0x1b)]633;E`a""
-			$Result += ""$([char]0x1b)]633;D`a""
-		}
-		else {
-			# Command finished command line
-			# OSC 633 ; E ; <CommandLine?> ; <Nonce?> ST
-			$Result = ""$([char]0x1b)]633;E;""
-			# Sanitize the command line to ensure it can get transferred to the terminal and can be parsed
-			# correctly. This isn't entirely safe but good for most cases, it's important for the Pt parameter
-			# to only be composed of _printable_ characters as per the spec.
-			if ($LastHistoryEntry.CommandLine) {
-				$CommandLine = $LastHistoryEntry.CommandLine
-			}
-			else {
-				$CommandLine = """"
-			}
-			$Result += $(__VSCode-Escape-Value $CommandLine)
-			$Result += "";$Nonce""
-			$Result += ""`a""
-			# Command finished exit code
-			# OSC 633 ; D [; <ExitCode>] ST
-			$Result += ""$([char]0x1b)]633;D;$FakeCode`a""
-		}
-	}
-	# Prompt started
-	# OSC 633 ; A ST
-	$Result += ""$([char]0x1b)]633;A`a""
-	# Current working directory
-	# OSC 633 ; <Property>=<Value> ST
-	$Result += if ($pwd.Provider.Name -eq 'FileSystem') { ""$([char]0x1b)]633;P;Cwd=$(__VSCode-Escape-Value $pwd.ProviderPath)`a"" }
-	# Before running the original prompt, put $? back to what it was:
-	if ($FakeCode -ne 0) {
-		Write-Error ""failure"" -ea ignore
-	}
-	# Run the original prompt
-	$Result += $Global:__VSCodeOriginalPrompt.Invoke()
-	# Write command started
-	$Result += ""$([char]0x1b)]633;B`a""
-	$Global:__LastHistoryId = $LastHistoryEntry.Id
-	return $Result
-}
-
-# Set IsWindows property
-if ($PSVersionTable.PSVersion -lt ""6.0"") {
-	# Windows PowerShell is only available on Windows
-	Write-Host -NoNewLine ""$([char]0x1b)]633;P;IsWindows=$true`a""
-}
-else {
-	Write-Host -NoNewLine ""$([char]0x1b)]633;P;IsWindows=$IsWindows`a""
-}
-
-# Set always on key handlers which map to default VS Code keybindings
-function Set-MappedKeyHandler {
-	param ([string[]] $Chord, [string[]]$Sequence)
-	try {
-		$Handler = Get-PSReadLineKeyHandler -Chord $Chord | Select-Object -First 1
-	}
- catch [System.Management.Automation.ParameterBindingException] {
-		# PowerShell 5.1 ships with PSReadLine 2.0.0 which does not have -Chord,
-		# so we check what's bound and filter it.
-		$Handler = Get-PSReadLineKeyHandler -Bound | Where-Object -FilterScript { $_.Key -eq $Chord } | Select-Object -First 1
-	}
-	if ($Handler) {
-		Set-PSReadLineKeyHandler -Chord $Sequence -Function $Handler.Function
-	}
-}
-
-$Global:__VSCodeHaltCompletions = $false
-function Set-MappedKeyHandlers {
-	Set-MappedKeyHandler -Chord Ctrl+Spacebar -Sequence 'F12,a'
-	Set-MappedKeyHandler -Chord Alt+Spacebar -Sequence 'F12,b'
-	Set-MappedKeyHandler -Chord Shift+Enter -Sequence 'F12,c'
-	Set-MappedKeyHandler -Chord Shift+End -Sequence 'F12,d'
-
-	# Conditionally enable suggestions
-	if ($env:VSCODE_SUGGEST -eq '1') {
-		Remove-Item Env:VSCODE_SUGGEST
-
-		# VS Code send completions request (may override Ctrl+Spacebar)
-		Set-PSReadLineKeyHandler -Chord 'F12,e' -ScriptBlock {
-			Send-Completions
-		}
-
-		# Suggest trigger characters
-		Set-PSReadLineKeyHandler -Chord ""-"" -ScriptBlock {
-			[Microsoft.PowerShell.PSConsoleReadLine]::Insert(""-"")
-			if (!$Global:__VSCodeHaltCompletions) {
-				Send-Completions
-			}
-		}
-
-		Set-PSReadLineKeyHandler -Chord 'F12,y' -ScriptBlock {
-			$Global:__VSCodeHaltCompletions = $true
-		}
-
-		Set-PSReadLineKeyHandler -Chord 'F12,z' -ScriptBlock {
-			$Global:__VSCodeHaltCompletions = $false
-		}
-	}
-}
-
-function Send-Completions {
-	$commandLine = """"
-	$cursorIndex = 0
-	# TODO: Since fuzzy matching exists, should completions be provided only for character after the
-	#       last space and then filter on the client side? That would let you trigger ctrl+space
-	#       anywhere on a word and have full completions available
-	[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$commandLine, [ref]$cursorIndex)
-	$completionPrefix = $commandLine
-
-	# Get completions
-	$result = ""`e]633;Completions""
-	if ($completionPrefix.Length -gt 0) {
-		# Get and send completions
-		$completions = TabExpansion2 -inputScript $completionPrefix -cursorColumn $cursorIndex
-		if ($null -ne $completions.CompletionMatches) {
-			$result += "";$($completions.ReplacementIndex);$($completions.ReplacementLength);$($cursorIndex);""
-			$result += $completions.CompletionMatches | ConvertTo-Json -Compress
-		}
-	}
-	$result += ""`a""
-
-	Write-Host -NoNewLine $result
-}
-
-# Register key handlers if PSReadLine is available
-if (Get-Module -Name PSReadLine) {
-	Set-MappedKeyHandlers
-}
-            ";
-
-            return ExecutePSCommandAsync(new PSCommand().AddScript(shellIntegrationScript), cancellationToken);
+            return ExecutePSCommandAsync(
+                new PSCommand().AddScript(shellIntegrationScript),
+                cancellationToken,
+                new PowerShellExecutionOptions { AddToHistory = false, ThrowOnError = false });
         }
 
         public Task SetInitialWorkingDirectoryAsync(string path, CancellationToken cancellationToken)
@@ -1262,16 +1078,34 @@ private void InvokeInput(string input, CancellationToken cancellationToken)
 
             try
             {
-                // For VS Code's shell integration feature, this replaces their
-                // PSConsoleHostReadLine function wrapper, as that global function is not available
-                // to users of PSES, since we already wrap ReadLine ourselves.
+                // For the terminal shell integration feature, we call PSConsoleHostReadLine specially as it's been wrapped.
+                // Normally it would not be available (since we wrap ReadLine ourselves),
+                // but in this case we've made the original just emit the user's input so that the wrapper works as intended.
                 if (_shellIntegrationEnabled)
                 {
-                    System.Console.Write("\x1b]633;C\a");
+                    // Save the user's input to our special global variable so PSConsoleHostReadLine can read it.
+                    InvokePSCommand(
+                        new PSCommand().AddScript("$global:__psEditorServices_userInput = $args[0]").AddArgument(input),
+                        new PowerShellExecutionOptions { ThrowOnError = false, WriteOutputToHost = false },
+                        cancellationToken);
+
+                    // Invoke the PSConsoleHostReadLine wrapper. We don't write the output because it
+                    // returns the command line (user input) which would then be duplicate noise. Fortunately
+                    // it writes the shell integration sequences directly using [Console]::Write.
+                    InvokePSCommand(
+                        new PSCommand().AddScript("PSConsoleHostReadLine"),
+                        new PowerShellExecutionOptions { ThrowOnError = false, WriteOutputToHost = false },
+                        cancellationToken);
+
+                    // Reset our global variable.
+                    InvokePSCommand(
+                        new PSCommand().AddScript("$global:__psEditorServices_userInput = \"\""),
+                        new PowerShellExecutionOptions { ThrowOnError = false, WriteOutputToHost = false },
+                        cancellationToken);
                 }
 
                 InvokePSCommand(
-                    new PSCommand().AddScript(input, useLocalScope: false),
+                    new PSCommand().AddScript(input),
                     new PowerShellExecutionOptions
                     {
                         AddToHistory = true,

From e21c8c1d7e5ac833a9eed05be9b795bc61777399 Mon Sep 17 00:00:00 2001
From: Andy Jordan <2226434+andyleejordan@users.noreply.github.com>
Date: Wed, 3 Apr 2024 13:59:37 -0700
Subject: [PATCH 5/5] v3.19.0: Overhauled Terminal Shell Integration!

---
 CHANGELOG.md                                               | 7 +++++++
 PowerShellEditorServices.Common.props                      | 2 +-
 .../PowerShellEditorServices/PowerShellEditorServices.psd1 | 2 +-
 3 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 10c90056d..bf5c3f4ee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
 # PowerShell Editor Services Release History
 
+## v3.19.0
+### Wednesday, April 03, 2024
+
+See more details at the GitHub Release for [v3.19.0](https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v3.19.0).
+
+Overhauled Terminal Shell Integration!
+
 ## v3.18.1
 ### Tuesday, March 19, 2024
 
diff --git a/PowerShellEditorServices.Common.props b/PowerShellEditorServices.Common.props
index 80d45b7ba..6681ee99a 100644
--- a/PowerShellEditorServices.Common.props
+++ b/PowerShellEditorServices.Common.props
@@ -1,6 +1,6 @@
 <Project>
   <PropertyGroup>
-    <VersionPrefix>3.18.1</VersionPrefix>
+    <VersionPrefix>3.19.0</VersionPrefix>
     <VersionSuffix></VersionSuffix>
     <Company>Microsoft</Company>
     <Copyright>© Microsoft Corporation.</Copyright>
diff --git a/module/PowerShellEditorServices/PowerShellEditorServices.psd1 b/module/PowerShellEditorServices/PowerShellEditorServices.psd1
index ebeba42f9..0dbfcdae0 100644
--- a/module/PowerShellEditorServices/PowerShellEditorServices.psd1
+++ b/module/PowerShellEditorServices/PowerShellEditorServices.psd1
@@ -19,7 +19,7 @@ RootModule = if ($PSEdition -eq 'Core')
     }
 
 # Version number of this module.
-ModuleVersion = '3.18.1'
+ModuleVersion = '3.19.0'
 
 # ID used to uniquely identify this module
 GUID = '9ca15887-53a2-479a-9cda-48d26bcb6c47'
