From abc79ec7ab813107a33a644679ffed19db919a3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Wojtasik?= <przemyslaw.wojtasik@erlang-solutions.com> Date: Sat, 2 Apr 2022 21:32:49 +0200 Subject: [PATCH 1/4] Stop using logger --- config/config.exs | 9 --------- lib/gradient.ex | 4 +--- lib/gradient/ast_specifier.ex | 15 +++------------ 3 files changed, 4 insertions(+), 24 deletions(-) delete mode 100644 config/config.exs diff --git a/config/config.exs b/config/config.exs deleted file mode 100644 index d7659338..00000000 --- a/config/config.exs +++ /dev/null @@ -1,9 +0,0 @@ -import Config - -config :logger, - backends: [:console], - compile_time_purge_matching: [ - [level_lower_than: :error] - ] - -config :logger, :console, format: "[$level] $message\n" diff --git a/lib/gradient.ex b/lib/gradient.ex index e8bae2fc..623830d8 100644 --- a/lib/gradient.ex +++ b/lib/gradient.ex @@ -15,8 +15,6 @@ defmodule Gradient do alias Gradient.AstSpecifier alias Gradient.ElixirChecker - require Logger - @type options() :: [{:app_path, String.t()}, {:code_path, String.t()}] @spec type_check_file(String.t(), options()) :: :ok | :error @@ -44,7 +42,7 @@ defmodule Gradient do _ -> :ok end error -> - Logger.error("Can't load file - #{inspect(error)}") + IO.puts(IO.ANSI.format([:red, "Can't load file - #{inspect(error)}"])) :error end end diff --git a/lib/gradient/ast_specifier.ex b/lib/gradient/ast_specifier.ex index 585c2881..50db9820 100644 --- a/lib/gradient/ast_specifier.ex +++ b/lib/gradient/ast_specifier.ex @@ -9,8 +9,6 @@ defmodule Gradient.AstSpecifier do import Gradient.Tokens - require Logger - alias Gradient.Types @type token :: Types.token() @@ -398,14 +396,7 @@ defmodule Gradient.AstSpecifier do when elem(skip, 0) in [ :fun, :attribute, - :var, - nil, - :atom, - :char, - :float, - :integer, - :string, - :bin + :var ] do # NOTE fun - I skipped here checking &name/arity or &module.name/arity # skip forms that don't need analysis and do not display warning @@ -413,7 +404,7 @@ defmodule Gradient.AstSpecifier do end def mapper(form, tokens, _opts) do - Logger.warn("Not found mapper for #{inspect(form)}") + IO.puts(IO.ANSI.format([:yellow, "Not found mapper for #{inspect(form)}"])) pass_tokens(form, tokens) end @@ -512,7 +503,7 @@ defmodule Gradient.AstSpecifier do {[[g] | gs], ts} gs, {ags, ts} -> - Logger.error("Unsupported guards format #{inspect(gs)}") + IO.puts(IO.ANSI.format([:red, "Unsupported guards format #{inspect(gs)}"])) {gs ++ ags, ts} end) end From 9f26b1cfa5eea3c1d9848fc6e9a77db7b02642b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Wojtasik?= <przemyslaw.wojtasik@erlang-solutions.com> Date: Fri, 15 Apr 2022 09:33:44 +0200 Subject: [PATCH 2/4] wip --- lib/gradient/ast_specifier.ex | 119 ++++++++++++++++++++++++---------- lib/gradient/elixir_fmt.ex | 36 +++++++--- lib/gradient/tokens.ex | 51 +++++++++++++++ 3 files changed, 165 insertions(+), 41 deletions(-) diff --git a/lib/gradient/ast_specifier.ex b/lib/gradient/ast_specifier.ex index 50db9820..ef31ff69 100644 --- a/lib/gradient/ast_specifier.ex +++ b/lib/gradient/ast_specifier.ex @@ -21,6 +21,8 @@ defmodule Gradient.AstSpecifier do # Expressions that could have missing location @lineless_forms [:atom, :char, :float, :integer, :string, :bin, :cons, :tuple] + @ensure_location [:var | @lineless_forms] + # Api @doc """ @@ -255,7 +257,7 @@ defmodule Gradient.AstSpecifier do |> get_tuple(opts) |> case do {:tuple, tokens} -> - {anno, opts} = update_line_from_tokens(tokens, anno, opts) + {anno, opts} = update_loc_from_tokens(tokens, anno, opts) # drop a token that begins tuple tokens = drop_tokens_while(tokens, fn t -> elem(t, 0) in [:"{"] end) @@ -323,17 +325,38 @@ defmodule Gradient.AstSpecifier do |> pass_tokens(tokens) end - def mapper({:call, anno, name, args}, tokens, opts) do + def mapper({:call, anno, name, args} = c, tokens, opts) do # anno has correct line - {:ok, _, anno, opts, _} = get_line(anno, opts) + {:ok, _, _, opts, _} = get_line(anno, opts) name = remote_mapper(name) + {{:call, anno, _, _}, _tokens} = specify_line(c, tokens, opts) + + anno = + if is_integer(anno) do + anno + else + set_end_location(anno, get_closing_paren_loc(tokens)) + end + {args, tokens} = call_args_mapper(args, tokens, name, opts) {:call, anno, name, args} |> pass_tokens(tokens) end + def set_end_location(anno, :undefined), do: anno + # def set_end_location(anno, location) when is_integer(anno) do + # [location: anno, end_location: location] + # end + def set_end_location(anno, location) when is_tuple(anno) do + [location: anno, end_location: location] + end + + def set_end_location(anno, location) do + Keyword.put(anno, :end_location, location) + end + def mapper({:op, anno, op, left, right}, tokens, opts) do # anno has correct line {:ok, _, anno, opts, _} = get_line(anno, opts) @@ -383,7 +406,7 @@ defmodule Gradient.AstSpecifier do end def mapper({type, anno, value}, tokens, opts) - when type in @lineless_forms do + when type in @ensure_location do {:ok, line} = Keyword.fetch(opts, :line) anno = :erl_anno.set_line(line, anno) anno = :erl_anno.set_generated(Keyword.get(opts, :generated, false), anno) @@ -395,8 +418,7 @@ defmodule Gradient.AstSpecifier do def mapper(skip, tokens, _opts) when elem(skip, 0) in [ :fun, - :attribute, - :var + :attribute ] do # NOTE fun - I skipped here checking &name/arity or &module.name/arity # skip forms that don't need analysis and do not display warning @@ -543,7 +565,7 @@ defmodule Gradient.AstSpecifier do def cons_mapper({:cons, anno, value, tail}, tokens, opts) do {:ok, _, anno0, opts0, _} = get_line(anno, opts) - {anno, opts} = update_line_from_tokens(tokens, anno0, opts0) + {anno, opts} = update_loc_from_tokens(tokens, anno0, opts0) # drop a token that begins list tokens = drop_tokens_while(tokens, fn t -> elem(t, 0) in [:"["] end) @@ -662,59 +684,90 @@ defmodule Gradient.AstSpecifier do l2 <= l1 end + defp match_token_to_form({:identifier, {l1, _, _}, t_name}, {:var, l2, raw_name}) do + l2 == l1 && String.contains?(to_string(raw_name), <<to_string(t_name)::binary, "@">>) + end + + defp match_token_to_form({:paren_identifier, {l1, _, _}, name1}, {:call, l2, name2, _}) do + :erl_anno.line(l2) == l1 && match_remote_names(name1, name2) + end + defp match_token_to_form(_, _) do false end + def match_remote_names(name, {:remote, _, _, {:atom, _, name}}), do: true + def match_remote_names(name, {:atom, _, name}), do: true + def match_remote_names(name, name), do: true + def match_remote_names(_, _), do: false + + defp literal_loc({line, col_start, _}, literal) do + col_end = col_start + length(to_charlist(literal)) + [location: {line, col_start}, end_location: {line, col_end}] + end + @spec take_loc_from_token(token(), form()) :: form() - defp take_loc_from_token({:int, {line, _, _}, _}, {:integer, _, value}) do - {:integer, line, value} + defp take_loc_from_token({:int, loc, _}, {:integer, _, value}) do + {:integer, literal_loc(loc, value), value} end - defp take_loc_from_token({:char, {line, _, _}, _}, {:integer, _, value}) do - {:integer, line, value} + defp take_loc_from_token({:char, loc, _}, {:integer, _, value}) do + {:integer, literal_loc(loc, value), value} end - defp take_loc_from_token({:flt, {line, _, _}, _}, {:float, _, value}) do - {:float, line, value} + defp take_loc_from_token({:flt, loc, _}, {:float, _, value}) do + {:float, literal_loc(loc, value), value} end - defp take_loc_from_token({:atom, {line, _, _}, _}, {:atom, _, value}) do - {:atom, line, value} + defp take_loc_from_token({:atom, loc, _}, {:atom, _, value}) do + {:atom, literal_loc(loc, value), value} end - defp take_loc_from_token({:alias, {line, _, _}, _}, {:atom, _, value}) do - {:atom, line, value} + defp take_loc_from_token({:alias, loc, _}, {:atom, _, value}) do + {:atom, literal_loc(loc, value), value} end - defp take_loc_from_token({:kw_identifier, {line, _, _}, _}, {:atom, _, value}) do - {:atom, line, value} + defp take_loc_from_token({:kw_identifier, loc, _}, {:atom, _, value}) do + {:atom, literal_loc(loc, value), value} end - defp take_loc_from_token({:list_string, {l1, _, _}, _}, {:cons, _, _, _} = charlist) do - charlist_set_loc(charlist, l1) + defp take_loc_from_token({:list_string, loc, v}, {:cons, _, _, _} = charlist) do + charlist_set_loc(charlist, literal_loc(loc, v)) end defp take_loc_from_token( - {:bin_string, {l1, _, _}, _}, + {:bin_string, loc, _}, {:bin, _, [{:bin_element, _, {:string, _, v2}, :default, :default}]} ) do - {:bin, l1, [{:bin_element, l1, {:string, l1, v2}, :default, :default}]} + loc = literal_loc(loc, v2) + {:bin, loc, [{:bin_element, loc, {:string, loc, v2}, :default, :default}]} end defp take_loc_from_token({:str, _, _}, {:string, loc, v2}) do + # FIXME missing col {:string, loc, v2} end - defp take_loc_from_token({true, {line, _, _}}, {:atom, _, true}) do - {:atom, line, true} + defp take_loc_from_token({true, loc}, {:atom, _, true}) do + {:atom, literal_loc(loc, true), true} end - defp take_loc_from_token({false, {line, _, _}}, {:atom, _, false}) do - {:atom, line, false} + defp take_loc_from_token({false, loc}, {:atom, _, false}) do + {:atom, literal_loc(loc, true), false} end - defp take_loc_from_token(_, _), do: nil + defp take_loc_from_token({:identifier, loc, name}, {:var, _, raw_name}) do + {:var, literal_loc(loc, name), raw_name} + end + + defp take_loc_from_token({:paren_identifier, {line, col, _}, _}, {:call, anno, name, args}) do + {:call, :erl_anno.set_location({line, col}, anno), name, args} + end + + defp take_loc_from_token(t, e) do + IO.puts(IO.ANSI.format([:red, "Cannot take loc from token - #{inspect(t)}, #{inspect(e)}"])) + nil + end def cons_to_charlist({nil, _}), do: [] @@ -728,12 +781,12 @@ defmodule Gradient.AstSpecifier do def charlist_set_loc({nil, _}, loc), do: {nil, loc} - def update_line_from_tokens([token | _], anno, opts) do - line = get_line_from_token(token) - {:erl_anno.set_line(line, anno), Keyword.put(opts, :line, line)} + def update_loc_from_tokens([token | _], anno, opts) do + {line, col} = get_loc_from_token(token) + {:erl_anno.set_location({line, col}, anno), Keyword.put(opts, :line, line)} end - def update_line_from_tokens(_, anno, opts) do + def update_loc_from_tokens(_, anno, opts) do {anno, opts} end @@ -763,7 +816,7 @@ defmodule Gradient.AstSpecifier do end defp set_form_end_line(opts, form, forms) do - if elem(form, 0) not in [:bin, :cons] do + if elem(form, 0) not in [:bin] do set_form_end_line_(opts, form, forms) else opts diff --git a/lib/gradient/elixir_fmt.ex b/lib/gradient/elixir_fmt.ex index 7c20bfe4..91ee4b38 100644 --- a/lib/gradient/elixir_fmt.ex +++ b/lib/gradient/elixir_fmt.ex @@ -306,12 +306,12 @@ defmodule Gradient.ElixirFmt do @spec highlight_in_context(tuple(), [String.t()], options()) :: iodata() def highlight_in_context(expression, context, opts) do - line = elem(expression, 1) + anno = elem(expression, 1) context |> Enum.with_index(1) - |> filter_context(line, 2) - |> underscore_line(line, opts) + |> filter_context(anno, 2) + |> underscore_line(anno, opts) |> Enum.join("\n") end @@ -322,18 +322,35 @@ defmodule Gradient.ElixirFmt do Enum.filter(lines, fn {_, number} -> number in range end) end - def underscore_line(lines, line, opts) do + def end_location(anno) when is_list(anno) do + Keyword.get(anno, :end_location, :undefined) + end + def end_location(_anno) do + :undefined + end + + def underscore_line(lines, anno, opts) do + line = :erl_anno.line(anno) + column = :erl_anno.column(anno) + IO.inspect(column, label: "COLUMN") + endl = end_location(anno) + IO.inspect(endl, label: "END LOCATION") + Enum.map(lines, fn {str, n} -> if(n == line) do colors = get_colors_with_default(opts) {:ok, use_colors} = Keyword.fetch(colors, :use_colors) {:ok, color} = Keyword.fetch(colors, :underscored_line) - line_str = to_string(n) <> " " <> str + {bef, aft} = split_at_col(str, column) + indent = to_string(n) <> " " <> bef [ - IO.ANSI.underline(), - IO.ANSI.format_fragment([color, line_str], use_colors), - IO.ANSI.reset() + indent, + [ + IO.ANSI.underline(), + IO.ANSI.format_fragment([color, aft], use_colors), + IO.ANSI.reset() + ] ] else to_string(n) <> " " <> str @@ -341,6 +358,9 @@ defmodule Gradient.ElixirFmt do end) end + def split_at_col(str, col) when is_integer(col), do: String.split_at(str, col - 1) + def split_at_col(str, _), do: {"", str} + def get_ex_file_path([{:attribute, 1, :file, {path, 1}} | _]), do: {:ok, path} def get_ex_file_path(_), do: {:error, :not_found} diff --git a/lib/gradient/tokens.ex b/lib/gradient/tokens.ex index 42f99ce5..9d1ff14b 100644 --- a/lib/gradient/tokens.ex +++ b/lib/gradient/tokens.ex @@ -117,6 +117,12 @@ defmodule Gradient.Tokens do end) end + @doc """ + Get location from token. + """ + @spec get_loc_from_token(T.token()) :: {integer(), integer()} + def get_loc_from_token(token), do: {elem(elem(token, 1), 0), elem(elem(token, 1), 1)} + @doc """ Get line from token. """ @@ -159,8 +165,53 @@ defmodule Gradient.Tokens do |> Enum.concat() end + @spec select_tokens_in_paren(T.tokens(), T.line()) :: T.tokens() + def select_tokens_in_paren(tokens, line) do + tokens + |> drop_tokens_to_line(line) + |> find_opening_paren() + |> find_closing_paren() + end + + def get_closing_paren_loc(tokens, line \\ 0) do + case select_tokens_in_paren(tokens, line) do + [] -> + :undefined + + list -> + {line, col, _} = elem(List.last(list), 1) + {line, col} + end + end + # Private + defp find_opening_paren(tokens) do + drop_tokens_while(tokens, fn + {:"(", _} -> false + _ -> true + end) + end + + defp find_closing_paren(tokens, init \\ 0) + defp find_closing_paren([], _), do: [] + + defp find_closing_paren(tokens, init) do + Enum.reduce_while(tokens, {[], init}, fn + {:")", _} = t, {ts, 1} -> {:halt, [t | ts]} + {:")", _} = t, {ts, open} when open > 1 -> {:cont, {[t | ts], open - 1}} + {:"(", _} = t, {ts, open} -> {:cont, {[t | ts], open + 1}} + t, {ts, open} -> {:cont, {[t | ts], open}} + end) + |> case do + {_, _open} -> + raise "Cannot find closing paren in given tokens" + + list -> + list + end + end + defp flatten_token(token) do case token do {:bin_string, _, [s]} = t when is_binary(s) -> From 51e66f4ae4ebe533d00bc02bfbf13547e4a82715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Wojtasik?= <przemyslaw.wojtasik@erlang-solutions.com> Date: Fri, 15 Apr 2022 20:03:40 +0200 Subject: [PATCH 3/4] Make formatter able to highlight more than one line --- lib/gradient/anno.ex | 40 ++++++++++++++++ lib/gradient/elixir_fmt.ex | 94 +++++++++++++++++++++++++------------- 2 files changed, 101 insertions(+), 33 deletions(-) create mode 100644 lib/gradient/anno.ex diff --git a/lib/gradient/anno.ex b/lib/gradient/anno.ex new file mode 100644 index 00000000..c001bd8d --- /dev/null +++ b/lib/gradient/anno.ex @@ -0,0 +1,40 @@ +defmodule Gradient.Anno do + @type anno :: keyword() + @type location :: {non_neg_integer(), pos_integer()} + @type line :: non_neg_integer() + + @max_col 1000 + + @spec end_location(anno()) :: location() + def end_location(anno) when is_list(anno) do + case Keyword.fetch(anno, :end_location) do + {:ok, {line, col}} -> {abs_line(line(anno), line), col} + :error -> line(anno) + end + end + + def end_location(anno), do: {line(anno), @max_col} + + def end_line(anno) when is_list(anno) do + case Keyword.fetch(anno, :end_location) do + {:ok, {line, _}} -> abs_line(line(anno), line) + :error -> line(anno) + end + end + + def end_line(anno), do: line(anno) + + @spec line(anno()) :: line() + def line(anno), do: :erl_anno.line(:erl_anno.from_term(anno)) + + @spec location(anno()) :: location() + def location(anno) do + case :erl_anno.location(:erl_anno.from_term(anno)) do + {line, col} -> {line, col} + line -> {line, 1} + end + end + + def abs_line(startl, endl) when startl > endl, do: 2 * startl - endl + def abs_line(_, endl), do: endl +end diff --git a/lib/gradient/elixir_fmt.ex b/lib/gradient/elixir_fmt.ex index 91ee4b38..e3b1bfc1 100644 --- a/lib/gradient/elixir_fmt.ex +++ b/lib/gradient/elixir_fmt.ex @@ -23,6 +23,7 @@ defmodule Gradient.ElixirFmt do alias Gradient.ElixirType alias Gradient.ElixirExpr alias Gradient.Types + alias Gradient.Anno @type colors_opts() :: [ use_colors: boolean(), @@ -311,54 +312,81 @@ defmodule Gradient.ElixirFmt do context |> Enum.with_index(1) |> filter_context(anno, 2) - |> underscore_line(anno, opts) + |> maybe_underscore_lines(anno, opts) |> Enum.join("\n") end - def filter_context(lines, loc, ctx_size \\ 1) do - line = :erl_anno.line(loc) - range = (line - ctx_size)..(line + ctx_size) + def filter_context(lines, anno, ctx_size \\ 1) do + line = Anno.line(anno) + end_line = Anno.end_line(anno) + range = (line - ctx_size)..(end_line + ctx_size) Enum.filter(lines, fn {_, number} -> number in range end) end - def end_location(anno) when is_list(anno) do - Keyword.get(anno, :end_location, :undefined) - end - def end_location(_anno) do - :undefined - end - - def underscore_line(lines, anno, opts) do - line = :erl_anno.line(anno) - column = :erl_anno.column(anno) - IO.inspect(column, label: "COLUMN") - endl = end_location(anno) - IO.inspect(endl, label: "END LOCATION") + def maybe_underscore_lines(lines, anno, opts) do + Anno.location(anno) |> IO.inspect(label: "START LOC") + Anno.end_location(anno) |> IO.inspect(label: "END LOC") Enum.map(lines, fn {str, n} -> - if(n == line) do + if need_underscore?(n, anno) do colors = get_colors_with_default(opts) - {:ok, use_colors} = Keyword.fetch(colors, :use_colors) - {:ok, color} = Keyword.fetch(colors, :underscored_line) - {bef, aft} = split_at_col(str, column) - indent = to_string(n) <> " " <> bef - - [ - indent, - [ - IO.ANSI.underline(), - IO.ANSI.format_fragment([color, aft], use_colors), - IO.ANSI.reset() - ] - ] + underscore_line(str, n, anno, colors) else - to_string(n) <> " " <> str + [to_string(n), " ", str] end end) end - def split_at_col(str, col) when is_integer(col), do: String.split_at(str, col - 1) + def underscore_line(str, n, anno, colors) do + {start_line, start_col} = Anno.location(anno) + {end_line, end_col} = Anno.end_location(anno) + + case n do + l when l == start_line and l == end_line -> + {prefix, str} = split_at_col(str, start_col) + {str, suffix} = split_at_col(str, end_col - start_col) + [prefix, make_underscore(str, colors), suffix] + + ^start_line -> + {prefix, str} = split_at_col(str, start_col) + [prefix, make_underscore(str, colors)] + + ^end_line -> + {str, suffix} = split_at_col(str, end_col) + [indent, str] = separate_indent(str) + [indent, make_underscore(str, colors), suffix] + + _otherwise -> + separate_indent(str) + end + |> add_line_number(n) + end + + def add_line_number(iolist, n), do: [to_string(n), " ", iolist] + + def separate_indent(str) do + trim_str = String.trim(str) + indent = gen_indent(String.length(str) - String.length(trim_str)) + [indent, trim_str] + end + + def gen_indent(length), do: Stream.cycle(' ') |> Stream.take(length) |> Enum.to_list() + + def make_underscore(text, colors) do + {:ok, use_colors} = Keyword.fetch(colors, :use_colors) + {:ok, color} = Keyword.fetch(colors, :underscored_line) + + [ + IO.ANSI.underline(), + IO.ANSI.format_fragment([color, text], use_colors), + IO.ANSI.reset() + ] + end + + def need_underscore?(index, anno), do: index >= Anno.line(anno) && index <= Anno.end_line(anno) + + def split_at_col(str, col) when is_integer(col), do: String.split_at(str, col) def split_at_col(str, _), do: {"", str} def get_ex_file_path([{:attribute, 1, :file, {path, 1}} | _]), do: {:ok, path} From 4816ebe67cae16e9103398e43203492addb106a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Wojtasik?= <przemyslaw.wojtasik@erlang-solutions.com> Date: Fri, 20 May 2022 15:15:28 +0200 Subject: [PATCH 4/4] wip --- .tool-versions | 2 +- examples/simple_app/.tool-versions | 1 + examples/simple_app/lib/simple_app/pipe_op.ex | 29 +++++++++++++++++++ lib/gradient/elixir_fmt.ex | 1 + test/gradient/ast_specifier_test.exs | 22 +++++++------- test/gradient/elixir_fmt_test.exs | 2 +- 6 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 examples/simple_app/.tool-versions create mode 100644 examples/simple_app/lib/simple_app/pipe_op.ex diff --git a/.tool-versions b/.tool-versions index 24efada7..df042d56 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -elixir 1.12.3 +elixir 1.13.3 erlang 24.1 diff --git a/examples/simple_app/.tool-versions b/examples/simple_app/.tool-versions new file mode 100644 index 00000000..a5704253 --- /dev/null +++ b/examples/simple_app/.tool-versions @@ -0,0 +1 @@ +elixir 1.11.4 diff --git a/examples/simple_app/lib/simple_app/pipe_op.ex b/examples/simple_app/lib/simple_app/pipe_op.ex new file mode 100644 index 00000000..9c92e6c2 --- /dev/null +++ b/examples/simple_app/lib/simple_app/pipe_op.ex @@ -0,0 +1,29 @@ +defmodule SimpleApp.PipeOp do + + use Gradient.TypeAnnotation + + @spec int_inc(integer()) :: integer() + def int_inc(int) do + int + 1 + end + + def easy_pipe do + '1' + |> int_inc() + '1' + |> int_inc() + '1' + |> int_inc() + '1' + |> int_inc() + end + + def easy_pipe2 do + int_inc( + {%{a: 1, b: 2, c: 3}, + %{a: 1, b: 2, c: 3}, + %{a: 1, b: 2, c: 3}}) + + end + +end diff --git a/lib/gradient/elixir_fmt.ex b/lib/gradient/elixir_fmt.ex index e3b1bfc1..3932ee5b 100644 --- a/lib/gradient/elixir_fmt.ex +++ b/lib/gradient/elixir_fmt.ex @@ -308,6 +308,7 @@ defmodule Gradient.ElixirFmt do @spec highlight_in_context(tuple(), [String.t()], options()) :: iodata() def highlight_in_context(expression, context, opts) do anno = elem(expression, 1) + IO.inspect(expression) context |> Enum.with_index(1) diff --git a/test/gradient/ast_specifier_test.exs b/test/gradient/ast_specifier_test.exs index 0d47d76d..7242378a 100644 --- a/test/gradient/ast_specifier_test.exs +++ b/test/gradient/ast_specifier_test.exs @@ -37,7 +37,7 @@ defmodule Gradient.AstSpecifierTest do [block, inline | _] = AstSpecifier.run_mappers(ast, tokens) |> Enum.reverse() - assert {:function, 2, :int, 0, [{:clause, 2, [], [], [{:integer, 2, 1}]}]} = inline + assert {:function, 2, :int, 0, [{:clause, 2, [], [], [{:integer, [location: {2, 16}, end_location: {2, 17}], 1}]}]} = inline assert {:function, 4, :int_block, 0, [{:clause, 4, [], [], [{:integer, 5, 2}]}]} = block end @@ -46,7 +46,7 @@ defmodule Gradient.AstSpecifierTest do {tokens, ast} = load("Elixir.Basic.Float.beam", "basic/float.ex") [block, inline | _] = AstSpecifier.run_mappers(ast, tokens) |> Enum.reverse() - assert {:function, 2, :float, 0, [{:clause, 2, [], [], [{:float, 2, 0.12}]}]} = inline + assert {:function, 2, :float, 0, [{:clause, 2, [], [], [{:float, [location: {2, 18}, end_location: {2, 22}], 0.12}]}]} = inline assert {:function, 4, :float_block, 0, [{:clause, 4, [], [], [{:float, 5, 0.12}]}]} = block end @@ -56,9 +56,9 @@ defmodule Gradient.AstSpecifierTest do [block, inline | _] = AstSpecifier.run_mappers(ast, tokens) |> Enum.reverse() - assert {:function, 2, :atom, 0, [{:clause, 2, [], [], [{:atom, 2, :ok}]}]} = inline + assert {:function, 2, :atom, 0, [{:clause, 2, [], [], [{:atom, [location: {2, 17}, end_location: {2, 19}], :ok}]}]} = inline - assert {:function, 4, :atom_block, 0, [{:clause, 4, [], [], [{:atom, 5, :ok}]}]} = block + assert {:function, 4, :atom_block, 0, [{:clause, 4, [], [], [{:atom, [location: {5, 5}, end_location: {5, 7}], :ok}]}]} = block end test "char" do @@ -516,10 +516,10 @@ defmodule Gradient.AstSpecifierTest do {tokens, _} = example_data() opts = [end_line: -1] - assert {{:integer, 21, 12}, tokens} = + assert {{:integer, [location: {21, 9}, end_location: {21, 11}], 12}, tokens} = AstSpecifier.specify_line({:integer, 21, 12}, tokens, opts) - assert {{:integer, 22, 12}, _tokens} = + assert {{:integer, [location: {22, 5}, end_location: {22, 7}], 12}, _tokens} = AstSpecifier.specify_line({:integer, 20, 12}, tokens, opts) end @@ -1043,8 +1043,8 @@ defmodule Gradient.AstSpecifierTest do [ {:map, 7, [ - {:map_field_assoc, 7, {:atom, 7, :a}, {:integer, 7, 12}}, - {:map_field_assoc, 7, {:atom, 7, :b}, {:call, 7, {:atom, 7, :empty_map}, []}} + {:map_field_assoc, 7, {:atom, {:atom, [location: {7, 7}, end_location: {7, 8}], :a}, :a}, {:integer, {:atom, [location: {7, 10}, end_location: {7, 12}], :a}, 12}}, + {:map_field_assoc, 7, {:atom, [location: {7, 14}, end_location: {7, 15}], :b}, {:call, [location: {7, 17}, end_location: {7, 27}], {:atom, 7, :empty_map}, []}} ]} ]} ]} = test_map @@ -1633,13 +1633,13 @@ defmodule Gradient.AstSpecifierTest do {tokensB, astB} = load("Elixir.NestedModules.ModuleB.beam", "nested_modules.ex") {tokens, ast} = load("Elixir.NestedModules.beam", "nested_modules.ex") - assert {:function, 3, :name, 0, [{:clause, 3, [], [], [{:atom, 4, :module_a}]}]} = + assert {:function, 3, :name, 0, [{:clause, 3, [], [], [{:atom, [location: {4, 7}, end_location: {4, 15}], :module_a}]}]} = List.last(AstSpecifier.run_mappers(astA, tokensA)) - assert {:function, 9, :name, 0, [{:clause, 9, [], [], [{:atom, 10, :module_b}]}]} = + assert {:function, 9, :name, 0, [{:clause, 9, [], [], [{:atom, [location: {10, 7}, end_location: {10, 15}], :module_b}]}]} = List.last(AstSpecifier.run_mappers(astB, tokensB)) - assert {:function, 14, :name, 0, [{:clause, 14, [], [], [{:atom, 15, :module}]}]} = + assert {:function, 14, :name, 0, [{:clause, 14, [], [], [{:atom, [location: {15, 5}, end_location: {15, 11}], :module}]}]} = List.last(AstSpecifier.run_mappers(ast, tokens)) end diff --git a/test/gradient/elixir_fmt_test.exs b/test/gradient/elixir_fmt_test.exs index b70f0a5e..551d57a1 100644 --- a/test/gradient/elixir_fmt_test.exs +++ b/test/gradient/elixir_fmt_test.exs @@ -21,7 +21,7 @@ defmodule Gradient.ElixirFmtTest do res = ElixirFmt.try_highlight_in_context(expression, opts) expected = - {:ok, "29 def bool_id(x) do\n30 _x = 13\n\e[4m\e[31m31 12\e[0m\n32 end\n33 "} + {:ok, "29 def bool_id(x) do\n30 _x = 13\n31\e[4m\e[31m 12\e[0m\n32 end\n33 "} assert res == expected end