gcv / julia-snail

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

Intended use / workflow #83

Closed jdm204 closed 2 years ago

jdm204 commented 2 years ago

Hi, first of all I want to thank you for your work on this package - the jump to def / display plots in a buffer features in particular have massively improved my emacs-julia experience!

In general, I'm a bit confused as to the intended workflow (I haven't used SLIME/CIDER, maybe that's why). I come from R land (RStudio -> ESS), and in general there doesn't seem to be a distinction between evaluating some code and copying it into the REPL, which I think is the source of my confusion.

A simplified example of a session for me looks like the following:

using CSV, DataFramesMeta, CairoMakie, Statistics

# read in data
df = CSV.read("./pdx-rna/MAP006_IM/quant.sf", delim="\t", DataFrame)

# do some transformation
long = @chain df begin
    @rsubset :Length > 100
    @rtransform :mreads = :NumReads * 1e6
end

# want to eyeball some statistic
mean(long.mreads)

# some plotting, added begin / end so can send with C-c
begin
    f = Figure(title = "Foo")
    plot(f[1,1], long.Length, long.mreads)
    plot(f[2,1], long.EffectiveLength, long.mreads)
    f
end

If I julia-snail-send-buffer-file then I don't get any output, and have to switch to the REPL and run mean(long.mreads) etc manually.

julia-snail-send-top-level-form looks like what I'm used to in ESS with C-c - send a function if point is in one, otherwise send contiguous block of code at point, but despite the documentation saying Currently only works on blocks terminated with ‘end’., it doesn't work for the two end terminated blocks in my above example (if I am interpreting what that means correctly), instead I get No top-level form at point. The only end-terminated block that it works for me is functions.

