var _ = require('underscore'); var intl = require('../intl'); var Errors = require('../util/errors'); var CommandProcessError = Errors.CommandProcessError; var GitError = Errors.GitError; var Warning = Errors.Warning; var CommandResult = Errors.CommandResult; var shortcutMap = { 'git commit': /^(gc|git ci)($|\s)/, 'git add': /^ga($|\s)/, 'git checkout': /^(go|git co)($|\s)/, 'git rebase': /^gr($|\s)/, 'git branch': /^(gb|git br)($|\s)/, 'git status': /^(gst|gs|git st)($|\s)/, 'git help': /^git$/ }; var instantCommands = [ [/^git help($|\s)/, function() { var lines = [ intl.str('git-version'), '
', intl.str('git-usage'), _.escape(intl.str('git-usage-command')), '
', intl.str('git-supported-commands'), '
' ]; var commands = GitOptionParser.prototype.getMasterOptionMap(); // build up a nice display of what we support _.each(commands, function(commandOptions, command) { lines.push('git ' + command); _.each(commandOptions, function(vals, optionName) { lines.push('\t ' + optionName); }, this); }, this); // format and throw var msg = lines.join('\n'); msg = msg.replace(/\t/g, '   '); throw new CommandResult({ msg: msg }); }] ]; var regexMap = { // ($|\s) means that we either have to end the string // after the command or there needs to be a space for options 'git commit': /^git +commit($|\s)/, 'git add': /^git +add($|\s)/, 'git checkout': /^git +checkout($|\s)/, 'git rebase': /^git +rebase($|\s)/, 'git reset': /^git +reset($|\s)/, 'git branch': /^git +branch($|\s)/, 'git revert': /^git +revert($|\s)/, 'git log': /^git +log($|\s)/, 'git merge': /^git +merge($|\s)/, 'git show': /^git +show($|\s)/, 'git status': /^git +status($|\s)/, 'git cherry-pick': /^git +cherry-pick($|\s)/, 'git fakeTeamwork': /^git +fakeTeamwork($|\s)/, 'git fetch': /^git +fetch *?$/, 'git pull': /^git +pull($|\s)/, 'git clone': /^git +clone *?$/ }; var parse = function(str) { var method; var options; // see if we support this particular command _.each(regexMap, function(regex, thisMethod) { if (regex.exec(str)) { options = str.slice(thisMethod.length + 1); method = thisMethod.slice('git '.length); } }); if (!method) { return false; } // we support this command! // parse off the options and assemble the map / general args var parsedOptions = new GitOptionParser(method, options); return { toSet: { generalArgs: parsedOptions.generalArgs, supportedMap: parsedOptions.supportedMap, method: method, options: options, eventName: 'processGitCommand' } }; }; /** * GitOptionParser */ function GitOptionParser(method, options) { this.method = method; this.rawOptions = options; this.supportedMap = this.getMasterOptionMap()[method]; if (this.supportedMap === undefined) { throw new Error('No option map for ' + method); } this.generalArgs = []; this.explodeAndSet(); } GitOptionParser.prototype.getMasterOptionMap = function() { // here a value of false means that we support it, even if its just a // pass-through option. If the value is not here (aka will be undefined // when accessed), we do not support it. return { commit: { '--amend': false, '-a': false, // warning '-am': false, // warning '-m': false }, status: {}, log: {}, add: {}, 'cherry-pick': {}, branch: { '-d': false, '-D': false, '-f': false, '-a': false, '-r': false, '--contains': false }, checkout: { '-b': false, '-B': false, '-': false }, reset: { '--hard': false, '--soft': false // this will raise an error but we catch it in gitEngine }, merge: {}, rebase: { '-i': false // the mother of all options }, revert: {}, show: {}, clone: {}, fetch: {}, pull: { '--rebase': false }, fakeTeamwork: {} }; }; GitOptionParser.prototype.explodeAndSet = function() { // split on spaces, except when inside quotes var exploded = this.rawOptions.match(/('.*?'|".*?"|\S+)/g) || []; for (var i = 0; i < exploded.length; i++) { var part = exploded[i]; if (part.slice(0,1) == '-') { // it's an option, check supportedMap if (this.supportedMap[part] === undefined) { throw new CommandProcessError({ msg: intl.str( 'option-not-supported', { option: part } ) }); } // go through and include all the next args until we hit another option or the end var optionArgs = []; var next = i + 1; while (next < exploded.length && exploded[next].slice(0,1) != '-') { optionArgs.push(exploded[next]); next += 1; } i = next - 1; // **phew** we are done grabbing those. theseArgs is truthy even with an empty array this.supportedMap[part] = optionArgs; } else { // must be a general arg this.generalArgs.push(part); } } }; exports.shortcutMap = shortcutMap; exports.instantCommands = instantCommands; exports.parse = parse; exports.regexMap = regexMap;