Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python support #273

Closed
jkroes opened this issue Apr 12, 2020 · 8 comments
Closed

Python support #273

jkroes opened this issue Apr 12, 2020 · 8 comments
Labels

Comments

@jkroes
Copy link

jkroes commented Apr 12, 2020

Describe the bug
Interactive Python REPLs interpret blank lines as the end of a code block/chunk/function/whatever. This conflicts with everyday Python coding conventions in which blank lines are used as logical separators in scripts. Thus there is a situation in which executing anything less than an entire file fails. Numerous issues have documented this, and some commenters have left code suggestions. All of these code suggestions currently fail/fall on their face or only attempt to fix a particular use case or function:

#74
#114
#108
#233
#234
#270

I've tried to implement a fix that would enable e.g. neoterm-repl-send to work with Python with blank lines. The best I've achieved as a newcomer to Vim is not that. Instead, it is a function that handles visual selections, but only with iPython magic command %cpaste:

let g:neoterm_repl_python = 'python --no-autodindent'

function! Test(...) range
         let lines = getline(a:firstline, a:lastline)
         call neoterm#exec({'cmd': ['%cpaste -q', '']})
         sleep 100m
         call neoterm#exec({'cmd': add(lines, '')})
         sleep 100m
         call neoterm#exec({'cmd': ['--', '']})
endfunction

map <localleader>z :call Test()<cr>

This is an ugly hack. It requires you to first start a terminal, then run iPython manually, then use the mapping. It can only be used for Python but can't distinguish when Python is active as a REPL in a terminal. It prints out the wrapping %cpaste -q and -- on the adjacent lines surrounding your code.

A better solution may be to enable removing lines without non-space characters using something like the following code: filter(a:command, 'v:val !~ "^\\s*$"'), conditional on (i)Python being the active repl and also assuming a:command is a list of lines to execute in the repl. The only issue is, I haven't been able to find the proper place to insert something like this, despite two days of experimentation.

To Reproduce
Try running any Python code with unindented blank lines separating indented code lines in Neoterm.

Expected behavior
Being able to run Python in a REPL without praying to the indentation Gods.

@jkroes jkroes added the bug label Apr 12, 2020
@incoggnito
Copy link
Contributor

incoggnito commented Apr 12, 2020

I wonder why ipython does not offer a setting to handle the blank lines...

It prints out the wrapping %cpaste -q and -- on the adjacent lines surrounding your code.

Why you chose %cpaste instead of register and %paste?

It can only be used for Python but can't distinguish when Python is active as a REPL in a terminal

Maybe you could use g:neoterm_repl_command ?

A better solution may be to enable removing lines without non-space characters using something like the following code: filter(a:command, 'v:val !~ "^\s*$"'), conditional on (i)Python being the active repl and also assuming a:command is a list of lines to execute in the repl. The only issue is, I haven't been able to find the proper place to insert something like this, despite two days of experimentation.

Good idea. I will try this later when i've implemented venvs.

@jkroes
Copy link
Author

jkroes commented Apr 12, 2020

Good idea on g:neoterm_repl_command. Here is an implementation of Python and iPython support for neoterm-repl-send and neoterm-repl-send-line (my most frequently used mappings): https://github.com/jkroes/neoterm/commit/d6c50989ea0ca0bdd81b6f03fc6d9fe89227d761

Explanation: For both REPLs, code is executed line-by-line, unless indentation is encountered. Indentation starts a code block, and the block is executed as a whole upon the first blank line. So we remove the blank lines entirely, with one exception. For the Python REPL, blank lines are needed to separate a code block from a subsequent line starting at the global scope. E.g., without a line separating a function def from a non-indented call to the function. Python issues a syntax error. But iPython doesn't. By using a closure to wrap neoterm.repl.exec, we can track the last submitted line. If it's indented but the current line isn't indented, we add a single blank line back into the list of submitted lines.

There may be other corner cases I am unaware of that need to be handled, but I haven't seen any yet.

@incoggnito
Copy link
Contributor

incoggnito commented Apr 13, 2020

Very cool, so the paste-magic method is almost unnecessary. The only difference is that the code is broken down into several blocks. I would leave it up to the user which behaviour is preferred. I merged my local branch with your branch and left the paste-magic method as optional. So far, my tests show no errors.

  function! Inner(command) closure
    if g:neoterm_repl_ipython_magic == 1 && g:neoterm_repl_command =~ "ipython"
      call setreg('+', a:command, 'l')
      call g:neoterm.repl.instance().exec(add(["%paste"], g:neoterm_eof))
    else
      if g:neoterm_repl_command =~ "python"
        " ....
        " more code
        " ....
      endif
      call g:neoterm.repl.instance().exec(add(command, g:neoterm_eof))
    endif
  endfunction

  return funcref('Inner')
endfunction

Is it ok if I add your code to the pull request #274?

@jkroes
Copy link
Author

jkroes commented Apr 13, 2020

@incoggnito By all means!

@incoggnito
Copy link
Contributor

There is a little bug when sending a function with TREPLSendSelection. The function is sent completely, but i have to execute it manually. Can you reproduce that?

@jkroes
Copy link
Author

jkroes commented Apr 18, 2020

@incoggnito Are you sending a function definition without a subsequent call? When I send a function definition only, with the last line indented, I need to enter insert mode in the terminal and manually hit ENTER. If it's followed by a non-indented line, the entire selection executes. This was my original design. Execution only happens when I can be sure that a complete code block has been sent; that is, when indentation decreases back to the global scope. This may not be the best heuristic, but for me it was a compromise that gave the same results for selections as well as line-by-line evaluation.

@incoggnito
Copy link
Contributor

Ok, that's fine.

@kassio
Copy link
Owner

kassio commented Apr 19, 2020

can we close this one, now that #274 was merged?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants