795 lines · 21.5 KB · JavaScript
RawDownload
/** * class HelpFormatter * * Formatter for generating usage messages and argument help strings. Only the * name of this class is considered a public API. All the methods provided by * the class are considered an implementation detail. * * Do not call in your code, use this class only for inherits your own forvatter * * ToDo add [additonal formatters][1] * * [1]:http://docs.python.org/dev/library/argparse.html#formatter-class **/'use strict'; var sprintf = require('sprintf-js').sprintf; // Constantsvar c = require('../const'); var $$ = require('../utils');  /*:nodoc:* internal * new Support(parent, heding) * - parent (object): parent section * - heading (string): header string * **/function Section(parent, heading) {  this._parent = parent;  this._heading = heading;  this._items = [];} /*:nodoc:* internal * Section#addItem(callback) -> Void * - callback (array): tuple with function and args * * Add function for single element **/Section.prototype.addItem = function (callback) {  this._items.push(callback);}; /*:nodoc:* internal * Section#formatHelp(formatter) -> string * - formatter (HelpFormatter): current formatter * * Form help section string * **/Section.prototype.formatHelp = function (formatter) {  var itemHelp, heading;   // format the indented section  if (this._parent) {    formatter._indent();  }   itemHelp = this._items.map(function (item) {    var obj, func, args;     obj = formatter;    func = item[0];    args = item[1];    return func.apply(obj, args);  });  itemHelp = formatter._joinParts(itemHelp);   if (this._parent) {    formatter._dedent();  }   // return nothing if the section was empty  if (!itemHelp) {    return '';  }   // add the heading if the section was non-empty  heading = '';  if (this._heading && this._heading !== c.SUPPRESS) {    var currentIndent = formatter.currentIndent;    heading = $$.repeat(' ', currentIndent) + this._heading + ':' + c.EOL;  }   // join the section-initialize newline, the heading and the help  return formatter._joinParts([ c.EOL, heading, itemHelp, c.EOL ]);}; /** * new HelpFormatter(options) * * #### Options: * - `prog`: program name * - `indentIncriment`: indent step, default value 2 * - `maxHelpPosition`: max help position, default value = 24 * - `width`: line width * **/var HelpFormatter = module.exports = function HelpFormatter(options) {  options = options || {};   this._prog = options.prog;   this._maxHelpPosition = options.maxHelpPosition || 24;  this._width = (options.width || ((process.env.COLUMNS || 80) - 2));   this._currentIndent = 0;  this._indentIncriment = options.indentIncriment || 2;  this._level = 0;  this._actionMaxLength = 0;   this._rootSection = new Section(null);  this._currentSection = this._rootSection;   this._whitespaceMatcher = new RegExp('\\s+', 'g');  this._longBreakMatcher = new RegExp(c.EOL + c.EOL + c.EOL + '+', 'g');}; HelpFormatter.prototype._indent = function () {  this._currentIndent += this._indentIncriment;  this._level += 1;}; HelpFormatter.prototype._dedent = function () {  this._currentIndent -= this._indentIncriment;  this._level -= 1;  if (this._currentIndent < 0) {    throw new Error('Indent decreased below 0.');  }}; HelpFormatter.prototype._addItem = function (func, args) {  this._currentSection.addItem([ func, args ]);}; //// Message building methods// /** * HelpFormatter#startSection(heading) -> Void * - heading (string): header string * * Start new help section * * See alse [code example][1] * * ##### Example * *      formatter.startSection(actionGroup.title); *      formatter.addText(actionGroup.description); *      formatter.addArguments(actionGroup._groupActions); *      formatter.endSection(); * **/HelpFormatter.prototype.startSection = function (heading) {  this._indent();  var section = new Section(this._currentSection, heading);  var func = section.formatHelp.bind(section);  this._addItem(func, [ this ]);  this._currentSection = section;}; /** * HelpFormatter#endSection -> Void * * End help section * * ##### Example * *      formatter.startSection(actionGroup.title); *      formatter.addText(actionGroup.description); *      formatter.addArguments(actionGroup._groupActions); *      formatter.endSection(); **/HelpFormatter.prototype.endSection = function () {  this._currentSection = this._currentSection._parent;  this._dedent();}; /** * HelpFormatter#addText(text) -> Void * - text (string): plain text * * Add plain text into current section * * ##### Example * *      formatter.startSection(actionGroup.title); *      formatter.addText(actionGroup.description); *      formatter.addArguments(actionGroup._groupActions); *      formatter.endSection(); * **/HelpFormatter.prototype.addText = function (text) {  if (text && text !== c.SUPPRESS) {    this._addItem(this._formatText, [ text ]);  }}; /** * HelpFormatter#addUsage(usage, actions, groups, prefix) -> Void * - usage (string): usage text * - actions (array): actions list * - groups (array): groups list * - prefix (string): usage prefix * * Add usage data into current section * * ##### Example * *      formatter.addUsage(this.usage, this._actions, []); *      return formatter.formatHelp(); * **/HelpFormatter.prototype.addUsage = function (usage, actions, groups, prefix) {  if (usage !== c.SUPPRESS) {    this._addItem(this._formatUsage, [ usage, actions, groups, prefix ]);  }}; /** * HelpFormatter#addArgument(action) -> Void * - action (object): action * * Add argument into current section * * Single variant of [[HelpFormatter#addArguments]] **/HelpFormatter.prototype.addArgument = function (action) {  if (action.help !== c.SUPPRESS) {    var self = this;     // find all invocations    var invocations = [ this._formatActionInvocation(action) ];    var invocationLength = invocations[0].length;     var actionLength;     if (action._getSubactions) {      this._indent();      action._getSubactions().forEach(function (subaction) {         var invocationNew = self._formatActionInvocation(subaction);        invocations.push(invocationNew);        invocationLength = Math.max(invocationLength, invocationNew.length);       });      this._dedent();    }     // update the maximum item length    actionLength = invocationLength + this._currentIndent;    this._actionMaxLength = Math.max(this._actionMaxLength, actionLength);     // add the item to the list    this._addItem(this._formatAction, [ action ]);  }}; /** * HelpFormatter#addArguments(actions) -> Void * - actions (array): actions list * * Mass add arguments into current section * * ##### Example * *      formatter.startSection(actionGroup.title); *      formatter.addText(actionGroup.description); *      formatter.addArguments(actionGroup._groupActions); *      formatter.endSection(); * **/HelpFormatter.prototype.addArguments = function (actions) {  var self = this;  actions.forEach(function (action) {    self.addArgument(action);  });}; //// Help-formatting methods// /** * HelpFormatter#formatHelp -> string * * Format help * * ##### Example * *      formatter.addText(this.epilog); *      return formatter.formatHelp(); * **/HelpFormatter.prototype.formatHelp = function () {  var help = this._rootSection.formatHelp(this);  if (help) {    help = help.replace(this._longBreakMatcher, c.EOL + c.EOL);    help = $$.trimChars(help, c.EOL) + c.EOL;  }  return help;}; HelpFormatter.prototype._joinParts = function (partStrings) {  return partStrings.filter(function (part) {    return (part && part !== c.SUPPRESS);  }).join('');}; HelpFormatter.prototype._formatUsage = function (usage, actions, groups, prefix) {  if (!prefix && typeof prefix !== 'string') {    prefix = 'usage: ';  }   actions = actions || [];  groups = groups || [];    // if usage is specified, use that  if (usage) {    usage = sprintf(usage, { prog: this._prog });     // if no optionals or positionals are available, usage is just prog  } else if (!usage && actions.length === 0) {    usage = this._prog;     // if optionals and positionals are available, calculate usage  } else if (!usage) {    var prog = this._prog;    var optionals = [];    var positionals = [];    var actionUsage;    var textWidth;     // split optionals from positionals    actions.forEach(function (action) {      if (action.isOptional()) {        optionals.push(action);      } else {        positionals.push(action);      }    });     // build full usage string    actionUsage = this._formatActionsUsage([].concat(optionals, positionals), groups);    usage = [ prog, actionUsage ].join(' ');     // wrap the usage parts if it's too long    textWidth = this._width - this._currentIndent;    if ((prefix.length + usage.length) > textWidth) {       // break usage into wrappable parts      var regexpPart = new RegExp('\\(.*?\\)+|\\[.*?\\]+|\\S+', 'g');      var optionalUsage = this._formatActionsUsage(optionals, groups);      var positionalUsage = this._formatActionsUsage(positionals, groups);        var optionalParts = optionalUsage.match(regexpPart);      var positionalParts = positionalUsage.match(regexpPart) || [];       if (optionalParts.join(' ') !== optionalUsage) {        throw new Error('assert "optionalParts.join(\' \') === optionalUsage"');      }      if (positionalParts.join(' ') !== positionalUsage) {        throw new Error('assert "positionalParts.join(\' \') === positionalUsage"');      }       // helper for wrapping lines      /*eslint-disable func-style*/ // node 0.10 compat      var _getLines = function (parts, indent, prefix) {        var lines = [];        var line = [];         var lineLength = prefix ? prefix.length - 1 : indent.length - 1;         parts.forEach(function (part) {          if (lineLength + 1 + part.length > textWidth) {            lines.push(indent + line.join(' '));            line = [];            lineLength = indent.length - 1;          }          line.push(part);          lineLength += part.length + 1;        });         if (line) {          lines.push(indent + line.join(' '));        }        if (prefix) {          lines[0] = lines[0].substr(indent.length);        }        return lines;      };       var lines, indent, parts;      // if prog is short, follow it with optionals or positionals      if (prefix.length + prog.length <= 0.75 * textWidth) {        indent = $$.repeat(' ', (prefix.length + prog.length + 1));        if (optionalParts) {          lines = [].concat(            _getLines([ prog ].concat(optionalParts), indent, prefix),            _getLines(positionalParts, indent)          );        } else if (positionalParts) {          lines = _getLines([ prog ].concat(positionalParts), indent, prefix);        } else {          lines = [ prog ];        }         // if prog is long, put it on its own line      } else {        indent = $$.repeat(' ', prefix.length);        parts = optionalParts.concat(positionalParts);        lines = _getLines(parts, indent);        if (lines.length > 1) {          lines = [].concat(            _getLines(optionalParts, indent),            _getLines(positionalParts, indent)          );        }        lines = [ prog ].concat(lines);      }      // join lines into usage      usage = lines.join(c.EOL);    }  }   // prefix with 'usage:'  return prefix + usage + c.EOL + c.EOL;}; HelpFormatter.prototype._formatActionsUsage = function (actions, groups) {  // find group indices and identify actions in groups  var groupActions = [];  var inserts = [];  var self = this;   groups.forEach(function (group) {    var end;    var i;     var start = actions.indexOf(group._groupActions[0]);    if (start >= 0) {      end = start + group._groupActions.length;       //if (actions.slice(start, end) === group._groupActions) {      if ($$.arrayEqual(actions.slice(start, end), group._groupActions)) {        group._groupActions.forEach(function (action) {          groupActions.push(action);        });         if (!group.required) {          if (inserts[start]) {            inserts[start] += ' [';          } else {            inserts[start] = '[';          }          inserts[end] = ']';        } else {          if (inserts[start]) {            inserts[start] += ' (';          } else {            inserts[start] = '(';          }          inserts[end] = ')';        }        for (i = start + 1; i < end; i += 1) {          inserts[i] = '|';        }      }    }  });   // collect all actions format strings  var parts = [];   actions.forEach(function (action, actionIndex) {    var part;    var optionString;    var argsDefault;    var argsString;     // suppressed arguments are marked with None    // remove | separators for suppressed arguments    if (action.help === c.SUPPRESS) {      parts.push(null);      if (inserts[actionIndex] === '|') {        inserts.splice(actionIndex, actionIndex);      } else if (inserts[actionIndex + 1] === '|') {        inserts.splice(actionIndex + 1, actionIndex + 1);      }       // produce all arg strings    } else if (!action.isOptional()) {      part = self._formatArgs(action, action.dest); 

Lines 1–500 of 795.