Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add methods for formatOptions and formatCommands in Help, with example of option help groups #2248

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions examples/help-option-groups.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
const { Command, Help, Option } = require('commander');

class MyHelp extends Help {
constructor() {
super();
this.optionGroups = {};
}

visibleOptionGroups(cmd, helper) {
const result = { 'Options:': [] };

// Invert the optionGroups object to a map of optionName to groupName.
const groupLookup = new Map();
Object.keys(this.optionGroups)
.concat()
.forEach((title) => {
result[title] = [];
this.optionGroups[title].forEach((optionName) => {
// (only supporting option appearing in one group for now, last one wins)
groupLookup.set(optionName, title);
});
});

// Build list of options in each group title (in order as returned by visibleOptions).
helper.visibleOptions(cmd).forEach((option) => {
const title = groupLookup.get(option.attributeName()) ?? 'Options:';
result[title].push(option);
});

// Remove empty groups.
Object.keys(result).forEach((title) => {
if (result[title].length === 0) {
delete result[title];
}
});

return result;
}

formatOptions(formatItem, formatList, cmd, helper) {
let output = [];
const visibleOptionGroups = helper.visibleOptionGroups(cmd, helper);
Object.keys(visibleOptionGroups).forEach((groupTitle) => {
const optionList = visibleOptionGroups[groupTitle].map((opt) => {
return formatItem(
helper.optionTerm(opt),
helper.optionDescription(opt),
);
});
output = output.concat([groupTitle, formatList(optionList), '']);
});
return output;
}
}

class MyCommand extends Command {
createCommand(name) {
return new MyCommand(name);
}
createHelp() {
return Object.assign(new MyHelp(), this.configureHelp());
}
}

const program = new MyCommand();

program
.option('-c, --carbon')
.option('-r, --rabbit', 'cuddly little friends')
.option('-o, --oxygen')
.addOption(new Option('-s, --sheep'))
.option('--no-sheep') // negated
.option('-n', 'neon') // short
.option('--armadillo') // long
.option('--zzz')
.option('--dog', 'faithful furry companions')
.option('--no-dog');

program.configureHelp({
optionGroups: {
'Animal Options:': ['rabbit', 'armadillo', 'sheep', 'dog'],
'Element Options:': ['carbon', 'oxygen', 'n' /* neon */],
'Help Options:': ['help'],
},
});

program.parse();

// Try the following:
// node help-option-groups.js --help
81 changes: 61 additions & 20 deletions lib/help.js
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,61 @@ class Help {
return argument.description;
}

/**
* Format single item with term and description.
* @callback FormatItemCallback
* @param {string} term
* @param {string} description
* @returns {string}
*/

/**
* Format array of items by joining the lines and indenting each.
* @callback FormatListCallback
* @param {string[]} items
* @returns {string}
*/

/**
* Return formatted options.
*
* @param {FormatItemCallback} formatItem
* @param {FormatListCallback} formatList
* @param {Command} cmd
* @param {Help} helper
* @returns
*/
formatOptions(formatItem, formatList, cmd, helper) {
const optionList = helper.visibleOptions(cmd).map((option) => {
return formatItem(
helper.optionTerm(option),
helper.optionDescription(option),
);
});
if (optionList.length == 0) return [];
return ['Options:', formatList(optionList), ''];
}

/**
* Return formatted Commands.
*
* @param {FormatItemCallback} formatItem
* @param {FormatListCallback} formatList
* @param {Command} cmd
* @param {Help} helper
* @returns
*/
formatCommands(formatItem, formatList, cmd, helper) {
const commandList = helper.visibleCommands(cmd).map((cmd) => {
return formatItem(
helper.subcommandTerm(cmd),
helper.subcommandDescription(cmd),
);
});
if (commandList.length === 0) return [];
return ['Commands:', formatList(commandList), ''];
}

/**
* Generate the built-in help text.
*
Expand Down Expand Up @@ -410,16 +465,9 @@ class Help {
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), '']);
}
output = output.concat(
this.formatOptions(formatItem, formatList, cmd, helper),
);

if (this.showGlobalOptions) {
const globalOptionList = helper
Expand All @@ -439,16 +487,9 @@ class Help {
}
}

// 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), '']);
}
output = output.concat(
this.formatCommands(formatItem, formatList, cmd, helper),
);

return output.join('\n');
}
Expand Down