This is a large commit that does the following: * Removes backticks from messages and comments. All references to backticks have been replaced with double quotes everywhere the code is not interpreted as Markdown, (ie. anywhere outside documentation and markdown files, such as in code comments, or error messages). * Variables in Exception messages are printed using `inspect` The way no-matching error message are printed, have changed because now we use inspect for printing variables. The following file and their respective test have been changed: - lib/elixir/lib/exception.ex - lib/elixir/lib/inspect/algebra.ex - lib/elixir/test/elixir/inspect_test.exs - lib/ex_unit/test/ex_unit/formatter_test.exs - lib/elixir/test/elixir/exception_test.exs * Properly use Title Case for Mix, Git, Dializer * Use backticks when citing a command **************************************************** CONVENTION FOR RENAMING USING BACKTICKS AND QUOTES https://github.com/elixir-lang/elixir/pull/3697#issuecomment-138811747 1. Backticks should never be printed in error messages, neither be included anywhere where Markdown code is not interpreted as such. 2. Do not use single quotes anywhere. We should favor double quotes everywhere, to avoid confusion 3. If you want to format something in error messages, use inspect. For example, if you want to show the dependency name and that is an atom, instead of the dependency "foo", let's show the dependency :foo. Less noise and may click better 4. Similarly, if you want to show something with double quotes, call inspect, as it handles escaping as well as the quotes 5. Things like "--all" just add verbosity, we can definitely read --all without ambiguity. 6. When using switches with a single hyphen (such as "-o"), we can make it explicit in the text: e.g. "... give the switch -o when choosing ..." **************************************************** SHELL COMMANDS TO DETECT CODE BREAKING THE RULES * Detect values surrounded by single-quotes where we want double-quotes. # regular variables ag "'#{" # constants or ENV variables ag "'[A-Z][A-Z0-9_-]+" # switches ag -i "(?<!(\[))'--?[a-z0-9][a-z0-9_-]+'" # atoms ag -i "(?<!(\[))':[a-z0-9][a-z0-9_-]+'" # DETECT BACKTICKS OUTSIDE DOCS ag "^\s+(?<!(#))#[^#\r\n]*\`" --ignore "*.md" ag '^\s+["%].*`' --ignore "*.md" ag -s '(raise|Error)\b(?!(`|/)).*\`' * Spot mentions to running a command that is not using backticks: commands="elixir|elixirc|mix|iex|ex_doc|git|make|rebar|dialyzer|erl|rm|cd|mkdir|rmdir|ln|ls|pwd" actions="runs?|running|executes?|executing|types?|typing|enters?|entering|calls?|calling" ag '(?i)('${actions}')(?-i)\b[^`\r\n/]+[\ \t]+(?!(`))('${commands}')' ag '(?-i)\b(?!(`))('${commands}')[\ \t]+[^`\r\n/]+(?i)(commands?)' * Spot where a command is mentioned: #commands="erlang|elixir|eex|iex|ex_unit|ex_doc|logger|elixirc|git|make|rebar|dialyzer|erl|rm|cd|mkdir|rmdir|ln|ls|pwd" commands="mix|elixirc|git|make|rebar|dialyzer|erl|rm|cd|mkdir|rmdir|ln|ls|pwd" ag -s '(?<!(\`))(?<!(\.))(?<!(/))(?<!(:))(?<!(_))\b('${commands}')\b(?!(:))(?!(\?))(?!(_))(?!(-))(?!(\.))(?!(\`))(?!(/))(?!(>))' \ --ignore "*.erl" --ignore "*.yrl" --ignore "*.src"
118 lines
3.7 KiB
Elixir
118 lines
3.7 KiB
Elixir
defmodule Record.Extractor do
|
|
@moduledoc false
|
|
|
|
# Retrieve a record definition from an Erlang file using
|
|
# the same lookup as the *include* attribute from Erlang modules.
|
|
def extract(name, from: file) when is_binary(file) do
|
|
extract_record(name, from_file(file))
|
|
end
|
|
|
|
# Retrieve a record definition from an Erlang file using
|
|
# the same lookup as the *include_lib* attribute from Erlang modules.
|
|
def extract(name, from_lib: file) when is_binary(file) do
|
|
extract_record(name, from_lib_file(file))
|
|
end
|
|
|
|
# Retrieve all records definitions from an Erlang file using
|
|
# the same lookup as the *include* attribute from Erlang modules.
|
|
def extract_all(from: file) when is_binary(file) do
|
|
extract_all_records(from_file(file))
|
|
end
|
|
|
|
# Retrieve all records definitions from an Erlang file using
|
|
# the same lookup as the *include_lib* attribute from Erlang modules.
|
|
def extract_all(from_lib: file) when is_binary(file) do
|
|
extract_all_records(from_lib_file(file))
|
|
end
|
|
|
|
# Find file using the same lookup as the *include* attribute from Erlang modules.
|
|
defp from_file(file) do
|
|
file = String.to_char_list(file)
|
|
case :code.where_is_file(file) do
|
|
:non_existing -> file
|
|
realfile -> realfile
|
|
end
|
|
end
|
|
|
|
# Find file using the same lookup as the *include_lib* attribute from Erlang modules.
|
|
defp from_lib_file(file) do
|
|
[app|path] = :filename.split(String.to_char_list(file))
|
|
case :code.lib_dir(List.to_atom(app)) do
|
|
{:error, _} ->
|
|
raise ArgumentError, "lib file #{file} could not be found"
|
|
libpath ->
|
|
:filename.join([libpath|path])
|
|
end
|
|
end
|
|
|
|
# Retrieve the record with the given name from the given file
|
|
defp extract_record(name, file) do
|
|
form = read_file(file)
|
|
records = extract_records(form)
|
|
if record = List.keyfind(records, name, 0) do
|
|
parse_record(record, form)
|
|
else
|
|
raise ArgumentError, "no record #{name} found at #{file}"
|
|
end
|
|
end
|
|
|
|
# Retrieve all records from the given file
|
|
defp extract_all_records(file) do
|
|
form = read_file(file)
|
|
records = extract_records(form)
|
|
for rec = {name, _fields} <- records, do: {name, parse_record(rec, form)}
|
|
end
|
|
|
|
# Parse the given file and extract all existent records.
|
|
defp extract_records(form) do
|
|
for {:attribute, _, :record, record} <- form, do: record
|
|
end
|
|
|
|
# Read a file and return its abstract syntax form that also
|
|
# includes record but with macros and other attributes expanded,
|
|
# such as "-include(...)" and "-include_lib(...)". This is done
|
|
# by using Erlang's epp.
|
|
defp read_file(file) do
|
|
case :epp.parse_file(file, []) do
|
|
{:ok, form} ->
|
|
form
|
|
other ->
|
|
raise "error parsing file #{file}, got: #{inspect(other)}"
|
|
end
|
|
end
|
|
|
|
# Parse a tuple with name and fields and returns a
|
|
# list of tuples where the first element is the field
|
|
# and the second is its default value.
|
|
defp parse_record({_name, fields}, form) do
|
|
cons = List.foldr fields, {nil, 0}, fn f, acc ->
|
|
{:cons, 0, parse_field(f), acc}
|
|
end
|
|
eval_record(cons, form)
|
|
end
|
|
|
|
defp parse_field({:typed_record_field, record_field, _type}) do
|
|
parse_field(record_field)
|
|
end
|
|
|
|
defp parse_field({:record_field, _, key}) do
|
|
{:tuple, 0, [key, {:atom, 0, :undefined}]}
|
|
end
|
|
|
|
defp parse_field({:record_field, _, key, value}) do
|
|
{:tuple, 0, [key, value]}
|
|
end
|
|
|
|
defp eval_record(cons, form) do
|
|
form = form ++
|
|
[{:function, 0, :hello, 0, [
|
|
{:clause, 0, [], [], [cons]}]}]
|
|
|
|
{:function, 0, :hello, 0, [
|
|
{:clause, 0, [], [], [record_ast]}]} = :erl_expand_records.module(form, []) |> List.last
|
|
|
|
{:value, record, _} = :erl_eval.expr(record_ast, [])
|
|
record
|
|
end
|
|
end
|