step into closures from elsewhere (draft)

This commit is contained in:
LenAnderson 2024-06-27 11:49:12 -04:00
parent aefa31a912
commit 173c5ef53e
5 changed files with 39 additions and 0 deletions

View File

@ -747,6 +747,7 @@ export class QuickReply {
} }
async executeFromEditor() { async executeFromEditor() {
if (this.isExecuting) return; if (this.isExecuting) return;
const oText = this.message;
this.isExecuting = true; this.isExecuting = true;
this.editorDom.classList.add('qr--isExecuting'); this.editorDom.classList.add('qr--isExecuting');
const noSyntax = this.editorDom.querySelector('#qr--modal-messageHolder').classList.contains('qr--noSyntax'); const noSyntax = this.editorDom.querySelector('#qr--modal-messageHolder').classList.contains('qr--noSyntax');
@ -797,6 +798,12 @@ export class QuickReply {
this.abortController = new SlashCommandAbortController(); this.abortController = new SlashCommandAbortController();
this.debugController = new SlashCommandDebugController(); this.debugController = new SlashCommandDebugController();
this.debugController.onBreakPoint = async(closure, executor)=>{ this.debugController.onBreakPoint = async(closure, executor)=>{
//TODO move debug code into its own element, separate from the QR
//TODO populate debug code from closure.fullText and get locations, highlights, etc. from that
//TODO keep some kind of reference (human identifier) *where* the closure code comes from?
//TODO QR name, chat input, deserialized closure, ... ?
this.editorMessage.value = closure.fullText;
this.editorMessage.dispatchEvent(new Event('input', { bubbles:true }));
this.editorDebugState.innerHTML = ''; this.editorDebugState.innerHTML = '';
let ci = -1; let ci = -1;
const varNames = []; const varNames = [];
@ -1177,6 +1184,8 @@ export class QuickReply {
if (noSyntax) { if (noSyntax) {
this.editorDom.querySelector('#qr--modal-messageHolder').classList.add('qr--noSyntax'); this.editorDom.querySelector('#qr--modal-messageHolder').classList.add('qr--noSyntax');
} }
this.editorMessage.value = oText;
this.editorMessage.dispatchEvent(new Event('input', { bubbles:true }));
this.editorExecutePromise = null; this.editorExecutePromise = null;
this.editorExecuteBtn.classList.remove('qr--busy'); this.editorExecuteBtn.classList.remove('qr--busy');
this.editorPopup.dlg.classList.remove('qr--hide'); this.editorPopup.dlg.classList.remove('qr--hide');

View File

@ -1737,6 +1737,10 @@ async function runCallback(args, name) {
throw new Error(`"${name}" is not callable.`); throw new Error(`"${name}" is not callable.`);
} }
closure.scope.parent = scope; closure.scope.parent = scope;
if (args._debugController && !closure.debugController) {
closure.debugController = args._debugController;
}
while (closure.providedArgumentList.pop());
closure.argumentList.forEach(arg => { closure.argumentList.forEach(arg => {
if (Object.keys(args).includes(arg.name)) { if (Object.keys(args).includes(arg.name)) {
const providedArg = new SlashCommandNamedArgumentAssignment(); const providedArg = new SlashCommandNamedArgumentAssignment();

View File

@ -1,6 +1,7 @@
import { SlashCommandAbortController } from './SlashCommandAbortController.js'; import { SlashCommandAbortController } from './SlashCommandAbortController.js';
import { SlashCommandArgument, SlashCommandNamedArgument } from './SlashCommandArgument.js'; import { SlashCommandArgument, SlashCommandNamedArgument } from './SlashCommandArgument.js';
import { SlashCommandClosure } from './SlashCommandClosure.js'; import { SlashCommandClosure } from './SlashCommandClosure.js';
import { SlashCommandDebugController } from './SlashCommandDebugController.js';
import { PARSER_FLAG } from './SlashCommandParser.js'; import { PARSER_FLAG } from './SlashCommandParser.js';
import { SlashCommandScope } from './SlashCommandScope.js'; import { SlashCommandScope } from './SlashCommandScope.js';
@ -12,6 +13,7 @@ import { SlashCommandScope } from './SlashCommandScope.js';
* _scope:SlashCommandScope, * _scope:SlashCommandScope,
* _parserFlags:{[id:PARSER_FLAG]:boolean}, * _parserFlags:{[id:PARSER_FLAG]:boolean},
* _abortController:SlashCommandAbortController, * _abortController:SlashCommandAbortController,
* _debugController:SlashCommandDebugController,
* _hasUnnamedArgument:boolean, * _hasUnnamedArgument:boolean,
* [id:string]:string|SlashCommandClosure, * [id:string]:string|SlashCommandClosure,
* }} NamedArguments * }} NamedArguments

View File

@ -24,6 +24,8 @@ export class SlashCommandClosure {
/**@type {SlashCommandDebugController}*/ debugController; /**@type {SlashCommandDebugController}*/ debugController;
/**@type {(done:number, total:number)=>void}*/ onProgress; /**@type {(done:number, total:number)=>void}*/ onProgress;
/**@type {string}*/ rawText; /**@type {string}*/ rawText;
/**@type {string}*/ fullText;
/**@type {string}*/ parserContext;
/**@type {number}*/ /**@type {number}*/
get commandCount() { get commandCount() {
@ -58,6 +60,12 @@ export class SlashCommandClosure {
const after = remaining.slice(match.index + match[0].length); const after = remaining.slice(match.index + match[0].length);
const replacer = match[1] ? scope.pipe : match[2] ? scope.getVariable(match[2], match[3]) : scope.macroList.find(it=>it.key == match[4])?.value; const replacer = match[1] ? scope.pipe : match[2] ? scope.getVariable(match[2], match[3]) : scope.macroList.find(it=>it.key == match[4])?.value;
if (replacer instanceof SlashCommandClosure) { if (replacer instanceof SlashCommandClosure) {
replacer.abortController = this.abortController;
replacer.breakController = this.breakController;
replacer.scope.parent = this.scope;
if (this.debugController && !replacer.debugController) {
replacer.debugController = this.debugController;
}
isList = true; isList = true;
if (match.index > 0) { if (match.index > 0) {
listValues.push(before); listValues.push(before);
@ -94,6 +102,9 @@ export class SlashCommandClosure {
closure.abortController = this.abortController; closure.abortController = this.abortController;
closure.breakController = this.breakController; closure.breakController = this.breakController;
closure.debugController = this.debugController; closure.debugController = this.debugController;
closure.rawText = this.rawText;
closure.fullText = this.fullText;
closure.parserContext = this.parserContext;
closure.onProgress = this.onProgress; closure.onProgress = this.onProgress;
return closure; return closure;
} }
@ -256,6 +267,7 @@ export class SlashCommandClosure {
_scope: this.scope, _scope: this.scope,
_parserFlags: executor.parserFlags, _parserFlags: executor.parserFlags,
_abortController: this.abortController, _abortController: this.abortController,
_debugController: this.debugController,
_hasUnnamedArgument: executor.unnamedArgumentList.length > 0, _hasUnnamedArgument: executor.unnamedArgumentList.length > 0,
}; };
let value; let value;
@ -266,6 +278,9 @@ export class SlashCommandClosure {
const closure = arg.value; const closure = arg.value;
closure.scope.parent = this.scope; closure.scope.parent = this.scope;
closure.breakController = this.breakController; closure.breakController = this.breakController;
if (this.debugController && !closure.debugController) {
closure.debugController = this.debugController;
}
if (closure.executeNow) { if (closure.executeNow) {
args[arg.name] = (await closure.execute())?.pipe; args[arg.name] = (await closure.execute())?.pipe;
} else { } else {
@ -285,6 +300,7 @@ export class SlashCommandClosure {
// substitute unnamed argument // substitute unnamed argument
if (executor.unnamedArgumentList.length == 0) { if (executor.unnamedArgumentList.length == 0) {
//TODO no pipe injection on first executor in a closure?
if (executor.injectPipe) { if (executor.injectPipe) {
value = this.scope.pipe; value = this.scope.pipe;
args._hasUnnamedArgument = this.scope.pipe !== null && this.scope.pipe !== undefined; args._hasUnnamedArgument = this.scope.pipe !== null && this.scope.pipe !== undefined;
@ -298,6 +314,9 @@ export class SlashCommandClosure {
const closure = v; const closure = v;
closure.scope.parent = this.scope; closure.scope.parent = this.scope;
closure.breakController = this.breakController; closure.breakController = this.breakController;
if (this.debugController && !closure.debugController) {
closure.debugController = this.debugController;
}
if (closure.executeNow) { if (closure.executeNow) {
v = (await closure.execute())?.pipe; v = (await closure.execute())?.pipe;
} else { } else {

View File

@ -117,6 +117,8 @@ export class SlashCommandParser {
/**@type {SlashCommandExecutor[]}*/ commandIndex; /**@type {SlashCommandExecutor[]}*/ commandIndex;
/**@type {SlashCommandScope[]}*/ scopeIndex; /**@type {SlashCommandScope[]}*/ scopeIndex;
/**@type {string}*/ parserContext;
get userIndex() { return this.index; } get userIndex() { return this.index; }
get ahead() { get ahead() {
@ -610,6 +612,7 @@ export class SlashCommandParser {
this.commandIndex = []; this.commandIndex = [];
this.scopeIndex = []; this.scopeIndex = [];
this.macroIndex = []; this.macroIndex = [];
this.parserContext = uuidv4();
const closure = this.parseClosure(true); const closure = this.parseClosure(true);
return closure; return closure;
} }
@ -637,6 +640,8 @@ export class SlashCommandParser {
if (!isRoot) this.take(2); // discard opening {: if (!isRoot) this.take(2); // discard opening {:
const textStart = this.index; const textStart = this.index;
let closure = new SlashCommandClosure(this.scope); let closure = new SlashCommandClosure(this.scope);
closure.parserContext = this.parserContext;
closure.fullText = this.text;
closure.abortController = this.abortController; closure.abortController = this.abortController;
closure.debugController = this.debugController; closure.debugController = this.debugController;
this.scope = closure.scope; this.scope = closure.scope;