Skip to content

Commit c693292

Browse files
committed
feat: add signature help
1 parent 33aef26 commit c693292

File tree

7 files changed

+87
-219
lines changed

7 files changed

+87
-219
lines changed

lib/next_ls.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,7 @@ defmodule NextLS do
713713
result =
714714
dispatch(lsp.assigns.registry, :databases, fn entries ->
715715
for {pid, _} <- entries do
716-
SignatureHelp.fetch(URI.parse(uri).path, {position.line + 1, position.character + 1}, pid, lsp.assigns.logger)
716+
SignatureHelp.fetch(URI.parse(uri).path, {position.line + 1, position.character + 1}, pid)
717717
end
718718
end)
719719

lib/next_ls/helpers/ast_helpers.ex

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -152,64 +152,4 @@ defmodule NextLS.ASTHelpers do
152152
end
153153
end)
154154
end
155-
156-
defmodule Functions do
157-
@moduledoc false
158-
159-
alias Sourceror.Zipper, as: Z
160-
161-
def get_function_params(code, identifier, line, _col) do
162-
ast =
163-
NextLS.Parser.parse!(code, columns: true)
164-
165-
identifier = String.to_atom(identifier)
166-
167-
{_ast, args} =
168-
Macro.prewalk(ast, nil, fn
169-
{^identifier, [line: ^line, column: _], args} = ast, _acc -> {ast, args}
170-
other, acc -> {other, acc}
171-
end)
172-
173-
if args do
174-
args
175-
else
176-
[]
177-
end
178-
end
179-
180-
def get_function_name_from_params(code, line, col) do
181-
pos = [line: line + 1, column: col + 1]
182-
183-
ast =
184-
case Spitfire.parse(code) do
185-
{:ok, ast} ->
186-
ast
187-
188-
{:error, ast, _errors} ->
189-
ast
190-
end
191-
192-
{_ast, result} =
193-
ast
194-
|> Z.zip()
195-
|> Z.traverse(nil, fn tree, acc ->
196-
node = Z.node(tree)
197-
range = Sourceror.get_range(node)
198-
199-
if not is_nil(range) and
200-
match?({:., _, [{:__aliases__, _, _aliases}, _identifier]}, node) do
201-
if Sourceror.compare_positions(range.end, pos) == :lt do
202-
{:., _, [{:__aliases__, _, aliases}, identifier]} = node
203-
{tree, {aliases, identifier}}
204-
else
205-
{tree, acc}
206-
end
207-
else
208-
{tree, acc}
209-
end
210-
end)
211-
212-
result
213-
end
214-
end
215155
end

lib/next_ls/logger.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ defmodule NextLS.Logger do
2424

2525
def handle_cast({:log, type, msg}, state) do
2626
apply(GenLSP, type, [state.lsp, String.trim("[Next LS] #{msg}")])
27-
27+
2828
case type do
2929
:log -> Logger.debug(msg)
3030
:warning -> Logger.warning(msg)

lib/next_ls/signature_help.ex

Lines changed: 41 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,50 @@
11
defmodule NextLS.SignatureHelp do
22
@moduledoc false
33

4-
import NextLS.DB.Query
5-
6-
alias GenLSP.Enumerations.MarkupKind
7-
alias GenLSP.Structures.MarkupContent
84
alias GenLSP.Structures.ParameterInformation
95
alias GenLSP.Structures.SignatureHelp
106
alias GenLSP.Structures.SignatureInformation
11-
alias NextLS.ASTHelpers
12-
alias NextLS.DB
13-
14-
def fetch(file, {line, col}, db, _logger) do
15-
code = File.read!(file)
16-
17-
{mod, func} =
18-
ASTHelpers.Functions.get_function_name_from_params(code, line, col)
19-
20-
query =
21-
~Q"""
22-
SELECT
23-
*
24-
FROM
25-
symbols
26-
WHERE
27-
symbols.module = ?
28-
AND symbols.name = ?;
29-
"""
30-
31-
args = [Enum.map_join(mod, ".", &Atom.to_string/1), Atom.to_string(func)]
32-
33-
symbol = DB.query(db, query, args)
34-
35-
result =
36-
case symbol do
37-
nil ->
7+
alias NextLS.Definition
8+
9+
def fetch(file, {line, col}, db) do
10+
case Definition.fetch(file, {line, col}, db) do
11+
nil ->
12+
nil
13+
14+
[] ->
15+
nil
16+
17+
[[_, _mod, _file, type, label, params, _line, _col | _] | _] = _definition ->
18+
if type in ["def", "defp"] do
19+
term_params =
20+
:erlang.binary_to_term(params)
21+
22+
code_params =
23+
term_params
24+
|> Macro.to_string()
25+
|> String.replace_prefix("[", "(")
26+
|> String.replace_suffix("]", ")")
27+
28+
params_info =
29+
term_params
30+
|> Enum.map(&Macro.to_string/1)
31+
|> Enum.map(fn name ->
32+
%ParameterInformation{
33+
label: name
34+
}
35+
end)
36+
37+
%SignatureHelp{
38+
signatures: [
39+
%SignatureInformation{
40+
label: "#{label}#{code_params}",
41+
parameters: params_info
42+
}
43+
]
44+
}
45+
else
3846
nil
39-
40-
[] ->
41-
nil
42-
43-
[[_, _mod, file, type, label, params, line, col | _] | _] = _definition ->
44-
if type in ["def", "defp"] do
45-
code_params = params |> :erlang.binary_to_term() |> Macro.to_string() |> dbg()
46-
47-
signature_params =
48-
params
49-
|> :erlang.binary_to_term()
50-
|> Enum.map(fn {name, _, _} ->
51-
%ParameterInformation{
52-
label: Atom.to_string(name)
53-
}
54-
end)
55-
|> dbg()
56-
57-
%SignatureHelp{
58-
signatures: [
59-
%SignatureInformation{
60-
label: "#{label}.#{code_params}",
61-
documentation: "need help",
62-
parameters: signature_params
63-
# active_parameter: 0
64-
}
65-
]
66-
# active_signature: 1,
67-
# active_parameter: 0
68-
}
69-
else
70-
nil
71-
end
72-
end
73-
74-
result
47+
end
48+
end
7549
end
7650
end

