Skip to content

Commit

Permalink
Add support for hot reload in Dart projects
Browse files Browse the repository at this point in the history
  • Loading branch information
DanTup committed Oct 26, 2021
1 parent 9488713 commit d8c5f6f
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 4 deletions.
9 changes: 9 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,11 @@
"light": "media/commands/hot-reload-tb.png"
}
},
{
"command": "dart.hotReload",
"title": "Hot Reload",
"category": "Dart"
},
{
"command": "flutter.openTimeline",
"title": "Open Observatory Timeline",
Expand Down Expand Up @@ -1004,6 +1009,10 @@
"command": "flutter.openTimeline",
"when": "dart-code:anyFlutterProjectLoaded && inDebugMode"
},
{
"command": "dart.hotReload",
"when": "inDebugMode"
},
{
"command": "dart.openDevTools",
"when": "dart-code:anyProjectLoaded"
Expand Down
16 changes: 15 additions & 1 deletion src/debug/dart_debug_impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1697,9 +1697,13 @@ export class DartDebugSession extends DebugSession {
this.logDapResponse(response);
this.sendResponse(response);
break;
case "hotReload":
await this.reloadSources();
this.logDapResponse(response);
this.sendResponse(response);
break;
// Flutter requests that may be sent during test runs or other places
// that we don't currently support.
case "hotReload":
case "hotRestart":
// TODO: Get rid of this!
this.log(`Ignoring Flutter customRequest ${request} for non-Flutter-run app`, LogSeverity.Warn);
Expand Down Expand Up @@ -2193,6 +2197,16 @@ export class DartDebugSession extends DebugSession {
setTimeout(() => this.pollForMemoryUsage(), this.pollforMemoryMs).unref();
}

private async reloadSources(): Promise<void> {
if (!this.vmService)
return;

const result = await this.vmService.getVM();
const vm = result.result as VM;

await Promise.all(vm.isolates.map((isolateRef) => this.vmService!.callMethod("reloadSources", { isolateId: isolateRef.id })));
}

protected logStdout(message: string) {
this.logToUserBuffered(message, "stdout");
}
Expand Down
5 changes: 3 additions & 2 deletions src/extension/commands/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,9 @@ export class DebugCommands implements IAmDisposable {
}));

