Skip to content

Commit

Permalink
Add debugger variable scopes with messages and process info (#681)
Browse files Browse the repository at this point in the history
* delete duplicated capability

* add process messages as debugger variable scope

* improve display of keyword variables

* add process info as debugger variable scope
  • Loading branch information
lukaszsamson authored Mar 6, 2022
1 parent ba6252f commit 99ab6e9
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 10 deletions.
26 changes: 24 additions & 2 deletions apps/elixir_ls_debugger/lib/debugger/server.ex
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,9 @@ defmodule ElixirLS.Debugger.Server do
{pid, %Frame{} = frame} ->
{state, args_id} = ensure_var_id(state, pid, frame.args)
{state, bindings_id} = ensure_var_id(state, pid, frame.bindings)
{state, messages_id} = ensure_var_id(state, pid, frame.messages)
process_info = Process.info(pid)
{state, process_info_id} = ensure_var_id(state, pid, process_info)

vars_scope = %{
"name" => "variables",
Expand All @@ -526,7 +529,27 @@ defmodule ElixirLS.Debugger.Server do
"expensive" => false
}

scopes = if Enum.count(frame.args) > 0, do: [vars_scope, args_scope], else: [vars_scope]
messages_scope = %{
"name" => "messages",
"variablesReference" => messages_id,
"namedVariables" => 0,
"indexedVariables" => Enum.count(frame.messages),
"expensive" => false
}

process_info_scope = %{
"name" => "process info",
"variablesReference" => process_info_id,
"namedVariables" => length(process_info),
"indexedVariables" => 0,
"expensive" => false
}

scopes =
[vars_scope, process_info_scope]
|> Kernel.++(if Enum.count(frame.args) > 0, do: [args_scope], else: [])
|> Kernel.++(if Enum.count(frame.messages) > 0, do: [messages_scope], else: [])

{state, scopes}

nil ->
Expand Down Expand Up @@ -1006,7 +1029,6 @@ defmodule ElixirLS.Debugger.Server do
"supportsConditionalBreakpoints" => true,
"supportsHitConditionalBreakpoints" => true,
"supportsLogPoints" => true,
"supportsEvaluateForHovers" => false,
"exceptionBreakpointFilters" => [],
"supportsStepBack" => false,
"supportsSetVariable" => false,
Expand Down
10 changes: 7 additions & 3 deletions apps/elixir_ls_debugger/lib/debugger/stacktrace.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule ElixirLS.Debugger.Stacktrace do
"""

defmodule Frame do
defstruct [:level, :file, :module, :function, :args, :line, :bindings]
defstruct [:level, :file, :module, :function, :args, :line, :bindings, :messages]

def name(%__MODULE__{} = frame) do
"#{inspect(frame.module)}.#{frame.function}/#{Enum.count(frame.args)}"
Expand All @@ -17,14 +17,17 @@ defmodule ElixirLS.Debugger.Stacktrace do
[{level, {module, function, args}} | backtrace_rest] =
:int.meta(meta_pid, :backtrace, :all)

messages = :int.meta(meta_pid, :messages)

first_frame = %Frame{
level: level,
module: module,
function: function,
args: args,
file: get_file(module),
line: break_line(pid),
bindings: get_bindings(meta_pid, level)
bindings: get_bindings(meta_pid, level),
messages: messages
}

# If backtrace_rest is empty, calling stack_frames causes an exception
Expand All @@ -44,7 +47,8 @@ defmodule ElixirLS.Debugger.Stacktrace do
args: args,
file: get_file(mod),
line: line,
bindings: Enum.into(bindings, %{})
bindings: Enum.into(bindings, %{}),
messages: messages
}
end
end
Expand Down
32 changes: 27 additions & 5 deletions apps/elixir_ls_debugger/lib/debugger/variables.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,31 @@ defmodule ElixirLS.Debugger.Variables do
def child_type(var) when is_map(var), do: :named
def child_type(var) when is_bitstring(var), do: :indexed
def child_type(var) when is_tuple(var), do: :indexed
def child_type(var) when is_list(var), do: :indexed

def child_type(var) when is_list(var) do
if Keyword.keyword?(var) do
:named
else
:indexed
end
end

def child_type(_var), do: nil

def children(var, start, count) when is_list(var) do
start = start || 0
count = count || Enum.count(var)

var
|> Enum.slice(start, count)
|> with_index_as_name(start)
sliced =
var
|> Enum.slice(start, count)

if Keyword.keyword?(var) do
sliced
else
sliced
|> with_index_as_name(start)
end
end

def children(var, start, count) when is_tuple(var) do
Expand Down Expand Up @@ -90,7 +105,14 @@ defmodule ElixirLS.Debugger.Variables do
def type(var) when is_float(var), do: "float"
def type(var) when is_function(var), do: "function"
def type(var) when is_integer(var), do: "integer"
def type(var) when is_list(var), do: "list"

def type(var) when is_list(var) do
if Keyword.keyword?(var) and var != [] do
"keyword"
else
"list"
end
end

def type(%name{}), do: "%#{inspect(name)}{}"

Expand Down
7 changes: 7 additions & 0 deletions apps/elixir_ls_debugger/test/debugger_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,13 @@ defmodule ElixirLS.Debugger.ServerTest do
"namedVariables" => 1,
"variablesReference" => vars_id
},
%{
"expensive" => false,
"indexedVariables" => 0,
"name" => "process info",
"namedVariables" => _,
"variablesReference" => _
},
%{
"expensive" => false,
"indexedVariables" => 1,
Expand Down
18 changes: 18 additions & 0 deletions apps/elixir_ls_debugger/test/variables_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ defmodule ElixirLS.Debugger.VariablesTest do
assert Variables.type([1]) == "list"
assert Variables.type('asd') == "list"

assert Variables.type(abc: 123) == "keyword"

assert Variables.type(%{}) == "map"
assert Variables.type(%{asd: 123}) == "map"
assert Variables.type(%{"asd" => 123}) == "map"
Expand Down Expand Up @@ -87,6 +89,8 @@ defmodule ElixirLS.Debugger.VariablesTest do
assert Variables.num_children([1]) == 1
assert Variables.num_children('asd') == 3

assert Variables.num_children(abc: 123) == 1

assert Variables.num_children(%{}) == 0
assert Variables.num_children(%{asd: 123}) == 1
assert Variables.num_children(%{"asd" => 123}) == 1
Expand All @@ -104,6 +108,20 @@ defmodule ElixirLS.Debugger.VariablesTest do
assert Variables.children('asd', 0, 10) == [{"0", 97}, {"1", 115}, {"2", 100}]
end

test "keyword" do
assert Variables.children([abc: 123], 0, 10) == [abc: 123]

assert Variables.children([abc1: 121, abc2: 122, abc3: 123, abc4: 124], 0, 2) == [
abc1: 121,
abc2: 122
]

assert Variables.children([abc1: 121, abc2: 122, abc3: 123, abc4: 124], 1, 2) == [
abc2: 122,
abc3: 123
]
end

test "tuple" do
assert Variables.children({}, 0, 10) == []
assert Variables.children({1}, 0, 10) == [{"0", 1}]
Expand Down

0 comments on commit 99ab6e9

Please sign in to comment.