Skip to content

Commit 9f26b1c

Browse files
committed
wip
1 parent abc79ec commit 9f26b1c

File tree

3 files changed

+165
-41
lines changed

3 files changed

+165
-41
lines changed

lib/gradient/ast_specifier.ex

Lines changed: 86 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ defmodule Gradient.AstSpecifier do
2121
# Expressions that could have missing location
2222
@lineless_forms [:atom, :char, :float, :integer, :string, :bin, :cons, :tuple]
2323

24+
@ensure_location [:var | @lineless_forms]
25+
2426
# Api
2527

2628
@doc """
@@ -255,7 +257,7 @@ defmodule Gradient.AstSpecifier do
255257
|> get_tuple(opts)
256258
|> case do
257259
{:tuple, tokens} ->
258-
{anno, opts} = update_line_from_tokens(tokens, anno, opts)
260+
{anno, opts} = update_loc_from_tokens(tokens, anno, opts)
259261
# drop a token that begins tuple
260262
tokens = drop_tokens_while(tokens, fn t -> elem(t, 0) in [:"{"] end)
261263

@@ -323,17 +325,38 @@ defmodule Gradient.AstSpecifier do
323325
|> pass_tokens(tokens)
324326
end
325327

326-
def mapper({:call, anno, name, args}, tokens, opts) do
328+
def mapper({:call, anno, name, args} = c, tokens, opts) do
327329
# anno has correct line
328-
{:ok, _, anno, opts, _} = get_line(anno, opts)
330+
{:ok, _, _, opts, _} = get_line(anno, opts)
329331
name = remote_mapper(name)
330332

333+
{{:call, anno, _, _}, _tokens} = specify_line(c, tokens, opts)
334+
335+
anno =
336+
if is_integer(anno) do
337+
anno
338+
else
339+
set_end_location(anno, get_closing_paren_loc(tokens))
340+
end
341+
331342
{args, tokens} = call_args_mapper(args, tokens, name, opts)
332343

333344
{:call, anno, name, args}
334345
|> pass_tokens(tokens)
335346
end
336347

348+
def set_end_location(anno, :undefined), do: anno
349+
# def set_end_location(anno, location) when is_integer(anno) do
350+
# [location: anno, end_location: location]
351+
# end
352+
def set_end_location(anno, location) when is_tuple(anno) do
353+
[location: anno, end_location: location]
354+
end
355+
356+
def set_end_location(anno, location) do
357+
Keyword.put(anno, :end_location, location)
358+
end
359+
337360
def mapper({:op, anno, op, left, right}, tokens, opts) do
338361
# anno has correct line
339362
{:ok, _, anno, opts, _} = get_line(anno, opts)
@@ -383,7 +406,7 @@ defmodule Gradient.AstSpecifier do
383406
end
384407

385408
def mapper({type, anno, value}, tokens, opts)
386-
when type in @lineless_forms do
409+
when type in @ensure_location do
387410
{:ok, line} = Keyword.fetch(opts, :line)
388411
anno = :erl_anno.set_line(line, anno)
389412
anno = :erl_anno.set_generated(Keyword.get(opts, :generated, false), anno)
@@ -395,8 +418,7 @@ defmodule Gradient.AstSpecifier do
395418
def mapper(skip, tokens, _opts)
396419
when elem(skip, 0) in [
397420
:fun,
398-
:attribute,
399-
:var
421+
:attribute
400422
] do
401423
# NOTE fun - I skipped here checking &name/arity or &module.name/arity
402424
# skip forms that don't need analysis and do not display warning
@@ -543,7 +565,7 @@ defmodule Gradient.AstSpecifier do
543565
def cons_mapper({:cons, anno, value, tail}, tokens, opts) do
544566
{:ok, _, anno0, opts0, _} = get_line(anno, opts)
545567

546-
{anno, opts} = update_line_from_tokens(tokens, anno0, opts0)
568+
{anno, opts} = update_loc_from_tokens(tokens, anno0, opts0)
547569
# drop a token that begins list
548570
tokens = drop_tokens_while(tokens, fn t -> elem(t, 0) in [:"["] end)
549571

@@ -662,59 +684,90 @@ defmodule Gradient.AstSpecifier do
662684
l2 <= l1
663685
end
664686

687+
defp match_token_to_form({:identifier, {l1, _, _}, t_name}, {:var, l2, raw_name}) do
688+
l2 == l1 && String.contains?(to_string(raw_name), <<to_string(t_name)::binary, "@">>)
689+
end
690+
691+
defp match_token_to_form({:paren_identifier, {l1, _, _}, name1}, {:call, l2, name2, _}) do
692+
:erl_anno.line(l2) == l1 && match_remote_names(name1, name2)
693+
end
694+
665695
defp match_token_to_form(_, _) do
666696
false
667697
end
668698

699+
def match_remote_names(name, {:remote, _, _, {:atom, _, name}}), do: true
700+
def match_remote_names(name, {:atom, _, name}), do: true
701+
def match_remote_names(name, name), do: true
702+
def match_remote_names(_, _), do: false
703+
704+
defp literal_loc({line, col_start, _}, literal) do
705+
col_end = col_start + length(to_charlist(literal))
706+
[location: {line, col_start}, end_location: {line, col_end}]
707+
end
708+
669709
@spec take_loc_from_token(token(), form()) :: form()
670-
defp take_loc_from_token({:int, {line, _, _}, _}, {:integer, _, value}) do
671-
{:integer, line, value}
710+
defp take_loc_from_token({:int, loc, _}, {:integer, _, value}) do
711+
{:integer, literal_loc(loc, value), value}
672712
end
673713

674-
defp take_loc_from_token({:char, {line, _, _}, _}, {:integer, _, value}) do
675-
{:integer, line, value}
714+
defp take_loc_from_token({:char, loc, _}, {:integer, _, value}) do
715+
{:integer, literal_loc(loc, value), value}
676716
end
677717

678-
defp take_loc_from_token({:flt, {line, _, _}, _}, {:float, _, value}) do
679-
{:float, line, value}
718+
defp take_loc_from_token({:flt, loc, _}, {:float, _, value}) do
719+
{:float, literal_loc(loc, value), value}
680720
end
681721

682-
defp take_loc_from_token({:atom, {line, _, _}, _}, {:atom, _, value}) do
683-
{:atom, line, value}
722+
defp take_loc_from_token({:atom, loc, _}, {:atom, _, value}) do
723+
{:atom, literal_loc(loc, value), value}
684724
end
685725

686-
defp take_loc_from_token({:alias, {line, _, _}, _}, {:atom, _, value}) do
687-
{:atom, line, value}
726+
defp take_loc_from_token({:alias, loc, _}, {:atom, _, value}) do
727+
{:atom, literal_loc(loc, value), value}
688728
end
689729

690-
defp take_loc_from_token({:kw_identifier, {line, _, _}, _}, {:atom, _, value}) do
691-
{:atom, line, value}
730+
defp take_loc_from_token({:kw_identifier, loc, _}, {:atom, _, value}) do
731+
{:atom, literal_loc(loc, value), value}
692732
end
693733

694-
defp take_loc_from_token({:list_string, {l1, _, _}, _}, {:cons, _, _, _} = charlist) do
695-
charlist_set_loc(charlist, l1)
734+
defp take_loc_from_token({:list_string, loc, v}, {:cons, _, _, _} = charlist) do
735+
charlist_set_loc(charlist, literal_loc(loc, v))
696736
end
697737

698738
defp take_loc_from_token(
699-
{:bin_string, {l1, _, _}, _},
739+
{:bin_string, loc, _},
700740
{:bin, _, [{:bin_element, _, {:string, _, v2}, :default, :default}]}
701741
) do
702-
{:bin, l1, [{:bin_element, l1, {:string, l1, v2}, :default, :default}]}
742+
loc = literal_loc(loc, v2)
743+
{:bin, loc, [{:bin_element, loc, {:string, loc, v2}, :default, :default}]}
703744
end
704745

705746
defp take_loc_from_token({:str, _, _}, {:string, loc, v2}) do
747+
# FIXME missing col
706748
{:string, loc, v2}
707749
end
708750

709-
defp take_loc_from_token({true, {line, _, _}}, {:atom, _, true}) do
710-
{:atom, line, true}
751+
defp take_loc_from_token({true, loc}, {:atom, _, true}) do
752+
{:atom, literal_loc(loc, true), true}
711753
end
712754

713-
defp take_loc_from_token({false, {line, _, _}}, {:atom, _, false}) do
714-
{:atom, line, false}
755+
defp take_loc_from_token({false, loc}, {:atom, _, false}) do
756+
{:atom, literal_loc(loc, true), false}
715757
end
716758

717-
defp take_loc_from_token(_, _), do: nil
759+
defp take_loc_from_token({:identifier, loc, name}, {:var, _, raw_name}) do
760+
{:var, literal_loc(loc, name), raw_name}
761+
end
762+
763+
defp take_loc_from_token({:paren_identifier, {line, col, _}, _}, {:call, anno, name, args}) do
764+
{:call, :erl_anno.set_location({line, col}, anno), name, args}
765+
end
766+
767+
defp take_loc_from_token(t, e) do
768+
IO.puts(IO.ANSI.format([:red, "Cannot take loc from token - #{inspect(t)}, #{inspect(e)}"]))
769+
nil
770+
end
718771

719772
def cons_to_charlist({nil, _}), do: []
720773

@@ -728,12 +781,12 @@ defmodule Gradient.AstSpecifier do
728781

729782
def charlist_set_loc({nil, _}, loc), do: {nil, loc}
730783

731-
def update_line_from_tokens([token | _], anno, opts) do
732-
line = get_line_from_token(token)
733-
{:erl_anno.set_line(line, anno), Keyword.put(opts, :line, line)}
784+
def update_loc_from_tokens([token | _], anno, opts) do
785+
{line, col} = get_loc_from_token(token)
786+
{:erl_anno.set_location({line, col}, anno), Keyword.put(opts, :line, line)}
734787
end
735788

736-
def update_line_from_tokens(_, anno, opts) do
789+
def update_loc_from_tokens(_, anno, opts) do
737790
{anno, opts}
738791
end
739792

@@ -763,7 +816,7 @@ defmodule Gradient.AstSpecifier do
763816
end
764817

765818
defp set_form_end_line(opts, form, forms) do
766-
if elem(form, 0) not in [:bin, :cons] do
819+
if elem(form, 0) not in [:bin] do
767820
set_form_end_line_(opts, form, forms)
768821
else
769822
opts

lib/gradient/elixir_fmt.ex

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -306,12 +306,12 @@ defmodule Gradient.ElixirFmt do
306306

307307
@spec highlight_in_context(tuple(), [String.t()], options()) :: iodata()
308308
def highlight_in_context(expression, context, opts) do
309-
line = elem(expression, 1)
309+
anno = elem(expression, 1)
310310

311311
context
312312
|> Enum.with_index(1)
313-
|> filter_context(line, 2)
314-
|> underscore_line(line, opts)
313+
|> filter_context(anno, 2)
314+
|> underscore_line(anno, opts)
315315
|> Enum.join("\n")
316316
end
317317

@@ -322,25 +322,45 @@ defmodule Gradient.ElixirFmt do
322322
Enum.filter(lines, fn {_, number} -> number in range end)
323323
end
324324

325-
def underscore_line(lines, line, opts) do
325+
def end_location(anno) when is_list(anno) do
326+
Keyword.get(anno, :end_location, :undefined)
327+
end
328+
def end_location(_anno) do
329+
:undefined
330+
end
331+
332+
def underscore_line(lines, anno, opts) do
333+
line = :erl_anno.line(anno)
334+
column = :erl_anno.column(anno)
335+
IO.inspect(column, label: "COLUMN")
336+
endl = end_location(anno)
337+
IO.inspect(endl, label: "END LOCATION")
338+
326339
Enum.map(lines, fn {str, n} ->
327340
if(n == line) do
328341
colors = get_colors_with_default(opts)
329342
{:ok, use_colors} = Keyword.fetch(colors, :use_colors)
330343
{:ok, color} = Keyword.fetch(colors, :underscored_line)
331-
line_str = to_string(n) <> " " <> str
344+
{bef, aft} = split_at_col(str, column)
345+
indent = to_string(n) <> " " <> bef
332346

333347
[
334-
IO.ANSI.underline(),
335-
IO.ANSI.format_fragment([color, line_str], use_colors),
336-
IO.ANSI.reset()
348+
indent,
349+
[
350+
IO.ANSI.underline(),
351+
IO.ANSI.format_fragment([color, aft], use_colors),
352+
IO.ANSI.reset()
353+
]
337354
]
338355
else
339356
to_string(n) <> " " <> str
340357
end
341358
end)
342359
end
343360

361+
def split_at_col(str, col) when is_integer(col), do: String.split_at(str, col - 1)
362+
def split_at_col(str, _), do: {"", str}
363+
344364
def get_ex_file_path([{:attribute, 1, :file, {path, 1}} | _]), do: {:ok, path}
345365
def get_ex_file_path(_), do: {:error, :not_found}
346366

lib/gradient/tokens.ex

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ defmodule Gradient.Tokens do
117117
end)
118118
end
119119

120+
@doc """
121+
Get location from token.
122+
"""
123+
@spec get_loc_from_token(T.token()) :: {integer(), integer()}
124+
def get_loc_from_token(token), do: {elem(elem(token, 1), 0), elem(elem(token, 1), 1)}
125+
120126
@doc """
121127
Get line from token.
122128
"""
@@ -159,8 +165,53 @@ defmodule Gradient.Tokens do
159165
|> Enum.concat()
160166
end
161167

168+
@spec select_tokens_in_paren(T.tokens(), T.line()) :: T.tokens()
169+
def select_tokens_in_paren(tokens, line) do
170+
tokens
171+
|> drop_tokens_to_line(line)
172+
|> find_opening_paren()
173+
|> find_closing_paren()
174+
end
175+
176+
def get_closing_paren_loc(tokens, line \\ 0) do
177+
case select_tokens_in_paren(tokens, line) do
178+
[] ->
179+
:undefined
180+
181+
list ->
182+
{line, col, _} = elem(List.last(list), 1)
183+
{line, col}
184+
end
185+
end
186+
162187
# Private
163188

189+
defp find_opening_paren(tokens) do
190+
drop_tokens_while(tokens, fn
191+
{:"(", _} -> false
192+
_ -> true
193+
end)
194+
end
195+
196+
defp find_closing_paren(tokens, init \\ 0)
197+
defp find_closing_paren([], _), do: []
198+
199+
defp find_closing_paren(tokens, init) do
200+
Enum.reduce_while(tokens, {[], init}, fn
201+
{:")", _} = t, {ts, 1} -> {:halt, [t | ts]}
202+
{:")", _} = t, {ts, open} when open > 1 -> {:cont, {[t | ts], open - 1}}
203+
{:"(", _} = t, {ts, open} -> {:cont, {[t | ts], open + 1}}
204+
t, {ts, open} -> {:cont, {[t | ts], open}}
205+
end)
206+
|> case do
207+
{_, _open} ->
208+
raise "Cannot find closing paren in given tokens"
209+
210+
list ->
211+
list
212+
end
213+
end
214+
164215
defp flatten_token(token) do
165216
case token do
166217
{:bin_string, _, [s]} = t when is_binary(s) ->

0 commit comments

Comments
 (0)