jpalardy/vim-slime

let vim-slime ignore triple backtiks in markdown filles

pkyoung opened this issue · 6 comments

Dear,
Thank you for the helpful plugin.

I'd like to know how to used vim-slime with code block in md files.
In example below, I like to execute cmd1 and cmd2 only by hitting c-c c-c.

```
cmd1
cmd2 <- cursor here

cmd3
```

This is similar issue with #209, and I made comment there also.

Thank you.

Hi @pkyoung

At the bottom of this section, there is a bit about g:slime_cell_delimiter

If you want to send blocks of code between two delimiters, emulating the cell-like mode of REPL environments like ipython, matlab, etc., you can set the cell delimiter on the g:slime_cell_delimiter variable and use the SlimeSendCell mapping to send the block of code

let g:slime_cell_delimiter = "#%%"
nmap <leader>s <Plug>SlimeSendCell

Can you try that and let me know if that works for you? (using triple backticks 😄 )

Hi @jpalardy , thank you for this amazing plugin and I just wanted to mention the g:slime_cell_delimiter functionality works fantastically.

I'd like to add on a suggestion that I think is still in line with the title of this issue: having functions other than slime#send_cell() be able to ignore the cell delimiter, just as you have done here:

let line_ini = search(cell_delimiter, 'bcnW')

My vimscript is pretty rough so I wanted to get an idea of your thoughts on this kind of functionality before really diving into it myself. I imagine that if that delimiter search and replace is evaluated further downstream (say, at send_range or even at the final send), it could make the code-block experience much nicer.

The reason for this is that occasionally, I do happen to want to run only the first few lines or last few lines of a chunk of code:

print(5)

# code that I don't to re-run

print("hi")

As the cell_delimiter is currently implemented, if I just wanted to run the first and last lines (for whatever reason), I would need to go back to resorting to temporarily adding blank lines between the delimiters:


print(5)

# code that I don't to re-run

print("hi")

That works decently well and honestly is not much of a big deal. Not much wrong with having the blank lines and if I really wanted to, I supposed splitting that code into multiple chunks is also an option.

I only bring this up because (with my minimal understanding) it looks like the lines of code used to strip out the delimiters when sending a cell through slime could very easily be relocated to apply to any slime send function. And I cannot really imagine a time where a user would specify a cell delimiter but only want that delimiter to be ignored during cell block sends, rather than any send.

What would you think about having all the slime send functions ignore the delimiters? If it sounds like a good idea to you but you don't have the time right now, I can try to see if I can implement this myself when I get a chance.

Thanks again for the stellar plugin!

Hi @pvelayudhan

I read your comment a few times, but I'm not sure I understand the situation and what you want to happen.

Could you make a bigger example and try to include:

  • what you want to send
  • what you don't want to send
  • in a realistic use-case (actual code)

Thanks 👍

Definitely. Also I only just realized I forgot to escape the cell delimiters in the above example.

Let's say everything between the dashed lines is the contents of a .Rmd file (which has regular markdown interspersed with code blocks that begin with ```{r} and end with ```). And I have set in my config let g:slime_cell_delimiter = "```"

-------------
# This is the title of my file

This is a sentence.

```{r}
1 library(ggplot2)
2 library(dplyr)
3
4 print(1)
5 print(2)
6
7 print(10)
8 print(3)
```
-------------

If I try to slime send an entire cell, the behaviour is perfect:

-------------
# This is the title of my file

This is a sentence.

```{r} # line 1-8 sends if cursor is here
1 library(ggplot2) # line 1-8 sends if cursor is here
2 library(dplyr) # line 1-8 sends if cursor is here
3 # line 1-8 sends if cursor is here
4 print(1) # line 1-8 sends if cursor is here
5 print(2) # line 1-8 sends if cursor is here
6 # line 1-8 sends if cursor is here
7 print(10) # line 1-8 sends if cursor is here
8 print(3) # line 1-8 sends if cursor is here
``` # line 1-8 sends if cursor is here
-------------

If I try to send the paragraph im on with vip followed by slime region send, I get:

-------------
# This is the title of my file

This is a sentence.

```{r} # this fails because backticks
1 library(ggplot2) # this fails because backticks
2 library(dplyr) # this fails because backticks
3 # nothing happens
4 print(1) # this sends 4-5
5 print(2) # this sends 4-5
6 # nothing happens
7 print(10) # this fails because backticks
8 print(3) # this fails because backticks
``` # this fails because backticks
-------------

What I want to get is:

-------------
# This is the title of my file

This is a sentence.

```{r} # this sends 1-2
1 library(ggplot2) # this sends 1-2
2 library(dplyr) # this sends 1-2
3 # nothing happens
4 print(1) # this sends 4-5
5 print(2) # this sends 4-5
6 # nothing happens
7 print(10) # sends 7-8
8 print(3) # this sends 7-8
``` # this sends 7-8
-------------

My workaround in the mean time has been to add in bonus blank space before the first paragraph of code and after the last paragraph of code:

-------------
# This is the title of my file

This is a sentence.

```{r}
0
1 library(ggplot2) # this sends 1-2
2 library(dplyr) # this sends 1-2
3 # nothing happens
4 print(1) # this sends 4-5
5 print(2) # this sends 4-5
6 # nothing happens
7 print(10) # sends 7-8
8 print(3) # this sends 7-8
9
```
-------------

And then I remove lines 0 and 9 later. Let me know if it is still a little unclear!

Hi @pvelayudhan

Super clear now 🎉

I can relate to this example … especially since I'm a big Rmd user myself. But, interestingly, I did converge on "cheating" with empty lines, I can think of 2 reasons:

  • vim-slime didn't always have a "cell" mode (in fact, it's relatively recent), so I developed a lot of habits before
  • I personally feel it's easier 🤷

So, now I tend to structure my code in semi-logical paragraphs…

The mid-level solution is to pre-select your code before hitting ctrl-c, ctrl-c. A combination of paragraph movements (with { and }) with the o key and some tweaking can get you there.

But it's easy to break out of flow, if you have to think about block boundaries 🤔

A more final solution is to write some custom logic to "do what you mean", selection-wise:

  • look up, find the next empty line or cell delimiter
  • now, look down, find same
  • make that a selection
  • call slime send

In practice, I don't know if "what you mean" and what other people would mean/want is the same 😄

If you want to give it a try — let me know how that goes. You can also consider posting your solution here for others.

Another solution is to modify the text before the send. 💡

Thank you for the response and it is very fair that your Rmd habits were developed for this to not really be an issue lol. I switched over to vim-slime from Nvim-R (also a great plugin, but just for R) where I became used to slinging paragraphs right out of the gates of the cells.

I've opted for the "pre-select" code option that I'll share here. This is like "vip" command but will ignore the starting and ending triple backticks. It's not pretty but it works 😄:

function paragraph_select()
    vim.cmd.normal("{j")
    local line_start = vim.api.nvim_get_current_line()
    local has_cell_start = (line_start == "```{r}")
    vim.cmd.normal("}k")
    local line_end = vim.api.nvim_get_current_line()
    local has_cell_end = (line_end == "```")
    if (has_cell_start and has_cell_end) then
        vim.cmd.normal("{jjV}kk")
    elseif (has_cell_start) then
        vim.cmd.normal("{jjV}k")
    elseif (has_cell_end) then
        vim.cmd.normal("vipk")
    else
        vim.cmd.normal("vip")
    end
end

Then back in vimscript I have:

nnoremap ; :lua paragraph_select()<CR>:SlimeSend<CR>}j

To send the current paragraph with ";". Works perfectly fine for my purposes : )

Cheers!