@@ -10,189 +10,13 @@ defmodule ElixirLS.LanguageServer.Providers.CodeLens do
1010 disable this feature.
1111 """
1212
13- alias ElixirLS.LanguageServer . { Server , SourceFile }
14- alias ElixirSense.Core.Parser
15- alias ElixirSense.Core.State
16- alias Erl2ex.Convert . { Context , ErlForms }
17- alias Erl2ex.Pipeline . { Parse , ModuleData , ExSpec }
13+ alias ElixirLS.LanguageServer.Providers.CodeLens
1814 import ElixirLS.LanguageServer.Protocol
1915
20- defmodule ContractTranslator do
21- def translate_contract ( fun , contract , is_macro ) do
22- # FIXME: Private module
23- { [ % ExSpec { specs: [ spec ] } | _ ] , _ } =
24- "-spec foo#{ contract } ."
25- # FIXME: Private module
26- |> Parse . string ( )
27- |> hd ( )
28- |> elem ( 0 )
29- # FIXME: Private module
30- |> ErlForms . conv_form ( % Context {
31- in_type_expr: true ,
32- # FIXME: Private module
33- module_data: % ModuleData { }
34- } )
16+ def spec_code_lens ( server_instance_id , uri , text ) ,
17+ do: CodeLens.Spec . code_lens ( server_instance_id , uri , text )
3518
36- spec
37- |> Macro . postwalk ( & tweak_specs / 1 )
38- |> drop_macro_env ( is_macro )
39- |> Macro . to_string ( )
40- |> String . replace ( "()" , "" )
41- |> Code . format_string! ( line_length: :infinity )
42- |> IO . iodata_to_binary ( )
43- |> String . replace_prefix ( "foo" , to_string ( fun ) )
44- end
45-
46- defp tweak_specs ( { :list , _meta , args } ) do
47- case args do
48- [ { :{} , _ , [ { :atom , _ , [ ] } , { wild , _ , _ } ] } ] when wild in [ :_ , :any ] -> quote do: keyword ( )
49- list -> list
50- end
51- end
52-
53- defp tweak_specs ( { :nonempty_list , _meta , args } ) do
54- case args do
55- [ { :any , _ , [ ] } ] -> quote do: [ ... ]
56- _ -> args ++ quote do: [ ... ]
57- end
58- end
59-
60- defp tweak_specs ( { :%{} , _meta , fields } ) do
61- fields =
62- Enum . map ( fields , fn
63- { :map_field_exact , _ , [ key , value ] } -> { key , value }
64- { key , value } -> quote do: { optional ( unquote ( key ) ) , unquote ( value ) }
65- field -> field
66- end )
67- |> Enum . reject ( & match? ( { { :optional , _ , [ { :any , _ , [ ] } ] } , { :any , _ , [ ] } } , & 1 ) )
68-
69- fields
70- |> Enum . find_value ( fn
71- { :__struct__ , struct_type } when is_atom ( struct_type ) -> struct_type
72- _ -> nil
73- end )
74- |> case do
75- nil -> { :%{} , [ ] , fields }
76- struct_type -> { { :. , [ ] , [ struct_type , :t ] } , [ ] , [ ] }
77- end
78- end
79-
80- # Undo conversion of _ to any() when inside binary spec
81- defp tweak_specs ( { :<<>> , _ , children } ) do
82- children =
83- Macro . postwalk ( children , fn
84- { :any , _ , [ ] } -> quote do: _
85- other -> other
86- end )
87-
88- { :<<>> , [ ] , children }
89- end
90-
91- defp tweak_specs ( { :_ , _ , _ } ) do
92- quote do: any ( )
93- end
94-
95- defp tweak_specs ( { :when , [ ] , [ spec , substitutions ] } ) do
96- substitutions = Enum . reject ( substitutions , & match? ( { :_ , { :any , _ , [ ] } } , & 1 ) )
97-
98- case substitutions do
99- [ ] -> spec
100- _ -> { :when , [ ] , [ spec , substitutions ] }
101- end
102- end
103-
104- defp tweak_specs ( node ) do
105- node
106- end
107-
108- defp drop_macro_env ( ast , false ) , do: ast
109-
110- defp drop_macro_env ( { :"::" , [ ] , [ { :foo , [ ] , [ _env | rest ] } , res ] } , true ) do
111- { :"::" , [ ] , [ { :foo , [ ] , rest } , res ] }
112- end
113- end
114-
115- def spec_code_lens ( server_instance_id , uri , text ) do
116- resp =
117- for { _ , line , { mod , fun , arity } , contract , is_macro } <- Server . suggest_contracts ( uri ) ,
118- SourceFile . function_def_on_line? ( text , line , fun ) ,
119- spec = ContractTranslator . translate_contract ( fun , contract , is_macro ) do
120- build_code_lens (
121- line ,
122- "@spec #{ spec } " ,
123- "spec:#{ server_instance_id } " ,
124- % {
125- "uri" => uri ,
126- "mod" => to_string ( mod ) ,
127- "fun" => to_string ( fun ) ,
128- "arity" => arity ,
129- "spec" => spec ,
130- "line" => line
131- }
132- )
133- end
134-
135- { :ok , resp }
136- end
137-
138- def test_code_lens ( uri , src ) do
139- file_path = SourceFile . path_from_uri ( uri )
140-
141- if imports? ( src , ExUnit.Case ) do
142- test_calls = calls_to ( src , :test )
143- describe_calls = calls_to ( src , :describe )
144-
145- calls_lenses =
146- for { line , _col } <- test_calls ++ describe_calls do
147- test_filter = "#{ file_path } :#{ line } "
148-
149- build_code_lens ( line , "Run test" , "elixir.test.run" , test_filter )
150- end
151-
152- file_lens = build_code_lens ( 1 , "Run test" , "elixir.test.run" , file_path )
153-
154- { :ok , [ file_lens | calls_lenses ] }
155- end
156- end
157-
158- @ spec imports? ( String . t ( ) , [ atom ( ) ] | atom ( ) ) :: boolean ( )
159- defp imports? ( buffer , modules ) do
160- buffer_file_metadata =
161- buffer
162- |> Parser . parse_string ( true , true , 1 )
163-
164- imports_set =
165- buffer_file_metadata . lines_to_env
166- |> get_imports ( )
167- |> MapSet . new ( )
168-
169- modules
170- |> List . wrap ( )
171- |> MapSet . new ( )
172- |> MapSet . subset? ( imports_set )
173- end
174-
175- defp get_imports ( lines_to_env ) do
176- % State.Env { imports: imports } =
177- lines_to_env
178- |> Enum . max_by ( fn { k , _v } -> k end )
179- |> elem ( 1 )
180-
181- imports
182- end
183-
184- @ spec calls_to ( String . t ( ) , atom ( ) | { atom ( ) , integer ( ) } ) :: [ { pos_integer ( ) , pos_integer ( ) } ]
185- defp calls_to ( buffer , function ) do
186- buffer_file_metadata =
187- buffer
188- |> Parser . parse_string ( true , true , 1 )
189-
190- buffer_file_metadata . calls
191- |> Enum . map ( fn { _k , v } -> v end )
192- |> List . flatten ( )
193- |> Enum . filter ( fn call_info -> call_info . func == function end )
194- |> Enum . map ( fn call -> call . position end )
195- end
19+ def test_code_lens ( uri , text ) , do: CodeLens.Test . code_lens ( uri , text )
19620
19721 def build_code_lens ( line , title , command , argument ) do
19822 % {
0 commit comments