From decc231c6c03694af626d1823f74f7aeda59d9d6 Mon Sep 17 00:00:00 2001 From: Rishi Benegal Date: Fri, 14 Feb 2025 09:15:26 -0500 Subject: [PATCH 1/2] Set swift.swiftSDK for target platforms SourceKit-LSP now provides code editing support for non-macOS Darwin platforms starting Swift 6.1 using the --swift-sdk flag. Set this flag to the appropriate target triple when using the "Select Target Platform" feature on macOS. Issue: #1335 --- src/commands.ts | 2 +- src/commands/switchPlatform.ts | 53 ++++++++++++++++++++++------------ src/extension.ts | 6 +--- 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/commands.ts b/src/commands.ts index b88e6cecb..57f1bb3b6 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -107,7 +107,7 @@ export function register(ctx: WorkspaceContext): vscode.Disposable[] { } }), // Note: This is only available on macOS (gated in `package.json`) because its the only OS that has the iOS SDK available. - vscode.commands.registerCommand("swift.switchPlatform", () => switchPlatform()), + vscode.commands.registerCommand("swift.switchPlatform", () => switchPlatform(ctx)), vscode.commands.registerCommand(Commands.RESET_PACKAGE, () => resetPackage(ctx)), vscode.commands.registerCommand("swift.runScript", () => runSwiftScript(ctx)), vscode.commands.registerCommand("swift.openPackage", () => { diff --git a/src/commands/switchPlatform.ts b/src/commands/switchPlatform.ts index 821a8ccfb..7c0ce1e9e 100644 --- a/src/commands/switchPlatform.ts +++ b/src/commands/switchPlatform.ts @@ -13,13 +13,19 @@ //===----------------------------------------------------------------------===// import * as vscode from "vscode"; -import { DarwinCompatibleTarget, SwiftToolchain } from "../toolchain/toolchain"; +import { + DarwinCompatibleTarget, + SwiftToolchain, + getDarwinTargetTriple, +} from "../toolchain/toolchain"; import configuration from "../configuration"; +import { Version } from "../utilities/version"; +import { WorkspaceContext } from "../WorkspaceContext"; /** - * Switches the target SDK to the platform selected in a QuickPick UI. + * Switches the appropriate SDK setting to the platform selected in a QuickPick UI. */ -export async function switchPlatform() { +export async function switchPlatform(ctx: WorkspaceContext) { const picked = await vscode.window.showQuickPick( [ { value: undefined, label: "macOS" }, @@ -29,28 +35,39 @@ export async function switchPlatform() { { value: DarwinCompatibleTarget.visionOS, label: "visionOS" }, ], { - placeHolder: "Select a new target", + placeHolder: "Select a new target platform", } ); if (picked) { + if (ctx.toolchain.swiftVersion.isLessThan(new Version(6, 1, 0))) { + vscode.window.showWarningMessage( + "Code editing support for non-macOS platforms is only available starting Swift 6.1" + ); + } + // show a status item as getSDKForTarget can sometimes take a long while to xcrun for the SDK + const statusItemText = `Setting target platform to ${picked.label}`; + ctx.statusItem.start(statusItemText); try { - const sdkForTarget = picked.value - ? await SwiftToolchain.getSDKForTarget(picked.value) - : ""; - if (sdkForTarget !== undefined) { - if (sdkForTarget !== "") { - configuration.sdk = sdkForTarget; - vscode.window.showWarningMessage( - `Selecting the ${picked.label} SDK will provide code editing support, but compiling with this SDK will have undefined results.` - ); - } else { - configuration.sdk = undefined; - } + if (picked.value) { + // verify that the SDK for the platform actually exists + await SwiftToolchain.getSDKForTarget(picked.value); + } + const swiftSDKTriple = picked.value ? getDarwinTargetTriple(picked.value) : ""; + if (swiftSDKTriple !== "") { + // set a swiftSDK for non-macOS Darwin platforms so that SourceKit-LSP can provide syntax highlighting + configuration.swiftSDK = swiftSDKTriple; + vscode.window.showWarningMessage( + `Selecting the ${picked.label} target platform will provide code editing support, but compiling with a ${picked.label} SDK will have undefined results.` + ); } else { - vscode.window.showErrorMessage("Unable to obtain requested SDK path"); + // set swiftSDK to undefined for macOS and other platforms + configuration.swiftSDK = undefined; } } catch { - vscode.window.showErrorMessage("Unable to obtain requested SDK path"); + vscode.window.showErrorMessage( + `Unable set the Swift SDK setting to ${picked.label}, verify that the SDK exists` + ); } + ctx.statusItem.end(statusItemText); } } diff --git a/src/extension.ts b/src/extension.ts index 612ea9e3b..4c5bae82b 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -103,11 +103,7 @@ export async function activate(context: vscode.ExtensionContext): Promise { event.affectsConfiguration("swift.SDK") || event.affectsConfiguration("swift.swiftSDK") ) { - // FIXME: There is a bug stopping us from restarting SourceKit-LSP directly. - // As long as it's fixed we won't need to reload on newer versions. - showReloadExtensionNotification( - "Changing the Swift SDK path requires the project be reloaded." - ); + vscode.commands.executeCommand("swift.restartLSPServer"); } }) ); From d3d8d3e2ea7e67c667ca6ae97c76a5c39125e510 Mon Sep 17 00:00:00 2001 From: Rishi Benegal Date: Tue, 25 Feb 2025 10:07:59 -0500 Subject: [PATCH 2/2] Restrict Switch Platform to macOS and Swift 6.1 Currently this command only supports Darwin SDKs on macOS, and code editing support from Swift 6.1 and above. Only show the command for these cases. Also add a unit test for the command. Issue: #1335 --- README.md | 2 +- package.json | 2 +- src/commands.ts | 4 +- src/commands/switchPlatform.ts | 12 +-- src/contextKeys.ts | 15 ++++ src/extension.ts | 4 + .../commands/switchPlatform.test.ts | 85 +++++++++++++++++++ 7 files changed, 112 insertions(+), 12 deletions(-) create mode 100644 test/unit-tests/commands/switchPlatform.test.ts diff --git a/README.md b/README.md index 7f8069c26..4a0fd60c4 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ The extension adds the following commands, available via the command palette. The following command is only available on macOS: -- **Select Target Platform**: This is an experimental command that offers code completion for iOS and tvOS projects. +- **Select Target Platform**: This is an experimental command that offers code editing support for iOS, tvOS, watchOS and visionOS projects. #### Building and Debugging diff --git a/package.json b/package.json index e61380dca..a9f5726c2 100644 --- a/package.json +++ b/package.json @@ -805,7 +805,7 @@ }, { "command": "swift.switchPlatform", - "when": "swift.isActivated && isMac" + "when": "swift.isActivated && isMac && swift.switchPlatformAvailable" }, { "command": "swift.insertFunctionComment", diff --git a/src/commands.ts b/src/commands.ts index 57f1bb3b6..253e463ca 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -106,7 +106,9 @@ export function register(ctx: WorkspaceContext): vscode.Disposable[] { return runTestMultipleTimes(ctx.currentFolder, item, true); } }), - // Note: This is only available on macOS (gated in `package.json`) because its the only OS that has the iOS SDK available. + // Note: switchPlatform is only available on macOS and Swift 6.1 or later + // (gated in `package.json`) because it's the only OS and toolchain combination that + // has Darwin SDKs available and supports code editing with SourceKit-LSP vscode.commands.registerCommand("swift.switchPlatform", () => switchPlatform(ctx)), vscode.commands.registerCommand(Commands.RESET_PACKAGE, () => resetPackage(ctx)), vscode.commands.registerCommand("swift.runScript", () => runSwiftScript(ctx)), diff --git a/src/commands/switchPlatform.ts b/src/commands/switchPlatform.ts index 7c0ce1e9e..e65dfda2c 100644 --- a/src/commands/switchPlatform.ts +++ b/src/commands/switchPlatform.ts @@ -19,7 +19,6 @@ import { getDarwinTargetTriple, } from "../toolchain/toolchain"; import configuration from "../configuration"; -import { Version } from "../utilities/version"; import { WorkspaceContext } from "../WorkspaceContext"; /** @@ -39,12 +38,7 @@ export async function switchPlatform(ctx: WorkspaceContext) { } ); if (picked) { - if (ctx.toolchain.swiftVersion.isLessThan(new Version(6, 1, 0))) { - vscode.window.showWarningMessage( - "Code editing support for non-macOS platforms is only available starting Swift 6.1" - ); - } - // show a status item as getSDKForTarget can sometimes take a long while to xcrun for the SDK + // show a status item as getSDKForTarget can sometimes take a long while to run xcrun to find the SDK const statusItemText = `Setting target platform to ${picked.label}`; ctx.statusItem.start(statusItemText); try { @@ -60,8 +54,8 @@ export async function switchPlatform(ctx: WorkspaceContext) { `Selecting the ${picked.label} target platform will provide code editing support, but compiling with a ${picked.label} SDK will have undefined results.` ); } else { - // set swiftSDK to undefined for macOS and other platforms - configuration.swiftSDK = undefined; + // set swiftSDK to an empty string for macOS and other platforms + configuration.swiftSDK = ""; } } catch { vscode.window.showErrorMessage( diff --git a/src/contextKeys.ts b/src/contextKeys.ts index e550c7707..ed735af9c 100644 --- a/src/contextKeys.ts +++ b/src/contextKeys.ts @@ -77,6 +77,11 @@ interface ContextKeys { * Whether the SourceKit-LSP server supports documentation live preview. */ supportsDocumentationLivePreview: boolean; + + /** + * Whether the swift.switchPlatform command is available. + */ + switchPlatformAvailable: boolean; } /** Creates the getters and setters for the VS Code Swift extension's context keys. */ @@ -92,6 +97,7 @@ function createContextKeys(): ContextKeys { let createNewProjectAvailable: boolean = false; let supportsReindexing: boolean = false; let supportsDocumentationLivePreview: boolean = false; + let switchPlatformAvailable: boolean = false; return { get isActivated() { @@ -200,6 +206,15 @@ function createContextKeys(): ContextKeys { value ); }, + + get switchPlatformAvailable() { + return switchPlatformAvailable; + }, + + set switchPlatformAvailable(value: boolean) { + switchPlatformAvailable = value; + vscode.commands.executeCommand("setContext", "swift.switchPlatformAvailable", value); + }, }; } diff --git a/src/extension.ts b/src/extension.ts index 4c5bae82b..748e8b522 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -76,12 +76,16 @@ export async function activate(context: vscode.ExtensionContext): Promise { contextKeys.createNewProjectAvailable = toolchain.swiftVersion.isGreaterThanOrEqual( new Version(5, 8, 0) ); + contextKeys.switchPlatformAvailable = toolchain.swiftVersion.isGreaterThanOrEqual( + new Version(6, 1, 0) + ); return toolchain; }) .catch(error => { outputChannel.log("Failed to discover Swift toolchain"); outputChannel.log(error); contextKeys.createNewProjectAvailable = false; + contextKeys.switchPlatformAvailable = false; return undefined; }); diff --git a/test/unit-tests/commands/switchPlatform.test.ts b/test/unit-tests/commands/switchPlatform.test.ts new file mode 100644 index 000000000..f2c54f105 --- /dev/null +++ b/test/unit-tests/commands/switchPlatform.test.ts @@ -0,0 +1,85 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the VS Code Swift open source project +// +// Copyright (c) 2021-2025 the VS Code Swift project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of VS Code Swift project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import { expect } from "chai"; +import * as vscode from "vscode"; +import { + mockObject, + mockGlobalObject, + mockGlobalModule, + MockedObject, + mockFn, + instance, +} from "../../MockUtils"; +import { + DarwinCompatibleTarget, + SwiftToolchain, + getDarwinTargetTriple, +} from "../../../src/toolchain/toolchain"; +import { WorkspaceContext } from "../../../src/WorkspaceContext"; +import { switchPlatform } from "../../../src/commands/switchPlatform"; +import { StatusItem } from "../../../src/ui/StatusItem"; +import configuration from "../../../src/configuration"; + +suite("Switch Target Platform Unit Tests", () => { + const mockedConfiguration = mockGlobalModule(configuration); + const windowMock = mockGlobalObject(vscode, "window"); + const mockSwiftToolchain = mockGlobalModule(SwiftToolchain); + let mockContext: MockedObject; + let mockedStatusItem: MockedObject; + + setup(() => { + mockedStatusItem = mockObject({ + start: mockFn(), + end: mockFn(), + }); + mockContext = mockObject({ + statusItem: instance(mockedStatusItem), + }); + }); + + test("Call Switch Platform and switch to iOS", async () => { + const selectedItem = { value: DarwinCompatibleTarget.iOS, label: "iOS" }; + windowMock.showQuickPick.resolves(selectedItem); + mockSwiftToolchain.getSDKForTarget.resolves(""); + expect(mockedConfiguration.swiftSDK).to.equal(""); + + await switchPlatform(instance(mockContext)); + + expect(windowMock.showQuickPick).to.have.been.calledOnce; + expect(windowMock.showWarningMessage).to.have.been.calledOnceWithExactly( + "Selecting the iOS target platform will provide code editing support, but compiling with a iOS SDK will have undefined results." + ); + expect(mockedStatusItem.start).to.have.been.called; + expect(mockedStatusItem.end).to.have.been.called; + expect(mockedConfiguration.swiftSDK).to.equal( + getDarwinTargetTriple(DarwinCompatibleTarget.iOS) + ); + }); + + test("Call Switch Platform and switch to macOS", async () => { + const selectedItem = { value: undefined, label: "macOS" }; + windowMock.showQuickPick.resolves(selectedItem); + mockSwiftToolchain.getSDKForTarget.resolves(""); + expect(mockedConfiguration.swiftSDK).to.equal(""); + + await switchPlatform(instance(mockContext)); + + expect(windowMock.showQuickPick).to.have.been.calledOnce; + expect(windowMock.showWarningMessage).to.not.have.been.called; + expect(mockedStatusItem.start).to.have.been.called; + expect(mockedStatusItem.end).to.have.been.called; + expect(mockedConfiguration.swiftSDK).to.equal(""); + }); +});