nonsequitur / inf-ruby

218 stars 69 forks source link

ruby-block-send sends unexpected block #171

Open bo-tato opened 1 year ago

bo-tato commented 1 year ago
begin
  puts "outer" # cursor on this line
  begin
    puts "inner"
  end
  puts "outer again"
end

here if we do ruby send block with the cursor on the first puts "outer", I think expected behavior is to send the block containing puts "outer" but it sends puts "inner" block, as ruby-send-block is using ruby-end-of-block which uses ruby-move-to-block which moves to what it calls the sibling block, in other words just the next end statement, not the end of the block containing the cursor. Another unexpected behavior is when the cursor is on the ending line of a block:

begin
  puts "first block"
end # cursor on this line
begin
  puts "second block"
end

with the cursor on the end line of the first block, ruby- block-send sends the second block.

In https://github.com/bo-tato/inf-ruby/tree/fix-ruby-send-block I change ruby-send-block to use er/ruby-forward-up which gives the behavior I'd expect for both those cases. I don't open a pull request as that introduces a dependency on expand-region package that I assume is undesired. If it's desired to change ruby-send-block to behave like this I can try to code it without adding extra dependency

bo-tato commented 1 year ago

I realize if we're ok with depending on expand-region it has another method that makes ruby-block-send even simpler, just:

(-let (((start . end) (er/get-ruby-block)))
    (ruby-send-region start end))
dgutov commented 1 year ago

Hi!

Speaking of the first example, arguably, either of the behaviors can make sense. expand-region behaves in a particular way, if only because of the name (expand).

Do you maybe have an example of other major mode where block-related commands behave in this specific way? Just as a way of making this argument.

bo-tato commented 1 year ago

Interesting, I'm just starting to learn ruby but I can see now some might expect the current behavior for the first example, that send block sends the next complete block after the cursor. To me at least though it's more intuitive that it sends the block the cursor is in. If anything the traditional send to repl commands for emacs I think are all sending the expression before the cursor not after. The one major mode I know with a comparable command is cider-eval-list-at-point for cider mode for clojure. If we translate the first example to clojure:

(do
  (println "outer") ; [cursor here]
  (do
    (println "inner"))
  (println "outer again"))

It will print outer, inner, outer again. The list (pair of parenthesis around cursor), is equivalent in ruby I think to the block (begin/end pair around cursor).

translating the example to python:

if True:
    print("outer")
    if True:
        print("inner")
    print("outer again")

I have in my emacs config the function:

(defun my/python-shell-send-block ()
  "Send the current block to the inferior python process for evaluation."
  (interactive)
  (save-excursion
    (let ((beginning (python-nav-beginning-of-block))
          (end (python-nav-end-of-block)))
      (python-shell-send-region beginning end))))

which behaves in this same way (sending the block the cursor is in), but I just copied that function from online somewhere, I don't think it's made it into any actual package. It's interesting python-nav-end-of-block is part of the official python package and it's behavior is different from ruby-end-of-block causing the difference in block-send. On the first outer, ruby-end-of-block will go to the end of the inner block, where python-nav-end-of-block will go to the end of the outer block.

dgutov commented 1 year ago

It's interesting python-nav-end-of-block is part of the official python package and it's behavior is different from ruby-end-of-block causing the difference in block-send. On the first outer, ruby-end-of-block will go to the end of the inner block, where python-nav-end-of-block will go to the end of the outer block.

You are right.

ruby-end-of-block and its twin are more simplistic. The behavior of python-nav-end-of-block should suit us better here, so it would be a good idea to adapt its algorithm.