Skip to content

Commit

Permalink
feat: workspace symbols
Browse files Browse the repository at this point in the history
  • Loading branch information
mhanberg committed Jun 25, 2023
1 parent d9fdb2d commit 998429d
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 20 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Still in heavy development, currently supporting the following features:

- Compiler Diagnostics
- Code Formatting
- Workspace Symbols

## Editor Support

Expand Down
49 changes: 42 additions & 7 deletions lib/next_ls.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ defmodule NextLS do
alias GenLSP.Requests.{
Initialize,
Shutdown,
TextDocumentFormatting
TextDocumentFormatting,
WorkspaceSymbol
}

alias GenLSP.Structures.{
Expand All @@ -29,13 +30,15 @@ defmodule NextLS do
InitializeResult,
Position,
Range,
Location,
SaveOptions,
ServerCapabilities,
TextDocumentItem,
TextDocumentSyncOptions,
TextEdit,
WorkDoneProgressBegin,
WorkDoneProgressEnd
WorkDoneProgressEnd,
SymbolInformation
}

alias NextLS.Runtime
Expand Down Expand Up @@ -94,12 +97,38 @@ defmodule NextLS do
save: %SaveOptions{include_text: true},
change: TextDocumentSyncKind.full()
},
document_formatting_provider: true
document_formatting_provider: true,
workspace_symbol_provider: true
},
server_info: %{name: "NextLS"}
}, assign(lsp, root_uri: root_uri)}
end

def handle_request(%WorkspaceSymbol{params: %{query: _query}}, lsp) do
symbols =
for %SymbolTable.Symbol{} = symbol <- SymbolTable.symbols(lsp.assigns.symbol_table) do
%SymbolInformation{
name: to_string(symbol.name),
kind: elixir_kind_to_lsp_kind(symbol.type),
location: %Location{
uri: "file://#{symbol.file}",
range: %Range{
start: %Position{
line: symbol.line - 1,
character: symbol.col - 1
},
end: %Position{
line: symbol.line - 1,
character: symbol.col - 1
}
}
}
}
end

{:reply, symbols, lsp}
end

def handle_request(%TextDocumentFormatting{params: %{text_document: %{uri: uri}}}, lsp) do
document = lsp.assigns.documents[uri]
runtime = lsp.assigns.runtime
Expand Down Expand Up @@ -215,8 +244,6 @@ defmodule NextLS do
},
%{assigns: %{ready: true}} = lsp
) do
dbg(self())
dbg(Node.self())
token = token()

progress_start(lsp, token, "Compiling...")
Expand Down Expand Up @@ -276,10 +303,13 @@ defmodule NextLS do

def handle_info({:tracer, payload}, lsp) do
SymbolTable.put_symbols(lsp.assigns.symbol_table, payload)
GenLSP.log(lsp, "[NextLS] Updated the symbols table!")
{:noreply, lsp}
end

def handle_info(:publish, lsp) do
GenLSP.log(lsp, "[NextLS] Compiled!")

all =
for {_namespace, cache} <- DiagnosticCache.get(lsp.assigns.cache), {file, diagnostics} <- cache, reduce: %{} do
d -> Map.update(d, file, diagnostics, fn value -> value ++ diagnostics end)
Expand Down Expand Up @@ -354,8 +384,7 @@ defmodule NextLS do
end

def handle_info(message, lsp) do
IO.puts("catcall")
dbg(message)
GenLSP.log(lsp, "[NextLS] Unhanded message: #{inspect(message)}")
{:noreply, lsp}
end

Expand Down Expand Up @@ -401,4 +430,10 @@ defmodule NextLS do
_ -> "dev"
end
end

defp elixir_kind_to_lsp_kind(:defmodule), do: GenLSP.Enumerations.SymbolKind.module()
defp elixir_kind_to_lsp_kind(:defstruct), do: GenLSP.Enumerations.SymbolKind.struct()

defp elixir_kind_to_lsp_kind(kind) when kind in [:def, :defp, :defmacro, :defmacrop],
do: GenLSP.Enumerations.SymbolKind.function()
end
45 changes: 43 additions & 2 deletions lib/next_ls/symbol_table.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ defmodule NextLS.SymbolTable do

@spec put_symbols(pid() | atom(), list(tuple())) :: :ok
def put_symbols(server, symbols), do: GenServer.cast(server, {:put_symbols, symbols})

