Merge branch 'st-parser-accept-multiple-named' into char-find-and-sendas-extended
| @@ -15,13 +15,13 @@ import { SlashCommandScope } from './SlashCommandScope.js'; | ||
| 15 | 15 | * _abortController:SlashCommandAbortController, |
| 16 | 16 | * _debugController:SlashCommandDebugController, |
| 17 | 17 | * _hasUnnamedArgument:boolean, |
| 18 | 18 | * [id:string]:string|SlashCommandClosure|(string|SlashCommandClosure)[], |
| 19 | 19 | * }} NamedArguments |
| 20 | 20 | */ |
| 21 | 21 | |
| 22 | 22 | /** |
| 23 | 23 | * Alternative object for local JSDocs, where you don't need existing pipe, scope, etc. arguments |
| 24 | 24 | * @typedef {{[id:string]:string|SlashCommandClosure|(string|SlashCommandClosure)[]}} NamedArgumentsCapture |
| 25 | 25 | */ |
| 26 | 26 | |
| 27 | 27 | /** |
| @@ -2,6 +2,7 @@ import { substituteParams } from '../../script.js'; | ||
| 2 | 2 | import { delay, escapeRegex, uuidv4 } from '../utils.js'; |
| 3 | 3 | import { SlashCommand } from './SlashCommand.js'; |
| 4 | 4 | import { SlashCommandAbortController } from './SlashCommandAbortController.js'; |
| 5 | +import { SlashCommandNamedArgument } from './SlashCommandArgument.js'; | |
| 5 | 6 | import { SlashCommandBreak } from './SlashCommandBreak.js'; |
| 6 | 7 | import { SlashCommandBreakController } from './SlashCommandBreakController.js'; |
| 7 | 8 | import { SlashCommandBreakPoint } from './SlashCommandBreakPoint.js'; |
| @@ -53,7 +54,7 @@ export class SlashCommandClosure { | ||
| 53 | 54 | * |
| 54 | 55 | * @param {string} text |
| 55 | 56 | * @param {SlashCommandScope} scope |
| 56 | - * @returns | |
| 57 | + * @returns {string|SlashCommandClosure|(string|SlashCommandClosure)[]} | |
| 57 | 58 | */ |
| 58 | 59 | substituteParams(text, scope = null) { |
| 59 | 60 | let isList = false; |
| @@ -379,6 +380,52 @@ export class SlashCommandClosure { | ||
| 379 | 380 | * @param {import('./SlashCommand.js').NamedArguments} args |
| 380 | 381 | */ |
| 381 | 382 | async substituteNamedArguments(executor, args) { |
| 383 | + /** | |
| 384 | + * Handles the assignment of named arguments, considering if they accept multiple values | |
| 385 | + * @param {string} name The name of the argument, as defined for the command execution | |
| 386 | + * @param {string|SlashCommandClosure|(string|SlashCommandClosure)[]} value The value to be assigned | |
| 387 | + */ | |
| 388 | + const assign = (name, value) => { | |
| 389 | + // If an array is supposed to be assigned, assign it one by one | |
| 390 | + if (Array.isArray(value)) { | |
| 391 | + for (const val of value) { | |
| 392 | + assign(name, val); | |
| 393 | + } | |
| 394 | + return; | |
| 395 | + } | |
| 396 | + | |
| 397 | + const definition = executor.command.namedArgumentList.find(x => x.name == name); | |
| 398 | + | |
| 399 | + // Prefer definition name if a valid named args defintion is found | |
| 400 | + name = definition?.name ?? name; | |
| 401 | + | |
| 402 | + // Unescape named argument | |
| 403 | + if (value && typeof value == 'string') { | |
| 404 | + value = value | |
| 405 | + .replace(/\\\{/g, '{') | |
| 406 | + .replace(/\\\}/g, '}'); | |
| 407 | + } | |
| 408 | + | |
| 409 | + // If the named argument accepts multiple values, we have to make sure to build an array correctly | |
| 410 | + if (definition?.acceptsMultiple) { | |
| 411 | + if (args[name] !== undefined) { | |
| 412 | + // If there already is something for that named arg, make the value is an array and add to it | |
| 413 | + let currentValue = args[name]; | |
| 414 | + if (!Array.isArray(currentValue)) { | |
| 415 | + currentValue = [currentValue]; | |
| 416 | + } | |
| 417 | + currentValue.push(value); | |
| 418 | + args[name] = currentValue; | |
| 419 | + } else { | |
| 420 | + // If there is nothing in there, we create an array with that singular value | |
| 421 | + args[name] = [value]; | |
| 422 | + } | |
| 423 | + } else { | |
| 424 | + args[name] !== undefined && console.debug(`Named argument assigned multiple times: ${name}`); | |
| 425 | + args[name] = value; | |
| 426 | + } | |
| 427 | + }; | |
| 428 | + | |
| 382 | 429 | // substitute named arguments |
| 383 | 430 | for (const arg of executor.namedArgumentList) { |
| 384 | 431 | if (arg.value instanceof SlashCommandClosure) { |
| @@ -390,19 +437,12 @@ export class SlashCommandClosure { | ||
| 390 | 437 | closure.debugController = this.debugController; |
| 391 | 438 | } |
| 392 | 439 | if (closure.executeNow) { |
| 393 | 440 | args[assign(arg.name] =, (await closure.execute())?.pipe); |
| 394 | 441 | } else { |
| 395 | 442 | args[assign(arg.name] =, closure); |
| 396 | 443 | } |
| 397 | 444 | } else { |
| 398 | 445 | args[assign(arg.name] =, this.substituteParams(arg.value)); |
| 399 | - } | |
| 400 | - // unescape named argument | |
| 401 | - if (typeof args[arg.name] == 'string') { | |
| 402 | - args[arg.name] = args[arg.name] | |
| 403 | - ?.replace(/\\\{/g, '{') | |
| 404 | - ?.replace(/\\\}/g, '}') | |
| 405 | - ; | |
| 406 | 446 | } |
| 407 | 447 | } |
| 408 | 448 | } |
| @@ -424,6 +464,7 @@ export class SlashCommandClosure { | ||
| 424 | 464 | } else { |
| 425 | 465 | value = []; |
| 426 | 466 | for (let i = 0; i < executor.unnamedArgumentList.length; i++) { |
| 467 | + /** @type {string|SlashCommandClosure|(string|SlashCommandClosure)[]} */ | |
| 427 | 468 | let v = executor.unnamedArgumentList[i].value; |
| 428 | 469 | if (v instanceof SlashCommandClosure) { |
| 429 | 470 | /**@type {SlashCommandClosure}*/ |