Skip to content
Draft
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
10 changes: 2 additions & 8 deletions src/FolderContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,7 @@ export class FolderContext implements ExternalFolderContext, vscode.Disposable {

let toolchain: SwiftToolchain;
try {
toolchain = await SwiftToolchain.create(
workspaceContext.extensionContext.extensionPath,
folder
);
toolchain = await SwiftToolchain.create(workspaceContext.swiftly, folder);
} catch (error) {
// This error case is quite hard for the user to get in to, but possible.
// Typically on startup the toolchain creation failure is going to happen in
Expand All @@ -116,10 +113,7 @@ export class FolderContext implements ExternalFolderContext, vscode.Disposable {
if (userMadeSelection) {
// User updated toolchain settings, retry once
try {
toolchain = await SwiftToolchain.create(
workspaceContext.extensionContext.extensionPath,
folder
);
toolchain = await SwiftToolchain.create(workspaceContext.swiftly, folder);
workspaceContext.logger.info(
`Successfully created toolchain for ${FolderContext.uriName(folder)} after user selection`,
FolderContext.uriName(folder)
Expand Down
2 changes: 2 additions & 0 deletions src/WorkspaceContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { SwiftPluginTaskProvider } from "./tasks/SwiftPluginTaskProvider";
import { SwiftTaskProvider } from "./tasks/SwiftTaskProvider";
import { TaskManager } from "./tasks/TaskManager";
import { BuildFlags } from "./toolchain/BuildFlags";
import { Swiftly } from "./toolchain/swiftly";
import { SwiftToolchain } from "./toolchain/toolchain";
import { ProjectPanelProvider } from "./ui/ProjectPanelProvider";
import { StatusItem } from "./ui/StatusItem";
Expand Down Expand Up @@ -96,6 +97,7 @@ export class WorkspaceContext implements ExternalWorkspaceContext, vscode.Dispos
public loggerFactory: SwiftLoggerFactory;

constructor(
public swiftly: Swiftly | undefined,
public extensionContext: vscode.ExtensionContext,
public contextKeys: ContextKeys,
public logger: SwiftLogger,
Expand Down
3 changes: 3 additions & 0 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import { runTest } from "./commands/runTest";
import { switchPlatform } from "./commands/switchPlatform";
import { extractTestItemsAndCount, runTestMultipleTimes } from "./commands/testMultipleTimes";
import { SwiftLogger } from "./logging/SwiftLogger";
import { Swiftly } from "./toolchain/swiftly";
import { SwiftToolchain } from "./toolchain/toolchain";
import { PackageNode, PlaygroundNode } from "./ui/ProjectPanelProvider";
import { showToolchainSelectionQuickPick } from "./ui/ToolchainSelection";
Expand All @@ -67,6 +68,7 @@ export type WorkspaceContextWithToolchain = WorkspaceContext & { toolchain: Swif

export function registerToolchainCommands(
ctx: WorkspaceContext | undefined,
swiftly: Swiftly | undefined,
logger: SwiftLogger
): vscode.Disposable[] {
return [
Expand All @@ -76,6 +78,7 @@ export function registerToolchainCommands(
vscode.commands.registerCommand("swift.selectToolchain", () =>
showToolchainSelectionQuickPick(
ctx?.currentFolder?.toolchain ?? ctx?.globalToolchain,
swiftly,
logger,
ctx?.currentFolder?.folder
)
Expand Down
55 changes: 31 additions & 24 deletions src/commands/installSwiftly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@ import * as path from "path";
import * as vscode from "vscode";

import { SwiftLogger } from "../logging/SwiftLogger";
import { Swiftly } from "../toolchain/swiftly";
import { Swiftly, SwiftlyFactory } from "../toolchain/swiftly";
import { Workbench } from "../utilities/commands";
import { installSwiftlyToolchainWithProgress } from "./installSwiftlyToolchain";

/**
* Prompts user for Swiftly installation with directory customization options
* @param logger Optional logger
* @returns A promise that resolves to true if the user has opted to install swiftly, false otherwise
* Prompts user for Swiftly installation with directory customization options.
*
* @param logger The logger to use for logging messages.
* @returns A promise that resolves to `true` if the user has opted to install swiftly, `false` otherwise.
*/
export async function promptForSwiftlyInstallation(logger?: SwiftLogger): Promise<boolean> {
export async function promptForSwiftlyInstallation(logger: SwiftLogger): Promise<boolean> {
const installMessage = `A .swift-version file was detected. Install Swiftly to automatically manage Swift toolchain versions for this project.`;

const selection = await vscode.window.showWarningMessage(
Expand Down Expand Up @@ -66,19 +67,19 @@ This process involves updating your shell profile in order to add swiftly to you
await vscode.workspace
.getConfiguration("swift")
.update("disableSwiftlyInstallPrompt", true, vscode.ConfigurationTarget.Global);
logger?.info("Swiftly installation prompt suppressed by user");
logger.info("Swiftly installation prompt suppressed by user");
}

return false;
}

/**
* Installs Swiftly with progress tracking and user feedback
* @param options Installation options
* @param logger Optional logger
* @returns Promise<boolean> true if installation succeeded
* Installs Swiftly with progress tracking and user feedback.
*
* @param logger The logger to use for logging messages.
* @returns A promise that resolves to `true` if installation succeeded, `false` otherwise.
*/
export async function installSwiftlyWithProgress(logger?: SwiftLogger): Promise<boolean> {
export async function installSwiftlyWithProgress(logger: SwiftLogger): Promise<boolean> {
try {
await vscode.window.withProgress(
{
Expand All @@ -92,14 +93,14 @@ export async function installSwiftlyWithProgress(logger?: SwiftLogger): Promise<
);
return true;
} catch (error) {
logger?.error(`Failed to install Swiftly: ${error}`);
logger.error(`Failed to install Swiftly: ${error}`);
const message = error instanceof Error ? error.message : String(error);
void vscode.window.showErrorMessage(`Failed to install Swiftly: ${message}`);
return false;
}
}

async function promptToRestartVSCode(): Promise<void> {
async function promptToRestartVSCode(): Promise<boolean> {
const selection = await vscode.window.showInformationMessage(
"Restart VS Code",
{
Expand All @@ -110,23 +111,30 @@ async function promptToRestartVSCode(): Promise<void> {
);
if (selection === "Quit Visual Studio Code") {
await vscode.commands.executeCommand(Workbench.ACTION_QUIT);
return true;
}
return false;
}

/**
* Main function to handle missing Swiftly detection and installation
* @param swiftVersionFiles A list of swift version files that will need to be installed
* @param logger Optional logger
* @returns Promise<boolean> true if Swiftly was installed or already exists
* Main function to handle missing Swiftly detection and installation.
*
* Asks the user whether or not they want to install Swiftly to manage their Swift toolchains. It will
* also install the provided Swift toolchain versions after installing Swiftly.
*
* @param swiftlyFactory Factory used to create a Swiftly instance after installation.
* @param swiftVersions An array of Swift toolchain versions to install.
* @param logger The logger to use for logging messages.
* @returns A promise that resolves to `true` if Swiftly was installed or already exists, `false` otherwise.
*/
export async function handleMissingSwiftly(
swiftlyFactory: SwiftlyFactory,
swiftVersions: string[],
extensionRoot: string,
logger?: SwiftLogger
logger: SwiftLogger
): Promise<boolean> {
// Check if the user wants to disable the prompt
if (vscode.workspace.getConfiguration("swift").get("disableSwiftlyInstallPrompt", false)) {
logger?.debug("Swiftly installation prompt is suppressed");
logger.debug("Swiftly installation prompt is suppressed");
return false;
}

Expand All @@ -141,12 +149,11 @@ export async function handleMissingSwiftly(
}

// Install toolchains
const swiftlyPath = path.join(Swiftly.defaultHomeDir(), "bin/swiftly");
const swiftly = swiftlyFactory.create(path.join(Swiftly.defaultHomeDir(), "bin/swiftly"));
for (const version of swiftVersions) {
await installSwiftlyToolchainWithProgress(version, extensionRoot, logger, swiftlyPath);
await installSwiftlyToolchainWithProgress(swiftly, version);
}

// VS Code needs to be restarted after installing swiftly
await promptToRestartVSCode();
return true;
return await promptToRestartVSCode();
}
38 changes: 19 additions & 19 deletions src/commands/installSwiftlyToolchain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import * as vscode from "vscode";

import { WorkspaceContext } from "../WorkspaceContext";
import { SwiftLogger } from "../logging/SwiftLogger";
import { Swiftly, SwiftlyProgressData } from "../toolchain/swiftly";
import { SwiftToolchain } from "../toolchain/toolchain";
import {
Expand All @@ -31,10 +30,8 @@ import {
* @returns A promise that resolves to true if installation succeeded, false otherwise
*/
export async function installSwiftlyToolchainWithProgress(
version: string,
extensionRoot: string,
logger?: SwiftLogger,
swiftlyPath?: string
swiftly: Swiftly,
version: string
): Promise<boolean> {
try {
await vscode.window.withProgress(
Expand All @@ -48,9 +45,8 @@ export async function installSwiftlyToolchainWithProgress(

let lastProgress = 0;

await Swiftly.installToolchain(
await swiftly.installToolchain(
version,
extensionRoot,
(progressData: SwiftlyProgressData) => {
if (progressData.complete) {
// Swiftly will also verify the signature and extract the toolchain after the
Expand All @@ -74,9 +70,7 @@ export async function installSwiftlyToolchainWithProgress(
});
lastProgress = progressData.step.percent;
},
logger,
token,
swiftlyPath
token
);
}
);
Expand All @@ -87,12 +81,12 @@ export async function installSwiftlyToolchainWithProgress(
} catch (error) {
const errorMessage = (error as Error).message;
if (errorMessage.includes(Swiftly.cancellationMessage)) {
logger?.info(`Installation of Swift ${version} was cancelled by user`);
swiftly.logger.info(`Installation of Swift ${version} was cancelled by user`);
// Don't show error message for user-initiated cancellation
return false;
}

logger?.error(new Error(`Failed to install Swift ${version}`, { cause: error }));
swiftly.logger.error(new Error(`Failed to install Swift ${version}`, { cause: error }));
void vscode.window.showErrorMessage(`Failed to install Swift ${version}: ${error}`);
return false;
}
Expand All @@ -106,13 +100,20 @@ export async function promptToInstallSwiftlyToolchain(
type: "stable" | "snapshot"
): Promise<void> {
if (!Swiftly.isSupported()) {
ctx.logger?.warn("Swiftly is not supported on this platform.");
ctx.logger.error("Swiftly is not supported on this platform.");
void vscode.window.showErrorMessage(
"Swiftly is not supported on this platform. Only macOS and Linux are supported."
);
return;
}

const swiftly = ctx.swiftly;
if (!swiftly) {
ctx.logger.error("Swiftly is not installed.");
void vscode.window.showErrorMessage("Swiftly is not installed on this machine.");
return;
}

let branch: string | undefined = undefined;
if (type === "snapshot") {
// Prompt user to enter the branch for snapshot toolchains
Expand All @@ -127,7 +128,7 @@ export async function promptToInstallSwiftlyToolchain(
}
}

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

if (availableToolchains.length === 0) {
ctx.logger?.debug("No toolchains available for installation via Swiftly.");
Expand Down Expand Up @@ -180,9 +181,8 @@ export async function promptToInstallSwiftlyToolchain(
// Install the toolchain via Swiftly
if (
!(await installSwiftlyToolchainWithProgress(
selectedToolchain.toolchain.version.name,
ctx.extensionContext.extensionPath,
ctx.logger
swiftly,
selectedToolchain.toolchain.version.name
))
) {
return;
Expand All @@ -196,12 +196,12 @@ export async function promptToInstallSwiftlyToolchain(
if (target === vscode.ConfigurationTarget.Workspace) {
await Promise.all(
vscode.workspace.workspaceFolders?.map(folder =>
Swiftly.use(selectedToolchain.toolchain.version.name, folder.uri.fsPath)
swiftly.use(selectedToolchain.toolchain.version.name, folder.uri.fsPath)
) ?? []
);
return;
}
await Swiftly.use(selectedToolchain.toolchain.version.name);
await swiftly.use(selectedToolchain.toolchain.version.name);
},
},
selectedDeveloperDir.developerDir,
Expand Down
Loading