Skip to content

[CS2] Add #! support for executable scripts on Linux. #3946

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

Merged
merged 51 commits into from
Jul 19, 2017
Merged
Changes from 2 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
6c48af3
Add #! support for executable scripts on Linux.
Apr 16, 2015
221dfc4
refactor option parsing
Apr 20, 2017
988f2af
address comments
Apr 20, 2017
fa3fe8b
intermediate save
May 24, 2017
aee066f
add note saying where OptionParser is used in coffee command
Apr 28, 2017
3c1fb7f
add some more work
May 24, 2017
a10c653
fix flatten functions
May 24, 2017
d5e8d74
Merge branch '2' of github.com:jashkenas/coffeescript
May 24, 2017
062fe62
Merge branch 'feature/uniform-hashbang-parsing'
May 24, 2017
c929ed9
refactor tests
May 31, 2017
b1acc4b
make argument processing less confusing
May 31, 2017
f3ea781
add basic test
May 31, 2017
200126f
Merge branch '2' into test/current-cli-optparse
May 31, 2017
7970e44
remove unused file
May 31, 2017
bc92ff3
compilation now hangs
May 31, 2017
7e0d9e0
remove unnecessary changes
Jun 2, 2017
0a05e0c
add tests!!!
Jun 2, 2017
3c49c8e
Merge branch '2' into feature/uniform-hashbang-parsing
Jun 2, 2017
0e1f27e
Merge branch 'test/current-cli-optparse' into feature/uniform-hashban…
Jun 2, 2017
42434b4
add/fix some tests
Jun 2, 2017
15eb624
clarify a test
Jun 2, 2017
bb9366c
Merge branch 'test/current-cli-optparse' into feature/uniform-hashban…
Jun 2, 2017
e1bcf84
fix helpers
Jun 9, 2017
7bc6591
fix opt parsing
Jun 9, 2017
7c4723d
fix infinite loop
Jun 9, 2017
768ada6
Merge remote-tracking branch 'upstream/2' into feature/uniform-hashba…
Jun 9, 2017
79c0e56
Merge remote-tracking branch 'upstream/2' into test/current-cli-optparse
Jun 9, 2017
ca7ad49
Merge branch 'test/current-cli-optparse' into feature/uniform-hashban…
Jun 9, 2017
daa5f4d
Merge remote-tracking branch 'upstream/2'
Jun 9, 2017
c75e2b1
Merge branch 'feature/uniform-hashbang-parsing'
Jun 9, 2017
23f31c6
Merge branch '2' of github.com:jashkenas/coffeescript
Jun 19, 2017
d495841
make rule building easier to read
Jun 19, 2017
bafa82f
add tests for flag overlap
Jun 19, 2017
9247444
revamp argument parsing again and add more thorough testing
Jun 19, 2017
b509b46
add tests, comment, clean unused method
Jun 19, 2017
31bbeb2
address review comments
Jun 20, 2017
7abe19c
Merge remote-tracking branch 'upstream/2'
Jun 21, 2017
3944d94
add test for direct invocation of shebang scripts
Jun 21, 2017
f41463d
move shebang parsing test to separate file and check for browser
Jun 21, 2017
356f3ad
remove TODO
Jun 21, 2017
7c17e23
example backwards compatible warnings
Jun 21, 2017
179687d
Merge remote-tracking branch 'upstream/2'
Jun 29, 2017
5b9b786
add correct tests for warning 1
Jun 29, 2017
8448a77
add tests for warnings
Jun 29, 2017
14df734
commit output js libs and update docs
Jun 29, 2017
b9291ae
respond to review comments
Jun 30, 2017
f306859
respond to review comments
Jul 7, 2017
4093574
Merge branch '2' of github.com:jashkenas/coffeescript
Jul 7, 2017
4856fd6
fix example output
Jul 7, 2017
f7e8c2b
Rewrite argument parsing documentation to be more concise; add it to …
GeoffreyBooth Jul 9, 2017
6fb80c1
Don’t mention deprecated syntax; clean up variable names
GeoffreyBooth Jul 9, 2017
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
79 changes: 38 additions & 41 deletions src/optparse.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -25,37 +25,26 @@ exports.OptionParser = class OptionParser
# parsers that allow you to attach callback actions for every flag. Instead,
# you're responsible for interpreting the options object.
parse: (args) ->
options = arguments: []
skippingArgument = no
originalArgs = args
args = normalizeArguments args
for arg, i in args
if skippingArgument
skippingArgument = no
continue
if arg is '--'
pos = originalArgs.indexOf '--'
options.arguments = options.arguments.concat originalArgs[(pos + 1)..]
state =
argsLeft: args[..]
options: {}
while (arg = state.argsLeft.shift())?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve been trying to avoid assignments inside conditionals. Can we just use for arg in args?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I think about it, I think we can just do a single pass over the arguments beforehand to 1. group arguments with values (-o [dir]) 2. explode combined options (e.g. -abck) 3. identify the first non-optional argument, then use a for. I'll do that.

