Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debug extension #164

Merged
merged 1 commit into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -463,11 +463,23 @@ Customization variables:
- `julia-snail/ob-julia-capture-io t` : If true, all intermediate printing during evaluation will be captured by ob-julia and printed into your org notebook
- `julia-snail/ob-julia-resource-directory "./.ob-julia-snail/"`: Directory used to store automatically generated image files for display in org buffers. By default this is a local hidden directory, but it can be changed to e.g. `/tmp/` if you don't want to keep the image files around.

### Debug

This extension uses [DebugAdapter.jl](https://github.com/julia-vscode/DebugAdapter.jl) and [dape](https://github.com/svaante/dape) to allow debugging inside the REPL.

Use one of the following macros to initiate the debug session from the REPL:
- `@run` pause on first breakpoint.
- `@enter` pause on entry.

```julia
using .JuliaSnail.Extensions.Debug

@enter(println("hello world"))
```

## Future improvements

- The `libvterm` dependency forces the use of recent Emacs releases, forces Emacs to be build with module support, complicates support for Windows, and is generally quite gnarly. The Eat alternative requires Emacs 28. It would be much better to re-implement the REPL in Elisp and make sure it works on older Emacs versions.
- Completion does not pick up local variables.
- A real eldoc implementation would be great, but difficult to do with Julia’s generic functions.
- A debugger would be great.
- A real test suite which fully drives both Julia and Emacs and runs in a CI environment (like GitHub Actions) wouldn’t hurt, either.
80 changes: 80 additions & 0 deletions extensions/debug/Debug.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.

## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.

## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.

module Debug

export @enter, @run

Main.JuliaSnail.@with_pkg_env (@__DIR__) begin
import DebugAdapter
import Sockets
import Logging
end

port = 12124
conn = missing
session = missing
server = missing
ready = Channel{Bool}(1)

function init()
# Logging.global_logger(Logging.SimpleLogger(Logging.Debug))
end

function start()
global server = Sockets.listen(port)
Threads.@spawn begin
while true
@debug "Listening on port $port"
global conn = Sockets.accept(server)
@debug "Accepted connection"
global session = DebugAdapter.DebugSession(conn)
# When using the attach request, the terminate request does not work.
session.capabilities.supportsTerminateRequest = false
@debug "Starting debug session"
put!(ready, true)
DebugAdapter.run(session)
end
end
end

function run(mod, code, filepath; stop_on_entry=false)
if ismissing(server)
start()
end
soe = ":json-false"
if stop_on_entry
soe = "t"
end
Main.JuliaSnail.send_to_client("""
(dape `(:request "attach"
host "localhost"
port $port
:type "julia"
:stopOnEntry $soe))
""")
# Wait for the session to be ready.
take!(ready)
DebugAdapter.debug_code(session, mod, code, filepath)
end

macro enter(command)
Base.remove_linenums!(command)
:(run(Main, $(string(command)), $(string(__source__.file)), stop_on_entry=true))
end

macro run(command)
Base.remove_linenums!(command)
:(run(Main, $(string(command)), $(string(__source__.file)), stop_on_entry=false))
end
end
2 changes: 2 additions & 0 deletions extensions/debug/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[deps]
DebugAdapter = "17994d07-08fe-42cc-bc1b-7af499b1ea47"
11 changes: 11 additions & 0 deletions extensions/debug/debug.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
;;; debug.el --- Julia Snail -*- lexical-binding: t -*-

(defun julia-snail/debug-init (repl-buf)
(julia-snail--send-to-server
'("JuliaSnail" "Extensions")
"load([\"debug\" \"Debug.jl\"]); Debug.init()"
:repl-buf repl-buf
:async nil
:async-poll-maximum 120000))

(provide 'julia-snail/debug)