From 4ac964b5032b1e771f451cf1bcde3f17958bb9fb Mon Sep 17 00:00:00 2001 From: Techatrix Date: Thu, 6 Feb 2025 03:43:52 +0100 Subject: [PATCH] guard against exceptions when updating configuration When an exception occurs because it is updating a read-only settings.json to remove the outdated `initialSetupDone` config option, the extensions fails to initialize properly. See #392 --- src/zigProvider.ts | 7 ++++--- src/zigSetup.ts | 19 ++++++++++++++----- src/zigUtil.ts | 22 ++++++++++++++++++++++ src/zls.ts | 23 ++++++++++++++--------- 4 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/zigProvider.ts b/src/zigProvider.ts index 48c00c3..136e648 100644 --- a/src/zigProvider.ts +++ b/src/zigProvider.ts @@ -2,7 +2,7 @@ import vscode from "vscode"; import semver from "semver"; -import { resolveExePathAndVersion } from "./zigUtil"; +import { resolveExePathAndVersion, workspaceConfigUpdateNoThrow } from "./zigUtil"; interface ExeWithVersion { exe: string; @@ -50,13 +50,14 @@ export class ZigProvider implements vscode.Disposable { * @param zigPath The path to the zig executable. If `null`, the `zig.path` config option will be removed. */ public async setAndSave(zigPath: string | null) { + const zigConfig = vscode.workspace.getConfiguration("zig"); if (!zigPath) { - await vscode.workspace.getConfiguration("zig").update("path", undefined, true); + await workspaceConfigUpdateNoThrow(zigConfig, "path", undefined, true); return; } const newValue = this.resolveZigPathConfigOption(zigPath); if (!newValue) return; - await vscode.workspace.getConfiguration("zig").update("path", newValue.exe, true); + await workspaceConfigUpdateNoThrow(zigConfig, "path", newValue.exe, true); this.set(newValue); } diff --git a/src/zigSetup.ts b/src/zigSetup.ts index 5e5c36b..de96237 100644 --- a/src/zigSetup.ts +++ b/src/zigSetup.ts @@ -7,7 +7,14 @@ import semver from "semver"; import * as minisign from "./minisign"; import * as versionManager from "./versionManager"; -import { VersionIndex, ZigVersion, asyncDebounce, getHostZigName, resolveExePathAndVersion } from "./zigUtil"; +import { + VersionIndex, + ZigVersion, + asyncDebounce, + getHostZigName, + resolveExePathAndVersion, + workspaceConfigUpdateNoThrow, +} from "./zigUtil"; import { ZigProvider } from "./zigProvider"; let statusItem: vscode.StatusBarItem; @@ -52,7 +59,8 @@ async function installZig(context: vscode.ExtensionContext, temporaryVersion?: s try { const exePath = await versionManager.install(versionManagerConfig, version); - await vscode.workspace.getConfiguration("zig").update("path", undefined, true); + const zigConfig = vscode.workspace.getConfiguration("zig"); + await workspaceConfigUpdateNoThrow(zigConfig, "path", undefined, true); zigProvider.set({ exe: exePath, version: version }); } catch (err) { zigProvider.set(null); @@ -272,7 +280,8 @@ async function selectVersionAndInstall(context: vscode.ExtensionContext) { await installZig(context); break; case "Use Zig in PATH": - await vscode.workspace.getConfiguration("zig").update("path", "zig", true); + const zigConfig = vscode.workspace.getConfiguration("zig"); + await workspaceConfigUpdateNoThrow(zigConfig, "path", "zig", true); break; case "Manually Specify Path": const uris = await vscode.window.showOpenDialog({ @@ -573,10 +582,10 @@ export async function setupZig(context: vscode.ExtensionContext) { const zigConfig = vscode.workspace.getConfiguration("zig"); const zigPath = zigConfig.get("path", ""); if (zigPath.startsWith(context.globalStorageUri.fsPath)) { - await zigConfig.update("path", undefined, true); + await workspaceConfigUpdateNoThrow(zigConfig, "path", undefined, true); } - await zigConfig.update("initialSetupDone", undefined, true); + await workspaceConfigUpdateNoThrow(zigConfig, "initialSetupDone", undefined, true); await context.workspaceState.update("zig-version", undefined); } diff --git a/src/zigUtil.ts b/src/zigUtil.ts index c4ca6f4..44faae5 100644 --- a/src/zigUtil.ts +++ b/src/zigUtil.ts @@ -121,6 +121,28 @@ export function asyncDebounce Promise { + try { + await config.update(section, value, configurationTarget, overrideInLanguage); + } catch (err) { + if (err instanceof Error) { + void vscode.window.showErrorMessage(err.message); + } else { + void vscode.window.showErrorMessage("failed to update settings.json"); + } + } +} + // Check timestamp `key` to avoid automatically checking for updates // more than once in an hour. export async function shouldCheckUpdate(context: vscode.ExtensionContext, key: string): Promise { diff --git a/src/zls.ts b/src/zls.ts index 317540b..9adff2d 100644 --- a/src/zls.ts +++ b/src/zls.ts @@ -17,7 +17,7 @@ import semver from "semver"; import * as minisign from "./minisign"; import * as versionManager from "./versionManager"; -import { getHostZigName, handleConfigOption, resolveExePathAndVersion } from "./zigUtil"; +import { getHostZigName, handleConfigOption, resolveExePathAndVersion, workspaceConfigUpdateNoThrow } from "./zigUtil"; import { zigProvider } from "./zigSetup"; const ZIG_MODE: DocumentSelector = [ @@ -211,8 +211,13 @@ async function configurationMiddleware( switch (response) { case `Use ${optionName} instead`: const { [optionName]: newValue, ...updatedAdditionalOptions } = additionalOptions; - await configuration.update("additionalOptions", updatedAdditionalOptions, true); - await configuration.update(section, newValue, true); + await workspaceConfigUpdateNoThrow( + configuration, + "additionalOptions", + updatedAdditionalOptions, + true, + ); + await workspaceConfigUpdateNoThrow(configuration, section, newValue, true); break; case "Show zig.zls.additionalOptions": await vscode.commands.executeCommand("workbench.action.openSettingsJson", { @@ -347,10 +352,10 @@ async function isEnabled(): Promise { ); switch (response) { case "Yes": - await zlsConfig.update("enabled", "on", true); + await workspaceConfigUpdateNoThrow(zlsConfig, "enabled", "on", true); return true; case "No": - await zlsConfig.update("enabled", "off", true); + await workspaceConfigUpdateNoThrow(zlsConfig, "enabled", "off", true); return false; case undefined: return false; @@ -401,8 +406,8 @@ export async function activate(context: vscode.ExtensionContext) { const zlsConfig = vscode.workspace.getConfiguration("zig.zls"); const zlsPath = zlsConfig.get("path", ""); if (zlsPath.startsWith(context.globalStorageUri.fsPath)) { - await zlsConfig.update("enabled", "on", true); - await zlsConfig.update("path", undefined, true); + await workspaceConfigUpdateNoThrow(zlsConfig, "enabled", "on", true); + await workspaceConfigUpdateNoThrow(zlsConfig, "path", undefined, true); } } @@ -431,14 +436,14 @@ export async function activate(context: vscode.ExtensionContext) { statusItem, vscode.commands.registerCommand("zig.zls.enable", async () => { const zlsConfig = vscode.workspace.getConfiguration("zig.zls"); - await zlsConfig.update("enabled", "on", true); + await workspaceConfigUpdateNoThrow(zlsConfig, "enabled", "on", true); }), vscode.commands.registerCommand("zig.zls.stop", async () => { await stopClient(); }), vscode.commands.registerCommand("zig.zls.startRestart", async () => { const zlsConfig = vscode.workspace.getConfiguration("zig.zls"); - await zlsConfig.update("enabled", "on", true); + await workspaceConfigUpdateNoThrow(zlsConfig, "enabled", "on", true); await restartClient(context); }), vscode.commands.registerCommand("zig.zls.openOutput", () => {