if (arg.match(LONG_FLAG) ? arg.match(SHORT_FLAG))?
tryMatchOptionalArgument(arg, state, @rules)
else if (multiMatch = arg.match(MULTI_FLAG))?
# Normalize arguments by expanding merged flags into multiple
# flags. This allows you to have `-wl` be the same as `--watch --lint`.
normalized = "-#{multiArg}" for multiArg in multiMatch[1].split ''
state.argsLeft.unshift(normalized...)
else
# the CS option parser is a little odd; options after the first
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comments become the annotated source, so please use sentence case (capitalize first word, use periods).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, will fix. The first two lines are actually from the original source, so I wasn't sure whether I should change them.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. Never too late to fix things 😄

# non-option argument are treated as non-option arguments themselves.
# executable scripts do not need to have a `--` at the end of the
# shebang ("#!") line, and if they do, they won't work on Linux
state.argsLeft.unshift(arg) unless arg is '--'
break
isOption = !!(arg.match(LONG_FLAG) or arg.match(SHORT_FLAG))
# the CS option parser is a little odd; options after the first
# non-option argument are treated as non-option arguments themselves
seenNonOptionArg = options.arguments.length > 0
unless seenNonOptionArg
matchedRule = no
for rule in @rules
if rule.shortFlag is arg or rule.longFlag is arg
value = true
if rule.hasArgument
skippingArgument = yes
value = args[i + 1]
options[rule.name] = if rule.isList then (options[rule.name] or []).concat value else value
matchedRule = yes
break
throw new Error "unrecognized option: #{arg}" if isOption and not matchedRule
if seenNonOptionArg or not isOption
options.arguments.push arg
options
state.options.arguments = state.argsLeft[..]
state.options

# Return the help text for this **OptionParser**, listing and describing all
# of the valid options, for `--help` and such.
Expand Down Expand Up @@ -99,14 +88,22 @@ buildRule = (shortFlag, longFlag, description, options = {}) ->
isList: !!(match and match[2])
}

# Normalize arguments by expanding merged flags into multiple flags. This allows
# you to have `-wl` be the same as `--watch --lint`.
normalizeArguments = (args) ->
args = args[..]
result = []
for arg in args
if match = arg.match MULTI_FLAG
result.push '-' + l for l in match[1].split ''
else
result.push arg
result
addArgument = (rule, options, value) ->
options[rule.name] = if rule.isList
(options[rule.name] ? []).concat value
else value

tryMatchOptionalArgument = (arg, state, rules) ->
for rule in rules
if arg in [rule.shortFlag, rule.longFlag]
if rule.hasArgument
value = state.argsLeft.shift()
if not value?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unless

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, will change.

throw new Error "#{arg} requires a value, but was the last argument"
else
value = true

addArgument(rule, state.options, value)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid parentheses when possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, will fix.

return
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to avoid the implicit return?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If not, it would eventually leave the for loop and hit the throw statement. This was dealt with previously by keeping a boolean testing whether or not the rule was matched; I thought this was cleaner here since we don't then loop over the rest of the rules if we successfully match one.


throw new Error "unrecognized option: #{arg}"