@@ -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 ) ) )
823858end
0 commit comments