Skip to content

Commit

Permalink
added hot code reload button
Browse files Browse the repository at this point in the history
  • Loading branch information
iusildra committed Aug 14, 2023
1 parent 0e07b14 commit ed54f78
Show file tree
Hide file tree
Showing 5 changed files with 267 additions and 1 deletion.
13 changes: 13 additions & 0 deletions packages/metals-vscode/icons/hot_code_replace.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 22 additions & 1 deletion packages/metals-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@
"configuration": {
"title": "Metals",
"properties": {
"metals.debug.settings.hotCodeReplace": {
"type": "boolean",
"default": false,
"markdownDescription": "Allow Hot Code Replace (HCR) while debugging"
},
"metals.serverVersion": {
"type": "string",
"default": "1.0.0+20-46905735-SNAPSHOT",
Expand Down Expand Up @@ -419,6 +424,14 @@
}
},
"commands": [
{
"command": "metals.debug.hotCodeReplace",
"title": "Hot Code Replace",
"icon": {
"light": "icons/hot_code_replace.svg",
"dark": "icons/hot_code_replace.svg"
}
},
{
"command": "metals.reveal-active-file",
"category": "Metals",
Expand Down Expand Up @@ -692,6 +705,13 @@
"when": "view == metalsPackages"
}
],
"debug/toolBar": [
{
"command": "metals.debug.hotCodeReplace",
"group": "navigation@100",
"when": "inDebugMode && debugType == scala && scalaHotReloadOn"
}
],
"commandPalette": [
{
"command": "metals.show-tasty",
Expand Down Expand Up @@ -1085,7 +1105,8 @@
"metals-languageclient": "file:../metals-languageclient",
"promisify-child-process": "4.1.1",
"semver": "^7.5.2",
"vscode-languageclient": "8.1.0"
"vscode-languageclient": "8.1.0",
"vscode-extension-telemetry-wrapper": "^0.13.3"
},
"extensionPack": [
"scala-lang.scala"
Expand Down
69 changes: 69 additions & 0 deletions packages/metals-vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
tests as vscodeTextExplorer,
debug,
DebugSessionCustomEvent,
DebugSession,
} from "vscode";
import {
LanguageClient,
Expand Down Expand Up @@ -104,6 +105,9 @@ import {
SCALA_LANGID,
} from "./consts";
import { ScalaCodeLensesParams } from "./debugger/types";
import { initializeHotCodeReplace } from "./hotCodeReplace";
import { instrumentOperationAsVsCodeCommand } from "vscode-extension-telemetry-wrapper";
import * as vscode from "vscode";

const outputChannel = window.createOutputChannel("Metals");
const downloadJava = "Download Java";
Expand Down Expand Up @@ -1143,6 +1147,15 @@ function launchMetals(
}
);
context.subscriptions.push(decorationsRangesDidChangeDispoasable);
context.subscriptions.push(
instrumentOperationAsVsCodeCommand(
"metals.debug.hotCodeReplace",
async () => {
await applyHCR();
}
)
);
initializeHotCodeReplace(context);
},
(reason) => {
if (reason instanceof Error) {
Expand Down Expand Up @@ -1350,3 +1363,59 @@ function handleUserNotification(customEvent: DebugSessionCustomEvent) {
window.showInformationMessage(customEvent.body.message);
}
}

async function applyHCR() {
const debugSession: DebugSession | undefined =
vscode.debug.activeDebugSession;
if (!debugSession) {
return;
}

if (debugSession.configuration.noDebug) {
vscode.window
.showWarningMessage(
"Failed to apply the changes because hot code replace is not supported by run mode, " +
"would you like to restart the program?"
)
.then((res) => {
if (res === "Yes") {
vscode.commands.executeCommand("workbench.action.debug.restart");
}
});

return;
}

const start = new Date().getTime();
const redefineRequest = debugSession.customRequest("redefineClasses");
vscode.window.setStatusBarMessage(
"$(sync~spin) Applying code changes...",
redefineRequest
);
const response = await redefineRequest;
const elapsed = new Date().getTime() - start;
const humanVisibleDelay = elapsed < 150 ? 150 : 0;
if (humanVisibleDelay) {
await new Promise((resolve) => {
setTimeout(resolve, humanVisibleDelay);
});
}

if (response?.errorMessage) {
vscode.window.showErrorMessage(response.errorMessage);
return;
}

if (!response?.changedClasses?.length) {
vscode.window.showWarningMessage(
"Cannot find any changed classes for hot replace!"
);
return;
}

const changed = response.changedClasses.length;
vscode.window.setStatusBarMessage(
`$(check) Class${changed > 1 ? "es" : ""} successfully reloaded`,
5 * 1000
);
}
106 changes: 106 additions & 0 deletions packages/metals-vscode/src/hotCodeReplace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

// Adapter from https://github.com/microsoft/vscode-java-debug/blob/main/src/hotCodeReplace.ts

import * as vscode from "vscode";

import { SCALA_LANGID } from "./consts";
import { window } from "vscode";

const suppressedReasons: Set<string> = new Set();

export const YES_BUTTON = "Yes";

export const NO_BUTTON = "No";

const NEVER_BUTTON = "Do not show again";

enum HcrChangeType {
ERROR = "ERROR",
WARNING = "WARNING",
STARTING = "STARTING",
END = "END",
BUILD_COMPLETE = "BUILD_COMPLETE",
}

export function initializeHotCodeReplace(context: vscode.ExtensionContext) {
vscode.commands.executeCommand(
"setContext",
"scalaHotReloadOn",
hotReplaceIsOn()
);
vscode.workspace.onDidChangeConfiguration((event) => {
if (event.affectsConfiguration("metals.debug.settings.hotCodeReplace")) {
vscode.commands.executeCommand(
"setContext",
"scalaHotReloadOn",
hotReplaceIsOn()
);
}
});
vscode.debug.onDidStartDebugSession((session) => {
if (session?.configuration.noDebug && !vscode.debug.activeDebugSession) {
vscode.commands.executeCommand("setContext", "scalaHotReloadOn", false);
}
});
vscode.debug.onDidChangeActiveDebugSession((session) => {
vscode.commands.executeCommand(
"setContext",
"scalaHotReloadOn",
session && !session.configuration.noDebug
);
});
context.subscriptions.push(
vscode.debug.onDidTerminateDebugSession((session) => {
const t = session ? session.type : undefined;
if (t === SCALA_LANGID) {
suppressedReasons.clear();
}
})
);
}

export function handleHotCodeReplaceCustomEvent(
hcrEvent: vscode.DebugSessionCustomEvent
) {
if (hcrEvent.body.changeType === HcrChangeType.BUILD_COMPLETE) {
if (hotReplaceIsOn()) {
return vscode.window.withProgress(
{ location: vscode.ProgressLocation.Window },
(progress) => {
progress.report({ message: "Applying code changes..." });
return hcrEvent.session.customRequest("redefineClasses");
}
);
}
}

if (
hcrEvent.body.changeType === HcrChangeType.ERROR ||
hcrEvent.body.changeType === HcrChangeType.WARNING
) {
if (!suppressedReasons.has(hcrEvent.body.message)) {
window
.showWarningMessage(
`Hot code replace failed - ${hcrEvent.body.message}. Would you like to restart the debug session?`
)
.then((res) => {
if (res === NEVER_BUTTON) {
suppressedReasons.add(hcrEvent.body.message);
} else if (res === YES_BUTTON) {
vscode.commands.executeCommand("workbench.action.debug.restart");
}
});
}
}
return undefined;
}

function hotReplaceIsOn(): boolean {
return (
vscode.workspace
.getConfiguration("metals.debug.settings")
.get("hotCodeReplace") ?? false
);
}
57 changes: 57 additions & 0 deletions packages/metals-vscode/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,42 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==

"@microsoft/1ds-core-js@3.2.13", "@microsoft/1ds-core-js@^3.2.3":
version "3.2.13"
resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz#0c105ed75091bae3f1555c0334704fa9911c58fb"
integrity sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg==
dependencies:
"@microsoft/applicationinsights-core-js" "2.8.15"
"@microsoft/applicationinsights-shims" "^2.0.2"
"@microsoft/dynamicproto-js" "^1.1.7"

"@microsoft/1ds-post-js@^3.2.3":
version "3.2.13"
resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz#560aacac8a92fdbb79e8c2ebcb293d56e19f51aa"
integrity sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA==
dependencies:
"@microsoft/1ds-core-js" "3.2.13"
"@microsoft/applicationinsights-shims" "^2.0.2"
"@microsoft/dynamicproto-js" "^1.1.7"

"@microsoft/applicationinsights-core-js@2.8.15":
version "2.8.15"
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz#8fa466474260e01967fe649f14dd9e5ff91dcdc8"
integrity sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ==
dependencies:
"@microsoft/applicationinsights-shims" "2.0.2"
"@microsoft/dynamicproto-js" "^1.1.9"

"@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085"
integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg==

"@microsoft/dynamicproto-js@^1.1.7", "@microsoft/dynamicproto-js@^1.1.9":
version "1.1.9"
resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz#7437db7aa061162ee94e4131b69a62b8dad5dea6"
integrity sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ==

"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
Expand Down Expand Up @@ -282,6 +318,14 @@
async "^3.2.2"
semver "^7.3.5"

"@vscode/extension-telemetry@^0.6.2":
version "0.6.2"
resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.6.2.tgz#b86814ee680615730da94220c2b03ea9c3c14a8e"
integrity sha512-yb/wxLuaaCRcBAZtDCjNYSisAXz3FWsSqAha5nhHcYxx2ZPdQdWuZqVXGKq0ZpHVndBWWtK6XqtpCN2/HB4S1w==
dependencies:
"@microsoft/1ds-core-js" "^3.2.3"
"@microsoft/1ds-post-js" "^3.2.3"

"@vscode/test-electron@^2.3.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@vscode/test-electron/-/test-electron-2.3.0.tgz#de0ba2f5d36546a83cd481b458cbdbb7cc0f7049"
Expand Down Expand Up @@ -2445,6 +2489,19 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=

uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==

vscode-extension-telemetry-wrapper@^0.13.3:
version "0.13.3"
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry-wrapper/-/vscode-extension-telemetry-wrapper-0.13.3.tgz#685fe92843b07fe785e416926721e26f867c3a69"
integrity sha512-k/PbUbH9/xqiMXI2g2RXpDg+4/v08t3NzdPc7HuDPF3A1XcYkgYwsPnS/bqsKZNymSQdbLvVuie6STMxbDX9KQ==
dependencies:
"@vscode/extension-telemetry" "^0.6.2"
uuid "^8.3.2"

vscode-jsonrpc@8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.1.0.tgz#cb9989c65e219e18533cc38e767611272d274c94"
Expand Down

0 comments on commit ed54f78

Please sign in to comment.