Skip to content

Commit 57ccc2e

Browse files
authored
feat(definition): local function (#78)
1 parent d0ae204 commit 57ccc2e

File tree

7 files changed

+626
-402
lines changed

7 files changed

+626
-402
lines changed

Diff for: .github/workflows/ci.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ jobs:
4343
MIX_ENV: test
4444
run: mix compile
4545

46+
- name: remove tmp dir
47+
run: rm -rf tmp
48+
4649
- name: Run Tests
4750
run: mix test
4851

Diff for: lib/next_ls.ex

+40-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ defmodule NextLS do
2121
Initialize,
2222
Shutdown,
2323
TextDocumentDocumentSymbol,
24+
TextDocumentDefinition,
2425
TextDocumentFormatting,
2526
WorkspaceSymbol
2627
}
@@ -45,6 +46,7 @@ defmodule NextLS do
4546
alias NextLS.DiagnosticCache
4647
alias NextLS.Runtime
4748
alias NextLS.SymbolTable
49+
alias NextLS.Definition
4850

4951
def start_link(args) do
5052
{args, opts} =
@@ -97,12 +99,43 @@ defmodule NextLS do
9799
},
98100
document_formatting_provider: true,
99101
workspace_symbol_provider: true,
100-
document_symbol_provider: true
102+
document_symbol_provider: true,
103+
definition_provider: true
101104
},
102105
server_info: %{name: "NextLS"}
103106
}, assign(lsp, root_uri: root_uri)}
104107
end
105108

109+
def handle_request(%TextDocumentDefinition{params: %{text_document: %{uri: uri}, position: position}}, lsp) do
110+
result =
111+
case Definition.fetch(
112+
URI.parse(uri).path,
113+
{position.line + 1, position.character + 1},
114+
:symbol_table,
115+
:reference_table
116+
) do
117+
nil ->
118+
nil
119+
120+
[{file, line, column} | _] ->
121+
%Location{
122+
uri: "file://#{file}",
123+
range: %Range{
124+
start: %Position{
125+
line: line - 1,
126+
character: column - 1
127+
},
128+
end: %Position{
129+
line: line - 1,
130+
character: column - 1
131+
}
132+
}
133+
}
134+
end
135+
136+
{:reply, result, lsp}
137+
end
138+
106139
def handle_request(%TextDocumentDocumentSymbol{params: %{text_document: %{uri: uri}}}, lsp) do
107140
symbols =
108141
try do
@@ -343,6 +376,12 @@ defmodule NextLS do
343376
{:noreply, lsp}
344377
end
345378

379+
def handle_info({{:tracer, :local_function}, payload}, lsp) do
380+
SymbolTable.put_reference(lsp.assigns.symbol_table, payload)
381+
GenLSP.log(lsp, "[NextLS] Updated the reference table!")
382+
{:noreply, lsp}
383+
end
384+
346385
def handle_info(:publish, lsp) do
347386
GenLSP.log(lsp, "[NextLS] Compiled!")
348387

Diff for: lib/next_ls/definition.ex

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
defmodule NextLS.Definition do
2+
def fetch(file, {line, col}, dets_symbol_table, dets_ref_table) do
3+
ref =
4+
:dets.select(
5+
dets_ref_table,
6+
[
7+
{{{:"$1", {{:"$2", :"$3"}, {:"$4", :"$5"}}}, :"$6"},
8+
[
9+
{:andalso,
10+
{:andalso, {:andalso, {:andalso, {:==, :"$1", file}, {:"=<", :"$2", line}}, {:"=<", :"$3", col}},
11+
{:"=<", line, :"$4"}}, {:"=<", col, :"$5"}}
12+
], [:"$6"]}
13+
]
14+
)
15+
16+
:dets.traverse(dets_symbol_table, fn x -> {:continue, x} end)
17+
18+
case ref do
19+
[ref] ->
20+
:dets.select(
21+
dets_symbol_table,
22+
[
23+
{{:_, %{line: :"$3", name: :"$2", module: :"$1", col: :"$4", file: :"$5"}},
24+
[{:andalso, {:==, :"$1", ref.module}, {:==, :"$2", ref.func}}], [{{:"$5", :"$3", :"$4"}}]}
25+
]
26+
)
27+
28+
_ ->
29+
nil
30+
end
31+
end
32+
end

Diff for: lib/next_ls/symbol_table.ex

