Skip to content

Commit 683153e

Browse files
author
José Valim
committed
Switches are now overridden by default but can be kept in order if chosen, closes #782
1 parent 8a77e87 commit 683153e

File tree

4 files changed

+45
-20
lines changed

4 files changed

+45
-20
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* [Macro] `Macro.expand` also considers macros from the current `__ENV__` module
66
* [Mix] Improve support for compilation of `.erl` files
77
* [Mix] Add support for compilation of `.yrl` and `.xrl` files
8+
* [OptionParser] Switches are now overridden by default but can be kept in order if chosen
89
* [Typespec] Better error reporting for invalid typespecs
910

1011
* bug fix

lib/elixir/lib/option_parser.ex

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,19 @@ defmodule OptionParser do
2323
2424
## Switches
2525
26-
Extra information about switches can be given as argument too. This is useful
27-
in order to say a switch must behave as a boolean, list, etc. The following
28-
types are supported:
26+
Extra information about switches can be given as argument too.
27+
This is useful in order to say a switch must behave as a boolean
28+
or if duplicated switches should be kept, overriden or accumulated.
2929
30-
* `:boolean` - They never consume the next value unless it is true/false;
30+
The following types are supported:
31+
32+
* `:boolean` - Mark the given switch as boolean. Boolean switches
33+
never consumes the following value unless it is
34+
true or false;
35+
36+
The following extra options are supported:
37+
38+
* `:keep` - Keep duplicated items in the list instead of overriding;
3139
3240
Examples:
3341
@@ -80,16 +88,18 @@ defmodule OptionParser do
8088

8189
defp parse(["-" <> option|t], aliases, switches, dict, args, all) do
8290
{ option, value } = normalize_option(option, aliases)
91+
kind = switches[option]
8392

8493
if value == nil do
8594
{ value, t } =
86-
case switch_type(switches, option) do
87-
:boolean -> boolean_from_tail(t)
88-
_ -> value_from_tail(t)
95+
if is_switch_a? :boolean, kind do
96+
boolean_from_tail(t)
97+
else
98+
value_from_tail(t)
8999
end
90100
end
91101

92-
dict = store_option dict, option, value
102+
dict = store_option dict, option, value, kind
93103
parse(t, aliases, switches, dict, args, all)
94104
end
95105

@@ -112,16 +122,22 @@ defmodule OptionParser do
112122
defp value_from_tail([h|t]), do: { h, t }
113123
defp value_from_tail([]), do: { true, [] }
114124

115-
defp store_option(dict, option, value) when value in ["false", "true"] do
116-
store_option(dict, option, binary_to_atom(value))
125+
defp store_option(dict, option, value, switches) when value in ["true", "false"] do
126+
store_option dict, option, binary_to_atom(value), switches
117127
end
118128

119-
defp store_option(dict, option, value) do
120-
[{ option, value }|dict]
129+
defp store_option(dict, option, value, kind) do
130+
if is_switch_a? :keep, kind do
131+
[{ option, value }|dict]
132+
else
133+
[{ option, value }|Keyword.delete(dict, option)]
134+
end
121135
end
122136

123137
defp reverse_dict(dict, switches) do
124-
switches = lc { k, :boolean } inlist switches, not Keyword.has_key?(dict, k), do: { k, false }
138+
switches = lc { k, v } inlist switches,
139+
is_switch_a?(:boolean, v),
140+
not Keyword.has_key?(dict, k), do: { k, false }
125141
Enum.reverse switches ++ dict
126142
end
127143

@@ -150,7 +166,7 @@ defmodule OptionParser do
150166
defp is_no?("no-" <> _), do: true
151167
defp is_no?(_), do: false
152168

153-
defp switch_type(switches, option) do
154-
switches[option] || :default
155-
end
169+
defp is_switch_a?(kind, list) when is_list(list), do: List.member?(list, kind)
170+
defp is_switch_a?(kind, kind), do: true
171+
defp is_switch_a?(_, _), do: false
156172
end

lib/elixir/test/elixir/option_parser_test.exs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,18 @@ defmodule OptionParserTest do
7171
end
7272

7373
test "parses more than one key/value options" do
74-
assert OptionParser.parse(["--source", "from_docs/", "--docs", "false"]) == { [source: "from_docs/", docs: false], [] }
74+
assert OptionParser.parse(["--source", "from_docs/", "--docs", "false"]) ==
75+
{ [source: "from_docs/", docs: false], [] }
7576
end
7677

77-
test "accumulates options accumulators" do
78-
assert OptionParser.parse(["--require", "foo", "--require", "bar", "baz"]) == { [require: "foo", require: "bar"], ["baz"] }
78+
test "overrides options by default" do
79+
assert OptionParser.parse(["--require", "foo", "--require", "bar", "baz"]) ==
80+
{ [require: "bar"], ["baz"] }
81+
end
82+
83+
test "keeps options on keep" do
84+
assert OptionParser.parse(["--require", "foo", "--require", "bar", "baz"], switches: [require: :keep]) ==
85+
{ [require: "foo", require: "bar"], ["baz"] }
7986
end
8087

8188
test "parses mixed options" do

lib/mix/lib/mix/tasks/run.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ defmodule Mix.Tasks.Run do
2525
"""
2626
def run(args) do
2727
{ opts, head } = OptionParser.parse_head(args,
28-
aliases: [r: :require, pr: :parallel_require])
28+
aliases: [r: :require, pr: :parallel_require],
29+
switches: [parallel_require: :keep, require: :keep])
2930

3031
Enum.each opts, fn({ key, value }) ->
3132
case key do

0 commit comments

Comments
 (0)