Skip to content

Commit

Permalink
feat(debugger): Update filewatch to look for .map and .js and send /r…
Browse files Browse the repository at this point in the history
…eload to MC (#248)

Similar to this PR
[here](#246) but leans
into VSCode settings for added flexibility. By using VSCode settings,
users can choose to save settings globally or per workspace and it also
gives us a place for documentation.

Thanks for the general glob pattern idea Dingsel. Will this solve your
use cases?

- file watching is off by default
- if enabled then both source map clear and /reload happen on source
changes.
- if only `localRoot` is defined (no `sourceMapRoot`), the file watch
pattern is *.js files from `localRoot`
- if `sourceMapRoot` is defined, the file watch pattern is for *.js and
*.map from `sourceMapRoot`
- if `globPattern` is specified, the root is the workspace.
- delay for sending /reload to MC, minimum is 100ms to debounce the file
changes.

Added a button to the home panel to open Settings UI directly to the
debugger page.

Addresses issue #130

---------

Co-authored-by: Raphael Landaverde <[email protected]>
  • Loading branch information
chmeyer-ms and rlandav authored Oct 29, 2024
1 parent 2b1b24f commit e63585a
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 30 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,15 @@ Now, hit "Start Debugging" inside of Visual Studio Code.

As with when you debug against Minecraft clients, you can set breakpoints in your code by clicking on the left-hand side of the editor, on specific lines of code.

#### Minecraft Debugger Home Panel
The Activity Bar icon ![image](/icons/creeper_icon.png) will open the Minecraft Debugger home panel. Here you will find shortcuts for common actions, like opening Settings and showing the Diagnostics panel.

##### Minecraft Command Shortcuts
Add shortcuts for your favorite Minecraft commands.

##### Script Profiler
After setting a local path for saving captures, use `Start Profiler` and `Stop Profiler` to create performance captures of your actively running add-on.

#### Diagnostics Window
When attatched to a game server running Minecraft 1.21.10 or above, the debugger can display high level statistics to help diagnost performance issues.

Expand Down
15 changes: 15 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,21 @@
],
"configuration": {
"properties": {
"minecraft-debugger.reloadOnSourceChanges.enabled": {
"type": "boolean",
"default": false,
"description": "Reload Minecraft on source changes. Watch localRoot or sourceMapRoot."
},
"minecraft-debugger.reloadOnSourceChanges.delay": {
"type": "number",
"default": 100,
"description": "Delay in milliseconds between detecting source changes and reloading Minecraft."
},
"minecraft-debugger.reloadOnSourceChanges.globPattern": {
"type": "string",
"default": "",
"description": "Override the default locations and monitor any workspace files that match the glob pattern."
},
"minecraft-debugger.showDiagnosticViewOnConnect": {
"type": "boolean",
"default": false,
Expand Down
85 changes: 63 additions & 22 deletions src/Session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
InputBoxOptions,
QuickPickItem,
QuickPickOptions,
RelativePattern,
Uri,
workspace,
window,
Expand Down Expand Up @@ -128,7 +129,7 @@ export class Session extends DebugSession {
private _threads = new Set<number>();
private _requests = new Map<number, PendingResponse>();
private _sourceMaps: SourceMaps = new SourceMaps('');
private _fileWatcher?: FileSystemWatcher;
private _sourceFileWatcher?: FileSystemWatcher;
private _activeThreadId: number = 0; // the one being debugged
private _localRoot: string = '';
private _sourceMapRoot?: string;
Expand Down Expand Up @@ -167,6 +168,11 @@ export class Session extends DebugSession {
this._eventEmitter.removeAllListeners('start-profiler');
this._eventEmitter.removeAllListeners('stop-profiler');
this._eventEmitter.removeAllListeners('request-debugger-status');

if (this._sourceFileWatcher) {
this._sourceFileWatcher.dispose();
this._sourceFileWatcher = undefined;
}
}

// ------------------------------------------------------------------------
Expand Down Expand Up @@ -661,7 +667,7 @@ export class Session extends DebugSession {
);

// watch for source map changes
this.createSourceMapFileWatcher(this._sourceMapRoot);
this.createSourceMapFileWatcher(this._localRoot, this._sourceMapRoot);

// Now that a connection is established, and capabilities have been delivered, send this event to
// tell VSCode to ask Minecraft/debugee for config data (breakpoints etc).
Expand Down Expand Up @@ -689,11 +695,6 @@ export class Session extends DebugSession {
this._connectionSocket.destroy();
}
this._connectionSocket = undefined;

if (this._fileWatcher) {
this._fileWatcher.dispose();
this._fileWatcher = undefined;
}
}

// close and terminate session (could be from debugee request)
Expand Down Expand Up @@ -1015,22 +1016,62 @@ export class Session extends DebugSession {
}
}

private createSourceMapFileWatcher(sourceMapRoot?: string) {
if (this._fileWatcher) {
this._fileWatcher.dispose();
this._fileWatcher = undefined;
private createSourceMapFileWatcher(localRoot: string, sourceMapRoot?: string) {
if (this._sourceFileWatcher) {
this._sourceFileWatcher.dispose();
this._sourceFileWatcher = undefined;
}
if (sourceMapRoot) {
this._fileWatcher = workspace.createFileSystemWatcher('**/*.{map}', false, false, false);
this._fileWatcher.onDidChange(uri => {
this._sourceMaps.reset();
});
this._fileWatcher.onDidCreate(uri => {
this._sourceMaps.reset();
});
this._fileWatcher.onDidDelete(uri => {
this._sourceMaps.reset();
});

const config = workspace.getConfiguration('minecraft-debugger');
const reloadOnSourceChangesEnabled = config.get<boolean>('reloadOnSourceChanges.enabled');
if (!reloadOnSourceChangesEnabled) {
return;
}

const reloadOnSourceChangesDelay = Math.max(config.get<number>('reloadOnSourceChanges.delay') ?? 0, 0);
const reloadOnSourceChangesGlobPattern = config.get<string>('reloadOnSourceChanges.globPattern');

// Either monitor the build output (TS->JS) by looking at .map and .js files in sourceMapRoot,
// or monitor .js files directly if not using TS or source maps by looking at localRoot,
// or monitor a specific glob pattern for all files within the workspace.
let globPattern: RelativePattern | undefined = undefined;
if (reloadOnSourceChangesGlobPattern) {
const workspaceFolders = workspace.workspaceFolders;
if (workspaceFolders && workspaceFolders.length > 0) {
globPattern = new RelativePattern(workspaceFolders[0].uri.fsPath ?? '', reloadOnSourceChangesGlobPattern);
}
}
else if (sourceMapRoot) {
globPattern = new RelativePattern(sourceMapRoot, '**/*.{map,js}');
}
else if (localRoot) {
globPattern = new RelativePattern(localRoot, '**/*.js');
}

if (globPattern) {
this._sourceFileWatcher = workspace.createFileSystemWatcher(globPattern, false, false, false);
}

const doReload = (): void => {
this._sourceMaps.clearCache();
this.onRunMinecraftCommand('say §aPerforming Auto-Reload');
this.onRunMinecraftCommand('reload');
};

const debounce = (func: () => void, wait: number): (() => void) => {
let timeout: NodeJS.Timeout;
return () => {
clearTimeout(timeout);
timeout = setTimeout(func, wait);
};
};

const debouncedReload = debounce(doReload, reloadOnSourceChangesDelay);

if (this._sourceFileWatcher) {
this._sourceFileWatcher.onDidChange(debouncedReload);
this._sourceFileWatcher.onDidCreate(debouncedReload);
this._sourceFileWatcher.onDidDelete(debouncedReload);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/SourceMaps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ export class SourceMaps {
: SourceMapConsumer.LEAST_UPPER_BOUND;
}

public reset() {
public clearCache() {
this._sourceMapCache.reset();
}

Expand Down
4 changes: 4 additions & 0 deletions src/panels/HomeViewProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ export class HomeViewProvider implements vscode.WebviewViewProvider {
vscode.commands.executeCommand('minecraft-debugger.showMinecraftDiagnostics');
break;
}
case 'show-settings': {
vscode.commands.executeCommand('workbench.action.openSettings', '@ext:mojang-studios.minecraft-debugger');
break;
}
case 'run-minecraft-command': {
if (!message.command || message.command.trim() === '') {
vscode.window.showErrorMessage('Minecraft Command Shortcut can not be empty.');
Expand Down
11 changes: 8 additions & 3 deletions webview-ui/src/home_panel/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@

// Copyright (C) Microsoft Corporation. All rights reserved.

import { useEffect, useRef, useState } from 'react';
import { useEffect, useState } from 'react';
import CommandSection from './controls/CommandSection'
import { CommandButton, CommandHandlers, getCommandHandlers } from './handlers/CommandHandlers';
import DiagnosticSection from './controls/DiagnosticsSection';
import GeneralSection from './controls/GeneralSection';
import ProfilerSection from './controls/ProfilerSection';
import { CaptureItem, ProfilerHandlers, getProfilerHandlers } from './handlers/ProfilerHandlers';
import StatusSection from './controls/StatusSection';
Expand All @@ -22,6 +22,10 @@ const onShowDiagnosticsPanel = () => {
vscode.postMessage({ type: 'show-diagnostics' });
};

const onShowSettings = () => {
vscode.postMessage({ type: 'show-settings' });
};

const onRunCommand = (command: string) => {
vscode.postMessage({ type: 'run-minecraft-command', command: command });
};
Expand Down Expand Up @@ -121,8 +125,9 @@ const App = () => {
<StatusSection
debuggerConnected={debuggerConnected}
/>
<DiagnosticSection
<GeneralSection
onShowDiagnosticsPanel={onShowDiagnosticsPanel}
onShowSettings={onShowSettings}
/>
<CommandSection
debuggerConnected={debuggerConnected}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,23 @@
import React from 'react';
import { VSCodeButton } from '@vscode/webview-ui-toolkit/react';

interface DiagnosticsSectionProps {
interface GeneralSectionProps {
onShowDiagnosticsPanel: () => void;
onShowSettings(): void;
}

const DiagnosticsSection: React.FC<DiagnosticsSectionProps> = ({ onShowDiagnosticsPanel }) => {
const GeneralSection: React.FC<GeneralSectionProps> = ({ onShowDiagnosticsPanel, onShowSettings }) => {
return (
<div className="section">
<h3 className="title">Diagnostics</h3>
<h3 className="title">Actions</h3>
<VSCodeButton className="standard-button" onClick={onShowDiagnosticsPanel}>
Show Diagnostics
</VSCodeButton>
<VSCodeButton className="standard-button" onClick={onShowSettings}>
Show Settings
</VSCodeButton>
</div>
);
};

export default DiagnosticsSection;
export default GeneralSection;

0 comments on commit e63585a

Please sign in to comment.