Skip to content

Commit defbe09

Browse files
committed
wip
1 parent 80d0679 commit defbe09

File tree

3 files changed

+131
-13
lines changed

3 files changed

+131
-13
lines changed

Diff for: lib/next_ls.ex

+17-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
defmodule NextLS do
2+
3+
4+
25
@moduledoc false
36
use GenLSP
47

@@ -18,6 +21,7 @@ defmodule NextLS do
1821
}
1922

2023
alias GenLSP.Requests.{
24+
TextDocumentDocumentSymbol,
2125
Initialize,
2226
Shutdown,
2327
TextDocumentFormatting,
@@ -38,7 +42,8 @@ defmodule NextLS do
3842
TextEdit,
3943
WorkDoneProgressBegin,
4044
WorkDoneProgressEnd,
41-
SymbolInformation
45+
SymbolInformation,
46+
DocumentSymbol
4247
}
4348

4449
alias NextLS.Runtime
@@ -98,12 +103,22 @@ defmodule NextLS do
98103
change: TextDocumentSyncKind.full()
99104
},
100105
document_formatting_provider: true,
101-
workspace_symbol_provider: true
106+
workspace_symbol_provider: true,
107+
document_symbol_provider: true
102108
},
103109
server_info: %{name: "NextLS"}
104110
}, assign(lsp, root_uri: root_uri)}
105111
end
106112

113+
def handle_request(%TextDocumentDocumentSymbol{params: %{text_document: %{uri: uri}}}, lsp) do
114+
file = URI.parse(uri).path
115+
116+
symbols = SymbolTable.symbols(lsp.assigns.symbol_table, file) |> List.wrap()
117+
dbg symbols
118+
119+
{:reply, symbols, lsp}
120+
end
121+
107122
def handle_request(%WorkspaceSymbol{params: %{query: query}}, lsp) do
108123
filter = fn sym ->
109124
if query == "" do

Diff for: lib/next_ls/symbol_table.ex

+107-9
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
defmodule NextLS.SymbolTable do
2+
23
@moduledoc false
34
use GenServer
45

6+
alias GenLSP.Structures.DocumentSymbol
7+
alias GenLSP.Structures.Range
8+
alias GenLSP.Structures.Position
9+
510
defmodule Symbol do
6-
defstruct [:file, :module, :type, :name, :line, :col]
11+
defstruct [:file, :module, :type, :name, :line, :col, :document]
712

813
def new(args) do
914
struct(__MODULE__, args)
@@ -20,6 +25,9 @@ defmodule NextLS.SymbolTable do
2025
@spec symbols(pid() | atom()) :: list(struct())
2126
def symbols(server), do: GenServer.call(server, :symbols)
2227

28+
@spec symbols(pid() | atom(), String.t()) :: list(struct())
29+
def symbols(server, file), do: GenServer.call(server, {:symbols, file})
30+
2331
def close(server), do: GenServer.call(server, :close)
2432

2533
def init(args) do
@@ -36,10 +44,26 @@ defmodule NextLS.SymbolTable do
3644
{:ok, %{table: name}}
3745
end
3846

47+
def handle_call({:symbols, file}, _, state) do
48+
symbols =
49+
case :dets.lookup(state.table, file) do
50+
[{_, symbols} | _rest] -> symbols
51+
_ -> []
52+
end
53+
54+
{:reply, symbols, state}
55+
end
56+
3957
def handle_call(:symbols, _, state) do
4058
symbols =
4159
:dets.foldl(
42-
fn {_key, symbol}, acc -> [symbol | acc] end,
60+
fn {_key, symbol}, acc ->
61+
if String.match?(to_string(symbol.name), ~r/__.*__/) do
62+
acc
63+
else
64+
[symbol | acc]
65+
end
66+
end,
4367
[],
4468
state.table
4569
)
@@ -59,10 +83,17 @@ defmodule NextLS.SymbolTable do
5983
module_line: module_line,
6084
struct: struct,
6185
file: file,
62-
defs: defs
86+
defs: defs,
87+
ast: ast
6388
} = symbols
6489

90+
{_new_ast, acc} = Macro.prewalk(ast, nil, &walker/2)
91+
92+
document = File.read!(file)
6593
:dets.delete(state.table, mod)
94+
:dets.delete(state.table, file)
95+
96+
:dets.insert(state.table, {file, acc})
6697

6798
:dets.insert(
6899
state.table,
@@ -73,7 +104,8 @@ defmodule NextLS.SymbolTable do
73104
type: :defmodule,
74105
name: Macro.to_string(mod),
75106
line: module_line,
76-
col: 1
107+
col: 1,
108+
document: document
77109
}}
78110
)
79111

@@ -89,14 +121,13 @@ defmodule NextLS.SymbolTable do
89121
type: :defstruct,
90122
name: "%#{Macro.to_string(mod)}{}",
91123
line: meta[:line],
92-
col: 1
124+
col: 1,
125+
document: document
93126
}}
94127
)
95128
end
96129

