import { AutoCompleteNameResult } from '../autocomplete/AutoCompleteNameResult.js'; import { AutoCompleteOption } from '../autocomplete/AutoCompleteOption.js'; import { AutoCompleteSecondaryNameResult } from '../autocomplete/AutoCompleteSecondaryNameResult.js'; import { SlashCommand } from './SlashCommand.js'; import { SlashCommandNamedArgument } from './SlashCommandArgument.js'; import { SlashCommandClosure } from './SlashCommandClosure.js'; import { SlashCommandCommandAutoCompleteOption } from './SlashCommandCommandAutoCompleteOption.js'; import { SlashCommandEnumAutoCompleteOption } from './SlashCommandEnumAutoCompleteOption.js'; import { SlashCommandExecutor } from './SlashCommandExecutor.js'; import { SlashCommandNamedArgumentAutoCompleteOption } from './SlashCommandNamedArgumentAutoCompleteOption.js'; export class SlashCommandAutoCompleteNameResult extends AutoCompleteNameResult { /**@type {SlashCommandExecutor}*/ executor; /** * @param {SlashCommandExecutor} executor * @param {Object.} commands */ constructor(executor, commands) { super( executor.name, executor.start, Object .keys(commands) .map(key=>new SlashCommandCommandAutoCompleteOption(commands[key], key)) , false, ()=>`No matching slash commands for "/${this.name}"`, ()=>'No slash commands found!', ); this.executor = executor; } getSecondaryNameAt(text, index, isSelect) { const namedResult = this.getNamedArgumentAt(text, index, isSelect); if (!namedResult || namedResult.optionList.length == 0 || !namedResult.isRequired) { const unnamedResult = this.getUnnamedArgumentAt(text, index, isSelect); if (!namedResult) return unnamedResult; if (namedResult && unnamedResult) { const combinedResult = new AutoCompleteSecondaryNameResult( namedResult.name, namedResult.start, [...namedResult.optionList, ...unnamedResult.optionList], ); combinedResult.isRequired = namedResult.isRequired || unnamedResult.isRequired; return combinedResult; } } return namedResult; } getNamedArgumentAt(text, index, isSelect) { function getSplitRegex() { try { return new RegExp('(?<==)'); } catch { // For browsers that don't support lookbehind return new RegExp('=(.*)'); } } if (!Array.isArray(this.executor.command?.namedArgumentList)) { return null; } const notProvidedNamedArguments = this.executor.command.namedArgumentList.filter(arg=>!this.executor.namedArgumentList.find(it=>it.name == arg.name)); let name; let value; let start; let cmdArg; let argAssign; const unamedArgLength = this.executor.endUnnamedArgs - this.executor.startUnnamedArgs; const namedArgsFollowedBySpace = text[this.executor.endNamedArgs] == ' '; if (this.executor.startNamedArgs <= index && this.executor.endNamedArgs + (namedArgsFollowedBySpace ? 1 : 0) >= index) { // cursor is somewhere within the named arguments (including final space) argAssign = this.executor.namedArgumentList.find(it=>it.start <= index && it.end >= index); if (argAssign) { const [argName, ...v] = text.slice(argAssign.start, index).split(getSplitRegex()); name = argName; value = v.join(''); start = argAssign.start; cmdArg = this.executor.command.namedArgumentList.find(it=>[it.name, `${it.name}=`].includes(argAssign.name)); if (cmdArg) notProvidedNamedArguments.push(cmdArg); } else { name = ''; start = index; } } else if (unamedArgLength > 0 && index >= this.executor.startUnnamedArgs && index <= this.executor.endUnnamedArgs) { // cursor is somewhere within the unnamed arguments //TODO if index is in first array item and that is a string, treat it as an unfinished named arg if (typeof this.executor.unnamedArgumentList[0].value == 'string') { if (index <= this.executor.startUnnamedArgs + this.executor.unnamedArgumentList[0].value.length) { name = this.executor.unnamedArgumentList[0].value.slice(0, index - this.executor.startUnnamedArgs); start = this.executor.startUnnamedArgs; } else { return null; } } else { return null; } } else { return null; } if (name.includes('=') && cmdArg) { // if cursor is already behind "=" check for enums /**@type {SlashCommandNamedArgument} */ if (cmdArg && cmdArg.enumList?.length) { if (isSelect && cmdArg.enumList.includes(value) && argAssign && argAssign.end == index) { return null; } const result = new AutoCompleteSecondaryNameResult( value, start + name.length, cmdArg.enumList.map(it=>new SlashCommandEnumAutoCompleteOption(this.executor.command, it)), true, ); result.isRequired = true; return result; } } if (notProvidedNamedArguments.length > 0) { const result = new AutoCompleteSecondaryNameResult( name, start, notProvidedNamedArguments.map(it=>new SlashCommandNamedArgumentAutoCompleteOption(it, this.executor.command)), false, ); result.isRequired = notProvidedNamedArguments.find(it=>it.isRequired) != null; return result; } return null; } getUnnamedArgumentAt(text, index, isSelect) { if (!Array.isArray(this.executor.command?.unnamedArgumentList)) { return null; } const lastArgIsBlank = this.executor.unnamedArgumentList.slice(-1)[0]?.value == ''; const notProvidedArguments = this.executor.command.unnamedArgumentList.slice(this.executor.unnamedArgumentList.length - (lastArgIsBlank ? 1 : 0)); let value; let start; let cmdArg; let argAssign; if (this.executor.startUnnamedArgs <= index && this.executor.endUnnamedArgs + 1 >= index) { // cursor is somwehere in the unnamed args const idx = this.executor.unnamedArgumentList.findIndex(it=>it.start <= index && it.end >= index); if (idx > -1) { argAssign = this.executor.unnamedArgumentList[idx]; cmdArg = this.executor.command.unnamedArgumentList[idx]; if (cmdArg && cmdArg.enumList.length > 0) { value = argAssign.value.toString().slice(0, index - argAssign.start); start = argAssign.start; } else { return null; } } else { value = ''; start = index; cmdArg = notProvidedArguments[0]; } } else { return null; } if (cmdArg == null || cmdArg.enumList.length == 0) return null; const result = new AutoCompleteSecondaryNameResult( value, start, cmdArg.enumList.map(it=>new SlashCommandEnumAutoCompleteOption(this.executor.command, it)), false, ); const isCompleteValue = cmdArg.enumList.find(it=>it.value == value); const isSelectedValue = isSelect && isCompleteValue; result.isRequired = cmdArg.isRequired && !isSelectedValue && !isCompleteValue; return result; } }