Inital Commit
[oweals/finalsclub.git] / node_modules / jade / support / coffee-script / src / optparse.coffee
1 # A simple **OptionParser** class to parse option flags from the command-line.
2 # Use it like so:
3 #
4 #     parser  = new OptionParser switches, helpBanner
5 #     options = parser.parse process.argv
6 #
7 # The first non-option is considered to be the start of the file (and file
8 # option) list, and all subsequent arguments are left unparsed.
9 exports.OptionParser = class OptionParser
10
11   # Initialize with a list of valid options, in the form:
12   #
13   #     [short-flag, long-flag, description]
14   #
15   # Along with an an optional banner for the usage help.
16   constructor: (rules, banner) ->
17     @banner = banner
18     @rules  = buildRules rules
19
20   # Parse the list of arguments, populating an `options` object with all of the
21   # specified options, and returning it. `options.arguments` will be an array
22   # containing the remaning non-option arguments. This is a simpler API than
23   # many option parsers that allow you to attach callback actions for every
24   # flag. Instead, you're responsible for interpreting the options object.
25   parse: (args) ->
26     options = arguments: []
27     args    = normalizeArguments args
28     for arg, i in args
29       isOption = !!(arg.match(LONG_FLAG) or arg.match(SHORT_FLAG))
30       matchedRule = no
31       for rule in @rules
32         if rule.shortFlag is arg or rule.longFlag is arg
33           value = if rule.hasArgument then args[i += 1] else true
34           options[rule.name] = if rule.isList then (options[rule.name] or []).concat value else value
35           matchedRule = yes
36           break
37       throw new Error "unrecognized option: #{arg}" if isOption and not matchedRule
38       if not isOption
39         options.arguments = args.slice i
40         break
41     options
42
43   # Return the help text for this **OptionParser**, listing and describing all
44   # of the valid options, for `--help` and such.
45   help: ->
46     lines = ['Available options:']
47     lines.unshift "#{@banner}\n" if @banner
48     for rule in @rules
49       spaces  = 15 - rule.longFlag.length
50       spaces  = if spaces > 0 then Array(spaces + 1).join(' ') else ''
51       letPart = if rule.shortFlag then rule.shortFlag + ', ' else '    '
52       lines.push '  ' + letPart + rule.longFlag + spaces + rule.description
53     "\n#{ lines.join('\n') }\n"
54
55 # Helpers
56 # -------
57
58 # Regex matchers for option flags.
59 LONG_FLAG  = /^(--\w[\w\-]+)/
60 SHORT_FLAG = /^(-\w)/
61 MULTI_FLAG = /^-(\w{2,})/
62 OPTIONAL   = /\[(\w+(\*?))\]/
63
64 # Build and return the list of option rules. If the optional *short-flag* is
65 # unspecified, leave it out by padding with `null`.
66 buildRules = (rules) ->
67   for tuple in rules
68     tuple.unshift null if tuple.length < 3
69     buildRule tuple...
70
71 # Build a rule from a `-o` short flag, a `--output [DIR]` long flag, and the
72 # description of what the option does.
73 buildRule = (shortFlag, longFlag, description, options) ->
74   match     = longFlag.match(OPTIONAL)
75   longFlag  = longFlag.match(LONG_FLAG)[1]
76   options or= {}
77   {
78     name:         longFlag.substr 2
79     shortFlag:    shortFlag
80     longFlag:     longFlag
81     description:  description
82     hasArgument:  !!(match and match[1])
83     isList:       !!(match and match[2])
84   }
85
86 # Normalize arguments by expanding merged flags into multiple flags. This allows
87 # you to have `-wl` be the same as `--watch --lint`.
88 normalizeArguments = (args) ->
89   args = args.slice 0
90   result = []
91   for arg in args
92     if match = arg.match MULTI_FLAG
93       result.push '-' + l for l in match[1].split ''
94     else
95       result.push arg
96   result