97-
for {name, {:v1, type, _meta, clauses}} <- defs,
98-
not String.match?(to_string(name), ~r/__.*__/),
99-
{meta, _, _, _} <- clauses do
130+
for {name, {:v1, type, _meta, clauses}} <- defs, {meta, _, _, _} <- clauses do
100131
:dets.insert(
101132
state.table,
102133
{mod,
@@ -106,11 +137,78 @@ defmodule NextLS.SymbolTable do
106137
type: type,
107138
name: name,
108139
line: meta[:line],
109-
col: meta[:column] || 1
140+
col: meta[:column] || 1,
141+
document: document
110142
}}
111143
)
112144
end
113145

114146
{:noreply, state}
115147
end
148+
149+
defp elixir_kind_to_lsp_kind(:defmodule), do: GenLSP.Enumerations.SymbolKind.module()
150+
defp elixir_kind_to_lsp_kind(:defstruct), do: GenLSP.Enumerations.SymbolKind.struct()
151+
152+
defp elixir_kind_to_lsp_kind(kind) when kind in [:def, :defp, :defmacro, :defmacrop],
153+
do: GenLSP.Enumerations.SymbolKind.function()
154+
155+
defp walker({:defmodule, meta, [name | _children]} = macro, nil) do
156+
{macro,
157+
%DocumentSymbol{
158+
name: Macro.to_string(name) |> String.replace("\n", ""),
159+
kind: GenLSP.Enumerations.SymbolKind.module(),
160+
children: [],
161+
range: %Range{
162+
start: %Position{line: meta[:do][:line] - 1, character: meta[:do][:column] - 1},
163+
end: %Position{line: meta[:end][:line] - 1, character: meta[:end][:column] - 1}
164+
},
165+
selection_range: %Range{
166+
start: %Position{line: meta[:line] - 1, character: meta[:column] - 1},
167+
end: %Position{line: meta[:line] - 1, character: meta[:column] - 1}
168+
}
169+
}}
170+
end
171+
172+
# TODO: this needs to be a normal recursive function traversal, so that we don't walk
173+
# these AST nodes anyway
174+
defp walker({:defmodule, _meta, [_name | _children]} = macro, %DocumentSymbol{} = doc) do
175+
{_, child} = Macro.prewalk(macro, nil, &walker/2)
176+
177+
dbg(child)
178+
{macro, %DocumentSymbol{doc | children: doc.children ++ [child]}}
179+
end
180+
181+
defp walker({type, meta, [name | _children]} = macro, %DocumentSymbol{} = root)
182+
when type in [:def, :defp, :defmacro, :defmacro, :defstruct] do
183+
{macro,
184+
%DocumentSymbol{
185+
root
186+
| children:
187+
root.children ++
188+
[
189+
%DocumentSymbol{
190+
name: Macro.to_string(name) |> String.replace("\n", "") |> dbg(),
191+
kind: elixir_kind_to_lsp_kind(type),
192+
range: %Range{
193+
start: %Position{
194+
line: meta[:line] - 1,
195+
character: meta[:column] - 1
196+
},
197+
end: %Position{
198+
line: (meta[:end] || meta[:end_of_expression] || meta)[:line] - 1,
199+
character: (meta[:end] || meta[:end_of_expression] || meta)[:column] - 1
200+
}
201+
},
202+
selection_range: %Range{
203+
start: %Position{line: meta[:line] - 1, character: meta[:column] - 1},
204+
end: %Position{line: meta[:line] - 1, character: meta[:column] - 1}
205+
}
206+
}
207+
]
208+
}}
209+
end
210+
211+
defp walker(other, acc) do
212+
{other, acc}
213+
end
116214
end

Diff for: priv/monkey/_next_ls_private_compiler.ex

+7-2
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,16 @@ defmodule NextLSPrivate.Tracer do
1111

1212
{:ok, {_, [{'Dbgi', bin}]}} = :beam_lib.chunks(bytecode, ['Dbgi'])
1313

14-
{:debug_info_v1, _, {_, %{line: line, struct: struct}, _}} = :erlang.binary_to_term(bin)
14+
ast =
15+
env.file
16+
|> File.read!()
17+
|> Code.string_to_quoted!(token_metadata: true, columns: true)
18+
19+
{:debug_info_v1, _, {_, %{line: line, struct: struct}, _} = foo} = :erlang.binary_to_term(bin)
1520

1621
Process.send(
1722
parent,
18-
{:tracer, %{file: env.file, module: env.module, module_line: line, struct: struct, defs: defs}},
23+
{:tracer, %{file: env.file, module: env.module, module_line: line, struct: struct, defs: defs, ast: ast}},
1924
[]
2025
)
2126

0 commit comments

Comments
 (0)