test/next_ls/helpers/ast_helpers_test.exs

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -74,53 +74,4 @@ defmodule NextLS.ASTHelpersTest do
7474
assert {{5, 5}, {5, 8}} == Aliases.extract_alias_range(code, {start, stop}, :Four)
7575
end
7676
end
77-
78-
describe "extract function params" do
79-
test "simple function params" do
80-
code = """
81-
@doc "foo doc"
82-
def foo(bar, baz) do
83-
:ok
84-
end
85-
"""
86-
87-
assert [{:bar, [line: 2, column: 9], nil}, {:baz, [line: 2, column: 14], nil}] ==
88-
ASTHelpers.Functions.get_function_params(code, "foo", 2, 5)
89-
end
90-
end
91-
92-
describe "extract function name from params" do
93-
test "alias function" do
94-
code = """
95-
defmodule MyModule do
96-
List.starts_with?(
97-
end
98-
"""
99-
100-
assert {[:List], :starts_with?} ==
101-
ASTHelpers.Functions.get_function_name_from_params(code, 2, 21)
102-
end
103-
104-
test "nested alias function" do
105-
code = """
106-
defmodule MyModule do
107-
List.starts_with?(String.trim()
108-
end
109-
"""
110-
111-
assert {[:String], :trim} ==
112-
ASTHelpers.Functions.get_function_name_from_params(code, 2, 37)
113-
end
114-
115-
test "simple function" do
116-
code = """
117-
defmodule MyModule do
118-
put_in(
119-
end
120-
"""
121-
122-
assert :put_in ==
123-
ASTHelpers.Functions.get_function_name_from_params(code, 2, 21)
124-
end
125-
end
12677
end

test/next_ls/signature_help_test.exs

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ defmodule NextLS.SignatureHelpTest do
2929

3030
File.write!(imported, """
3131
defmodule Imported do
32-
def boom(boom1, _boom2) do
32+
def boom([] = boom1, _boom2) do
3333
boom1
3434
end
3535
end
@@ -40,12 +40,22 @@ defmodule NextLS.SignatureHelpTest do
4040
File.write!(bar, """
4141
defmodule Bar do
4242
def run() do
43-
Remote.bang!()
43+
Remote.bang!("!")
4444
end
4545
end
4646
""")
4747

48-
[bar: bar, imported: imported, remote: remote]
48+
baz = Path.join(cwd, "my_proj/lib/baz.ex")
49+
50+
File.write!(baz, """
51+
defmodule Baz do
52+
def run() do
53+
Imported.boom([1, 2], 1)
54+
end
55+
end
56+
""")
57+
58+
[bar: bar, imported: imported, remote: remote, baz: baz]
4959
end
5060

5161
setup :with_lsp
@@ -63,56 +73,49 @@ defmodule NextLS.SignatureHelpTest do
6373
id: 4,
6474
jsonrpc: "2.0",
6575
params: %{
66-
position: %{line: 3, character: 16},
76+
position: %{line: 2, character: 13},
6777
textDocument: %{uri: uri}
6878
}
6979
})
7080

7181
assert_result 4, %{
72-
"activeParameter" => 0,
73-
"activeSignature" => 0,
7482
"signatures" => [
7583
%{
76-
"activeParameter" => 0,
7784
"parameters" => [
7885
%{"label" => "bang"}
7986
],
80-
"documentation" => "need help",
81-
"label" => "bang!"
87+
"label" => "bang!(bang)"
8288
}
8389
]
8490
}
8591
end
8692

87-
test "get signature help 2", %{client: client, bar: bar} = context do
93+
test "get signature help 2", %{client: client, baz: baz} = context do
8894
assert :ok == notify(client, %{method: "initialized", jsonrpc: "2.0", params: %{}})
8995

9096
assert_is_ready(context, "my_proj")
9197
assert_notification "$/progress", %{"value" => %{"kind" => "end", "message" => "Finished indexing!"}}
9298

93-
uri = uri(bar)
99+
uri = uri(baz)
94100

95101
request(client, %{
96102
method: "textDocument/signatureHelp",
97103
id: 4,
98104
jsonrpc: "2.0",
99105
params: %{
100-
position: %{line: 8, character: 10},
106+
position: %{line: 2, character: 13},
101107
textDocument: %{uri: uri}
102108
}
103109
})
104110

105111
assert_result 4, %{
106-
"activeParameter" => 0,
107-
"activeSignature" => 0,
108112
"signatures" => [
109113
%{
110-
"activeParameter" => 0,
111114
"parameters" => [
112-
%{"label" => "bang"}
115+
%{"label" => "[] = boom1"},
116+
%{"label" => "_boom2"}
113117
],
114-
"documentation" => "need help",
115-
"label" => "bang!"
118+
"label" => "boom([] = boom1, _boom2)"
116119
}
117120
]
118121
}

0 commit comments

Comments
 (0)