+35-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ defmodule NextLS.SymbolTable do
2222
@spec put_symbols(pid() | atom(), list(tuple())) :: :ok
2323
def put_symbols(server, symbols), do: GenServer.cast(server, {:put_symbols, symbols})
2424

25+
@spec put_reference(pid() | atom(), map()) :: :ok
26+
def put_reference(server, reference), do: GenServer.cast(server, {:put_reference, reference})
27+
2528
@spec symbols(pid() | atom()) :: list(struct())
2629
def symbols(server), do: GenServer.call(server, :symbols)
2730

@@ -42,7 +45,13 @@ defmodule NextLS.SymbolTable do
4245
type: :duplicate_bag
4346
)
4447

45-
{:ok, %{table: name}}
48+
{:ok, ref_name} =
49+
:dets.open_file(:reference_table,
50+
file: Path.join(path, "reference_table.dets") |> String.to_charlist(),
51+
type: :duplicate_bag
52+
)
53+
54+
{:ok, %{table: name, reference_table: ref_name}}
4655
end
4756

4857
def handle_call({:symbols, file}, _, state) do
@@ -74,10 +83,30 @@ defmodule NextLS.SymbolTable do
7483

7584
def handle_call(:close, _, state) do
7685
:dets.close(state.table)
86+
:dets.close(state.reference_table)
7787

7888
{:reply, :ok, state}
7989
end
8090

91+
def handle_cast({:put_reference, reference}, state) do
92+
%{
93+
meta: meta,
94+
func: func,
95+
arity: _arity,
96+
file: file,
97+
module: _module
98+
} = reference
99+
100+
range = {{meta[:line], meta[:column]}, {meta[:line], meta[:column] + String.length(to_string(func))}}
101+
102+
:dets.insert(state.reference_table, {
103+
{file, range},
104+
reference
105+
})
106+
107+
{:noreply, state}
108+
end
109+
81110
def handle_cast({:put_symbols, symbols}, state) do
82111
%{
83112
module: mod,
@@ -120,6 +149,9 @@ defmodule NextLS.SymbolTable do
120149
end
121150

122151
for {name, {:v1, type, _meta, clauses}} <- defs, {meta, _, _, _} <- clauses do
152+
_ = foo()
153+
_ = foo()
154+
123155
:dets.insert(
124156
state.table,
125157
{mod,
@@ -136,4 +168,6 @@ defmodule NextLS.SymbolTable do
136168

137169
{:noreply, state}
138170
end
171+
172+
def foo(), do: :ok
139173
end

Diff for: priv/monkey/_next_ls_private_compiler.ex

+37-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,33 @@
11
defmodule NextLSPrivate.Tracer do
2+
def trace(:start, env) do
3+
:ok
4+
end
5+
6+
def trace({:local_function, meta, func, arity}, env) do
7+
parent = "NEXTLS_PARENT_PID" |> System.get_env() |> Base.decode64!() |> :erlang.binary_to_term()
8+
9+
module =
10+
case Macro.Env.lookup_import(env, {func, arity}) do
11+
[{_, module}] -> module
12+
[] -> env.module
13+
end
14+
15+
Process.send(
16+
parent,
17+
{{:tracer, :local_function},
18+
%{
19+
meta: meta,
20+
func: func,
21+
arity: arity,
22+
file: env.file,
23+
module: module
24+
}},
25+
[]
26+
)
27+
28+
:ok
29+
end
30+
231
def trace({:on_module, bytecode, _}, env) do
332
parent = "NEXTLS_PARENT_PID" |> System.get_env() |> Base.decode64!() |> :erlang.binary_to_term()
433

@@ -15,7 +44,14 @@ defmodule NextLSPrivate.Tracer do
1544

1645
Process.send(
1746
parent,
18-
{:tracer, %{file: env.file, module: env.module, module_line: line, struct: struct, defs: defs}},
47+
{:tracer,
48+
%{
49+
file: env.file,
50+
module: env.module,
51+
module_line: line,
52+
struct: struct,
53+
defs: defs
54+
}},
1955
[]
2056
)
2157

Diff for: test/next_ls/symbol_table_test.exs

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ defmodule NextLS.SymbolTableTest do
55
alias NextLS.SymbolTable
66

77
setup %{tmp_dir: dir} do
8+
File.mkdir_p!(dir)
89
pid = start_supervised!({SymbolTable, [path: dir]})
910

1011
Process.link(pid)

0 commit comments

Comments
 (0)