| 
						 | 
						const { humanReadableArgName } = require('./argument.js');
/** * TypeScript import types for JSDoc, used by Visual Studio Code IntelliSense and `npm run typescript-checkJS` * https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#import-types
 * @typedef { import("./argument.js").Argument } Argument * @typedef { import("./command.js").Command } Command * @typedef { import("./option.js").Option } Option */
// @ts-check
// Although this is a class, methods are static in style to allow override using subclass or just functions.
class Help {  constructor() {    this.helpWidth = undefined;    this.sortSubcommands = false;    this.sortOptions = false;  }
  /**   * Get an array of the visible subcommands. Includes a placeholder for the implicit help command, if there is one.   *   * @param {Command} cmd   * @returns {Command[]}   */
  visibleCommands(cmd) {    const visibleCommands = cmd.commands.filter(cmd => !cmd._hidden);    if (cmd._hasImplicitHelpCommand()) {      // Create a command matching the implicit help command.
      const [, helpName, helpArgs] = cmd._helpCommandnameAndArgs.match(/([^ ]+) *(.*)/);      const helpCommand = cmd.createCommand(helpName)        .helpOption(false);      helpCommand.description(cmd._helpCommandDescription);      if (helpArgs) helpCommand.arguments(helpArgs);      visibleCommands.push(helpCommand);    }    if (this.sortSubcommands) {      visibleCommands.sort((a, b) => {        // @ts-ignore: overloaded return type
        return a.name().localeCompare(b.name());      });    }    return visibleCommands;  }
  /**   * Get an array of the visible options. Includes a placeholder for the implicit help option, if there is one.   *   * @param {Command} cmd   * @returns {Option[]}   */
  visibleOptions(cmd) {    const visibleOptions = cmd.options.filter((option) => !option.hidden);    // Implicit help
    const showShortHelpFlag = cmd._hasHelpOption && cmd._helpShortFlag && !cmd._findOption(cmd._helpShortFlag);    const showLongHelpFlag = cmd._hasHelpOption && !cmd._findOption(cmd._helpLongFlag);    if (showShortHelpFlag || showLongHelpFlag) {      let helpOption;      if (!showShortHelpFlag) {        helpOption = cmd.createOption(cmd._helpLongFlag, cmd._helpDescription);      } else if (!showLongHelpFlag) {        helpOption = cmd.createOption(cmd._helpShortFlag, cmd._helpDescription);      } else {        helpOption = cmd.createOption(cmd._helpFlags, cmd._helpDescription);      }      visibleOptions.push(helpOption);    }    if (this.sortOptions) {      const getSortKey = (option) => {        // WYSIWYG for order displayed in help with short before long, no special handling for negated.
        return option.short ? option.short.replace(/^-/, '') : option.long.replace(/^--/, '');      };      visibleOptions.sort((a, b) => {        return getSortKey(a).localeCompare(getSortKey(b));      });    }    return visibleOptions;  }
  /**   * Get an array of the arguments if any have a description.   *   * @param {Command} cmd   * @returns {Argument[]}   */
  visibleArguments(cmd) {    // Side effect! Apply the legacy descriptions before the arguments are displayed.
    if (cmd._argsDescription) {      cmd._args.forEach(argument => {        argument.description = argument.description || cmd._argsDescription[argument.name()] || '';      });    }
    // If there are any arguments with a description then return all the arguments.
    if (cmd._args.find(argument => argument.description)) {      return cmd._args;    };    return [];  }
  /**   * Get the command term to show in the list of subcommands.   *   * @param {Command} cmd   * @returns {string}   */
  subcommandTerm(cmd) {    // Legacy. Ignores custom usage string, and nested commands.
    const args = cmd._args.map(arg => humanReadableArgName(arg)).join(' ');    return cmd._name +      (cmd._aliases[0] ? '|' + cmd._aliases[0] : '') +      (cmd.options.length ? ' [options]' : '') + // simplistic check for non-help option
      (args ? ' ' + args : '');  }
  /**   * Get the option term to show in the list of options.   *   * @param {Option} option   * @returns {string}   */
  optionTerm(option) {    return option.flags;  }
  /**   * Get the argument term to show in the list of arguments.   *   * @param {Argument} argument   * @returns {string}   */
  argumentTerm(argument) {    return argument.name();  }
  /**   * Get the longest command term length.   *   * @param {Command} cmd   * @param {Help} helper   * @returns {number}   */
  longestSubcommandTermLength(cmd, helper) {    return helper.visibleCommands(cmd).reduce((max, command) => {      return Math.max(max, helper.subcommandTerm(command).length);    }, 0);  };
  /**   * Get the longest option term length.   *   * @param {Command} cmd   * @param {Help} helper   * @returns {number}   */
  longestOptionTermLength(cmd, helper) {    return helper.visibleOptions(cmd).reduce((max, option) => {      return Math.max(max, helper.optionTerm(option).length);    }, 0);  };
  /**   * Get the longest argument term length.   *   * @param {Command} cmd   * @param {Help} helper   * @returns {number}   */
  longestArgumentTermLength(cmd, helper) {    return helper.visibleArguments(cmd).reduce((max, argument) => {      return Math.max(max, helper.argumentTerm(argument).length);    }, 0);  };
  /**   * Get the command usage to be displayed at the top of the built-in help.   *   * @param {Command} cmd   * @returns {string}   */
  commandUsage(cmd) {    // Usage
    let cmdName = cmd._name;    if (cmd._aliases[0]) {      cmdName = cmdName + '|' + cmd._aliases[0];    }    let parentCmdNames = '';    for (let parentCmd = cmd.parent; parentCmd; parentCmd = parentCmd.parent) {      parentCmdNames = parentCmd.name() + ' ' + parentCmdNames;    }    return parentCmdNames + cmdName + ' ' + cmd.usage();  }
  /**   * Get the description for the command.   *   * @param {Command} cmd   * @returns {string}   */
  commandDescription(cmd) {    // @ts-ignore: overloaded return type
    return cmd.description();  }
  /**   * Get the command description to show in the list of subcommands.   *   * @param {Command} cmd   * @returns {string}   */
  subcommandDescription(cmd) {    // @ts-ignore: overloaded return type
    return cmd.description();  }
  /**   * Get the option description to show in the list of options.   *   * @param {Option} option   * @return {string}   */
  optionDescription(option) {    const extraInfo = [];    // Some of these do not make sense for negated boolean and suppress for backwards compatibility.
    if (option.argChoices && !option.negate) {      extraInfo.push(        // use stringify to match the display of the default value
        `choices: ${option.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}`);    }    if (option.defaultValue !== undefined && !option.negate) {      extraInfo.push(`default: ${option.defaultValueDescription || JSON.stringify(option.defaultValue)}`);    }    if (option.envVar !== undefined) {      extraInfo.push(`env: ${option.envVar}`);    }    if (extraInfo.length > 0) {      return `${option.description} (${extraInfo.join(', ')})`;    }
    return option.description;  };
  /**   * Get the argument description to show in the list of arguments.   *   * @param {Argument} argument   * @return {string}   */
  argumentDescription(argument) {    const extraInfo = [];    if (argument.argChoices) {      extraInfo.push(        // use stringify to match the display of the default value
        `choices: ${argument.argChoices.map((choice) => JSON.stringify(choice)).join(', ')}`);    }    if (argument.defaultValue !== undefined) {      extraInfo.push(`default: ${argument.defaultValueDescription || JSON.stringify(argument.defaultValue)}`);    }    if (extraInfo.length > 0) {      const extraDescripton = `(${extraInfo.join(', ')})`;      if (argument.description) {        return `${argument.description} ${extraDescripton}`;      }      return extraDescripton;    }    return argument.description;  }
  /**   * Generate the built-in help text.   *   * @param {Command} cmd   * @param {Help} helper   * @returns {string}   */
  formatHelp(cmd, helper) {    const termWidth = helper.padWidth(cmd, helper);    const helpWidth = helper.helpWidth || 80;    const itemIndentWidth = 2;    const itemSeparatorWidth = 2; // between term and description
    function formatItem(term, description) {      if (description) {        const fullText = `${term.padEnd(termWidth + itemSeparatorWidth)}${description}`;        return helper.wrap(fullText, helpWidth - itemIndentWidth, termWidth + itemSeparatorWidth);      }      return term;    };    function formatList(textArray) {      return textArray.join('\n').replace(/^/gm, ' '.repeat(itemIndentWidth));    }
    // Usage
    let output = [`Usage: ${helper.commandUsage(cmd)}`, ''];
    // Description
    const commandDescription = helper.commandDescription(cmd);    if (commandDescription.length > 0) {      output = output.concat([commandDescription, '']);    }
    // Arguments
    const argumentList = helper.visibleArguments(cmd).map((argument) => {      return formatItem(helper.argumentTerm(argument), helper.argumentDescription(argument));    });    if (argumentList.length > 0) {      output = output.concat(['Arguments:', formatList(argumentList), '']);    }
    // Options
    const optionList = helper.visibleOptions(cmd).map((option) => {      return formatItem(helper.optionTerm(option), helper.optionDescription(option));    });    if (optionList.length > 0) {      output = output.concat(['Options:', formatList(optionList), '']);    }
    // Commands
    const commandList = helper.visibleCommands(cmd).map((cmd) => {      return formatItem(helper.subcommandTerm(cmd), helper.subcommandDescription(cmd));    });    if (commandList.length > 0) {      output = output.concat(['Commands:', formatList(commandList), '']);    }
    return output.join('\n');  }
  /**   * Calculate the pad width from the maximum term length.   *   * @param {Command} cmd   * @param {Help} helper   * @returns {number}   */
  padWidth(cmd, helper) {    return Math.max(      helper.longestOptionTermLength(cmd, helper),      helper.longestSubcommandTermLength(cmd, helper),      helper.longestArgumentTermLength(cmd, helper)    );  };
  /**   * Wrap the given string to width characters per line, with lines after the first indented.   * Do not wrap if insufficient room for wrapping (minColumnWidth), or string is manually formatted.   *   * @param {string} str   * @param {number} width   * @param {number} indent   * @param {number} [minColumnWidth=40]   * @return {string}   *   */
  wrap(str, width, indent, minColumnWidth = 40) {    // Detect manually wrapped and indented strings by searching for line breaks
    // followed by multiple spaces/tabs.
    if (str.match(/[\n]\s+/)) return str;    // Do not wrap if not enough room for a wrapped column of text (as could end up with a word per line).
    const columnWidth = width - indent;    if (columnWidth < minColumnWidth) return str;
    const leadingStr = str.substr(0, indent);    const columnText = str.substr(indent);
    const indentString = ' '.repeat(indent);    const regex = new RegExp('.{1,' + (columnWidth - 1) + '}([\\s\u200B]|$)|[^\\s\u200B]+?([\\s\u200B]|$)', 'g');    const lines = columnText.match(regex) || [];    return leadingStr + lines.map((line, i) => {      if (line.slice(-1) === '\n') {        line = line.slice(0, line.length - 1);      }      return ((i > 0) ? indentString : '') + line.trimRight();    }).join('\n');  }}
exports.Help = Help;
  |