Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a77057910 | ||
|
|
e2be2579c5 | ||
|
|
f9ec6cfb5f | ||
|
|
cc1d7c5823 | ||
|
|
7649a5dba4 | ||
|
|
d221e9a161 | ||
|
|
4638831103 | ||
|
|
60b2928678 | ||
|
|
3f0c583045 | ||
|
|
0ce2fbb3e4 | ||
|
|
04c5c80353 | ||
|
|
1cb8f3a654 | ||
|
|
7b025dd982 | ||
|
|
eff51e4045 | ||
|
|
ea5df228ed | ||
|
|
769b69ad69 | ||
|
|
0e725fb906 | ||
|
|
4b317a75d8 | ||
|
|
2977a46865 | ||
|
|
d92b2e6831 | ||
|
|
0a7f6e7cd2 | ||
|
|
8bd43ed677 | ||
|
|
bcf8563d38 | ||
|
|
352379112a | ||
|
|
f8c66c8516 | ||
|
|
f090045e99 | ||
|
|
4256daf6a7 | ||
|
|
8c77ada8d2 | ||
|
|
63ed270c68 | ||
|
|
acdc55bd90 | ||
|
|
d2bfd10299 | ||
|
|
b2e32e4ccd | ||
|
|
0c80905f63 | ||
|
|
429fc2094b | ||
|
|
e8c3ed2881 | ||
|
|
3d583e7cdc | ||
|
|
7b1b0d93d8 | ||
|
|
9839bfa4ad | ||
|
|
0b640d985c | ||
|
|
b161d80b4c | ||
|
|
748b70d796 | ||
|
|
0267f312b1 | ||
|
|
8a45ca8905 | ||
|
|
05c3e98f64 | ||
|
|
212acf437a | ||
|
|
fc0af0542b | ||
|
|
14272c98b4 | ||
|
|
42ffd31e0e | ||
|
|
7aeb1b6930 | ||
|
|
f3f64cdba2 | ||
|
|
5b8a30be6b | ||
|
|
e90b877575 | ||
|
|
3d2ad9ac77 | ||
|
|
881bdd5534 | ||
|
|
c3bdfc9ba7 | ||
|
|
262e8b5f22 | ||
|
|
2f5afcaab9 | ||
|
|
4e42b75e17 | ||
|
|
c6f117d60f | ||
|
|
80c0ff84fb | ||
|
|
59b4b5b9bc | ||
|
|
2945939f3e | ||
|
|
3d9aa3e978 | ||
|
|
860fcc6eaa | ||
|
|
73f40cb10e | ||
|
|
8b56252e1b | ||
|
|
b8a0717873 | ||
|
|
8dbfcba237 | ||
|
|
5bb4f43e9d | ||
|
|
808f8981b3 | ||
|
|
73049ac355 | ||
|
|
d77ccf9415 | ||
|
|
9e644dfba1 | ||
|
|
e00eedec45 | ||
|
|
4016864db2 | ||
|
|
38c7edd203 | ||
|
|
fe3ab0cb77 | ||
|
|
9f81ac89e5 | ||
|
|
cd4c1976c9 | ||
|
|
87be134a5b | ||
|
|
a5ea8a63bf | ||
|
|
742258340c | ||
|
|
1be3335f0b | ||
|
|
307198c750 |
+88
-53
@@ -1,6 +1,41 @@
|
||||
# v0.10.3 (2013-10-02)
|
||||
|
||||
* Enhancements
|
||||
* [Enum] Add `Enum.take_every/2`
|
||||
* [IEx] IEx now respects signals sent from the Ctrl+G menu
|
||||
* [Kernel] Allow documentation for types with `@typedoc`
|
||||
* [Mix] Allow apps to be selected in umbrella projects
|
||||
* [Record] Generated record functions `new` and `update` also take options with strings as keys
|
||||
* [Stream] Add `Stream.unfold/1`
|
||||
|
||||
* Bug fixes
|
||||
* [Dict] Fix a bug when a HashDict was marked as equal when one was actually a subset of the other
|
||||
* [EEx] Solve issue where `do` blocks inside templates were not properly aligned
|
||||
* [ExUnit] Improve checks and have better error reports on poorly aligned doctests
|
||||
* [Kernel] Fix handling of multiple heredocs on the same line
|
||||
* [Kernel] Provide better error messages for match, guard and quoting errors
|
||||
* [Kernel] Make `Kernel.raise/2` a macro to avoid messing up stacktraces
|
||||
* [Kernel] Ensure `&()` works on quoted blocks with only one expression
|
||||
* [Mix] Address an issue where a dependency was not compiled in the proper order when specified in different projects
|
||||
* [Mix] Ensure `compile: false` is a valid mechanism for disabling the compilation of dependencies
|
||||
* [Regex] Fix bug on `Regex.scan/3` when capturing groups and the regex has no groups
|
||||
* [String] Fix a bug with `String.split/2` when given an empty pattern
|
||||
* [Typespec] Guarantee typespecs error reports point to the proper line
|
||||
|
||||
* Deprecations
|
||||
* [Kernel] The previous partial application syntax (without the `&` operator) has now been deprecated
|
||||
* [Regex] `Regex.captures/3` is deprecated in favor of `Regex.named_captures/3`
|
||||
* [String] `String.valid_codepoint?/1` is deprecated in favor of pattern matching with `<<_ :: utf8 >>`
|
||||
|
||||
* Backwards incompatible changes
|
||||
* [IEx] The `r/0` helper has been removed as it caused surprising behaviour when many modules with dependencies were accumulated
|
||||
* [Mix] `Mix.Version` was renamed to `Version`
|
||||
* [Mix] `File.IteratorError` was renamed to `IO.StreamError`
|
||||
* [Mix] `mix new` now defaults to the `--sup` option, use `--bare` to get the previous behaviour
|
||||
|
||||
# v0.10.2 (2013-09-03)
|
||||
|
||||
* enhancements
|
||||
* Enhancements
|
||||
* [CLI] Add `--verbose` to elixirc, which now is non-verbose by default
|
||||
* [Dict] Add `Dict.Behaviour` as a convenience to create your own dictionaries
|
||||
* [Enum] Add `Enum.split/2`, `Enum.reduce/2`, `Enum.flat_map/2`, `Enum.chunks/2`, `Enum.chunks/4`, `Enum.chunks_by/2`, `Enum.concat/1` and `Enum.concat/2`
|
||||
@@ -24,7 +59,7 @@
|
||||
* [String] Add `String.ljust/2`, `String.rjust/2`, `String.ljust/3` and `String.rjust/3`
|
||||
* [URI] `URI.parse/1` supports IPv6 addresses
|
||||
|
||||
* bug fix
|
||||
* Bug fixes
|
||||
* [Behaviour] Do not compile behaviour docs if docs are disabled on compilation
|
||||
* [ExUnit] Doctests no longer eat too much space and provides detailed reports for poorly indented lines
|
||||
* [File] Fix a bug where `File.touch(file, datetime)` was not setting the proper datetime when the file did not exist
|
||||
@@ -42,7 +77,7 @@
|
||||
* [String] `String.slice/3` still returns the proper result when there is no length to be extracted
|
||||
* [System] `System.get_env/0` now returns a list of tuples as previously advertised
|
||||
|
||||
* deprecations
|
||||
* Deprecations
|
||||
* [Dict] `Dict.update/3` is deprecated in favor of `Dict.update!/3`
|
||||
* [Enum] `Enum.min/2` and `Enum.max/2` are deprecated in favor of `Enum.min_by/2` and `Enum.max_by/2`
|
||||
* [Enum] `Enum.join/2` and `Enum.map_join/3` with a char list are deprecated
|
||||
@@ -55,7 +90,7 @@
|
||||
* [Macro] `Macro.unescape_binary/1` and `Macro.unescape_binary/2` are deprecated in favor of `Macro.unescape_string/1` and `Macro.unescape_string/2`
|
||||
* [Mix] `:umbrella` option for umbrella paths has been deprecated in favor of `:in_umbrella`
|
||||
|
||||
* backwards incompatible changes
|
||||
* Backwards incompatible changes
|
||||
* [IO] IO functions now only accept iolists as arguments
|
||||
* [Kernel] `Binary.Chars` was renamed to `String.Chars`
|
||||
* [Kernel] The previous ambiguous import syntax `import :functions, Foo` was removed in favor of `import Foo, only: :functions`
|
||||
@@ -63,7 +98,7 @@
|
||||
|
||||
# v0.10.1 (2013-08-03)
|
||||
|
||||
* enhancements
|
||||
* Enhancements
|
||||
* [Behaviour] Add support for `defmacrocallback/1`
|
||||
* [Enum] Add `Enum.shuffle/1`
|
||||
* [ExUnit] The `:trace` option now also reports run time for each test
|
||||
@@ -86,7 +121,7 @@
|
||||
* [String] Add `Regex.scan/3` now supports capturing groups
|
||||
* [String] Add `String.reverse/1`
|
||||
|
||||
* bug fix
|
||||
* Bug fixes
|
||||
* [Behaviour] Ensure callbacks are stored in the definition order
|
||||
* [CLI] Speed up boot time on Elixir .bat files
|
||||
* [IEx] Reduce cases where IEx parser can get stuck
|
||||
@@ -101,19 +136,19 @@
|
||||
* [OptionParser] Ensure `:keep` and `:integer`|`:float` can be given together as options
|
||||
* [OptionParser] Ensure `--no-flag` sets `:flag` to false when `:flag` is a registered boolean switch
|
||||
|
||||
* deprecations
|
||||
* Deprecations
|
||||
* [Kernel] `function(Mod.fun/arity)` and `function(fun/arity)` are deprecated in favor of `&Mod.fun/arity` and `&fun/arity`
|
||||
* [Kernel] `function/3` is deprecated in favor of `Module.function/3`
|
||||
* [Kernel] `Kernel.ParallelCompiler` now receives a set of callbacks instead of a single one
|
||||
* [Mix] `:test_coverage` option now expect keywords arguments and the `--cover` flag is now treated as a boolean
|
||||
|
||||
* backwards incompatible changes
|
||||
* Backwards incompatible changes
|
||||
* [Regex] `Regex.scan/3` now always returns a list of lists, normalizing the result, instead of list with mixed lists and binaries
|
||||
* [System] `System.halt/2` was removed since the current Erlang implementation of such function is bugged
|
||||
|
||||
# v0.10.0 (2013-07-15)
|
||||
|
||||
* enhancements
|
||||
* Enhancements
|
||||
* [ExUnit] Support `trace: true` option which gives detailed reporting on test runs
|
||||
* [HashDict] Optimize `HashDict` to store pairs in a cons cell reducing storage per key by half
|
||||
* [Kernel] Add pretty printing support for inspect
|
||||
@@ -126,7 +161,7 @@
|
||||
* [Mix] Projects can now define an `:elixir` key to outline supported Elixir versions
|
||||
* [Typespec] Improve error messages to contain file, line and the typespec itself
|
||||
|
||||
* bug fix
|
||||
* Bug fixes
|
||||
* [CLI] Elixir can now run on Unix directories with `:` in its path
|
||||
* [Kernel] `match?/2` does not leak variables to outer scope
|
||||
* [Kernel] Keep `head|tail` format when splicing at the tail
|
||||
@@ -135,7 +170,7 @@
|
||||
* [Mix] Empty projects no longer leave empty ebin files on `mix compile`
|
||||
* [Module] Calling `Module.register_attribute/3` no longer automatically changes it to persisted or accumulated
|
||||
|
||||
* deprecations
|
||||
* Deprecations
|
||||
* [Enum] Receiving the index of iteration in `Enum.map/2` and `Enum.each/2` is deprecated in favor of `Stream.with_index/1`
|
||||
* [File] `File.iterator/1` and `File.biniterator/1` are deprecated in favor of `IO.stream/1` and `IO.binstream/1`
|
||||
* [File] `File.iterator!/2` and `File.biniterator!/2` are deprecated in favor of `File.stream!/2` and `File.binstream!/2`
|
||||
@@ -144,7 +179,7 @@
|
||||
* [Mix] Deprecate `mix run EXPR` in favor of `mix run -e EXPR`
|
||||
* [Record] `Record.__index__/2` deprecated in favor of `Record.__record__(:index, key)`
|
||||
|
||||
* backwards incompatible changes
|
||||
* Backwards incompatible changes
|
||||
* [Kernel] The `Binary.Inspect` protocol has been renamed to `Inspect`
|
||||
* [Kernel] Tighten up the grammar rules regarding parentheses omission, previously the examples below would compile but now they raise an error message:
|
||||
|
||||
@@ -157,7 +192,7 @@
|
||||
|
||||
# v0.9.3 (2013-06-23)
|
||||
|
||||
* enhancements
|
||||
* Enhancements
|
||||
* [File] Add `File.chgrp`, `File.chmod` and `File.chown`
|
||||
* [Kernel] Add `--warnings-as-errors` to Elixir's compiler options
|
||||
* [Kernel] Print warnings to stderr
|
||||
@@ -171,7 +206,7 @@
|
||||
* [Mix] `github: ...` shortcut now uses the faster `git` schema instead of `https`
|
||||
* [Record] Allow types to be given to `defrecordp`
|
||||
|
||||
* bug fix
|
||||
* Bug fixes
|
||||
* [Kernel] The elixir executable on Windows now supports the same options as the UNIX one
|
||||
* [Kernel] Improve error messages on default clauses clash
|
||||
* [Kernel] `__MODULE__.Foo` now returns `Foo` when outside of a Module
|
||||
@@ -180,12 +215,12 @@
|
||||
* [Kernel] Allow `(x, y) when z` in function clauses and try expressions
|
||||
* [Mix] Mix now properly evaluates rebar scripts
|
||||
|
||||
* deprecations
|
||||
* Deprecations
|
||||
* [Code] `Code.string_to_ast/1` has been deprecated in favor of `Code.string_to_quoted/1`
|
||||
* [Macro] `Macro.to_binary/1` has been deprecated in favor of `Macro.to_string/1`
|
||||
* [Typespec] Deprecate `(fun(...) -> ...)` in favor of `(... -> ...)`
|
||||
|
||||
* backwards incompatible changes
|
||||
* Backwards incompatible changes
|
||||
* [Bitwise] Precedence of operators used by the Bitwise module were changed, check `elixir_parser.yrl` for more information
|
||||
* [File] `rm_rf` and `cp_r` now returns a tuple with three elements on failures
|
||||
* [Kernel] The quoted representation for `->` clauses changed from a tuple with two elements to a tuple with three elements to support metadata
|
||||
@@ -195,7 +230,7 @@
|
||||
|
||||
# v0.9.2 (2013-06-13)
|
||||
|
||||
* enhancements
|
||||
* Enhancements
|
||||
* [ExUnit] `capture_io` now captures prompt by default
|
||||
* [Mix] Automatically import git dependencies from Rebar
|
||||
* [Mix] Support for dependencies directly from the umbrella application
|
||||
@@ -203,7 +238,7 @@
|
||||
* [String] Add `String.contains?`
|
||||
* [URI] Implement `Binary.Chars` (aka `to_binary`) for `URI.Info`
|
||||
|
||||
* bug fix
|
||||
* Bug fixes
|
||||
* [HashDict] Ensure HashDict uses exact match throughout its implementation
|
||||
* [IEx] Do not interpret ANSI codes in IEx results
|
||||
* [IEx] Ensure `--cookie` is set before accessing remote shell
|
||||
@@ -211,21 +246,21 @@
|
||||
* [Mix] Fix usage of shell expressions in `Mix.Shell.cmd`
|
||||
* [Mix] Start the application by default on escripts
|
||||
|
||||
* deprecations
|
||||
* Deprecations
|
||||
* [Regex] `Regex.index/2` is deprecated in favor `Regex.run/3`
|
||||
* [Kernel] `super` no longer supports implicit arguments
|
||||
|
||||
* backwards incompatible changes
|
||||
* Backwards incompatible changes
|
||||
* [Kernel] The `=~` operator now returns true or false instead of an index
|
||||
|
||||
# v0.9.1 (2013-05-30)
|
||||
|
||||
* enhancements
|
||||
* Enhancements
|
||||
* [IEx] Limit the number of entries kept in history and allow it to be configured
|
||||
* [Kernel] Add `String.start_with?` and `String.end_with?`
|
||||
* [Typespec] Allow keywords, e.g. `[foo: integer, bar: boolean | module]`, in typespecs
|
||||
|
||||
* bug fix
|
||||
* Bug fixes
|
||||
* [Dict] `Enum.to_list` and `Dict.to_list` now return the same results for dicts
|
||||
* [IEx] Enable shell customization via the `IEx.Options` module
|
||||
* [Kernel] Fix a bug where `unquote_splicing` did not work on the left side of a stab op
|
||||
@@ -236,12 +271,12 @@
|
||||
|
||||
# v0.9.0 (2013-05-23)
|
||||
|
||||
* enhancements
|
||||
* Enhancements
|
||||
* [ExUnit] `ExUnit.CaptureIO` now accepts an input to be used during capture
|
||||
* [IEx] Add support for .iex files that are loaded during shell's boot process
|
||||
* [IEx] Add `import_file/1` helper
|
||||
|
||||
* backwards incompatible changes
|
||||
* Backwards incompatible changes
|
||||
* [Enum] `Enum.Iterator` was replaced by the more composable and functional `Enumerable` protocol which supports reductions
|
||||
* [File] `File.iterator/1` and `File.biniterator/1` have been removed in favor of the safe `File.iterator!/1` and `File.biniterator!/1` ones
|
||||
* [Kernel] Erlang R15 is no longer supported
|
||||
@@ -249,7 +284,7 @@
|
||||
|
||||
# v0.8.3 (2013-05-22)
|
||||
|
||||
* enhancements
|
||||
* Enhancements
|
||||
* [CLI] Flags `-p` and `-pr` fails if pattern match no files
|
||||
* [CLI] Support `--hidden` and `--cookie` flags for distributed Erlang
|
||||
* [Enum] Add `Enum.to_list/1`, `Enum.member?/2`, `Enum.uniq/2`, `Enum.max/1`, `Enum.max/2`, `Enum.min/1` and `Enum.min/2`
|
||||
@@ -272,7 +307,7 @@
|
||||
* [Mix] Load beam files available at `MIX_PATH` on CLI usage
|
||||
* [String] Add `String.valid?` and `String.valid_character?`
|
||||
|
||||
* bug fix
|
||||
* Bug fixes
|
||||
* [ExUnit] Handle exit messages from in ExUnit
|
||||
* [ExUnit] Failures on ExUnit's setup_all now invalidates all tests
|
||||
* [Kernel] Ensure we don't splice keyword args unecessarily
|
||||
@@ -284,7 +319,7 @@
|
||||
* [Regex] Fix badmatch with `Regex.captures(%r/(.)/g, "cat")`
|
||||
* [URI] Downcase host and scheme and URIs
|
||||
|
||||
* deprecations
|
||||
* Deprecations
|
||||
* [Code] `Code.eval` is deprecated in favor of `Code.eval_string`
|
||||
* [Exception] `Exception.format_entry` is deprecated in favor of `Exception.format_stacktrace_entry`
|
||||
* [ExUnit] `assert left inlist right` is deprecated in favor of `assert left in right`
|
||||
@@ -293,7 +328,7 @@
|
||||
* [Kernel] `var_context` in quote was deprecated in favor of `context`
|
||||
* [Kernel] `Enum.at!` and `Dict.get!` is deprecated in favor of `Enum.fetch!` and `Dict.fetch!`
|
||||
|
||||
* backwards incompatible changes
|
||||
* Backwards incompatible changes
|
||||
* [Dict] `List.Dict` was moved to `ListDict`
|
||||
* [IO] `IO.gets`, `IO.getn` and friends now return binaries when reading from stdio
|
||||
* [Kernel] Precedence of `|>` has changed to lower to support constructs like `1..5 |> Enum.to_list`
|
||||
@@ -301,7 +336,7 @@
|
||||
|
||||
# v0.8.2 (2013-04-20)
|
||||
|
||||
* enhancements
|
||||
* Enhancements
|
||||
* [ExUnit] Use ANSI escape codes in CLI output
|
||||
* [ExUnit] Include suite run time on CLI results
|
||||
* [ExUnit] Add support to doctests, allowing test cases to be generated from code samples
|
||||
@@ -314,7 +349,7 @@
|
||||
* [Mix] Support `--cover` on mix test and `test_coverage` on Mixfiles
|
||||
* [Record] Each record now provides `Record.options` with the options supported by its `new` and `update` functions
|
||||
|
||||
* bug fix
|
||||
* Bug fixes
|
||||
* [Binary] inspect no longer escapes standalone hash `#`
|
||||
* [IEx] The `h` helper can now retrieve docs for special forms
|
||||
* [Kernel] Record optimizations were not being triggered in functions inside the record module
|
||||
@@ -322,11 +357,11 @@
|
||||
* [Kernel] Fix a bug where nested records could not use the Record[] syntax
|
||||
* [Path] Fix a bug on `Path.expand` when expanding paths starting with `~`
|
||||
|
||||
* deprecations
|
||||
* Deprecations
|
||||
* [Kernel] `setelem/3` is deprecated in favor of `set_elem/3`
|
||||
* [Kernel] `function(:is_atom, 1)` is deprecated in favor of `function(is_atom/1)`
|
||||
|
||||
* backwards incompatible changes
|
||||
* Backwards incompatible changes
|
||||
* [Kernel] `unquote` now only applies to the closest quote. If your code contains a quote that contains another quote that calls unquote, it will no longer work. Use `Macro.escape` instead and pass your quoted contents up in steps, for example:
|
||||
|
||||
quote do
|
||||
@@ -341,7 +376,7 @@
|
||||
|
||||
# v0.8.1 (2013-02-17)
|
||||
|
||||
* enhancements
|
||||
* Enhancements
|
||||
* [ExUnit] Tests can now receive metadata set on setup/teardown callbacks
|
||||
* [ExUnit] Add support to ExUnit.CaseTemplate to share callbacks in between test cases
|
||||
* [IO] Add `IO.ANSI` to make it easy to write ANSI escape codes
|
||||
@@ -354,13 +389,13 @@
|
||||
* [OptionParser] Switches are now overridden by default but can be kept in order if chosen
|
||||
* [Typespec] Better error reporting for invalid typespecs
|
||||
|
||||
* bug fix
|
||||
* Bug fixes
|
||||
* [Mix] Allow Mix projects to be generated with just one letter
|
||||
|
||||
* backwards incompatible changes
|
||||
* Backwards incompatible changes
|
||||
* [Kernel] `before_compile` and `after_compile` callbacks now receive the environment as first argument instead of the module
|
||||
|
||||
* deprecations
|
||||
* Deprecations
|
||||
* [ExUnit] Explicitly defined test/setup/teardown functions are deprecated
|
||||
* [Kernel] Tidy up and clean `quote` API
|
||||
* [Kernel] Old `:local.(args)` syntax is deprecated
|
||||
@@ -368,7 +403,7 @@
|
||||
|
||||
# v0.8.0 (2013-01-28)
|
||||
|
||||
* enhancements
|
||||
* Enhancements
|
||||
* [Binary] Support `<< "string" :: utf8 >>` as in Erlang
|
||||
* [Binary] Support `\a` escape character in binaries
|
||||
* [Binary] Support syntax shortcut for specifying size in bit syntax
|
||||
@@ -391,16 +426,16 @@
|
||||
* [String] Add `String.capitalize` and `String.slice`
|
||||
* [System] Add `System.tmp_dir`, `System.cwd` and `System.user_home`
|
||||
|
||||
* bug fix
|
||||
* Bug fixes
|
||||
* [Kernel] `import` with `only` accepts functions starting with underscore
|
||||
* [String] `String.first` and `String.last` return nil for empty binaries
|
||||
* [String] `String.rstrip` and `String.lstrip` now verify if argument is a binary
|
||||
* [Typespec] Support `...` inside typespec's lists
|
||||
|
||||
* backwards incompatible changes
|
||||
* Backwards incompatible changes
|
||||
* [Kernel] The AST now allows metadata to be attached to each node. This means the second item in the AST is no longer an integer (representing the line), but a keywords list. Code that relies on the line information from AST or that manually generate AST nodes need to be properly updated
|
||||
|
||||
* deprecations
|
||||
* Deprecations
|
||||
* [Dict] Deprecate `Binary.Dict` and `OrdDict` in favor of `HashDict` and `ListDict`
|
||||
* [File] Deprecate path related functions in favor of the module `Path`
|
||||
* [Kernel] The `/>` operator has been deprecated in favor of `|>`
|
||||
@@ -410,7 +445,7 @@
|
||||
|
||||
# v0.7.2 (2012-12-04)
|
||||
|
||||
* enhancements
|
||||
* Enhancements
|
||||
* [CLI] `--debug-info` is now true by default
|
||||
* [ExUnit] Make ExUnit exit happen in two steps allowing developers to add custom `at_exit` hooks
|
||||
* [IEx] Many improvements to helpers functions `h/1`, `s/1` and others
|
||||
@@ -423,12 +458,12 @@
|
||||
* [Record] Record accessors are now optimized and can be up to 6x faster in some cases
|
||||
* [String] Support `\xXX` and `\x{HEX}` escape sequences in strings, char lists and regexes
|
||||
|
||||
* bug fix
|
||||
* Bug fixes
|
||||
* [Bootstrap] Compiling Elixir source no longer fails if environment variables contain utf-8 entries
|
||||
* [IEx] IEx will now wait for all command line options to be processed before starting
|
||||
* [Kernel] Ensure proper stacktraces when showing deprecations
|
||||
|
||||
* deprecations
|
||||
* Deprecations
|
||||
* [Enum] `Enum.qsort` is deprecated in favor of `Enum.sort`
|
||||
* [List] `List.sort` and `List.uniq` have been deprecated in favor of their `Enum` counterparts
|
||||
* [Record] Default-based generated functions are deprecated
|
||||
@@ -436,7 +471,7 @@
|
||||
|
||||
# v0.7.1 (2012-11-18)
|
||||
|
||||
* enhancements
|
||||
* Enhancements
|
||||
* [IEx] Only show documented functions and also show docs for default generated functions
|
||||
* [IO] Add `IO.binread`, `IO.binwrite` and `IO.binreadline` to handle raw binary file operations
|
||||
* [ExUnit] Add support for user configuration at `HOME/.ex_unit.exs`
|
||||
@@ -454,20 +489,20 @@
|
||||
* [Typespec] Define a default type `t` for protocols and records
|
||||
* [Typespec] Add support for the access protocol in typespecs
|
||||
|
||||
* bug fix
|
||||
* Bug fixes
|
||||
* [Kernel] Fix an issue where variables inside clauses remained unassigned
|
||||
* [Kernel] Ensure `defoverridable` functions can be referred in many clauses
|
||||
* [Kernel] Allow keywords as function names when following a dot (useful when integrating with erlang libraries)
|
||||
* [File] File is opened by default on binary mode instead of utf-8
|
||||
|
||||
* deprecations
|
||||
* Deprecations
|
||||
* [Behaviour] `defcallback/1` is deprecated in favor of `defcallback/2` which matches erlang `@callbacks`
|
||||
* [Enum] `Enum.times` is deprecated in favor of using ranges
|
||||
* [System] `halt` moved to `System` module
|
||||
|
||||
# v0.7.0 (2012-10-20)
|
||||
|
||||
* enhancements
|
||||
* Enhancements
|
||||
* [Behaviour] Add Behaviour with a simple callback DSL to define callbacks
|
||||
* [Binary] Add a Dict binary that converts its keys to binaries on insertion
|
||||
* [Binary] Optimize `Binary.Inspect` and improve inspect for floats
|
||||
@@ -493,7 +528,7 @@
|
||||
* [Regex] Add `:return` option to `Regex.run` and `Regex.scan`
|
||||
* [String] Add a String module responsible for handling UTf-8 binaries
|
||||
|
||||
* bug fix
|
||||
* Bug fixes
|
||||
* [File] `File.cp` and `File.cp_r` now preserves the file's mode
|
||||
* [IEx] Fix a bug where printing to `:stdio` on `IEx` was causing it to hang
|
||||
* [Macro] Fix a bug where quoted expressions were not behaving the same as their non-quoted counterparts
|
||||
@@ -501,12 +536,12 @@
|
||||
* [Mix] Mix now exits with status 1 in case of failures
|
||||
* [Protocol] Avoid false positives on protocol dispatch (a bug caused the dispatch to be triggered to an invalid protocol)
|
||||
|
||||
* backwards incompatible changes
|
||||
* Backwards incompatible changes
|
||||
* [ExUnit] `setup` and `teardown` callbacks now receives the test name as second argument
|
||||
* [Kernel] Raw function definition with `def/4`, `defp/4`, `defmacro/4`, `defmacrop/4` now evaluates all arguments. The previous behaviour was accidental and did not properly evaluate all arguments
|
||||
* [Kernel] Change tuple-related (`elem` and `setelem`), Enum functions (`find_index`, `nth!` and `times`) and List functions (List.key*) to zero-index
|
||||
|
||||
* deprecations
|
||||
* Deprecations
|
||||
* [Code] `Code.require_file` and `Code.load_file` now expect the full name as argument
|
||||
* [Enum] `List.reverse/1` and `List.zip/2` were moved to `Enum`
|
||||
* [GenServer] Rename `GenServer.Behavior` to `GenServer.Behaviour`
|
||||
@@ -516,14 +551,14 @@
|
||||
|
||||
# v0.6.0 (2012-08-01)
|
||||
|
||||
* incompatible changes
|
||||
* Backwards incompatible changes
|
||||
* [Kernel] Compile files now follow `Elixir-ModuleName` convention to solve issues with Erlang embedded mode. This removes the `__MAIN__` pseudo-variable as modules are now located inside `Elixir` namespace
|
||||
* [Kernel] `__using__` callback triggered by `use` now receives just one argument. Caller information can be accessed via macros using `__CALLER__`
|
||||
* [Kernel] Comprehensions syntax changed to be more compatible with Erlang behavior
|
||||
* [Kernel] loop and recur are removed in favor of recursion with named functions
|
||||
* [Module] Removed data functions in favor of unifying the attributes API
|
||||
|
||||
* deprecations
|
||||
* Deprecations
|
||||
* [Access] The semantics of the access protocol were reduced from a broad query API to simple data structure key-based access
|
||||
* [ExUnit] Some assertions are deprecated in favor of simply using `assert()`
|
||||
* [File] `File.read_info` is deprecated in favor of `File.stat`
|
||||
@@ -535,7 +570,7 @@
|
||||
* [Module] `Module.function_defined?` is deprecated in favor of `Module.defines?`
|
||||
* [Module] `Module.defined_functions` is deprecated in favor of `Module.definitions_in`
|
||||
|
||||
* enhancements
|
||||
* Enhancements
|
||||
* [Enum] Enhance Enum protocol to support `Enum.count`
|
||||
* [Enum] Optimize functions when a list is given as collection
|
||||
* [Enum] Add `find_index`, `nth!` and others
|
||||
|
||||
@@ -38,4 +38,4 @@ readlink_f () {
|
||||
|
||||
SELF=$(readlink_f "$0")
|
||||
SCRIPT_PATH=$(dirname "$SELF")
|
||||
ELIXIR_NO_CLI=1 exec "$SCRIPT_PATH"/elixir --no-halt --erl "-nouser -s Elixir.IEx.CLI" +iex "$@"
|
||||
ELIXIR_NO_CLI=1 exec "$SCRIPT_PATH"/elixir --no-halt --erl "-user Elixir.IEx.CLI" +iex "$@"
|
||||
|
||||
@@ -31,7 +31,7 @@ defmodule EEx.Compiler do
|
||||
defp generate_buffer([{ :start_expr, line, mark, chars }|t], buffer, scope, state) do
|
||||
{ contents, t } = generate_buffer(t, "", [chars|scope], state.dict([]).line(line).start_line(line))
|
||||
buffer = state.engine.handle_expr(buffer, mark, contents)
|
||||
generate_buffer(t, buffer, scope, state.dict([]))
|
||||
generate_buffer(t, buffer, scope, state)
|
||||
end
|
||||
|
||||
defp generate_buffer([{ :middle_expr, line, _, chars }|t], buffer, [current|scope], state) do
|
||||
@@ -68,7 +68,7 @@ defmodule EEx.Compiler do
|
||||
else
|
||||
key = length(state.dict)
|
||||
placeholder = '__EEX__(' ++ integer_to_list(key) ++ ');'
|
||||
{ current ++ placeholder ++ new_lines ++ chars, state.update_dict([{key, buffer}|&1]) }
|
||||
{ current ++ placeholder ++ new_lines ++ chars, state.update_dict(&[{key, buffer}|&1]) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -108,7 +108,7 @@ defmodule EEx.Compiler do
|
||||
end
|
||||
|
||||
defp insert_quotes(list, dict) when is_list(list) do
|
||||
Enum.map list, insert_quotes(&1, dict)
|
||||
Enum.map list, &insert_quotes(&1, dict)
|
||||
end
|
||||
|
||||
defp insert_quotes(other, _dict) do
|
||||
|
||||
@@ -136,7 +136,7 @@ defmodule EEx.Tokenizer do
|
||||
end
|
||||
|
||||
defp end_index(tokens) do
|
||||
Enum.find_index(tokens, match?({ :end, _ }, &1)) || :infinity
|
||||
Enum.find_index(tokens, &match?({ :end, _ }, &1)) || :infinity
|
||||
end
|
||||
|
||||
defp check_spaces(string, token) do
|
||||
|
||||
@@ -264,6 +264,21 @@ foo
|
||||
assert_eval expected, string
|
||||
end
|
||||
|
||||
test "evaluates nested do expressions" do
|
||||
string = """
|
||||
<% y = ["a", "b", "c"] %>
|
||||
<%= cond do %>
|
||||
<% "a" in y -> %>
|
||||
Good
|
||||
<% true -> %>
|
||||
<% if true do %>true<% else %>false<% end %>
|
||||
Bad
|
||||
<% end %>
|
||||
"""
|
||||
|
||||
assert_eval "\n\n Good\n \n", string
|
||||
end
|
||||
|
||||
test "unicode" do
|
||||
template = """
|
||||
• <%= "•" %> •
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
module=nil, %% the current module
|
||||
function=nil, %% the current function
|
||||
vars=[], %% a dict of defined variables and their alias
|
||||
list_vars=nil, %% a list of vars passed down to Macro.Env
|
||||
backup_vars=nil, %% a copy of vars to be used on ^var
|
||||
temp_vars=nil, %% a set of all variables defined in a particular assign
|
||||
clause_vars=nil, %% a dict of all variables defined in a particular clause
|
||||
|
||||
@@ -166,7 +166,7 @@ defmodule Code do
|
||||
end
|
||||
|
||||
defp validate_requires(kind, requires) do
|
||||
valid = is_list(requires) and Enum.all?(requires, is_atom(&1))
|
||||
valid = is_list(requires) and Enum.all?(requires, &is_atom(&1))
|
||||
|
||||
unless valid do
|
||||
raise ArgumentError, "expected :#{kind} option given to eval in the format: [module]"
|
||||
|
||||
@@ -99,7 +99,7 @@ defmodule Dict.Behaviour do
|
||||
defoverridable values: 1
|
||||
|
||||
def equal?(dict1, dict2) do
|
||||
case size(dict1) == size(dict2) do
|
||||
case Dict.size(dict1) == Dict.size(dict2) do
|
||||
false -> false
|
||||
true ->
|
||||
try do
|
||||
@@ -122,4 +122,4 @@ defmodule Dict.Behaviour do
|
||||
defoverridable merge: 2, merge: 3
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+31
-28
@@ -473,7 +473,7 @@ defmodule Enum do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.filter_map([1, 2, 3], fn(x) -> rem(x, 2) == 0 end, &1 * 2)
|
||||
iex> Enum.filter_map([1, 2, 3], fn(x) -> rem(x, 2) == 0 end, &(&1 * 2))
|
||||
[4]
|
||||
|
||||
"""
|
||||
@@ -648,14 +648,9 @@ defmodule Enum do
|
||||
|
||||
"""
|
||||
@spec join(t) :: String.t
|
||||
@spec join(t, String.t | char_list) :: String.t | char_list
|
||||
@spec join(t, String.t) :: String.t
|
||||
def join(collection, joiner // "")
|
||||
|
||||
def join(collection, joiner) when is_list(joiner) do
|
||||
IO.write "Enum.join/2 with a char list is deprecated, please use do an explicit conversion instead\n#{Exception.format_stacktrace}"
|
||||
:unicode.characters_to_list join(collection, :unicode.characters_to_binary(joiner))
|
||||
end
|
||||
|
||||
def join(collection, joiner) when is_binary(joiner) do
|
||||
Enumerable.reduce(collection, "", fn
|
||||
entry, "" -> to_string(entry)
|
||||
@@ -708,14 +703,9 @@ defmodule Enum do
|
||||
|
||||
"""
|
||||
@spec map_join(t, (element -> any)) :: String.t
|
||||
@spec map_join(t, String.t | char_list, (element -> any)) :: String.t | char_list
|
||||
@spec map_join(t, String.t, (element -> any)) :: String.t
|
||||
def map_join(collection, joiner // "", mapper)
|
||||
|
||||
def map_join(collection, joiner, mapper) when is_list(joiner) do
|
||||
IO.write "Enum.map_join/3 with a char list is deprecated, please use do an explicit conversion instead\n#{Exception.format_stacktrace}"
|
||||
:unicode.characters_to_list map_join(collection, :unicode.characters_to_binary(joiner), mapper)
|
||||
end
|
||||
|
||||
def map_join(collection, joiner, mapper) when is_binary(joiner) do
|
||||
Enumerable.reduce(collection, "", fn
|
||||
entry, "" -> to_string(mapper, entry)
|
||||
@@ -902,8 +892,8 @@ defmodule Enum do
|
||||
end
|
||||
|
||||
def sort(collection) do
|
||||
fun = &1 <= &2
|
||||
Enumerable.reduce(collection, [], sort_reducer(&1, &2, fun)) |>
|
||||
fun = &(&1 <= &2)
|
||||
Enumerable.reduce(collection, [], &sort_reducer(&1, &2, fun)) |>
|
||||
sort_terminator(fun)
|
||||
end
|
||||
|
||||
@@ -922,7 +912,7 @@ defmodule Enum do
|
||||
end
|
||||
|
||||
def sort(collection, fun) do
|
||||
Enumerable.reduce(collection, [], sort_reducer(&1, &2, fun)) |>
|
||||
Enumerable.reduce(collection, [], &sort_reducer(&1, &2, fun)) |>
|
||||
sort_terminator(fun)
|
||||
end
|
||||
|
||||
@@ -1041,6 +1031,31 @@ defmodule Enum do
|
||||
take(list, count)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a collection of every `nth` items in the collection,
|
||||
starting with the first.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Enum.take_every(1..10, 2)
|
||||
[1, 3, 5, 7, 9]
|
||||
|
||||
"""
|
||||
@spec take_every(t, integer) :: list
|
||||
def take_every(_collection, 0), do: []
|
||||
def take_every(collection, nth) do
|
||||
res = Enumerable.reduce(collection, nil, fn
|
||||
x, { acc, ^nth } -> { [x | acc], 1 }
|
||||
_, { acc, count } -> { acc, count + 1 }
|
||||
x, nil -> { [x], 1 }
|
||||
end)
|
||||
|
||||
case res do
|
||||
nil -> []
|
||||
{ list, _ } -> :lists.reverse(list)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Takes the items at the beginning of `collection` while `fun` returns `true`.
|
||||
Expects an ordered collection.
|
||||
@@ -1169,12 +1184,6 @@ defmodule Enum do
|
||||
reduce(collection, &Kernel.max(&1, &2))
|
||||
end
|
||||
|
||||
@doc false
|
||||
def max(coll, fun) do
|
||||
IO.write "Enum.max/2 is deprecated, please use Enum.max_by/2 instead\n#{Exception.format_stacktrace}"
|
||||
max_by(coll, fun)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the maximum value as calculated by the given function.
|
||||
Raises `EmptyError` if the collection is empty.
|
||||
@@ -1229,12 +1238,6 @@ defmodule Enum do
|
||||
reduce(collection, &Kernel.min(&1, &2))
|
||||
end
|
||||
|
||||
@doc false
|
||||
def min(coll, fun) do
|
||||
IO.write "Enum.min/2 is deprecated, please use Enum.min_by/2 instead\n#{Exception.format_stacktrace}"
|
||||
min_by(coll, fun)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the minimum value as calculated by the given function.
|
||||
Raises `EmptyError` if the collection is empty.
|
||||
|
||||
@@ -47,13 +47,6 @@ defexception File.CopyError, [reason: nil, action: "", source: nil, destination:
|
||||
end
|
||||
end
|
||||
|
||||
defexception File.IteratorError, reason: nil do
|
||||
def message(exception) do
|
||||
formatted = iolist_to_binary(:file.format_error(reason exception))
|
||||
"error during file iteration: #{formatted}"
|
||||
end
|
||||
end
|
||||
|
||||
defmodule File do
|
||||
@moduledoc """
|
||||
This module contains functions to manipulate files.
|
||||
@@ -945,7 +938,7 @@ defmodule File do
|
||||
"""
|
||||
def ls(path // ".") do
|
||||
case F.list_dir(path) do
|
||||
{ :ok, file_list } -> { :ok, Enum.map(file_list, to_string(&1)) }
|
||||
{ :ok, file_list } -> { :ok, Enum.map(file_list, &to_string(&1)) }
|
||||
{ :error, _ } = error -> error
|
||||
end
|
||||
end
|
||||
|
||||
@@ -60,7 +60,7 @@ defmodule GenFSM.Behaviour do
|
||||
end
|
||||
|
||||
def short_paid(:coin, state_data) do
|
||||
{ :next_state, :paid_in_full, state_data.update_coins(&1 + 1) }
|
||||
{ :next_state, :paid_in_full, &state_data.update_coins(&1 + 1) }
|
||||
end
|
||||
|
||||
def short_paid(:request_coffee, state_data) do
|
||||
@@ -84,7 +84,7 @@ defmodule GenFSM.Behaviour do
|
||||
|
||||
|
||||
def paid_in_full(:coin, state_data) do
|
||||
{ :next_state, :paid_in_full, state_data.update_coins(&1 + 1) }
|
||||
{ :next_state, :paid_in_full, &state_data.update_coins(&1 + 1) }
|
||||
end
|
||||
|
||||
def paid_in_full(:request_coffee, _state_data) do
|
||||
|
||||
@@ -107,12 +107,6 @@ defmodule HashDict do
|
||||
dict
|
||||
end
|
||||
|
||||
@doc false
|
||||
def update(dict, key, fun) when is_function(fun, 1) do
|
||||
IO.write "HashDict.update/3 is deprecated, please use HashDict.update!/3 instead\n#{Exception.format_stacktrace}"
|
||||
update!(dict, key, fun)
|
||||
end
|
||||
|
||||
def update!(dict, key, fun) when is_function(fun, 1) do
|
||||
case dict_put(dict, key, { :update, nil, fun }) do
|
||||
{ dict, 0 } ->
|
||||
@@ -159,13 +153,13 @@ defmodule HashDict do
|
||||
|
||||
def merge(dict1, dict2, callback) when is_record(dict1, HashDict) and is_record(dict2, HashDict) and elem(dict1, 1) < elem(dict2, 1) do
|
||||
dict_fold dict1, dict2, fn [k|v1], acc ->
|
||||
update(acc, k, v1, callback.(k, v1, &1))
|
||||
update(acc, k, v1, &callback.(k, v1, &1))
|
||||
end
|
||||
end
|
||||
|
||||
def merge(dict1, dict2, callback) when is_record(dict1, HashDict) and is_record(dict2, HashDict) do
|
||||
dict_fold dict2, dict1, fn [k|v2], acc ->
|
||||
update(acc, k, v2, callback.(k, &1, v2))
|
||||
update(acc, k, v2, &callback.(k, &1, v2))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ defmodule HashSet do
|
||||
end
|
||||
|
||||
def to_list(set) do
|
||||
set_fold(set, [], [&1|&2]) |> :lists.reverse
|
||||
set_fold(set, [], &[&1|&2]) |> :lists.reverse
|
||||
end
|
||||
|
||||
def put(set, member) do
|
||||
|
||||
@@ -267,9 +267,9 @@ defimpl Inspect, for: List do
|
||||
:io_lib.printable_list(thing) ->
|
||||
<< ?', Inspect.BitString.escape(String.from_char_list!(thing), ?') :: binary, ?' >>
|
||||
keyword?(thing) && not opts.raw ->
|
||||
surround_many("[", thing, "]", opts.limit, keyword(&1, opts))
|
||||
surround_many("[", thing, "]", opts.limit, &keyword(&1, opts))
|
||||
true ->
|
||||
surround_many("[", thing, "]", opts.limit, Kernel.inspect(&1, opts))
|
||||
surround_many("[", thing, "]", opts.limit, &Kernel.inspect(&1, opts))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -317,7 +317,7 @@ defimpl Inspect, for: Tuple do
|
||||
def inspect(tuple, opts) do
|
||||
unless opts.raw do
|
||||
record_inspect(tuple, opts)
|
||||
end || surround_many("{", tuple_to_list(tuple), "}", opts.limit, Kernel.inspect(&1, opts))
|
||||
end || surround_many("{", tuple_to_list(tuple), "}", opts.limit, &Kernel.inspect(&1, opts))
|
||||
end
|
||||
|
||||
## Helpers
|
||||
@@ -331,7 +331,7 @@ defimpl Inspect, for: Tuple do
|
||||
else
|
||||
surround_record(name, fields, tail, opts)
|
||||
end
|
||||
end || surround_many("{", [name|tail], "}", opts.limit, Kernel.inspect(&1, opts))
|
||||
end || surround_many("{", [name|tail], "}", opts.limit, &Kernel.inspect(&1, opts))
|
||||
end
|
||||
|
||||
defp record_fields(name) do
|
||||
@@ -351,7 +351,7 @@ defimpl Inspect, for: Tuple do
|
||||
|
||||
concat(
|
||||
Inspect.Atom.inspect(name, opts),
|
||||
surround_many("[", Enum.zip(fields, tail), "]", opts.limit, keyword(&1, opts))
|
||||
surround_many("[", Enum.zip(fields, tail), "]", opts.limit, &keyword(&1, opts))
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -82,6 +82,28 @@ defmodule Inspect.Algebra do
|
||||
defrecordp :doc_break, str: " " :: binary
|
||||
defrecordp :doc_group, doc: :doc_nil :: t
|
||||
|
||||
defmacrop is_doc(doc) do
|
||||
if __CALLER__.in_guard? do
|
||||
do_is_doc(doc)
|
||||
else
|
||||
var = quote do: doc
|
||||
quote do
|
||||
unquote(var) = unquote(doc)
|
||||
unquote(do_is_doc(var))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp do_is_doc(doc) do
|
||||
quote do
|
||||
unquote(doc) |> is_binary or
|
||||
unquote(doc) |> is_integer or
|
||||
unquote(doc) == :doc_nil or
|
||||
(unquote(doc) |> is_tuple and
|
||||
elem(unquote(doc), 0) in [:doc_cons, :doc_nest, :doc_break, :doc_group])
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns `:doc_nil` which is a document entity used to represent
|
||||
nothingness. Takes no arguments.
|
||||
@@ -107,13 +129,17 @@ defmodule Inspect.Algebra do
|
||||
|
||||
"""
|
||||
@spec concat(t, t) :: doc_cons_t
|
||||
def concat(x, y), do: doc_cons(left: x, right: y)
|
||||
def concat(x, y) when is_doc(x) and is_doc(y) do
|
||||
doc_cons(left: x, right: y)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Concatenates a list of documents.
|
||||
"""
|
||||
@spec concat([t]) :: doc_cons_t
|
||||
def concat(docs), do: folddoc(docs, concat(&1, &2))
|
||||
def concat(docs) do
|
||||
folddoc(docs, &concat(&1, &2))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Nests document entity `x` positions deep. Nesting will be
|
||||
@@ -127,8 +153,13 @@ defmodule Inspect.Algebra do
|
||||
|
||||
"""
|
||||
@spec nest(t, non_neg_integer) :: doc_nest_t
|
||||
def nest(x, 0), do: x
|
||||
def nest(x, i) when is_integer(i), do: doc_nest(indent: i, doc: x)
|
||||
def nest(x, 0) when is_doc(x) do
|
||||
x
|
||||
end
|
||||
|
||||
def nest(x, i) when is_doc(x) and is_integer(i) do
|
||||
doc_nest(indent: i, doc: x)
|
||||
end
|
||||
|
||||
@doc %S"""
|
||||
Document entity representing a break. This break can
|
||||
@@ -198,7 +229,9 @@ defmodule Inspect.Algebra do
|
||||
|
||||
"""
|
||||
@spec group(t) :: doc_group_t
|
||||
def group(d), do: doc_group(doc: d)
|
||||
def group(d) when is_doc(d) do
|
||||
doc_group(doc: d)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Inserts a mandatory single space between two document entities.
|
||||
@@ -273,11 +306,11 @@ defmodule Inspect.Algebra do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> doc = Inspect.Algebra.surround_many("[", Enum.to_list(1..5), "]", :infinity, integer_to_binary(&1))
|
||||
iex> doc = Inspect.Algebra.surround_many("[", Enum.to_list(1..5), "]", :infinity, &integer_to_binary(&1))
|
||||
iex> Inspect.Algebra.pretty(doc, 5)
|
||||
"[1,\n 2,\n 3,\n 4,\n 5]"
|
||||
|
||||
iex> doc = Inspect.Algebra.surround_many("[", Enum.to_list(1..5), "]", 3, integer_to_binary(&1))
|
||||
iex> doc = Inspect.Algebra.surround_many("[", Enum.to_list(1..5), "]", 3, &integer_to_binary(&1))
|
||||
iex> Inspect.Algebra.pretty(doc, 20)
|
||||
"[1, 2, 3, ...]"
|
||||
|
||||
|
||||
+16
-21
@@ -1,7 +1,17 @@
|
||||
defexception IO.StreamError, reason: nil do
|
||||
def message(exception) do
|
||||
formatted = iolist_to_binary(:file.format_error(reason exception))
|
||||
"error during streaming: #{formatted}"
|
||||
end
|
||||
end
|
||||
|
||||
defmodule IO do
|
||||
@moduledoc """
|
||||
Module responsible for doing IO. Many functions in this
|
||||
module expects an IO device and an io data encoded in UTF-8.
|
||||
Use the bin* functions if the data is binary, useful when
|
||||
working with raw bytes or when no unicode conversion should
|
||||
be performed.
|
||||
|
||||
An IO device must be a pid or an atom representing a process.
|
||||
For convenience, Elixir provides `:stdio` and `:stderr` as
|
||||
@@ -18,9 +28,6 @@ defmodule IO do
|
||||
|
||||
* A list of binaries or a list of char lists (as described above);
|
||||
|
||||
* If none of the above, `to_string` is invoked on the
|
||||
given argument;
|
||||
|
||||
"""
|
||||
|
||||
import :erlang, only: [group_leader: 0]
|
||||
@@ -160,7 +167,7 @@ defmodule IO do
|
||||
Gets a number of bytes from the io device. If the
|
||||
io device is a unicode device, `count` implies
|
||||
the number of unicode codepoints to be retrieved.
|
||||
Otherwise, `count` is the number of raw bytes to be retrieved.
|
||||
Otherwise, `count` is the number of raw bytes to be retrieved.
|
||||
It returns:
|
||||
|
||||
* `data` - The input characters.
|
||||
@@ -220,17 +227,11 @@ defmodule IO do
|
||||
Here is an example on how we mimic an echo server
|
||||
from the command line:
|
||||
|
||||
Enum.each IO.stream(:stdio, :line), IO.write(&1)
|
||||
Enum.each IO.stream(:stdio, :line), &IO.write(&1)
|
||||
|
||||
"""
|
||||
def stream(device, line_or_bytes) do
|
||||
stream(map_dev(device), line_or_bytes, &1, &2)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def stream(device) do
|
||||
IO.write "IO.stream(device) is deprecated, please use IO.stream(device, :line) instead\n#{Exception.format_stacktrace}"
|
||||
stream(device, :line)
|
||||
fn(acc, f) -> stream(map_dev(device), line_or_bytes, acc, f) end
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -240,13 +241,7 @@ defmodule IO do
|
||||
This reads the io as a raw binary.
|
||||
"""
|
||||
def binstream(device, line_or_bytes) do
|
||||
binstream(map_dev(device), line_or_bytes, &1, &2)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def binstream(device) do
|
||||
IO.write "IO.binstream(device) is deprecated, please use IO.binstream(device, :line) instead\n#{Exception.format_stacktrace}"
|
||||
binstream(device, :line)
|
||||
fn(acc, f) -> binstream(map_dev(device), line_or_bytes, acc, f) end
|
||||
end
|
||||
|
||||
@doc false
|
||||
@@ -255,7 +250,7 @@ defmodule IO do
|
||||
:eof ->
|
||||
acc
|
||||
{ :error, reason } ->
|
||||
raise File.IteratorError, reason: reason
|
||||
raise IO.StreamError, reason: reason
|
||||
data ->
|
||||
stream(device, what, fun.(data, acc), fun)
|
||||
end
|
||||
@@ -267,7 +262,7 @@ defmodule IO do
|
||||
:eof ->
|
||||
acc
|
||||
{ :error, reason } ->
|
||||
raise File.IteratorError, reason: reason
|
||||
raise IO.StreamError, reason: reason
|
||||
data ->
|
||||
binstream(device, what, fun.(data, acc), fun)
|
||||
end
|
||||
|
||||
+42
-74
@@ -1,5 +1,3 @@
|
||||
import Kernel, except: [raise: 1, raise: 2]
|
||||
|
||||
defmodule Kernel do
|
||||
@moduledoc """
|
||||
`Kernel` provides the default macros and functions
|
||||
@@ -479,20 +477,6 @@ defmodule Kernel do
|
||||
:erlang.binary_to_existing_atom(binary, encoding)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def binary_to_list(binary) do
|
||||
IO.write "binary_to_list/1 is deprecated. Please use String.to_char_list!/1 instead "
|
||||
IO.write "unless you are working with bytes, then you should use :binary.bin_to_list/1\n#{Exception.format_stacktrace}"
|
||||
:erlang.binary_to_list(binary)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def binary_to_list(binary, start, stop) do
|
||||
IO.write "binary_to_list/3 is deprecated. Please use String.to_char_list!/1 instead "
|
||||
IO.write "unless you are working with bytes, then you should use :binary.bin_to_list/3\n#{Exception.format_stacktrace}"
|
||||
:erlang.binary_to_list(binary, start, stop)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns an Erlang term which is the result of decoding the binary
|
||||
object `binary`, which must be encoded according to the Erlang external
|
||||
@@ -873,13 +857,6 @@ defmodule Kernel do
|
||||
:erlang.list_to_atom(list)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def list_to_binary(list) do
|
||||
IO.write "list_to_binary/1 is deprecated. Please use String.from_char_list!/1 instead "
|
||||
IO.write "unless you are working with bytes, then you should use iolist_to_binary/1\n#{Exception.format_stacktrace}"
|
||||
:erlang.list_to_binary(list)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns a bitstring which is made from the integers and bitstrings in `bitstring_list`.
|
||||
(the last tail in `bitstring_list` is allowed to be a bitstring.)
|
||||
@@ -2015,8 +1992,7 @@ defmodule Kernel do
|
||||
|
||||
case is_atom(expanded) do
|
||||
false ->
|
||||
raise ArgumentError,
|
||||
message: "invalid arguments for use, expected an atom or alias as argument"
|
||||
:erlang.error ArgumentError.exception(message: "invalid arguments for use, expected an atom or alias as argument")
|
||||
true ->
|
||||
quote do
|
||||
require unquote(expanded)
|
||||
@@ -2026,7 +2002,14 @@ defmodule Kernel do
|
||||
end
|
||||
|
||||
@doc %S"""
|
||||
Inspect the given arguments according to the `Inspect` protocol.
|
||||
Inspect the given argument according to the `Inspect` protocol.
|
||||
The second argument is a keywords list with options to control
|
||||
inspection.
|
||||
|
||||
The second argument may also be an instance of the `Inspect.Opts`
|
||||
record and, in such cases, instead of returning a string, it
|
||||
returns an algebra document which can be converted to a string
|
||||
via `Inspect.Algebra`.
|
||||
|
||||
## Options
|
||||
|
||||
@@ -2061,7 +2044,7 @@ defmodule Kernel do
|
||||
representation of an Elixir term. In such cases, the inspected result
|
||||
must start with `#`. For example, inspecting a function will return:
|
||||
|
||||
inspect &1 + &2
|
||||
inspect &(&1 + &2)
|
||||
#=> #Function<...>
|
||||
|
||||
"""
|
||||
@@ -2103,12 +2086,6 @@ defmodule Kernel do
|
||||
quote do: String.Chars.to_string(unquote(arg))
|
||||
end
|
||||
|
||||
@doc false
|
||||
def to_binary(chars) do
|
||||
IO.write "to_binary/1 is deprecated, please use to_string/1 instead\n#{Exception.format_stacktrace}"
|
||||
String.Chars.to_string(chars)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Convert the argument to a list according to the List.Chars protocol.
|
||||
|
||||
@@ -2184,12 +2161,12 @@ defmodule Kernel do
|
||||
Match can also be used to filter or find a value in an enumerable:
|
||||
|
||||
list = [{:a, 1}, {:b, 2}, {:a, 3}]
|
||||
Enum.filter list, match?({:a, _}, &1)
|
||||
Enum.filter list, &match?({:a, _}, &1)
|
||||
|
||||
Guard clauses can also be given to the match:
|
||||
|
||||
list = [{:a, 1}, {:b, 2}, {:a, 3}]
|
||||
Enum.filter list, match?({:a, x } when x < 2, &1)
|
||||
Enum.filter list, &match?({:a, x } when x < 2, &1)
|
||||
|
||||
However, variables assigned in the match will not be available
|
||||
outside of the function call:
|
||||
@@ -2208,7 +2185,7 @@ defmodule Kernel do
|
||||
end
|
||||
|
||||
defmacro match?(left, right) do
|
||||
{ left, _ } = falsify_var(left, [], falsify_all(&1, &2))
|
||||
{ left, _ } = falsify_var(left, [], &falsify_all/2)
|
||||
|
||||
quote do
|
||||
case unquote(right) do
|
||||
@@ -2237,7 +2214,7 @@ defmodule Kernel do
|
||||
|
||||
defp falsify_var({ :when, meta, [left, right] }, acc, fun) do
|
||||
{ left, acc } = falsify_var(left, acc, fun)
|
||||
{ right, acc } = falsify_var(right, acc, falsify_selected(&1, &2))
|
||||
{ right, acc } = falsify_var(right, acc, &falsify_selected/2)
|
||||
{ { :when, meta, [left, right] }, acc }
|
||||
end
|
||||
|
||||
@@ -2258,7 +2235,7 @@ defmodule Kernel do
|
||||
end
|
||||
|
||||
defp falsify_var(list, acc, fun) when is_list(list) do
|
||||
:lists.mapfoldl(falsify_var(&1, &2, fun), acc, list)
|
||||
:lists.mapfoldl(&falsify_var(&1, &2, fun), acc, list)
|
||||
end
|
||||
|
||||
defp falsify_var(other, acc, _fun) do
|
||||
@@ -2652,8 +2629,8 @@ defmodule Kernel do
|
||||
new_acc =
|
||||
case condition do
|
||||
{ :_, _, atom } when is_atom(atom) ->
|
||||
raise ArgumentError, message: <<"unbound variable _ inside cond. ",
|
||||
"If you want the last clause to match, you probably meant to use true ->">>
|
||||
:erlang.error ArgumentError.exception(message: <<"unbound variable _ inside cond. ",
|
||||
"If you want the last clause to match, you probably meant to use true ->">>)
|
||||
x when is_atom(x) and not x in [false, nil] ->
|
||||
clause
|
||||
_ ->
|
||||
@@ -3055,12 +3032,12 @@ defmodule Kernel do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> [1, [2], 3] |> List.flatten |> Enum.map(&1 * 2)
|
||||
iex> [1, [2], 3] |> List.flatten |> Enum.map(&(&1 * 2))
|
||||
[2,4,6]
|
||||
|
||||
The expression above is simply translated to:
|
||||
|
||||
Enum.map(List.flatten([1, [2], 3]), &1 * 2)
|
||||
Enum.map(List.flatten([1, [2], 3]), &(&1 * 2))
|
||||
|
||||
Be aware of operator precendence when using this operator.
|
||||
For example, the following expression:
|
||||
@@ -3095,7 +3072,7 @@ defmodule Kernel do
|
||||
end
|
||||
|
||||
defp pipeline_op(_, arg) do
|
||||
raise ArgumentError, message: "unsupported expression in pipeline |> operator: #{Macro.to_string arg}"
|
||||
:erlang.error ArgumentError.exception(message: "unsupported expression in pipeline |> operator: #{Macro.to_string arg}")
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -3120,12 +3097,10 @@ defmodule Kernel do
|
||||
|
||||
"""
|
||||
@spec raise(binary | atom | tuple) :: no_return
|
||||
def raise(msg) when is_binary(msg) do
|
||||
:erlang.error RuntimeError[message: msg]
|
||||
end
|
||||
|
||||
def raise(exception) do
|
||||
raise(exception, [])
|
||||
defmacro raise(msg) when is_binary(msg) do
|
||||
quote do
|
||||
:erlang.error RuntimeError[message: unquote(msg)]
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -3146,8 +3121,16 @@ defmodule Kernel do
|
||||
|
||||
"""
|
||||
@spec raise(tuple | atom, list) :: no_return
|
||||
def raise(exception, args) do
|
||||
:erlang.error exception.exception(args)
|
||||
defmacro raise(exception, args // []) do
|
||||
quote do
|
||||
exception = unquote(exception)
|
||||
case exception do
|
||||
e when is_binary(e) ->
|
||||
:erlang.error RuntimeError.new(message: exception)
|
||||
_ ->
|
||||
:erlang.error exception.exception(unquote(args))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -3288,15 +3271,15 @@ defmodule Kernel do
|
||||
{ :error, _ } ->
|
||||
:elixir_aliases.ensure_loaded(caller.line, caller.file, atom, caller.context_modules)
|
||||
_ ->
|
||||
raise ArgumentError, message: "cannot use module #{inspect atom} in access protocol because it does not export __record__/1"
|
||||
:erlang.error ArgumentError.exception(message: "cannot use module #{inspect atom} in access protocol because it does not export __record__/1")
|
||||
end
|
||||
end
|
||||
|
||||
Record.access(atom, fields, args, caller)
|
||||
false ->
|
||||
case caller.in_match? do
|
||||
true -> raise ArgumentError, message: << "the access protocol cannot be used inside match clauses ",
|
||||
"(for example, on the left hand side of a match or in function signatures)" >>
|
||||
true -> :erlang.error ArgumentError.exception(message: << "the access protocol cannot be used inside match clauses ",
|
||||
"(for example, on the left hand side of a match or in function signatures)" >>)
|
||||
false -> quote do: Access.access(unquote(element), unquote(args))
|
||||
end
|
||||
end
|
||||
@@ -3347,14 +3330,14 @@ defmodule Kernel do
|
||||
funs = Macro.escape(funs, unquote: true)
|
||||
quote bind_quoted: [funs: funs, opts: opts] do
|
||||
target = Keyword.get(opts, :to) ||
|
||||
raise(ArgumentError, message: "Expected to: to be given as argument")
|
||||
:erlang.error ArgumentError.exception(message: "Expected to: to be given as argument")
|
||||
|
||||
append_first = Keyword.get(opts, :append_first, false)
|
||||
|
||||
lc fun inlist List.wrap(funs) do
|
||||
case Macro.extract_args(fun) do
|
||||
{ name, args } -> :ok
|
||||
:error -> raise ArgumentError, message: "invalid syntax in defdelegate #{Macro.to_string(fun)}"
|
||||
:error -> :erlang.error ArgumentError.exception(message: "invalid syntax in defdelegate #{Macro.to_string(fun)}")
|
||||
end
|
||||
|
||||
actual_args =
|
||||
@@ -3388,12 +3371,6 @@ defmodule Kernel do
|
||||
string
|
||||
end
|
||||
|
||||
@doc false
|
||||
defmacro sigil_B(string, []) do
|
||||
IO.write "%B() is deprecated, please use %S() instead\n#{Exception.format_stacktrace}"
|
||||
string
|
||||
end
|
||||
|
||||
@doc """
|
||||
Handles the sigil %s. It returns a string as if it was double quoted
|
||||
string, unescaping characters and replacing interpolations.
|
||||
@@ -3410,12 +3387,6 @@ defmodule Kernel do
|
||||
{ :<<>>, line, Macro.unescape_tokens(pieces) }
|
||||
end
|
||||
|
||||
@doc false
|
||||
defmacro sigil_b({ :<<>>, line, pieces }, []) do
|
||||
IO.write "%b() is deprecated, please use %s() instead\n#{Exception.format_stacktrace}"
|
||||
{ :<<>>, line, Macro.unescape_tokens(pieces) }
|
||||
end
|
||||
|
||||
@doc """
|
||||
Handles the sigil %C. It simply returns a char list
|
||||
without escaping characters and without interpolations.
|
||||
@@ -3466,13 +3437,13 @@ defmodule Kernel do
|
||||
|
||||
"""
|
||||
defmacro sigil_r({ :<<>>, _line, [string] }, options) when is_binary(string) do
|
||||
binary = Macro.unescape_string(string, Regex.unescape_map(&1))
|
||||
binary = Macro.unescape_string(string, fn(x) -> Regex.unescape_map(x) end)
|
||||
regex = Regex.compile!(binary, :binary.list_to_bin(options))
|
||||
Macro.escape(regex)
|
||||
end
|
||||
|
||||
defmacro sigil_r({ :<<>>, line, pieces }, options) do
|
||||
binary = { :<<>>, line, Macro.unescape_tokens(pieces, Regex.unescape_map(&1)) }
|
||||
binary = { :<<>>, line, Macro.unescape_tokens(pieces, fn(x) -> Regex.unescape_map(x) end) }
|
||||
quote do: Regex.compile!(unquote(binary), unquote(:binary.list_to_bin(options)))
|
||||
end
|
||||
|
||||
@@ -3612,11 +3583,8 @@ defmodule Kernel do
|
||||
defp split_words(string, modifiers) do
|
||||
mod = case modifiers do
|
||||
[] -> ?s
|
||||
[mod] when mod in [?b] ->
|
||||
IO.write "%w()b is deprecated, please use %w()s instead\n#{Exception.format_stacktrace}"
|
||||
?s
|
||||
[mod] when mod in [?s, ?a, ?c] -> mod
|
||||
_else -> raise ArgumentError, message: "modifier must be one of: s, a, c"
|
||||
_else -> :erlang.error ArgumentError.exception(message: "modifier must be one of: s, a, c")
|
||||
end
|
||||
|
||||
case is_binary(string) do
|
||||
|
||||
@@ -15,12 +15,12 @@ defmodule Kernel.CLI do
|
||||
System.argv(argv)
|
||||
|
||||
run fn ->
|
||||
command_results = Enum.map(Enum.reverse(config.commands), process_command(&1, config))
|
||||
command_results = Enum.map(Enum.reverse(config.commands), &process_command(&1, config))
|
||||
command_errors = lc { :error, msg } inlist command_results, do: msg
|
||||
errors = Enum.reverse(config.errors) ++ command_errors
|
||||
|
||||
if errors != [] do
|
||||
Enum.each(errors, IO.puts(:stderr, &1))
|
||||
Enum.each(errors, &IO.puts(:stderr, &1))
|
||||
System.halt(1)
|
||||
end
|
||||
end, config.halt
|
||||
@@ -77,7 +77,7 @@ defmodule Kernel.CLI do
|
||||
defp shared_option?(list, config, callback) do
|
||||
case process_shared(list, config) do
|
||||
{ [h|hs], _ } when h == hd(list) ->
|
||||
new_config = config.update_errors ["#{h} : Unknown option" | &1]
|
||||
new_config = config.update_errors &["#{h} : Unknown option" | &1]
|
||||
callback.(hs, new_config)
|
||||
{ new_list, new_config } ->
|
||||
callback.(new_list, new_config)
|
||||
@@ -120,17 +120,17 @@ defmodule Kernel.CLI do
|
||||
end
|
||||
|
||||
defp process_shared(["-pa", h|t], config) do
|
||||
Enum.each Path.wildcard(Path.expand(h)), Code.prepend_path(&1)
|
||||
Enum.each Path.wildcard(Path.expand(h)), &Code.prepend_path(&1)
|
||||
process_shared t, config
|
||||
end
|
||||
|
||||
defp process_shared(["-pz", h|t], config) do
|
||||
Enum.each Path.wildcard(Path.expand(h)), Code.append_path(&1)
|
||||
Enum.each Path.wildcard(Path.expand(h)), &Code.append_path(&1)
|
||||
process_shared t, config
|
||||
end
|
||||
|
||||
defp process_shared(["--app", h|t], config) do
|
||||
process_shared t, config.update_commands [{:app, h}|&1]
|
||||
process_shared t, config.update_commands &[{:app, h}|&1]
|
||||
end
|
||||
|
||||
defp process_shared(["--no-halt"|t], config) do
|
||||
@@ -138,15 +138,15 @@ defmodule Kernel.CLI do
|
||||
end
|
||||
|
||||
defp process_shared(["-e", h|t], config) do
|
||||
process_shared t, config.update_commands [{:eval, h}|&1]
|
||||
process_shared t, config.update_commands &[{:eval, h}|&1]
|
||||
end
|
||||
|
||||
defp process_shared(["-r", h|t], config) do
|
||||
process_shared t, config.update_commands [{:require, h}|&1]
|
||||
process_shared t, config.update_commands &[{:require, h}|&1]
|
||||
end
|
||||
|
||||
defp process_shared(["-pr", h|t], config) do
|
||||
process_shared t, config.update_commands [{:parallel_require, h}|&1]
|
||||
process_shared t, config.update_commands &[{:parallel_require, h}|&1]
|
||||
end
|
||||
|
||||
defp process_shared([erl, _|t], config) when erl in ["--erl", "--sname", "--name", "--cookie"] do
|
||||
@@ -176,15 +176,15 @@ defmodule Kernel.CLI do
|
||||
end
|
||||
|
||||
defp process_argv(["-S", h|t], config) do
|
||||
{ config.update_commands([{:script, h}|&1]), t }
|
||||
{ config.update_commands(&[{:script, h}|&1]), t }
|
||||
end
|
||||
|
||||
defp process_argv([h|t] = list, config) do
|
||||
case h do
|
||||
"-" <> _ ->
|
||||
shared_option? list, config, process_argv(&1, &2)
|
||||
shared_option? list, config, &process_argv(&1, &2)
|
||||
_ ->
|
||||
{ config.update_commands([{:file, h}|&1]), t }
|
||||
{ config.update_commands(&[{:file, h}|&1]), t }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -203,19 +203,19 @@ defmodule Kernel.CLI do
|
||||
end
|
||||
|
||||
defp process_compiler(["--no-docs"|t], config) do
|
||||
process_compiler t, config.update_compiler_options([{:docs, false}|&1])
|
||||
process_compiler t, config.update_compiler_options(&[{:docs, false}|&1])
|
||||
end
|
||||
|
||||
defp process_compiler(["--no-debug-info"|t], config) do
|
||||
process_compiler t, config.update_compiler_options([{:debug_info, false}|&1])
|
||||
process_compiler t, config.update_compiler_options(&[{:debug_info, false}|&1])
|
||||
end
|
||||
|
||||
defp process_compiler(["--ignore-module-conflict"|t], config) do
|
||||
process_compiler t, config.update_compiler_options([{:ignore_module_conflict, true}|&1])
|
||||
process_compiler t, config.update_compiler_options(&[{:ignore_module_conflict, true}|&1])
|
||||
end
|
||||
|
||||
defp process_compiler(["--warnings-as-errors"|t], config) do
|
||||
process_compiler t, config.update_compiler_options([{:warnings_as_errors, true}|&1])
|
||||
process_compiler t, config.update_compiler_options(&[{:warnings_as_errors, true}|&1])
|
||||
end
|
||||
|
||||
defp process_compiler(["--verbose"|t], config) do
|
||||
@@ -225,15 +225,15 @@ defmodule Kernel.CLI do
|
||||
defp process_compiler([h|t] = list, config) do
|
||||
case h do
|
||||
"-" <> _ ->
|
||||
shared_option? list, config, process_compiler(&1, &2)
|
||||
shared_option? list, config, &process_compiler(&1, &2)
|
||||
_ ->
|
||||
pattern = if :filelib.is_dir(h), do: "#{h}/**/*.ex", else: h
|
||||
process_compiler t, config.update_compile [pattern|&1]
|
||||
process_compiler t, config.update_compile &[pattern|&1]
|
||||
end
|
||||
end
|
||||
|
||||
defp process_compiler([], config) do
|
||||
{ config.update_commands([{:compile, config.compile}|&1]), [] }
|
||||
{ config.update_commands(&[{:compile, config.compile}|&1]), [] }
|
||||
end
|
||||
|
||||
# Process iex options
|
||||
@@ -253,15 +253,15 @@ defmodule Kernel.CLI do
|
||||
end
|
||||
|
||||
defp process_iex(["-S", h|t], config) do
|
||||
{ config.update_commands([{:script, h}|&1]), t }
|
||||
{ config.update_commands(&[{:script, h}|&1]), t }
|
||||
end
|
||||
|
||||
defp process_iex([h|t] = list, config) do
|
||||
case h do
|
||||
"-" <> _ ->
|
||||
shared_option? list, config, process_iex(&1, &2)
|
||||
shared_option? list, config, &process_iex(&1, &2)
|
||||
_ ->
|
||||
{ config.update_commands([{:file, h}|&1]), t }
|
||||
{ config.update_commands(&[{:file, h}|&1]), t }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -315,10 +315,10 @@ defmodule Kernel.CLI do
|
||||
defp process_command({:require, pattern}, _config) when is_binary(pattern) do
|
||||
files = Path.wildcard(pattern)
|
||||
files = Enum.uniq(files)
|
||||
files = Enum.filter files, :filelib.is_regular(&1)
|
||||
files = Enum.filter files, &:filelib.is_regular(&1)
|
||||
|
||||
if files != [] do
|
||||
Enum.map files, Code.require_file(&1)
|
||||
Enum.map files, &Code.require_file(&1)
|
||||
:ok
|
||||
else
|
||||
{ :error, "-r : No files matched pattern #{pattern}" }
|
||||
@@ -328,7 +328,7 @@ defmodule Kernel.CLI do
|
||||
defp process_command({:parallel_require, pattern}, _config) when is_binary(pattern) do
|
||||
files = Path.wildcard(pattern)
|
||||
files = Enum.uniq(files)
|
||||
files = Enum.filter files, :filelib.is_regular(&1)
|
||||
files = Enum.filter files, &:filelib.is_regular(&1)
|
||||
|
||||
if files != [] do
|
||||
Kernel.ParallelRequire.files(files)
|
||||
@@ -341,9 +341,9 @@ defmodule Kernel.CLI do
|
||||
defp process_command({:compile, patterns}, config) do
|
||||
:filelib.ensure_dir(:filename.join(config.output, "."))
|
||||
|
||||
files = Enum.map patterns, Path.wildcard(&1)
|
||||
files = Enum.map patterns, &Path.wildcard(&1)
|
||||
files = Enum.uniq(Enum.concat(files))
|
||||
files = Enum.filter files, :filelib.is_regular(&1)
|
||||
files = Enum.filter files, &:filelib.is_regular(&1)
|
||||
|
||||
if files != [] do
|
||||
Code.compiler_options(config.compiler_options)
|
||||
|
||||
@@ -75,7 +75,7 @@ defmodule Kernel.RecordRewriter do
|
||||
if :lists.member({ function, length(args) + 1 }, optimizable) do
|
||||
case record_field_info(function) do
|
||||
{ kind, field } ->
|
||||
if index = Enum.find_index(fields, field == &1) do
|
||||
if index = Enum.find_index(fields, &(field == &1)) do
|
||||
optimize_record_accessor_call(line, res, kind, field, index, left, args)
|
||||
end
|
||||
nil ->
|
||||
@@ -352,7 +352,7 @@ defmodule Kernel.RecordRewriter do
|
||||
end
|
||||
|
||||
defp extract_vars({ :tuple, _, args }, vars) do
|
||||
[Enum.map(args, extract_vars(&1, []))|vars]
|
||||
[Enum.map(args, &extract_vars(&1, []))|vars]
|
||||
end
|
||||
|
||||
defp extract_vars(_, vars) do
|
||||
|
||||
@@ -80,12 +80,12 @@ defmodule Kernel.SpecialForms do
|
||||
|
||||
<<102, rest :: binary>>
|
||||
|
||||
The type can be integer, float, binary, bytes, bitstring,
|
||||
bits, utf8, utf16 or utf32, e.g.:
|
||||
The type can be integer, float, bitstring/bits, binary/bytes,
|
||||
utf8, utf16 or utf32, e.g.:
|
||||
|
||||
<<102 :: float, rest :: binary>>
|
||||
|
||||
Integer can be any arbitrary precision integer. A float is an
|
||||
An integer can be any arbitrary precision integer. A float is an
|
||||
IEEE 754 binary32 or binary64 floating point number. A bitstring
|
||||
is an arbitrary series of bits. A binary is a special case of
|
||||
bitstring that has a total size divisible by 8.
|
||||
@@ -179,7 +179,120 @@ defmodule Kernel.SpecialForms do
|
||||
defmacro :<<>>.(args)
|
||||
|
||||
@doc """
|
||||
`alias` is used to setup atom aliases, often useful with modules names.
|
||||
Defines a remote call or an alias.
|
||||
|
||||
The dot (`.`) in Elixir can be used for remote calls:
|
||||
|
||||
iex> String.downcase("FOO")
|
||||
"foo"
|
||||
|
||||
In this example above, we have used `.` to invoke `downcase` in the
|
||||
`String` alias, passing "FOO" as argument. We can also use the dot
|
||||
for creating aliases:
|
||||
|
||||
iex> Hello.World
|
||||
Hello.World
|
||||
|
||||
This time, we have joined two aliases, defining the final alias
|
||||
`Hello.World`.
|
||||
|
||||
## Syntax
|
||||
|
||||
The right side of `.` may be a word starting in upcase, which represents
|
||||
an alias, a word starting with lowercase or underscore, any valid language
|
||||
operator or any name wrapped in single- or double-quotes. Those are all valid
|
||||
examples:
|
||||
|
||||
iex> Kernel.Sample
|
||||
Kernel.Sample
|
||||
iex> Kernel.length([1,2,3])
|
||||
3
|
||||
iex> Kernel.+(1, 2)
|
||||
3
|
||||
iex> Kernel."length"([1,2,3])
|
||||
3
|
||||
iex> Kernel.'+'(1, 2)
|
||||
3
|
||||
|
||||
Note that `Kernel."HELLO"` will be treated as a remote call and not an alias.
|
||||
This choice was done so every time single- or double-quotes are used, we have
|
||||
a remote call irregardless of the quote contents. This decision is also reflected
|
||||
in the quoted expressions discussed below.
|
||||
|
||||
## Runtime (dynamic) behaviour
|
||||
|
||||
The result returned by `.` is always specified by the right-side:
|
||||
|
||||
iex> x = String
|
||||
iex> x.downcase("FOO")
|
||||
"foo"
|
||||
iex> x.Sample
|
||||
String.Sample
|
||||
|
||||
In case the right-side is also dynamic, `.`'s behaviour can be reproduced
|
||||
at runtime via `apply/3` and `Module.concat/2`:
|
||||
|
||||
iex> apply(Kernel, :+, [1,2])
|
||||
3
|
||||
iex> Module.concat(Kernel, Sample)
|
||||
Kernel.Sample
|
||||
|
||||
## Quoted expression
|
||||
|
||||
When `.` is used, the quoted expression may take two distinct
|
||||
forms. When the right side starts with a lowercase letter (or
|
||||
underscore):
|
||||
|
||||
iex> quote do: String.downcase("FOO")
|
||||
{{:., [], [{:__aliases__, [alias: false], [:String]}, :downcase]}, [], ["FOO"]}
|
||||
|
||||
Notice we have an inner tuple, containing the atom `:.` representing
|
||||
the dot as first element:
|
||||
|
||||
{:., [], [{:__aliases__, [alias: false], [:String]}, :downcase]}
|
||||
|
||||
This tuple follows the general quoted expression structure in Elixir,
|
||||
with the name as first argument, some keyword list as metadata as second,
|
||||
and the number of arguments as third. In this case, the arguments is the
|
||||
alias `String` and the atom `:downcase`. The second argument is **always**
|
||||
an atom:
|
||||
|
||||
iex> quote do: String."downcase"("FOO")
|
||||
{{:., [], [{:__aliases__, [alias: false], [:String]}, :downcase]}, [], ["FOO"]}
|
||||
|
||||
The tuple containing `:.` is wrapped in another tuple, which actually
|
||||
represents the function call, and has `"FOO"` as argument.
|
||||
|
||||
When the right side is an alias (i.e. starts with uppercase), we get instead:
|
||||
|
||||
iex> quote do: Hello.World
|
||||
{:__aliases__, [alias: false], [:Hello, :World]}
|
||||
|
||||
We got into more details about aliases in the `__aliases__` special form
|
||||
documentation.
|
||||
|
||||
## Unquoting
|
||||
|
||||
We can also use unquote to generate a remote call in a quoted expression:
|
||||
|
||||
iex> x = :downcase
|
||||
iex> quote do: String.unquote(x)("FOO")
|
||||
{{:., [], [{:__aliases__, [alias: false], [:String]}, :downcase]}, [], ["FOO"]}
|
||||
|
||||
Similar to `Kernel."HELLO"`, `unquote(x)` will always generate a remote call,
|
||||
independent of the value of `x`. To generate an alias via the quoted expression,
|
||||
one needs to rely on `Module.concat/2`:
|
||||
|
||||
iex> x = Sample
|
||||
iex> quote do: Module.concat(String, unquote(x))
|
||||
{{:., [], [{:__aliases__, [alias: false], [:Module]}, :concat]}, [],
|
||||
[{:__aliases__, [alias: false], [:String]}, Sample]}
|
||||
|
||||
"""
|
||||
defmacro (:.).(left, right)
|
||||
|
||||
@doc """
|
||||
`alias` is used to setup aliases, often useful with modules names.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -217,8 +330,7 @@ defmodule Kernel.SpecialForms do
|
||||
defmacro alias(module, opts)
|
||||
|
||||
@doc """
|
||||
`require` is used to require the presence of external
|
||||
modules so macros can be invoked.
|
||||
Requires a given module to be compiled and loaded.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -246,6 +358,8 @@ defmodule Kernel.SpecialForms do
|
||||
defmacro require(module, opts)
|
||||
|
||||
@doc """
|
||||
Imports function and macros from other modules.
|
||||
|
||||
`import` allows one to easily access functions or macros from
|
||||
others modules without using the qualified name.
|
||||
|
||||
@@ -335,14 +449,16 @@ defmodule Kernel.SpecialForms do
|
||||
defmacro import(module, opts)
|
||||
|
||||
@doc """
|
||||
Returns the current environment information as a `Macro.Env`
|
||||
record. In the environment you can access the current filename,
|
||||
Returns the current environment information as a `Macro.Env[]` record.
|
||||
|
||||
In the environment you can access the current filename,
|
||||
line numbers, set up aliases, the current function and others.
|
||||
"""
|
||||
defmacro __ENV__
|
||||
|
||||
@doc """
|
||||
Returns the current module name as an atom or `nil` otherwise.
|
||||
|
||||
Although the module can be accessed in the `__ENV__`, this macro
|
||||
is a convenient shortcut.
|
||||
"""
|
||||
@@ -350,6 +466,7 @@ defmodule Kernel.SpecialForms do
|
||||
|
||||
@doc """
|
||||
Returns the current file name as a binary.
|
||||
|
||||
Although the file can be accessed in the `__ENV__`, this macro
|
||||
is a convenient shortcut.
|
||||
"""
|
||||
@@ -395,8 +512,8 @@ defmodule Kernel.SpecialForms do
|
||||
Read the Stacktrace information section below for more information;
|
||||
* `:hygiene` - Allows a developer to disable hygiene selectively;
|
||||
* `:context` - Sets the resolution context;
|
||||
* `:binding` - Passes a binding to the macro. Whenever a binding is given,
|
||||
`unquote` is automatically disabled;
|
||||
* `:bind_quoted` - Passes a binding to the macro. Whenever a binding is given,
|
||||
`unquote` is automatically disabled;
|
||||
|
||||
## Macro literals
|
||||
|
||||
@@ -709,8 +826,8 @@ defmodule Kernel.SpecialForms do
|
||||
end
|
||||
end
|
||||
|
||||
In fact, the `:binding` option is recommended every time one
|
||||
desires to inject a value into the quote.
|
||||
In fact, the `:bind_quoted` option is recommended every time
|
||||
one desires to inject a value into the quote.
|
||||
"""
|
||||
defmacro quote(opts, block)
|
||||
|
||||
@@ -820,6 +937,9 @@ defmodule Kernel.SpecialForms do
|
||||
iex> lc <<r::8, g::8, b::8>> inbits pixels, do: {r, g, b}
|
||||
[{213,45,132},{64,76,32},{76,0,0},{234,32,15}]
|
||||
|
||||
Variable assignments in the `do` block of the comprehension
|
||||
are not reflected outside of the block.
|
||||
|
||||
Note: Unlike Erlang, Elixir comprehension filters
|
||||
never behave as guards when it comes to errors. Errors in
|
||||
list comprehensions will always be raised. Consider this
|
||||
@@ -837,10 +957,11 @@ defmodule Kernel.SpecialForms do
|
||||
defmacro lc(args)
|
||||
|
||||
@doc """
|
||||
Defines a bit comprehension. It follows the same syntax as
|
||||
a list comprehension but expects each element returned to
|
||||
be a bitstring. For example, here is how to remove all
|
||||
spaces from a string:
|
||||
Defines a bit comprehension.
|
||||
|
||||
It follows the same syntax and behaviour as a list comprehension
|
||||
but expects each element returned to be a bitstring. For example,
|
||||
here is how to remove all spaces from a string:
|
||||
|
||||
iex> bc <<c>> inbits " hello world ", c != ? , do: <<c>>
|
||||
"helloworld"
|
||||
@@ -849,6 +970,8 @@ defmodule Kernel.SpecialForms do
|
||||
defmacro bc(args)
|
||||
|
||||
@doc """
|
||||
Internal special form for block expressions.
|
||||
|
||||
This is the special form used whenever we have a block
|
||||
of expressions in Elixir. This special form is private
|
||||
and should not be invoked directly:
|
||||
@@ -860,7 +983,7 @@ defmodule Kernel.SpecialForms do
|
||||
defmacro __block__(args)
|
||||
|
||||
@doc """
|
||||
Captures a call as an anonymous function.
|
||||
Captures an anonymous function.
|
||||
|
||||
The most common format to capture a function is via module,
|
||||
name and arity:
|
||||
@@ -938,30 +1061,31 @@ defmodule Kernel.SpecialForms do
|
||||
defmacro unquote(name)(expr)
|
||||
|
||||
@doc """
|
||||
This is the special form used whenever we have to temporarily
|
||||
change the scope information of a block. Used when `quote` is
|
||||
invoked with `location: :keep` to execute a given block as if
|
||||
it belonged to another file.
|
||||
Internal special form for modifying scope information.
|
||||
|
||||
Used when `quote` is invoked with `location: :keep` to execute
|
||||
a given block as if it belonged to another file.
|
||||
|
||||
quote location: :keep, do: 1
|
||||
#=> { :__scope__, [line: 1], [[file: "iex"],[do: 1]] }
|
||||
|
||||
Check `quote/1` for more information.
|
||||
Check `quote/2` for more information.
|
||||
"""
|
||||
defmacro __scope__(opts, args)
|
||||
|
||||
@doc """
|
||||
This is the special form used to hold aliases information.
|
||||
Internal special form to hold aliases information.
|
||||
|
||||
It is usually compiled to an atom:
|
||||
|
||||
quote do: Foo.Bar #=>
|
||||
{ :__aliases__, [], [:Foo,:Bar] }
|
||||
iex> quote do: Foo.Bar
|
||||
{:__aliases__, [alias: false], [:Foo, :Bar]}
|
||||
|
||||
Elixir represents `Foo.Bar` as `__aliases__` so calls can be
|
||||
unambiguously identified by the operator `:.`. For example:
|
||||
|
||||
quote do: Foo.bar #=>
|
||||
{{:.,[],[{:__aliases__,[],[:Foo]},:bar]},[],[]}
|
||||
iex> quote do: Foo.bar
|
||||
{{:., [], [{:__aliases__, [alias: false], [:Foo]}, :bar]}, [], []}
|
||||
|
||||
Whenever an expression iterator sees a `:.` as the tuple key,
|
||||
it can be sure that it represents a call and the second argument
|
||||
@@ -978,7 +1102,7 @@ defmodule Kernel.SpecialForms do
|
||||
4) When the head element of aliases is not an atom, it is expanded at runtime:
|
||||
|
||||
quote do: some_var.Foo
|
||||
{:__aliases__,[],[{:some_var,[],:quoted},:Bar]}
|
||||
{:__aliases__, [], [{:some_var, [], Elixir}, :Foo]}
|
||||
|
||||
Since `some_var` is not available at compilation time, the compiler
|
||||
expands such expression to:
|
||||
|
||||
@@ -159,7 +159,7 @@ defmodule Kernel.Typespec do
|
||||
@doc """
|
||||
Defines a `type`, `typep` or `opaque` by receiving Erlang's typespec.
|
||||
"""
|
||||
def define_type(module, kind, { name, _, vars } = type) when kind in [:type, :typep, :opaque] do
|
||||
def define_type(caller, kind, { name, _, vars } = type) when kind in [:type, :typep, :opaque] do
|
||||
{ kind, export } =
|
||||
case kind do
|
||||
:type -> { :type, true }
|
||||
@@ -167,12 +167,35 @@ defmodule Kernel.Typespec do
|
||||
:opaque -> { :opaque, true }
|
||||
end
|
||||
|
||||
module = caller.module
|
||||
arity = length(vars)
|
||||
|
||||
Module.compile_typespec module, kind, type
|
||||
if export, do:
|
||||
Module.compile_typespec(module, :export_type, [{ name, length(vars) }])
|
||||
|
||||
if export do
|
||||
Module.compile_typespec(module, :export_type, [{ name, arity }])
|
||||
end
|
||||
|
||||
define_doc(caller, kind, name, arity, export)
|
||||
type
|
||||
end
|
||||
|
||||
defp define_doc(caller, kind, name, arity, export) do
|
||||
module = caller.module
|
||||
doc = Module.get_attribute(module, :typedoc)
|
||||
|
||||
if doc do
|
||||
if export do
|
||||
Module.add_doc(module, caller.line, kind, { name, arity }, doc)
|
||||
else
|
||||
:elixir_errors.warn "#{caller.file}:#{caller.line}: type #{name}/#{arity} is private, " <>
|
||||
"@typedoc's are always discarded for private types\n"
|
||||
end
|
||||
end
|
||||
|
||||
Module.delete_attribute(module, :typedoc)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Defines a `spec` by receiving Erlang's typespec.
|
||||
"""
|
||||
@@ -193,7 +216,7 @@ defmodule Kernel.Typespec do
|
||||
for modules being compiled.
|
||||
"""
|
||||
def defines_type?(module, name, arity) do
|
||||
finder = match?({ ^name, _, vars } when length(vars) == arity, &1)
|
||||
finder = &match?({ ^name, _, vars } when length(vars) == arity, &1)
|
||||
:lists.any(finder, Module.get_attribute(module, :type)) or
|
||||
:lists.any(finder, Module.get_attribute(module, :opaque))
|
||||
end
|
||||
@@ -204,7 +227,7 @@ defmodule Kernel.Typespec do
|
||||
"""
|
||||
def defines_spec?(module, name, arity) do
|
||||
tuple = { name, arity }
|
||||
:lists.any(match?(^tuple, &1), Module.get_attribute(module, :spec))
|
||||
:lists.any(&match?(^tuple, &1), Module.get_attribute(module, :spec))
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -213,7 +236,7 @@ defmodule Kernel.Typespec do
|
||||
"""
|
||||
def defines_callback?(module, name, arity) do
|
||||
tuple = { name, arity }
|
||||
:lists.any(match?(^tuple, &1), Module.get_attribute(module, :callback))
|
||||
:lists.any(&match?(^tuple, &1), Module.get_attribute(module, :callback))
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -255,6 +278,27 @@ defmodule Kernel.Typespec do
|
||||
quote do: unquote(name)(unquote_splicing(args)) :: unquote(typespec_to_ast(type))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns all type docs available from the module's beam code.
|
||||
|
||||
It is returned as a list of tuples where the first element is the pair of type
|
||||
name and arity and the second element is the documentation.
|
||||
|
||||
The module has to have a corresponding beam file on the disk which can be
|
||||
located by the runtime system.
|
||||
"""
|
||||
# This is in Kernel.Typespec because it works very similar to beam_types and
|
||||
# uses some of the introspection available here.
|
||||
def beam_typedocs(module) do
|
||||
case abstract_code(module) do
|
||||
{ :ok, abstract_code } ->
|
||||
type_docs = lc { :attribute, _, :typedoc, tup } inlist abstract_code, do: tup
|
||||
List.flatten(type_docs)
|
||||
_ ->
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns all types available from the module's beam code.
|
||||
|
||||
@@ -368,7 +412,7 @@ defmodule Kernel.Typespec do
|
||||
vars = lc { :var, _, _ } = var inlist args, do: var
|
||||
type = { name, spec, vars }
|
||||
|
||||
define_type(caller.module, kind, type)
|
||||
define_type(caller, kind, type)
|
||||
end
|
||||
|
||||
@doc false
|
||||
|
||||
@@ -356,22 +356,16 @@ defmodule Keyword do
|
||||
:lists.keymember(key, 1, keywords)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def update(dict, key, fun) when is_function(fun, 1) do
|
||||
IO.write "Keyword.update/3 is deprecated, please use Keyword.update!/3 instead\n#{Exception.format_stacktrace}"
|
||||
update!(dict, key, fun)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Updates the `key` with the given function. If the `key` does
|
||||
not exist, raises `KeyError`.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Keyword.update!([a: 1], :a, &1 * 2)
|
||||
iex> Keyword.update!([a: 1], :a, &(&1 * 2))
|
||||
[a: 2]
|
||||
|
||||
iex> Keyword.update!([a: 1], :b, &1 * 2)
|
||||
iex> Keyword.update!([a: 1], :b, &(&1 * 2))
|
||||
** (KeyError) key not found: :b
|
||||
|
||||
"""
|
||||
@@ -394,10 +388,10 @@ defmodule Keyword do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Keyword.update([a: 1], :a, 13, &1 * 2)
|
||||
iex> Keyword.update([a: 1], :a, 13, &(&1 * 2))
|
||||
[a: 2]
|
||||
|
||||
iex> Keyword.update([a: 1], :b, 11, &1 * 2)
|
||||
iex> Keyword.update([a: 1], :b, 11, &(&1 * 2))
|
||||
[a: 1, b: 11]
|
||||
|
||||
"""
|
||||
|
||||
+1
-13
@@ -12,18 +12,6 @@ defmodule List do
|
||||
|
||||
@compile :inline_list_funcs
|
||||
|
||||
@doc false
|
||||
def concat(list) when is_list(list) do
|
||||
IO.write "List.concat/1 is deprecated, please use Enum.concat/1 instead\n#{Exception.format_stacktrace}"
|
||||
:lists.append(list)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def concat(list, elements) when is_list(list) and is_list(elements) do
|
||||
IO.write "List.concat/2 is deprecated, please use Enum.concat/2 instead\n#{Exception.format_stacktrace}"
|
||||
list ++ elements
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deletes the given item from the list. Returns a list without the item.
|
||||
If the item occurs more than once in the list, just the first occurrence
|
||||
@@ -307,7 +295,7 @@ defmodule List do
|
||||
|
||||
"""
|
||||
def unzip(list) when is_list(list) do
|
||||
:lists.map tuple_to_list(&1), zip(list)
|
||||
:lists.map &tuple_to_list/1, zip(list)
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
||||
@@ -111,12 +111,6 @@ defmodule ListDict do
|
||||
lc { k, _ } = tuple inlist dict, not :lists.member(k, keys), do: tuple
|
||||
end
|
||||
|
||||
@doc false
|
||||
def update(dict, key, fun) when is_function(fun, 1) do
|
||||
IO.write "ListDict.update/3 is deprecated, please use ListDict.update!/3 instead\n#{Exception.format_stacktrace}"
|
||||
update!(dict, key, fun)
|
||||
end
|
||||
|
||||
def update!([{key, value}|dict], key, fun) do
|
||||
[{key, fun.(value)}|delete(dict, key)]
|
||||
end
|
||||
|
||||
+7
-18
@@ -5,6 +5,7 @@ defmodule Macro do
|
||||
This module provides conveniences for working with macros.
|
||||
"""
|
||||
|
||||
@typedoc "Abstract Syntax Tree (AST) node"
|
||||
@type t :: { t, t } | { t, Keyword.t, t } | atom | number | binary | list
|
||||
|
||||
@doc false
|
||||
@@ -95,18 +96,6 @@ defmodule Macro do
|
||||
:elixir_quote.escape(expr, Keyword.get(opts, :unquote, false)) |> elem(0)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def unescape_binary(chars) do
|
||||
IO.write "Macro.unescape_binary/1 is deprecated, please use Macro.unescape_string/1 instead\n#{Exception.format_stacktrace}"
|
||||
unescape_string(chars)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def unescape_binary(chars, map) do
|
||||
IO.write "Macro.unescape_binary/2 is deprecated, please use Macro.unescape_string/2 instead\n#{Exception.format_stacktrace}"
|
||||
unescape_string(chars, map)
|
||||
end
|
||||
|
||||
@doc %S"""
|
||||
Unescape the given chars. This is the unescaping behavior
|
||||
used by default in Elixir single- and double-quoted strings.
|
||||
@@ -174,7 +163,7 @@ defmodule Macro do
|
||||
|
||||
Using the unescape_map defined above is easy:
|
||||
|
||||
Macro.unescape_string "example\\n", unescape_map(&1)
|
||||
Macro.unescape_string "example\\n", &unescape_map(&1)
|
||||
|
||||
"""
|
||||
@spec unescape_string(String.t, (non_neg_integer -> non_neg_integer | false)) :: String.t
|
||||
@@ -336,7 +325,7 @@ defmodule Macro do
|
||||
defmacrop kw_keywords, do: [:do, :catch, :rescue, :after, :else]
|
||||
|
||||
defp is_kw_blocks?([_|_] = kw) do
|
||||
Enum.all?(kw, match?({x, _} when x in kw_keywords, &1))
|
||||
Enum.all?(kw, &match?({x, _} when x in kw_keywords, &1))
|
||||
end
|
||||
defp is_kw_blocks?(_), do: false
|
||||
|
||||
@@ -529,7 +518,7 @@ defmodule Macro do
|
||||
aliases ->
|
||||
aliases = lc alias inlist aliases, do: (expand_once(alias, env, cache) |> elem(0))
|
||||
|
||||
case :lists.all(is_atom(&1), aliases) do
|
||||
case :lists.all(&is_atom/1, aliases) do
|
||||
true ->
|
||||
receiver = :elixir_aliases.concat(aliases)
|
||||
:elixir_tracker.record_alias(receiver, env.module)
|
||||
@@ -611,7 +600,7 @@ defmodule Macro do
|
||||
defp to_erl_env(_env, cache), do: cache
|
||||
|
||||
defp is_partial?(args) do
|
||||
:lists.any(match?({ :&, _, [_] }, &1), args)
|
||||
:lists.any(&match?({ :&, _, [_] }, &1), args)
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -686,7 +675,7 @@ defmodule Macro do
|
||||
end
|
||||
|
||||
defp expand_all_until({ list, cache }, env) when is_list(list) do
|
||||
:lists.mapfoldl(expand_all(&1, env, &2), cache, list)
|
||||
:lists.mapfoldl(&expand_all(&1, env, &2), cache, list)
|
||||
end
|
||||
|
||||
defp expand_all_until({ other, cache }, _env) do
|
||||
@@ -712,7 +701,7 @@ defmodule Macro do
|
||||
end
|
||||
|
||||
defp do_safe_term({ left, right }), do: do_safe_term(left) || do_safe_term(right)
|
||||
defp do_safe_term(terms) when is_list(terms), do: Enum.find_value(terms, do_safe_term(&1))
|
||||
defp do_safe_term(terms) when is_list(terms), do: Enum.find_value(terms, &do_safe_term(&1))
|
||||
defp do_safe_term(terms) when is_tuple(terms), do: { :unsafe, terms }
|
||||
defp do_safe_term(_), do: nil
|
||||
end
|
||||
|
||||
@@ -477,10 +477,11 @@ defmodule Module do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Attaches documentation to a given function. It expects
|
||||
the module the function belongs to, the line (a non negative
|
||||
Attaches documentation to a given function or type. It expects
|
||||
the module the function/type belongs to, the line (a non negative
|
||||
integer), the kind (def or defmacro), a tuple representing
|
||||
the function and its arity and the documentation, which should
|
||||
the function and its arity, the function signature (the signature
|
||||
should be omitted for types) and the documentation, which should
|
||||
be either a binary or a boolean.
|
||||
|
||||
## Examples
|
||||
@@ -491,12 +492,14 @@ defmodule Module do
|
||||
end
|
||||
|
||||
"""
|
||||
def add_doc(_module, _line, kind, _tuple, _signature, doc) when kind in [:defp, :defmacrop] do
|
||||
def add_doc(module, line, kind, tuple, signature // [], doc)
|
||||
|
||||
def add_doc(_module, _line, kind, _tuple, _signature, doc) when kind in [:defp, :defmacrop, :typep] do
|
||||
if doc, do: { :error, :private_doc }, else: :ok
|
||||
end
|
||||
|
||||
def add_doc(module, line, kind, tuple, signature, doc) when
|
||||
kind in [:def, :defmacro] and (is_binary(doc) or is_boolean(doc) or doc == nil) do
|
||||
kind in [:def, :defmacro, :type, :opaque] and (is_binary(doc) or is_boolean(doc) or doc == nil) do
|
||||
assert_not_compiled!(:add_doc, module)
|
||||
table = docs_table_for(module)
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ defmodule Module.DispatchTracker do
|
||||
neighbours = (lc { _, _ } = t inlist neighbours, do: t) |> :ordsets.from_list
|
||||
remaining = :ordsets.subtract(neighbours, vertices)
|
||||
vertices = :ordsets.union(neighbours, vertices)
|
||||
:lists.foldl(reduce_reachable(d, &1, &2), vertices, remaining)
|
||||
:lists.foldl(&reduce_reachable(d, &1, &2), vertices, remaining)
|
||||
end
|
||||
|
||||
defp to_pid(pid) when is_pid(pid), do: pid
|
||||
@@ -249,7 +249,7 @@ defmodule Module.DispatchTracker do
|
||||
end
|
||||
|
||||
defp has_imports?(d, mod) do
|
||||
Enum.any?(:digraph.in_neighbours(d, mod), match?({ :import, _, _ }, &1))
|
||||
Enum.any?(:digraph.in_neighbours(d, mod), &match?({ :import, _, _ }, &1))
|
||||
end
|
||||
|
||||
# Yanks a local node. Returns its in and out vertices in a tuple.
|
||||
@@ -282,7 +282,7 @@ defmodule Module.DispatchTracker do
|
||||
@doc false
|
||||
def collect_unused_locals(pid, private) do
|
||||
reachable = reachable(pid)
|
||||
:lists.foldl(collect_unused_locals(&1, &2, reachable), [], private)
|
||||
:lists.foldl(&collect_unused_locals(&1, &2, reachable), [], private)
|
||||
end
|
||||
|
||||
defp collect_unused_locals({ tuple, kind, 0 }, acc, reachable) do
|
||||
|
||||
+55
-24
@@ -77,9 +77,6 @@ defmodule Record do
|
||||
example, is only available inside the `User` module and nowhere else.
|
||||
You can find more information in `Kernel.defrecordp/2` docs.
|
||||
|
||||
In general, though, records are used as part of a module's public
|
||||
interface. For such use cases, Elixir provides record modules.
|
||||
|
||||
## defrecord
|
||||
|
||||
By using `defrecord`, a developer can make a Record definition
|
||||
@@ -97,7 +94,7 @@ defmodule Record do
|
||||
user() #=> User[name: "José", age: 25]
|
||||
user(age: 26) #=> User[name: "José", age: 26]
|
||||
|
||||
Notice that now, since the record definition is accessible, Elixir
|
||||
Notice that now since the record definition is accessible, Elixir
|
||||
shows the record nicely formatted, no longer as a simple tuple. We
|
||||
can get the raw formatting by passing `raw: true` to `inspect`:
|
||||
|
||||
@@ -112,9 +109,9 @@ defmodule Record do
|
||||
User[] #=> User[name: "José", age: 25]
|
||||
User[age: 26] #=> User[name: "José", age: 26]
|
||||
|
||||
The macro name is replaced by the module name and the parenthesis
|
||||
are replaced by brackets. When the shortcut syntax is used, there
|
||||
is no need to import the record.
|
||||
The macro name is replaced by the module name and the parentheses
|
||||
are replaced by brackets. When this syntax is used, there is no
|
||||
need to import the record.
|
||||
|
||||
Before we sum up the differences between `defrecord` and
|
||||
`defrecordp`, there is one last functionality introduced by
|
||||
@@ -142,6 +139,12 @@ defmodule Record do
|
||||
records flexibility at the cost of performance since
|
||||
there is more work happening at runtime.
|
||||
|
||||
The above calls (new and update) can interchangeably accept both
|
||||
atom and string keys for field names, however not both at the same time.
|
||||
Please also note that atom keys are faster. This feature allows to
|
||||
"sanitize" untrusted dictionaries and initialize/update records without
|
||||
using `binary_to_existing_atom/1`.
|
||||
|
||||
To sum up, `defrecordp` should be used when you don't want
|
||||
to expose the record information while `defrecord` should be used
|
||||
whenever you want to share a record within your code or with other
|
||||
@@ -175,7 +178,7 @@ defmodule Record do
|
||||
to_string WeekDate[year: 2013, week: 26, week_day: 4]
|
||||
"2013-W26-4"
|
||||
|
||||
A protocol can be implemented for any record defined via `defrecord`.
|
||||
A protocol can be implemented for any record.
|
||||
"""
|
||||
|
||||
@type t :: tuple
|
||||
@@ -627,9 +630,8 @@ defmodule Record do
|
||||
# an ordered dict of options (opts) and it will try to fetch
|
||||
# the given key from the ordered dict, falling back to the
|
||||
# default value if one does not exist.
|
||||
selective = lc { k, v } inlist values do
|
||||
quote do: Keyword.get(opts, unquote(k), unquote(v))
|
||||
end
|
||||
atom_selective = lc { k, v } inlist values, do: initialize_lookup(k, v)
|
||||
string_selective = lc { k, v } inlist values, do: initialize_lookup(atom_to_binary(k), v)
|
||||
|
||||
quote do
|
||||
@doc false
|
||||
@@ -637,7 +639,17 @@ defmodule Record do
|
||||
|
||||
@doc false
|
||||
def new([]), do: { __MODULE__, unquote_splicing(defaults) }
|
||||
def new(opts) when is_list(opts), do: { __MODULE__, unquote_splicing(selective) }
|
||||
def new([{key, _}|_] = opts) when is_atom(key), do: { __MODULE__, unquote_splicing(atom_selective) }
|
||||
def new([{key, _}|_] = opts) when is_binary(key), do: { __MODULE__, unquote_splicing(string_selective) }
|
||||
end
|
||||
end
|
||||
|
||||
defp initialize_lookup(k, v) do
|
||||
quote do
|
||||
case :lists.keyfind(unquote(k), 1, opts) do
|
||||
false -> unquote(v)
|
||||
{_, v} -> v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -728,15 +740,14 @@ defmodule Record do
|
||||
# Define an updater method that receives a
|
||||
# keyword list and updates the record.
|
||||
defp updater(values) do
|
||||
fields =
|
||||
lc {key, _default} inlist values do
|
||||
index = find_index(values, key, 1)
|
||||
quote do
|
||||
Keyword.get(keywords, unquote(key), elem(record, unquote(index)))
|
||||
end
|
||||
end
|
||||
atom_fields =
|
||||
lc {key, _default} inlist values, do: updater_lookup(key, key, values)
|
||||
|
||||
contents = quote do: { __MODULE__, unquote_splicing(fields) }
|
||||
string_fields =
|
||||
lc {key, _default} inlist values, do: updater_lookup(atom_to_binary(key), key, values)
|
||||
|
||||
atom_contents = quote do: { __MODULE__, unquote_splicing(atom_fields) }
|
||||
string_contents = quote do: { __MODULE__, unquote_splicing(string_fields) }
|
||||
|
||||
quote do
|
||||
@doc false
|
||||
@@ -744,8 +755,22 @@ defmodule Record do
|
||||
record
|
||||
end
|
||||
|
||||
def update(keywords, record) do
|
||||
unquote(contents)
|
||||
def update([{key, _}|_] = keywords, record) when is_atom(key) do
|
||||
unquote(atom_contents)
|
||||
end
|
||||
def update([{key, _}|_] = keywords, record) when is_binary(key) do
|
||||
unquote(string_contents)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp updater_lookup(k, key, values) do
|
||||
v = find_index(values, key, 1)
|
||||
|
||||
quote do
|
||||
case :lists.keyfind(unquote(k), 1, keywords) do
|
||||
false -> elem(record, unquote(v))
|
||||
{_, value} -> value
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -768,6 +793,7 @@ defmodule Record do
|
||||
defp core_specs(values) do
|
||||
types = lc { _, _, spec } inlist values, do: spec
|
||||
options = if values == [], do: [], else: [options_specs(values)]
|
||||
values_specs = if values == [], do: [], else: values_specs(values)
|
||||
|
||||
quote do
|
||||
unless Kernel.Typespec.defines_type?(__MODULE__, :t, 0) do
|
||||
@@ -775,7 +801,7 @@ defmodule Record do
|
||||
end
|
||||
|
||||
unless Kernel.Typespec.defines_type?(__MODULE__, :options, 0) do
|
||||
@type options :: unquote(options)
|
||||
@type options :: unquote(options) | [{String.t, unquote(values_specs)}]
|
||||
end
|
||||
|
||||
@spec new :: t
|
||||
@@ -793,6 +819,11 @@ defmodule Record do
|
||||
{ :|, [], [{ k, v }, acc] }
|
||||
end, { k, v }, t
|
||||
end
|
||||
defp values_specs([{ _, _, v }|t]) do
|
||||
:lists.foldl fn { _, _, v }, acc ->
|
||||
{ :|, [], [v, acc] }
|
||||
end, v, t
|
||||
end
|
||||
|
||||
defp accessor_specs([{ :__exception__, _, _ }|t], 1, acc) do
|
||||
accessor_specs(t, 2, acc)
|
||||
@@ -814,7 +845,7 @@ defmodule Record do
|
||||
|
||||
## Helpers
|
||||
|
||||
defp is_keyword(list) when is_list(list), do: :lists.all(is_keyword_tuple(&1), list)
|
||||
defp is_keyword(list) when is_list(list), do: :lists.all(&is_keyword_tuple/1, list)
|
||||
defp is_keyword(_), do: false
|
||||
|
||||
defp is_keyword_tuple({ x, _ }) when is_atom(x), do: true
|
||||
|
||||
+32
-13
@@ -1,10 +1,10 @@
|
||||
defmodule Regex do
|
||||
@moduledoc %S"""
|
||||
Regular expressions for Elixir built on top of the re module
|
||||
Regular expressions for Elixir built on top of the `re` module
|
||||
in the Erlang Standard Library. More information can be found
|
||||
in the re documentation: http://www.erlang.org/doc/man/re.html
|
||||
in the [`re` documentation](http://www.erlang.org/doc/man/re.html).
|
||||
|
||||
Regular expressions in Elixir can be created using Regex.compile!
|
||||
Regular expressions in Elixir can be created using `Regex.compile!`
|
||||
or using the special form with `%r`:
|
||||
|
||||
# A simple regular expressions that matches foo anywhere in the string
|
||||
@@ -13,10 +13,10 @@ defmodule Regex do
|
||||
# A regular expression with case insensitive options and handling for unicode chars
|
||||
%r/foo/iu
|
||||
|
||||
The re module provides several options, the ones available in Elixir, followed by
|
||||
The `re` module provides several options, the ones available in Elixir, followed by
|
||||
their shortcut in parenthesis, are:
|
||||
|
||||
* `unicode` (u) - enable unicode specific patterns like \p
|
||||
* `unicode` (u) - enables unicode specific patterns like \p
|
||||
* `caseless` (i) - add case insensitivity
|
||||
* `dotall` (s) - causes dot to match newlines and also set newline to anycrlf.
|
||||
The new line setting can be overridden by setting `(*CR)` or `(*LF)` or
|
||||
@@ -27,8 +27,8 @@ defmodule Regex do
|
||||
allow `#` to delimit comments
|
||||
* `firstline` (f) - forces the unanchored pattern to match before or at the first
|
||||
newline, though the matched text may continue over the newline
|
||||
* `ungreedy` (r) - invert the "greediness" of the regexp
|
||||
* `groups` (g) - compile with info about groups available
|
||||
* `ungreedy` (r) - inverts the "greediness" of the regexp
|
||||
* `groups` (g) - compiles with info about groups available
|
||||
|
||||
The options not available are:
|
||||
|
||||
@@ -53,10 +53,18 @@ defmodule Regex do
|
||||
|
||||
The given options can either be a binary with the characters
|
||||
representing the same regex options given to the `%r` sigil,
|
||||
or a list of options, as expected by the Erlang `re` docs.
|
||||
or a list of options, as expected by the [Erlang `re` docs](http://www.erlang.org/doc/man/re.html).
|
||||
|
||||
It returns `{ :ok, regex }` in case of success,
|
||||
`{ :error, reason }` otherwise.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Regex.compile("foo")
|
||||
{:ok, %r"foo"}
|
||||
iex> Regex.compile("*foo")
|
||||
{:error, {'nothing to repeat', 0}}
|
||||
|
||||
"""
|
||||
@spec compile(binary, binary | [term]) :: t
|
||||
def compile(source, options // "")
|
||||
@@ -148,21 +156,27 @@ defmodule Regex do
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def captures(regex, string, options // []) do
|
||||
IO.write "Regex.captures/3 is deprecated, please use Regex.named_captures/3\n#{Exception.format_stacktrace}"
|
||||
named_captures(regex, string, options)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the given captures as a keyword list or `nil` if no captures
|
||||
are found. Requires the regex to be compiled with the groups option.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Regex.captures(%r/c(?<foo>d)/g, "abcd")
|
||||
iex> Regex.named_captures(%r/c(?<foo>d)/g, "abcd")
|
||||
[foo: "d"]
|
||||
iex> Regex.captures(%r/a(?<foo>b)c(?<bar>d)/g, "abcd")
|
||||
iex> Regex.named_captures(%r/a(?<foo>b)c(?<bar>d)/g, "abcd")
|
||||
[foo: "b", bar: "d"]
|
||||
iex> Regex.captures(%r/a(?<foo>b)c(?<bar>d)/g, "efgh")
|
||||
iex> Regex.named_captures(%r/a(?<foo>b)c(?<bar>d)/g, "efgh")
|
||||
nil
|
||||
|
||||
"""
|
||||
def captures(regex(groups: groups) = regex, string, options // []) do
|
||||
def named_captures(regex(groups: groups) = regex, string, options // []) do
|
||||
options = Keyword.put_new(options, :capture, :groups)
|
||||
results = run(regex, string, options)
|
||||
if results, do: Enum.zip(groups, results)
|
||||
@@ -249,13 +263,14 @@ defmodule Regex do
|
||||
|
||||
options = [{ :capture, captures, return }, :global]
|
||||
case :re.run(string, compiled, options) do
|
||||
:match -> []
|
||||
:nomatch -> []
|
||||
{ :match, results } -> results
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Split the given target into the number of parts specified.
|
||||
Splits the given target into the number of parts specified.
|
||||
If no number of parts is given, it defaults to `:infinity`.
|
||||
|
||||
## Examples
|
||||
@@ -265,6 +280,10 @@ defmodule Regex do
|
||||
["a","b-c"]
|
||||
iex> Regex.split(%r/-/, "abc")
|
||||
["abc"]
|
||||
iex> Regex.split(%r//, "abc")
|
||||
["a", "b", "c", ""]
|
||||
iex> Regex.split(%r//, "abc", trim: true)
|
||||
["a", "b", "c"]
|
||||
"""
|
||||
|
||||
def split(regex, string, options // [])
|
||||
|
||||
+42
-17
@@ -8,7 +8,7 @@ defmodule Stream do
|
||||
|
||||
iex> range = 1..5
|
||||
1..5
|
||||
iex> Enum.map range, &1 * 2
|
||||
iex> Enum.map range, &(&1 * 2)
|
||||
[2,4,6,8,10]
|
||||
|
||||
In the example above, as we mapped over the range, the elements being
|
||||
@@ -16,8 +16,8 @@ defmodule Stream do
|
||||
module allows us to map the range, without triggering its enumeration:
|
||||
|
||||
iex> range = 1..3
|
||||
iex> stream = Stream.map(range, &1 * 2)
|
||||
iex> Enum.map(stream, &1 + 1)
|
||||
iex> stream = Stream.map(range, &(&1 * 2))
|
||||
iex> Enum.map(stream, &(&1 + 1))
|
||||
[3,5,7]
|
||||
|
||||
Notice we started with a range and then we created a stream that is
|
||||
@@ -34,9 +34,9 @@ defmodule Stream do
|
||||
example:
|
||||
|
||||
1..3 |>
|
||||
Enum.map(IO.inspect(&1)) |>
|
||||
Enum.map(&1 * 2) |>
|
||||
Enum.map(IO.inspect(&1))
|
||||
Enum.map(&IO.inspect(&1)) |>
|
||||
Enum.map(&(&1 * 2)) |>
|
||||
Enum.map(&IO.inspect(&1))
|
||||
1
|
||||
2
|
||||
3
|
||||
@@ -50,9 +50,9 @@ defmodule Stream do
|
||||
was iterated three times. Let's see an example with streams:
|
||||
|
||||
stream = 1..3 |>
|
||||
Stream.map(IO.inspect(&1)) |>
|
||||
Stream.map(&1 * 2) |>
|
||||
Stream.map(IO.inspect(&1))
|
||||
Stream.map(&IO.inspect(&1)) |>
|
||||
Stream.map(&(&1 * 2)) |>
|
||||
Stream.map(&IO.inspect(&1))
|
||||
Enum.to_list(stream)
|
||||
1
|
||||
2
|
||||
@@ -81,8 +81,8 @@ defmodule Stream do
|
||||
|
||||
This module also allows us to create streams from any enumerable:
|
||||
|
||||
iex> stream = Stream.map([1,2,3], &1 * 2)
|
||||
iex> Enum.map(stream, &1 + 1)
|
||||
iex> stream = Stream.map([1,2,3], &(&1 * 2))
|
||||
iex> Enum.map(stream, &(&1 + 1))
|
||||
[3,5,7]
|
||||
|
||||
By simply passing a list (which is an enumerable) as the first argument
|
||||
@@ -187,7 +187,7 @@ defmodule Stream do
|
||||
"""
|
||||
@spec cycle(Enumerable.t) :: t
|
||||
def cycle(enumerable) do
|
||||
do_cycle(enumerable, &1, &2)
|
||||
&do_cycle(enumerable, &1, &2)
|
||||
end
|
||||
|
||||
defp do_cycle(enumerable, acc, fun) do
|
||||
@@ -225,7 +225,7 @@ defmodule Stream do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> stream = Stream.drop_while(1..10, &1 <= 5)
|
||||
iex> stream = Stream.drop_while(1..10, &(&1 <= 5))
|
||||
iex> Enum.to_list(stream)
|
||||
[6,7,8,9,10]
|
||||
|
||||
@@ -271,7 +271,7 @@ defmodule Stream do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Stream.iterate(0, &1+1) |> Enum.take(5)
|
||||
iex> Stream.iterate(0, &(&1+1)) |> Enum.take(5)
|
||||
[0,1,2,3,4]
|
||||
|
||||
"""
|
||||
@@ -365,7 +365,7 @@ defmodule Stream do
|
||||
@spec repeatedly((() -> element)) :: t
|
||||
def repeatedly(generator_fun)
|
||||
when is_function(generator_fun, 0) do
|
||||
do_repeatedly(generator_fun, &1, &2)
|
||||
&do_repeatedly(generator_fun, &1, &2)
|
||||
end
|
||||
|
||||
defp do_repeatedly(generator_fun, acc, fun) do
|
||||
@@ -388,7 +388,7 @@ defmodule Stream do
|
||||
|
||||
"""
|
||||
@spec take(Enumerable.t, non_neg_integer) :: t
|
||||
def take(_enumerable, 0), do: Lazy[enumerable: [], fun: & &1]
|
||||
def take(_enumerable, 0), do: Lazy[enumerable: [], fun: &(&1)]
|
||||
|
||||
def take(enumerable, n) when n > 0 do
|
||||
Lazy[enumerable: enumerable,
|
||||
@@ -407,7 +407,7 @@ defmodule Stream do
|
||||
|
||||
## Examples
|
||||
|
||||
iex> stream = Stream.take_while(1..100, &1 <= 5)
|
||||
iex> stream = Stream.take_while(1..100, &(&1 <= 5))
|
||||
iex> Enum.to_list(stream)
|
||||
[1,2,3,4,5]
|
||||
|
||||
@@ -427,6 +427,31 @@ defmodule Stream do
|
||||
acc: true]
|
||||
end
|
||||
|
||||
@doc """
|
||||
Emit a sequence of values and accumulators. Successive values are generated by
|
||||
calling `next_fun` with the previous accumulator.
|
||||
|
||||
If the return value is nil iteration ends.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Stream.unfold(5, fn 0 -> nil; n -> {n, n-1} end) |> Enum.to_list()
|
||||
[5, 4, 3, 2, 1]
|
||||
"""
|
||||
@spec unfold(acc, (acc -> { element, acc } | nil)) :: t
|
||||
def unfold(acc, f) do
|
||||
fn acc1, f1 ->
|
||||
do_unfold(acc, f, acc1, f1)
|
||||
end
|
||||
end
|
||||
|
||||
defp do_unfold(gen_acc, gen_fun, acc, fun) do
|
||||
case gen_fun.(gen_acc) do
|
||||
nil -> acc
|
||||
{ v, new_gen_acc } -> do_unfold(new_gen_acc, gen_fun, fun.(v, acc), fun)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a stream where each item in the enumerable will
|
||||
be accompanied by its index.
|
||||
|
||||
+32
-26
@@ -155,7 +155,7 @@ defmodule String do
|
||||
@spec split(t) :: [t]
|
||||
defdelegate split(binary), to: String.Unicode
|
||||
|
||||
@doc """
|
||||
@doc %S"""
|
||||
Divides a string into substrings based on a pattern,
|
||||
returning a list of these substrings. The pattern can
|
||||
be a string, a list of strings or a regular expression.
|
||||
@@ -168,6 +168,8 @@ defmodule String do
|
||||
|
||||
## Examples
|
||||
|
||||
Splitting with a string pattern:
|
||||
|
||||
iex> String.split("a,b,c", ",")
|
||||
["a", "b", "c"]
|
||||
iex> String.split("a,b,c", ",", global: false)
|
||||
@@ -175,15 +177,30 @@ defmodule String do
|
||||
iex> String.split(" a b c ", " ", trim: true)
|
||||
["a", "b", "c"]
|
||||
|
||||
A list of patterns:
|
||||
|
||||
iex> String.split("1,2 3,4", [" ", ","])
|
||||
["1", "2", "3", "4"]
|
||||
|
||||
A regular expression:
|
||||
|
||||
iex> String.split("a,b,c", %r{,})
|
||||
["a", "b", "c"]
|
||||
iex> String.split("a,b,c", %r{,}, global: false)
|
||||
["a", "b,c"]
|
||||
iex> String.split("a,b", %r{\\.})
|
||||
["a,b"]
|
||||
iex> String.split(" a b c ", %r{\s}, trim: true)
|
||||
["a", "b", "c"]
|
||||
|
||||
Splitting on empty patterns returns codepoints:
|
||||
|
||||
iex> String.split("abc", %r{})
|
||||
["a", "b", "c", ""]
|
||||
iex> String.split("abc", "")
|
||||
["a", "b", "c", ""]
|
||||
iex> String.split("abc", "", trim: true)
|
||||
["a", "b", "c"]
|
||||
iex> String.split("abc", "", global: false)
|
||||
["a", "bc"]
|
||||
|
||||
"""
|
||||
@spec split(t, t | [t] | Regex.t) :: [t]
|
||||
@@ -192,6 +209,8 @@ defmodule String do
|
||||
|
||||
def split("", _pattern, _options), do: [""]
|
||||
|
||||
def split(binary, "", options), do: split(binary, %r""u, options)
|
||||
|
||||
def split(binary, pattern, options) when is_regex(pattern) do
|
||||
Regex.split(pattern, binary, options)
|
||||
end
|
||||
@@ -642,27 +661,14 @@ defmodule String do
|
||||
def valid_character?(<<_ :: utf8>> = codepoint), do: valid?(codepoint)
|
||||
def valid_character?(_), do: false
|
||||
|
||||
@doc %S"""
|
||||
Checks whether `str` is a valid codepoint.
|
||||
@doc false
|
||||
def valid_codepoint?(binary) do
|
||||
IO.write "String.valid_codepoint?/1 is deprecated, please use match against << _ :: utf8 >> instead\n#{Exception.format_stacktrace}"
|
||||
do_valid_codepoint?(binary)
|
||||
end
|
||||
|
||||
Note that the empty string is considered invalid, as are
|
||||
strings containing multiple codepoints.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> String.valid_codepoint?("a")
|
||||
true
|
||||
iex> String.valid_codepoint?("ø")
|
||||
true
|
||||
iex> String.valid_codepoint?(<<0xffff :: 16>>)
|
||||
false
|
||||
iex> String.valid_codepoint?("asdf")
|
||||
false
|
||||
|
||||
"""
|
||||
@spec valid_codepoint?(codepoint) :: boolean
|
||||
def valid_codepoint?(<<_ :: utf8>>), do: true
|
||||
def valid_codepoint?(_), do: false
|
||||
defp do_valid_codepoint?(<<_ :: utf8>>), do: true
|
||||
defp do_valid_codepoint?(_), do: false
|
||||
|
||||
@doc """
|
||||
Returns unicode graphemes in the string.
|
||||
@@ -947,7 +953,7 @@ defmodule String do
|
||||
@spec starts_with?(t, t | [t]) :: boolean
|
||||
|
||||
def starts_with?(string, prefixes) when is_list(prefixes) do
|
||||
Enum.any?(prefixes, do_starts_with(string, &1))
|
||||
Enum.any?(prefixes, &do_starts_with(string, &1))
|
||||
end
|
||||
|
||||
def starts_with?(string, prefix) do
|
||||
@@ -983,7 +989,7 @@ defmodule String do
|
||||
@spec ends_with?(t, t | [t]) :: boolean
|
||||
|
||||
def ends_with?(string, suffixes) when is_list(suffixes) do
|
||||
Enum.any?(suffixes, do_ends_with(string, &1))
|
||||
Enum.any?(suffixes, &do_ends_with(string, &1))
|
||||
end
|
||||
|
||||
def ends_with?(string, suffix) do
|
||||
@@ -1022,7 +1028,7 @@ defmodule String do
|
||||
@spec contains?(t, t | [t]) :: boolean
|
||||
|
||||
def contains?(string, matches) when is_list(matches) do
|
||||
Enum.any?(matches, do_contains(string, &1))
|
||||
Enum.any?(matches, &do_contains(string, &1))
|
||||
end
|
||||
|
||||
def contains?(string, match) do
|
||||
|
||||
@@ -54,7 +54,7 @@ defmodule URI do
|
||||
that implements the `String.Chars` protocol (i.e. can be converted
|
||||
to a binary).
|
||||
"""
|
||||
def encode_query(l), do: Enum.map_join(l, "&", pair(&1))
|
||||
def encode_query(l), do: Enum.map_join(l, "&", &pair/1)
|
||||
|
||||
@doc """
|
||||
Given a query string of the form "key1=value1&key=value2...", produces an
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
defmodule Mix.Version do
|
||||
defmodule Version do
|
||||
@moduledoc %S"""
|
||||
This module provides functions for parsing and matching
|
||||
versions with requirements.
|
||||
|
||||
A version is a string or a `Mix.Version.Schema` generated
|
||||
after parsing via `Mix.Version.parse/1`. A requirement is
|
||||
A version is a string or a `Version.Schema` generated
|
||||
after parsing via `Version.parse/1`. A requirement is
|
||||
a string that follows a specific format.
|
||||
|
||||
`Mix.Version` parsing and requirements follows
|
||||
`Version` parsing and requirements follows
|
||||
[SemVer 2.0 schema](http://semver.org/) and you will get
|
||||
the most of Mix' version system by following it. In order
|
||||
to support integration with projects that may
|
||||
@@ -54,8 +54,8 @@ defmodule Mix.Version do
|
||||
|
||||
"""
|
||||
|
||||
@type t :: String.t | Mix.Version.Schema.t
|
||||
@type requirement :: String.t | Mix.Version.Requirement.t
|
||||
@type t :: String.t | Version.Schema.t
|
||||
@type requirement :: String.t | Version.Requirement.t
|
||||
|
||||
@type matchable :: { major :: String.t | non_neg_integer,
|
||||
minor :: non_neg_integer | nil,
|
||||
@@ -83,7 +83,7 @@ defmodule Mix.Version do
|
||||
"""
|
||||
@spec match?(t, requirement) :: boolean
|
||||
def match?(version, requirement) when is_binary(requirement) do
|
||||
case Mix.Version.Parser.parse_requirement(requirement) do
|
||||
case Version.Parser.parse_requirement(requirement) do
|
||||
{ :ok, req } ->
|
||||
match?(version, req)
|
||||
{ :error, reason } ->
|
||||
@@ -109,20 +109,20 @@ defmodule Mix.Version do
|
||||
"""
|
||||
@spec valid?(String.t | Schema.t) :: boolean
|
||||
def valid?(string) when is_binary(string) do
|
||||
Mix.Version.Parser.valid_version?(string)
|
||||
Version.Parser.valid_version?(string)
|
||||
end
|
||||
|
||||
def valid?(Mix.Version.Schema[major: nil]), do: false
|
||||
def valid?(Mix.Version.Schema[]), do: true
|
||||
def valid?(Version.Schema[major: nil]), do: false
|
||||
def valid?(Version.Schema[]), do: true
|
||||
|
||||
@doc """
|
||||
Parse a version into a matchable value.
|
||||
"""
|
||||
@spec parse(String.t) :: { :ok, Schema.t } | { :error, term }
|
||||
def parse(string) when is_binary(string) do
|
||||
case Mix.Version.Parser.parse_version(string) do
|
||||
case Version.Parser.parse_version(string) do
|
||||
{ :ok, matchable } -> from_matchable(matchable).source(string).build(get_build(string))
|
||||
{ :error, _ } -> Mix.Version.Schema[source: string]
|
||||
{ :error, _ } -> Version.Schema[source: string]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -139,17 +139,17 @@ defmodule Mix.Version do
|
||||
@doc """
|
||||
Get the matchable representation.
|
||||
"""
|
||||
@spec to_matchable(String.t | Schema.t) :: Mix.Version.matchable
|
||||
@spec to_matchable(String.t | Schema.t) :: Version.matchable
|
||||
def to_matchable(Schema[major: nil, source: source]) do
|
||||
{ source, nil, nil, [] }
|
||||
end
|
||||
|
||||
def to_matchable(Mix.Version.Schema[major: major, minor: minor, patch: patch, pre: nil]) do
|
||||
def to_matchable(Version.Schema[major: major, minor: minor, patch: patch, pre: nil]) do
|
||||
{ major, minor, patch, [] }
|
||||
end
|
||||
|
||||
def to_matchable(Mix.Version.Schema[major: major, minor: minor, patch: patch, pre: pre]) do
|
||||
{ major, minor, patch, Mix.Version.Parser.parse_pre(pre) }
|
||||
def to_matchable(Version.Schema[major: major, minor: minor, patch: patch, pre: pre]) do
|
||||
{ major, minor, patch, Version.Parser.parse_pre(pre) }
|
||||
end
|
||||
|
||||
def to_matchable(string) do
|
||||
@@ -157,11 +157,11 @@ defmodule Mix.Version do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Convert a matchable to a `Mix.Version`.
|
||||
Convert a matchable to a `Version`.
|
||||
"""
|
||||
@spec from_matchable(Mix.Version.matchable) :: Schema.t
|
||||
@spec from_matchable(Version.matchable) :: Schema.t
|
||||
def from_matchable({ source, nil, nil, nil }) when is_binary(source) do
|
||||
Mix.Version.Schema[source: source]
|
||||
Version.Schema[source: source]
|
||||
end
|
||||
|
||||
def from_matchable({ major, minor, patch, pre }) do
|
||||
@@ -184,7 +184,7 @@ defmodule Mix.Version do
|
||||
end
|
||||
end
|
||||
|
||||
Mix.Version.Schema[major: major, minor: minor, patch: patch, pre: pre, source: source]
|
||||
Version.Schema[major: major, minor: minor, patch: patch, pre: pre, source: source]
|
||||
end
|
||||
|
||||
defmodule Parser.DSL do
|
||||
@@ -251,12 +251,12 @@ defmodule Mix.Version do
|
||||
end
|
||||
|
||||
deflexer acc do
|
||||
Enum.filter(Enum.reverse(acc), &1 != :' ')
|
||||
Enum.filter(Enum.reverse(acc), &(&1 != :' '))
|
||||
end
|
||||
|
||||
@version_regex %r/^(\d+)(?:\.(\d+)(?:\.(\d+))?)?(?:\-([^\s]+))?(?:\+[^\d]+)?$/
|
||||
|
||||
@spec parse_requirement(String.t) :: { :ok, Mix.Version.Requirement.t } | { :error, binary | atom }
|
||||
@spec parse_requirement(String.t) :: { :ok, Version.Requirement.t } | { :error, binary | atom }
|
||||
def parse_requirement(source) do
|
||||
lexed = lexer(source, [])
|
||||
|
||||
@@ -280,7 +280,7 @@ defmodule Mix.Version do
|
||||
defp nillify(""), do: nil
|
||||
defp nillify(o), do: o
|
||||
|
||||
@spec parse_version(String.t) :: { :ok, Mix.Version.matchable } | { :error, :invalid_version }
|
||||
@spec parse_version(String.t) :: { :ok, Version.matchable } | { :error, :invalid_version }
|
||||
def parse_version(string) when is_binary(string) do
|
||||
if valid_version?(string) do
|
||||
destructure [_, major, minor, patch, pre], Regex.run(@version_regex, string)
|
||||
@@ -364,7 +364,7 @@ defmodule Mix.Version do
|
||||
end
|
||||
|
||||
defp approximate(version) do
|
||||
Mix.Version.from_matchable(case Regex.run(@version_regex, version) do
|
||||
Version.from_matchable(case Regex.run(@version_regex, version) do
|
||||
[_, major] ->
|
||||
{ binary_to_integer(major) + 1, 0, 0, [] }
|
||||
|
||||
@@ -387,19 +387,19 @@ defmodule Mix.Version do
|
||||
end
|
||||
|
||||
defp to_condition([:'==', version | _]) do
|
||||
version = Mix.Version.to_matchable(version)
|
||||
version = Version.to_matchable(version)
|
||||
|
||||
{ :'==', :'$_', { :const, version } }
|
||||
end
|
||||
|
||||
defp to_condition([:'!=', version | _]) do
|
||||
version = Mix.Version.to_matchable(version)
|
||||
version = Version.to_matchable(version)
|
||||
|
||||
{ :'/=', :'$_', { :const, version } }
|
||||
end
|
||||
|
||||
defp to_condition([:'~>', version | _]) do
|
||||
from = Mix.Version.parse(version)
|
||||
from = Version.parse(version)
|
||||
to = approximate(version)
|
||||
|
||||
{ :andalso, to_condition([:'>=', to_string(from)]),
|
||||
@@ -407,7 +407,7 @@ defmodule Mix.Version do
|
||||
end
|
||||
|
||||
defp to_condition([:'>', version | _]) do
|
||||
{ major, minor, patch, pre } = Mix.Version.to_matchable(version)
|
||||
{ major, minor, patch, pre } = Version.to_matchable(version)
|
||||
|
||||
{ :andalso, { :not, { :is_binary, :'$1' } },
|
||||
{ :orelse, { :'>', {{ :'$1', :'$2', :'$3' }},
|
||||
@@ -423,7 +423,7 @@ defmodule Mix.Version do
|
||||
end
|
||||
|
||||
defp to_condition([:'>=', version | _]) do
|
||||
matchable = Mix.Version.to_matchable(version)
|
||||
matchable = Version.to_matchable(version)
|
||||
|
||||
{ :orelse, { :andalso, { :not, { :is_binary, :'$1' } },
|
||||
{ :'==', :'$_', { :const, matchable } } },
|
||||
@@ -431,7 +431,7 @@ defmodule Mix.Version do
|
||||
end
|
||||
|
||||
defp to_condition([:'<', version | _]) do
|
||||
{ major, minor, patch, pre } = Mix.Version.to_matchable(version)
|
||||
{ major, minor, patch, pre } = Version.to_matchable(version)
|
||||
|
||||
{ :andalso, { :not, { :is_binary, :'$1' } },
|
||||
{ :orelse, { :'<', {{ :'$1', :'$2', :'$3' }},
|
||||
@@ -447,7 +447,7 @@ defmodule Mix.Version do
|
||||
end
|
||||
|
||||
defp to_condition([:'<=', version | _]) do
|
||||
matchable = Mix.Version.to_matchable(version)
|
||||
matchable = Version.to_matchable(version)
|
||||
|
||||
{ :orelse, { :andalso, { :not, { :is_binary, :'$1' } },
|
||||
{ :'==', :'$_', { :const, matchable } } },
|
||||
@@ -468,26 +468,26 @@ defmodule Mix.Version do
|
||||
end
|
||||
end
|
||||
|
||||
defimpl String.Chars, for: Mix.Version.Schema do
|
||||
def to_string(Mix.Version.Schema[source: source]) do
|
||||
defimpl String.Chars, for: Version.Schema do
|
||||
def to_string(Version.Schema[source: source]) do
|
||||
source
|
||||
end
|
||||
end
|
||||
|
||||
defimpl Inspect, for: Mix.Version.Schema do
|
||||
defimpl Inspect, for: Version.Schema do
|
||||
def inspect(self, _opts) do
|
||||
"#Mix.Version.Schema<" <> to_string(self) <> ">"
|
||||
"#Version.Schema<" <> to_string(self) <> ">"
|
||||
end
|
||||
end
|
||||
|
||||
defimpl String.Chars, for: Mix.Version.Requirement do
|
||||
defimpl String.Chars, for: Version.Requirement do
|
||||
def to_string({ _, source, _ }) do
|
||||
source
|
||||
end
|
||||
end
|
||||
|
||||
defimpl Inspect, for: Mix.Version.Requirement do
|
||||
defimpl Inspect, for: Version.Requirement do
|
||||
def inspect({ _, source, _ }, _opts) do
|
||||
"#Mix.Version.Requirement<" <> source <> ">"
|
||||
"#Version.Requirement<" <> source <> ">"
|
||||
end
|
||||
end
|
||||
@@ -50,9 +50,9 @@ defmodule String.Unicode do
|
||||
seqs = Enum.map File.stream!(seqs_path), fn(line) ->
|
||||
[ _name, codepoints ] = :binary.split(line, ";", [:global])
|
||||
codepoints = :binary.split(codepoints, " ", [:global])
|
||||
codepoints = Enum.map codepoints, Regex.replace(%r/\s+/, &1, "")
|
||||
codepoints = Enum.map codepoints, &Regex.replace(%r/\s+/, &1, "")
|
||||
codepoints = Enum.filter codepoints, fn(x) -> size(x) > 0 end
|
||||
Enum.map codepoints, to_binary.(&1)
|
||||
Enum.map codepoints, to_binary
|
||||
end
|
||||
|
||||
# Downcase
|
||||
|
||||
@@ -225,6 +225,7 @@ core_main() ->
|
||||
<<"lib/elixir/lib/inspect/algebra.ex">>,
|
||||
<<"lib/elixir/lib/inspect.ex">>,
|
||||
<<"lib/elixir/lib/range.ex">>,
|
||||
<<"lib/elixir/lib/regex.ex">>,
|
||||
<<"lib/elixir/lib/string.ex">>,
|
||||
<<"lib/elixir/lib/string/chars.ex">>,
|
||||
<<"lib/elixir/lib/io.ex">>,
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
lookup_definition/2,
|
||||
delete_definition/2,
|
||||
wrap_definition/6,
|
||||
wrap_definition/8,
|
||||
store_definition/7,
|
||||
store_definition/9,
|
||||
store_each/8,
|
||||
unwrap_stored_definitions/2,
|
||||
format_error/1]).
|
||||
@@ -72,25 +70,17 @@ delete_definition(Module, Tuple) ->
|
||||
%% before evaluating the function body), we would see both definitions.
|
||||
|
||||
wrap_definition(Kind, Meta, Call, Expr, CheckClauses, S) ->
|
||||
do_wrap_definition(Kind, Meta, CheckClauses, [
|
||||
Call, Expr, elixir_scope:serialize(S)
|
||||
Line = ?line(Meta),
|
||||
?wrap_call(Line, ?MODULE, store_definition, [
|
||||
{atom, Line, Kind},
|
||||
{integer, Line, Line},
|
||||
{atom, Line, CheckClauses},
|
||||
{var, Line, '_@MODULE'},
|
||||
Call,
|
||||
Expr,
|
||||
elixir_scope:serialize(S)
|
||||
]).
|
||||
|
||||
wrap_definition(Kind, Meta, Name, Args, Guards, Expr, CheckClauses, S) ->
|
||||
do_wrap_definition(Kind, Meta, CheckClauses, [
|
||||
Name, Args, Guards, Expr, elixir_scope:serialize(S)
|
||||
]).
|
||||
|
||||
do_wrap_definition(Kind, Meta, CheckClauses, Extra) ->
|
||||
Line = ?line(Meta),
|
||||
Invoke =
|
||||
[{atom, Line, Kind},
|
||||
{integer, Line, Line},
|
||||
{atom, Line, CheckClauses},
|
||||
{var, Line, '_@MODULE'}] ++ Extra,
|
||||
|
||||
?wrap_call(Line, ?MODULE, store_definition, Invoke).
|
||||
|
||||
% Invoked by the wrap definition with the function abstract tree.
|
||||
% Each function is then added to the function table.
|
||||
|
||||
@@ -139,10 +129,7 @@ store_definition(Kind, Line, CheckClauses, Module, Name, Args, Guards, Body, #el
|
||||
[store_each(false, Kind, File, Location, Table, CTable, 0,
|
||||
default_function_for(Kind, Name, Default)) || Default <- Defaults],
|
||||
|
||||
{ Name, Arity };
|
||||
|
||||
store_definition(Kind, Line, CheckClauses, Module, Name, Args, Guards, Body, RawS) ->
|
||||
store_definition(Kind, Line, CheckClauses, Module, Name, Args, Guards, Body, elixir_scope:deserialize(RawS)).
|
||||
{ Name, Arity }.
|
||||
|
||||
%% @on_definition
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ warn(Warning) ->
|
||||
elixir_code_server:cast(register_warning),
|
||||
io:put_chars(standard_error, Warning).
|
||||
|
||||
%% Handle inspecting for exceptions
|
||||
%% Handle inspecting for exceptions modules
|
||||
|
||||
inspect(Atom) when is_atom(Atom) ->
|
||||
'Elixir.Inspect.Atom':inspect(Atom).
|
||||
@@ -169,6 +169,10 @@ handle_file_error(File, {Line,erl_lint,{unsafe_var,Var,{In,_Where}}}) ->
|
||||
Message = io_lib:format("cannot define variable ~ts inside ~ts", [format_var(Var), Translated]),
|
||||
raise(Line, File, 'Elixir.CompileError', iolist_to_binary(Message));
|
||||
|
||||
handle_file_error(File, {Line,erl_lint,{spec_fun_undefined,{M,F,A}}}) ->
|
||||
Message = io_lib:format("spec for undefined function ~ts.~ts/~B", [inspect(M), F, A]),
|
||||
raise(Line, File, 'Elixir.CompileError', iolist_to_binary(Message));
|
||||
|
||||
handle_file_error(File, {Line,Module,Desc}) ->
|
||||
form_error(Line, File, Module, Desc).
|
||||
|
||||
@@ -178,18 +182,6 @@ assert_no_function_scope(_Meta, _Kind, #elixir_scope{function=nil}) -> [];
|
||||
assert_no_function_scope(Meta, Kind, S) ->
|
||||
syntax_error(Meta, S#elixir_scope.file, "cannot invoke ~ts inside a function", [Kind]).
|
||||
|
||||
assert_no_match_or_guard_scope(Meta, Kind, S) ->
|
||||
assert_no_match_scope(Meta, Kind, S),
|
||||
assert_no_guard_scope(Meta, Kind, S).
|
||||
|
||||
assert_no_match_scope(Meta, Kind, #elixir_scope{context=match} = S) ->
|
||||
syntax_error(Meta, S#elixir_scope.file, "cannot invoke ~ts inside match clause", [Kind]);
|
||||
assert_no_match_scope(_Meta, _Kind, _S) -> [].
|
||||
|
||||
assert_no_guard_scope(Meta, Kind, #elixir_scope{context=guard} = S) ->
|
||||
syntax_error(Meta, S#elixir_scope.file, "cannot invoke ~ts inside guard", [Kind]);
|
||||
assert_no_guard_scope(_Meta, _Kind, _S) -> [].
|
||||
|
||||
assert_module_scope(Meta, Kind, #elixir_scope{module=nil,file=File}) ->
|
||||
syntax_error(Meta, File, "cannot invoke ~ts outside module", [Kind]);
|
||||
assert_module_scope(_Meta, _Kind, #elixir_scope{module=Module}) -> Module.
|
||||
@@ -198,6 +190,18 @@ assert_function_scope(Meta, Kind, #elixir_scope{function=nil,file=File}) ->
|
||||
syntax_error(Meta, File, "cannot invoke ~ts outside function", [Kind]);
|
||||
assert_function_scope(_Meta, _Kind, #elixir_scope{function=Function}) -> Function.
|
||||
|
||||
assert_no_match_or_guard_scope(Meta, Kind, S) ->
|
||||
assert_no_match_scope(Meta, Kind, S),
|
||||
assert_no_guard_scope(Meta, Kind, S).
|
||||
|
||||
assert_no_match_scope(Meta, _Kind, #elixir_scope{context=match} = S) ->
|
||||
compile_error(Meta, S#elixir_scope.file, "invalid pattern in match clause", []);
|
||||
assert_no_match_scope(_Meta, _Kind, _S) -> [].
|
||||
|
||||
assert_no_guard_scope(Meta, _Kind, #elixir_scope{context=guard} = S) ->
|
||||
compile_error(Meta, S#elixir_scope.file, "invalid pattern in guard", []);
|
||||
assert_no_guard_scope(_Meta, _Kind, _S) -> [].
|
||||
|
||||
%% Helpers
|
||||
|
||||
raise(Meta, File, Kind, Message) when is_list(Meta) ->
|
||||
|
||||
@@ -48,6 +48,9 @@ capture(Meta, { { '.', _, [_, Fun] }, _, Args } = Expr, S) when is_atom(Fun), is
|
||||
capture(Meta, { { '.', _, [_] }, _, Args } = Expr, S) when is_list(Args) ->
|
||||
do_capture(Meta, Expr, S, false);
|
||||
|
||||
capture(Meta, { '__block__', _, [Expr] }, S) ->
|
||||
capture(Meta, Expr, S);
|
||||
|
||||
capture(Meta, { '__block__', _, _ } = Expr, S) ->
|
||||
Message = "invalid args for &, block expressions are not allowed, got: ~ts",
|
||||
syntax_error(Meta, S#elixir_scope.file, Message, ['Elixir.Macro':to_string(Expr)]);
|
||||
|
||||
@@ -189,16 +189,6 @@ translate({Kind, Meta, [Call, Expr]}, S) when ?defs(Kind) ->
|
||||
(not QC#elixir_quote.unquoted) andalso (not QE#elixir_quote.unquoted),
|
||||
{ elixir_def:wrap_definition(Kind, Meta, TCall, TExpr, CheckClauses, SE), SE };
|
||||
|
||||
translate({Kind, Meta, [Name, Args, Guards, Expr]}, S) when ?defs(Kind) ->
|
||||
elixir_errors:deprecation(Meta, S#elixir_scope.file, "~ts/4 is deprecated, please use ~ts/2 instead", [Kind, Kind]),
|
||||
assert_module_scope(Meta, Kind, S),
|
||||
assert_no_function_scope(Meta, Kind, S),
|
||||
{ TName, SN } = translate_each(Name, S),
|
||||
{ TArgs, SA } = translate_each(Args, SN),
|
||||
{ TGuards, SG } = translate_each(Guards, SA),
|
||||
{ TExpr, SE } = translate_each(Expr, SG),
|
||||
{ elixir_def:wrap_definition(Kind, Meta, TName, TArgs, TGuards, TExpr, false, SE), SE };
|
||||
|
||||
%% Apply - Optimize apply by checking what doesn't need to be dispatched dynamically
|
||||
|
||||
translate({ apply, Meta, [Left, Right, Args] }, S) when is_list(Args) ->
|
||||
@@ -319,6 +309,7 @@ expand_module(_Raw, Module, S) ->
|
||||
elixir_aliases:concat([S#elixir_scope.module, Module]).
|
||||
|
||||
is_reserved_data(moduledoc) -> true;
|
||||
is_reserved_data(typedoc) -> true;
|
||||
is_reserved_data(doc) -> true;
|
||||
is_reserved_data(_) -> false.
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ eval_quoted(Module, Quoted, RawBinding, Opts) ->
|
||||
|
||||
scope_for_eval(Module, #elixir_scope{} = S) ->
|
||||
S#elixir_scope{module=Module};
|
||||
|
||||
scope_for_eval(Module, Opts) ->
|
||||
scope_for_eval(Module, elixir:scope_for_eval(Opts)).
|
||||
|
||||
@@ -69,8 +68,9 @@ compile(Line, Module, Block, Vars, #elixir_scope{context_modules=FileModules} =
|
||||
{ Base, Export, Private, Def, Defmacro, Functions } = elixir_def:unwrap_stored_definitions(FileList, Module),
|
||||
|
||||
{ All, Forms0 } = functions_form(Line, File, Module, Base, Export, Def, Defmacro, Functions, C),
|
||||
Forms1 = specs_form(Line, Module, Private, Defmacro, Forms0, C),
|
||||
Forms1 = specs_form(Module, Private, Defmacro, Forms0, C),
|
||||
Forms2 = attributes_form(Line, File, Module, Forms1),
|
||||
Forms3 = typedocs_form(Module, Forms2),
|
||||
|
||||
case ets:lookup(data_table(Module), 'on_load') of
|
||||
[] -> ok;
|
||||
@@ -82,11 +82,12 @@ compile(Line, Module, Block, Vars, #elixir_scope{context_modules=FileModules} =
|
||||
elixir_tracker:ensure_no_function_conflict(Line, File, Module, AllFunctions),
|
||||
elixir_tracker:ensure_all_imports_used(Line, File, Module),
|
||||
elixir_tracker:warn_unused_local(File, Module, Private),
|
||||
warn_unused_docs(Line, File, Module, All),
|
||||
warn_invalid_docs(Line, File, Module, All),
|
||||
warn_unused_docs(Line, File, Module),
|
||||
|
||||
Final = [
|
||||
{ attribute, Line, file, { FileList, Line } },
|
||||
{ attribute, Line, module, Module } | Forms2
|
||||
{ attribute, Line, module, Module } | Forms3
|
||||
],
|
||||
|
||||
Binary = load_form(Line, Final, S),
|
||||
@@ -198,9 +199,21 @@ attributes_form(Line, _File, Module, Current) ->
|
||||
|
||||
ets:foldl(Transform, Current, Table).
|
||||
|
||||
%% Add typedocs to the form
|
||||
typedocs_form(Module, Current) ->
|
||||
Table = docs_table(Module),
|
||||
Transform = fun({ Tuple, Line, Kind, _Sig, Doc }, Acc) ->
|
||||
case Kind of
|
||||
type -> [{ attribute, Line, typedoc, { Tuple, Doc } } | Acc];
|
||||
opaque -> [{ attribute, Line, typedoc, { Tuple, Doc } } | Acc];
|
||||
_ -> Acc
|
||||
end
|
||||
end,
|
||||
ets:foldl(Transform, Current, Table).
|
||||
|
||||
%% Specs
|
||||
|
||||
specs_form(Line, Module, Private, Defmacro, Forms, C) ->
|
||||
specs_form(Module, Private, Defmacro, Forms, C) ->
|
||||
Defmacrop = [Tuple || { Tuple, defmacrop, _, _, _ } <- Private],
|
||||
case elixir_compiler:get_opt(internal, C) of
|
||||
true -> Forms;
|
||||
@@ -212,16 +225,17 @@ specs_form(Line, Module, Private, Defmacro, Forms, C) ->
|
||||
'Elixir.Module':delete_attribute(Module, spec),
|
||||
'Elixir.Module':delete_attribute(Module, callback),
|
||||
|
||||
Temp = specs_attributes(Line, spec, Forms, Specs),
|
||||
specs_attributes(Line, callback, Temp, Callbacks)
|
||||
Temp = specs_attributes(spec, Forms, Specs),
|
||||
specs_attributes(callback, Temp, Callbacks)
|
||||
end.
|
||||
|
||||
specs_attributes(Line, Type, Forms, Specs) ->
|
||||
specs_attributes(Type, Forms, Specs) ->
|
||||
Keys = lists:foldl(fun({ Tuple, Value }, Acc) ->
|
||||
lists:keystore(Tuple, 1, Acc, { Tuple, Value } )
|
||||
lists:keystore(Tuple, 1, Acc, { Tuple, Value })
|
||||
end, [], Specs),
|
||||
lists:foldl(fun({ Tuple, _ }, Acc) ->
|
||||
Values = [V || { K, V } <- Specs, K == Tuple],
|
||||
{ type, Line, _, _ } = hd(Values),
|
||||
[{ attribute, Line, Type, { Tuple, Values } }|Acc]
|
||||
end, Forms, Keys).
|
||||
|
||||
@@ -282,18 +296,31 @@ check_module_availability(Line, File, Module, Compiler) ->
|
||||
[]
|
||||
end.
|
||||
|
||||
warn_unused_docs(_Line, _File, 'Elixir.Kernel', _All) -> ok;
|
||||
warn_unused_docs(_Line, _File, 'Elixir.Kernel.SpecialForms', _All) -> ok;
|
||||
warn_unused_docs(_Line, File, Module, All) ->
|
||||
warn_invalid_docs(_Line, _File, 'Elixir.Kernel', _All) -> ok;
|
||||
warn_invalid_docs(_Line, _File, 'Elixir.Kernel.SpecialForms', _All) -> ok;
|
||||
warn_invalid_docs(_Line, File, Module, All) ->
|
||||
ets:foldl(fun
|
||||
({ Tuple, Line, _, _, _ }, Acc) ->
|
||||
({ _, _, Kind, _, _ }, _) when Kind == type; Kind == opaque ->
|
||||
ok;
|
||||
({ Tuple, Line, _, _, _ }, _) ->
|
||||
case lists:member(Tuple, All) of
|
||||
true -> Acc;
|
||||
false ->
|
||||
elixir_errors:handle_file_warning(File, { Line, ?MODULE, { invalid_doc, Tuple } })
|
||||
elixir_errors:handle_file_warning(File, { Line, ?MODULE, { invalid_doc, Tuple } });
|
||||
true ->
|
||||
ok
|
||||
end
|
||||
end, ok, docs_table(Module)).
|
||||
|
||||
warn_unused_docs(Line, File, Module) ->
|
||||
lists:foreach(fun(Attribute) ->
|
||||
case ets:member(data_table(Module), Attribute) of
|
||||
true ->
|
||||
elixir_errors:handle_file_warning(File, { Line, ?MODULE, { unused_doc, Attribute } });
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
end, [typedoc]).
|
||||
|
||||
% EXTRA FUNCTIONS
|
||||
|
||||
add_info_function(Line, File, Module, Export, Def, Defmacro, C) ->
|
||||
@@ -329,7 +356,10 @@ module_clause(Module) ->
|
||||
{ clause, 0, [{ atom, 0, module }], [], [{ atom, 0, Module }] }.
|
||||
|
||||
docs_clause(Module, true) ->
|
||||
Docs = ordsets:from_list(ets:tab2list(docs_table(Module))),
|
||||
Docs = ordsets:from_list(
|
||||
[{Tuple, Line, Kind, Sig, Doc} ||
|
||||
{Tuple, Line, Kind, Sig, Doc} <- ets:tab2list(docs_table(Module)),
|
||||
Kind =/= type, Kind =/= opaque]),
|
||||
{ clause, 0, [{ atom, 0, docs }], [], [elixir_utils:elixir_to_erl(Docs)] };
|
||||
|
||||
docs_clause(_Module, _) ->
|
||||
@@ -389,6 +419,12 @@ munge_stacktrace(Info, []) ->
|
||||
format_error({ invalid_doc, { Name, Arity } }) ->
|
||||
io_lib:format("docs provided for nonexistent function or macro ~ts/~B", [Name, Arity]);
|
||||
|
||||
format_error({ unused_doc, typedoc }) ->
|
||||
"@typedoc provided but no type follows it";
|
||||
|
||||
format_error({ unused_doc, doc }) ->
|
||||
"@doc provided but no definition follows it";
|
||||
|
||||
format_error({ internal_function_overridden, { Name, Arity } }) ->
|
||||
io_lib:format("function ~ts/~B is internal and should not be overridden", [Name, Arity]);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ handle({ _, Meta, Args } = Original, S, Opt) when is_list(Args), S#elixir_scope.
|
||||
{ Call, Def, SC } when Def /= [] ->
|
||||
Final = validate(Meta, Def, SC),
|
||||
Block = setelement(3, Original, Call),
|
||||
elixir_errors:deprecation(Meta, S#elixir_scope.file, "partial application without capture is deprecated"),
|
||||
elixir_fn:fn(Meta, [{ Final, Meta, Block }], SC);
|
||||
_ -> error
|
||||
end;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
%% Implements Elixir quote.
|
||||
-module(elixir_quote).
|
||||
-export([escape/2, erl_escape/3, erl_quote/4, linify/2, unquote/6, join/5]).
|
||||
-export([escape/2, erl_escape/3, erl_quote/4,
|
||||
linify/2, unquote/4, tail_join/3, join/2]).
|
||||
-include("elixir.hrl").
|
||||
|
||||
%% Apply the line from site call on quoted contents.
|
||||
@@ -24,10 +25,10 @@ do_linify(_, Else) -> Else.
|
||||
|
||||
%% Some expressions cannot be unquoted at compilation time.
|
||||
%% This function is responsible for doing runtime unquoting.
|
||||
unquote(_File, _Line, Meta, Left, { '__aliases__', _, Args }, nil) ->
|
||||
unquote(Meta, Left, { '__aliases__', _, Args }, nil) ->
|
||||
{ '__aliases__', Meta, [Left|Args] };
|
||||
|
||||
unquote(_File, _Line, Meta, Left, Right, nil) when is_atom(Right) ->
|
||||
unquote(Meta, Left, Right, nil) when is_atom(Right) ->
|
||||
case atom_to_list(Right) of
|
||||
"Elixir." ++ _ ->
|
||||
{ '__aliases__', Meta, [Left, Right] };
|
||||
@@ -35,33 +36,48 @@ unquote(_File, _Line, Meta, Left, Right, nil) when is_atom(Right) ->
|
||||
{ { '.', Meta, [Left, Right] }, Meta, [] }
|
||||
end;
|
||||
|
||||
unquote(_File, _Line, Meta, Left, { Right, _, Context }, nil) when is_atom(Right), is_atom(Context) ->
|
||||
unquote(Meta, Left, { Right, _, Context }, nil) when is_atom(Right), is_atom(Context) ->
|
||||
{ { '.', Meta, [Left, Right] }, Meta, [] };
|
||||
|
||||
unquote(_File, _Line, Meta, Left, { Right, _, Args }, nil) when is_atom(Right) ->
|
||||
unquote(Meta, Left, { Right, _, Args }, nil) when is_atom(Right) ->
|
||||
{ { '.', Meta, [Left, Right] }, Meta, Args };
|
||||
|
||||
unquote(File, Line, _Meta, _Left, Right, nil) ->
|
||||
elixir_errors:syntax_error(Line, File, "expected unquote after dot to return an atom, "
|
||||
"an alias or a quoted call, got: ~ts", ['Elixir.Macro':to_string(Right)]);
|
||||
unquote(_Meta, _Left, Right, nil) ->
|
||||
argument_error(<<"expected unquote after dot to return an atom, an alias or a quoted call, got: ",
|
||||
('Elixir.Macro':to_string(Right))/binary>>);
|
||||
|
||||
unquote(_File, _Line, Meta, Left, Right, Args) when is_atom(Right) ->
|
||||
unquote(Meta, Left, Right, Args) when is_atom(Right) ->
|
||||
{ { '.', Meta, [Left, Right] }, Meta, Args };
|
||||
|
||||
unquote(_File, _Line, Meta, Left, { Right, _, Context }, Args) when is_atom(Right), is_atom(Context) ->
|
||||
unquote(Meta, Left, { Right, _, Context }, Args) when is_atom(Right), is_atom(Context) ->
|
||||
{ { '.', Meta, [Left, Right] }, Meta, Args };
|
||||
|
||||
unquote(File, Line, _Meta, _Left, Right, _Args) ->
|
||||
elixir_errors:syntax_error(Line, File, "expected unquote after dot with args to return an atom "
|
||||
"or a quoted call, got: ~ts", ['Elixir.Macro':to_string(Right)]).
|
||||
unquote(_Meta, _Left, Right, _Args) ->
|
||||
argument_error(<<"expected unquote after dot with args to return an atom or a quoted call, got: ",
|
||||
('Elixir.Macro':to_string(Right))/binary>>).
|
||||
|
||||
join(_File, _Line, Left, Right, Rest) when is_list(Left), is_list(Right), is_list(Rest) ->
|
||||
Rest ++ Left ++ Right;
|
||||
join(Left, Right) when is_list(Right) ->
|
||||
validate_join(Left),
|
||||
Left ++ Right.
|
||||
|
||||
join(_File, _Line, Left, Right, Rest) ->
|
||||
[H|T] = lists:reverse(Rest ++ Left),
|
||||
tail_join(Left, Right, Tail) when is_list(Right), is_list(Tail) ->
|
||||
validate_join(Left),
|
||||
Tail ++ Left ++ Right;
|
||||
|
||||
tail_join(Left, Right, Tail) when is_list(Left) ->
|
||||
validate_join(Left),
|
||||
[H|T] = lists:reverse(Tail ++ Left),
|
||||
lists:reverse([{ '|', [], [H, Right] }|T]).
|
||||
|
||||
validate_join(List) when is_list(List) ->
|
||||
ok;
|
||||
validate_join(List) when not is_list(List) ->
|
||||
argument_error(<<"expected a list with quoted expressions in unquote_splicing/1, got: ",
|
||||
('Elixir.Kernel':inspect(List))/binary>>).
|
||||
|
||||
argument_error(Message) ->
|
||||
error('Elixir.ArgumentError':exception([{message,Message}])).
|
||||
|
||||
%% Escapes the given expression. It is similar to quote, but
|
||||
%% lines are kept and hygiene mechanisms are disabled.
|
||||
escape(Expr, Unquote) ->
|
||||
@@ -214,9 +230,9 @@ do_quote(Other, Q, _) ->
|
||||
%% Quote helpers
|
||||
|
||||
do_quote_call(Left, Meta, Expr, Args, Q, S) ->
|
||||
All = [?line(Meta), meta(Meta, Q), Left, { unquote, Meta, [Expr] }, Args],
|
||||
All = [meta(Meta, Q), Left, { unquote, Meta, [Expr] }, Args],
|
||||
{ TAll, TQ } = lists:mapfoldl(fun(X, Acc) -> do_quote(X, Acc, S) end, Q, All),
|
||||
{ { { '.', Meta, [elixir_quote, unquote] }, Meta, [{ '__FILE__', [], nil }|TAll] }, TQ }.
|
||||
{ { { '.', Meta, [elixir_quote, unquote] }, Meta, TAll }, TQ }.
|
||||
|
||||
do_quote_fa(Target, Meta, Args, F, A, Q, S) ->
|
||||
NewMeta =
|
||||
@@ -264,24 +280,24 @@ do_splice([{ '|', Meta, [{ unquote_splicing, _, [Left] }, Right] }|T], #elixir_q
|
||||
%% 1, 2 and 3, which could even be unquotes.
|
||||
{ TT, QT } = do_splice(T, Q, S, [], []),
|
||||
{ TR, QR } = do_quote(Right, QT, S),
|
||||
|
||||
%% Do the joining at runtime when we are aware of the values.
|
||||
Args = [{ '__FILE__', [], nil }, ?line(Meta), Left, TR, TT],
|
||||
{ { { '.', Meta, [elixir_quote, join] }, Meta, Args }, QR#elixir_quote{unquoted=true} };
|
||||
{ do_runtime_join(Meta, tail_join, [Left, TR, TT]), QR#elixir_quote{unquoted=true} };
|
||||
|
||||
do_splice(List, Q, S) ->
|
||||
do_splice(List, Q, S, [], []).
|
||||
|
||||
do_splice([{ unquote_splicing, _, [Expr] }|T], #elixir_quote{unquote=true} = Q, S, Buffer, Acc) ->
|
||||
do_splice(T, Q#elixir_quote{unquoted=true}, S, [], do_splice_join(do_splice_join(Expr, Buffer), Acc));
|
||||
do_splice([{ unquote_splicing, Meta, [Expr] }|T], #elixir_quote{unquote=true} = Q, S, Buffer, Acc) ->
|
||||
do_splice(T, Q#elixir_quote{unquoted=true}, S, [], do_runtime_join(Meta, join, [Expr, do_join(Buffer, Acc)]));
|
||||
|
||||
do_splice([H|T], Q, S, Buffer, Acc) ->
|
||||
{ TH, TQ } = do_quote(H, Q, S),
|
||||
do_splice(T, TQ, S, [TH|Buffer], Acc);
|
||||
|
||||
do_splice([], Q, _S, Buffer, Acc) ->
|
||||
{ do_splice_join(Buffer, Acc), Q }.
|
||||
{ do_join(Buffer, Acc), Q }.
|
||||
|
||||
do_splice_join(Left, []) -> Left;
|
||||
do_splice_join([], Right) -> Right;
|
||||
do_splice_join(Left, Right) -> { { '.', [], ['Elixir.Kernel', '++'] }, [], [Left, Right] }.
|
||||
do_join(Left, []) -> Left;
|
||||
do_join([], Right) -> Right;
|
||||
do_join(Left, Right) -> { { '.', [], ['Elixir.Kernel', '++'] }, [], [Left, Right] }.
|
||||
|
||||
do_runtime_join(Meta, Fun, Args) ->
|
||||
{ { '.', Meta, [elixir_quote, Fun] }, Meta, Args }.
|
||||
|
||||
@@ -81,24 +81,21 @@ build_ex_var(Line, Key, Name, S) when is_integer(Line) ->
|
||||
% Handle Macro.Env conversion
|
||||
|
||||
to_erl_env({ 'Elixir.Macro.Env', Module, File, _Line, Function, Aliases, Context,
|
||||
Requires, Functions, Macros, ContextModules, MacroAliases, Vars }) ->
|
||||
Requires, Functions, Macros, ContextModules, MacroAliases, _Vars }) ->
|
||||
#elixir_scope{module=Module,file=File,
|
||||
function=Function,aliases=Aliases,context=Context,
|
||||
requires=Requires,macros=Macros,functions=Functions,
|
||||
context_modules=ContextModules,macro_aliases=MacroAliases,
|
||||
list_vars=Vars}.
|
||||
context_modules=ContextModules,macro_aliases=MacroAliases}.
|
||||
|
||||
to_ex_env({ Line, #elixir_scope{module=Module,file=File,
|
||||
function=Function,aliases=Aliases,context=Context,
|
||||
requires=Requires,macros=Macros,functions=Functions,
|
||||
context_modules=ContextModules,macro_aliases=MacroAliases,
|
||||
vars=Vars,list_vars=ListVars} }) when is_integer(Line) ->
|
||||
context_modules=ContextModules,macro_aliases=MacroAliases, vars=Vars} }) when is_integer(Line) ->
|
||||
{ 'Elixir.Macro.Env', Module, File, Line, Function, Aliases,
|
||||
Context, Requires, Functions, Macros, ContextModules, MacroAliases,
|
||||
list_vars(ListVars, Vars) }.
|
||||
list_vars(Vars) }.
|
||||
|
||||
list_vars(nil, Vars) -> [K || { K, _ } <- Vars];
|
||||
list_vars(Other, _) -> Other.
|
||||
list_vars(Vars) -> [K || { K, _ } <- Vars].
|
||||
|
||||
% Provides a tuple with only the scope information we want to serialize.
|
||||
|
||||
|
||||
@@ -658,6 +658,9 @@ remove_heredoc_spaces([], Buffer, _Spaces, _Original) ->
|
||||
%% the remaining of the document and the number of spaces the heredoc
|
||||
%% is aligned.
|
||||
|
||||
extract_heredoc_body(_Line, Marker, [{line,NewLine}|Rest], Buffer) ->
|
||||
extract_heredoc_body(NewLine, Marker, Rest, Buffer);
|
||||
|
||||
extract_heredoc_body(Line, Marker, Rest, Buffer) ->
|
||||
case extract_heredoc_line(Marker, Rest, Buffer, 0) of
|
||||
{ ok, NewBuffer, NewRest } ->
|
||||
|
||||
@@ -397,7 +397,9 @@ translate_each({ Atom, Meta, Args } = Original, S) when is_atom(Atom) ->
|
||||
elixir_dispatch:dispatch_import(Meta, Atom, Args, S, Callback);
|
||||
Else -> Else
|
||||
end;
|
||||
Else -> Else
|
||||
Else ->
|
||||
elixir_errors:deprecation(Meta, S#elixir_scope.file, "partial application without capture is deprecated"),
|
||||
Else
|
||||
end;
|
||||
|
||||
%% Remote calls
|
||||
@@ -437,7 +439,9 @@ translate_each({ { '.', _, [Left, Right] }, Meta, Args } = Original, S) when is_
|
||||
end;
|
||||
Else -> Else
|
||||
end;
|
||||
Else -> Else
|
||||
Else ->
|
||||
elixir_errors:deprecation(Meta, S#elixir_scope.file, "partial application without capture is deprecated"),
|
||||
Else
|
||||
end;
|
||||
|
||||
%% Anonymous function calls
|
||||
|
||||
@@ -214,6 +214,10 @@ defmodule DictTest.Common do
|
||||
|
||||
dict2 = Dict.put(dict2, :a, 3)
|
||||
refute Dict.equal?(dict1, dict2)
|
||||
|
||||
dict3 = HashDict.new(a: 2, b: 3, f: 5, c: 123, z: 666)
|
||||
refute Dict.equal?(dict1, dict3)
|
||||
refute Dict.equal?(dict3, dict1)
|
||||
end
|
||||
|
||||
test "unsupported dict" do
|
||||
|
||||
@@ -152,14 +152,14 @@ defmodule EnumTest.List do
|
||||
end
|
||||
|
||||
test :filter_with_match do
|
||||
assert Enum.filter([1, 2, 3], match?(1, &1)) == [1]
|
||||
assert Enum.filter([1, 2, 3], match?(x when x < 3, &1)) == [1, 2]
|
||||
assert Enum.filter([1, 2, 3], match?(_, &1)) == [1, 2, 3]
|
||||
assert Enum.filter([1, 2, 3], &match?(1, &1)) == [1]
|
||||
assert Enum.filter([1, 2, 3], &match?(x when x < 3, &1)) == [1, 2]
|
||||
assert Enum.filter([1, 2, 3], &match?(_, &1)) == [1, 2, 3]
|
||||
end
|
||||
|
||||
test :filter_map do
|
||||
assert Enum.filter_map([1, 2, 3], fn(x) -> rem(x, 2) == 0 end, &1 * 2) == [4]
|
||||
assert Enum.filter_map([2, 4, 6], fn(x) -> rem(x, 2) == 0 end, &1 * 2) == [4, 8, 12]
|
||||
assert Enum.filter_map([1, 2, 3], fn(x) -> rem(x, 2) == 0 end, &(&1 * 2)) == [4]
|
||||
assert Enum.filter_map([2, 4, 6], fn(x) -> rem(x, 2) == 0 end, &(&1 * 2)) == [4, 8, 12]
|
||||
end
|
||||
|
||||
test :flat_map do
|
||||
@@ -185,15 +185,15 @@ defmodule EnumTest.List do
|
||||
end
|
||||
|
||||
test :map_join do
|
||||
assert Enum.map_join([], " = ", &1 * 2) == ""
|
||||
assert Enum.map_join([1, 2, 3], " = ", &1 * 2) == "2 = 4 = 6"
|
||||
assert Enum.map_join([1, 2, 3], &1 * 2) == "246"
|
||||
assert Enum.map_join([], " = ", &(&1 * 2)) == ""
|
||||
assert Enum.map_join([1, 2, 3], " = ", &(&1 * 2)) == "2 = 4 = 6"
|
||||
assert Enum.map_join([1, 2, 3], &(&1 * 2)) == "246"
|
||||
end
|
||||
|
||||
test :join_empty do
|
||||
fun = fn (acc, _) -> acc end
|
||||
assert Enum.join(fun, ".") == ""
|
||||
assert Enum.map_join(fun, ".", &1 + 0) == ""
|
||||
assert Enum.map_join(fun, ".", &(&1 + 0)) == ""
|
||||
end
|
||||
|
||||
test :map do
|
||||
@@ -229,7 +229,7 @@ defmodule EnumTest.List do
|
||||
|
||||
test :sort do
|
||||
assert Enum.sort([5, 3, 2, 4, 1]) == [1, 2, 3, 4, 5]
|
||||
assert Enum.sort([5, 3, 2, 4, 1], &1 > &2) == [5, 4, 3, 2, 1]
|
||||
assert Enum.sort([5, 3, 2, 4, 1], &(&1 > &2)) == [5, 4, 3, 2, 1]
|
||||
end
|
||||
|
||||
test :split do
|
||||
@@ -264,6 +264,13 @@ defmodule EnumTest.List do
|
||||
assert Enum.take([], 3) == []
|
||||
end
|
||||
|
||||
test :take_every do
|
||||
assert Enum.take_every([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 2) == [1, 3, 5, 7, 9]
|
||||
assert Enum.take_every([], 2) == []
|
||||
assert Enum.take_every([1, 2], 2) == [1]
|
||||
assert Enum.take_every([1, 2, 3], 0) == []
|
||||
end
|
||||
|
||||
test :take_while do
|
||||
assert Enum.take_while([1, 2, 3], fn(x) -> x > 3 end) == []
|
||||
assert Enum.take_while([1, 2, 3], fn(x) -> x <= 1 end) == [1]
|
||||
@@ -377,10 +384,10 @@ defmodule EnumTest.Range do
|
||||
|
||||
test :any? do
|
||||
range = Range.new(first: 0, last: 5)
|
||||
refute Enum.any?(range, &1 > 10)
|
||||
refute Enum.any?(range, &(&1 > 10))
|
||||
|
||||
range = Range.new(first: 0, last: 5)
|
||||
assert Enum.any?(range, &1 > 3)
|
||||
assert Enum.any?(range, &(&1 > 3))
|
||||
|
||||
range = Range.new(first: 1, last: 0)
|
||||
assert Enum.any?(range)
|
||||
@@ -510,17 +517,17 @@ defmodule EnumTest.Range do
|
||||
|
||||
test :filter_with_match do
|
||||
range = Range.new(first: 1, last: 3)
|
||||
assert Enum.filter(range, match?(1, &1)) == [1]
|
||||
assert Enum.filter(range, match?(x when x < 3, &1)) == [1, 2]
|
||||
assert Enum.filter(range, match?(_, &1)) == [1, 2, 3]
|
||||
assert Enum.filter(range, &match?(1, &1)) == [1]
|
||||
assert Enum.filter(range, &match?(x when x < 3, &1)) == [1, 2]
|
||||
assert Enum.filter(range, &match?(_, &1)) == [1, 2, 3]
|
||||
end
|
||||
|
||||
test :filter_map do
|
||||
range = Range.new(first: 1, last: 3)
|
||||
assert Enum.filter_map(range, fn(x) -> rem(x, 2) == 0 end, &1 * 2) == [4]
|
||||
assert Enum.filter_map(range, fn(x) -> rem(x, 2) == 0 end, &(&1 * 2)) == [4]
|
||||
|
||||
range = Range.new(first: 2, last: 6)
|
||||
assert Enum.filter_map(range, fn(x) -> rem(x, 2) == 0 end, &1 * 2) == [4, 8, 12]
|
||||
assert Enum.filter_map(range, fn(x) -> rem(x, 2) == 0 end, &(&1 * 2)) == [4, 8, 12]
|
||||
end
|
||||
|
||||
test :flat_map do
|
||||
@@ -558,11 +565,11 @@ defmodule EnumTest.Range do
|
||||
|
||||
test :map_join do
|
||||
range = Range.new(first: 1, last: 0)
|
||||
assert Enum.map_join(range, " = ", &1 * 2) == "2 = 0"
|
||||
assert Enum.map_join(range, " = ", &(&1 * 2)) == "2 = 0"
|
||||
|
||||
range = Range.new(first: 1, last: 3)
|
||||
assert Enum.map_join(range, " = ", &1 * 2) == "2 = 4 = 6"
|
||||
assert Enum.map_join(range, &1 * 2) == "246"
|
||||
assert Enum.map_join(range, " = ", &(&1 * 2)) == "2 = 4 = 6"
|
||||
assert Enum.map_join(range, &(&1 * 2)) == "246"
|
||||
end
|
||||
|
||||
test :map do
|
||||
@@ -597,9 +604,9 @@ defmodule EnumTest.Range do
|
||||
assert Enum.sort(Range.new(first: 2, last: 1)) == [1, 2]
|
||||
assert Enum.sort(Range.new(first: 1, last: 1)) == [1]
|
||||
|
||||
assert Enum.sort(Range.new(first: 3, last: 1), &1 > &2) == [3, 2, 1]
|
||||
assert Enum.sort(Range.new(first: 2, last: 1), &1 > &2) == [2, 1]
|
||||
assert Enum.sort(Range.new(first: 1, last: 1), &1 > &2) == [1]
|
||||
assert Enum.sort(Range.new(first: 3, last: 1), &(&1 > &2)) == [3, 2, 1]
|
||||
assert Enum.sort(Range.new(first: 2, last: 1), &(&1 > &2)) == [2, 1]
|
||||
assert Enum.sort(Range.new(first: 1, last: 1), &(&1 > &2)) == [1]
|
||||
end
|
||||
|
||||
test :split do
|
||||
@@ -642,6 +649,12 @@ defmodule EnumTest.Range do
|
||||
assert Enum.take(range, 3) == [1, 0]
|
||||
end
|
||||
|
||||
test :take_every do
|
||||
assert Enum.take_every(1..10, 2) == [1, 3, 5, 7, 9]
|
||||
assert Enum.take_every(1..2, 2) == [1]
|
||||
assert Enum.take_every(1..3, 0) == []
|
||||
end
|
||||
|
||||
test :take_while do
|
||||
range = Range.new(first: 1, last: 3)
|
||||
assert Enum.take_while(range, fn(x) -> x > 3 end) == []
|
||||
|
||||
@@ -84,6 +84,19 @@ defmodule Kernel.ExceptionTest do
|
||||
assert ErlangError.new(original: :sample).message == "erlang error: :sample"
|
||||
end
|
||||
|
||||
test :raise_preserves_the_stacktrace do
|
||||
stacktrace =
|
||||
try do
|
||||
raise "a"
|
||||
rescue _ ->
|
||||
[top|_] = System.stacktrace
|
||||
top
|
||||
end
|
||||
file = to_char_list(__FILE__)
|
||||
assert {Kernel.ExceptionTest, :test_raise_preserves_the_stacktrace, _,
|
||||
[file: ^file, line: 90]} = stacktrace # line #90 is sensitive
|
||||
end
|
||||
|
||||
defp empty_tuple, do: {}
|
||||
defp a_tuple, do: { :foo, :bar, :baz }
|
||||
defp a_list, do: [ :foo, :bar, :baz ]
|
||||
|
||||
@@ -550,7 +550,7 @@ defmodule FileTest do
|
||||
|
||||
test :open_a_file_with_function do
|
||||
file = fixture_path("file.txt")
|
||||
assert File.open(file, IO.read(&1, :line)) == { :ok, "FOO\n" }
|
||||
assert File.open(file, &IO.read(&1, :line)) == { :ok, "FOO\n" }
|
||||
end
|
||||
|
||||
test :open_a_missing_file! do
|
||||
@@ -562,7 +562,7 @@ defmodule FileTest do
|
||||
|
||||
test :open_a_file_with_function! do
|
||||
file = fixture_path("file.txt")
|
||||
assert File.open!(file, IO.read(&1, :line)) == "FOO\n"
|
||||
assert File.open!(file, &IO.read(&1, :line)) == "FOO\n"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -85,31 +85,31 @@ defmodule HashDictTest do
|
||||
test :update do
|
||||
dict = filled_dict(8)
|
||||
|
||||
dict = HashDict.update!(dict, 1, &1 * 2)
|
||||
dict = HashDict.update!(dict, 1, &(&1 * 2))
|
||||
assert HashDict.get(dict, 1) == 2
|
||||
assert HashDict.size(dict) == 8
|
||||
|
||||
assert_raise KeyError, fn ->
|
||||
HashDict.update!(dict, 11, &1 * 2)
|
||||
HashDict.update!(dict, 11, &(&1 * 2))
|
||||
end
|
||||
|
||||
dict = HashDict.update(dict, 3, 3, &1 * 2)
|
||||
dict = HashDict.update(dict, 3, 3, &(&1 * 2))
|
||||
assert HashDict.get(dict, 3) == 6
|
||||
assert HashDict.size(dict) == 8
|
||||
|
||||
dict = HashDict.update(dict, 11, 13, &1 * 2)
|
||||
dict = HashDict.update(dict, 11, 13, &(&1 * 2))
|
||||
assert HashDict.get(dict, 11) == 13
|
||||
assert HashDict.size(dict) == 9
|
||||
|
||||
assert_raise KeyError, fn->
|
||||
HashDict.update!(dict, 11.0, &1 * 2)
|
||||
HashDict.update!(dict, 11.0, &(&1 * 2))
|
||||
end
|
||||
|
||||
dict = HashDict.update(dict, 11.0, 15, &1 * 2)
|
||||
dict = HashDict.update(dict, 11.0, 15, &(&1 * 2))
|
||||
assert HashDict.get(dict, 11.0) == 15
|
||||
assert HashDict.size(dict) == 10
|
||||
|
||||
dict = HashDict.update(dict, 11.0, 15, &1 * 2)
|
||||
dict = HashDict.update(dict, 11.0, 15, &(&1 * 2))
|
||||
assert HashDict.get(dict, 11.0) == 30
|
||||
assert HashDict.get(dict, 11) == 13
|
||||
assert HashDict.size(dict) == 10
|
||||
@@ -302,6 +302,6 @@ defmodule HashDictTest do
|
||||
end
|
||||
|
||||
defp filled_dict(range) do
|
||||
Enum.reduce 1..range, HashDict.new, HashDict.put(&2, &1, &1)
|
||||
Enum.reduce 1..range, HashDict.new, &HashDict.put(&2, &1, &1)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -110,7 +110,7 @@ defmodule HashSetTest do
|
||||
end
|
||||
|
||||
defp filled_set(range) do
|
||||
Enum.reduce 1..range, HashSet.new, HashSet.put(&2, &1)
|
||||
Enum.reduce 1..range, HashSet.new, &HashSet.put(&2, &1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -101,9 +101,9 @@ bar
|
||||
end
|
||||
|
||||
test :partial_application do
|
||||
assert (<< &1, 2 >>).(1) == << 1, 2 >>
|
||||
assert (<< &1, &2 >>).(1, 2) == << 1, 2 >>
|
||||
assert (<< &2, &1 >>).(2, 1) == << 1, 2 >>
|
||||
assert (&<< &1, 2 >>).(1) == << 1, 2 >>
|
||||
assert (&<< &1, &2 >>).(1, 2) == << 1, 2 >>
|
||||
assert (&<< &2, &1 >>).(2, 1) == << 1, 2 >>
|
||||
end
|
||||
|
||||
test :literal do
|
||||
|
||||
@@ -45,12 +45,6 @@ defmodule Kernel.ErrorsTest do
|
||||
'"""\nbar'
|
||||
end
|
||||
|
||||
test :invalid_partial do
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: partial variable &2 cannot be defined without &1",
|
||||
'&2 + 3'
|
||||
end
|
||||
|
||||
test :unexpected_end do
|
||||
assert_compile_fail SyntaxError,
|
||||
"nofile:1: unexpected token: end",
|
||||
@@ -119,6 +113,16 @@ defmodule Kernel.ErrorsTest do
|
||||
'''
|
||||
end
|
||||
|
||||
test :invalid_match_pattern do
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:2: invalid pattern in match clause",
|
||||
'''
|
||||
case true do
|
||||
true && true -> true
|
||||
end
|
||||
'''
|
||||
end
|
||||
|
||||
test :different_defs_with_defaults do
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:3: def hello/3 defaults conflicts with def hello/2",
|
||||
@@ -571,6 +575,24 @@ defmodule Kernel.ErrorsTest do
|
||||
'case [] do; [] when Hello.something_that_does_not_exist == [] -> :ok; end'
|
||||
end
|
||||
|
||||
test :typespec_errors do
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:2: type foo() undefined",
|
||||
'''
|
||||
defmodule Example do
|
||||
@type omg :: foo
|
||||
end
|
||||
'''
|
||||
|
||||
assert_compile_fail CompileError,
|
||||
"nofile:2: spec for undefined function Example.omg/0",
|
||||
'''
|
||||
defmodule Example do
|
||||
@spec omg :: atom
|
||||
end
|
||||
'''
|
||||
end
|
||||
|
||||
test :macros_error_stacktrace do
|
||||
assert [{:erlang, :+, [1, :foo], _}, {ErrorsTest, :sample, 1, _}|_] =
|
||||
rescue_stacktrace(""")
|
||||
|
||||
@@ -83,6 +83,10 @@ defmodule Kernel.FnTest do
|
||||
assert (&(&1.file(&2))).(__ENV__, "Hello").file == "Hello"
|
||||
end
|
||||
|
||||
test "capture block like" do
|
||||
assert (&(!is_atom(&1))).(:foo) == false
|
||||
end
|
||||
|
||||
test "capture other" do
|
||||
assert (& &1).(:ok) == :ok
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
Code.require_file "../test_helper.exs", __DIR__
|
||||
|
||||
defmodule Kernel.PartialApplicationTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
test :partial_with_simple_call_and_one_item do
|
||||
fun = minus(10, &1)
|
||||
assert fun.(5) == 5
|
||||
assert fun.(3) == 7
|
||||
end
|
||||
|
||||
test :partial_with_simple_call_and_all_items do
|
||||
fun = minus(&1, &2)
|
||||
assert fun.(10, 5) == 5
|
||||
assert fun.(13, 6) == 7
|
||||
end
|
||||
|
||||
test :partial_with_funcall_and_one_item do
|
||||
fun = minus(&1, &2)
|
||||
fun = fun.(10, &1)
|
||||
assert fun.(5) == 5
|
||||
assert fun.(3) == 7
|
||||
end
|
||||
|
||||
test :partial_with_funcall_and_all_items do
|
||||
fun = minus(&1, &2)
|
||||
fun = fun.(&1, &2)
|
||||
assert fun.(10, 5) == 5
|
||||
assert fun.(13, 6) == 7
|
||||
end
|
||||
|
||||
test :partial_with_opposite_order do
|
||||
fun = minus(&2, &1)
|
||||
assert fun.(10, 5) == -5
|
||||
assert fun.(13, 6) == -7
|
||||
end
|
||||
|
||||
test :partial_with_duplicated do
|
||||
fun = minus(&1, &1)
|
||||
assert fun.(10) == 0
|
||||
assert fun.(13) == 0
|
||||
end
|
||||
|
||||
test :partial_with_internal_macro do
|
||||
fun = if(&1, do: 1, else: 2)
|
||||
assert fun.(20) == 1
|
||||
assert fun.(nil) == 2
|
||||
end
|
||||
|
||||
test :partial_application_for_module_calls do
|
||||
fun = Enum.reverse(&1)
|
||||
assert fun.([1, 2, 3]) == [3, 2, 1]
|
||||
end
|
||||
|
||||
test :partial_application_for_operator do
|
||||
assert Enum.map([1, 2, 3], &1 * 2) == [2, 4, 6]
|
||||
end
|
||||
|
||||
defp minus(x, y) do
|
||||
x - y
|
||||
end
|
||||
end
|
||||
@@ -59,7 +59,7 @@ defmodule Kernel.QuoteTest do
|
||||
assert Code.eval_quoted(quote(do: Foo.unquote(Bar))) == { Elixir.Foo.Bar, [] }
|
||||
assert Code.eval_quoted(quote(do: Foo.unquote(quote do: Bar))) == { Elixir.Foo.Bar, [] }
|
||||
|
||||
assert_raise SyntaxError, fn ->
|
||||
assert_raise ArgumentError, fn ->
|
||||
quote(do: foo.unquote(1))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -249,7 +249,7 @@ defmodule Kernel.RecordRewriterTest do
|
||||
|
||||
test "updater call is rewriten" do
|
||||
{ clause, rewriten } =
|
||||
{ clause(fn(x = Range[]) -> x.update_first(&1 + 1) end), clause(fn(x = Range[]) -> Range.update_first(&1 + 1, x) end) }
|
||||
{ clause(fn(x = Range[]) -> x.update_first(&(&1 + 1)) end), clause(fn(x = Range[]) -> Range.update_first(&(&1 + 1), x) end) }
|
||||
assert optimize_clause(clause) == { rewriten, [x: Range], { Range, nil } }
|
||||
end
|
||||
|
||||
|
||||
@@ -402,8 +402,35 @@ defmodule Kernel.WarningTest do
|
||||
purge [Sample]
|
||||
end
|
||||
|
||||
test :typedoc_on_typep do
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample do
|
||||
@typedoc "Something"
|
||||
@typep priv :: any
|
||||
@spec foo() :: priv
|
||||
def foo(), do: nil
|
||||
end
|
||||
"""
|
||||
end) =~ "nofile:3: type priv/0 is private, @typedoc's are always discarded for private types"
|
||||
after
|
||||
purge [Sample]
|
||||
end
|
||||
|
||||
test :typedoc_with_no_type do
|
||||
assert capture_err(fn ->
|
||||
Code.eval_string """
|
||||
defmodule Sample do
|
||||
@typedoc "Something"
|
||||
end
|
||||
"""
|
||||
end) =~ "nofile:1: @typedoc provided but no type follows it"
|
||||
after
|
||||
purge [Sample]
|
||||
end
|
||||
|
||||
defp purge(list) when is_list(list) do
|
||||
Enum.each list, purge(&1)
|
||||
Enum.each list, &purge/1
|
||||
end
|
||||
|
||||
defp purge(module) when is_atom(module) do
|
||||
|
||||
@@ -308,7 +308,7 @@ defmodule KernelTest do
|
||||
end
|
||||
|
||||
test "nested pipelines" do
|
||||
assert [1, [2], 3] |> List.flatten |> Enum.map(&1 * 2) == [2, 4, 6]
|
||||
assert [1, [2], 3] |> List.flatten |> Enum.map(&(&1 * 2)) == [2, 4, 6]
|
||||
end
|
||||
|
||||
test "local call" do
|
||||
@@ -316,12 +316,12 @@ defmodule KernelTest do
|
||||
end
|
||||
|
||||
test "pipeline with capture" do
|
||||
assert Enum.map([1, 2, 3], &1 |> twice |> twice) == [4, 8, 12]
|
||||
assert Enum.map([1, 2, 3], &(&1 |> twice |> twice)) == [4, 8, 12]
|
||||
end
|
||||
|
||||
test "non-call" do
|
||||
assert 1 |> (&1*2).() == 2
|
||||
assert [1] |> hd(&1).() == 1
|
||||
assert 1 |> (&(&1*2)).() == 2
|
||||
assert [1] |> (&hd(&1)).() == 1
|
||||
|
||||
import CompileAssertion
|
||||
assert_compile_fail ArgumentError, "unsupported expression in pipeline |> operator: 2", "1 |> 2"
|
||||
@@ -330,7 +330,7 @@ defmodule KernelTest do
|
||||
defp twice(a), do: a * 2
|
||||
|
||||
defp local(list) do
|
||||
Enum.map(list, &1 * 2)
|
||||
Enum.map(list, &(&1 * 2))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -111,15 +111,15 @@ defmodule KeywordTest do
|
||||
end
|
||||
|
||||
test :update do
|
||||
assert Keyword.update!([a: 1], :a, &1 * 2) == [a: 2]
|
||||
assert Keyword.update!([a: 1], :a, &(&1 * 2)) == [a: 2]
|
||||
assert_raise KeyError, fn ->
|
||||
assert Keyword.update!([a: 1], :b, &1 * 2)
|
||||
assert Keyword.update!([a: 1], :b, &(&1 * 2))
|
||||
end
|
||||
end
|
||||
|
||||
test :update_with_initial do
|
||||
assert Keyword.update([a: 1], :a, 13, &1 * 2) == [a: 2]
|
||||
assert Keyword.update([a: 1], :b, 11, &1 * 2) == [a: 1, b: 11]
|
||||
assert Keyword.update([a: 1], :a, 13, &(&1 * 2)) == [a: 2]
|
||||
assert Keyword.update([a: 1], :b, 11, &(&1 * 2)) == [a: 1, b: 11]
|
||||
end
|
||||
|
||||
defp create_empty_keywords, do: []
|
||||
|
||||
@@ -13,11 +13,11 @@ defmodule ListTest do
|
||||
end
|
||||
|
||||
test :partial_application do
|
||||
assert ([&1, 2]).(1) == [1, 2]
|
||||
assert ([&1, &2]).(1, 2) == [1, 2]
|
||||
assert ([&2, &1]).(2, 1) == [1, 2]
|
||||
assert ([&1|&2]).(1, 2) == [1|2]
|
||||
assert ([&1, &2|&3]).(1, 2, 3) == [1, 2|3]
|
||||
assert (&[&1, 2]).(1) == [1, 2]
|
||||
assert (&[&1, &2]).(1, 2) == [1, 2]
|
||||
assert (&[&2, &1]).(2, 1) == [1, 2]
|
||||
assert (&[&1|&2]).(1, 2) == [1|2]
|
||||
assert (&[&1, &2|&3]).(1, 2, 3) == [1, 2|3]
|
||||
end
|
||||
|
||||
test :wrap do
|
||||
@@ -133,4 +133,19 @@ defmodule ListTest do
|
||||
assert List.replace_at([1, 2, 3], -1, 0) == [1, 2, 0]
|
||||
assert List.replace_at([1, 2, 3], -4, 0) == [1, 2, 3]
|
||||
end
|
||||
|
||||
test :heredoc do
|
||||
assert [ "a\n" ] == [ """ ]
|
||||
a
|
||||
"""
|
||||
|
||||
assert [ "a\n", "b\n", "c\n", 1 ] == [ """, """, """, 1 ]
|
||||
a
|
||||
"""
|
||||
b
|
||||
"""
|
||||
c
|
||||
"""
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -125,7 +125,7 @@ defmodule ModuleTest do
|
||||
|
||||
test :registered_attributes do
|
||||
assert [{:register_example, [:it_works]}, {:register_example, [:still_works]}] ==
|
||||
Enum.filter __MODULE__.__info__(:attributes), match?({ :register_example, _ }, &1)
|
||||
Enum.filter __MODULE__.__info__(:attributes), &match?({ :register_example, _ }, &1)
|
||||
end
|
||||
|
||||
@some_attribute [1]
|
||||
|
||||
@@ -47,8 +47,8 @@ defmodule RangeTest do
|
||||
assert Enum.count(1..3) == 3
|
||||
assert Enum.count(3..1) == 3
|
||||
|
||||
assert Enum.map(1..3, &1 * 2) == [2, 4, 6]
|
||||
assert Enum.map(3..1, &1 * 2) == [6, 4, 2]
|
||||
assert Enum.map(1..3, &(&1 * 2)) == [2, 4, 6]
|
||||
assert Enum.map(3..1, &(&1 * 2)) == [6, 4, 2]
|
||||
end
|
||||
|
||||
test :inspect do
|
||||
|
||||
@@ -70,6 +70,8 @@ defmodule RecordTest.Macros do
|
||||
defrecord Record, [a: 1, b: 2]
|
||||
end
|
||||
|
||||
defrecord RecordTest.FooTest, foo: nil, bar: nil
|
||||
|
||||
defmodule RecordTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
@@ -92,7 +94,7 @@ defmodule RecordTest do
|
||||
|
||||
test :dynamic_update do
|
||||
record = RecordTest.DynamicName.new
|
||||
assert record.update_a(10 + &1).a == 10
|
||||
assert record.update_a(&(10 + &1)).a == 10
|
||||
end
|
||||
|
||||
test :is_record do
|
||||
@@ -162,6 +164,22 @@ defmodule RecordTest do
|
||||
assert is_record(namespace, :xmlNamespace)
|
||||
end
|
||||
|
||||
test :string_names do
|
||||
a = RecordTest.FooTest.new([{"foo", 1}, {"bar", 1}])
|
||||
assert a.foo == 1
|
||||
assert a.bar == 1
|
||||
a = a.update([{"foo", 2}, {"bar", 2}])
|
||||
assert a.foo == 2
|
||||
assert a.bar == 2
|
||||
end
|
||||
|
||||
test :string_names_import do
|
||||
record = RecordTest.FileInfo.new([{"type", :regular}, {"access", 100}])
|
||||
assert record.type == :regular
|
||||
assert record.access == 100
|
||||
assert record.update([{"access", 101}]).access == 101
|
||||
end
|
||||
|
||||
defp empty_tuple, do: {}
|
||||
defp a_tuple, do: { :foo, :bar, :baz }
|
||||
defp a_list, do: [ :foo, :bar, :baz ]
|
||||
|
||||
@@ -68,13 +68,13 @@ defmodule Regex.BinaryTest do
|
||||
assert Regex.match?(%r/foo$/, "afoo")
|
||||
end
|
||||
|
||||
test :captures do
|
||||
assert Keyword.equal? Regex.captures(%r/(?<foo>c)(?<bar>d)/g, 'abcd'), [bar: 'd', foo: 'c']
|
||||
assert Regex.captures(%r/c(?<foo>d)/g, 'abcd') == [foo: 'd']
|
||||
assert Regex.captures(%r/c(?<foo>d)/g, 'no_match') == nil
|
||||
assert Regex.captures(%r/c(?<foo>d|e)/g, 'abcd abce') == [foo: 'd']
|
||||
assert Regex.captures(%r/c(?<foo>d)/g, 'abcd', return: :binary) == [foo: "d"]
|
||||
assert Regex.captures(%r/c(.)/g, 'cat') == []
|
||||
test :named_captures do
|
||||
assert Keyword.equal? Regex.named_captures(%r/(?<foo>c)(?<bar>d)/g, 'abcd'), [bar: 'd', foo: 'c']
|
||||
assert Regex.named_captures(%r/c(?<foo>d)/g, 'abcd') == [foo: 'd']
|
||||
assert Regex.named_captures(%r/c(?<foo>d)/g, 'no_match') == nil
|
||||
assert Regex.named_captures(%r/c(?<foo>d|e)/g, 'abcd abce') == [foo: 'd']
|
||||
assert Regex.named_captures(%r/c(?<foo>d)/g, 'abcd', return: :binary) == [foo: "d"]
|
||||
assert Regex.named_captures(%r/c(.)/g, 'cat') == []
|
||||
end
|
||||
|
||||
test :sigil_R do
|
||||
@@ -107,6 +107,7 @@ defmodule Regex.BinaryTest do
|
||||
end
|
||||
|
||||
test :scan_with_groups do
|
||||
assert Regex.scan(%r/cd/g, 'abcd', capture: :groups) == []
|
||||
assert Regex.scan(%r/c(?<foo>d)/g, 'abcd', capture: :groups) == [['d']]
|
||||
assert Regex.scan(%r/c(?<foo>d)/g, 'no_match', capture: :groups) == []
|
||||
assert Regex.scan(%r/c(?<foo>d|e)/g, 'abcd abce', capture: :groups) == [['d'], ['e']]
|
||||
|
||||
@@ -4,10 +4,10 @@ defmodule StreamTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
test "streams as enumerables" do
|
||||
stream = Stream.map([1,2,3], &1 * 2)
|
||||
stream = Stream.map([1,2,3], &(&1 * 2))
|
||||
|
||||
# Reduce
|
||||
assert Enum.map(stream, &1 + 1) == [3,5,7]
|
||||
assert Enum.map(stream, &(&1 + 1)) == [3,5,7]
|
||||
# Member
|
||||
assert Enum.member?(stream, 4)
|
||||
refute Enum.member?(stream, 1)
|
||||
@@ -16,10 +16,10 @@ defmodule StreamTest do
|
||||
end
|
||||
|
||||
test "streams are composable" do
|
||||
stream = Stream.map([1,2,3], &1 * 2)
|
||||
stream = Stream.map([1,2,3], &(&1 * 2))
|
||||
assert is_lazy(stream)
|
||||
|
||||
stream = Stream.map(stream, &1 + 1)
|
||||
stream = Stream.map(stream, &(&1 + 1))
|
||||
assert is_lazy(stream)
|
||||
|
||||
assert Enum.to_list(stream) == [3,5,7]
|
||||
@@ -77,15 +77,15 @@ defmodule StreamTest do
|
||||
end
|
||||
|
||||
test :drop_while do
|
||||
stream = Stream.drop_while(1..10, &1 <= 5)
|
||||
stream = Stream.drop_while(1..10, &(&1 <= 5))
|
||||
assert is_lazy(stream)
|
||||
assert Enum.to_list(stream) == [6,7,8,9,10]
|
||||
|
||||
assert Enum.to_list(Stream.drop_while(1..5, &1 <= 0)) == [1,2,3,4,5]
|
||||
assert Enum.to_list(Stream.drop_while(1..3, &1 <= 5)) == []
|
||||
assert Enum.to_list(Stream.drop_while(1..5, &(&1 <= 0))) == [1,2,3,4,5]
|
||||
assert Enum.to_list(Stream.drop_while(1..3, &(&1 <= 5))) == []
|
||||
|
||||
nats = Stream.iterate(1, &(&1 + 1))
|
||||
assert Stream.drop_while(nats, &1 <= 5) |> Enum.take(5) == [6,7,8,9,10]
|
||||
assert Stream.drop_while(nats, &(&1 <= 5)) |> Enum.take(5) == [6,7,8,9,10]
|
||||
end
|
||||
|
||||
test :filter do
|
||||
@@ -98,18 +98,18 @@ defmodule StreamTest do
|
||||
end
|
||||
|
||||
test :iterate do
|
||||
stream = Stream.iterate(0, &1+2)
|
||||
stream = Stream.iterate(0, &(&1+2))
|
||||
assert Enum.take(stream, 5) == [0,2,4,6,8]
|
||||
stream = Stream.iterate(5, &1+2)
|
||||
stream = Stream.iterate(5, &(&1+2))
|
||||
assert Enum.take(stream, 5) == [5,7,9,11,13]
|
||||
|
||||
# Only calculate values if needed
|
||||
stream = Stream.iterate("HELLO", raise(&1))
|
||||
stream = Stream.iterate("HELLO", &raise/1)
|
||||
assert Enum.take(stream, 1) == ["HELLO"]
|
||||
end
|
||||
|
||||
test :map do
|
||||
stream = Stream.map([1,2,3], &1 * 2)
|
||||
stream = Stream.map([1,2,3], &(&1 * 2))
|
||||
assert is_lazy(stream)
|
||||
assert Enum.to_list(stream) == [2,4,6]
|
||||
|
||||
@@ -162,12 +162,12 @@ defmodule StreamTest do
|
||||
end
|
||||
|
||||
test :take_while do
|
||||
stream = Stream.take_while(1..1000, &1 <= 5)
|
||||
stream = Stream.take_while(1..1000, &(&1 <= 5))
|
||||
assert is_lazy(stream)
|
||||
assert Enum.to_list(stream) == [1,2,3,4,5]
|
||||
|
||||
assert Enum.to_list(Stream.take_while(1..1000, &1 <= 0)) == []
|
||||
assert Enum.to_list(Stream.take_while(1..3, &1 <= 5)) == [1,2,3]
|
||||
assert Enum.to_list(Stream.take_while(1..1000, &(&1 <= 0))) == []
|
||||
assert Enum.to_list(Stream.take_while(1..3, &(&1 <= 5))) == [1,2,3]
|
||||
|
||||
nats = Stream.iterate(1, &(&1 + 1))
|
||||
assert Enum.to_list(Stream.take_while(nats, &(&1 <= 5))) == [1,2,3,4,5]
|
||||
@@ -175,6 +175,19 @@ defmodule StreamTest do
|
||||
stream = Stream.drop(1..100, 5)
|
||||
assert Stream.take_while(stream, &(&1 < 11)) |> Enum.to_list == [6,7,8,9,10]
|
||||
end
|
||||
|
||||
test :unfold do
|
||||
stream = Stream.unfold(10, fn x -> if x > 0, do: {x, x-1}, else: nil end)
|
||||
assert Enum.take(stream, 5) == [10, 9, 8, 7, 6]
|
||||
stream = Stream.unfold(5, fn x -> if x > 0, do: {x, x-1}, else: nil end)
|
||||
assert Enum.to_list(stream) == [5, 4, 3, 2, 1]
|
||||
|
||||
# Only calculate values if needed
|
||||
stream = Stream.unfold(1, fn x -> if x > 0, do: {x, x-1}, else: throw(:boom) end)
|
||||
assert Enum.take(stream, 1) == [1]
|
||||
stream = Stream.unfold(5, fn x -> if x > 0, do: {x, x-1}, else: nil end)
|
||||
assert Enum.to_list(Stream.take(stream, 2)) == [5, 4]
|
||||
end
|
||||
|
||||
test :with_index do
|
||||
stream = Stream.with_index([1,2,3])
|
||||
|
||||
@@ -36,6 +36,10 @@ defmodule StringTest do
|
||||
|
||||
assert String.split(" a b c ", " ", trim: true) == ["a", "b", "c"]
|
||||
assert String.split(" a b c ", " ", trim: true, global: false) == ["a b c "]
|
||||
|
||||
assert String.split("abé", "") == ["a", "b", "é", ""]
|
||||
assert String.split("abé", "", global: false) == ["a", "bé"]
|
||||
assert String.split("abé", "", trim: true) == ["a", "b", "é"]
|
||||
end
|
||||
|
||||
test :split_with_regex do
|
||||
@@ -196,7 +200,8 @@ defmodule StringTest do
|
||||
end
|
||||
|
||||
test :mixed_codepoints do
|
||||
assert String.codepoints("ϖͲϥЫݎߟΈټϘለДШव׆ש؇؊صلټܗݎޥޘ߉ऌ૫ሏᶆ℆ℙℱ ⅚Ⅷ↠∈⌘①ffi") == ["ϖ", "Ͳ", "ϥ", "Ы", "ݎ", "ߟ", "Έ", "ټ", "Ϙ", "ለ", "Д", "Ш", "व", "׆", "ש", "؇", "؊", "ص", "ل", "ټ", "ܗ", "ݎ", "ޥ", "ޘ", "߉", "ऌ", "૫", "ሏ", "ᶆ", "℆", "ℙ", "ℱ", " ", "⅚", "Ⅷ", "↠", "∈", "⌘", "①", "ffi"]
|
||||
assert String.codepoints("ϖͲϥЫݎߟΈټϘለДШव׆ש؇؊صلټܗݎޥޘ߉ऌ૫ሏᶆ℆ℙℱ ⅚Ⅷ↠∈⌘①ffi") ==
|
||||
["ϖ", "Ͳ", "ϥ", "Ы", "ݎ", "ߟ", "Έ", "ټ", "Ϙ", "ለ", "Д", "Ш", "व", "׆", "ש", "؇", "؊", "ص", "ل", "ټ", "ܗ", "ݎ", "ޥ", "ޘ", "߉", "ऌ", "૫", "ሏ", "ᶆ", "℆", "ℙ", "ℱ", " ", "⅚", "Ⅷ", "↠", "∈", "⌘", "①", "ffi"]
|
||||
end
|
||||
|
||||
test :graphemes do
|
||||
@@ -298,15 +303,6 @@ defmodule StringTest do
|
||||
refute String.valid_character?("ab")
|
||||
end
|
||||
|
||||
test :valid_codepoint? do
|
||||
assert String.valid_codepoint?("a")
|
||||
assert String.valid_codepoint?("ø")
|
||||
assert String.valid_codepoint?("あ")
|
||||
|
||||
refute String.valid_codepoint?(<<0xffff :: 16>>)
|
||||
refute String.valid_codepoint?("ab")
|
||||
end
|
||||
|
||||
test :to_integer do
|
||||
assert String.to_integer("12") === {12, ""}
|
||||
assert String.to_integer("-12") === {-12, ""}
|
||||
|
||||
@@ -25,8 +25,8 @@ defmodule TupleTest do
|
||||
end
|
||||
|
||||
test :partial_application do
|
||||
assert ({ &1, 2 }).(1) == { 1, 2 }
|
||||
assert ({ &1, &2 }).(1, 2) == { 1, 2 }
|
||||
assert ({ &2, &1 }).(2, 1) == { 1, 2 }
|
||||
assert (&{ &1, 2 }).(1) == { 1, 2 }
|
||||
assert (&{ &1, &2 }).(1, 2) == { 1, 2 }
|
||||
assert (&{ &2, &1 }).(2, 1) == { 1, 2 }
|
||||
end
|
||||
end
|
||||
@@ -488,4 +488,29 @@ defmodule Typespec.TypeTest do
|
||||
after
|
||||
Code.compiler_options debug_info: false
|
||||
end
|
||||
|
||||
test "typedoc retrieval" do
|
||||
Code.compiler_options debug_info: true
|
||||
|
||||
{ :module, T, binary, _ } = defmodule T do
|
||||
@typedoc "A"
|
||||
@type a :: any
|
||||
@typep b :: any
|
||||
@typedoc "C"
|
||||
@opaque c(x, y) :: {x, y}
|
||||
@type d :: any
|
||||
@spec uses_b() :: b
|
||||
def uses_b(), do: nil
|
||||
end
|
||||
|
||||
:code.delete(T)
|
||||
:code.purge(T)
|
||||
|
||||
assert [
|
||||
{{:c, 2}, "C"},
|
||||
{{:a, 0}, "A"}
|
||||
] = Kernel.Typespec.beam_typedocs(binary)
|
||||
after
|
||||
Code.compiler_options debug_info: false
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
Code.require_file "../test_helper.exs", __DIR__
|
||||
Code.require_file "test_helper.exs", __DIR__
|
||||
|
||||
defmodule Mix.VersionTest do
|
||||
defmodule VersionTest do
|
||||
use ExUnit.Case, async: true
|
||||
alias Mix.Version.Parser, as: P
|
||||
alias Mix.Version, as: V
|
||||
alias Version.Parser, as: P
|
||||
alias Version, as: V
|
||||
|
||||
test "lexes specifications properly" do
|
||||
assert P.lexer("== != > >= < <= ~>", []) == [:'==', :'!=', :'>', :'>=', :'<', :'<=', :'~>']
|
||||
@@ -119,9 +119,9 @@ function_call_with_parens_args_and_nested_when_test() ->
|
||||
%% Partial application
|
||||
|
||||
require_partial_application_test() ->
|
||||
{ Fun, _ } = eval("List.flatten(&1)"),
|
||||
{ Fun, _ } = eval("&List.flatten(&1)"),
|
||||
Fun = fun 'Elixir.List':flatten/1.
|
||||
|
||||
import_partial_application_test() ->
|
||||
{ Fun, _ } = eval("is_atom(&1)"),
|
||||
{ Fun, _ } = eval("&is_atom(&1)"),
|
||||
Fun = fun erlang:is_atom/1.
|
||||
|
||||
@@ -247,9 +247,9 @@ defmodule ExUnit.Assertions do
|
||||
|
||||
@doc """
|
||||
Asserts a message was or is going to be received. Unlike
|
||||
`assert_received`, it has a default timeout of 100 miliseconds.
|
||||
`assert_received`, it has a default timeout of 100 milliseconds.
|
||||
|
||||
The given `expected` argument must be a pattern.
|
||||
The given `expected` argument has to be a pattern.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -263,17 +263,20 @@ defmodule ExUnit.Assertions do
|
||||
|
||||
assert_receive { :hello, _ }
|
||||
|
||||
x = 5
|
||||
assert_receive { :count, ^x }
|
||||
|
||||
"""
|
||||
defmacro assert_receive(expected, timeout // 100, message // nil) do
|
||||
do_assert_receive(expected, timeout, message)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Asserts a message was received and is in the current process mailbox.
|
||||
The given `expected` argument must to be a match pattern.
|
||||
|
||||
Asserts a message was received and is in the current process' mailbox.
|
||||
Timeout is set to 0, so there is no waiting time.
|
||||
|
||||
The given `expected` argument has to be a pattern.
|
||||
|
||||
## Examples
|
||||
|
||||
self <- :hello
|
||||
|
||||
@@ -145,7 +145,7 @@ defmodule ExUnit.DocTest do
|
||||
|
||||
tests = Enum.filter(extract(module), fn(test) ->
|
||||
fa = test.fun_arity
|
||||
Enum.all?(except, &1 != fa) and Enum.all?(only, &1 == fa)
|
||||
Enum.all?(except, &(&1 != fa)) and Enum.all?(only, &(&1 == fa))
|
||||
end)
|
||||
|
||||
Enum.map_reduce(tests, 1, fn(test, acc) ->
|
||||
@@ -383,7 +383,7 @@ defmodule ExUnit.DocTest do
|
||||
end
|
||||
|
||||
defp strip_indent(line, indent) do
|
||||
line |> String.slice(indent, String.length(line))
|
||||
String.slice(line, indent, String.length(line)) || ""
|
||||
end
|
||||
|
||||
defp extract_tests([], _line, "", "", [], _) do
|
||||
|
||||
@@ -61,13 +61,13 @@ defmodule ExUnit.Runner do
|
||||
defp wait_until_available(config) do
|
||||
receive do
|
||||
{ _pid, :case_finished, _test_case } ->
|
||||
loop config.update_taken_cases(&1-1)
|
||||
loop config.update_taken_cases(&(&1-1))
|
||||
end
|
||||
end
|
||||
|
||||
defp spawn_cases(config, cases) do
|
||||
Enum.each cases, spawn_case(config, &1)
|
||||
loop config.update_taken_cases(&1+length(cases))
|
||||
Enum.each cases, &spawn_case(config, &1)
|
||||
loop config.update_taken_cases(&(&1+length(cases)))
|
||||
end
|
||||
|
||||
defp spawn_case(config, test_case) do
|
||||
@@ -97,7 +97,7 @@ defmodule ExUnit.Runner do
|
||||
tests = Enum.map tests, fn test -> test.failure({ :invalid, test_case }) end
|
||||
self_pid <- { self, :case_finished, test_case, tests }
|
||||
else
|
||||
Enum.each tests, run_test(config, &1, context)
|
||||
Enum.each tests, &run_test(config, &1, context)
|
||||
|
||||
test_case = try do
|
||||
case_name.__ex_unit__(:teardown_all, context)
|
||||
@@ -113,7 +113,7 @@ defmodule ExUnit.Runner do
|
||||
|
||||
receive do
|
||||
{ ^case_pid, :case_finished, test_case, tests } ->
|
||||
Enum.map tests, config.formatter.test_finished(config.formatter_id, &1)
|
||||
Enum.map tests, &config.formatter.test_finished(config.formatter_id, &1)
|
||||
config.formatter.case_finished(config.formatter_id, test_case)
|
||||
pid <- { case_pid, :case_finished, test_case }
|
||||
{ :DOWN, ^case_ref, :process, ^case_pid, { error, stacktrace } } ->
|
||||
|
||||
@@ -56,11 +56,11 @@ defmodule ExUnit.Server do
|
||||
end
|
||||
|
||||
def handle_cast({:add_async_case, name}, config) do
|
||||
{ :noreply, config.update_async_cases [name|&1] }
|
||||
{ :noreply, config.update_async_cases &[name|&1] }
|
||||
end
|
||||
|
||||
def handle_cast({:add_sync_case, name}, config) do
|
||||
{ :noreply, config.update_sync_cases [name|&1] }
|
||||
{ :noreply, config.update_sync_cases &[name|&1] }
|
||||
end
|
||||
|
||||
def handle_cast(request, config) do
|
||||
|
||||
@@ -141,7 +141,6 @@ defmodule ExUnit.DocTestTest.IndentationNotEnough do
|
||||
def not_enough, do: :ok
|
||||
end
|
||||
|
||||
|
||||
defmodule ExUnit.DocTestTest do
|
||||
use ExUnit.Case
|
||||
|
||||
|
||||
+37
-27
@@ -171,10 +171,23 @@ defmodule IEx do
|
||||
match?({ :ok, true }, :application.get_env(:iex, :started))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns `string` escaped using the specified color.
|
||||
ANSI escapes in `string` are not processed in any way.
|
||||
"""
|
||||
def color(color_name, string) do
|
||||
colors = IEx.Options.get(:colors)
|
||||
enabled = colors[:enabled]
|
||||
IO.ANSI.escape_fragment("%{#{colors[color_name]}}", enabled)
|
||||
<> string <> IO.ANSI.escape_fragment("%{reset}", enabled)
|
||||
end
|
||||
|
||||
## Callbacks
|
||||
|
||||
# This is a callback invoked by Erlang shell utilities
|
||||
# when someone press Ctrl+G and adds 's Elixir.IEx'.
|
||||
@doc false
|
||||
def start(config // []) do
|
||||
def start(config // [], callback // fn -> end) do
|
||||
spawn fn ->
|
||||
config =
|
||||
case config do
|
||||
@@ -182,9 +195,14 @@ defmodule IEx do
|
||||
opts -> boot_config(opts)
|
||||
end
|
||||
|
||||
Process.flag(:trap_exit, true)
|
||||
case :init.notify_when_started(self()) do
|
||||
:started -> :ok
|
||||
_ -> :init.wait_until_started()
|
||||
end
|
||||
|
||||
start_iex()
|
||||
callback.()
|
||||
|
||||
set_expand_fun()
|
||||
run_after_spawn()
|
||||
IEx.Server.start(config)
|
||||
@@ -192,19 +210,6 @@ defmodule IEx do
|
||||
end
|
||||
|
||||
@doc false
|
||||
def dont_display_result, do: :"do not show this result in iex"
|
||||
|
||||
## Boot Helpers
|
||||
|
||||
defp start_iex do
|
||||
:application.start(:elixir)
|
||||
:application.start(:iex)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the default config used to launch IEx. This config is also used by
|
||||
`IEx.TestFramework`.
|
||||
"""
|
||||
def boot_config(opts) do
|
||||
scope = :elixir.scope_for_eval(
|
||||
file: "iex",
|
||||
@@ -218,6 +223,22 @@ defmodule IEx do
|
||||
]
|
||||
end
|
||||
|
||||
@doc false
|
||||
def dont_display_result, do: :"do not show this result in iex"
|
||||
|
||||
## Helpers
|
||||
|
||||
defp start_iex do
|
||||
:application.start(:elixir)
|
||||
:application.start(:iex)
|
||||
|
||||
# Disable ANSI-escape-sequence-based coloring on Windows
|
||||
# Can be overriden in .iex
|
||||
if match?({ :win32, _ }, :os.type()) do
|
||||
IEx.Options.set :colors, enabled: false
|
||||
end
|
||||
end
|
||||
|
||||
defp set_expand_fun do
|
||||
gl = Process.group_leader
|
||||
glnode = node gl
|
||||
@@ -226,7 +247,7 @@ defmodule IEx do
|
||||
ensure_module_exists glnode, IEx.Remsh
|
||||
expand_fun = IEx.Remsh.expand node
|
||||
else
|
||||
expand_fun = IEx.Autocomplete.expand &1
|
||||
expand_fun = &IEx.Autocomplete.expand(&1)
|
||||
end
|
||||
|
||||
:io.setopts gl, [expand_fun: expand_fun, binary: true, encoding: :unicode]
|
||||
@@ -242,15 +263,4 @@ defmodule IEx do
|
||||
defp run_after_spawn do
|
||||
lc fun inlist Enum.reverse(after_spawn), do: fun.()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns `string` escaped using the specified color. ANSI escapes in `string`
|
||||
are not processed in any way.
|
||||
"""
|
||||
def color(color_name, string) do
|
||||
colors = IEx.Options.get(:colors)
|
||||
enabled = colors[:enabled]
|
||||
IO.ANSI.escape_fragment("%{#{colors[color_name]}}", enabled)
|
||||
<> string <> IO.ANSI.escape_fragment("%{reset}", enabled)
|
||||
end
|
||||
end
|
||||
|
||||
+83
-26
@@ -1,3 +1,39 @@
|
||||
# IEx sets the Erlang user to be IEx.CLI via the command line.
|
||||
# While this works most of the time, there are some problems
|
||||
# that may happen depending on how the booting process goes.
|
||||
# Those problems need to be manually tested in case changes are
|
||||
# done to this file.
|
||||
#
|
||||
# 1. In some situations, printing something before the shell
|
||||
# starts becomes very slow. To verify this feature, just
|
||||
# get an existing project and run:
|
||||
#
|
||||
# $ mix clean
|
||||
# $ iex -S mix
|
||||
#
|
||||
# If the printing of data is slower than usual. This particular
|
||||
# bug has arisen;
|
||||
#
|
||||
# 2. In some situations, connecting to a remote node via --remsh
|
||||
# is not possible. This can be tested by starting two iex nodes:
|
||||
#
|
||||
# $ iex --sname foo
|
||||
# $ iex --sname bar --remsh foo@localhost
|
||||
#
|
||||
# 3. When still using --remsh, we need to guarantee the arguments
|
||||
# are processed on the local node and not the remote one. For such,
|
||||
# one can replace the last line above by:
|
||||
#
|
||||
# $ iex --sname bar --remsh foo@localhost -e IO.inspect node
|
||||
#
|
||||
# And verify that the local node name is printed.
|
||||
#
|
||||
# 4. Finally, in some other circunstances, printing messages may become
|
||||
# borked. This can be verified with:
|
||||
#
|
||||
# $ iex -e ":error_logger.info_msg("foo~nbar", [])"
|
||||
#
|
||||
# By the time those instructions have been written, all tests above pass.
|
||||
defmodule IEx.CLI do
|
||||
@moduledoc false
|
||||
|
||||
@@ -15,15 +51,11 @@ defmodule IEx.CLI do
|
||||
"""
|
||||
def start do
|
||||
if tty_works? do
|
||||
:user.start_out
|
||||
:elixir.start_cli
|
||||
tty
|
||||
:user_drv.start([:"tty_sl -c -e", tty_args])
|
||||
else
|
||||
:user.start
|
||||
:elixir.start_cli
|
||||
IO.puts "Warning: could not run smart terminal, falling back to dumb one"
|
||||
config = [dot_iex_path: find_dot_iex(:init.get_plain_arguments)]
|
||||
IEx.start(config)
|
||||
local_start()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -40,30 +72,55 @@ defmodule IEx.CLI do
|
||||
end
|
||||
end
|
||||
|
||||
defp tty do
|
||||
plain_args = :init.get_plain_arguments
|
||||
config = [dot_iex_path: find_dot_iex(plain_args)]
|
||||
function = fn -> IEx.start(config) end
|
||||
|
||||
args =
|
||||
if remote = get_remsh(plain_args) do
|
||||
if is_alive do
|
||||
case :rpc.call remote, :code, :ensure_loaded, [IEx] do
|
||||
{ :badrpc, reason } ->
|
||||
abort "Could not contact remote node #{remote}, reason: #{inspect reason}. Aborting..."
|
||||
{ :module, IEx } ->
|
||||
{ remote, :erlang, :apply, [function, []] }
|
||||
_ ->
|
||||
abort "Could not find IEx on remote node #{remote}. Aborting..."
|
||||
end
|
||||
else
|
||||
abort "In order to use --remsh, you need to name the current node using --name or --sname. Aborting..."
|
||||
defp tty_args do
|
||||
if remote = get_remsh(:init.get_plain_arguments) do
|
||||
if is_alive do
|
||||
case :rpc.call remote, :code, :ensure_loaded, [IEx] do
|
||||
{ :badrpc, reason } ->
|
||||
abort "Could not contact remote node #{remote}, reason: #{inspect reason}. Aborting..."
|
||||
{ :module, IEx } ->
|
||||
{ remote, :erlang, :apply, [remote_start_function, []] }
|
||||
_ ->
|
||||
abort "Could not find IEx on remote node #{remote}. Aborting..."
|
||||
end
|
||||
else
|
||||
{ :erlang, :apply, [function, []] }
|
||||
abort "In order to use --remsh, you need to name the current node using --name or --sname. Aborting..."
|
||||
end
|
||||
else
|
||||
{ :erlang, :apply, [local_start_function, []] }
|
||||
end
|
||||
end
|
||||
|
||||
:user_drv.start([:"tty_sl -c -e", args])
|
||||
def local_start do
|
||||
IEx.start(config(), fn -> :elixir.start_cli end)
|
||||
end
|
||||
|
||||
defp local_start_function do
|
||||
&local_start/0
|
||||
end
|
||||
|
||||
defp remote_start_function do
|
||||
ref = make_ref
|
||||
config = config()
|
||||
|
||||
parent = spawn_link fn ->
|
||||
receive do
|
||||
{ :begin, ^ref, other } ->
|
||||
:elixir.start_cli
|
||||
other <- { :done, ref }
|
||||
end
|
||||
end
|
||||
|
||||
fn ->
|
||||
IEx.start(config, fn ->
|
||||
parent <- { :begin, ref, self }
|
||||
receive do: ({ :done, ^ref } -> :ok)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
defp config do
|
||||
[dot_iex_path: find_dot_iex(:init.get_plain_arguments)]
|
||||
end
|
||||
|
||||
defp abort(msg) do
|
||||
|
||||
+18
-27
@@ -22,7 +22,6 @@ defmodule IEx.Helpers do
|
||||
* `ls/1` — lists the contents of the specified directory
|
||||
* `m/0` — prints loaded modules
|
||||
* `pwd/0` — prints the current working directory
|
||||
* `r/0` — recompile and reload all modules that were previously reloaded
|
||||
* `r/1` — recompiles and reloads the given module's source file
|
||||
* `s/1` — prints spec information
|
||||
* `t/1` — prints type information
|
||||
@@ -62,8 +61,14 @@ defmodule IEx.Helpers do
|
||||
c "baz.ex"
|
||||
#=> [Baz]
|
||||
"""
|
||||
def c(files, path // ".") do
|
||||
{ erls, exs } = Enum.partition(List.wrap(files), &String.ends_with?(&1, ".erl"))
|
||||
def c(files, path // ".") when is_binary(path) do
|
||||
files = List.wrap(files)
|
||||
|
||||
unless Enum.all?(files, &is_binary/1) do
|
||||
raise ArgumentError, message: "expected a binary or a list of binaries as argument"
|
||||
end
|
||||
|
||||
{ erls, exs } = Enum.partition(files, &String.ends_with?(&1, ".erl"))
|
||||
|
||||
modules = Enum.map(erls, fn(source) ->
|
||||
{ module, binary } = compile_erlang(source)
|
||||
@@ -83,8 +88,8 @@ defmodule IEx.Helpers do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Prints the list of all loaded modules with paths to their corresponding .beam
|
||||
files.
|
||||
Prints the list of all loaded modules with paths to
|
||||
their corresponding `.beam` files.
|
||||
"""
|
||||
def m do
|
||||
all = Enum.map :code.all_loaded, fn { mod, file } -> { inspect(mod), file } end
|
||||
@@ -260,7 +265,7 @@ defmodule IEx.Helpers do
|
||||
"""
|
||||
def v do
|
||||
inspect_opts = IEx.Options.get(:inspect)
|
||||
IEx.History.each(print_history_entry(&1, inspect_opts))
|
||||
IEx.History.each(&print_history_entry(&1, inspect_opts))
|
||||
end
|
||||
|
||||
defp print_history_entry(config, inspect_opts) do
|
||||
@@ -278,21 +283,13 @@ defmodule IEx.Helpers do
|
||||
IEx.History.nth(n).result
|
||||
end
|
||||
|
||||
@doc """
|
||||
Reloads all modules that have already been reloaded with `r/1` at any point
|
||||
in the current IEx session.
|
||||
"""
|
||||
def r do
|
||||
List.flatten(Enum.map(iex_reloaded, do_r(&1)))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Recompiles and reloads the specified module's source file.
|
||||
|
||||
Please note that all the modules defined in the same file as `module`
|
||||
are recompiled and reloaded.
|
||||
"""
|
||||
def r(module) do
|
||||
def r(module) when is_atom(module) do
|
||||
case do_r(module) do
|
||||
mods when is_list(mods) -> { module, mods }
|
||||
other -> other
|
||||
@@ -306,11 +303,9 @@ defmodule IEx.Helpers do
|
||||
:nosource
|
||||
|
||||
String.ends_with?(source, ".erl") ->
|
||||
Process.put(:iex_reloaded, :ordsets.add_element(module, iex_reloaded))
|
||||
[ compile_erlang(source) |> elem(0) ]
|
||||
[compile_erlang(source) |> elem(0)]
|
||||
|
||||
true ->
|
||||
Process.put(:iex_reloaded, :ordsets.add_element(module, iex_reloaded))
|
||||
Enum.map(Code.load_file(source), fn {name, _} -> name end)
|
||||
end
|
||||
end
|
||||
@@ -319,7 +314,7 @@ defmodule IEx.Helpers do
|
||||
Load the given module's beam code (and ensures any previous
|
||||
old version was properly purged before).
|
||||
"""
|
||||
def l(module) do
|
||||
def l(module) when is_atom(module) do
|
||||
:code.purge(module)
|
||||
:code.load_file(module)
|
||||
end
|
||||
@@ -342,10 +337,6 @@ defmodule IEx.Helpers do
|
||||
end
|
||||
end
|
||||
|
||||
defp iex_reloaded do
|
||||
Process.get(:iex_reloaded) || :ordsets.new
|
||||
end
|
||||
|
||||
defp source(module) do
|
||||
source = module.module_info(:compile)[:source]
|
||||
|
||||
@@ -365,7 +356,7 @@ defmodule IEx.Helpers do
|
||||
@doc """
|
||||
Changes the current working directory to the given path.
|
||||
"""
|
||||
def cd(directory) do
|
||||
def cd(directory) when is_binary(directory) do
|
||||
case File.cd(expand_home(directory)) do
|
||||
:ok -> pwd
|
||||
{ :error, :enoent } ->
|
||||
@@ -377,7 +368,7 @@ defmodule IEx.Helpers do
|
||||
Produces a simple list of a directory's contents.
|
||||
If `path` points to a file, prints its full path.
|
||||
"""
|
||||
def ls(path // ".") do
|
||||
def ls(path // ".") when is_binary(path) do
|
||||
path = expand_home(path)
|
||||
case File.ls(path) do
|
||||
{ :ok, items } ->
|
||||
@@ -404,7 +395,7 @@ defmodule IEx.Helpers do
|
||||
|
||||
defp ls_print(path, list) do
|
||||
# print items in multiple columns (2 columns in the worst case)
|
||||
lengths = Enum.map(list, String.length(&1))
|
||||
lengths = Enum.map(list, &String.length(&1))
|
||||
maxlen = maxlength(lengths)
|
||||
width = min(maxlen, 30) + 5
|
||||
ls_print(path, list, width)
|
||||
@@ -423,7 +414,7 @@ defmodule IEx.Helpers do
|
||||
end
|
||||
|
||||
defp maxlength(list) do
|
||||
Enum.reduce(list, 0, max(&1, &2))
|
||||
Enum.reduce(list, 0, &max(&1, &2))
|
||||
end
|
||||
|
||||
defp format_item(path, representation) do
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
defmodule IEx.History do
|
||||
@moduledoc false
|
||||
|
||||
@doc """
|
||||
Initializes IEx process variables. All history
|
||||
information is kept in the process dictionary.
|
||||
"""
|
||||
def init do
|
||||
Process.put(:iex_history_start_counter, 1)
|
||||
Process.put(:iex_history_counter, 1)
|
||||
end
|
||||
|
||||
### append ###
|
||||
|
||||
@doc """
|
||||
Appends one entry to the history with the given counter.
|
||||
"""
|
||||
def append(entry, counter) do
|
||||
Process.put({:iex_history, counter}, entry)
|
||||
Process.put(:iex_history_counter, counter+1)
|
||||
@@ -93,8 +98,9 @@ defmodule IEx.History do
|
||||
:erlang.garbage_collect()
|
||||
end
|
||||
|
||||
### each ###
|
||||
|
||||
@doc """
|
||||
Enumerates each item in the history.
|
||||
"""
|
||||
def each(fun) do
|
||||
each(Process.get(:iex_history_start_counter),
|
||||
Process.get(:iex_history_counter),
|
||||
@@ -111,8 +117,9 @@ defmodule IEx.History do
|
||||
:ok
|
||||
end
|
||||
|
||||
### nth ###
|
||||
|
||||
@doc """
|
||||
Gets the nth item in the history.
|
||||
"""
|
||||
def nth(n) do
|
||||
entry = case n do
|
||||
n when n >= 0 ->
|
||||
@@ -122,7 +129,7 @@ defmodule IEx.History do
|
||||
Process.get({:iex_history, counter + n})
|
||||
end
|
||||
if nil?(entry) do
|
||||
raise "Out of bounds"
|
||||
raise "v(#{n}) is out of bounds"
|
||||
end
|
||||
entry
|
||||
end
|
||||
|
||||
@@ -147,7 +147,7 @@ defmodule IEx.Introspection do
|
||||
Enum.find docs, fn(doc) ->
|
||||
case elem(doc, 0) do
|
||||
{ ^function, max } when max > min ->
|
||||
defaults = Enum.count elem(doc, 3), match?({ ://, _, _ }, &1)
|
||||
defaults = Enum.count elem(doc, 3), &match?({ ://, _, _ }, &1)
|
||||
min + defaults >= max
|
||||
_ ->
|
||||
false
|
||||
@@ -156,7 +156,7 @@ defmodule IEx.Introspection do
|
||||
end
|
||||
|
||||
defp print_doc({ { fun, _ }, _line, kind, args, doc }) do
|
||||
args = Enum.map_join(args, ", ", print_doc_arg(&1))
|
||||
args = Enum.map_join(args, ", ", &print_doc_arg(&1))
|
||||
IO.puts IEx.color(:info, "* #{kind} #{fun}(#{args})\n")
|
||||
if doc, do: IO.write IEx.color(:info, doc)
|
||||
end
|
||||
@@ -252,8 +252,8 @@ defmodule IEx.Introspection do
|
||||
end
|
||||
|
||||
defp beam_specs(module) do
|
||||
specs = Enum.map(Kernel.Typespec.beam_specs(module), {:spec, &1})
|
||||
callbacks = Enum.map(Kernel.Typespec.beam_callbacks(module), {:callback, &1})
|
||||
specs = Enum.map(Kernel.Typespec.beam_specs(module), &{:spec, &1})
|
||||
callbacks = Enum.map(Kernel.Typespec.beam_callbacks(module), &{:callback, &1})
|
||||
Enum.concat(specs, callbacks)
|
||||
end
|
||||
|
||||
|
||||
+102
-94
@@ -4,14 +4,14 @@ defmodule IEx.Server do
|
||||
@doc """
|
||||
Eval loop for an IEx session. Its responsibilities include:
|
||||
|
||||
* loading of .iex files
|
||||
* reading input
|
||||
* trapping exceptions in the code being evaluated
|
||||
* keeping expression history
|
||||
* loading of .iex files
|
||||
* reading input
|
||||
* trapping exceptions in the code being evaluated
|
||||
* keeping expression history
|
||||
|
||||
"""
|
||||
def start(config) do
|
||||
init_systems()
|
||||
IEx.History.init
|
||||
|
||||
{ _, _, scope } = :elixir.eval('require IEx.Helpers', [], 0, config.scope)
|
||||
config = config.scope(scope)
|
||||
@@ -26,40 +26,52 @@ defmodule IEx.Server do
|
||||
|
||||
old_flag = Process.flag(:trap_exit, true)
|
||||
self_pid = self
|
||||
|
||||
# We have one loop for receiving input and
|
||||
# another loop for evaluating contents.
|
||||
pid = spawn_link(fn -> input_loop(self_pid) end)
|
||||
|
||||
try do
|
||||
do_loop(config.input_pid(pid))
|
||||
eval_loop(config.input_pid(pid))
|
||||
after
|
||||
Process.exit(pid, :normal)
|
||||
Process.flag(:trap_exit, old_flag)
|
||||
end
|
||||
end
|
||||
|
||||
defp do_loop(config) do
|
||||
## Eval loop
|
||||
|
||||
defp eval_loop(config) do
|
||||
prefix = config.cache != []
|
||||
config.input_pid <- { :do_input, prefix, config.counter }
|
||||
config.input_pid <- { :do_input, self, prefix, config.counter }
|
||||
wait_input(config)
|
||||
end
|
||||
|
||||
defp wait_input(config) do
|
||||
pid = config.input_pid
|
||||
|
||||
receive do
|
||||
{ :input, line } ->
|
||||
unless line == :eof do
|
||||
new_config =
|
||||
try do
|
||||
counter = config.counter
|
||||
code = config.cache
|
||||
eval(code, line, counter, config)
|
||||
catch
|
||||
kind, error ->
|
||||
print_error(kind, Exception.normalize(kind, error), System.stacktrace)
|
||||
config.cache('')
|
||||
end
|
||||
|
||||
do_loop(new_config)
|
||||
end
|
||||
{ :input, ^pid, data } when is_binary(data) ->
|
||||
new_config =
|
||||
try do
|
||||
line = String.to_char_list!(data)
|
||||
counter = config.counter
|
||||
code = config.cache
|
||||
eval(code, line, counter, config)
|
||||
catch
|
||||
kind, error ->
|
||||
print_error(kind, Exception.normalize(kind, error), System.stacktrace)
|
||||
config.cache('')
|
||||
end
|
||||
|
||||
eval_loop(new_config)
|
||||
{ :input, ^pid, :eof } ->
|
||||
:ok
|
||||
{ :input, ^pid, { :error, :interrupted } } ->
|
||||
io_error "** (EXIT) interrupted"
|
||||
eval_loop(config.cache(''))
|
||||
{ :input, ^pid, { :error, :terminated } } ->
|
||||
:ok
|
||||
{ :EXIT, _pid, :normal } ->
|
||||
wait_input(config)
|
||||
{ :EXIT, pid, reason } ->
|
||||
@@ -68,16 +80,6 @@ defmodule IEx.Server do
|
||||
end
|
||||
end
|
||||
|
||||
defp init_systems() do
|
||||
IEx.History.init
|
||||
|
||||
# Disable ANSI-escape-sequence-based coloring on Windows
|
||||
# Can be overriden in .iex
|
||||
if match?({ :win32, _ }, :os.type()) do
|
||||
IEx.Options.set :colors, enabled: false
|
||||
end
|
||||
end
|
||||
|
||||
# Instead of doing just `:elixir.eval`, we first parse the expression to see
|
||||
# if it's well formed. If parsing succeeds, we evaluate the AST as usual.
|
||||
#
|
||||
@@ -114,7 +116,7 @@ defmodule IEx.Server do
|
||||
|
||||
config = config.cache(code).scope(nil).result(result)
|
||||
update_history(config)
|
||||
config.update_counter(&1+1).cache('').binding(new_binding).scope(scope).result(nil)
|
||||
config.update_counter(&(&1+1)).cache('').binding(new_binding).scope(scope).result(nil)
|
||||
|
||||
{ :error, { line_no, error, token } } ->
|
||||
if token == [] do
|
||||
@@ -128,70 +130,10 @@ defmodule IEx.Server do
|
||||
end
|
||||
end
|
||||
|
||||
# Locates and loads an .iex file from one of predefined locations. Returns
|
||||
# new config.
|
||||
defp load_dot_iex(config, path // nil) do
|
||||
candidates = if path do
|
||||
[path]
|
||||
else
|
||||
Enum.map [".iex", "~/.iex"], Path.expand(&1)
|
||||
end
|
||||
|
||||
path = Enum.find candidates, fn path -> File.regular?(path) end
|
||||
|
||||
if nil?(path) do
|
||||
config
|
||||
else
|
||||
try do
|
||||
code = File.read!(path)
|
||||
scope = :elixir.scope_for_eval(config.scope, file: path)
|
||||
|
||||
# Evaluate the contents in the same environment do_loop will run in
|
||||
{ _result, binding, scope } =
|
||||
:elixir.eval(String.to_char_list!(code),
|
||||
config.binding,
|
||||
0,
|
||||
scope)
|
||||
|
||||
scope = :elixir.scope_for_eval(scope, file: "iex")
|
||||
config.binding(binding).scope(scope)
|
||||
catch
|
||||
kind, error ->
|
||||
print_error(kind, Exception.normalize(kind, error), System.stacktrace)
|
||||
System.halt(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp update_history(config) do
|
||||
IEx.History.append(config, config.counter)
|
||||
end
|
||||
|
||||
defp input_loop(iex_pid) do
|
||||
receive do
|
||||
{ :do_input, prefix, counter } ->
|
||||
iex_pid <- { :input, io_get(prefix, counter) }
|
||||
end
|
||||
input_loop(iex_pid)
|
||||
end
|
||||
|
||||
defp io_get(prefix, counter) do
|
||||
prefix = if prefix, do: "..."
|
||||
|
||||
prompt =
|
||||
if is_alive do
|
||||
"#{prefix || remote_prefix}(#{node})#{counter}> "
|
||||
else
|
||||
"#{prefix || "iex"}(#{counter})> "
|
||||
end
|
||||
|
||||
case IO.gets(:stdio, prompt) do
|
||||
:eof -> :eof
|
||||
{ :error, _ } -> ''
|
||||
data -> String.to_char_list!(data)
|
||||
end
|
||||
end
|
||||
|
||||
defp io_put(result) do
|
||||
IO.puts :stdio, IEx.color(:eval_result, inspect(result, inspect_opts))
|
||||
end
|
||||
@@ -208,10 +150,76 @@ defmodule IEx.Server do
|
||||
end
|
||||
end
|
||||
|
||||
## Load dot iex helpers
|
||||
|
||||
# Locates and loads an .iex file from one of predefined locations. Returns
|
||||
# new config.
|
||||
defp load_dot_iex(config, path // nil) do
|
||||
candidates = if path do
|
||||
[path]
|
||||
else
|
||||
Enum.map [".iex", "~/.iex"], &Path.expand/1
|
||||
end
|
||||
|
||||
path = Enum.find candidates, &File.regular?/1
|
||||
|
||||
if nil?(path) do
|
||||
config
|
||||
else
|
||||
eval_dot_iex(config, path)
|
||||
end
|
||||
end
|
||||
|
||||
defp eval_dot_iex(config, path) do
|
||||
try do
|
||||
code = File.read!(path)
|
||||
scope = :elixir.scope_for_eval(config.scope, file: path)
|
||||
|
||||
# Evaluate the contents in the same environment eval_loop will run in
|
||||
{ _result, binding, scope } =
|
||||
:elixir.eval(String.to_char_list!(code),
|
||||
config.binding,
|
||||
0,
|
||||
scope)
|
||||
|
||||
scope = :elixir.scope_for_eval(scope, file: "iex")
|
||||
config.binding(binding).scope(scope)
|
||||
catch
|
||||
kind, error ->
|
||||
print_error(kind, Exception.normalize(kind, error), System.stacktrace)
|
||||
System.halt(1)
|
||||
end
|
||||
end
|
||||
|
||||
## Input loop
|
||||
|
||||
defp input_loop(pid) do
|
||||
receive do
|
||||
{ :do_input, ^pid, prefix, counter } ->
|
||||
pid <- { :input, self, io_get(prefix, counter) }
|
||||
end
|
||||
input_loop(pid)
|
||||
end
|
||||
|
||||
defp io_get(prefix, counter) do
|
||||
prefix = if prefix, do: "..."
|
||||
|
||||
prompt =
|
||||
if is_alive do
|
||||
"#{prefix || remote_prefix}(#{node})#{counter}> "
|
||||
else
|
||||
"#{prefix || "iex"}(#{counter})> "
|
||||
end
|
||||
|
||||
IO.gets(:stdio, prompt)
|
||||
end
|
||||
|
||||
defp remote_prefix do
|
||||
if node == node(:erlang.group_leader), do: "iex", else: "rem"
|
||||
end
|
||||
|
||||
## Error handling
|
||||
|
||||
defp print_error(:error, exception, stacktrace) do
|
||||
print_stacktrace stacktrace, fn ->
|
||||
"** (#{inspect exception.__record__(:name)}) #{exception.message}"
|
||||
|
||||
@@ -112,7 +112,7 @@ defmodule IEx.HelpersTest do
|
||||
:ok
|
||||
"""
|
||||
|
||||
assert "** (RuntimeError) Out of bounds" <> _
|
||||
assert "** (RuntimeError) v(0) is out of bounds" <> _
|
||||
= capture_iex("v(0)")
|
||||
assert capture_iex("1\n2\nv(2)") == "1\n2\n2"
|
||||
assert capture_iex("1\n2\nv(2)") == capture_iex("1\n2\nv(-1)")
|
||||
@@ -133,7 +133,7 @@ defmodule IEx.HelpersTest do
|
||||
assert ["ebin", "lib", "mix.exs", "test"]
|
||||
= capture_io(fn -> ls end)
|
||||
|> String.split
|
||||
|> Enum.map(String.strip(&1))
|
||||
|> Enum.map(&String.strip(&1))
|
||||
|> Enum.sort
|
||||
assert capture_io(fn -> ls "~" end) == capture_io(fn -> ls System.user_home end)
|
||||
end
|
||||
@@ -266,20 +266,13 @@ defmodule IEx.HelpersTest do
|
||||
cleanup_modules([Sample])
|
||||
end
|
||||
|
||||
test "r helper basic" do
|
||||
assert r == []
|
||||
test "r helper unavailable" do
|
||||
assert_raise UndefinedFunctionError, "undefined function: :non_existent_module.module_info/1", fn ->
|
||||
r :non_existent_module
|
||||
end
|
||||
|
||||
# There is no source file for the module defined in IEx
|
||||
assert ":ok\n** (Code.LoadError) could not load" <> _
|
||||
= capture_iex("{:module, Sample, _, {:run,0}} = #{String.strip test_module_code}; :ok\nr Sample")
|
||||
after
|
||||
cleanup_modules([Sample])
|
||||
end
|
||||
|
||||
test "r helper" do
|
||||
test "r helper elixir" do
|
||||
assert_raise UndefinedFunctionError, "undefined function: Sample.run/0", fn ->
|
||||
Sample.run
|
||||
end
|
||||
@@ -296,8 +289,7 @@ defmodule IEx.HelpersTest do
|
||||
Sample.run
|
||||
end
|
||||
|
||||
assert [Sample] = r()
|
||||
end) =~ %r"^.*?sample\.ex:1: redefining module Sample\n.*?sample\.ex:1: redefining module Sample\n$"
|
||||
end) =~ %r"^.*?sample\.ex:1: redefining module Sample\n$"
|
||||
end
|
||||
after
|
||||
# Clean up old version produced by the r helper
|
||||
@@ -376,7 +368,7 @@ defmodule IEx.HelpersTest do
|
||||
try do
|
||||
fun.()
|
||||
after
|
||||
Enum.each names, File.rm(&1)
|
||||
Enum.each names, &File.rm/1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -29,11 +29,11 @@ defmodule IEx.InteractionTest do
|
||||
end
|
||||
|
||||
test "empty history at the start" do
|
||||
assert "** (RuntimeError) Out of bounds" <> _ = capture_iex("v(-1)")
|
||||
assert "** (RuntimeError) v(-1) is out of bounds" <> _ = capture_iex("v(-1)")
|
||||
end
|
||||
|
||||
test "empty history at the start redux" do
|
||||
assert "** (RuntimeError) Out of bounds" <> _ = capture_iex("v(1)")
|
||||
assert "** (RuntimeError) v(1) is out of bounds" <> _ = capture_iex("v(1)")
|
||||
end
|
||||
|
||||
test "no break" do
|
||||
|
||||
@@ -45,9 +45,9 @@ defmodule IEx.OptionsTest do
|
||||
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) Out of bounds" <> _ = capture_iex("1\n2\n3\n4\nv(1)", opts)
|
||||
assert "1\n2\n3\n4\n** (RuntimeError) Out of bounds" <> _ = capture_iex("1\n2\n3\n4\nv(-4)", opts)
|
||||
assert "1\n2\n3\n4\n2\n** (RuntimeError) Out of bounds" <> _ = capture_iex("1\n2\n3\n4\nv(2)\nv(2)", opts)
|
||||
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 "bad option" do
|
||||
|
||||
@@ -25,6 +25,12 @@ defmodule Mix.CLI do
|
||||
args
|
||||
end
|
||||
|
||||
defp get_task(["-" <> _|_]) do
|
||||
Mix.shell.error "** (Mix) Cannot implicitly pass flags to default mix task, " <>
|
||||
"please invoke instead: mix #{Mix.project[:default_task]}"
|
||||
exit(1)
|
||||
end
|
||||
|
||||
defp get_task([h|t]) do
|
||||
{ h, t }
|
||||
end
|
||||
|
||||
+12
-9
@@ -29,7 +29,7 @@ defmodule Mix.Deps do
|
||||
{ app :: atom, requirement :: String.t, opts :: Keyword.t }
|
||||
|
||||
The application name must be an atom, the version requirement must
|
||||
be a string according to the specification outline in `Mix.Version`
|
||||
be a string according to the specification outline in `Version`
|
||||
and opts is a keyword lists that may include options for the underlying
|
||||
SCM or options used by Mix. Each set of options is documented below.
|
||||
|
||||
@@ -105,22 +105,25 @@ defmodule Mix.Deps do
|
||||
defdelegate children(), to: Mix.Deps.Retriever
|
||||
|
||||
@doc """
|
||||
Return all dependencies depending on the given dependencies.
|
||||
Return all given dependencies and their depending dependencies.
|
||||
"""
|
||||
def depending(deps, all_deps // all)
|
||||
def with_depending(deps, all_deps // all) do
|
||||
deps ++ do_with_depending(deps, all_deps)
|
||||
|> Enum.uniq(&(&1.app))
|
||||
end
|
||||
|
||||
def depending([], _all_deps) do
|
||||
defp do_with_depending([], _all_deps) do
|
||||
[]
|
||||
end
|
||||
|
||||
def depending(deps, all_deps) do
|
||||
defp do_with_depending(deps, all_deps) do
|
||||
dep_names = Enum.map(deps, fn dep -> dep.app end)
|
||||
|
||||
parents = Enum.filter all_deps, fn dep ->
|
||||
Enum.any?(dep.deps, fn child_dep -> child_dep.app in dep_names end)
|
||||
end
|
||||
|
||||
parents ++ depending(parents, all_deps)
|
||||
do_with_depending(parents, all_deps) ++ parents
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -321,10 +324,10 @@ defmodule Mix.Deps do
|
||||
end)
|
||||
|
||||
[ opts[:dest] | sub_dirs ]
|
||||
|> Enum.map(Path.wildcard(&1))
|
||||
|> Enum.map(&Path.wildcard(&1))
|
||||
|> Enum.concat
|
||||
|> Enum.map(Path.join(&1, "ebin"))
|
||||
|> Enum.filter(File.dir?(&1))
|
||||
|> Enum.map(&Path.join(&1, "ebin"))
|
||||
|> Enum.filter(&File.dir?(&1))
|
||||
end
|
||||
|
||||
def load_paths(Mix.Dep[manager: manager, opts: opts]) when manager in [:make, nil] do
|
||||
|
||||
@@ -83,7 +83,9 @@ defmodule Mix.Deps.Converger do
|
||||
|
||||
# Look for an overriding dep in the upper breadths, if
|
||||
# found return a new acc without the overriden dep and
|
||||
# with the proper status set on the overrider
|
||||
# with the proper status set on the overrider. The
|
||||
# overrider is moved to the front of the accumulator to
|
||||
# preserve the position of the removed dep.
|
||||
defp overriden_deps(acc, upper_breadths, dep) do
|
||||
overriden = Enum.any?(upper_breadths, fn(other) ->
|
||||
other.app == dep.app
|
||||
@@ -92,18 +94,21 @@ defmodule Mix.Deps.Converger do
|
||||
if overriden do
|
||||
Mix.Dep[app: app] = dep
|
||||
|
||||
Enum.map(acc, fn other ->
|
||||
Mix.Dep[app: other_app, opts: other_opts] = other
|
||||
{ overrider, acc } =
|
||||
Enum.reduce(acc, { nil , [] }, fn other, { overrider, acc } ->
|
||||
Mix.Dep[app: other_app, opts: other_opts] = other
|
||||
|
||||
cond do
|
||||
app == other_app && (other_opts[:override] || converge?(dep, other)) ->
|
||||
other
|
||||
app == other_app ->
|
||||
other.status({ :overriden, dep })
|
||||
true ->
|
||||
other
|
||||
end
|
||||
end)
|
||||
cond do
|
||||
app == other_app && (other_opts[:override] || converge?(dep, other)) ->
|
||||
{ other, acc }
|
||||
app == other_app ->
|
||||
{ other.status({ :overriden, dep }), acc }
|
||||
true ->
|
||||
{ overrider, [other|acc] }
|
||||
end
|
||||
end)
|
||||
|
||||
[ overrider | Enum.reverse(acc) ]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ defmodule Mix.Deps.Retriever do
|
||||
def children() do
|
||||
scms = Mix.SCM.available
|
||||
from = current_source(:mix)
|
||||
Enum.map(Mix.project[:deps], update(&1, scms, from))
|
||||
Enum.map(Mix.project[:deps], &update(&1, scms, from))
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -52,7 +52,7 @@ defmodule Mix.Deps.Retriever do
|
||||
|
||||
# The manager must be nil because mix supports mix,
|
||||
# rebar and make dependencies/managers.
|
||||
(Mix.project[:deps] || []) |> Enum.map(update(&1, scms, from))
|
||||
(Mix.project[:deps] || []) |> Enum.map(&update(&1, scms, from))
|
||||
end) |> Enum.concat
|
||||
end
|
||||
|
||||
@@ -62,7 +62,7 @@ defmodule Mix.Deps.Retriever do
|
||||
from = current_source(:rebar)
|
||||
|
||||
# Rebar dependencies are always managed by rebar.
|
||||
Mix.Rebar.deps(config) |> Enum.map(update(&1, scms, from, :rebar))
|
||||
Mix.Rebar.deps(config) |> Enum.map(&update(&1, scms, from, :rebar))
|
||||
end) |> Enum.concat
|
||||
end
|
||||
|
||||
@@ -208,7 +208,7 @@ defmodule Mix.Deps.Retriever do
|
||||
defp vsn_match?(nil, _actual), do: true
|
||||
defp vsn_match?(req, actual) when is_regex(req), do: actual =~ req
|
||||
defp vsn_match?(req, actual) when is_binary(req) do
|
||||
Mix.Version.match?(actual, req)
|
||||
Version.match?(actual, req)
|
||||
end
|
||||
|
||||
defp mixfile?(dep) do
|
||||
|
||||
@@ -12,14 +12,14 @@ defmodule Mix.Local do
|
||||
Append archives paths into Erlang code path.
|
||||
"""
|
||||
def append_archives do
|
||||
Enum.each(archives_ebin, Code.append_path(&1))
|
||||
Enum.each(archives_ebin, &Code.append_path(&1))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Append mix paths into Erlang code path.
|
||||
"""
|
||||
def append_paths do
|
||||
Enum.each(Mix.Utils.mix_paths, Code.append_path(&1))
|
||||
Enum.each(Mix.Utils.mix_paths, &Code.append_path(&1))
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -28,7 +28,7 @@ defmodule Mix.Local do
|
||||
def all_tasks, do: Mix.Task.load_tasks(archives_ebin)
|
||||
|
||||
defp archives_ebin do
|
||||
Path.join(archives_path, "*.ez") |> Path.wildcard |> Enum.map(archive_ebin(&1))
|
||||
Path.join(archives_path, "*.ez") |> Path.wildcard |> Enum.map(&archive_ebin(&1))
|
||||
end
|
||||
|
||||
defp archive_ebin(archive_file) do
|
||||
|
||||
+25
-15
@@ -141,27 +141,37 @@ defmodule Mix.Project do
|
||||
def recur(post_config // [], fun) do
|
||||
if apps_path = config[:apps_path] do
|
||||
paths = Path.wildcard(Path.join(apps_path, "*"))
|
||||
paths = Enum.filter(paths, File.dir?(&1))
|
||||
|
||||
projects = Enum.map paths, fn path ->
|
||||
dir = Path.basename(path)
|
||||
app = dir |> String.downcase |> binary_to_atom
|
||||
{ app, path }
|
||||
end
|
||||
|
||||
projects = topsort_projects(projects, Path.expand(apps_path))
|
||||
|
||||
results = Enum.map projects, fn { app, app_path } ->
|
||||
in_project(app, app_path, post_config, fun)
|
||||
end
|
||||
|
||||
results
|
||||
paths
|
||||
|> Enum.filter(&File.dir?(&1))
|
||||
|> extract_projects
|
||||
|> filter_projects(config[:apps])
|
||||
|> topsort_projects(Path.expand(apps_path))
|
||||
|> recur_projects(post_config, fun)
|
||||
else
|
||||
# Note that post_config isnt used for this case
|
||||
[fun.(get)]
|
||||
end
|
||||
end
|
||||
|
||||
defp extract_projects(paths) do
|
||||
lc path inlist paths do
|
||||
app = path |> Path.basename |> String.downcase |> binary_to_atom
|
||||
{ app, path }
|
||||
end
|
||||
end
|
||||
|
||||
defp filter_projects(pairs, nil), do: pairs
|
||||
defp filter_projects(pairs, apps) when is_list(apps) do
|
||||
lc { app, _ } = pair inlist pairs, app in apps, do: pair
|
||||
end
|
||||
|
||||
defp recur_projects(pairs, post_config, fun) do
|
||||
lc { app, path } inlist pairs do
|
||||
in_project(app, path, post_config, fun)
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Runs the given `fun` inside the given project by changing
|
||||
the current working directory and loading the given project
|
||||
@@ -200,7 +210,7 @@ defmodule Mix.Project do
|
||||
def load_paths do
|
||||
paths =
|
||||
recur(fn _ ->
|
||||
Enum.map(config[:load_paths], Path.expand(&1))
|
||||
Enum.map(config[:load_paths], &Path.expand(&1))
|
||||
end) |> Enum.concat
|
||||
paths ++ compile_paths
|
||||
end
|
||||
|
||||
@@ -63,7 +63,7 @@ defmodule Mix.Rebar do
|
||||
def deps(config) do
|
||||
if deps = config[:deps] do
|
||||
deps_dir = config[:deps_dir] || "deps"
|
||||
Enum.map(deps, parse_dep(&1, deps_dir))
|
||||
Enum.map(deps, &parse_dep(&1, deps_dir))
|
||||
else
|
||||
[]
|
||||
end
|
||||
@@ -78,9 +78,9 @@ defmodule Mix.Rebar do
|
||||
|
||||
if sub_dirs = config[:sub_dirs] do
|
||||
sub_dirs = sub_dirs
|
||||
|> Enum.map(Path.wildcard(&1))
|
||||
|> Enum.map(&Path.wildcard(&1))
|
||||
|> Enum.concat
|
||||
|> Enum.filter(File.dir?(&1))
|
||||
|> Enum.filter(&File.dir?(&1))
|
||||
|
||||
Enum.map(sub_dirs, fn(dir) ->
|
||||
recur(dir, fun)
|
||||
|
||||
@@ -94,7 +94,7 @@ defmodule Mix.SCM.Git do
|
||||
defp get_lock_rev(_), do: nil
|
||||
|
||||
defp get_lock_opts(opts) do
|
||||
lock_opts = Enum.find_value [:branch, :ref, :tag], List.keyfind(opts, &1, 0)
|
||||
lock_opts = Enum.find_value [:branch, :ref, :tag], &List.keyfind(opts, &1, 0)
|
||||
lock_opts = List.wrap(lock_opts)
|
||||
if opts[:submodules] do
|
||||
lock_opts ++ [submodules: true]
|
||||
|
||||
@@ -14,11 +14,7 @@ defmodule Mix.SCM.Path do
|
||||
cond do
|
||||
raw = opts[:path] ->
|
||||
Keyword.put opts, :dest, Path.expand(raw)
|
||||
opts[:umbrella] || opts[:in_umbrella] ->
|
||||
if opts[:umbrella] do
|
||||
Mix.shell.info ":umbrella option in path dependency is deprecated in favor of :in_umbrella"
|
||||
end
|
||||
|
||||
opts[:in_umbrella] ->
|
||||
path = "../#{app}"
|
||||
|
||||
opts
|
||||
|
||||
+11
-13
@@ -1,11 +1,9 @@
|
||||
defmodule Mix.Server do
|
||||
@moduledoc false
|
||||
|
||||
alias :ordsets, as: Ordset
|
||||
use GenServer.Behaviour
|
||||
|
||||
defrecord Config, tasks: Ordset.new, projects: [], mixfile: [],
|
||||
shell: Mix.Shell.IO, scm: Ordset.new, env: nil, post_config: []
|
||||
defrecord Config, tasks: :ordsets.new, projects: [], mixfile: [],
|
||||
shell: Mix.Shell.IO, scm: :ordsets.new, env: nil, post_config: []
|
||||
|
||||
defrecord Project, name: nil, config: nil, rec_enabled?: true, io_done: false
|
||||
|
||||
@@ -48,11 +46,11 @@ defmodule Mix.Server do
|
||||
end
|
||||
|
||||
def handle_call(:clear_tasks, _from, config) do
|
||||
{ :reply, config.tasks, config.tasks(Ordset.new) }
|
||||
{ :reply, config.tasks, config.tasks(:ordsets.new) }
|
||||
end
|
||||
|
||||
def handle_call({ :has_task?, task, app }, _from, config) do
|
||||
{ :reply, Ordset.is_element({task, app}, config.tasks), config }
|
||||
{ :reply, :ordsets.is_element({task, app}, config.tasks), config }
|
||||
end
|
||||
|
||||
def handle_call(:pop_project, _from, config) do
|
||||
@@ -102,35 +100,35 @@ defmodule Mix.Server do
|
||||
end
|
||||
|
||||
def handle_cast({ :add_task, task, app }, config) do
|
||||
{ :noreply, config.update_tasks Ordset.add_element({task, app}, &1) }
|
||||
{ :noreply, config.update_tasks &:ordsets.add_element({task, app}, &1) }
|
||||
end
|
||||
|
||||
def handle_cast({ :delete_task, task, app }, config) do
|
||||
{ :noreply, config.update_tasks Ordset.del_element({task, app}, &1) }
|
||||
{ :noreply, config.update_tasks &:ordsets.del_element({task, app}, &1) }
|
||||
end
|
||||
|
||||
def handle_cast({ :delete_task, task }, config) do
|
||||
{ :noreply, config.update_tasks Ordset.filter(fn {t, _} -> t != task end, &1) }
|
||||
{ :noreply, config.update_tasks &:ordsets.filter(fn {t, _} -> t != task end, &1) }
|
||||
end
|
||||
|
||||
def handle_cast({ :push_project, name, conf }, config) do
|
||||
conf = Keyword.merge(conf, config.post_config)
|
||||
project = Project[name: name, config: conf]
|
||||
config = config.post_config([])
|
||||
.update_projects([project|&1])
|
||||
.update_projects(&[project|&1])
|
||||
{ :noreply, config }
|
||||
end
|
||||
|
||||
def handle_cast({ :post_config, value }, config) do
|
||||
{ :noreply, config.update_post_config(&1 |> Keyword.merge(value)) }
|
||||
{ :noreply, config.update_post_config(&Keyword.merge(&1, value)) }
|
||||
end
|
||||
|
||||
def handle_cast({ :add_scm, mod }, config) do
|
||||
{ :noreply, config.update_scm Ordset.add_element(mod, &1) }
|
||||
{ :noreply, config.update_scm &:ordsets.add_element(mod, &1) }
|
||||
end
|
||||
|
||||
def handle_cast({ :mixfile_cache, app, new }, config) do
|
||||
{ :noreply, config.update_mixfile(&1 |> Keyword.merge([{ app, new }])) }
|
||||
{ :noreply, config.update_mixfile(&Keyword.merge(&1, [{ app, new }])) }
|
||||
end
|
||||
|
||||
def handle_cast(:clear_mixfile_cache, config) do
|
||||
|
||||
@@ -12,7 +12,7 @@ defmodule Mix.Shell.IO do
|
||||
"""
|
||||
def cmd(command) do
|
||||
put_app
|
||||
Mix.Shell.cmd(command, IO.write(&1))
|
||||
Mix.Shell.cmd(command, &IO.write(&1))
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
||||
@@ -23,7 +23,7 @@ defmodule Mix.Shell.Process do
|
||||
|
||||
## Examples
|
||||
|
||||
flush IO.inspect(&1)
|
||||
flush &IO.inspect(&1)
|
||||
|
||||
"""
|
||||
def flush(callback // fn(x) -> x end) do
|
||||
|
||||
@@ -17,7 +17,7 @@ defmodule Mix.Task do
|
||||
defmacro __using__(_opts) do
|
||||
quote do
|
||||
Enum.each [:shortdoc, :hidden, :recursive],
|
||||
Module.register_attribute(__MODULE__, &1, persist: true)
|
||||
&Module.register_attribute(__MODULE__, &1, persist: true)
|
||||
|
||||
@behaviour Mix.Task
|
||||
end
|
||||
@@ -34,7 +34,7 @@ defmodule Mix.Task do
|
||||
def load_tasks(paths) do
|
||||
Enum.reduce(paths, [], fn(path, matches) ->
|
||||
{ :ok, files } = :erl_prim_loader.list_dir(path |> to_char_list)
|
||||
Enum.reduce(files, matches, match_tasks(&1, &2))
|
||||
Enum.reduce(files, matches, &match_tasks/2)
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ defmodule Mix.Tasks.Clean do
|
||||
manifests = Mix.Tasks.Compile.manifests
|
||||
Enum.each(manifests, fn(manifest) ->
|
||||
Enum.each Mix.Utils.read_manifest(manifest),
|
||||
&1 |> String.split("\t") |> hd |> File.rm
|
||||
&(&1 |> String.split("\t") |> hd |> File.rm)
|
||||
File.rm(manifest)
|
||||
end)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user