Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
ef55412
add elixir_check command
lukaszsamson May 24, 2025
baa1812
fix unicode handling in CodeUnit
lukaszsamson May 24, 2025
c7e66d0
do not crash when getting process info on dead process
lukaszsamson May 25, 2025
2ec2b18
Do not crash if Exception.blame crashes on user code or stdlib error
lukaszsamson May 26, 2025
c803728
fix crash on invalid locals_without_parens
lukaszsamson May 27, 2025
d5d4671
respect :* in locals_without_parens
lukaszsamson May 27, 2025
fc30f74
fix crash on invalid settings
lukaszsamson May 27, 2025
10606c5
Partially revert "do not crash when getting process info on dead proc…
lukaszsamson Jun 2, 2025
f0cd04d
Revert "assume 1.14+"
lukaszsamson Jun 1, 2025
696c8f0
wip
lukaszsamson Mar 11, 2025
974c1b6
wip
lukaszsamson Mar 11, 2025
49899ca
checkpoint
lukaszsamson Mar 13, 2025
70020d1
cleanup
lukaszsamson Mar 16, 2025
cf04201
launch and attach
lukaszsamson Mar 16, 2025
1df2c48
remove derive
lukaszsamson Jun 1, 2025
383745e
Refactor sequence id handling
lukaszsamson Jun 7, 2025
f462b49
remove not needed clause
lukaszsamson Jun 7, 2025
6005259
migration to gen_lsp part 1
lukaszsamson Jun 7, 2025
11acc7b
step 2
lukaszsamson Jun 8, 2025
24c2012
refactor
lukaszsamson Jun 8, 2025
a8efe43
step 3
lukaszsamson Jun 8, 2025
497af6c
step 4
lukaszsamson Jun 8, 2025
7053581
step 5
lukaszsamson Jun 8, 2025
3353bdc
step 6
lukaszsamson Jun 8, 2025
20a0350
step 7
lukaszsamson Jun 9, 2025
a060c7b
step 8
lukaszsamson Jun 9, 2025
c85d715
cleanup
lukaszsamson Jun 9, 2025
525c2b4
step 9
lukaszsamson Jun 9, 2025
372ebae
step 10
lukaszsamson Jun 9, 2025
1f68992
step 11
lukaszsamson Jun 9, 2025
e4b088b
fix tests
lukaszsamson Jun 10, 2025
4abe835
test fixes
lukaszsamson Jun 13, 2025
545ccb1
cleanup
lukaszsamson Jun 13, 2025
a2e206b
fixing tests
lukaszsamson Jun 13, 2025
07fb812
fix bugs
lukaszsamson Jun 15, 2025
88b55aa
fixes
lukaszsamson Jun 15, 2025
dda6945
fix tests
lukaszsamson Jun 15, 2025
e665801
fix tests
lukaszsamson Jun 15, 2025
20325c2
fix tests
lukaszsamson Jun 15, 2025
1b4aff4
missing migrations
lukaszsamson Jun 15, 2025
0066c90
move legacy macros to test
lukaszsamson Jun 15, 2025
5929042
format
lukaszsamson Jun 15, 2025
5ab947e
pattern match requests
lukaszsamson Jun 15, 2025
846374a
refactor capabilites
lukaszsamson Jun 16, 2025
cd4103f
fix tests
lukaszsamson Jun 16, 2025
3f45c2c
format
lukaszsamson Jun 16, 2025
d48394c
add types
lukaszsamson Jun 16, 2025
2051763
Revert "add types"
lukaszsamson Jun 16, 2025
60cd479
...
lukaszsamson Jun 16, 2025
4e83fa9
fixes
lukaszsamson Jun 16, 2025
0ffcc8a
fix
lukaszsamson Jun 16, 2025
ed2e61a
fix ci
lukaszsamson Jun 16, 2025
6b4776a
vendor schematic
lukaszsamson Jun 16, 2025
0983a3e
fix dialyzer issues
lukaszsamson Jun 17, 2025
702f390
fix dialyzer issues
lukaszsamson Jun 17, 2025
32a600d
fix dialyzer
lukaszsamson Jun 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,24 +90,34 @@ jobs:
include:
- elixir: 1.14.x
otp: 23.x
tests_may_fail: false
- elixir: 1.14.x
otp: 24.x
tests_may_fail: false
- elixir: 1.14.x
otp: 25.x
tests_may_fail: false
- elixir: 1.14.x
otp: 26.x
tests_may_fail: false
- elixir: 1.15.x
otp: 24.x
tests_may_fail: false
- elixir: 1.15.x
otp: 25.x
tests_may_fail: false
- elixir: 1.15.x
otp: 26.x
tests_may_fail: false
- elixir: 1.16.x
otp: 24.x
tests_may_fail: false
- elixir: 1.16.x
otp: 25.x
tests_may_fail: false
- elixir: 1.16.x
otp: 26.x
tests_may_fail: false
- elixir: 1.17.x
otp: 25.x
tests_may_fail: false
Expand Down
5 changes: 3 additions & 2 deletions apps/debug_adapter/lib/debug_adapter/breakpoint_condition.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ defmodule ElixirLS.DebugAdapter.BreakpointCondition do
module,
module,
non_neg_integer,
Macro.Env.t(),
String.t(),
String.t() | nil,
String.t()
Expand All @@ -29,7 +30,7 @@ defmodule ElixirLS.DebugAdapter.BreakpointCondition do
)
end

