summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rwxr-xr-xbin/iex2
-rw-r--r--lib/elixir/lib/application.ex8
-rw-r--r--lib/elixir/lib/io/ansi.ex68
-rw-r--r--lib/elixir/test/elixir/application_test.exs1
-rw-r--r--lib/elixir/test/elixir/io/ansi_test.exs3
-rw-r--r--lib/ex_unit/lib/ex_unit.ex4
-rw-r--r--lib/iex/lib/iex.ex192
-rw-r--r--lib/iex/lib/iex/evaluator.ex19
-rw-r--r--lib/iex/lib/iex/helpers.ex4
-rw-r--r--lib/iex/lib/iex/introspection.ex2
-rw-r--r--lib/iex/lib/iex/options.ex59
-rw-r--r--lib/iex/lib/iex/server.ex2
-rw-r--r--lib/iex/mix.exs32
-rw-r--r--lib/iex/test/iex/evaluator_test.exs1
-rw-r--r--lib/iex/test/iex/interaction_test.exs80
-rw-r--r--lib/iex/test/iex/options_test.exs67
-rw-r--r--lib/iex/test/iex/server_test.exs7
-rw-r--r--lib/iex/test/test_helper.exs35
-rw-r--r--lib/mix/lib/mix/cli.ex4
-rw-r--r--lib/mix/lib/mix/project.ex30
-rw-r--r--lib/mix/lib/mix/tasks/loadconfig.ex2
-rw-r--r--lib/mix/lib/mix/tasks/new.ex26
-rw-r--r--lib/mix/test/mix/project_test.exs2
-rw-r--r--lib/mix/test/mix/tasks/loadconfig_test.exs4
25 files changed, 376 insertions, 280 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d59c0d720..24b4df09b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -33,6 +33,7 @@
* [Process] `Process.spawn/1`, `Process.spawn/3`, `Process.spawn_link/1`, `Process.spawn_link/3`, `Process.spawn_monitor/1`, `Process.spawn_monitor/3`, `Process.send/2` and `Process.self/0` are deprecated in favor of the ones in `Kernel`
* Deprecations
+ * [IEx] IEx.Options is deprecated in favor of `IEx.configure/1` and `IEx.configuration/0`
* [Kernel] `lc` and `bc` comprehensions are deprecated in favor of `for`
* [Macro] `Macro.safe_terms/1` is deprecated
* [Process] `Process.delete/0` is deprecated
@@ -40,6 +41,7 @@
* [String] Deprecate `:global` option in `String.split/3` in favor of `parts: :infinity`
* Backwards incompatible changes
+ * [IEx] IEx no longer loads an `.iex.exs` file at the current path. Instead, IEx should be configured via the new Mix config
* [ExUnit] `ExUnit.Test` and `ExUnit.TestCase` has been converted to structs
* [ExUnit] The test and callback context has been converted to maps
* [Kernel] `File.Stat`, `HashDict`, `HashSet`, `Inspect.Opts`, `Macro.Env`, `Range`, `Regex` and `Version.Requirement` have been converted to structs. This means `is_record/2` checks will no longer work, instead, you can pattern match on them using `%Range{}` and similar
diff --git a/bin/iex b/bin/iex
index bb0d8d7d7..acb90cea7 100755
--- a/bin/iex
+++ b/bin/iex
@@ -18,7 +18,7 @@ if [ $# -gt 0 ] && ([ "$1" = "--help" ] || [ "$1" = "-h" ]); then
--detached Starts the Erlang VM detached from console
--gen-debug Turns on default debugging for all GenServers
--remsh \"name\" Connects to a node using a remote shell
- --dot-iex \"path\" Overrides default .iex.exs file and uses path instead;
+ --dot-iex \"path\" Overrides default ~/.iex.exs file and uses path instead;
path can be empty, then no file will be loaded
** Options marked with (*) can be given more than once
diff --git a/lib/elixir/lib/application.ex b/lib/elixir/lib/application.ex
index 610c9c653..d62b5ced1 100644
--- a/lib/elixir/lib/application.ex
+++ b/lib/elixir/lib/application.ex
@@ -112,6 +112,14 @@ defmodule Application do
@type start_type :: :permanent | :transient | :temporary
@doc """
+ Returns all key-value pairs for `app`.
+ """
+ @spec get_all_env(app) :: [{key,value}]
+ def get_all_env(app) do
+ :application.get_all_env(app)
+ end
+
+ @doc """
Returns the value for `key` in `app`'s environment.
If the specified application is not loaded, or the configuration parameter
diff --git a/lib/elixir/lib/io/ansi.ex b/lib/elixir/lib/io/ansi.ex
index 3db0ee3d3..d8582cbbf 100644
--- a/lib/elixir/lib/io/ansi.ex
+++ b/lib/elixir/lib/io/ansi.ex
@@ -7,8 +7,8 @@ defmodule IO.ANSI.Sequence do
"\e[#{unquote(code)}#{unquote(terminator)}"
end
- defp escape_sequence(<< unquote(atom_to_binary(name)), rest :: binary >>) do
- {"\e[#{unquote(code)}#{unquote(terminator)}", rest}
+ defp escape_sequence(unquote(atom_to_list(name))) do
+ unquote(name)()
end
end
end
@@ -130,15 +130,8 @@ defmodule IO.ANSI do
@doc "Clear screen"
defsequence :clear, "2", "J"
-
- # Catch spaces between codes
- defp escape_sequence(<< ?\s, rest :: binary >>) do
- escape_sequence(rest)
- end
-
defp escape_sequence(other) do
- [spec|_] = String.split(other, ~r/(,|\})/)
- raise ArgumentError, message: "invalid ANSI sequence specification: #{spec}"
+ raise ArgumentError, message: "invalid ANSI sequence specification: #{other}"
end
@doc ~S"""
@@ -163,8 +156,8 @@ defmodule IO.ANSI do
"""
@spec escape(String.t, emit :: boolean) :: String.t
def escape(string, emit \\ terminal?) do
- {rendered, emitted} = do_escape(string, false, emit, false, [])
- if emitted and emit do
+ {rendered, emitted} = do_escape(string, emit, false, nil, [])
+ if emitted do
rendered <> reset
else
rendered
@@ -193,34 +186,43 @@ defmodule IO.ANSI do
"""
@spec escape_fragment(String.t, emit :: boolean) :: String.t
def escape_fragment(string, emit \\ terminal?) do
- {rendered, _emitted} = do_escape(string, false, emit, false, [])
- rendered
+ {escaped, _emitted} = do_escape(string, emit, false, nil, [])
+ escaped
end
- defp do_escape(<< ?%, ?{, rest :: binary >>, false, emit, _emitted, acc) do
- do_escape_sequence(rest, emit, acc)
- end
- defp do_escape(<< ?,, rest :: binary >>, true, emit, _emitted, acc) do
- do_escape_sequence(rest, emit, acc)
+ defp do_escape(<<?}, t :: binary>>, emit, emitted, buffer, acc) when is_list(buffer) do
+ sequences =
+ buffer
+ |> Enum.reverse()
+ |> :string.tokens(',')
+ |> Enum.map(&(&1 |> :string.strip |> escape_sequence))
+ |> Enum.reverse()
+
+ if emit and sequences != [] do
+ do_escape(t, emit, true, nil, sequences ++ acc)
+ else
+ do_escape(t, emit, emitted, nil, acc)
+ end
end
- defp do_escape(<< ?\s, rest :: binary >>, true, emit, emitted, acc) do
- do_escape(rest, true, emit, emitted, acc)
+
+ defp do_escape(<<h, t :: binary>>, emit, emitted, buffer, acc) when is_list(buffer) do
+ do_escape(t, emit, emitted, [h|buffer], acc)
end
- defp do_escape(<< ?}, rest :: binary >>, true, emit, emitted, acc) do
- do_escape(rest, false, emit, emitted, acc)
+
+ defp do_escape(<<>>, _emit, _emitted, buffer, _acc) when is_list(buffer) do
+ buffer = iodata_to_binary Enum.reverse(buffer)
+ raise ArgumentError, message: "missing } for escape fragment #{buffer}"
end
- defp do_escape(<< x :: [binary, size(1)], rest :: binary>>, false, emit, emitted, acc) do
- do_escape(rest, false, emit, emitted, [x|acc])
+
+ defp do_escape(<<?%, ?{, t :: binary>>, emit, emitted, nil, acc) do
+ do_escape(t, emit, emitted, [], acc)
end
- defp do_escape("", false, _emit, emitted, acc) do
- {iodata_to_binary(Enum.reverse(acc)), emitted}
+
+ defp do_escape(<<h, t :: binary>>, emit, emitted, nil, acc) do
+ do_escape(t, emit, emitted, nil, [h|acc])
end
- defp do_escape_sequence(rest, emit, acc) do
- {code, rest} = escape_sequence(rest)
- if emit do
- acc = [code|acc]
- end
- do_escape(rest, true, emit, true, acc)
+ defp do_escape(<<>>, _emit, emitted, nil, acc) do
+ {iodata_to_binary(Enum.reverse(acc)), emitted}
end
end
diff --git a/lib/elixir/test/elixir/application_test.exs b/lib/elixir/test/elixir/application_test.exs
index d655842b9..20258320f 100644
--- a/lib/elixir/test/elixir/application_test.exs
+++ b/lib/elixir/test/elixir/application_test.exs
@@ -11,6 +11,7 @@ defmodule ApplicationTest do
assert Application.put_env(:elixir, :unknown, :known) == :ok
assert Application.fetch_env(:elixir, :unknown) == {:ok, :known}
assert Application.get_env(:elixir, :unknown, :default) == :known
+ assert {:unknown, :known} in Application.get_all_env(:elixir)
assert Application.delete_env(:elixir, :unknown) == :ok
assert Application.get_env(:elixir, :unknown, :default) == :default
diff --git a/lib/elixir/test/elixir/io/ansi_test.exs b/lib/elixir/test/elixir/io/ansi_test.exs
index 5239d4354..f05a06799 100644
--- a/lib/elixir/test/elixir/io/ansi_test.exs
+++ b/lib/elixir/test/elixir/io/ansi_test.exs
@@ -27,6 +27,9 @@ defmodule IO.ANSITest do
end
test :no_emit do
+ assert IO.ANSI.escape("Hello, %{}world!", false) ==
+ "Hello, world!"
+
assert IO.ANSI.escape("Hello, %{red,bright}world!", false) ==
"Hello, world!"
end
diff --git a/lib/ex_unit/lib/ex_unit.ex b/lib/ex_unit/lib/ex_unit.ex
index 57e3b851b..242a8eda9 100644
--- a/lib/ex_unit/lib/ex_unit.ex
+++ b/lib/ex_unit/lib/ex_unit.ex
@@ -163,7 +163,7 @@ defmodule ExUnit do
"""
def configure(options) do
Enum.each options, fn {k, v} ->
- :application.set_env(:ex_unit, k, v)
+ Application.put_env(:ex_unit, k, v)
end
end
@@ -171,7 +171,7 @@ defmodule ExUnit do
Returns ExUnit configuration.
"""
def configuration do
- :application.get_all_env(:ex_unit)
+ Application.get_all_env(:ex_unit)
end
@doc """
diff --git a/lib/iex/lib/iex.ex b/lib/iex/lib/iex.ex
index d175a5ed2..d7b07a59d 100644
--- a/lib/iex/lib/iex.ex
+++ b/lib/iex/lib/iex.ex
@@ -120,16 +120,15 @@ defmodule IEx do
Connecting an Elixir shell to a remote node without Elixir is
**not** supported.
- ## The .iex.exs file
+ ## The ~/.iex.exs file
- When starting IEx, it will look for a local `.iex.exs` file (located in the current
- working directory), then a global one (located at `~/.iex.exs`) and will load the
- first one it finds (if any). The code in the chosen .iex file will be
- evaluated in the shell's context. So, for instance, any modules that are
- loaded or variables that are bound in the .iex file will be available in the
- shell after it has booted.
+ When starting IEx, it will look for a global configuration file
+ (located at `~/.iex.exs`) and load it if available. The code in the
+ chosen .iex file will be evaluated in the shell's context. So, for
+ instance, any modules that are loaded or variables that are bound
+ in the .iex file will be available in the shell after it has booted.
- Sample contents of a local .iex file:
+ Sample contents of a .iex file:
# source another `.iex` file
import_file "~/.iex.exs"
@@ -151,23 +150,19 @@ defmodule IEx do
iex(1)> value
13
- It is possible to override the default loading sequence for `.iex.exs` file by
- supplying the `--dot-iex` option to iex. See `iex --help`.
+ It is possible to load another file by supplying the `--dot-iex`
+ option to iex. See `iex --help`.
## Configuring the shell
There are a number of customization options provided by the shell. Take a look
- at the docs for the `IEx.Options` module by typing `h IEx.Options`.
+ at the docs for the `IEx.configure/1` function by typing `h IEx.configure/1`.
- The main functions there are `IEx.Options.get/1` and `IEx.Options.set/2`. One
- can also use `IEx.Options.list/0` to get the list of all supported options.
- `IEx.Options.print_help/1` will print documentation for the given option.
-
- In particular, it might be convenient to customize those options inside your
- `.iex.exs` file like this:
+ Those options can be configured in your project configuration file or globally
+ by calling `IEx.configure/1` from your `~/.iex.exs` file like this:
# .iex
- IEx.Options.set :inspect, limit: 3
+ IEx.configure(inspect: [limit: 3])
### now run the shell ###
@@ -214,17 +209,109 @@ defmodule IEx do
"""
@doc """
+ Configures IEx.
+
+ The supported options are: `:colors`, `:inspect`,
+ `:default_prompt`, `:alive_prompt` and `:history_size`.
+
+ ## Colors
+
+ A keyword list that encapsulates all color settings used by the
+ shell. See documentation for the `IO.ANSI` module for the list of
+ supported colors and attributes.
+
+ The value is a keyword list. List of supported keys:
+
+ * `:enabled` - boolean value that allows for switching the coloring on and off
+ * `:eval_result` - color for an expression's resulting value
+ * `:eval_info` - … various informational messages
+ * `:eval_error` - … error messages
+ * `:stack_app` - … the app in stack traces
+ * `:stack_info` - … the remaining info in stacktraces
+ * `:ls_directory` - … for directory entries (ls helper)
+ * `:ls_device` - … device entries (ls helper)
+
+ When printing documentation, IEx will convert the markdown
+ documentation to ANSI as well. Those can be configured via:
+
+ * `:doc_code` — the attributes for code blocks (cyan, bright)
+ * `:doc_inline_code` - inline code (cyan)
+ * `:doc_headings` - h1 and h2 (yellow, bright)
+ * `:doc_title` — the overall heading for the output (reverse,yellow,bright)
+ * `:doc_bold` - (bright)
+ * `:doc_underline` - (underline)
+
+ ## Inspect
+
+ A keyword list containing inspect options used by the shell
+ when printing results of expression evaluation. Defailt to
+ pretty formatting with a limit of 50 entries.
+
+ See `Inspect.Opts` for the full list of options.
+
+ ## History size
+
+ Number of expressions and their results to keep in the history.
+ The value is an integer. When it is negative, the history is unlimited.
+
+ ## Prompt
+
+ This is an option determining the prompt displayed to the user
+ when awaiting input.
+
+ The value is a keyword list. Two prompt types:
+
+ * `:default_prompt` - used when `Node.alive?` returns false
+ * `:alive_prompt` - used when `Node.alive?` returns true
+
+ The part of the listed in the following of the prompt string is replaced.
+
+ * `%counter` - the index of the history
+ * `%prefix` - a prefix given by `IEx.Server`
+ * `%node` - the name of the local node
+
+ """
+ def configure(options) do
+ Enum.each options, fn {k, v} ->
+ Application.put_env(:iex, k, configure(k, v))
+ end
+ end
+
+ defp configure(k, v) when k in [:colors, :inspect] and is_list(v) do
+ Keyword.merge(Application.get_env(:iex, k), v)
+ end
+
+ defp configure(:history_size, v) when is_integer(v) do
+ v
+ end
+
+ defp configure(k, v) when k in [:default_prompt, :alive_prompt] and is_binary(v) do
+ v
+ end
+
+ defp configure(k, v) do
+ raise ArgumentError, message: "invalid value #{inspect v} for configuration #{inspect k}"
+ end
+
+ @doc """
+ Returns IEx configuration.
+ """
+ def configuration do
+ Application.get_all_env(:iex)
+ end
+
+ @doc """
Registers a function to be invoked after the IEx process is spawned.
"""
def after_spawn(fun) when is_function(fun) do
- :application.set_env(:iex, :after_spawn, [fun|after_spawn])
+ Application.put_env(:iex, :after_spawn, [fun|after_spawn])
end
@doc """
Returns registered `after_spawn` callbacks.
"""
def after_spawn do
- {:ok, list} = :application.get_env(:iex, :after_spawn)
+ {:ok, list} = Application.fetch_env(:iex, :after_spawn)
list
end
@@ -232,7 +319,7 @@ defmodule IEx do
Returns `true` if IEx was properly started.
"""
def started? do
- :application.get_env(:iex, :started) == {:ok, true}
+ Application.get_env(:iex, :started, false)
end
@doc """
@@ -240,7 +327,7 @@ defmodule IEx do
ANSI escapes in `string` are not processed in any way.
"""
def color(color_name, string) do
- colors = IEx.Options.get(:colors)
+ colors = Application.get_env(:iex, :colors)
enabled = colors[:enabled]
IO.ANSI.escape_fragment("%{#{colors[color_name]}}", enabled)
<> string <> IO.ANSI.escape_fragment("%{reset}", enabled)
@@ -345,7 +432,7 @@ defmodule IEx do
_ -> :init.wait_until_started()
end
- start_iex()
+ callback = start_iex(callback)
set_expand_fun()
run_after_spawn()
IEx.Server.start(opts, callback)
@@ -355,14 +442,61 @@ defmodule IEx do
@doc false
def dont_display_result, do: :"do not show this result in output"
+ @doc false
+ def default_colors do
+ [# Used by default on evaluation cycle
+ eval_interrupt: "yellow",
+ eval_result: "yellow",
+ eval_error: "red",
+ eval_info: "normal",
+ stack_app: "red,bright",
+ stack_info: "red",
+
+ # Used by ls
+ ls_directory: "blue",
+ ls_device: "green",
+
+ # Used by ansi docs
+ doc_bold: "bright",
+ doc_code: "cyan,bright",
+ doc_headings: "yellow,bright",
+ doc_inline_code: "cyan",
+ doc_underline: "underline",
+ doc_title: "reverse,yellow,bright"]
+ end
+
+ @doc false
+ def default_inspect do
+ [structs: true, binaries: :infer,
+ char_lists: :infer, limit: 50, pretty: true]
+ end
+
## Helpers
- defp start_iex do
- unless started? do
- :application.start(:elixir)
- :application.start(:iex)
- :application.set_env(:iex, :started, true)
- IEx.Options.set :colors, enabled: IO.ANSI.terminal?
+ defp start_iex(callback) do
+ if started? do
+ callback
+ else
+ Application.start(:elixir)
+ Application.start(:iex)
+ Application.put_env(:iex, :started, true)
+
+ fn ->
+ # The callback may actually configure IEx (for example,
+ # if it is a Mix project), so we wrap the original callback
+ # so we can normalize options afterwards.
+ callback.()
+
+ colors = default_colors
+ |> Keyword.merge(Application.get_env(:iex, :colors))
+ |> Keyword.put_new(:enabled, IO.ANSI.terminal?)
+
+ inspect = default_inspect
+ |> Keyword.merge(Application.get_env(:iex, :inspect))
+
+ Application.put_env(:iex, :colors, colors)
+ Application.put_env(:iex, :inspect, inspect)
+ end
end
end
diff --git a/lib/iex/lib/iex/evaluator.ex b/lib/iex/lib/iex/evaluator.ex
index 92b1a575d..204b9be20 100644
--- a/lib/iex/lib/iex/evaluator.ex
+++ b/lib/iex/lib/iex/evaluator.ex
@@ -39,18 +39,12 @@ defmodule IEx.Evaluator do
Returns the new config.
"""
def load_dot_iex(config, path \\ nil) do
- candidates = if path do
- [path]
- else
- Enum.map [".iex.exs", "~/.iex.exs"], &Path.expand/1
- end
+ path = path || "~/.iex.exs"
- path = Enum.find candidates, &File.regular?/1
-
- if nil?(path) do
- config
- else
+ if File.regular?(path) do
eval_dot_iex(config, path)
+ else
+ config
end
end
@@ -132,7 +126,8 @@ defmodule IEx.Evaluator do
end
defp update_history(counter, cache, result) do
- IEx.History.append({counter, cache, result}, counter, IEx.Options.get(:history_size))
+ IEx.History.append({counter, cache, result}, counter,
+ Application.get_env(:iex, :history_size))
end
defp io_put(result) do
@@ -144,7 +139,7 @@ defmodule IEx.Evaluator do
end
defp inspect_opts do
- [width: IEx.width] ++ IEx.Options.get(:inspect)
+ [width: IEx.width] ++ Application.get_env(:iex, :inspect)
end
## Error handling
diff --git a/lib/iex/lib/iex/helpers.ex b/lib/iex/lib/iex/helpers.ex
index 49a8be519..518c4400f 100644
--- a/lib/iex/lib/iex/helpers.ex
+++ b/lib/iex/lib/iex/helpers.ex
@@ -236,7 +236,7 @@ defmodule IEx.Helpers do
their results.
"""
def v do
- inspect_opts = IEx.Options.get(:inspect)
+ inspect_opts = Application.get_env(:iex, :inspect)
IEx.History.each(&print_history_entry(&1, inspect_opts))
end
@@ -299,7 +299,7 @@ defmodule IEx.Helpers do
Flushes all messages sent to the shell and prints them out.
"""
def flush do
- inspect_opts = IEx.Options.get(:inspect)
+ inspect_opts = Application.get_env(:iex, :inspect)
do_flush(inspect_opts)
end
diff --git a/lib/iex/lib/iex/introspection.ex b/lib/iex/lib/iex/introspection.ex
index 649f85d6e..44fab3a16 100644
--- a/lib/iex/lib/iex/introspection.ex
+++ b/lib/iex/lib/iex/introspection.ex
@@ -178,7 +178,7 @@ defmodule IEx.Introspection do
end
defp docs_options() do
- [width: IEx.width] ++ IEx.Options.get(:colors)
+ [width: IEx.width] ++ Application.get_env(:iex, :colors)
end
@doc """
diff --git a/lib/iex/lib/iex/options.ex b/lib/iex/lib/iex/options.ex
index b78a18d2f..1487dd59a 100644
--- a/lib/iex/lib/iex/options.ex
+++ b/lib/iex/lib/iex/options.ex
@@ -1,37 +1,5 @@
defmodule IEx.Options do
- @moduledoc """
- Provides an interface for adjusting options of the running IEx session.
-
- Changing options is usually done inside an IEx session or in your .iex.exs file.
- See `h(IEx)` for more info on the latter.
-
- If the value of an option is a keyword list, only those keys that are
- mentioned will be changed. The rest of the sub-options will keep their
- current values. Any extraneous keys are filtered out, i.e. not used.
-
- To get the list of all supported options, use `IEx.Options.list/0`.
- You can also get an option's description using `IEx.Options.print_help/1`.
-
- ## Examples
-
- iex(1)> ArgumentError[]
- ArgumentError[message: "argument error"]
-
- iex(2)> IEx.Options.set :inspect, structs: false
- [limit: 50, structs: true]
-
- iex(3)> ArgumentError[]
- {ArgumentError,:__exception__,"argument error"}
-
- iex(4)> IEx.Options.list
- [:colors,:inspect]
-
- iex(5)> IEx.Options.print_help :colors
- This is an aggregate option that encapsulates all color settings used
- by the shell.
- ... # omitted content
-
- """
+ @moduledoc false
@supported_options ~w(colors inspect history_size prompt)a
@@ -40,6 +8,7 @@ defmodule IEx.Options do
list.
"""
def get do
+ IO.write :stderr, "IEx.Options.get/0 is deprecated, please use IEx.configuration/0\n#{Exception.format_stacktrace}"
Enum.map list(), fn name ->
{name, get(name)}
end
@@ -53,6 +22,7 @@ defmodule IEx.Options do
for key <- @supported_options do
def get(unquote(key)) do
+ IO.write :stderr, "IEx.Options.get/1 is deprecated, please use IEx.configuration/0\n#{Exception.format_stacktrace}"
{:ok, value} = Application.fetch_env(:iex, unquote(key))
value
end
@@ -69,6 +39,7 @@ defmodule IEx.Options do
Returns a keyword list of old option values.
"""
def set(opts) do
+ IO.write :stderr, "IEx.Options.set/1 is deprecated, please use IEx.configure/1\n#{Exception.format_stacktrace}"
Enum.map opts, fn {name, val} ->
{name, set(name, val)}
end
@@ -85,6 +56,7 @@ defmodule IEx.Options do
def set(name, value)
def set(:colors, colors) when is_list(colors) do
+ IO.write :stderr, "IEx.Options.set/2 is deprecated, please use IEx.configure/1\n#{Exception.format_stacktrace}"
filter_and_merge(:colors, colors)
end
@@ -93,6 +65,7 @@ defmodule IEx.Options do
end
def set(:inspect, opts) when is_list(opts) do
+ IO.write :stderr, "IEx.Options.set/2 is deprecated, please use IEx.configure/1\n#{Exception.format_stacktrace}"
filter_and_merge(:inspect, opts)
end
@@ -101,6 +74,7 @@ defmodule IEx.Options do
end
def set(:history_size, size) when is_integer(size) do
+ IO.write :stderr, "IEx.Options.set/2 is deprecated, please use IEx.configure/1\n#{Exception.format_stacktrace}"
old_size = get(:history_size)
Application.put_env(:iex, :history_size, size)
old_size
@@ -111,6 +85,7 @@ defmodule IEx.Options do
end
def set(:prompt, prompts) when is_list(prompts) do
+ IO.write :stderr, "IEx.Options.set/2 is deprecated, please use IEx.configure/1\n#{Exception.format_stacktrace}"
filter_and_merge(:prompt, prompts)
end
@@ -193,6 +168,7 @@ defmodule IEx.Options do
Same as `help/1` but instead of returning a string, prints it.
"""
def print_help(name) do
+ IO.write :stderr, "IEx.Options is deprecated\n#{Exception.format_stacktrace}"
IO.ANSI.Docs.print help(name)
end
@@ -200,6 +176,7 @@ defmodule IEx.Options do
Returns all supported options as a list of names.
"""
def list() do
+ IO.write :stderr, "IEx.Options is deprecated\n#{Exception.format_stacktrace}"
@supported_options
end
@@ -211,23 +188,9 @@ defmodule IEx.Options do
raise ArgumentError, message: "Expected the value to be #{type}"
end
- defp raise_key(option_name, name) do
- raise ArgumentError, message: "Unsupported key '#{name}' for option '#{option_name}'"
- end
-
defp filter_and_merge(opt, values) when is_list(values) do
old_values = get(opt)
- filtered_values = filtered_kw(opt, old_values, values)
- :application.set_env(:iex, opt, Keyword.merge(old_values, filtered_values))
+ :application.set_env(:iex, opt, Keyword.merge(old_values, values))
old_values
end
-
- defp filtered_kw(opt, reference_kw, user_kw) do
- Enum.filter user_kw, fn {name, _} ->
- if not Keyword.has_key?(reference_kw, name) do
- raise_key(opt, name)
- end
- true
- end
- end
end
diff --git a/lib/iex/lib/iex/server.ex b/lib/iex/lib/iex/server.ex
index 0e2467fde..2eff0c549 100644
--- a/lib/iex/lib/iex/server.ex
+++ b/lib/iex/lib/iex/server.ex
@@ -269,7 +269,7 @@ defmodule IEx.Server do
{:default, prefix || "iex"}
end
- prompt = IEx.Options.get(:prompt)[mode]
+ prompt = Application.get_env(:iex, :"#{mode}_prompt")
|> String.replace("%counter", to_string(counter))
|> String.replace("%prefix", to_string(prefix))
|> String.replace("%node", to_string(node))
diff --git a/lib/iex/mix.exs b/lib/iex/mix.exs
index 1a966fa26..ce58ab129 100644
--- a/lib/iex/mix.exs
+++ b/lib/iex/mix.exs
@@ -8,34 +8,10 @@ defmodule IEx.Mixfile do
def application do
[env: [
after_spawn: [],
- colors: colors,
- inspect: [structs: true, binaries: :infer,
- char_lists: :infer, limit: 50, pretty: true],
+ colors: IEx.default_colors,
+ inspect: IEx.default_inspect,
history_size: 20,
- prompt: [default: "%prefix(%counter)>", alive: "%prefix(%node)%counter>" ]]]
- end
-
- defp colors do
- [enabled: true,
-
- # Used by default on evaluation cycle
- eval_interrupt: "yellow",
- eval_result: "yellow",
- eval_error: "red",
- eval_info: "normal",
- stack_app: "red,bright",
- stack_info: "red",
-
- # Used by ls
- ls_directory: "blue",
- ls_device: "green",
-
- # Used by ansi docs
- doc_bold: "bright",
- doc_code: "cyan,bright",
- doc_headings: "yellow,bright",
- doc_inline_code: "cyan",
- doc_underline: "underline",
- doc_title: "reverse,yellow,bright"]
+ default_prompt: "%prefix(%counter)>",
+ alive_prompt: "%prefix(%node)%counter>"]]
end
end
diff --git a/lib/iex/test/iex/evaluator_test.exs b/lib/iex/test/iex/evaluator_test.exs
index 098fc783f..0860956e4 100644
--- a/lib/iex/test/iex/evaluator_test.exs
+++ b/lib/iex/test/iex/evaluator_test.exs
@@ -12,7 +12,6 @@ defmodule IEx.EvaluatorTest do
{IEx, :three, 3, [file: "longer", line: 1234]},
{List, :four, 4, [file: "loc", line: 1]},
]
- IEx.Options.set :colors, enabled: false
expected = """
(elixir) loc:1: List.one/1
diff --git a/lib/iex/test/iex/interaction_test.exs b/lib/iex/test/iex/interaction_test.exs
index 6b424df12..577f6cc52 100644
--- a/lib/iex/test/iex/interaction_test.exs
+++ b/lib/iex/test/iex/interaction_test.exs
@@ -3,14 +3,18 @@ Code.require_file "../test_helper.exs", __DIR__
defmodule IEx.InteractionTest do
use IEx.Case
+ @doc """
+ Hello, I have %{red}ANSI%{reset} escapes.
+ """
+ def ansi_escapes, do: :ok
+
## Basic interaction
test "whole output" do
- IEx.Options.set :colors, enabled: false
-
assert capture_io("IO.puts \"Hello world\"", fn ->
IEx.Server.start([dot_iex_path: ""], fn -> end)
- end) =~ "Interactive Elixir (#{System.version}) - press Ctrl+C to exit (type h() ENTER for help)\niex(1)> Hello world\n:ok\niex(2)>"
+ end) =~ "Interactive Elixir (#{System.version}) - press Ctrl+C to exit (type h() ENTER for help)" <>
+ "\niex(1)> Hello world\n:ok\niex(2)>"
end
test "empty input" do
@@ -88,6 +92,51 @@ defmodule IEx.InteractionTest do
:code.delete(Sample)
end
+ test "prompt" do
+ opts = [default_prompt: "prompt(%counter)>"]
+ assert capture_iex("1\n", opts, [], true) == "prompt(1)> 1\nprompt(2)>"
+ end
+
+ unless match?({:win32,_}, :os.type) do
+ test "color" do
+ opts = [colors: [enabled: true, eval_result: "red"]]
+ assert capture_iex("1 + 2", opts) == "\e[31m3\e[0m"
+
+ # Sanity checks
+ assert capture_iex("IO.ANSI.escape(\"%{blue}hello\", true)", opts)
+ == "\e[31m\"\\e[34mhello\\e[0m\"\e[0m"
+ assert capture_iex("IO.puts IO.ANSI.escape(\"%{blue}hello\", true)", opts)
+ == "\e[34mhello\e[0m\n\e[31m:ok\e[0m"
+ assert capture_iex("IO.puts IO.ANSI.escape(\"%{blue}hello\", true)", [colors: [enabled: false]])
+ == "\e[34mhello\e[0m\n:ok"
+
+ # Test that ANSI escapes in the docs are left alone
+ opts = [colors: [enabled: true]]
+ assert capture_iex("h IEx.InteractionTest.ansi_escapes", opts)
+ == "* def ansi_escapes()\n\nHello, I have %{red}ANSI%{reset} escapes."
+
+ # Test that ANSI escapes in iex output are left alone
+ opts = [colors: [enabled: true, eval_result: "red", eval_info: "red"]]
+ assert capture_iex("\"%{red} %{blue}\"", opts) == "\e[31m\"%{red} %{blue}\"\e[0m"
+ assert capture_iex("IO.puts IEx.color(:eval_info, \"%{red} %{blue}\")", opts)
+ == "\e[31m%{red} %{blue}\e[0m\n\e[31m:ok\e[0m"
+ end
+ end
+
+ test "inspect opts" do
+ opts = [inspect: [binaries: :as_binaries, char_lists: :as_lists, structs: false, limit: 4]]
+ assert capture_iex("<<45,46,47>>\n[45,46,47]\n%IO.Stream{}", opts) ==
+ "<<45, 46, 47>>\n[45, 46, 47]\n%{__struct__: IO.Stream, device: nil, line_or_bytes: :line, raw: true}"
+ end
+
+ test "history size" do
+ opts = [history_size: 3]
+ assert capture_iex("1\n2\n3\nv(1)", opts) == "1\n2\n3\n1"
+ assert "1\n2\n3\n4\n** (RuntimeError) v(1) is out of bounds" <> _ = capture_iex("1\n2\n3\n4\nv(1)", opts)
+ assert "1\n2\n3\n4\n** (RuntimeError) v(-4) is out of bounds" <> _ = capture_iex("1\n2\n3\n4\nv(-4)", opts)
+ assert "1\n2\n3\n4\n2\n** (RuntimeError) v(2) is out of bounds" <> _ = capture_iex("1\n2\n3\n4\nv(2)\nv(2)", opts)
+ end
+
## .iex file loading
test "no .iex" do
@@ -98,7 +147,7 @@ defmodule IEx.InteractionTest do
File.write!("dot-iex", "my_variable = 144")
assert capture_iex("my_variable", [], [dot_iex_path: "dot-iex"]) == "144"
after
- File.rm!("dot-iex")
+ File.rm("dot-iex")
end
test "nested .iex" do
@@ -109,23 +158,28 @@ defmodule IEx.InteractionTest do
assert capture_iex(input, [], [dot_iex_path: "dot-iex"]) == "13\n14\nhello\n:ok"
after
File.rm("dot-iex-1")
- File.rm!("dot-iex")
+ File.rm("dot-iex")
end
test "receive exit" do
- assert capture_iex("spawn_link(fn -> exit(:bye) end)") =~ ~r"\*\* \(EXIT from #PID<\d+\.\d+\.\d+>\) :bye"
- assert capture_iex("spawn_link(fn -> exit({:bye, [:world]}) end)") =~ ~r"\*\* \(EXIT from #PID<\d+\.\d+\.\d+>\) {:bye, \[:world\]}"
+ assert capture_iex("spawn_link(fn -> exit(:bye) end)") =~
+ ~r"\*\* \(EXIT from #PID<\d+\.\d+\.\d+>\) :bye"
+ assert capture_iex("spawn_link(fn -> exit({:bye, [:world]}) end)") =~
+ ~r"\*\* \(EXIT from #PID<\d+\.\d+\.\d+>\) {:bye, \[:world\]}"
end
test "receive exit from exception" do
- # use exit/1 to fake an error so that an error message is not sent to the
- # error logger.
- assert capture_iex("spawn_link(fn -> exit({ArgumentError[],
- [{:not_a_real_module, :function, 0, []}]}) end)") =~ ~r"\*\* \(EXIT from #PID<\d+\.\d+\.\d+>\) an exception was raised:\n\s{4}\*\* \(ArgumentError\) argument error\n\s{8}:not_a_real_module\.function/0"
+ # use exit/1 to fake an error so that an error message
+ # is not sent to the error logger.
+ content = capture_iex("spawn_link(fn -> exit({ArgumentError[],
+ [{:not_a_real_module, :function, 0, []}]}) end)")
+ assert content =~ ~r"\*\* \(EXIT from #PID<\d+\.\d+\.\d+>\) an exception was raised:\n"
+ assert content =~ ~r"\s{4}\*\* \(ArgumentError\) argument error\n"
+ assert content =~ ~r"\s{8}:not_a_real_module\.function/0"
end
test "exit due to failed call" do
- assert capture_iex("exit({:bye, {:gen_server, :call, [self(), :hello]}})") =~ ~r"\*\* \(exit\) exited in: :gen_server\.call\(#PID<\d+\.\d+\.\d+>, :hello\)\n\s{4}\*\* \(EXIT\) :bye"
+ assert capture_iex("exit({:bye, {:gen_server, :call, [self(), :hello]}})") =~
+ ~r"\*\* \(exit\) exited in: :gen_server\.call\(#PID<\d+\.\d+\.\d+>, :hello\)\n\s{4}\*\* \(EXIT\) :bye"
end
-
end
diff --git a/lib/iex/test/iex/options_test.exs b/lib/iex/test/iex/options_test.exs
deleted file mode 100644
index 74c317cd7..000000000
--- a/lib/iex/test/iex/options_test.exs
+++ /dev/null
@@ -1,67 +0,0 @@
-Code.require_file "../test_helper.exs", __DIR__
-
-defmodule IEx.OptionsTest do
- use IEx.Case
-
- @doc """
- Hello, I have %{red}ANSI%{reset} escapes.
- """
- def ansi_escapes, do: :ok
-
- unless match?({:win32,_}, :os.type) do
- test "color" do
- opts = [colors: [enabled: true, eval_result: "red"]]
- assert capture_iex("1 + 2", opts) == "\e[31m3\e[0m"
-
- # Sanity checks
- assert capture_iex("IO.ANSI.escape(\"%{blue}hello\", true)", opts)
- == "\e[31m\"\\e[34mhello\\e[0m\"\e[0m"
- assert capture_iex("IO.puts IO.ANSI.escape(\"%{blue}hello\", true)", opts)
- == "\e[34mhello\e[0m\n\e[31m:ok\e[0m"
- assert capture_iex("IO.puts IO.ANSI.escape(\"%{blue}hello\", true)", [colors: [enabled: false]])
- == "\e[34mhello\e[0m\n:ok"
-
- # Test that ANSI escapes in the docs are left alone
- opts = [colors: [enabled: true]]
- assert capture_iex("h IEx.OptionsTest.ansi_escapes", opts)
- == "* def ansi_escapes()\n\nHello, I have %{red}ANSI%{reset} escapes."
-
- # Test that ANSI escapes in iex output are left alone
- opts = [colors: [enabled: true, eval_result: "red", eval_info: "red"]]
- assert capture_iex("\"%{red} %{blue}\"", opts) == "\e[31m\"%{red} %{blue}\"\e[0m"
- assert capture_iex("IO.puts IEx.color(:eval_info, \"%{red} %{blue}\")", opts)
- == "\e[31m%{red} %{blue}\e[0m\n\e[31m:ok\e[0m"
- end
- end
-
- test "inspect opts" do
- opts = [inspect: [binaries: :as_binaries, char_lists: :as_lists, structs: false, limit: 4]]
- assert capture_iex("<<45,46,47>>\n[45,46,47]\n%IO.Stream{}", opts) ==
- "<<45, 46, 47>>\n[45, 46, 47]\n%{__struct__: IO.Stream, device: nil, line_or_bytes: :line, raw: true}"
- end
-
- test "history size" do
- opts = [history_size: 3]
- assert capture_iex("1\n2\n3\nv(1)", opts) == "1\n2\n3\n1"
- assert "1\n2\n3\n4\n** (RuntimeError) v(1) is out of bounds" <> _ = capture_iex("1\n2\n3\n4\nv(1)", opts)
- assert "1\n2\n3\n4\n** (RuntimeError) v(-4) is out of bounds" <> _ = capture_iex("1\n2\n3\n4\nv(-4)", opts)
- assert "1\n2\n3\n4\n2\n** (RuntimeError) v(2) is out of bounds" <> _ = capture_iex("1\n2\n3\n4\nv(2)\nv(2)", opts)
- end
-
- test "prompt" do
- opts = [prompt: [default: "prompt(%counter)>", alive: "prompt(%counter)"]]
- assert capture_iex("1\n", opts, [], true) == "prompt(1)> 1\nprompt(2)>"
- end
-
- test "bad option" do
- assert_raise ArgumentError, fn ->
- IEx.Options.set :nonexistent_option, nil
- end
- end
-
- test "bad key" do
- assert_raise ArgumentError, fn ->
- IEx.Options.set :colors, nonexistent_color_name: "red"
- end
- end
-end
diff --git a/lib/iex/test/iex/server_test.exs b/lib/iex/test/iex/server_test.exs
index 17b460f80..ecbb65880 100644
--- a/lib/iex/test/iex/server_test.exs
+++ b/lib/iex/test/iex/server_test.exs
@@ -1,12 +1,7 @@
Code.require_file "../test_helper.exs", __DIR__
defmodule IEx.ServerTest do
- use IEx.Case
-
- setup do
- IEx.Options.set :colors, enabled: false
- :ok
- end
+ use IEx.Case, async: true
# Options
diff --git a/lib/iex/test/test_helper.exs b/lib/iex/test/test_helper.exs
index eca77ffe3..38e6f57a2 100644
--- a/lib/iex/test/test_helper.exs
+++ b/lib/iex/test/test_helper.exs
@@ -1,10 +1,11 @@
-:application.start(:iex)
+Application.start(:iex)
+Application.put_env(:iex, :colors, [enabled: false])
ExUnit.start [trace: "--trace" in System.argv]
defmodule IEx.Case do
+ use ExUnit.CaseTemplate
@moduledoc false
- #
# Provides convenience functions for testing IEx-related functionality.
# Use this module inside your test module like this:
#
@@ -20,27 +21,25 @@ defmodule IEx.Case do
# session, except colors are disabled by default and .iex files are not
# loaded.
#
- # You can provide your own IEx.Options and a path to a .iex file as
+ # You can provide your own IEx configuration and a path to a .iex file as
# additional arguments to the capture_iex function.
- #
- defmacro __using__(_) do
+ using do
quote do
- use ExUnit.Case, async: false
import ExUnit.CaptureIO
import unquote(__MODULE__)
+ end
+ end
- setup do
- opts = IEx.Options.get
- IEx.Options.set :colors, [enabled: false]
- {:ok, [iex_opts: opts]}
- end
+ setup do
+ opts = IEx.configuration |>
+ Keyword.take([:default_prompt, :alive_prompt, :inspect, :colors, :history_size])
+ {:ok, [iex_opts: opts]}
+ end
- teardown context do
- IEx.Options.set context[:iex_opts]
- :ok
- end
- end
+ teardown context do
+ IEx.configure context[:iex_opts]
+ :ok
end
@doc """
@@ -54,9 +53,7 @@ defmodule IEx.Case do
IEx.Server.start to be used in the normal .iex loading process.
"""
def capture_iex(input, options \\ [], server_options \\ [], capture_prompt \\ false) do
- Enum.each options, fn {opt, value} ->
- IEx.Options.set(opt, value)
- end
+ IEx.configure(options)
ExUnit.CaptureIO.capture_io([input: input, capture_prompt: capture_prompt], fn ->
server_options = Keyword.put_new(server_options, :dot_iex_path, "")
diff --git a/lib/mix/lib/mix/cli.ex b/lib/mix/lib/mix/cli.ex
index 42f8f86e1..e44d4bf22 100644
--- a/lib/mix/lib/mix/cli.ex
+++ b/lib/mix/lib/mix/cli.ex
@@ -51,6 +51,7 @@ defmodule Mix.CLI do
defp run_task(name, args) do
try do
if Mix.Project.get do
+ Mix.Task.run "loadconfig"
Mix.Task.run "deps.loadpaths", ["--no-deps-check"]
Mix.Task.run "loadpaths", ["--no-elixir-version-check"]
Mix.Task.reenable "deps.loadpaths"
@@ -87,7 +88,8 @@ defmodule Mix.CLI do
end
defp change_env(task) do
- if nil?(System.get_env("MIX_ENV")) && (env = Mix.Project.config[:preferred_cli_env][task]) do
+ if nil?(System.get_env("MIX_ENV")) &&
+ (env = Mix.Project.config[:preferred_cli_env][task]) do
Mix.env(env)
if project = Mix.Project.pop do
{project, _config, file} = project
diff --git a/lib/mix/lib/mix/project.ex b/lib/mix/lib/mix/project.ex
index a1a0c34e8..971fcdd35 100644
--- a/lib/mix/lib/mix/project.ex
+++ b/lib/mix/lib/mix/project.ex
@@ -44,7 +44,10 @@ defmodule Mix.Project do
# Push a project onto the project stack.
# Only the top of the stack can be accessed.
@doc false
- def push(atom, file \\ "nofile") when is_atom(atom) do
+ def push(atom, file \\ nil) when is_atom(atom) do
+ file = file ||
+ (atom && String.from_char_data!(atom.__info__(:compile)[:source]))
+
config = default_config
|> Keyword.merge(get_project_config(atom))
|> Keyword.drop(@private_config)
@@ -67,9 +70,9 @@ defmodule Mix.Project do
# The configuration that is pushed down to dependencies.
@doc false
def deps_config(config \\ config()) do
- [ build_path: build_path(config),
- build_per_environment: config[:build_per_environment],
- deps_path: deps_path(config) ]
+ [build_path: build_path(config),
+ build_per_environment: config[:build_per_environment],
+ deps_path: deps_path(config)]
end
@doc """
@@ -133,14 +136,17 @@ defmodule Mix.Project do
By default it includes the mix.exs file and the lock manifest.
"""
def config_files do
- project = get
- opts = [Mix.Dep.Lock.manifest]
-
- if project && (source = project.__info__(:compile)[:source]) do
- opts = [String.from_char_data!(source)|opts]
- end
-
- opts
+ [Mix.Dep.Lock.manifest] ++
+ case Mix.ProjectStack.peek do
+ {name, config, file} ->
+ configs = config[:config_path] || "config/config.exs"
+ |> Path.dirname
+ |> Path.join("*.exs")
+ |> Path.wildcard
+ [file|configs]
+ _ ->
+ []
+ end
end
@doc """
diff --git a/lib/mix/lib/mix/tasks/loadconfig.ex b/lib/mix/lib/mix/tasks/loadconfig.ex
index 9e5f2d0e9..ee8b133d3 100644
--- a/lib/mix/lib/mix/tasks/loadconfig.ex
+++ b/lib/mix/lib/mix/tasks/loadconfig.ex
@@ -71,7 +71,7 @@ defmodule Mix.Tasks.Loadconfig do
raise Mix.Error, message: "umbrella child #{inspect dep.app} has set the configuration for " <>
"key #{inspect k} in app #{inspect app} to #{inspect v2} but another umbrella child has " <>
"already set it to #{inspect v1}. You need to remove the configuration or resolve " <>
- "the conflict by setting a value in the umbrella config"
+ "the conflict by defining a config file and setting a value in your umbrella project"
end
end)
end
diff --git a/lib/mix/lib/mix/tasks/new.ex b/lib/mix/lib/mix/tasks/new.ex
index dc5c18541..a6f168866 100644
--- a/lib/mix/lib/mix/tasks/new.ex
+++ b/lib/mix/lib/mix/tasks/new.ex
@@ -79,6 +79,9 @@ defmodule Mix.Tasks.New do
create_file "lib/#{app}.ex", lib_app_template(assigns)
create_directory "lib/#{app}"
create_file "lib/#{app}/supervisor.ex", lib_supervisor_template(assigns)
+
+ create_directory "config"
+ create_file "config/config.exs", config_template(assigns)
end
create_directory "test"
@@ -259,6 +262,29 @@ defmodule Mix.Tasks.New do
end
"""
+ embed_template :config, """
+ # This file is responsible for configuring your application and
+ # its dependencies. It must return a keyword list containing the
+ # application name and another keyword list with the application
+ # key-value pairs.
+
+ # Note this configuration is loaded before any dependency and is
+ # restricted to this project. If another project depends on this
+ # project, this file won't be loaded nor affect the parent project.
+
+ # You can customize the configuration path by setting :config_path
+ # in your mix.exs file. For example, you can emulate configuration
+ # per environment by setting:
+ #
+ # config_path: "config/\#{Mix.env}.exs"
+ #
+ # Changing any file inside the config directory causes the whole
+ # project to be recompiled.
+
+ [dep1: [key: :value],
+ dep2: [key: :value]]
+ """
+
embed_template :lib, """
defmodule <%= @mod %> do
end
diff --git a/lib/mix/test/mix/project_test.exs b/lib/mix/test/mix/project_test.exs
index 157901749..1a0aaed5b 100644
--- a/lib/mix/test/mix/project_test.exs
+++ b/lib/mix/test/mix/project_test.exs
@@ -5,7 +5,7 @@ defmodule Mix.ProjectTest do
defmodule SampleProject do
def project do
- [ hello: "world" ]
+ [hello: "world"]
end
end
diff --git a/lib/mix/test/mix/tasks/loadconfig_test.exs b/lib/mix/test/mix/tasks/loadconfig_test.exs
index ed7284c76..fb0b3dc1d 100644
--- a/lib/mix/test/mix/tasks/loadconfig_test.exs
+++ b/lib/mix/test/mix/tasks/loadconfig_test.exs
@@ -7,8 +7,8 @@ defmodule Mix.Tasks.LoadconfigTest do
teardown do
Enum.each @apps, fn app ->
- Enum.each :application.get_all_env(app), fn {key, _} ->
- :application.unset_env(app, key, persist: true)
+ Enum.each Application.get_all_env(app), fn {key, _} ->
+ Application.delete_env(app, key, persist: true)
end
end
:ok