kaz-yos / eval-in-repl

Consistent ESS-like eval interface for various REPLs
174 stars 27 forks source link

Can't evaluate individual lines in functions #16

Open Wilfred opened 8 years ago

Wilfred commented 8 years ago

Given the Python:

def foo():
    print "hello"
    print "world"

If point is on the print "hello" line, then I'd expect it to just send that line to *Python*. Instead, it sends the whole function body.

kaz-yos commented 8 years ago

It looks for the "def" and try to send the entire functional definition block. I don't think I should change that behavior in a backward-incompatible way. Instead, you can select just that line and send the selection only.

Wilfred commented 8 years ago

The ESS function ess-eval-region-or-line-and-step is capable of just sending individual lines. Without this, eval-in-repl isn't equivalent.

We don't need to break backwards compatibility though. It could be a defcustom. Would you be happy with that?

kaz-yos commented 8 years ago

defcustom sounds like a good option. I'll investigate that. The current design is geared more toward lisp languages where sending a line makes little sense due to trailing parens.

Wilfred commented 8 years ago

Looking at the code, it's not actually sending the whole function definition. Consider the Python:

def foo():
    for x in range(5): # if point is here, we send both loops
        for y in range(5): # if point is here, we only send the inner loop
            print (x, y)

    print "done" # this is never sent

Is this intentional?

arichiardi commented 8 years ago

@kaz-yos I think this is a good feature to have in general, but needs a defcustom for sure. My use case for it in clojure is to be able to evaluate lines inside a (comment):

(comment
  (def a 3)
  (def b 4)
  (println "a & b are" a b))

The last line might be difficult to evaluate, but (def a 3) and (def b 4) would be nice to be able to. In general, a feature that allows you to peel off a layer of wrapping (in my case the strip off the (comment) layer) would really be a killer feature.

kaz-yos commented 8 years ago

@Wilfred I think in that case, the empty line between the two print functions is causing the problem. The function body recognition part is dependent on python.el. I don't think python.el can detect the remaing part of the body after the empty line.

kaz-yos commented 8 years ago

@arichiardi Maybe I should introduce a toggle between line-by-line execution and current evaluation scheme (goes back to the top-level expression) that users can change on the go. Meanwhile, you can select the line and send the selected line only.

arichiardi commented 8 years ago

Oh I need to try that, I also select the whole parenthesis, another idea could be to plug a function that does the selection for you before evaluating or returns nil in case no selection can be made. In my case one of the paredit function would select only if it finds.backward.

If the selection is returned you evail it in Repl, if nil is returned the standard behavior takes place.

kaz-yos commented 7 years ago

The Python blank line issue also reported in https://github.com/kaz-yos/eval-in-repl/issues/15 has been addressed in 0.9.5+.

kaz-yos commented 7 years ago

The new behavior to avoid the blank line issue seems to cause a problem in one-line execution.

C-RET at the second line of the following function

def foo():
    print "hello"
    print "world"

gives the following indentation error. Selecting a region starting at print in the second line will be ok.

>>> Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/var/folders/8r/4w4sm1d14pj9gm46ksf8jybc0000gn/T/py4484CLN", line 1
    print "hello"
    ^
IndentationError: unexpected indent

Maybe I can delete the preceding spaces when invoked on a line, but I'm not sure if that's safe in general.

TODO:

Wilfred commented 7 years ago

The robust solution is to unindent the current block:

(defun pse--start-indent-at-0 (string)
  "Given python code STRING, unindent it so the minimum indent is zero."
  (with-temp-buffer
    (insert string)
    (goto-char (point-min))
    (forward-to-indentation 0)
    (when (> (current-column) 0)
      (indent-rigidly (point-min) (point-max) (- (current-column))))
    (buffer-substring (point-min) (point-max))))