mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
4e57ca6833
* Add #! support for executable scripts on Linux. Pass arguments to executable script unchanged if using "#!/usr/bin/env coffee". (Previously, "./test.coffee -abck" would be turned into "-a -b -c -k", for example.) Fixes #1752. * refactor option parsing clean up parsing code and in the process fix oustanding bug where coffeescript modified arguments meant for an executable script * address comments * intermediate save * add note saying where OptionParser is used in coffee command * add some more work * fix flatten functions * refactor tests * make argument processing less confusing * add basic test * remove unused file * compilation now hangs * remove unnecessary changes * add tests!!! * add/fix some tests * clarify a test * fix helpers * fix opt parsing * fix infinite loop * make rule building easier to read * add tests for flag overlap * revamp argument parsing again and add more thorough testing * add tests, comment, clean unused method * address review comments * add test for direct invocation of shebang scripts * move shebang parsing test to separate file and check for browser * remove TODO * example backwards compatible warnings * add correct tests for warning 1 * add tests for warnings * commit output js libs and update docs * respond to review comments also add tests for help text * respond to review comments * fix example output * Rewrite argument parsing documentation to be more concise; add it to sidebar and body; add new output * Don’t mention deprecated syntax; clean up variable names
181 lines
5.6 KiB
JavaScript
181 lines
5.6 KiB
JavaScript
// Generated by CoffeeScript 2.0.0-beta3
|
|
(function() {
|
|
var LONG_FLAG, MULTI_FLAG, OPTIONAL, OptionParser, SHORT_FLAG, buildRule, buildRules, normalizeArguments, repeat,
|
|
slice = [].slice;
|
|
|
|
({repeat} = require('./helpers'));
|
|
|
|
exports.OptionParser = OptionParser = class OptionParser {
|
|
constructor(ruleDeclarations, banner) {
|
|
this.banner = banner;
|
|
this.rules = buildRules(ruleDeclarations);
|
|
}
|
|
|
|
parse(args) {
|
|
var argument, hasArgument, i, isList, len, name, options, positional, rules;
|
|
({rules, positional} = normalizeArguments(args, this.rules.flagDict));
|
|
options = {};
|
|
for (i = 0, len = rules.length; i < len; i++) {
|
|
({hasArgument, argument, isList, name} = rules[i]);
|
|
if (hasArgument) {
|
|
if (isList) {
|
|
if (options[name] == null) {
|
|
options[name] = [];
|
|
}
|
|
options[name].push(argument);
|
|
} else {
|
|
options[name] = argument;
|
|
}
|
|
} else {
|
|
options[name] = true;
|
|
}
|
|
}
|
|
if (positional[0] === '--') {
|
|
options.doubleDashed = true;
|
|
positional = positional.slice(1);
|
|
}
|
|
options.arguments = positional;
|
|
return options;
|
|
}
|
|
|
|
help() {
|
|
var i, len, letPart, lines, ref, rule, spaces;
|
|
lines = [];
|
|
if (this.banner) {
|
|
lines.unshift(`${this.banner}\n`);
|
|
}
|
|
ref = this.rules.ruleList;
|
|
for (i = 0, len = ref.length; i < len; i++) {
|
|
rule = ref[i];
|
|
spaces = 15 - rule.longFlag.length;
|
|
spaces = spaces > 0 ? repeat(' ', spaces) : '';
|
|
letPart = rule.shortFlag ? rule.shortFlag + ', ' : ' ';
|
|
lines.push(' ' + letPart + rule.longFlag + spaces + rule.description);
|
|
}
|
|
return `\n${lines.join('\n')}\n`;
|
|
}
|
|
|
|
};
|
|
|
|
LONG_FLAG = /^(--\w[\w\-]*)/;
|
|
|
|
SHORT_FLAG = /^(-\w)$/;
|
|
|
|
MULTI_FLAG = /^-(\w{2,})/;
|
|
|
|
OPTIONAL = /\[(\w+(\*?))\]/;
|
|
|
|
buildRules = function(ruleDeclarations) {
|
|
var flag, flagDict, i, j, len, len1, ref, rule, ruleList, tuple;
|
|
ruleList = (function() {
|
|
var i, len, results;
|
|
results = [];
|
|
for (i = 0, len = ruleDeclarations.length; i < len; i++) {
|
|
tuple = ruleDeclarations[i];
|
|
if (tuple.length < 3) {
|
|
tuple.unshift(null);
|
|
}
|
|
results.push(buildRule(...tuple));
|
|
}
|
|
return results;
|
|
})();
|
|
flagDict = {};
|
|
for (i = 0, len = ruleList.length; i < len; i++) {
|
|
rule = ruleList[i];
|
|
ref = [rule.shortFlag, rule.longFlag];
|
|
for (j = 0, len1 = ref.length; j < len1; j++) {
|
|
flag = ref[j];
|
|
if (!(flag != null)) {
|
|
continue;
|
|
}
|
|
if (flagDict[flag] != null) {
|
|
throw new Error(`flag ${flag} for switch ${rule.name} was already declared for switch ${flagDict[flag].name}`);
|
|
}
|
|
flagDict[flag] = rule;
|
|
}
|
|
}
|
|
return {ruleList, flagDict};
|
|
};
|
|
|
|
buildRule = function(shortFlag, longFlag, description) {
|
|
var match;
|
|
match = longFlag.match(OPTIONAL);
|
|
shortFlag = shortFlag != null ? shortFlag.match(SHORT_FLAG)[1] : void 0;
|
|
longFlag = longFlag.match(LONG_FLAG)[1];
|
|
return {
|
|
name: longFlag.replace(/^--/, ''),
|
|
shortFlag: shortFlag,
|
|
longFlag: longFlag,
|
|
description: description,
|
|
hasArgument: !!(match && match[1]),
|
|
isList: !!(match && match[2])
|
|
};
|
|
};
|
|
|
|
normalizeArguments = function(args, flagDict) {
|
|
var arg, argIndex, flag, i, innerOpts, j, k, lastOpt, len, len1, multiFlags, multiOpts, needsArgOpt, positional, ref, rule, rules, singleRule, withArg;
|
|
rules = [];
|
|
positional = [];
|
|
needsArgOpt = null;
|
|
for (argIndex = i = 0, len = args.length; i < len; argIndex = ++i) {
|
|
arg = args[argIndex];
|
|
if (needsArgOpt != null) {
|
|
withArg = Object.assign({}, needsArgOpt.rule, {
|
|
argument: arg
|
|
});
|
|
rules.push(withArg);
|
|
needsArgOpt = null;
|
|
continue;
|
|
}
|
|
multiFlags = (ref = arg.match(MULTI_FLAG)) != null ? ref[1].split('').map(function(flagName) {
|
|
return `-${flagName}`;
|
|
}) : void 0;
|
|
if (multiFlags != null) {
|
|
multiOpts = multiFlags.map(function(flag) {
|
|
var rule;
|
|
rule = flagDict[flag];
|
|
if (rule == null) {
|
|
throw new Error(`unrecognized option ${flag} in multi-flag ${arg}`);
|
|
}
|
|
return {rule, flag};
|
|
});
|
|
innerOpts = 2 <= multiOpts.length ? slice.call(multiOpts, 0, j = multiOpts.length - 1) : (j = 0, []), lastOpt = multiOpts[j++];
|
|
for (k = 0, len1 = innerOpts.length; k < len1; k++) {
|
|
({rule, flag} = innerOpts[k]);
|
|
if (rule.hasArgument) {
|
|
throw new Error(`cannot use option ${flag} in multi-flag ${arg} except as the last option, because it needs an argument`);
|
|
}
|
|
rules.push(rule);
|
|
}
|
|
if (lastOpt.rule.hasArgument) {
|
|
needsArgOpt = lastOpt;
|
|
} else {
|
|
rules.push(lastOpt.rule);
|
|
}
|
|
} else if ([LONG_FLAG, SHORT_FLAG].some(function(pat) {
|
|
return arg.match(pat) != null;
|
|
})) {
|
|
singleRule = flagDict[arg];
|
|
if (singleRule == null) {
|
|
throw new Error(`unrecognized option ${arg}`);
|
|
}
|
|
if (singleRule.hasArgument) {
|
|
needsArgOpt = {
|
|
rule: singleRule,
|
|
flag: arg
|
|
};
|
|
} else {
|
|
rules.push(singleRule);
|
|
}
|
|
} else {
|
|
positional = args.slice(argIndex);
|
|
break;
|
|
}
|
|
}
|
|
if (needsArgOpt != null) {
|
|
throw new Error(`value required for ${needsArgOpt.flag}, but it was the last argument provided`);
|
|
}
|
|
return {rules, positional};
|
|
};
|
|
|
|
}).call(this);
|