Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,7 @@ import { useLocalDependency } from "./commands/dependencies/useLocal";
import { generateLaunchConfigurations } from "./commands/generateLaunchConfigurations";
import { generateSourcekitConfiguration } from "./commands/generateSourcekitConfiguration";
import { insertFunctionComment } from "./commands/insertFunctionComment";
import {
installSwiftlySnapshotToolchain,
installSwiftlyToolchain,
} from "./commands/installSwiftlyToolchain";
import { promptToInstallSwiftlyToolchain } from "./commands/installSwiftlyToolchain";
import { newSwiftFile } from "./commands/newFile";
import { openDocumentation } from "./commands/openDocumentation";
import { openEducationalNote } from "./commands/openEducationalNote";
Expand Down Expand Up @@ -356,11 +353,11 @@ export function register(ctx: WorkspaceContext): vscode.Disposable[] {
),
vscode.commands.registerCommand(
Commands.INSTALL_SWIFTLY_TOOLCHAIN,
async () => await installSwiftlyToolchain(ctx)
async () => await promptToInstallSwiftlyToolchain(ctx, "stable")
),
vscode.commands.registerCommand(
Commands.INSTALL_SWIFTLY_SNAPSHOT_TOOLCHAIN,
async () => await installSwiftlySnapshotToolchain(ctx)
async () => await promptToInstallSwiftlyToolchain(ctx, "snapshot")
),
];
}
Expand Down
138 changes: 41 additions & 97 deletions src/commands/installSwiftlyToolchain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,22 @@
//
//===----------------------------------------------------------------------===//
import * as vscode from "vscode";
import { QuickPickItem } from "vscode";

import { WorkspaceContext } from "../WorkspaceContext";
import { SwiftLogger } from "../logging/SwiftLogger";
import { AvailableToolchain, Swiftly, SwiftlyProgressData } from "../toolchain/swiftly";
import { showReloadExtensionNotification } from "../ui/ReloadExtension";

interface SwiftlyToolchainItem extends QuickPickItem {
toolchain: AvailableToolchain;
}

async function downloadAndInstallToolchain(selected: SwiftlyToolchainItem, ctx: WorkspaceContext) {
return await installSwiftlyToolchainVersion(selected.toolchain.version.name, ctx.logger, true);
}
import { Swiftly, SwiftlyProgressData } from "../toolchain/swiftly";
import { askWhereToSetToolchain } from "../ui/ToolchainSelection";

