diff --git a/lib/next_ls.ex b/lib/next_ls.ex index bcde85c6..cc82357f 100644 --- a/lib/next_ls.ex +++ b/lib/next_ls.ex @@ -18,6 +18,7 @@ defmodule NextLS do } alias GenLSP.Requests.{ + TextDocumentDocumentSymbol, Initialize, Shutdown, TextDocumentFormatting, @@ -38,7 +39,8 @@ defmodule NextLS do TextEdit, WorkDoneProgressBegin, WorkDoneProgressEnd, - SymbolInformation + SymbolInformation, + DocumentSymbol } alias NextLS.Runtime @@ -98,12 +100,56 @@ defmodule NextLS do change: TextDocumentSyncKind.full() }, document_formatting_provider: true, - workspace_symbol_provider: true + workspace_symbol_provider: true, + document_symbol_provider: true }, server_info: %{name: "NextLS"} }, assign(lsp, root_uri: root_uri)} end + def handle_request(%TextDocumentDocumentSymbol{params: %{text_document: %{uri: uri}}}, lsp) do + file = URI.parse(uri).path + + {mod_symbol, children} = + for %SymbolTable.Symbol{} = symbol <- SymbolTable.symbols(lsp.assigns.symbol_table, file), reduce: {nil, []} do + {mod, children} -> + name = + if symbol.type != :defstruct do + "#{symbol.type} #{symbol.name}" + else + "#{symbol.name}" + end + + range = %Range{ + start: %Position{ + line: symbol.line - 1, + character: symbol.col - 1 + }, + end: %Position{ + line: symbol.line - 1, + character: symbol.col - 1 + } + } + + sym = %DocumentSymbol{ + name: name, + kind: elixir_kind_to_lsp_kind(symbol.type), + range: range, + selection_range: range + } + + if symbol.type == :defmodule do + {sym, children} + else + {mod, [sym | children]} + end + end + + symbols = %DocumentSymbol{mod_symbol | children: children} + + {:reply, [symbols], lsp} + end + def handle_request(%WorkspaceSymbol{params: %{query: query}}, lsp) do filter = fn sym -> if query == "" do diff --git a/lib/next_ls/symbol_table.ex b/lib/next_ls/symbol_table.ex index 695676b7..43091dba 100644 --- a/lib/next_ls/symbol_table.ex +++ b/lib/next_ls/symbol_table.ex @@ -20,6 +20,9 @@ defmodule NextLS.SymbolTable do @spec symbols(pid() | atom()) :: list(struct()) def symbols(server), do: GenServer.call(server, :symbols) + @spec symbols(pid() | atom(), String.t()) :: list(struct()) + def symbols(server, file), do: GenServer.call(server, {:symbols, file}) + def close(server), do: GenServer.call(server, :close) def init(args) do @@ -36,10 +39,34 @@ defmodule NextLS.SymbolTable do {:ok, %{table: name}} end + def handle_call({:symbols, file}, _, state) do + symbols = + :dets.foldl( + fn {_key, symbol}, acc -> + if file == symbol.file do + [symbol | acc] + else + acc + end + end, + [], + state.table + ) + |> Enum.reverse() + + {:reply, symbols, state} + end + def handle_call(:symbols, _, state) do symbols = :dets.foldl( - fn {_key, symbol}, acc -> [symbol | acc] end, + fn {_key, symbol}, acc -> + if String.match?(to_string(symbol.name), ~r/__.*__/) do + acc + else + [symbol | acc] + end + end, [], state.table ) @@ -94,9 +121,7 @@ defmodule NextLS.SymbolTable do ) end - for {name, {:v1, type, _meta, clauses}} <- defs, - not String.match?(to_string(name), ~r/__.*__/), - {meta, _, _, _} <- clauses do + for {name, {:v1, type, _meta, clauses}} <- defs, {meta, _, _, _} <- clauses do :dets.insert( state.table, {mod,