// Misc custom debug commands.
this.disposables.push(vs.commands.registerCommand("_flutter.hotReload.touchBar", (args: any) => vs.commands.executeCommand("flutter.hotReload", args)));
this.disposables.push(vs.commands.registerCommand("flutter.hotReload", async (args?: any) => {
this.disposables.push(vs.commands.registerCommand("_flutter.hotReload.touchBar", (args: any) => vs.commands.executeCommand("dart.hotReload", args)));
this.disposables.push(vs.commands.registerCommand("_flutter.hotReload", (args: any) => vs.commands.executeCommand("dart.hotReload", args)));
this.disposables.push(vs.commands.registerCommand("dart.hotReload", async (args?: any) => {
if (!debugSessions.length)
return;
if (!!workspaceContext.config?.flutterSyncScript) {
Expand Down
22 changes: 21 additions & 1 deletion src/test/dart_debug/debug/dart_cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { fsPath, getRandomInt } from "../../../shared/utils/fs";
import { resolvedPromise } from "../../../shared/utils/promises";
import { DartDebugClient } from "../../dart_debug_client";
import { createDebugClient, ensureFrameCategories, ensureMapEntry, ensureNoVariable, ensureVariable, ensureVariableWithIndex, getVariablesTree, isExternalPackage, isLocalPackage, isSdkFrame, isUserCode, spawnDartProcessPaused, startDebugger, waitAllThrowIfTerminates } from "../../debug_helpers";
import { activate, breakpointFor, closeAllOpenFiles, defer, delay, ensureArrayContainsArray, extApi, getAttachConfiguration, getDefinition, getLaunchConfiguration, getPackages, getResolvedDebugConfiguration, helloWorldBrokenFile, helloWorldDeferredEntryFile, helloWorldDeferredScriptFile, helloWorldExampleSubFolder, helloWorldExampleSubFolderMainFile, helloWorldFolder, helloWorldGettersFile, helloWorldGoodbyeFile, helloWorldHttpFile, helloWorldInspectionFile as helloWorldInspectFile, helloWorldLocalPackageFile, helloWorldLongRunningFile, helloWorldMainFile, helloWorldPartEntryFile, helloWorldPartFile, helloWorldStack60File, helloWorldThrowInExternalPackageFile, helloWorldThrowInLocalPackageFile, helloWorldThrowInSdkFile, myPackageFolder, openFile, positionOf, sb, setConfigForTest, uriFor, waitForResult, watchPromise, writeBrokenDartCodeIntoFileForTest } from "../../helpers";
import { activate, breakpointFor, closeAllOpenFiles, currentEditor, defer, delay, ensureArrayContainsArray, extApi, getAttachConfiguration, getDefinition, getLaunchConfiguration, getPackages, getResolvedDebugConfiguration, helloWorldBrokenFile, helloWorldDeferredEntryFile, helloWorldDeferredScriptFile, helloWorldExampleSubFolder, helloWorldExampleSubFolderMainFile, helloWorldFolder, helloWorldGettersFile, helloWorldGoodbyeFile, helloWorldHttpFile, helloWorldInspectionFile as helloWorldInspectFile, helloWorldLocalPackageFile, helloWorldLongRunningFile, helloWorldMainFile, helloWorldPartEntryFile, helloWorldPartFile, helloWorldReloadFile, helloWorldStack60File, helloWorldThrowInExternalPackageFile, helloWorldThrowInLocalPackageFile, helloWorldThrowInSdkFile, myPackageFolder, openFile, positionOf, sb, setConfigForTest, setTestContent, uriFor, waitForResult, watchPromise, writeBrokenDartCodeIntoFileForTest } from "../../helpers";


describe("dart cli debugger", () => {
Expand Down Expand Up @@ -207,6 +207,26 @@ describe("dart cli debugger", () => {
assert.equal(config!.cwd, fsPath(helloWorldExampleSubFolder));
});

it("can hot reload", async () => {
await openFile(helloWorldReloadFile);
const config = await startDebugger(dc, helloWorldReloadFile);
await waitAllThrowIfTerminates(dc,
dc.configurationSequence(),
dc.assertOutputContains("stdout", "ORIGINAL CONTENT"),
dc.launch(config),
);

// Replace the content and trigger hot reload.
const editor = currentEditor();
await setTestContent(editor.document.getText().replace("ORIGINAL CONTENT", "NEW CONTENT"));
await editor.document.save();

await vs.commands.executeCommand("dart.hotReload");
await dc.assertOutputContains("stdout", "NEW CONTENT");

await dc.terminateRequest();
});

it("can launch DevTools externally", async () => {

// TODO(dantup): Tests for embedded DevTools.
Expand Down
1 change: 1 addition & 0 deletions src/test/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const testFolder = path.join(ext.extensionPath, "src/test");
// Dart
export const helloWorldFolder = vs.Uri.file(path.join(testFolder, "test_projects/hello_world"));
export const helloWorldMainFile = vs.Uri.file(path.join(fsPath(helloWorldFolder), "bin/main.dart"));
export const helloWorldReloadFile = vs.Uri.file(path.join(fsPath(helloWorldFolder), "bin/reload.dart"));
export const helloWorldInspectionFile = vs.Uri.file(path.join(fsPath(helloWorldFolder), "bin/inspect.dart"));
export const helloWorldLongRunningFile = vs.Uri.file(path.join(fsPath(helloWorldFolder), "bin/long_running.dart"));
export const helloWorldMainLibFile = vs.Uri.file(path.join(fsPath(helloWorldFolder), "lib/basic.dart"));
Expand Down
9 changes: 9 additions & 0 deletions src/test/test_projects/hello_world/bin/reload.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'dart:async';

main() async {
Timer.periodic(Duration(milliseconds: 100), (_) => printSomething());
}

void printSomething() {
print('ORIGINAL CONTENT');
}

0 comments on commit d8c5f6f

Please sign in to comment.