Skip to content

Commit a8b2432

Browse files
committed
Add support for location specifying in pipe operator
1 parent e0c47ad commit a8b2432

File tree

2 files changed

+40
-5
lines changed

2 files changed

+40
-5
lines changed

lib/gradient/ast_specifier.ex

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ defmodule Gradient.AstSpecifier do
3737
- binary [X]
3838
- maps [X]
3939
- struct [X]
40-
- pipe [ ] TODO decide how to search for line in reversed form order
40+
- pipe [X] TODO decide how to search for line in reversed form order
4141
- range [X]
4242
- receive [X]
4343
- record [X]
@@ -55,6 +55,10 @@ defmodule Gradient.AstSpecifier do
5555
@type form :: Types.form()
5656
@type forms :: Types.forms()
5757
@type options :: Types.options()
58+
@type abstract_expr :: Types.abstract_expr()
59+
60+
# Expressions that could have missing location
61+
@lineless_forms [:atom, :char, :float, :integer, :string, :bin, :cons]
5862

5963
# Api
6064

@@ -364,6 +368,8 @@ defmodule Gradient.AstSpecifier do
364368

365369
name = remote_mapper(name)
366370

371+
{opts, args} = call_with_pipe_op(tokens, args, opts)
372+
367373
{args, tokens} = context_mapper_fold(args, tokens, opts)
368374

369375
{:call, anno, name, args}
@@ -820,4 +826,33 @@ defmodule Gradient.AstSpecifier do
820826
defp pass_tokens(form, tokens) do
821827
{form, tokens}
822828
end
829+
830+
@spec call_with_pipe_op(tokens(), [abstract_expr()], options()) :: {options, [abstract_expr]}
831+
def call_with_pipe_op(tokens, args, opts) do
832+
# Check whether the call is after |> operator. If true, the parent location is set to 0
833+
# and the first arg location is cleared (if this arg is a lineless form).
834+
# Clearing the location is required only for Elixir 1.13 or newer because from this version
835+
# the missing locations are specified, unfortunately sometimes not precise enough.
836+
{:ok, line} = Keyword.fetch(opts, :line)
837+
838+
case {List.first(drop_tokens_to_line(tokens, line)), is_first_arg_lineless?(args)} do
839+
{{:arrow_op, _loc, :|>}, true} ->
840+
{Keyword.put(opts, :line, 0), clear_first_arg_location(args)}
841+
842+
_ ->
843+
{opts, args}
844+
end
845+
end
846+
847+
def is_first_arg_lineless?([form | _]), do: is_lineless_form?(form)
848+
def is_first_arg_lineless?([]), do: false
849+
850+
def is_lineless_form?(form) do
851+
elem(form, 0) in @lineless_forms
852+
end
853+
854+
def clear_first_arg_location([form | t]), do: [clear_location(form) | t]
855+
def clear_first_arg_location([]), do: []
856+
857+
def clear_location(form), do: put_elem(form, 1, :erl_anno.set_line(0, elem(form, 1)))
823858
end

test/gradient/ast_specifier_test.exs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -566,13 +566,13 @@ defmodule Gradient.AstSpecifierTest do
566566
[
567567
{:call, 4, {:remote, 4, {:atom, 4, Enum}, {:atom, 4, :filter}},
568568
[
569-
{:cons, 4, {:integer, 4, 1},
570-
{:cons, 4,
569+
{:cons, 3, {:integer, 3, 1},
570+
{:cons, 3,
571571
{
572572
:integer,
573-
4,
573+
3,
574574
2
575-
}, {:cons, 4, {:integer, 4, 3}, {nil, 4}}}},
575+
}, {:cons, 3, {:integer, 3, 3}, {nil, 3}}}},
576576
{:fun, 4,
577577
{:clauses,
578578
[

0 commit comments

Comments
 (0)