From b9c500103e4bece5ee9b39b787cbb91df29b5c67 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Jun 2026 16:47:38 +0000 Subject: [PATCH 1/2] Initial plan From 64d5caf1e15445e402e8634e43644d936961d653 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Jun 2026 17:06:58 +0000 Subject: [PATCH 2/2] Update .NET SDK to 10.0 and PowerShell to 7.6.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Retarget the Hosting and Test projects from `net8.0` to `net10.0` (the new LTS), pin `global.json` to the 10.0.301 SDK, and move `Microsoft.PowerShell.SDK` from 7.4.14 to 7.6.3 — the PowerShell 7.6 LTS line that aligns with .NET 10, mirroring how 7.4 aligned with .NET 8. The .NET 10 SDK ships newer Roslyn analyzers that we enforce as build errors (`EnforceCodeStyleInBuild`), so a few pre-existing constructs needed cleanup to stay green: - `VersionUtils.IsPS74` matched *exactly* 7.4, but the variable-completion detail it gates was added in 7.4 and persists in 7.6. It now reads `IsPS74OrGreater` (>= 7.4); without it the `CompletesVariableInFile` test fails on 7.6. - Removed the unused `_workspaceService` field and constructor parameter from `ReferencesCodeLensProvider` — dead since we removed the notion of referenced files (IDE0052). - Adopted the C# 14 `field` keyword in `SynchronousTask.Result` and `ReferenceTable.IsInitialized` (IDE0032), null-conditional assignment in `PowerShellDebugContext` and `RemoteFileManagerService` (IDE0031), dropped redundant interface accessibility modifiers (IDE0040), and simplified a `TextEdit` reference. `LangVersion` is `latest`, which is now C# 14. I built core (`netstandard2.0`), Hosting, Test, and E2E (all `net10.0`) clean and confirmed the previously-failing completion test passes on 7.6.3. Drafted by Copilot (Claude Opus 4.8). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/copilot-instructions.md | 18 +++++++++--------- Directory.Packages.props | 2 +- PowerShellEditorServices.build.ps1 | 4 ++-- global.json | 2 +- .../PowerShellEditorServices.Hosting.csproj | 2 +- .../CodeLens/ReferencesCodeLensProvider.cs | 5 +---- .../Debugging/IPowerShellDebugContext.cs | 8 ++++---- .../Debugging/PowerShellDebugContext.cs | 5 +---- .../PowerShell/Execution/SynchronousTask.cs | 9 ++++----- .../Services/Symbols/ReferenceTable.cs | 10 ++++------ .../Services/Symbols/SymbolsService.cs | 4 ++-- .../Handlers/FormattingHandlers.cs | 4 ++-- .../Workspace/RemoteFileManagerService.cs | 5 +---- .../Utility/VersionUtils.cs | 4 ++-- .../PowerShellEditorServices.Test.E2E.csproj | 2 +- .../Completion/CompleteVariableInFile.cs | 6 +++--- .../PowerShellEditorServices.Test.csproj | 8 ++++---- 17 files changed, 43 insertions(+), 55 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 54626eb10..2eb49371a 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -2,7 +2,7 @@ ## Build & Test -Requires .NET SDK 8.0+. Use `dotnet` directly for building and testing — it's faster and +Requires .NET SDK 10.0+. Use `dotnet` directly for building and testing — it's faster and requires no extra tooling. The `Invoke-Build` script requires the `InvokeBuild` and `platyPS` PowerShell modules (platyPS is `#Requires`'d at the top, so the whole script fails without it), and is mainly needed to assemble the full PowerShell module for release. @@ -10,19 +10,19 @@ and is mainly needed to assemble the full PowerShell module for release. ```powershell # Build (run both; Hosting depends on the core library) dotnet publish src/PowerShellEditorServices/PowerShellEditorServices.csproj -f netstandard2.0 -dotnet publish src/PowerShellEditorServices.Hosting/PowerShellEditorServices.Hosting.csproj -f net8.0 +dotnet publish src/PowerShellEditorServices.Hosting/PowerShellEditorServices.Hosting.csproj -f net10.0 # Run all unit tests -dotnet test test/PowerShellEditorServices.Test/ --framework net8.0 +dotnet test test/PowerShellEditorServices.Test/ --framework net10.0 # Run a single test by name -dotnet test test/PowerShellEditorServices.Test/ --framework net8.0 --filter "FullyQualifiedName~CompletesCommandInFile" +dotnet test test/PowerShellEditorServices.Test/ --framework net10.0 --filter "FullyQualifiedName~CompletesCommandInFile" # Run tests by trait category -dotnet test test/PowerShellEditorServices.Test/ --framework net8.0 --filter "Category=Completions" +dotnet test test/PowerShellEditorServices.Test/ --framework net10.0 --filter "Category=Completions" # Run E2E tests -dotnet test test/PowerShellEditorServices.Test.E2E/ --framework net8.0 +dotnet test test/PowerShellEditorServices.Test.E2E/ --framework net10.0 ``` For assembling the full module or running the complete CI suite (including Windows PowerShell @@ -48,11 +48,11 @@ Protocol (DAP)** server for PowerShell, consumed by VS Code and other editors. - **`src/PowerShellEditorServices`** (`netstandard2.0`) — Core library containing all LSP/DAP handlers, services, and the PowerShell execution engine. Namespace: `Microsoft.PowerShell.EditorServices`. -- **`src/PowerShellEditorServices.Hosting`** (`net8.0`, `net462`) — Entry point layer that loads +- **`src/PowerShellEditorServices.Hosting`** (`net10.0`, `net462`) — Entry point layer that loads PSES into a PowerShell process via `StartEditorServicesCommand`. Uses a custom `AssemblyLoadContext` (`PsesLoadContext`) on .NET Core to isolate dependencies. - **`module/PowerShellEditorServices/`** — The shipped PowerShell module. The build assembles - compiled binaries into `bin/Core/` (net8.0) and `bin/Desktop/` (net462). The module manifest + compiled binaries into `bin/Core/` (net10.0) and `bin/Desktop/` (net462). The module manifest loads the appropriate DLL based on PowerShell edition. ### Key Services (registered in `PsesServiceCollectionExtensions`) @@ -136,7 +136,7 @@ Because of that, treat any change to an existing `public` member as potentially ### Multi-targeting The core library targets `netstandard2.0` for compatibility with both .NET Core and .NET -Framework. The hosting project and tests dual-target `net8.0` and `net462` (Windows PowerShell +Framework. The hosting project and tests dual-target `net10.0` and `net462` (Windows PowerShell 5.1). Non-Windows platforms skip `net462` targets. ## Pull Request Labels diff --git a/Directory.Packages.props b/Directory.Packages.props index c76c2a7e3..79213e9c3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,7 +6,7 @@ - + diff --git a/PowerShellEditorServices.build.ps1 b/PowerShellEditorServices.build.ps1 index 62cb53369..bdfaf9d58 100644 --- a/PowerShellEditorServices.build.ps1 +++ b/PowerShellEditorServices.build.ps1 @@ -40,7 +40,7 @@ $script:BuildInfoPath = "src/PowerShellEditorServices.Hosting/BuildInfo.cs" $script:NetFramework = @{ PS51 = 'net462' - PS74 = 'net8.0' + PS74 = 'net10.0' Standard = 'netstandard2.0' } @@ -58,7 +58,7 @@ Task FindDotNet { # Strip out semantic version metadata so it can be cast to `Version` [Version]$existingVersion, $null = (dotnet --version) -split " " -split "-" - Assert ($existingVersion -ge [Version]("8.0")) ".NET SDK 8.0 or higher is required, please update it: https://aka.ms/dotnet-cli" + Assert ($existingVersion -ge [Version]("10.0")) ".NET SDK 10.0 or higher is required, please update it: https://aka.ms/dotnet-cli" Write-Build DarkGreen "Using dotnet v$(dotnet --version) at path $((Get-Command dotnet).Source)" } diff --git a/global.json b/global.json index 910363ade..7c8e277cf 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.416", + "version": "10.0.301", "rollForward": "latestFeature", "allowPrerelease": false } diff --git a/src/PowerShellEditorServices.Hosting/PowerShellEditorServices.Hosting.csproj b/src/PowerShellEditorServices.Hosting/PowerShellEditorServices.Hosting.csproj index a41911e14..bf6862c7e 100644 --- a/src/PowerShellEditorServices.Hosting/PowerShellEditorServices.Hosting.csproj +++ b/src/PowerShellEditorServices.Hosting/PowerShellEditorServices.Hosting.csproj @@ -3,7 +3,7 @@ Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), PowerShellEditorServices.Common.props))\PowerShellEditorServices.Common.props" /> - net8.0;net462 + net10.0;net462 Microsoft.PowerShell.EditorServices.Hosting diff --git a/src/PowerShellEditorServices/Services/CodeLens/ReferencesCodeLensProvider.cs b/src/PowerShellEditorServices/Services/CodeLens/ReferencesCodeLensProvider.cs index 0307163cc..5696b4736 100644 --- a/src/PowerShellEditorServices/Services/CodeLens/ReferencesCodeLensProvider.cs +++ b/src/PowerShellEditorServices/Services/CodeLens/ReferencesCodeLensProvider.cs @@ -25,7 +25,6 @@ internal class ReferencesCodeLensProvider : ICodeLensProvider /// private readonly IDocumentSymbolProvider _symbolProvider; private readonly SymbolsService _symbolsService; - private readonly WorkspaceService _workspaceService; public static string Id => nameof(ReferencesCodeLensProvider); @@ -38,11 +37,9 @@ internal class ReferencesCodeLensProvider : ICodeLensProvider /// /// Construct a new ReferencesCodeLensProvider for a given EditorSession. /// - /// /// - public ReferencesCodeLensProvider(WorkspaceService workspaceService, SymbolsService symbolsService) + public ReferencesCodeLensProvider(SymbolsService symbolsService) { - _workspaceService = workspaceService; _symbolsService = symbolsService; // TODO: Pull this from components _symbolProvider = new ScriptDocumentSymbolProvider(); diff --git a/src/PowerShellEditorServices/Services/PowerShell/Debugging/IPowerShellDebugContext.cs b/src/PowerShellEditorServices/Services/PowerShell/Debugging/IPowerShellDebugContext.cs index 506109b7d..9c3de43d6 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Debugging/IPowerShellDebugContext.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Debugging/IPowerShellDebugContext.cs @@ -13,13 +13,13 @@ internal interface IPowerShellDebugContext DebuggerStopEventArgs LastStopEventArgs { get; } - public bool IsDebuggingRemoteRunspace { get; set; } + bool IsDebuggingRemoteRunspace { get; set; } - public event Action DebuggerStopped; + event Action DebuggerStopped; - public event Action DebuggerResuming; + event Action DebuggerResuming; - public event Action BreakpointUpdated; + event Action BreakpointUpdated; void Continue(); diff --git a/src/PowerShellEditorServices/Services/PowerShell/Debugging/PowerShellDebugContext.cs b/src/PowerShellEditorServices/Services/PowerShell/Debugging/PowerShellDebugContext.cs index f88aa38d5..cb548fa6f 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Debugging/PowerShellDebugContext.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Debugging/PowerShellDebugContext.cs @@ -114,10 +114,7 @@ public void SetDebugResuming(DebuggerResumeAction debuggerResumeAction) // then this came over LSP and we need to set it. _psesHost.SetExit(); - if (LastStopEventArgs is not null) - { - LastStopEventArgs.ResumeAction = debuggerResumeAction; - } + LastStopEventArgs?.ResumeAction = debuggerResumeAction; // We need to tell whatever is happening right now in the debug prompt to wrap up so we // can continue. However, if the host was initialized with the console REPL disabled, diff --git a/src/PowerShellEditorServices/Services/PowerShell/Execution/SynchronousTask.cs b/src/PowerShellEditorServices/Services/PowerShell/Execution/SynchronousTask.cs index fc7f97cd8..02017f1d7 100644 --- a/src/PowerShellEditorServices/Services/PowerShell/Execution/SynchronousTask.cs +++ b/src/PowerShellEditorServices/Services/PowerShell/Execution/SynchronousTask.cs @@ -25,9 +25,6 @@ internal abstract class SynchronousTask : ISynchronousTask private readonly CancellationToken _taskRequesterCancellationToken; private bool _executionCanceled; - - private TResult _result; - private ExceptionDispatchInfo _exceptionInfo; protected SynchronousTask( @@ -57,8 +54,10 @@ public TResult Result _exceptionInfo?.Throw(); - return _result; + return field; } + + private set; } public bool IsCanceled => _executionCanceled || _taskRequesterCancellationToken.IsCancellationRequested; @@ -123,7 +122,7 @@ private void SetException(Exception e) private void SetResult(TResult result) { - _result = result; + Result = result; _taskCompletionSource.SetResult(result); } } diff --git a/src/PowerShellEditorServices/Services/Symbols/ReferenceTable.cs b/src/PowerShellEditorServices/Services/Symbols/ReferenceTable.cs index 6cb8e52c4..b0dc8f7e7 100644 --- a/src/PowerShellEditorServices/Services/Symbols/ReferenceTable.cs +++ b/src/PowerShellEditorServices/Services/Symbols/ReferenceTable.cs @@ -22,8 +22,6 @@ internal sealed class ReferenceTable private readonly ConcurrentDictionary> _symbolReferences = new(StringComparer.OrdinalIgnoreCase); - private bool _isInited; - public ReferenceTable(ScriptFile parent) => _parent = parent; /// @@ -32,15 +30,15 @@ internal sealed class ReferenceTable public void TagAsChanged() { _symbolReferences.Clear(); - _isInited = false; + IsInitialized = false; } /// - /// Prefer checking if the dictionary has contents to determine if initialized. The field - /// `_isInited` is to guard against re-scanning files with no command references, but will + /// Prefer checking if the dictionary has contents to determine if initialized. The backing + /// field is to guard against re-scanning files with no command references, but will /// generally be less reliable of a check. /// - private bool IsInitialized => !_symbolReferences.IsEmpty || _isInited; + private bool IsInitialized { get => !_symbolReferences.IsEmpty || field; set; } internal IEnumerable TryGetReferences(SymbolReference? symbol) { diff --git a/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs b/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs index 99f8bbbc8..827b4eee5 100644 --- a/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs +++ b/src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs @@ -70,7 +70,7 @@ public SymbolsService( _codeLensProviders = new ConcurrentDictionary(); if (configurationService.CurrentSettings.EnableReferencesCodeLens) { - ReferencesCodeLensProvider referencesProvider = new(_workspaceService, this); + ReferencesCodeLensProvider referencesProvider = new(this); _ = _codeLensProviders.TryAdd(referencesProvider.ProviderId, referencesProvider); } @@ -495,7 +495,7 @@ void CloseUnopenedFiles() return; } - TryRegisterCodeLensProvider(new ReferencesCodeLensProvider(_workspaceService, this)); + TryRegisterCodeLensProvider(new ReferencesCodeLensProvider(this)); return; } diff --git a/src/PowerShellEditorServices/Services/TextDocument/Handlers/FormattingHandlers.cs b/src/PowerShellEditorServices/Services/TextDocument/Handlers/FormattingHandlers.cs index bf5f99d0f..64ccb3156 100644 --- a/src/PowerShellEditorServices/Services/TextDocument/Handlers/FormattingHandlers.cs +++ b/src/PowerShellEditorServices/Services/TextDocument/Handlers/FormattingHandlers.cs @@ -90,7 +90,7 @@ public override async Task Handle(DocumentFormattingParams re return s_emptyTextEditContainer; } - return new TextEditContainer(new OmniSharp.Extensions.LanguageServer.Protocol.Models.TextEdit + return new TextEditContainer(new TextEdit { NewText = formattedScript, Range = editRange @@ -184,7 +184,7 @@ public override async Task Handle(DocumentRangeFormattingPara return s_emptyTextEditContainer; } - return new TextEditContainer(new OmniSharp.Extensions.LanguageServer.Protocol.Models.TextEdit + return new TextEditContainer(new TextEdit { NewText = formattedScript, Range = editRange diff --git a/src/PowerShellEditorServices/Services/Workspace/RemoteFileManagerService.cs b/src/PowerShellEditorServices/Services/Workspace/RemoteFileManagerService.cs index 961338023..5b0ff6065 100644 --- a/src/PowerShellEditorServices/Services/Workspace/RemoteFileManagerService.cs +++ b/src/PowerShellEditorServices/Services/Workspace/RemoteFileManagerService.cs @@ -691,10 +691,7 @@ private void RemovePSEditFunction(IRunspaceInfo runspaceInfo) } try { - if (runspaceInfo.Runspace.Events != null) - { - runspaceInfo.Runspace.Events.ReceivedEvents.PSEventReceived -= HandlePSEventReceivedAsync; - } + runspaceInfo.Runspace.Events?.ReceivedEvents.PSEventReceived -= HandlePSEventReceivedAsync; if (runspaceInfo.Runspace.RunspaceStateInfo.State == RunspaceState.Opened) { diff --git a/src/PowerShellEditorServices/Utility/VersionUtils.cs b/src/PowerShellEditorServices/Utility/VersionUtils.cs index 0b8919294..1726943cf 100644 --- a/src/PowerShellEditorServices/Utility/VersionUtils.cs +++ b/src/PowerShellEditorServices/Utility/VersionUtils.cs @@ -48,9 +48,9 @@ internal static class VersionUtils public static bool IsPS7OrGreater { get; } = PSVersion.Major >= 7; /// - /// True if we are running in PowerShell 7.4, false otherwise. + /// True if we are running in PowerShell 7.4 or greater, false otherwise. /// - public static bool IsPS74 { get; } = PSVersion.Major == 7 && PSVersion.Minor == 4; + public static bool IsPS74OrGreater { get; } = PSVersion >= new Version(7, 4); /// /// True if we are running on Windows, false otherwise. diff --git a/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj b/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj index 82c0899f5..a5f9dcd90 100644 --- a/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj +++ b/test/PowerShellEditorServices.Test.E2E/PowerShellEditorServices.Test.E2E.csproj @@ -3,7 +3,7 @@ Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), PowerShellEditorServices.Common.props))\PowerShellEditorServices.Common.props" /> - net8.0 + net10.0 false diff --git a/test/PowerShellEditorServices.Test.Shared/Completion/CompleteVariableInFile.cs b/test/PowerShellEditorServices.Test.Shared/Completion/CompleteVariableInFile.cs index 3dbfb80c9..21742733c 100644 --- a/test/PowerShellEditorServices.Test.Shared/Completion/CompleteVariableInFile.cs +++ b/test/PowerShellEditorServices.Test.Shared/Completion/CompleteVariableInFile.cs @@ -21,9 +21,9 @@ internal static class CompleteVariableInFile public static readonly CompletionItem ExpectedCompletion = new() { Kind = CompletionItemKind.Variable, - // PowerShell 7.4 now lights up a type for the detail, otherwise it's the same as the - // label and therefore hidden. - Detail = Utility.VersionUtils.IsPS74 ? "[string]" : "", + // PowerShell 7.4 and greater light up a type for the detail, otherwise it's the same as + // the label and therefore hidden. + Detail = Utility.VersionUtils.IsPS74OrGreater ? "[string]" : "", FilterText = "$testVar1", InsertText = "$testVar1", Label = "testVar1", diff --git a/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj b/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj index 0566b8cf2..7a1fdd941 100644 --- a/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj +++ b/test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj @@ -2,7 +2,7 @@ - net8.0;net462 + net10.0;net462 Microsoft.PowerShell.EditorServices.Test x64 @@ -17,12 +17,12 @@ - + - - + +