Skip to content

Commit 68ab657

Browse files
authored
Merge pull request #57 from esl/support-options
Support options in mix gradient
2 parents 8d7e919 + 7c3ea81 commit 68ab657

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+509
-111
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,8 @@ gradient-*.tar
2525
# Temporary files, for example, from tests.
2626
/tmp/
2727

28+
# Beam files compiled from examples
29+
test/examples/_build/
30+
31+
# MacOS DS_Store
2832
.DS_Store

lib/gradient.ex

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ defmodule Gradient do
55
Options:
66
- `app_path` - Path to the app that contains file with code (for umbrella apps).
77
- `code_path` - Path to a file with code (e.g. when beam was compiled without project).
8+
- `no_gradualizer_check` - Skip Gradualizer checks if true.
9+
- `no_ex_check` - Skip Elixir checks if true.
10+
- `no_specify` - Skip AST specifying if true.
811
"""
912

1013
alias Gradient.ElixirFileUtils
@@ -20,13 +23,10 @@ defmodule Gradient do
2023
def type_check_file(file, opts \\ []) do
2124
opts = Keyword.put(opts, :return_errors, true)
2225

23-
with {:ok, forms} <- ElixirFileUtils.get_forms_from_beam(file) do
24-
forms =
25-
forms
26-
|> put_code_path(opts)
27-
|> AstSpecifier.specify()
26+
with {:ok, forms} <- ElixirFileUtils.get_forms(file) do
27+
forms = maybe_specify_forms(forms, opts)
2828

29-
case ElixirChecker.check(forms, opts) ++ :gradualizer.type_check_forms(forms, opts) do
29+
case maybe_gradient_check(forms, opts) ++ maybe_gradualizer_check(forms, opts) do
3030
[] ->
3131
:ok
3232

@@ -42,24 +42,56 @@ defmodule Gradient do
4242
end
4343
end
4444

45+
defp maybe_gradualizer_check(forms, opts) do
46+
unless opts[:no_gradualizer_check] do
47+
try do
48+
:gradualizer.type_check_forms(forms, opts)
49+
catch
50+
err ->
51+
{:attribute, _, :file, {path, _}} = hd(forms)
52+
[{path, err}]
53+
end
54+
else
55+
[]
56+
end
57+
end
58+
59+
defp maybe_gradient_check(forms, opts) do
60+
unless opts[:no_ex_check] do
61+
ElixirChecker.check(forms, opts)
62+
else
63+
[]
64+
end
65+
end
66+
67+
defp maybe_specify_forms(forms, opts) do
68+
unless opts[:no_specify] do
69+
forms
70+
|> put_code_path(opts)
71+
|> AstSpecifier.specify()
72+
else
73+
forms
74+
end
75+
end
76+
4577
defp put_code_path(forms, opts) do
46-
case Keyword.fetch(opts, :code_path) do
47-
{:ok, path} ->
48-
[{:attribute, 1, :file, {path, 1}} | tl(forms)]
78+
case opts[:code_path] do
79+
nil ->
80+
case opts[:app_path] do
81+
nil ->
82+
forms
4983

50-
:error ->
51-
case Keyword.fetch(opts, :app_path) do
52-
{:ok, app_path} ->
84+
app_path ->
5385
{:attribute, anno, :file, {path, line}} = hd(forms)
5486

5587
[
5688
{:attribute, anno, :file, {String.to_charlist(app_path) ++ '/' ++ path, line}}
5789
| tl(forms)
5890
]
59-
60-
:error ->
61-
forms
6291
end
92+
93+
path ->
94+
[{:attribute, 1, :file, {path, 1}} | tl(forms)]
6395
end
6496
end
6597
end

lib/gradient/doublespec.ex

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
defmodule Doublespec do
2+
@spec convert(integer()) :: float()
3+
@spec convert(atom()) :: binary()
4+
def convert(int) when is_integer(int), do: int / 1
5+
def convert(atom) when is_atom(atom), do: to_string(atom)
6+
7+
@spec convert(atom()) :: binary()
8+
def last_two(list) do
9+
[last, penultimate | _tail] = Enum.reverse(list)
10+
[penultimate, last]
11+
end
12+
13+
@spec last_two(atom()) :: atom()
14+
def last_three(:ok) do
15+
:ok
16+
end
17+
end

lib/gradient/elixir_file_utils.ex

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ defmodule Gradient.ElixirFileUtils do
55

66
alias Gradient.Types
77

8-
@type path() :: :file.filename() | String.t()
8+
@type path() :: :file.filename() | binary()
99

1010
@type abstract_forms() :: [:erl_parse.abstract_form() | :erl_parse.form_info()]
1111

@@ -20,9 +20,6 @@ defmodule Gradient.ElixirFileUtils do
2020
"""
2121
@spec get_forms_from_beam(path()) ::
2222
{:ok, abstract_forms()} | parsed_file_error()
23-
def get_forms_from_beam(path) when is_binary(path),
24-
do: get_forms_from_beam(String.to_charlist(path))
25-
2623
def get_forms_from_beam(path) do
2724
case :beam_lib.chunks(path, [:abstract_code]) do
2825
{:ok, {_module, [{:abstract_code, {:raw_abstract_v1, forms}}]}} ->
@@ -42,6 +39,36 @@ defmodule Gradient.ElixirFileUtils do
4239
end
4340
end
4441

