Skip to content
This repository was archived by the owner on Oct 8, 2025. It is now read-only.

Commit 7352dd8

Browse files
committed
feat: document symbols
Closes #41
1 parent 80d0679 commit 7352dd8

File tree

2 files changed

+77
-6
lines changed

2 files changed

+77
-6
lines changed

lib/next_ls.ex

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ defmodule NextLS do
1818
}
1919

2020
alias GenLSP.Requests.{
21+
TextDocumentDocumentSymbol,
2122
Initialize,
2223
Shutdown,
2324
TextDocumentFormatting,
@@ -38,7 +39,8 @@ defmodule NextLS do
3839
TextEdit,
3940
WorkDoneProgressBegin,
4041
WorkDoneProgressEnd,
41-
SymbolInformation
42+
SymbolInformation,
43+
DocumentSymbol
4244
}
4345

4446
alias NextLS.Runtime
@@ -98,12 +100,56 @@ defmodule NextLS do
98100
change: TextDocumentSyncKind.full()
99101
},
100102
document_formatting_provider: true,
101-
workspace_symbol_provider: true
103+
workspace_symbol_provider: true,
104+
document_symbol_provider: true
102105
},
103106
server_info: %{name: "NextLS"}
104107
}, assign(lsp, root_uri: root_uri)}
105108
end
106109

110+
def handle_request(%TextDocumentDocumentSymbol{params: %{text_document: %{uri: uri}}}, lsp) do
111+
file = URI.parse(uri).path
112+
113+
{mod_symbol, children} =
114+
for %SymbolTable.Symbol{} = symbol <- SymbolTable.symbols(lsp.assigns.symbol_table, file), reduce: {nil, []} do
115+
{mod, children} ->
116+
name =
117+
if symbol.type != :defstruct do
118+
"#{symbol.type} #{symbol.name}"
119+
else
120+
"#{symbol.name}"
121+
end
122+
123+
range = %Range{
124+
start: %Position{
125+
line: symbol.line - 1,
126+
character: symbol.col - 1
127+
},
128+
end: %Position{
129+
line: symbol.line - 1,
130+
character: symbol.col - 1
131+
}
132+
}
133+
134+
sym = %DocumentSymbol{
135+
name: name,
136+
kind: elixir_kind_to_lsp_kind(symbol.type),
137+
range: range,
138+
selection_range: range
139+
}
140+
141+
if symbol.type == :defmodule do
142+
{sym, children}
143+
else
144+
{mod, [sym | children]}
145+
end
146+
end
147+
148+
symbols = %DocumentSymbol{mod_symbol | children: children}
149+
150+
{:reply, [symbols], lsp}
151+
end
152+
107153
def handle_request(%WorkspaceSymbol{params: %{query: query}}, lsp) do
108154
filter = fn sym ->
109155
if query == "" do

lib/next_ls/symbol_table.ex

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ defmodule NextLS.SymbolTable do
2020
@spec symbols(pid() | atom()) :: list(struct())
2121
def symbols(server), do: GenServer.call(server, :symbols)
2222

23+
@spec symbols(pid() | atom(), String.t()) :: list(struct())
24+
def symbols(server, file), do: GenServer.call(server, {:symbols, file})
25+
2326
def close(server), do: GenServer.call(server, :close)
2427

2528
def init(args) do
@@ -36,10 +39,34 @@ defmodule NextLS.SymbolTable do
3639
{:ok, %{table: name}}
3740
end
3841

42+
def handle_call({:symbols, file}, _, state) do
43+
symbols =
44+
:dets.foldl(
45+
fn {_key, symbol}, acc ->
46+
if file == symbol.file do
47+
[symbol | acc]
48+
else
49+
acc
50+
end
51+
end,
52+
[],
53+
state.table
54+
)
55+
|> Enum.reverse()
56+
57+
{:reply, symbols, state}
58+
end
59+
3960
def handle_call(:symbols, _, state) do
4061
symbols =
4162
:dets.foldl(
42-
fn {_key, symbol}, acc -> [symbol | acc] end,
63+
fn {_key, symbol}, acc ->
64+
if String.match?(to_string(symbol.name), ~r/__.*__/) do
65+
acc
66+
else
67+
[symbol | acc]
68+
end
69+
end,
4370
[],
4471
state.table
4572
)
@@ -94,9 +121,7 @@ defmodule NextLS.SymbolTable do
94121
)
95122
end
96123

97-
for {name, {:v1, type, _meta, clauses}} <- defs,
98-
not String.match?(to_string(name), ~r/__.*__/),
99-
{meta, _, _, _} <- clauses do
124+
for {name, {:v1, type, _meta, clauses}} <- defs, {meta, _, _, _} <- clauses do
100125
:dets.insert(
101126
state.table,
102127
{mod,

0 commit comments

Comments
 (0)