From 62b1f34970d995c85d96c44031daf8c056cc4caf Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Wed, 15 Nov 2023 11:45:45 +0000 Subject: [PATCH] feat: deno upgrade prompt (#988) --- client/src/commands.ts | 23 +++++++++++-- client/src/extension.ts | 2 +- client/src/server_info.ts | 3 ++ client/src/status_bar.ts | 5 ++- client/src/tasks.ts | 1 + client/src/types.d.ts | 9 ++++++ client/src/upgrade.ts | 68 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 client/src/upgrade.ts diff --git a/client/src/commands.ts b/client/src/commands.ts index ae756c21..951759f5 100644 --- a/client/src/commands.ts +++ b/client/src/commands.ts @@ -17,7 +17,11 @@ import { } from "./lsp_extensions"; import * as tasks from "./tasks"; import { DenoTestController, TestingFeature } from "./testing"; -import type { DenoExtensionContext, TestCommandOptions } from "./types"; +import type { + DenoExtensionContext, + DidUpgradeCheckParams, + TestCommandOptions, +} from "./types"; import { WelcomePanel } from "./welcome"; import { assert, @@ -38,6 +42,7 @@ import type { Position, } from "vscode-languageclient/node"; import { getWorkspacesEnabledInfo } from "./enable"; +import { denoUpgradePromptAndExecute } from "./upgrade"; // deno-lint-ignore no-explicit-any export type Callback = (...args: any[]) => unknown; @@ -210,6 +215,16 @@ export function startLanguageServer( ); extensionContext.serverCapabilities = client.initializeResult?.capabilities; extensionContext.statusBar.refresh(extensionContext); + extensionContext.client.onNotification( + "deno/didUpgradeCheck", + (params: DidUpgradeCheckParams) => { + if (extensionContext.serverInfo) { + extensionContext.serverInfo.upgradeAvailable = + params.upgradeAvailable; + extensionContext.statusBar.refresh(extensionContext); + } + }, + ); if (testingFeature.enabled) { context.subscriptions.push(new DenoTestController(extensionContext)); @@ -399,11 +414,15 @@ export function welcome( }; } -export function openOutput( +export function statusBarClicked( _context: vscode.ExtensionContext, extensionContext: DenoExtensionContext, ) { return () => { extensionContext.outputChannel.show(true); + if (extensionContext.serverInfo?.upgradeAvailable) { + // Async dispatch on purpose. + denoUpgradePromptAndExecute(extensionContext.serverInfo.upgradeAvailable); + } }; } diff --git a/client/src/extension.ts b/client/src/extension.ts index a3342266..a4444c06 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -464,7 +464,7 @@ export async function activate( registerCommand("deno.client.restart", commands.startLanguageServer); registerCommand("deno.client.status", commands.status); registerCommand("deno.client.welcome", commands.welcome); - registerCommand("deno.client.openOutput", commands.openOutput); + registerCommand("deno.client.statusBarClicked", commands.statusBarClicked); } export function deactivate(): Thenable | undefined { diff --git a/client/src/server_info.ts b/client/src/server_info.ts index d7cf4f07..c4dd8092 100644 --- a/client/src/server_info.ts +++ b/client/src/server_info.ts @@ -1,12 +1,15 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. import { InitializeResult } from "vscode-languageclient"; +import { UpgradeAvailable } from "./types"; export class DenoServerInfo { readonly #fullVersion: string; + upgradeAvailable: UpgradeAvailable | null; constructor(serverInfo: InitializeResult["serverInfo"]) { this.#fullVersion = serverInfo?.version ?? ""; + this.upgradeAvailable = null; } /** Gets the version with configuration and architecture. Ex: x.x.x (release, x86_64-etc) */ diff --git a/client/src/status_bar.ts b/client/src/status_bar.ts index ea3200fb..15868382 100644 --- a/client/src/status_bar.ts +++ b/client/src/status_bar.ts @@ -12,7 +12,7 @@ export class DenoStatusBar { vscode.StatusBarAlignment.Right, 0, ); - this.#inner.command = "deno.client.openOutput"; + this.#inner.command = "deno.client.statusBarClicked"; } dispose() { @@ -22,6 +22,9 @@ export class DenoStatusBar { refresh(extensionContext: DenoExtensionContext) { if (extensionContext.serverInfo) { this.#inner.text = `Deno ${extensionContext.serverInfo.version}`; + if (extensionContext.serverInfo.upgradeAvailable) { + this.#inner.text += " (Upgrade available)"; + } this.#inner.tooltip = extensionContext.serverInfo.versionWithBuildInfo; } diff --git a/client/src/tasks.ts b/client/src/tasks.ts index beb0575d..f85a97f9 100644 --- a/client/src/tasks.ts +++ b/client/src/tasks.ts @@ -113,6 +113,7 @@ class DenoTaskProvider implements vscode.TaskProvider { group: vscode.TaskGroup.Test, problemMatchers: ["$deno-test"], }, + { command: "upgrade", group: undefined, problemMatchers: ["$deno"] }, ]; const tasks: vscode.Task[] = []; diff --git a/client/src/types.d.ts b/client/src/types.d.ts index b2c97c2c..41317b62 100644 --- a/client/src/types.d.ts +++ b/client/src/types.d.ts @@ -47,3 +47,12 @@ export interface DenoExtensionContext { export interface TestCommandOptions { inspect: boolean; } + +export interface UpgradeAvailable { + latestVersion: string; + isCanary: boolean; +} + +export interface DidUpgradeCheckParams { + upgradeAvailable: UpgradeAvailable | null; +} diff --git a/client/src/upgrade.ts b/client/src/upgrade.ts new file mode 100644 index 00000000..19197e09 --- /dev/null +++ b/client/src/upgrade.ts @@ -0,0 +1,68 @@ +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +import { EXTENSION_NS } from "./constants"; +import * as tasks from "./tasks"; +import { UpgradeAvailable } from "./types"; +import { assert, getDenoCommandName } from "./util"; +import * as vscode from "vscode"; + +export async function denoUpgradePromptAndExecute( + { latestVersion, isCanary }: UpgradeAvailable, +) { + const config = vscode.workspace.getConfiguration(EXTENSION_NS); + let prompt = isCanary + ? `A new canary release of Deno is available: ${latestVersion.slice(0, 7)}.` + : `A new release of Deno is available: ${latestVersion}.`; + prompt += " Would you like to upgrade?"; + const selection = await vscode.window.showInformationMessage( + prompt, + "Upgrade", + "Dismiss", + ); + if (selection !== "Upgrade") { + return; + } + const args = ["upgrade"]; + if (config.get("unstable")) { + args.push("--unstable"); + } + if (isCanary) { + args.push("--canary"); + } + args.push("--version"); + args.push(latestVersion); + const env = {} as Record; + const cacheDir: string | undefined | null = config.get("cache"); + if (cacheDir?.trim()) { + env["DENO_DIR"] = cacheDir.trim(); + } + const definition: tasks.DenoTaskDefinition = { + type: tasks.TASK_TYPE, + command: "upgrade", + args, + env, + }; + assert(vscode.workspace.workspaceFolders); + const target = vscode.workspace.workspaceFolders[0]; + const denoCommand = await getDenoCommandName(); + const task = tasks.buildDenoTask( + target, + denoCommand, + definition, + "upgrade", + args, + ["$deno"], + ); + task.presentationOptions = { + reveal: vscode.TaskRevealKind.Always, + panel: vscode.TaskPanelKind.Dedicated, + clear: true, + }; + const execution = await vscode.tasks.executeTask(task); + const disposable = vscode.tasks.onDidEndTask((event) => { + if (event.execution == execution) { + disposable.dispose(); + vscode.commands.executeCommand("deno.client.restart"); + } + }); +}