This introduces five new metadata nodes:
* `do` - contains metadata about do location in a function call with
`do/end` blocks
* `end` - contains metadata about end location in a function call with
`do/end` blocks
* `closing` - contains metadata about the closing pair, such as a `}`
in a tuple or in a map, or such as the closing `)` in a function call
with parens
* `eol` - is set to true when the opening pair, such as `{` or `(`, are
followed by the end of the line
* `delimiter` - contains the opening for sigils (such as `"{"`, `"/"`,
etc)
This commit introduces an optional callback `inspect/2`
that allows custom calendars to define a implementation
for the `Inspect` protocol for `Time`, `Date`,
`DateTime`, and `NaiveDateTime`.
Regexes are precompiled to binary values during code compilation.
These binary values may be incompatible between different PCRE versions
and OS endianness. This makes code less portable to achieve slight
performance improvement.
PCRE version and endianness are parts of Regex structure and may be
checked in runtime.
This commit adds this check and makes regex execution fall back
to binary matching if versions are incompatible.
This slightly reduces performance (around 5% for simple regexes) for
compatible versions because there is an additional version read.
Also for incompatible versions it's as fast as binary matching.
Let's see some use cases. First we configure IEx to use our custom
function:
iex> IEx.configure(inspect: [inspect_fun: &Pretty.inspect/2])
:ok
1. Pretty-printing integers
iex> :math.pow(2, 32) |> trunc()
4_294_967_296
2. Inspect tuples as records
iex> {:foo, :bar}
{:foo, :bar}
iex> {:person, "Alice", 30}
#person(name: "Alice", age: 30)
3. Overwrite struct's Inspect implementation
iex> URI.parse("https://elixir-lang.org")
#URI<https://elixir-lang.org>
---
Code for the `Pretty.inspect/2` function:
defmodule Pretty do
def inspect(integer, opts) when is_integer(integer) do
PrettyIntegers.inspect(integer, opts)
end
def inspect(tuple, opts) when is_tuple(tuple) do
records = %{
{:person, 2} => [:name, :age]
}
PrettyTuples.inspect(tuple, records, opts)
end
def inspect(%URI{} = uri, _opts) do
Inspect.Algebra.concat(["#URI<", URI.to_string(uri), ">"])
end
def inspect(term, opts) do
Inspect.inspect(term, opts)
end
end
defmodule PrettyIntegers do
def inspect(term, %Inspect.Opts{base: :decimal}) do
pretty_decimal(term, "_")
end
def inspect(term, %Inspect.Opts{base: base}) do
Integer.to_string(term, base_to_value(base))
|> prepend_prefix(base)
end
def pretty_decimal(n, separator) when n < 0 do
"-" <> pretty_decimal(abs(n), separator)
end
def pretty_decimal(n, separator) when n >= 1000 do
left = div(n, 1_000)
right = rem(n, 1_000)
right_str = :io_lib.format("~3..0B", [right]) |> IO.iodata_to_binary
pretty_decimal(left, separator) <> separator <> right_str
end
def pretty_decimal(n, _) do
Integer.to_string(n)
end
defp base_to_value(base) do
case base do
:binary -> 2
:octal -> 8
:hex -> 16
end
end
defp prepend_prefix(value, base) do
prefix = case base do
:binary -> "0b"
:octal -> "0o"
:hex -> "0x"
end
prefix <> value
end
end
defmodule PrettyTuples do
import Inspect.Algebra
def inspect({}, _records, opts), do: inspect_tuple([], opts)
def inspect(tuple, records, opts) do
list = Tuple.to_list(tuple)
[record_name | values] = list
case record_fields(records, record_name, tuple) do
{:ok, fields} ->
inspect_record(record_name, fields, values, opts)
_ ->
inspect_tuple(list, opts)
end
end
defp record_fields(records, record_name, tuple) do
fields_size = tuple_size(tuple) - 1
Map.fetch(records, {record_name, fields_size})
end
defp inspect_tuple(list, opts) do
inspect("{", list, "}", &to_doc/2, :flex, opts)
end
defp inspect_record(record_name, fields, values, opts) do
kwlist = Enum.zip(fields, values)
inspect("##{record_name}(", kwlist, ")", &Inspect.List.keyword/2, :strict, opts)
end
defp inspect(open, list, close, fun, break, opts) do
open = color(open, :tuple, opts)
sep = color(",", :tuple, opts)
close = color(close, :tuple, opts)
container_opts = [separator: sep, break: break]
container_doc(open, list, close, opts, fun, container_opts)
end
end
"Keyword.{take|drop|split}" don't offer support for an Enumerable of
keys to take/drop/split based on. However, the "Map" counterparts do.
With this commit, we hard-deprecate support for Enumerables of keys in
"Map.{take|drop|split}". Only lists of keys are allowed.
This fixes an issue where a registry's PIDPartition table would gain an
entry each time after the first call to Registry.register on a unique
registry.
We also guarantee unregistered works generally with "tricky keys".
Closes#8611.
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
The previous implementation was generating an unnecessary
intermediate function when using `&super/arity`.
In addition, capture expressions using `super` with sequential
value placeholders (`&n`) were not optimized like other functions.
For example, the capture expression `&foo(&1, &2, &3)` is
optimized to `&foo/3` because the value placeholders (`&n`) are
sequential `&1, &2, &3`.
The current approach discards the __aliases__ tuple and
do a simple check that allows some strange behavior like:
```elixir
iex(1)> alias :lists, as: :"Elixir.foobar"
:lists
iex(2)> :"Elixir.foobar"
:"Elixir.foobar"
iex(3)> alias :"Elixir.foobar", as: Baz
:"Elixir.foobar"
iex(3)> Baz
:lists
```