julia-snail-send-region doesn't seem exactly what I want because I have to highlight the code manually, and I don't see the output (e.g for the plot or the mean call.

Currently the only way I can think to work is by evaluating line-by-line which of course isn't ideal!

I saw #67 but the fact that this wasn't originally implemented makes me think I'm just fundamentally misunderstanding the intended workflow - if someone could point me in the right direction that would be amazing!

gcv commented 2 years ago

You're right that julia-snail-send-top-level-form doesn't currently support top-level begin to end blocks. I will look into making that work. In the meanwhile, would you consider using the code-cells package integration mode? See https://github.com/gcv/julia-snail#code-cells-integration-notebook-mode — I think it does most of what you want.

There are likely to still be some problems with looking at output. Let me know what you think. There's some unfinished work around it (#72).

gcv commented 2 years ago

I didn't really answer your basic question. In the case of mean(long.mreads), I just end up using the REPL for that directly, and then using its history to run it repeatedly. I tend to use the REPL as a notebook, but I've been enjoying code-cells lately.

As for the output issue discussed in #67, I started thinking that maybe Snail should just (optionally!) pop up a dedicated output buffer, much as it does for plots.

dahtah commented 2 years ago

Note that you can also use C-c C-l (julia-snail-send-line) to evaluate a single line directly in the REPL. It'd be good to have some basic workflow information in the docs, I can add something to the wiki.

dahtah commented 2 years ago

As for the output issue discussed in #67, I started thinking that maybe Snail should just (optionally!) pop up a dedicated output buffer, much as it does for plots.

That'd be nice. It should also be possible to use tooltips to display the contents of a variable directly in the source buffer, or its type. It'd be a good feature to have.

jdm204 commented 2 years ago

Thanks for the reply!

would you consider using the code-cells package integration mode?

I've just tried it out, cool package, it does what I want in that the eval command automatically evals the whole cell, avoiding me having to manually mark it, but I still don't see any output - I have to assign to a variable, hop over to the REPL and then display the variable.

I think I'm keen on having a keybind that lets me eval some code (one line or more) and see the result of that evaluation immediately.

I started thinking that maybe Snail should just (optionally!) pop up a dedicated output buffer, much as it does for plots.

That sounds fine to me - as long as there is some form of (eval result) history I don't think it matters whether the result appears in a popup or the REPL for me.

Hi @dahtah,

It should also be possible to use tooltips to display the contents of a variable directly in the source buffer, or its type. It'd be a good feature to have.

That sounds great!

Note that you can also use C-c C-l (julia-snail-send-line) to evaluate a single line directly in the REPL.

Thanks, this is what I'm currently using, but I find that I often want to evaluate a few lines at a time, and see the result of the final line, like:

x = some_list .* pi
y = [x; x.^2]
y |> sum |> sqrt

In ESS (R), I would put the cursor somewhere in those three lines and hit the eval command, and I would see the eval results of each line in the REPL, importantly including the final output that I'm interested in.

Thinking about it, it shouldn't be too hard to roll something to achieve that based on the function in #67 - I'm new (hopeless) at elisp but will give it a go!

jdm204 commented 2 years ago

The following small modification does pretty much exactly what I want, I think I've read that you shouldn't use mark in a function so it's probably bad elisp but it works at least! Now to figure out how to bind it to C-c C-c - if I use "C-c C-c" in the :bind directive it doesn't work and breaks the other snail keybinds.

(use-package julia-snail
  :straight t
  :hook (julia-mode . julia-snail-mode)
  :config
  (defun julia-snail-copy-repl-region ()
    "Copy the region (requires transient-mark) to the Julia REPL and evaluate it.
This is not module-context aware."
    (interactive)
    (save-excursion
    (if (null (use-region-p))
    (mark-paragraph))
      (let* ((block-start (region-beginning))
             (block-end (region-end))
             (text (s-trim (buffer-substring-no-properties block-start block-end))))
    (julia-snail--send-to-repl text)
    (julia-snail--flash-region (point-at-bol) (point-at-eol))
    (deactivate-mark))))
  :bind (:map julia-snail-mode-map
          ("C-M-g" . julia-snail-copy-repl-region)))
gcv commented 2 years ago

@jdm204: I just implemented a simple way of showing the result of evaluating something from Emacs. It just prints a (somewhat nicely formatted) message to the REPL. See https://github.com/gcv/julia-snail/commit/f64e0d7cbf195ceed7dfd2eecd9565dd7e1fa202. You need to set julia-snail-repl-display-eval-results to t to turn this on.

The change should hit MELPA in a couple of hours. Please give it a try and let me know if this helps with your workflow.

jdm204 commented 2 years ago

@jdm204: I just implemented a simple way of showing the result of evaluating something from Emacs. It just prints a (somewhat nicely formatted) message to the REPL. See f64e0d7. You need to set julia-snail-repl-display-eval-results to t to turn this on.

That works great, thanks! I do like that output from evaluating something from the 'script' looks different (the Info: and the bracket-thing to the left) to results from evaluating directly in the REPL.

The only thing I expected to work but didn't is that julia-snail-send-buffer-file didn't display anything (doing include on the same file in the REPL displays the last evaluation of the file).

For my workflow, I am now using julia-snail-send-region - in practice I am pretty much always doing mark-paragraph before julia-snail-send-region as it's nearly always a contiguous block of lines I want evaluated together.

Looking closer at ESS, the default eval command bound to C-c C-c is ess-eval-region-or-function-or-paragraph-and-step, which essentially does a send-region if there is one, otherwise send-top-level-form if there is one, otherwise the equivalent of mark-paragraph then send-region. Maybe just that I'm used to it, but I find this behaviour very convenient. Now that julia-snail-send-region can display its results, I think I can replicate the behaviour I want by writing a function similar to the ESS one that calls julia-snail-send-region or julia-snail-send-top-level-form depending on the context!

gcv commented 2 years ago

Have you looked at julia-snail-send-dwim?

jdm204 commented 2 years ago

Have you looked at julia-snail-send-dwim?

Yes - with the new repl-display-eval-results it's very close to what I want:

"if region active, evaluate it in current module; else if on top-level block, evaluate it in current module; else copy line to REPL"

What I am looking for is the same as this except replacing "else copy line to REPL" with "else do julia-snail-send-region with the region set to the paragraph at point".

gcv commented 2 years ago

Also worth noting is the recently-merged integration for org-babel. See #86.

gcv commented 2 years ago

Take a look at the new julia-snail-popup-display-eval-results feature. It should hit MELPA in a couple of hours.

gcv commented 2 years ago

I'll close this ticket since I'm not sure what else to do about it for now. If there's anything else, feel free to reopen or start a new discussion.