42+
@spec get_forms_from_ex(binary()) ::
43+
{:ok, abstract_forms()} | parsed_file_error()
44+
def get_forms_from_ex(path) do
45+
# For compiling many files concurrently, see Kernel.ParallelCompiler.compile/2.
46+
if File.exists?(path) do
47+
[{_module, bin}] = Code.compile_file(path)
48+
get_forms_from_beam(bin)
49+
else
50+
{:file_not_found, path}
51+
end
52+
end
53+
54+
def get_forms(path) do
55+
case Path.extname(path) do
56+
".beam" ->
57+
path
58+
|> to_charlist()
59+
|> get_forms_from_beam()
60+
61+
".ex" ->
62+
get_forms_from_ex(path)
63+
64+
_ ->
65+
[path]
66+
|> Module.concat()
67+
|> :code.which()
68+
|> get_forms_from_beam()
69+
end
70+
end
71+
4572
@spec load_tokens([:erl_parse.abstract_form()]) :: Types.tokens()
4673
def load_tokens(forms) do
4774
with [{:attribute, _, :file, {path, _}} | _] <- forms,

lib/gradient/elixir_fmt.ex

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ defmodule Gradient.ElixirFmt do
1313
1414
- `ex_fmt_type_fun`: function to pretty print an type AST in Elixir `(abstract_type() -> iodata())`.
1515
16+
- `{fancy, boolean()}`: do not use fancy error messages, default: true
17+
1618
- Gradualizer options, but some of them are overwritten by Gradient.
1719
"""
1820
@behaviour Gradient.Fmt
@@ -197,7 +199,7 @@ defmodule Gradient.ElixirFmt do
197199
def format_location(expression, fmt_type, opts \\ []) do
198200
case Keyword.get(opts, :fmt_location, :verbose) do
199201
^fmt_type -> FmtLib.format_location(expression, fmt_type)
200-
:verbose -> ""
202+
_ -> ""
201203
end
202204
end
203205

@@ -244,16 +246,23 @@ defmodule Gradient.ElixirFmt do
244246
@spec try_highlight_in_context(Types.abstract_expr(), options()) ::
245247
{:ok, iodata()} | {:error, term()}
246248
def try_highlight_in_context(expression, opts) do
247-
forms = Keyword.get(opts, :forms)
248-
249-
with :ok <- has_location?(expression),
250-
{:ok, path} <- get_ex_file_path(forms),
249+
with :ok <- print_fancy?(opts),
250+
:ok <- has_location?(expression),
251+
{:ok, path} <- get_ex_file_path(opts[:forms]),
251252
{:ok, code} <- File.read(path) do
252253
code_lines = String.split(code, ~r/\R/)
253254
{:ok, highlight_in_context(expression, code_lines, opts)}
254255
end
255256
end
256257

258+
def print_fancy?(opts) do
259+
if Keyword.get(opts, :fancy, true) do
260+
:ok
261+
else
262+
{:error, "The fancy mode is turn off"}
263+
end
264+
end
265+
257266
def has_location?(expression) do
258267
if elem(expression, 1) == 0 do
259268
{:error, "The location is missing in the expression"}

0 commit comments

Comments
 (0)