/**
* Installs a Swiftly toolchain by version string
* Installs a Swiftly toolchain and shows a progress notification to the user.
*
* @param version The toolchain version to install
* @param logger Optional logger for error reporting
* @param showReloadNotification Whether to show reload notification after installation
* @returns Promise<boolean> true if installation succeeded, false otherwise
*/
export async function installSwiftlyToolchainVersion(
export async function installSwiftlyToolchainWithProgress(
version: string,
logger?: SwiftLogger,
showReloadNotification: boolean = true,
token?: vscode.CancellationToken
logger?: SwiftLogger
): Promise<boolean> {
try {
await vscode.window.withProgress(
Expand All @@ -47,8 +36,7 @@ export async function installSwiftlyToolchainVersion(
title: `Installing Swift ${version}`,
cancellable: true,
},
async (progress, progressToken) => {
const effectiveToken = token || progressToken;
async (progress, token) => {
progress.report({ message: "Starting installation..." });

let lastProgress = 0;
Expand All @@ -71,7 +59,7 @@ export async function installSwiftlyToolchainVersion(
}
},
logger,
effectiveToken
token
);

progress.report({
Expand All @@ -81,11 +69,6 @@ export async function installSwiftlyToolchainVersion(
}
);

if (showReloadNotification) {
void showReloadExtensionNotification(
`Swift ${version} has been installed and activated. Visual Studio Code needs to be reloaded.`
);
}
return true;
} catch (error) {
const errorMessage = (error as Error).message;
Expand All @@ -104,7 +87,10 @@ export async function installSwiftlyToolchainVersion(
/**
* Shows a quick pick dialog to install available Swiftly toolchains
*/
export async function installSwiftlyToolchain(ctx: WorkspaceContext): Promise<void> {
export async function promptToInstallSwiftlyToolchain(
ctx: WorkspaceContext,
type: "stable" | "snapshot"
): Promise<void> {
if (!Swiftly.isSupported()) {
ctx.logger?.warn("Swiftly is not supported on this platform.");
void vscode.window.showErrorMessage(
Expand All @@ -121,7 +107,21 @@ export async function installSwiftlyToolchain(ctx: WorkspaceContext): Promise<vo
return;
}

const availableToolchains = await Swiftly.listAvailable(undefined, ctx.logger);
let branch: string | undefined = undefined;
if (type === "snapshot") {
// Prompt user to enter the branch for snapshot toolchains
branch = await vscode.window.showInputBox({
title: "Enter Swift Snapshot Branch",
prompt: "Enter the branch name to list snapshot toolchains (e.g., 'main-snapshot', '6.1-snapshot')",
placeHolder: "main-snapshot",
value: "main-snapshot",
});
if (!branch) {
return; // User cancelled input
}
}

const availableToolchains = await Swiftly.listAvailable(branch, ctx.logger);

if (availableToolchains.length === 0) {
ctx.logger?.debug("No toolchains available for installation via Swiftly.");
Expand All @@ -131,7 +131,9 @@ export async function installSwiftlyToolchain(ctx: WorkspaceContext): Promise<vo
return;
}

const uninstalledToolchains = availableToolchains.filter(toolchain => !toolchain.installed);
const uninstalledToolchains = availableToolchains
.filter(toolchain => !toolchain.installed)
.filter(toolchain => toolchain.version.type === type);

if (uninstalledToolchains.length === 0) {
ctx.logger?.debug("All available toolchains are already installed.");
Expand All @@ -154,85 +156,27 @@ export async function installSwiftlyToolchain(ctx: WorkspaceContext): Promise<vo
placeHolder: "Pick a Swift toolchain to install",
canPickMany: false,
});

if (!selected) {
return;
}

await downloadAndInstallToolchain(selected, ctx);
}

/**
* Shows a quick pick dialog to install available Swiftly snapshot toolchains
*/
export async function installSwiftlySnapshotToolchain(ctx: WorkspaceContext): Promise<void> {
if (!Swiftly.isSupported()) {
void vscode.window.showErrorMessage(
"Swiftly is not supported on this platform. Only macOS and Linux are supported."
);
const target = await askWhereToSetToolchain();
if (!target) {
return;
}

if (!(await Swiftly.isInstalled())) {
void vscode.window.showErrorMessage(
"Swiftly is not installed. Please install Swiftly first from https://www.swift.org/install/"
);
// Install the toolchain via Swiftly
if (!(await installSwiftlyToolchainWithProgress(selected.toolchain.version.name, ctx.logger))) {
return;
}

// Prompt user to enter the branch for snapshot toolchains
const branch = await vscode.window.showInputBox({
title: "Enter Swift Snapshot Branch",
prompt: "Enter the branch name to list snapshot toolchains (e.g., 'main-snapshot', '6.1-snapshot')",
placeHolder: "main-snapshot",
value: "main-snapshot",
});

if (!branch) {
return; // User cancelled input
}

const availableToolchains = await Swiftly.listAvailable(branch, ctx.logger);

if (availableToolchains.length === 0) {
ctx.logger?.debug("No toolchains available for installation via Swiftly.");
void vscode.window.showInformationMessage(
"No toolchains are available for installation via Swiftly."
);
return;
}

// Filter for only uninstalled snapshot toolchains
const uninstalledSnapshotToolchains = availableToolchains.filter(
toolchain => !toolchain.installed && toolchain.version.type === "snapshot"
);

if (uninstalledSnapshotToolchains.length === 0) {
ctx.logger?.debug("All available snapshot toolchains are already installed.");
void vscode.window.showInformationMessage(
"All available snapshot toolchains are already installed."
// Tell Swiftly to use the newly installed toolchain
if (target === vscode.ConfigurationTarget.Workspace) {
await Promise.all(
vscode.workspace.workspaceFolders?.map(folder =>
Swiftly.use(selected.toolchain.version.name, folder.uri.fsPath)
) ?? []
);
return;
}

const quickPickItems = uninstalledSnapshotToolchains.map(toolchain => ({
label: `$(cloud-download) ${toolchain.version.name}`,
description: "snapshot",
detail: `Date: ${
toolchain.version.type === "snapshot" ? toolchain.version.date || "Unknown" : "Unknown"
} • Branch: ${toolchain.version.type === "snapshot" ? toolchain.version.branch || "Unknown" : "Unknown"}`,
toolchain: toolchain,
}));

const selected = await vscode.window.showQuickPick(quickPickItems, {
title: "Install Swift Snapshot Toolchain via Swiftly",
placeHolder: "Pick a Swift snapshot toolchain to install",
canPickMany: false,
});

if (!selected) {
return;
}

await downloadAndInstallToolchain(selected, ctx);
await Swiftly.use(selected.toolchain.version.name);
}
9 changes: 3 additions & 6 deletions src/toolchain/swiftly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ import * as Stream from "stream";
import * as vscode from "vscode";
import { z } from "zod/v4/mini";

// Import the reusable installation function
import { installSwiftlyToolchainVersion } from "../commands/installSwiftlyToolchain";
import { installSwiftlyToolchainWithProgress } from "../commands/installSwiftlyToolchain";
import { ContextKeys } from "../contextKeys";
import { SwiftLogger } from "../logging/SwiftLogger";
import { showMissingToolchainDialog } from "../ui/ToolchainSelection";
Expand Down Expand Up @@ -153,8 +152,7 @@ export function parseSwiftlyMissingToolchainError(
export async function handleMissingSwiftlyToolchain(
version: string,
logger?: SwiftLogger,
folder?: vscode.Uri,
token?: vscode.CancellationToken
folder?: vscode.Uri
): Promise<boolean> {
logger?.info(`Attempting to handle missing toolchain: ${version}`);

Expand All @@ -167,7 +165,7 @@ export async function handleMissingSwiftlyToolchain(

// Use the existing installation function without showing reload notification
// (since we want to continue the current operation)
return await installSwiftlyToolchainVersion(version, logger, false, token);
return await installSwiftlyToolchainWithProgress(version, logger);
}

export class Swiftly {
Expand Down Expand Up @@ -532,7 +530,6 @@ export class Swiftly {
const installArgs = [
"install",
version,
"--use",
"--assume-yes",
"--post-install-file",
postInstallFilePath,
Expand Down
2 changes: 1 addition & 1 deletion src/ui/ToolchainSelection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ export async function removeToolchainPath() {
await swiftSettings.update("path", undefined, vscode.ConfigurationTarget.Workspace);
}

async function askWhereToSetToolchain(): Promise<vscode.ConfigurationTarget | undefined> {
export async function askWhereToSetToolchain(): Promise<vscode.ConfigurationTarget | undefined> {
if (!vscode.workspace.workspaceFolders) {
return vscode.ConfigurationTarget.Global;
}
Expand Down
Loading