Skip to content

Commit 6156f11

Browse files
authored
feat(definition): aliases (#83)
1 parent 1d3b022 commit 6156f11

File tree

7 files changed

+160
-40
lines changed

7 files changed

+160
-40
lines changed

Diff for: lib/next_ls.ex

+4-3
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ defmodule NextLS do
117117
nil ->
118118
nil
119119

120+
[] ->
121+
nil
122+
120123
[{file, line, column} | _] ->
121124
%Location{
122125
uri: "file://#{file}",
@@ -372,13 +375,11 @@ defmodule NextLS do
372375

373376
def handle_info({:tracer, payload}, lsp) do
374377
SymbolTable.put_symbols(lsp.assigns.symbol_table, payload)
375-
GenLSP.log(lsp, "[NextLS] Updated the symbols table!")
376378
{:noreply, lsp}
377379
end
378380

379-
def handle_info({{:tracer, :local_function}, payload}, lsp) do
381+
def handle_info({{:tracer, :reference}, payload}, lsp) do
380382
SymbolTable.put_reference(lsp.assigns.symbol_table, payload)
381-
GenLSP.log(lsp, "[NextLS] Updated the reference table!")
382383
{:noreply, lsp}
383384
end
384385

Diff for: lib/next_ls/definition.ex

+25-9
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,34 @@ defmodule NextLS.Definition do
1616
# :dets.traverse(dets_symbol_table, fn x -> {:continue, x} end) |> dbg
1717
# :dets.traverse(dets_ref_table, fn x -> {:continue, x} end) |> dbg
1818

19-
case ref do
20-
[ref] ->
21-
:dets.select(
22-
dets_symbol_table,
19+
# dbg(ref)
20+
21+
query =
22+
case ref do
23+
[%{type: :alias} = ref] ->
2324
[
24-
{{:_, %{line: :"$3", name: :"$2", module: :"$1", col: :"$4", file: :"$5"}},
25-
[{:andalso, {:==, :"$1", ref.module}, {:==, :"$2", ref.func}}], [{{:"$5", :"$3", :"$4"}}]}
25+
{{:_, %{line: :"$3", name: :"$2", file: :"$5", module: :"$1", col: :"$4"}},
26+
[
27+
{:andalso, {:==, :"$1", ref.module}, {:==, :"$2", Macro.to_string(ref.module)}}
28+
], [{{:"$5", :"$3", :"$4"}}]}
2629
]
27-
)
2830

29-
_ ->
30-
nil
31+
[%{type: :function} = ref] ->
32+
[
33+
{{:_, %{line: :"$3", name: :"$2", file: :"$5", module: :"$1", col: :"$4"}},
34+
[
35+
{:andalso, {:==, :"$1", ref.module}, {:==, :"$2", ref.identifier}}
36+
], [{{:"$5", :"$3", :"$4"}}]}
37+
]
38+
39+
_ ->
40+
nil
41+
end
42+
43+
if query do
44+
:dets.select(dets_symbol_table, query)
45+
else
46+
nil
3147
end
3248
end
3349
end

Diff for: lib/next_ls/runtime.ex

+7
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ defmodule NextLS.Runtime do
8787
|> :code.priv_dir()
8888
|> Path.join("monkey/_next_ls_private_compiler.ex")
8989
|> then(&:rpc.call(node, Code, :compile_file, [&1]))
90+
|> tap(fn
91+
{:badrpc, :EXIT, {error, _}} ->
92+
send(parent, {:log, error})
93+
94+
_ ->
95+
:ok
96+
end)
9097

9198
:rpc.call(node, Code, :put_compiler_option, [:parser_options, [columns: true, token_metadata: true]])
9299

Diff for: lib/next_ls/symbol_table.ex

+9-12
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,19 @@ defmodule NextLS.SymbolTable do
3636

3737
def init(args) do
3838
path = Keyword.fetch!(args, :path)
39+
symbol_table_name = Keyword.get(args, :symbol_table_name, :symbol_table)
40+
reference_table_name = Keyword.get(args, :reference_table_name, :reference_table)
3941

4042
File.mkdir_p!(path)
4143

4244
{:ok, name} =
43-
:dets.open_file(:symbol_table,
45+
:dets.open_file(symbol_table_name,
4446
file: Path.join(path, "symbol_table.dets") |> String.to_charlist(),
4547
type: :duplicate_bag
4648
)
4749

4850
{:ok, ref_name} =
49-
:dets.open_file(:reference_table,
51+
:dets.open_file(reference_table_name,
5052
file: Path.join(path, "reference_table.dets") |> String.to_charlist(),
5153
type: :duplicate_bag
5254
)
@@ -91,14 +93,14 @@ defmodule NextLS.SymbolTable do
9193
def handle_cast({:put_reference, reference}, state) do
9294
%{
9395
meta: meta,
94-
func: func,
95-
arity: _arity,
96-
file: file,
97-
module: _module
96+
identifier: identifier,
97+
file: file
9898
} = reference
9999

100100
col = meta[:column] || 0
101-
range = {{meta[:line], col}, {meta[:line], col + String.length(to_string(func))}}
101+
102+
range =
103+
{{meta[:line], col}, {meta[:line], col + String.length(to_string(identifier) |> String.replace("Elixir.", ""))}}
102104

103105
:dets.insert(state.reference_table, {
104106
{file, range},
@@ -150,9 +152,6 @@ defmodule NextLS.SymbolTable do
150152
end
151153

152154
for {name, {:v1, type, _meta, clauses}} <- defs, {meta, _, _, _} <- clauses do
153-
_ = foo()
154-
_ = foo()
155-
156155
:dets.insert(
157156
state.table,
158157
{mod,
@@ -169,6 +168,4 @@ defmodule NextLS.SymbolTable do
169168

170169
{:noreply, state}
171170
end
172-
173-
def foo(), do: :ok
174171
end

Diff for: priv/monkey/_next_ls_private_compiler.ex

+35-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,33 @@
11
defmodule NextLSPrivate.Tracer do
2-
def trace(:start, env) do
2+
def trace(:start, _env) do
3+
:ok
4+
end
5+
6+
def trace({:alias_reference, meta, module}, env) do
7+
parent = parent_pid()
8+
9+
alias_map = Map.new(env.aliases, fn {alias, mod} -> {mod, alias} end)
10+
11+
Process.send(
12+
parent,
13+
{{:tracer, :reference},
14+
%{
15+
meta: meta,
16+
identifier: Map.get(alias_map, module, module),
17+
file: env.file,
18+
type: :alias,
19+
module: module
20+
}},
21+
[]
22+
)
23+
324
:ok
425
end
526

627
def trace({type, meta, module, func, arity}, env)
728
when type in [:remote_function, :remote_macro, :imported_macro] and
829
module not in [:elixir_def, :elixir_utils, Kernel, Enum] do
9-
parent = "NEXTLS_PARENT_PID" |> System.get_env() |> Base.decode64!() |> :erlang.binary_to_term()
30+
parent = parent_pid()
1031

1132
if type == :remote_macro && meta[:closing][:line] != meta[:line] do
1233
# this is the case that a macro is getting expanded from inside
@@ -15,12 +36,13 @@ defmodule NextLSPrivate.Tracer do
1536
else
1637
Process.send(
1738
parent,
18-
{{:tracer, :local_function},
39+
{{:tracer, :reference},
1940
%{
2041
meta: meta,
21-
func: func,
42+
identifier: func,
2243
arity: arity,
2344
file: env.file,
45+
type: :function,
2446
module: module
2547
}},
2648
[]
@@ -31,16 +53,17 @@ defmodule NextLSPrivate.Tracer do
3153
end
3254

3355
def trace({type, meta, func, arity}, env) when type in [:local_function, :local_macro] do
34-
parent = "NEXTLS_PARENT_PID" |> System.get_env() |> Base.decode64!() |> :erlang.binary_to_term()
56+
parent = parent_pid()
3557

3658
Process.send(
3759
parent,
38-
{{:tracer, :local_function},
60+
{{:tracer, :reference},
3961
%{
4062
meta: meta,
41-
func: func,
63+
identifier: func,
4264
arity: arity,
4365
file: env.file,
66+
type: :function,
4467
module: env.module
4568
}},
4669
[]
@@ -50,7 +73,7 @@ defmodule NextLSPrivate.Tracer do
5073
end
5174

5275
def trace({:on_module, bytecode, _}, env) do
53-
parent = "NEXTLS_PARENT_PID" |> System.get_env() |> Base.decode64!() |> :erlang.binary_to_term()
76+
parent = parent_pid()
5477

5578
defs = Module.definitions_in(env.module)
5679

@@ -82,6 +105,10 @@ defmodule NextLSPrivate.Tracer do
82105
def trace(_event, _env) do
83106
:ok
84107
end
108+
109+
defp parent_pid() do
110+
"NEXTLS_PARENT_PID" |> System.get_env() |> Base.decode64!() |> :erlang.binary_to_term()
111+
end
85112
end
86113

87114
defmodule :_next_ls_private_compiler do

Diff for: test/next_ls/symbol_table_test.exs

+15-8
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,26 @@ defmodule NextLS.SymbolTableTest do
88
File.mkdir_p!(dir)
99

1010
# this fails with `{:error, incompatible_arguments}` on CI a lot, and I have no idea why
11-
pid =
12-
try do
13-
start_supervised!({SymbolTable, [path: dir]})
14-
rescue
15-
_ ->
16-
Process.sleep(250)
17-
start_supervised!({SymbolTable, [path: dir]})
18-
end
11+
pid = try_start_supervised({SymbolTable, [path: dir]}, 10)
1912

2013
Process.link(pid)
2114
[pid: pid, dir: dir]
2215
end
2316

17+
defp try_start_supervised(spec, 0) do
18+
start_supervised!(spec)
19+
end
20+
21+
defp try_start_supervised(spec, num) do
22+
try do
23+
start_supervised!(spec)
24+
rescue
25+
_ ->
26+
Process.sleep(250)
27+
try_start_supervised(spec, num - 1)
28+
end
29+
end
30+
2431
test "creates a dets table", %{dir: dir, pid: pid} do
2532
assert File.exists?(Path.join([dir, "symbol_table.dets"]))
2633
assert :sys.get_state(pid).table == :symbol_table

Diff for: test/next_ls_test.exs

+65
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,71 @@ defmodule NextLSTest do
801801
end
802802
end
803803

804+
describe "module go to definition" do
805+
setup %{cwd: cwd} do
806+
peace = Path.join(cwd, "lib/peace.ex")
807+
808+
File.write!(peace, """
809+
defmodule MyApp.Peace do
810+
def and_love() do
811+
"✌️"
812+
end
813+
end
814+
""")
815+
816+
bar = Path.join(cwd, "lib/bar.ex")
817+
818+
File.write!(bar, """
819+
defmodule Bar do
820+
alias MyApp.Peace
821+
def run() do
822+
Peace.and_love()
823+
end
824+
end
825+
""")
826+
827+
[bar: bar, peace: peace]
828+
end
829+
830+
setup :with_lsp
831+
832+
test "go to module definition", %{client: client, bar: bar, peace: peace} do
833+
assert :ok == notify(client, %{method: "initialized", jsonrpc: "2.0", params: %{}})
834+
assert_notification "window/logMessage", %{"message" => "[NextLS] Runtime ready..."}
835+
assert_notification "window/logMessage", %{"message" => "[NextLS] Compiled!"}
836+
837+
uri = uri(bar)
838+
839+
request(client, %{
840+
method: "textDocument/definition",
841+
id: 4,
842+
jsonrpc: "2.0",
843+
params: %{
844+
position: %{line: 3, character: 5},
845+
textDocument: %{uri: uri}
846+
}
847+
})
848+
849+
uri = uri(peace)
850+
851+
assert_result 4,
852+
%{
853+
"range" => %{
854+
"start" => %{
855+
"line" => 0,
856+
"character" => 0
857+
},
858+
"end" => %{
859+
"line" => 0,
860+
"character" => 0
861+
}
862+
},
863+
"uri" => ^uri
864+
},
865+
500
866+
end
867+
end
868+
804869
defp with_lsp(%{tmp_dir: tmp_dir}) do
805870
root_path = Path.absname(tmp_dir)
806871

0 commit comments

Comments
 (0)