gcv / julia-snail

An Emacs development environment for Julia
GNU General Public License v3.0
235 stars 23 forks source link

ob-julia extension #86

Closed MasonProtter closed 2 years ago

MasonProtter commented 2 years ago

I whipped up a quick ob-julia extension that I think for now is good enough for my personal uses. This allows one to use a julia-snail process to evaluate code in emacs Org Mode SRC blocks. Here's a little demo video of it in action:

https://user-images.githubusercontent.com/29157027/163515735-1f8fabcc-eb13-49da-8616-213c91ec18d2.mp4

No big deal if you don't want this or want something to be changed, I just figured I'd open this PR in case you think this is worth having in this repo, otherwise it can continue to live in my fork.

gcv commented 2 years ago

Looks promising. How do you manage the Julia side of things, i.e., start a REPL? I didn't notice any code for doing that.

MasonProtter commented 2 years ago

You simply do M-x julia-snail. I tried making it automatic here: https://github.com/gcv/julia-snail/blob/182b6822724cff548fd2404fb82b2455c92d6fb2/extensions/ob-julia/ob-julia.el#L15-L17 but it caused an error in org mode when trying to display the first evaluation that I wasn't able to understand so I commented it out.

gcv commented 2 years ago

Just doing M-x julia-snail is not enough for me, since ObJulia does not get bootstrapped. I put

-*- julia-snail-extensions: (repl-history formatter ob-julia) -*-

at the top of the source file, but it still errors out and complains about ObJulia being missing when I try to evaluate a code block.

You must have had to do something else to enable Snail for Org files...

MasonProtter commented 2 years ago

Hm, I don’t know why that didn’t work for you. I simply did customize-variable to make ob-julia get loaded globally, but I’m surprised that setting it locally didn’t work.

MasonProtter commented 2 years ago

What exactly is the error your getting?

gcv commented 2 years ago

I figured it out. It's related to #85 which you reported. When julia-snail first runs on a non-Julia source file, source-buf is nil which leads to all kinds of fun down the line. I'll work around that.

Overall, your extension looks nice and I'll merge it in soon. It's a possible solution to the problems discussed in #83.

I'd really like it to do smoother error handling. Right now the error just gets splattered in the org file, which neither looks good nor is easy to understand.

MasonProtter commented 2 years ago

Hm, so I just tried this and it seems to just be a problem with loading extensions with local variables in general. I suspect it's maybe because you used lexical scope instead of dynamical scope, but I don't know.

E.g. REPLHistory doesn't get loaded either. You can check names(JuliaSnail.Extensions; all=true) and you'll see that neither REPLHistory or Formatter are defined either.

If you instead do M-x customize-variable julia-snail-extensions, then you can add ob-julia and the other extensions to the variable there and everything should work.

gcv commented 2 years ago

I commented 7 minutes before you. :)

Do you think you could get Snail’s normal error pop-ups working with ob-julia?

MasonProtter commented 2 years ago

I'd really like it to do smoother error handling. Right now the error just gets splattered in the org file, which neither looks good nor is easy to understand.

Hm, well I quess if I just remove the try-catch block then use julia-snail's popup buffer for error handling. I kinda prefer splatting the error message because it provides the full path to file, whereas an error reported in that buffer just has the file name. Maybe this is unintentional though? I could open a separate issue about this if you like

MasonProtter commented 2 years ago

E.g. in a .jl buffer if I do

foo() = error("boo!")

and then evaluate foo() with julia-snail-send-line I get the error message

ERROR: boo!
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:33
 [2] foo()
   @ Main ~/Dropbox/Julia/foo.jl:20
 [3] top-level scope
   @ REPL[2]:1

whereas with julia-snail-send-region I get

boo!

error(s::String) at error.jl:33
foo() at foo.jl:20
top-level scope at foo.jl:21
eval at boot.jl:373 [inlined]
eval_in_module(fully_qualified_module_name::Vector{Symbol}, expr::Expr) at JuliaSnail.jl:189
eval_tmpfile(tmpfile::String, modpath::Vector{Symbol}, realfile::String, linenum::Int64) at JuliaSnail.jl:219
top-level scope at none:1
eval at boot.jl:373 [inlined]
eval_in_module(fully_qualified_module_name::Vector{Symbol}, expr::Expr) at JuliaSnail.jl:189
macro expansion at JuliaSnail.jl:747 [inlined]
(::Main.JuliaSnail.var"#29#33"{Sockets.TCPSocket})() at task.jl:423

where the important difference is between

 [2] foo()
   @ Main ~/Dropbox/Julia/foo.jl:20

and

foo() at foo.jl:20

The other thing is that I don’t like that this buffer doesn’t tell me what module the function being called was from.

MasonProtter commented 2 years ago

Okay, so I've made a customizable variable julia-snail/ob-julia-use-error-pane (defaults to t) that determines if the error uses julia-snail's error pane, or if the error is displayed inline as a returned value.

gcv commented 2 years ago

This is really nice. Thanks!!

gcv commented 2 years ago

@MasonProtter: Do you know if there's a way to register completion-at-point and xref functions to a Babel src block specific to Julia? Because then we'd really be in business!

MasonProtter commented 2 years ago

Yay!

Do you know if there's a way to register completion-at-point and xref functions to a Babel src block specific to Julia? Because then we'd really be in business!

The usual thing people apparently do is hit C-c ‘ which puts you in a ____-mode buffer with the same contents as your src block, and then you edit in that buffer where all your major mode hooks work and then hit C-c ‘ again to return to your document with your edits inserted. I don’t really like this all that much but maybe I’m just not used to it. Emacs-jupyter provides auto completion in its src blocks, inside org mode, so I know it’s possible.

Another thing we could look at is poly-mode which already has a poly-org set up.

MasonProtter commented 2 years ago

Okay, so I tried using C-c ' but that just causes errors because julia-snail assumes that there is a filename assocated with the resulting buffer, but in this case that assumption is untrue.

poly-org I didn't really like and did some very nasty things like automatically enabling itself just by being installed, but without necessarily being require'd which to me is a no go.

The approach in emacs-jupyter is interesting though. They define a new minor mode called emacs-jupyter-interaction-mode which is only active when you're inside an SRC block, and then provides completion through this minor mode. https://github.com/nnicandro/emacs-jupyter/blob/c702f1e90f9acded57297fbba88322f16f127b27/jupyter-org-client.el#L608