/if allow "rule" and "right" to be optional

aea95adf60b088bfff5e7776a023e1c171ef9144

Wolfsblvt <wolfsblvt@gmail.com>

1 files changed, +34 -16Showing whitespace changes
public/scripts/variables.js+34 -16
@@ -11,7 +11,7 @@ import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCom
11import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';11import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';
12import { PARSER_FLAG, SlashCommandParser } from './slash-commands/SlashCommandParser.js';12import { PARSER_FLAG, SlashCommandParser } from './slash-commands/SlashCommandParser.js';
13import { SlashCommandScope } from './slash-commands/SlashCommandScope.js';13import { SlashCommandScope } from './slash-commands/SlashCommandScope.js';
14import { isFalseBoolean, convertValueType } from './utils.js';14import { isFalseBoolean, convertValueType, isTrueBoolean } from './utils.js';
1515
16/** @typedef {import('./slash-commands/SlashCommandParser.js').NamedArguments} NamedArguments */16/** @typedef {import('./slash-commands/SlashCommandParser.js').NamedArguments} NamedArguments */
17/** @typedef {import('./slash-commands/SlashCommand.js').UnnamedArguments} UnnamedArguments */17/** @typedef {import('./slash-commands/SlashCommand.js').UnnamedArguments} UnnamedArguments */
@@ -463,7 +463,7 @@ function existsGlobalVariable(name) {
463/**463/**
464 * Parses boolean operands from command arguments.464 * Parses boolean operands from command arguments.
465 * @param {object} args Command arguments465 * @param {object} args Command arguments
466 * @returns {{a: string | number, b: string | number, rule: string}} Boolean operands466 * @returns {{a: string | number, b: string | number?, rule: string}} Boolean operands
467 */467 */
468export function parseBooleanOperands(args) {468export function parseBooleanOperands(args) {
469 // Resolution order: numeric literal, local variable, global variable, string literal469 // Resolution order: numeric literal, local variable, global variable, string literal
@@ -472,6 +472,9 @@ export function parseBooleanOperands(args) {
472 */472 */
473 function getOperand(operand) {473 function getOperand(operand) {
474 if (operand === undefined) {474 if (operand === undefined) {
475 return undefined;
476 }
477 if (operand === '') {
475 return '';478 return '';
476 }479 }
477480
@@ -500,9 +503,9 @@ export function parseBooleanOperands(args) {
500 return stringLiteral || '';503 return stringLiteral || '';
501 }504 }
502505
503 const left = getOperand(args.a || args.left || args.first || args.x);506 const left = getOperand(args.a ?? args.left ?? args.first ?? args.x);
504 const right = getOperand(args.b || args.right || args.second || args.y);507 const right = getOperand(args.b ?? args.right ?? args.second ?? args.y);
505 const rule = args.rule;508 const rule = args.rule ?? 'eq';
506509
507 return { a: left, b: right, rule };510 return { a: left, b: right, rule };
508}511}
@@ -511,16 +514,22 @@ export function parseBooleanOperands(args) {
511 * Evaluates a boolean comparison rule.514 * Evaluates a boolean comparison rule.
512 * @param {string} rule Boolean comparison rule515 * @param {string} rule Boolean comparison rule
513 * @param {string|number} a The left operand516 * @param {string|number} a The left operand
514 * @param {string|number} b The right operand517 * @param {string|number?} b The right operand
515 * @returns {boolean} True if the rule yields true, false otherwise518 * @returns {boolean} True if the rule yields true, false otherwise
516 */519 */
517export function evalBoolean(rule, a, b) {520export 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 }
522529
523 let result = false;530 // Restore old behavior, where b cannot be undefined
531 b = b ?? '';
532
524 if (typeof a === 'number' && typeof b === 'number') {533 if (typeof a === 'number' && typeof b === 'number') {
525 // only do numeric comparison if both operands are numbers534 // only do numeric comparison if both operands are numbers
526 const aNumber = Number(a);535 const aNumber = Number(a);
@@ -582,7 +591,7 @@ export function evalBoolean(rule, a, b) {
582 break;591 break;
583 default:592 default:
584 toastr.error('Unknown boolean comparison rule for type string.', 'Invalid /if command');593 toastr.error('Unknown boolean comparison rule for type string.', 'Invalid /if command');
585 throw new Error('Invalid command.');594 throw new Error('Unknown boolean comparison rule for type string.');
586 }595 }
587 }596 }
588597
@@ -1264,21 +1273,18 @@ export function registerVariableCommands() {
1264 typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER],1273 typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER],
1265 isRequired: true,1274 isRequired: true,
1266 enumProvider: commonEnumProviders.variables('all'),1275 enumProvider: commonEnumProviders.variables('all'),
1267 forceEnum: false,
1268 }),1276 }),
1269 SlashCommandNamedArgument.fromProps({1277 SlashCommandNamedArgument.fromProps({
1270 name: 'right',1278 name: 'right',
1271 description: 'right operand',1279 description: 'right operand',
1272 typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER],1280 typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER],
1273 isRequired: true,
1274 enumProvider: commonEnumProviders.variables('all'),1281 enumProvider: commonEnumProviders.variables('all'),
1275 forceEnum: false,
1276 }),1282 }),
1277 SlashCommandNamedArgument.fromProps({1283 SlashCommandNamedArgument.fromProps({
1278 name: 'rule',1284 name: 'rule',
1279 description: 'comparison rule',1285 description: 'comparison rule',
1280 typeList: [ARGUMENT_TYPE.STRING],1286 typeList: [ARGUMENT_TYPE.STRING],
1281 isRequired: true,1287 defaultValue: 'eq',
1282 enumList: [1288 enumList: [
1283 new SlashCommandEnumValue('gt', 'a > b'),1289 new SlashCommandEnumValue('gt', 'a > b'),
1284 new SlashCommandEnumValue('gte', 'a >= b'),1290 new SlashCommandEnumValue('gte', 'a >= b'),
@@ -1290,12 +1296,12 @@ export function registerVariableCommands() {
1290 new SlashCommandEnumValue('in', 'a includes b'),1296 new SlashCommandEnumValue('in', 'a includes b'),
1291 new SlashCommandEnumValue('nin', 'a not includes b'),1297 new SlashCommandEnumValue('nin', 'a not includes b'),
1292 ],1298 ],
1299 forceEnum: true,
1293 }),1300 }),
1294 SlashCommandNamedArgument.fromProps({1301 SlashCommandNamedArgument.fromProps({
1295 name: 'else',1302 name: 'else',
1296 description: 'command to execute if not true',1303 description: 'command to execute if not true',
1297 typeList: [ARGUMENT_TYPE.CLOSURE, ARGUMENT_TYPE.SUBCOMMAND],1304 typeList: [ARGUMENT_TYPE.CLOSURE, ARGUMENT_TYPE.SUBCOMMAND],
1298 isRequired: false,
1299 }),1305 }),
1300 ],1306 ],
1301 unnamedArgumentList: [1307 unnamedArgumentList: [
@@ -1314,6 +1320,11 @@ export function registerVariableCommands() {
1314 Numeric values and string literals for left and right operands supported.1320 Numeric values and string literals for left and right operands supported.
1315 </div>1321 </div>
1316 <div>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 <strong>Available rules:</strong>1328 <strong>Available rules:</strong>
1318 <ul>1329 <ul>
1319 <li>gt => a > b</li>1330 <li>gt => a > b</li>
@@ -1334,6 +1345,13 @@ export function registerVariableCommands() {
1334 <pre><code class="language-stscript">/if left=score right=10 rule=gte "/speak You win"</code></pre>1345 <pre><code class="language-stscript">/if left=score right=10 rule=gte "/speak You win"</code></pre>
1335 triggers a /speak command if the value of "score" is greater or equals 10.1346 triggers a /speak command if the value of "score" is greater or equals 10.
1336 </li>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 </ul>1355 </ul>
1338 </div>1356 </div>
1339 `,1357 `,