Skip to content

Commit d847992

Browse files
committed
dbg backport 1
1 parent b772097 commit d847992

1 file changed

Lines changed: 132 additions & 1 deletion

File tree

  • apps/debug_adapter/lib/debug_adapter

apps/debug_adapter/lib/debug_adapter/server.ex

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,79 @@ defmodule ElixirLS.DebugAdapter.Server do
128128
end
129129

130130
def dbg(code, options, %Macro.Env{} = caller) do
131+
options = Keyword.put(options, :print_location, false)
132+
131133
quote do
132134
{:current_stacktrace, stacktrace} = Process.info(self(), :current_stacktrace)
133135
GenServer.call(unquote(__MODULE__), {:dbg, binding(), __ENV__, stacktrace}, :infinity)
134136
unquote(Macro.dbg(code, options, caller))
135137
end
136138
end
137139

138-
def __next__(next?, binding, opts_or_env) when is_boolean(next?) do
140+
@doc """
141+
Annotate a quoted expression with line-by-line debugging steps.
142+
"""
143+
@spec annotate_quoted(Macro.t(), Macro.t(), Macro.Env.t()) :: Macro.t()
144+
def annotate_quoted(quoted, condition, %Macro.Env{} = caller) do
145+
prelude =
146+
quote do
147+
[
148+
env = unquote(Macro.escape(Macro.Env.prune_compile_info(caller))),
149+
next? = unquote(condition)
150+
]
151+
end
152+
153+
next_pry =
154+
fn line, _version, _binding ->
155+
quote do
156+
next? = unquote(__MODULE__).__next__(next?, binding(), %{env | line: unquote(line)})
157+
end
158+
end
159+
160+
annotate_quoted(quoted, prelude, caller.line, 0, :ok, fn _, _ -> :ok end, next_pry)
161+
end
162+
163+
defp annotate_quoted(maybe_block, prelude, line, version, binding, next_binding, next_pry)
164+
when is_list(prelude) do
165+
exprs =
166+
maybe_block
167+
|> unwrap_block()
168+
|> annotate_quoted(true, line, version, binding, {next_binding, next_pry})
169+
170+
{:__block__, [], prelude ++ exprs}
171+
end
172+
173+
defp annotate_quoted([expr | exprs], force?, line, version, binding, funs) do
174+
{next_binding, next_pry} = funs
175+
new_binding = next_binding.(expr, binding)
176+
{min_line, max_line} = line_range(expr, line)
177+
178+
if force? or min_line > line do
179+
[
180+
next_pry.(min_line, version, binding),
181+
expr | annotate_quoted(exprs, false, max_line, version + 1, new_binding, funs)
182+
]
183+
else
184+
[expr | annotate_quoted(exprs, false, max_line, version, new_binding, funs)]
185+
end
186+
end
187+
188+
defp annotate_quoted([], _force?, _line, _version, _binding, _funs) do
189+
[]
190+
end
191+
192+
def __next__(next?, binding, opts) when is_boolean(next?) and is_list(opts) do
193+
vars = for {key, _} when is_atom(key) <- binding, do: {key, nil}
194+
195+
env =
196+
opts
197+
|> Code.env_for_eval()
198+
|> :elixir_env.with_vars(vars)
199+
200+
__next__(next?, binding, env)
201+
end
202+
203+
def __next__(next?, binding, %Macro.Env{} = opts_or_env) when is_boolean(next?) do
139204
if next? do
140205
{:current_stacktrace, stacktrace} = Process.info(self(), :current_stacktrace)
141206

@@ -3179,6 +3244,72 @@ defmodule ElixirLS.DebugAdapter.Server do
31793244
Enum.map(asts, fn {ast, _pipe_index} -> Macro.to_string(ast) end)
31803245
end
31813246

3247+
defp line_range(ast, line) do
3248+
{_, {min, max}} =
3249+
Macro.prewalk(ast, {:infinity, line}, fn
3250+
{_, meta, _} = current_ast, {min_line, max_line} when is_list(meta) ->
3251+
case Keyword.fetch(meta, :line) do
3252+
{:ok, current_line} when current_line > 0 ->
3253+
{current_ast, {min(current_line, min_line), max(current_line, max_line)}}
3254+
3255+
_ ->
3256+
{current_ast, {min_line, max_line}}
3257+
end
3258+
3259+
current_ast, acc ->
3260+
{current_ast, acc}
3261+
end)
3262+
3263+
if min == :infinity, do: {line, max}, else: {min, max}
3264+
end
3265+
3266+
@doc false
3267+
def next_binding(ast, binding) do
3268+
{_, binding} =
3269+
Macro.prewalk(ast, binding, fn
3270+
{:=, _, [left, _right]}, acc ->
3271+
{:ok, match_binding(left, acc)}
3272+
3273+
{:case, _, [arg, _block]}, acc ->
3274+
{arg, acc}
3275+
3276+
{special_form, _, _}, acc
3277+
when special_form in [:cond, :fn, :for, :receive, :try, :with] ->
3278+
{:ok, acc}
3279+
3280+
current_ast, acc ->
3281+
{current_ast, acc}
3282+
end)
3283+
3284+
binding
3285+
end
3286+
3287+
@doc false
3288+
def match_binding(match, binding) do
3289+
{_, binding} =
3290+
Macro.prewalk(match, binding, fn
3291+
{name, _, nil} = var, acc when name != :_ and is_atom(name) ->
3292+
{var, Map.put(acc, name, var)}
3293+
3294+
{special_form, _, _}, acc when special_form in [:^, :"::"] ->
3295+
{:ok, acc}
3296+
3297+
current_ast, acc ->
3298+
{current_ast, acc}
3299+
end)
3300+
3301+
binding
3302+
end
3303+
3304+
@doc false
3305+
def next_var(id) do
3306+
{:next?, [version: -id], __MODULE__}
3307+
end
3308+
3309+
defp unwrap_block(expr), do: expr |> unwrap_block([]) |> Enum.reverse()
3310+
defp unwrap_block({:__block__, _, exprs}, acc), do: Enum.reduce(exprs, acc, &unwrap_block/2)
3311+
defp unwrap_block(expr, acc), do: [expr | acc]
3312+
31823313
defp env_with_line_from_asts(asts) do
31833314
line =
31843315
Enum.find_value(asts, fn

0 commit comments

Comments
 (0)