-
Notifications
You must be signed in to change notification settings - Fork 36
Expand file tree
/
Copy pathdap_repl.janet
More file actions
211 lines (190 loc) · 7.08 KB
/
Copy pathdap_repl.janet
File metadata and controls
211 lines (190 loc) · 7.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# dap-repl — interactive debug REPL driven by DAP Janet FFI
#
# Registers a /dap-repl slash command that opens a sub-mode where you
# type gdb/lldb-like commands directly. Every command calls the DAP
# Janet bindings (dap/launch, dap/step, dap/eval, dap/bp, etc.)
# and returns the JSON result.
#
# The REPL runs as a Janet coroutine: the harness/request-prompt
# mechanism injects each command's prompt into the chat. This means
# you get agent assistance between commands — you can ask "why is
# this variable null?" and the agent can call dap/stack-trace, etc.
#
# Architecture:
# Plugin (this file) → dap/launch, dap/step, etc. (Janet FFI)
# → C function (src/dap/janet_bindings.rs) → DAP_TX channel
# → tokio bridge task → DapSessionManager (src/dap/session.rs)
# → DapClient (src/dap/client.rs) → real adapter process
#
# ── DAP workflow with debugpy ─────────────────────────────────────────
#
# Launch a script:
# (dap/launch "test_program.py" "debugpy")
#
# Launch a package (python -m <module>):
# (dap/launch-module "test_mod" "debugpy")
#
# Launch pytest with xdist and a single test:
# (dap/launch-module "pytest" "debugpy")
# Then send args via dap/run-in-terminal, or pre-configure
# in the launch request's `args` field:
# (dap/launch-module "pytest" "debugpy" ["-n4" "tests/test_foo.py::test_bar"])
#
# Then set breakpoints, continue, inspect:
# (dap/bp "source_file.py" 142)
# (dap/continue)
# (dap/stack-trace)
# (dap/eval "response.status_code")
(def hooks ["on-prompt"])
# ── REPL state ──────────────────────────────────────────────────────
(var repl-active false)
(var last-file "")
(var last-adapter nil)
# ── Command dispatch ────────────────────────────────────────────────
(defn- repl-dispatch [cmd-str]
(def parts (string/split " " cmd-str))
(def head (get parts 0))
(match head
# Lifecycle
"launch" (do
(def file (get parts 1))
(def adapter (get parts 2))
(if (not file)
"usage: launch <file> [adapter]"
(do
(set last-file file)
(set last-adapter adapter)
(def res (if adapter
(dap/launch file adapter)
(dap/launch file)))
(or res "launch failed — check adapter"))))
"launch-module" (do
(def module (get parts 1))
(def adapter (get parts 2))
(if (not module)
"usage: launch-module <module> [adapter]"
(do
(def res (if adapter
(dap/launch-module module adapter)
(dap/launch-module module)))
(or res "launch-module failed — check adapter"))))
"attach" (do
(def pid-str (get parts 1))
(def adapter (get parts 2))
(if (not pid-str)
"usage: attach <pid> [adapter]"
(do
(def pid (scan-number pid-str))
(if (not pid)
(string "invalid pid: " pid-str)
(do
(def res (if adapter
(dap/attach pid adapter)
(dap/attach pid)))
(or res "attach failed — check adapter and pid"))))))
"terminate" (do
(def res (dap/terminate))
(set repl-active false)
(or res "session terminated"))
# Execution control
"c" (dap/continue)
"continue" (dap/continue)
"n" (dap/step)
"next" (dap/step)
"step" (dap/step)
"s" (dap/step-in)
"step-in" (dap/step-in)
"fin" (dap/step-out)
"finish" (dap/step-out)
"step-out" (dap/step-out)
# Inspection
"p" (dap/eval (string/join (array/slice parts 1) " "))
"print" (dap/eval (string/join (array/slice parts 1) " "))
"eval" (dap/eval (string/join (array/slice parts 1) " "))
"bt" (dap/stack-trace)
"backtrace" (dap/stack-trace)
"info threads" (dap/threads)
"threads" (dap/threads)
"sessions" (dap/sessions)
# Breakpoints
"bp" (do
(def file (get parts 1))
(def line (get parts 2))
(if (and file line)
(dap/bp file line)
"usage: bp <file> <line>"))
"break" (do
(def file (get parts 1))
(def line (get parts 2))
(if (and file line)
(dap/bp file line)
"usage: break <file> <line>"))
# Variables
"vars" (do
(def ref (get parts 1))
(if ref
(dap/vars ref)
"usage: vars <var-ref>"))
# Meta
"help" "
DAP REPL commands:
launch <file> [adapter] Start debugging a program
launch-module <mod> [adapter] Launch via python -m <mod> (e.g. pytest)
attach <pid> [adapter] Attach to a running process
terminate End debug session
c / continue Resume execution
n / next / step Step over current line
s / step-in Step into function call
fin / finish / step-out Step out of current function
p / print / eval <expr> Evaluate expression
bt / backtrace Show call stack
info threads List threads
bp / break <file> <line> Set breakpoint
sessions Show session status
vars <var-ref> Show variables
q / quit Exit REPL
help Show this message"
"q" (do
(set repl-active false)
"REPL exited — session still active. Use `terminate` to end it.")
"quit" (do
(set repl-active false)
"REPL exited — session still active. Use `terminate` to end it.")
# Unknown
(string "unknown command: " head " — try 'help'")))
# ── Hook — intercept prompts when REPL is active ────────────────────
(defn on-prompt [ctx]
(if (not repl-active)
nil
(do
(def prompt (ctx :prompt))
# Strip REPL prefix if present
(def clean (if (string/find "dap> " prompt)
(string/slice prompt 5)
prompt))
(if (empty? clean)
(do
(harness/request-prompt "dap> ")
"Type a command or 'help'")
(do
(def result (repl-dispatch clean))
(if repl-active
(harness/request-prompt "dap> "))
result)))))
# ── Slash command entry point ───────────────────────────────────────
(defn repl-start [args]
(if (not (dap/available?))
"DAP not available — build with --features dap,plugin and install a debug adapter"
(do
(set repl-active true)
(harness/request-prompt "dap> ")
"
DAP REPL started. Commands:
launch <file> [adapter] — start debugging
launch-module <mod> [adapter] — launch via python -m (e.g. pytest)
attach <pid> [adapter] — attach to process
c/continue, n/next/step, s/step-in, fin/step-out
p/eval <expr>, bt/backtrace, bp/break <file> <line>
sessions, vars <ref>, terminate, q/quit
")))
(harness/register-command "dap-repl" "repl-start")