@spec unregister_condition(module, module, non_neg_integer) :: :ok
@spec unregister_condition(module, module, [non_neg_integer]) :: :ok
def unregister_condition(name \\ __MODULE__, module, line) do
GenServer.cast(name, {:unregister_condition, {module, line}})
end
Expand All @@ -40,7 +41,7 @@ defmodule ElixirLS.DebugAdapter.BreakpointCondition do
end

@spec get_condition(module, non_neg_integer) ::
{String.t(), String.t(), non_neg_integer, non_neg_integer}
{Macro.Env.t(), String.t(), String.t(), String.t(), non_neg_integer}
def get_condition(name \\ __MODULE__, number) do
GenServer.call(name, {:get_condition, number})
end
Expand Down
8 changes: 4 additions & 4 deletions apps/debug_adapter/lib/debug_adapter/completions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defmodule ElixirLS.DebugAdapter.Completions do
snippet: snippet
})
when type in [:function, :macro] do
%{
%GenDAP.Structures.CompletionItem{
type: "function",
detail: Atom.to_string(type),
label: "#{name}/#{arity}",
Expand All @@ -29,7 +29,7 @@ defmodule ElixirLS.DebugAdapter.Completions do
other -> other
end

%{
%GenDAP.Structures.CompletionItem{
type: "module",
detail: if(subtype != nil, do: Atom.to_string(subtype)),
label: name,
Expand All @@ -41,7 +41,7 @@ defmodule ElixirLS.DebugAdapter.Completions do
type: :variable,
name: name
}) do
%{
%GenDAP.Structures.CompletionItem{
type: "variable",
label: name
}
Expand All @@ -58,7 +58,7 @@ defmodule ElixirLS.DebugAdapter.Completions do
:map_key -> "map key"
end

%{
%GenDAP.Structures.CompletionItem{
type: "field",
detail: detail,
label: name
Expand Down
59 changes: 59 additions & 0 deletions apps/debug_adapter/lib/debug_adapter/id_manager.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
defmodule ElixirLS.DebugAdapter.IdManager do
@moduledoc """
Global ID manager for Debug Adapter Protocol objects.

Uses ERTS :atomics module for thread-safe, shared mutable counters
that can be accessed from any process without blocking.
"""

@counter_ref :dap_id_counter

@doc """
Initializes the global ID counter.
Should be called once during server startup.
"""
def init do
# Create a single atomic counter starting at 1
counter = :atomics.new(1, [])
:atomics.put(counter, 1, 1)
:persistent_term.put(@counter_ref, counter)
:ok
end

@doc """
Gets the next unique ID atomically.
This is thread-safe and can be called from any process.
"""
def next_id do
case :persistent_term.get(@counter_ref, nil) do
nil ->
raise "IdManager not initialized. Call IdManager.init/0 first."

counter ->
:atomics.add_get(counter, 1, 1)
end
end

@doc """
Cleans up the global ID counter.
Should be called during server shutdown.
"""
def cleanup do
:persistent_term.erase(@counter_ref)
:ok
end

@doc """
Gets the current ID value without incrementing.
Mainly for testing/debugging purposes.
"""
def current_id do
case :persistent_term.get(@counter_ref, nil) do
nil ->
raise "IdManager not initialized. Call IdManager.init/0 first."

counter ->
:atomics.get(counter, 1)
end
end
end
103 changes: 72 additions & 31 deletions apps/debug_adapter/lib/debug_adapter/output.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ defmodule ElixirLS.DebugAdapter.Output do
"""
alias ElixirLS.Utils.WireProtocol
use GenServer
use ElixirLS.DebugAdapter.Protocol
import ElixirLS.DebugAdapter.Protocol.Basic

## Client API

Expand Down Expand Up @@ -38,31 +38,46 @@ defmodule ElixirLS.DebugAdapter.Output do
)
end

def send_event(server \\ __MODULE__, event, body) do
GenServer.call(server, {:send_event, event, body}, :infinity)
def send_event(server \\ __MODULE__, body) do
GenServer.call(server, {:send_event, body}, :infinity)
end

def debugger_console(server \\ __MODULE__, str) when is_binary(str) do
send_event(server, "output", %{"category" => "console", "output" => maybe_append_newline(str)})
send_event(server, %GenDAP.Events.OutputEvent{
seq: nil,
body: %{category: "console", output: maybe_append_newline(str)}
})
end

def debugger_important(server \\ __MODULE__, str) when is_binary(str) do
send_event(server, "output", %{
"category" => "important",
"output" => maybe_append_newline(str)
send_event(server, %GenDAP.Events.OutputEvent{
seq: nil,
body: %{
category: "important",
output: maybe_append_newline(str)
}
})
end

def debuggee_out(server \\ __MODULE__, str) when is_binary(str) do
send_event(server, "output", %{"category" => "stdout", "output" => maybe_append_newline(str)})
send_event(server, %GenDAP.Events.OutputEvent{
seq: nil,
body: %{category: "stdout", output: maybe_append_newline(str)}
})
end

def debuggee_err(server \\ __MODULE__, str) when is_binary(str) do
send_event(server, "output", %{"category" => "stderr", "output" => maybe_append_newline(str)})
send_event(server, %GenDAP.Events.OutputEvent{
seq: nil,
body: %{category: "stderr", output: maybe_append_newline(str)}
})
end

def ex_unit_event(server \\ __MODULE__, data) when is_map(data) do
send_event(server, "output", %{"category" => "ex_unit", "output" => "", "data" => data})
send_event(server, %GenDAP.Events.OutputEvent{
seq: nil,
body: %{category: "ex_unit", output: "", data: data}
})
end

def telemetry(server \\ __MODULE__, event, properties, measurements)
Expand All @@ -82,13 +97,16 @@ defmodule ElixirLS.DebugAdapter.Output do
"elixir_ls.mix_target" => Mix.target()
}

send_event(server, "output", %{
"category" => "telemetry",
"output" => event,
"data" => %{
"name" => event,
"properties" => Map.merge(common_properties, properties),
"measurements" => measurements
send_event(server, %GenDAP.Events.OutputEvent{
seq: nil,
body: %{
category: "telemetry",
output: event,
data: %{
"name" => event,
"properties" => Map.merge(common_properties, properties),
"measurements" => measurements
}
}
})
end
Expand All @@ -109,6 +127,15 @@ defmodule ElixirLS.DebugAdapter.Output do
end

@impl GenServer
def handle_call({:send_response, request_packet, body = %struct{}}, _from, seq) do
{:ok, dumped_body} =
SchematicV.dump(struct.schematic(), %{body | seq: seq, request_seq: request_packet["seq"]})

res = WireProtocol.send(dumped_body)

{:reply, res, seq + 1}
end

def handle_call({:send_response, request_packet, body}, _from, seq) do
res = WireProtocol.send(response(seq, request_packet["seq"], request_packet["command"], body))
{:reply, res, seq + 1}
Expand All @@ -120,25 +147,39 @@ defmodule ElixirLS.DebugAdapter.Output do
_from,
seq
) do
res =
WireProtocol.send(
error_response(
seq,
request_packet["seq"],
request_packet["command"],
message,
format,
variables,
send_telemetry,
show_user
)
{:ok, dumped_error} =
SchematicV.dump(
GenDAP.Structures.ErrorResponse.schematic(),
%GenDAP.Structures.ErrorResponse{
seq: seq,
request_seq: request_packet["seq"],
command: request_packet["command"],
type: "response",
success: false,
message: message,
body: %{
error: %GenDAP.Structures.Message{
# TODO unique ids
id: 1,
format: format,
variables: variables,
send_telemetry: send_telemetry,
show_user: show_user
}
}
}
)

res = WireProtocol.send(dumped_error)

{:reply, res, seq + 1}
end

def handle_call({:send_event, event, body}, _from, seq) do
res = WireProtocol.send(event(seq, event, body))
def handle_call({:send_event, body = %struct{seq: _}}, _from, seq) do
# IO.warn(inspect(%{body | seq: seq}))
{:ok, dumped_event} = SchematicV.dump(struct.schematic(), %{body | seq: seq})
# IO.warn(inspect(dumped_event))
res = WireProtocol.send(dumped_event)
{:reply, res, seq + 1}
end
end
22 changes: 22 additions & 0 deletions apps/debug_adapter/lib/debug_adapter/protocol.basic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ defmodule ElixirLS.DebugAdapter.Protocol.Basic do
end
end

defmacro response(seq, request_seq, command) do
quote do
%{
"type" => "response",
"command" => unquote(command),
"seq" => unquote(seq),
"request_seq" => unquote(request_seq),
"success" => true
}
end
end

defmacro response(seq, request_seq, command, body) do
quote do
%{
Expand Down Expand Up @@ -77,4 +89,14 @@ defmodule ElixirLS.DebugAdapter.Protocol.Basic do
}
end
end

defmacro event(seq, event) do
quote do
%{
"type" => "event",
"event" => unquote(event),
"seq" => unquote(seq)
}
end
end
end
Loading
Loading