1+ defmodule DescribeBlock do
2+ alias ElixirSense.Core.State.Env
3+
4+ @ struct_keys [ :line , :name , :body_scope_id ]
5+
6+ @ enforce_keys @ struct_keys
7+ defstruct @ struct_keys
8+
9+ def find_block_info ( line , lines_to_env_list , lines_to_env_list_length , source_lines ) do
10+ name = get_name ( source_lines , line )
11+
12+ body_scope_id =
13+ get_body_scope_id (
14+ line ,
15+ lines_to_env_list ,
16+ lines_to_env_list_length
17+ )
18+
19+ % DescribeBlock { line: line , body_scope_id: body_scope_id , name: name }
20+ end
21+
22+ defp get_name ( source_lines , declaration_line ) do
23+ % { "name" => name } =
24+ ~r/ ^\s *describe "(?<name>.*)" do/
25+ |> Regex . named_captures ( Enum . at ( source_lines , declaration_line - 1 ) )
26+
27+ name
28+ end
29+
30+ defp get_body_scope_id (
31+ declaration_line ,
32+ lines_to_env_list ,
33+ lines_to_env_list_length
34+ ) do
35+ env_index =
36+ lines_to_env_list
37+ |> Enum . find_index ( fn { line , _env } -> line == declaration_line end )
38+
39+ { _line , % { scope_id: declaration_scope_id } } =
40+ lines_to_env_list
41+ |> Enum . at ( env_index )
42+
43+ with true = env_index + 1 < lines_to_env_list_length ,
44+ next_env = Enum . at ( lines_to_env_list , env_index + 1 ) ,
45+ { _line , % Env { scope_id: body_scope_id } } <- next_env ,
46+ true = body_scope_id != declaration_scope_id do
47+ body_scope_id
48+ else
49+ _ -> nil
50+ end
51+ end
52+ end
53+
54+ defmodule TestBlock do
55+ @ struct_keys [ :name , :describe , :line ]
56+
57+ @ enforce_keys @ struct_keys
58+ defstruct @ struct_keys
59+ end
60+
161defmodule ElixirLS.LanguageServer.Providers.CodeLens.Test do
262 alias ElixirLS.LanguageServer.Providers.CodeLens
363 alias ElixirLS.LanguageServer.SourceFile
464 alias ElixirSense.Core.Parser
565 alias ElixirSense.Core.Metadata
66+ alias ElixirSense.Core.State.Env
667
768 @ run_test_command "elixir.test.run"
869
970 def code_lens ( uri , text ) do
1071 with { :ok , buffer_file_metadata } <- parse_source ( text ) do
72+ source_lines = SourceFile . lines ( text )
73+
1174 file_path = SourceFile . path_from_uri ( uri )
1275
13- function_lenses = get_function_lenses ( buffer_file_metadata , file_path )
14- module_lenses = get_module_lenses ( buffer_file_metadata , file_path )
76+ calls_list =
77+ buffer_file_metadata . calls
78+ |> Enum . map ( fn { _k , v } -> v end )
79+ |> List . flatten ( )
80+
81+ lines_to_env_list = Map . to_list ( buffer_file_metadata . lines_to_env )
82+
83+ describe_blocks = find_describe_blocks ( lines_to_env_list , calls_list , source_lines )
84+ describe_lenses = get_describe_lenses ( describe_blocks , file_path )
85+
86+ test_lenses =
87+ lines_to_env_list
88+ |> find_test_blocks ( calls_list , describe_blocks , source_lines )
89+ |> get_test_lenses ( file_path )
1590
16- { :ok , function_lenses ++ module_lenses }
91+ module_lenses =
92+ buffer_file_metadata
93+ |> get_test_modules ( )
94+ |> get_module_lenses ( file_path )
95+
96+ { :ok , test_lenses ++ describe_lenses ++ module_lenses }
1797 end
1898 end
1999
20- defp get_module_lenses ( % Metadata { } = metadata , file_path ) do
21- metadata
22- |> get_test_modules ( )
23- |> Enum . map ( & build_test_module_code_lens ( file_path , & 1 ) )
100+ def get_test_lenses ( test_blocks , file_path ) do
101+ test_blocks
102+ |> Enum . map ( fn block ->
103+ CodeLens . build_code_lens ( block . line , "Run test" , @ run_test_command , % {
104+ "filePath" => file_path ,
105+ "describe" =>
106+ if block . describe != nil do
107+ block . describe . name
108+ else
109+ nil
110+ end ,
111+ "testName" => block . name
112+ } )
113+ end )
24114 end
25115
26- defp get_test_modules ( % Metadata { lines_to_env: lines_to_env } ) do
27- lines_to_env
28- |> Enum . group_by ( fn { _line , env } -> env . module end )
29- |> Enum . filter ( fn { _module , module_lines_to_env } -> is_test_module? ( module_lines_to_env ) end )
30- |> Enum . map ( fn { module , [ { line , _env } | _rest ] } -> { module , line } end )
116+ def get_describe_lenses ( describe_blocks , file_path ) do
117+ describe_blocks
118+ |> Enum . map ( fn block ->
119+ CodeLens . build_code_lens ( block . line , "Run tests" , @ run_test_command , % {
120+ "filePath" => file_path ,
121+ "describe" => block . name
122+ } )
123+ end )
31124 end
32125
33- defp get_function_lenses ( % Metadata { } = metadata , file_path ) do
34- runnable_functions = [ { :test , 3 } , { :test , 2 } , { :describe , 2 } ]
35-
36- calls_list =
37- metadata . calls
38- |> Enum . map ( fn { _k , v } -> v end )
39- |> List . flatten ( )
40-
41- lines_to_env_list = Map . to_list ( metadata . lines_to_env )
126+ defp find_test_blocks ( lines_to_env_list , calls_list , describe_blocks , source_lines ) do
127+ runnable_functions = [ { :test , 3 } , { :test , 2 } ]
42128
43129 for func <- runnable_functions ,
44130 { line , _col } <- calls_to ( calls_list , func ) ,
45131 is_test_module? ( lines_to_env_list , line ) do
46- build_function_test_code_lens ( func , file_path , line )
132+ { _line , % { scope_id: scope_id } } =
133+ Enum . find ( lines_to_env_list , fn { env_line , _env } -> env_line == line end )
134+
135+ describe =
136+ describe_blocks
137+ |> Enum . find ( nil , fn describe ->
138+ describe . body_scope_id == scope_id
139+ end )
140+
141+ % { "name" => test_name } =
142+ ~r/ ^\s *test "(?<name>.*)"(,.*)? do/
143+ |> Regex . named_captures ( Enum . at ( source_lines , line - 1 ) )
144+
145+ % TestBlock { name: test_name , describe: describe , line: line }
146+ end
147+ end
148+
149+ defp find_describe_blocks ( lines_to_env_list , calls_list , source_lines ) do
150+ lines_to_env_list_length = length ( lines_to_env_list )
151+
152+ for { line , _col } <- calls_to ( calls_list , { :describe , 2 } ) ,
153+ is_test_module? ( lines_to_env_list , line ) do
154+ DescribeBlock . find_block_info (
155+ line ,
156+ lines_to_env_list ,
157+ lines_to_env_list_length ,
158+ source_lines
159+ )
47160 end
48161 end
49162
163+ defp get_module_lenses ( test_modules , file_path ) do
164+ test_modules
165+ |> Enum . map ( fn { module , line } ->
166+ CodeLens . build_code_lens ( line , "Run tests in module" , @ run_test_command , % {
167+ "filePath" => file_path ,
168+ "module" => module
169+ } )
170+ end )
171+ end
172+
173+ defp get_test_modules ( % Metadata { lines_to_env: lines_to_env } ) do
174+ lines_to_env
175+ |> Enum . group_by ( fn { _line , env } -> env . module end )
176+ |> Enum . filter ( fn { _module , module_lines_to_env } -> is_test_module? ( module_lines_to_env ) end )
177+ |> Enum . map ( fn { module , [ { line , _env } | _rest ] } -> { module , line } end )
178+ end
179+
50180 defp is_test_module? ( lines_to_env ) , do: is_test_module? ( lines_to_env , :infinity )
51181
52182 defp is_test_module? ( lines_to_env , line ) when is_list ( lines_to_env ) do
@@ -65,32 +195,8 @@ defmodule ElixirLS.LanguageServer.Providers.CodeLens.Test do
65195 call_info . func == function and call_info . arity === arity do
66196 call_info . position
67197 end
68-
69- # calls_list
70- # |> Enum.filter(fn call_info -> call_info.func == function and call_info.arity === arity end)
71- # |> Enum.map(fn call -> call.position end)
72- end
73-
74- defp build_test_module_code_lens ( file_path , { module , line } ) do
75- CodeLens . build_code_lens ( line , "Run tests in module" , @ run_test_command , % {
76- "file_path" => file_path ,
77- "module" => module
78- } )
79198 end
80199
81- defp build_function_test_code_lens ( title , file_path , line ) when is_binary ( title ) do
82- CodeLens . build_code_lens ( line , title , @ run_test_command , % {
83- "file_path" => file_path ,
84- "line" => line
85- } )
86- end
87-
88- defp build_function_test_code_lens ( { :test , _arity } , file_path , line ) ,
89- do: build_function_test_code_lens ( "Run test" , file_path , line )
90-
91- defp build_function_test_code_lens ( { :describe , _arity } , file_path , line ) ,
92- do: build_function_test_code_lens ( "Run tests" , file_path , line )
93-
94200 defp parse_source ( text ) do
95201 buffer_file_metadata =
96202 text
0 commit comments