mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 05:13:58 -07:00
Up to now `eslint-plugin-brace-rules` was used to enforce a common brace style for JavaScript code. This plugin was however updated the last time over 9 years ago and will be incompatible with ESLint v10, as that [removes `context.getSourceCode()`][1], the plugin relies on. To keep the eslint config working with ESLint v10, this replaces `eslint-plugin-brace-rules` with the [`@stylistic/brace-style`][2] rule from `@stylistic/eslint-plugin`, a package we already use. While `@stylistic/brace-style` doesn't offer an option to format braces in exactly the same way as before, the "allman" style seems to be the one closest to the existing code. [1]: https://eslint.org/blog/2025/11/eslint-v10.0.0-alpha.0-released/#removed-deprecated-rule-context-members [2]: https://eslint.style/rules/brace-style
171 lines
4.4 KiB
JavaScript
171 lines
4.4 KiB
JavaScript
import { Plugin } from 'tools/dap/plugin.js';
|
|
|
|
class BreakpointManager extends Plugin
|
|
{
|
|
constructor(jsDebugger, dapHandler)
|
|
{
|
|
super('BreakpointManager', 'manager');
|
|
this.breakpoints = [];
|
|
|
|
this.dbg = jsDebugger.instance;
|
|
this.jsDebugger = jsDebugger;
|
|
this.logger.debug('Setting up BreakpointManager');
|
|
jsDebugger.on('onNewScript', ({ script, global }) =>
|
|
{
|
|
if (!jsDebugger.debuggerAttached)
|
|
return;
|
|
|
|
this.logger.debug(`New script loaded: ${script.url}`);
|
|
const url = script.url;
|
|
|
|
if (!this.addBreakpointsByPath(url, script))
|
|
return;
|
|
|
|
const index = this.breakpoints.findIndex((bp) => bp.url === url);
|
|
if (index === -1)
|
|
return;
|
|
|
|
this.logger.debug(`Setting breakpoints for script: ${script.url}`);
|
|
const sourceReferenceIndex = jsDebugger.sourcesReferences.findIndex((src) => src.path === url);
|
|
this.breakpoints[index].lines.forEach((bp, i) =>
|
|
{
|
|
jsDebugger.pushEvent('breakpoint', {
|
|
'reason': 'changed',
|
|
'breakpoint': {
|
|
'id': ((index + 1) * 1000) + i,
|
|
'verified': bp.verified,
|
|
'message': bp.message,
|
|
'source': {
|
|
'path': url,
|
|
'sourceReference': sourceReferenceIndex + 1
|
|
}
|
|
}
|
|
}, this.name);
|
|
});
|
|
}, this.name);
|
|
|
|
jsDebugger.on('onDebuggerDetached', () =>
|
|
{
|
|
this.logger.debug('Debugger detached');
|
|
this.dbg.clearAllBreakpoints();
|
|
this.breakpoints = [];
|
|
}, this.name);
|
|
|
|
dapHandler.registerCommand('setBreakpoints', (req) =>
|
|
{
|
|
const path = req.arguments.source.path;
|
|
const name = req.arguments.source.name;
|
|
this.logger.debug(`Handling setBreakpoints command for source: ${req.arguments.source.path}`);
|
|
|
|
if (!path)
|
|
{
|
|
this.logger.error('Invalid source path or name');
|
|
return dapHandler.errorResponse(req, 'Invalid source path or name');
|
|
}
|
|
|
|
const index = this.createOrUpdateBreakpoint(name, path, req.arguments.breakpoints.map((bp) => ({ 'line': bp.line })));
|
|
this.addBreakpointsByPath(path);
|
|
|
|
const sourceReferenceIndex = jsDebugger.sourcesReferences.findIndex((src) => src.path === path);
|
|
const responses = this.breakpoints[index - 1].lines.map((bp, i) => ({
|
|
'id': (index * 1000) + i,
|
|
'verified': bp.verified ?? false,
|
|
'reason': bp.verified ? null : 'pending',
|
|
'message': bp.verified ? null: bp.message,
|
|
'source': {
|
|
'path': path,
|
|
'sourceReference': sourceReferenceIndex + 1
|
|
}
|
|
}));
|
|
|
|
return dapHandler.successResponse(req, { 'breakpoints': responses });
|
|
});
|
|
}
|
|
|
|
createOrUpdateBreakpoint(name, url, lines)
|
|
{
|
|
let index = this.breakpoints.findIndex((bp) => (!name || bp.name === name) && bp.url === url);
|
|
if (index === -1)
|
|
{
|
|
this.breakpoints.push({ name, 'url': url, lines });
|
|
index = this.breakpoints.length - 1;
|
|
}
|
|
else
|
|
this.breakpoints[index].lines = lines;
|
|
|
|
return index + 1;
|
|
}
|
|
|
|
addBreakpointsByPath(path, instance_script)
|
|
{
|
|
const infoBk = this.breakpoints.find((bp) => bp.url === path);
|
|
|
|
if (!infoBk)
|
|
return false;
|
|
|
|
const scripts = instance_script ? [instance_script] : this.dbg.findScripts({ 'url': path });
|
|
if (scripts.length === 0)
|
|
return false;
|
|
|
|
this.logger.trace(`Found ${scripts.length} scripts for path: ${path}`);
|
|
this.scriptTreeWalk(scripts, (script) =>
|
|
{
|
|
script.clearAllBreakpoints();
|
|
});
|
|
|
|
infoBk.lines.forEach((bp) =>
|
|
{
|
|
this.logger.debug(`Setting breakpoint at: ${uneval(bp)}`);
|
|
bp.verified = false;
|
|
bp.message = "No offset found";
|
|
this.scriptTreeWalk(scripts, (script) =>
|
|
{
|
|
const offsets = script.getPossibleBreakpointOffsets({
|
|
'line': bp.line,
|
|
});
|
|
|
|
// Continue walking the tree.
|
|
if (offsets.length === 0)
|
|
return true;
|
|
|
|
this.logger.debug(`Setted breakpoint at: ${uneval(offsets[0])}`);
|
|
script.setBreakpoint(offsets[0], { 'hit': this.handleBreakpoint.bind(this) });
|
|
bp.verified = true;
|
|
bp.message = "Breakpoint set";
|
|
// Stop walking the tree.
|
|
return false;
|
|
});
|
|
});
|
|
return true;
|
|
}
|
|
|
|
scriptTreeWalk(script, callback)
|
|
{
|
|
if (!script || script.length === 0)
|
|
return;
|
|
|
|
const queue = [...script];
|
|
while (queue.length > 0)
|
|
{
|
|
const currentScript = queue.shift();
|
|
if (callback(currentScript) === false)
|
|
return;
|
|
queue.push(...currentScript.getChildScripts());
|
|
this.logger.trace(`Queue length: ${queue.length}`);
|
|
}
|
|
}
|
|
|
|
handleBreakpoint(frame)
|
|
{
|
|
this.jsDebugger.stopInframe(frame, () =>
|
|
{
|
|
this.jsDebugger.pushEvent('stopped', {
|
|
'reason': 'breakpoint',
|
|
'threadId': 1,
|
|
'text': "Paused on breakpoint"
|
|
}, this.name);
|
|
});
|
|
}
|
|
}
|
|
|
|
export default BreakpointManager;
|