diff --git a/package.json b/package.json index 48e78dd..c553e7b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "opendream", - "version": "0.2.3", + "version": "0.2.4", "displayName": "OpenDream Dev Tools", "icon": "OD.png", "description": "Developer tools for OpenDream", @@ -40,10 +40,15 @@ "default": null, "description": "Path to your copy of the OpenDream source code. This should be the top level folder containing OpenDream.sln. Leave empty to use pre-compiled binaries instead." }, - "opendream.hotReload": { + "opendream.hotReloadResource": { "type": "boolean", "default": true, "description": "Automatically reload resources when they're changed on disk." + }, + "opendream.hotReloadCode": { + "type": "boolean", + "default": true, + "description": "Automatically recompile and hot reload code files when they're changed on disk." } } }, @@ -123,7 +128,24 @@ { "language": "dm" } - ] + ], + "commands": [ + { + "command": "opendream.hotReload", + "title": "Hot Reload", + "shortTitle": "$(flame)", + "icon": "$(flame)" + + } + ], + "menus": { + "debug/toolBar": [ + { + "command": "opendream.hotReload", + "group": "navigation" + } + ] + } }, "scripts": { "vscode:prepublish": "webpack --mode production", diff --git a/src/extension.ts b/src/extension.ts index 50f2b32..9d58b08 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -32,6 +32,8 @@ const LauncherBinVersionTagFile = "latestLauncherBuild.txt" let storagePath: string | undefined; +let hotReloadCommandFunction: (() => void); + /* Correctness notes: - Tasks passed to `executeTask` must have unique `type` values in their definitions, or VSC will confuse them. @@ -53,6 +55,10 @@ export async function activate(context: ExtensionContext) { // Register the task provider for OpenDream build tasks. context.subscriptions.push(vscode.tasks.registerTaskProvider('opendream', new OpenDreamTaskProvider())); + // ------------------------------------------------------------------------7 + // Register the hot reload command as a handle on the function which is set by the debug adapter + commands.registerCommand('opendream.hotReload', () => hotReloadCommandFunction()) + // ------------------------------------------------------------------------ // Register the debugger mode. @@ -106,8 +112,9 @@ export async function activate(context: ExtensionContext) { let buildClientPromise = openDream.buildClient(session.workspaceFolder); // Wait for the OD server to connect back to us, then stop listening. let socket = await socketPromise; - let hotReloadEnable:boolean = workspace.getConfiguration('opendream').get('hotReload') || false; - return new vscode.DebugAdapterInlineImplementation(new OpenDreamDebugAdapter(socket, buildClientPromise, hotReloadEnable)); + let hotReloadResourceEnable:boolean = workspace.getConfiguration('opendream').get('hotReloadResource') || false; + let hotReloadCodeEnable:boolean = workspace.getConfiguration('opendream').get('hotReloadCode') || false; + return new vscode.DebugAdapterInlineImplementation(new OpenDreamDebugAdapter(socket, buildClientPromise, hotReloadResourceEnable, hotReloadCodeEnable)); } })); @@ -165,12 +172,13 @@ class OpenDreamDebugAdapter implements vscode.DebugAdapter { private buffer = Buffer.alloc(0); private resourceWatcher?: vscode.FileSystemWatcher private interfaceWatcher?: vscode.FileSystemWatcher + private codeWatcher?: vscode.FileSystemWatcher private didSendMessageEmitter = new vscode.EventEmitter(); private sendMessageToEditor = this.didSendMessageEmitter.fire.bind(this.didSendMessageEmitter); onDidSendMessage = this.didSendMessageEmitter.event; - constructor(socket: net.Socket, buildClientPromise?: Promise, hotReloadAuto?: boolean) { + constructor(socket: net.Socket, buildClientPromise?: Promise, hotReloadResourceAuto?: boolean, hotReloadCodeAuto?: boolean) { this.socket = socket; this.buildClientPromise = buildClientPromise; @@ -220,9 +228,12 @@ class OpenDreamDebugAdapter implements vscode.DebugAdapter { } }); - if(hotReloadAuto) { + hotReloadCommandFunction = () => this.hotReloadCode() + + if(hotReloadResourceAuto) { this.resourceWatcher = vscode.workspace.createFileSystemWatcher(("**/*.{dmi,png,jpg,rsi,gif,bmp}"))//TODO all the sound file formats? this.interfaceWatcher = vscode.workspace.createFileSystemWatcher(("**/*.{dmf}")) + this.interfaceWatcher.onDidChange(() => {this.hotReloadInterface()}) this.interfaceWatcher.onDidCreate(() => {this.hotReloadInterface()}) @@ -231,6 +242,15 @@ class OpenDreamDebugAdapter implements vscode.DebugAdapter { this.resourceWatcher.onDidChange((file) => {this.hotReloadResource(file)}) this.resourceWatcher.onDidCreate((file) => {this.hotReloadResource(file)}) this.resourceWatcher.onDidDelete((file) => {this.hotReloadResource(file)}) + + + } + if(hotReloadCodeAuto){ + this.codeWatcher = vscode.workspace.createFileSystemWatcher(("**/*.{dm,dme}")) + + this.codeWatcher.onDidChange((file) => {this.hotReloadCode(file)}) + this.codeWatcher.onDidCreate((file) => {this.hotReloadCode(file)}) + this.codeWatcher.onDidDelete((file) => {this.hotReloadCode(file)}) } } @@ -244,6 +264,26 @@ class OpenDreamDebugAdapter implements vscode.DebugAdapter { this.sendMessageToGame({ type: 'request', command: 'hotreloadresource', arguments: {'file':resource.fsPath}}) } + private hotReloadCode(resource?:vscode.Uri): void { + if(resource?.fsPath.includes("DMStandard")) + return //ignore changes to DMStandard + console.log(`Hot reloading code triggered by ${resource?.fsPath}`) + vscode.tasks.fetchTasks({type: 'opendream'}).then((tasks) => { + for(let x of tasks) + if(x.name.includes("compile") && x.name.includes(".dme")) + return vscode.tasks.executeTask(x); + throw Error("Can't find compile task!") + }).then((execution)=>{ + vscode.tasks.onDidEndTask((event) => { + if(event.execution.task == execution.task) + console.log("compile complete, sending hot reload request") + commands.executeCommand('opendream.getFilenameJson').then( + (filename)=>this.sendMessageToGame({ type: 'request', command: 'hotreloadbytecode', arguments: {'file':filename}}) + ) + }) + }) + } + handleMessage(message: any): void { if (message.type == 'request' && message.command == 'disconnect') { // Kill the client when the Stop button is clicked. @@ -273,6 +313,8 @@ class OpenDreamDebugAdapter implements vscode.DebugAdapter { dispose() { this.resourceWatcher?.dispose() this.interfaceWatcher?.dispose() + this.codeWatcher?.dispose() + this.socket.destroy(); this.didSendMessageEmitter.dispose(); }