/if allow "rule" and "right" to be optional
| @@ -11,7 +11,7 @@ import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCom | ||
| 11 | 11 | import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js'; |
| 12 | 12 | import { PARSER_FLAG, SlashCommandParser } from './slash-commands/SlashCommandParser.js'; |
| 13 | 13 | import { SlashCommandScope } from './slash-commands/SlashCommandScope.js'; |
| 14 | 14 | import { isFalseBoolean, convertValueType, isTrueBoolean } from './utils.js'; |
| 15 | 15 | |
| 16 | 16 | /** @typedef {import('./slash-commands/SlashCommandParser.js').NamedArguments} NamedArguments */ |
| 17 | 17 | /** @typedef {import('./slash-commands/SlashCommand.js').UnnamedArguments} UnnamedArguments */ |
| @@ -463,7 +463,7 @@ function existsGlobalVariable(name) { | ||
| 463 | 463 | /** |
| 464 | 464 | * Parses boolean operands from command arguments. |
| 465 | 465 | * @param {object} args Command arguments |
| 466 | 466 | * @returns {{a: string | number, b: string | number?, rule: string}} Boolean operands |
| 467 | 467 | */ |
| 468 | 468 | export function parseBooleanOperands(args) { |
| 469 | 469 | // Resolution order: numeric literal, local variable, global variable, string literal |
| @@ -472,6 +472,9 @@ export function parseBooleanOperands(args) { | ||
| 472 | 472 | */ |
| 473 | 473 | function getOperand(operand) { |
| 474 | 474 | if (operand === undefined) { |
| 475 | + return undefined; | |
| 476 | + } | |
| 477 | + if (operand === '') { | |
| 475 | 478 | return ''; |
| 476 | 479 | } |
| 477 | 480 | |
| @@ -500,9 +503,9 @@ export function parseBooleanOperands(args) { | ||
| 500 | 503 | return stringLiteral || ''; |
| 501 | 504 | } |
| 502 | 505 | |
| 503 | 506 | const left = getOperand(args.a ||?? args.left ||?? args.first ||?? args.x); |
| 504 | 507 | const right = getOperand(args.b ||?? args.right ||?? args.second ||?? args.y); |
| 505 | 508 | const rule = args.rule ?? 'eq'; |
| 506 | 509 | |
| 507 | 510 | return { a: left, b: right, rule }; |
| 508 | 511 | } |
| @@ -511,16 +514,22 @@ export function parseBooleanOperands(args) { | ||
| 511 | 514 | * Evaluates a boolean comparison rule. |
| 512 | 515 | * @param {string} rule Boolean comparison rule |
| 513 | 516 | * @param {string|number} a The left operand |
| 514 | 517 | * @param {string|number?} b The right operand |
| 515 | 518 | * @returns {boolean} True if the rule yields true, false otherwise |
| 516 | 519 | */ |
| 517 | 520 | export function evalBoolean(rule, a, b) { |
| 518 | - if (!rule) { | |
| 521 | + let result = false; | |
| 519 | - toastr.warning('The rule must be specified for the boolean comparison.', 'Invalid command'); | |
| 522 | + | |
| 520 | - throw new Error('Invalid command.'); | |
| 523 | + if (b === undefined && rule === 'eq') { | |
| 524 | + // If right-hand side was not provided, whe just check if the left side is truthy | |
| 525 | + if (isTrueBoolean(String(a))) return true; | |
| 526 | + if (isFalseBoolean(String(a))) return false; | |
| 527 | + return !!a; | |
| 521 | 528 | } |
| 522 | 529 | |
| 523 | - let result = false; | |
| 530 | + // Restore old behavior, where b cannot be undefined | |
| 531 | + b = b ?? ''; | |
| 532 | + | |
| 524 | 533 | if (typeof a === 'number' && typeof b === 'number') { |
| 525 | 534 | // only do numeric comparison if both operands are numbers |
| 526 | 535 | const aNumber = Number(a); |
| @@ -582,7 +591,7 @@ export function evalBoolean(rule, a, b) { | ||
| 582 | 591 | break; |
| 583 | 592 | default: |
| 584 | 593 | toastr.error('Unknown boolean comparison rule for type string.', 'Invalid /if command'); |
| 585 | 594 | throw new Error('InvalidUnknown commandboolean comparison rule for type string.'); |
| 586 | 595 | } |
| 587 | 596 | } |
| 588 | 597 | |
| @@ -1264,21 +1273,18 @@ export function registerVariableCommands() { | ||
| 1264 | 1273 | typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER], |
| 1265 | 1274 | isRequired: true, |
| 1266 | 1275 | enumProvider: commonEnumProviders.variables('all'), |
| 1267 | - forceEnum: false, | |
| 1268 | 1276 | }), |
| 1269 | 1277 | SlashCommandNamedArgument.fromProps({ |
| 1270 | 1278 | name: 'right', |
| 1271 | 1279 | description: 'right operand', |
| 1272 | 1280 | typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER], |
| 1273 | - isRequired: true, | |
| 1274 | 1281 | enumProvider: commonEnumProviders.variables('all'), |
| 1275 | - forceEnum: false, | |
| 1276 | 1282 | }), |
| 1277 | 1283 | SlashCommandNamedArgument.fromProps({ |
| 1278 | 1284 | name: 'rule', |
| 1279 | 1285 | description: 'comparison rule', |
| 1280 | 1286 | typeList: [ARGUMENT_TYPE.STRING], |
| 1281 | 1287 | isRequireddefaultValue: true'eq', |
| 1282 | 1288 | enumList: [ |
| 1283 | 1289 | new SlashCommandEnumValue('gt', 'a > b'), |
| 1284 | 1290 | new SlashCommandEnumValue('gte', 'a >= b'), |
| @@ -1290,12 +1296,12 @@ export function registerVariableCommands() { | ||
| 1290 | 1296 | new SlashCommandEnumValue('in', 'a includes b'), |
| 1291 | 1297 | new SlashCommandEnumValue('nin', 'a not includes b'), |
| 1292 | 1298 | ], |
| 1299 | + forceEnum: true, | |
| 1293 | 1300 | }), |
| 1294 | 1301 | SlashCommandNamedArgument.fromProps({ |
| 1295 | 1302 | name: 'else', |
| 1296 | 1303 | description: 'command to execute if not true', |
| 1297 | 1304 | typeList: [ARGUMENT_TYPE.CLOSURE, ARGUMENT_TYPE.SUBCOMMAND], |
| 1298 | - isRequired: false, | |
| 1299 | 1305 | }), |
| 1300 | 1306 | ], |
| 1301 | 1307 | unnamedArgumentList: [ |
| @@ -1314,6 +1320,11 @@ export function registerVariableCommands() { | ||
| 1314 | 1320 | Numeric values and string literals for left and right operands supported. |
| 1315 | 1321 | </div> |
| 1316 | 1322 | <div> |
| 1323 | + If the rule is not provided, it defaults to <code>eq</code>.<br /> | |
| 1324 | + If no right operand is provided, it defaults to checking the <code>left</code> value to be truthy. | |
| 1325 | + A non-empty string or non-zero number is considered truthy, as is the value <code>true</code>. | |
| 1326 | + </div> | |
| 1327 | + <div> | |
| 1317 | 1328 | <strong>Available rules:</strong> |
| 1318 | 1329 | <ul> |
| 1319 | 1330 | <li>gt => a > b</li> |
| @@ -1334,6 +1345,13 @@ export function registerVariableCommands() { | ||
| 1334 | 1345 | <pre><code class="language-stscript">/if left=score right=10 rule=gte "/speak You win"</code></pre> |
| 1335 | 1346 | triggers a /speak command if the value of "score" is greater or equals 10. |
| 1336 | 1347 | </li> |
| 1348 | + <li> | |
| 1349 | + <pre><code class="language-stscript">/if left={{lastMessage}} rule=in right=surprise {: /echo SURPISE! :}</code></pre> | |
| 1350 | + executes a subcommand defined as a closure if the given value contains a specified word. | |
| 1351 | + <li> | |
| 1352 | + <pre><code class="language-stscript">/if left=myContent {: /echo "My content had some content." :}</code></pre> | |
| 1353 | + executes the defined subcommand, if the provided value of left is truthy (contains some kind of contant that is not empty or false) | |
| 1354 | + </li> | |
| 1337 | 1355 | </ul> |
| 1338 | 1356 | </div> |
| 1339 | 1357 | `, |