@spec symbols(pid() | atom()) :: list(struct())
def symbols(server), do: GenServer.call(server, :symbols)

def close(server), do: GenServer.call(server, :close)

def init(args) do
path = Keyword.fetch!(args, :path)

Expand All @@ -42,16 +45,54 @@ defmodule NextLS.SymbolTable do
{:reply, symbols, state}
end

def handle_call(:close, _, state) do
:dets.close(state.table)

{:reply, :ok, state}
end

def handle_cast({:put_symbols, symbols}, state) do
%{
module: mod,
module_line: module_line,
struct: struct,
file: file,
defs: defs
} = symbols

:dets.delete(state.table, mod)

for {name, {:v1, type, _meta, clauses}} <- defs, {meta, _, _, _} <- clauses do
:dets.insert(
state.table,
{mod,
%Symbol{
module: mod,
file: file,
type: :defmodule,
name: Macro.to_string(mod),
line: module_line,
col: 1
}}
)

if struct do
{_, _, meta, _} = defs[:__struct__]

:dets.insert(
state.table,
{mod,
%Symbol{
module: mod,
file: file,
type: :defstruct,
name: "%#{Macro.to_string(mod)}{}",
line: meta[:line],
col: 1
}}
)
end

for {name, {:v1, type, _meta, clauses}} <- defs, name != :__struct__, {meta, _, _, _} <- clauses do
:dets.insert(
state.table,
{mod,
Expand All @@ -61,7 +102,7 @@ defmodule NextLS.SymbolTable do
type: type,
name: name,
line: meta[:line],
col: meta[:column]
col: meta[:column] || 1
}}
)
end
Expand Down
12 changes: 10 additions & 2 deletions priv/monkey/_next_ls_private_compiler.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
defmodule NextLSPrivate.Tracer do
def trace({:on_module, _, _}, env) do
def trace({:on_module, bytecode, _}, env) do
parent = "NEXTLS_PARENT_PID" |> System.get_env() |> Base.decode64!() |> :erlang.binary_to_term()

defs = Module.definitions_in(env.module)
Expand All @@ -9,7 +9,15 @@ defmodule NextLSPrivate.Tracer do
{name, Module.get_definition(env.module, {name, arity})}
end

Process.send(parent, {:tracer, %{file: env.file, module: env.module, defs: defs}}, [])
{:ok, {_, [{'Dbgi', bin}]}} = :beam_lib.chunks(bytecode, ['Dbgi'])

{:debug_info_v1, _, {_, %{line: line, struct: struct}, _}} = :erlang.binary_to_term(bin)

Process.send(
parent,
{:tracer, %{file: env.file, module: env.module, module_line: line, struct: struct, defs: defs}},
[]
)

:ok
end
Expand Down
2 changes: 1 addition & 1 deletion test/next_ls/runtime_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ defmodule NextLs.RuntimeTest do
severity: :warning,
message:
"variable \"arg1\" is unused (if the variable is not meant to be used, prefix it with an underscore)",
position: 2,
position: 4,
compiler_name: "Elixir",
details: nil
}
Expand Down
20 changes: 15 additions & 5 deletions test/next_ls/symbol_table_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,38 @@ defmodule NextLS.SymbolTableTest do

assert [
%SymbolTable.Symbol{
module: "NextLS",
module: NextLS,
file: "/Users/alice/next_ls/lib/next_ls.ex",
type: :def,
name: :start_link,
line: 45,
col: nil
col: 1
},
%SymbolTable.Symbol{
module: "NextLS",
module: NextLS,
file: "/Users/alice/next_ls/lib/next_ls.ex",
type: :def,
name: :start_link,
line: 44,
col: nil
col: 1
},
%SymbolTable.Symbol{
module: NextLS,
file: "/Users/alice/next_ls/lib/next_ls.ex",
type: :defmodule,
name: "NextLS",
line: 1,
col: 1
}
] == SymbolTable.symbols(pid)
end

defp symbols() do
%{
file: "/Users/alice/next_ls/lib/next_ls.ex",
module: "NextLS",
module: NextLS,
module_line: 1,
struct: nil,
defs: [
start_link:
{:v1, :def, [line: 44],
Expand Down
Loading

0 comments on commit 998429d

Please sign in to comment.