8

Other editors and IDEs, such as VSCode and RStudio, have an integrated console/terminal/REPL with the following characteristics:

  • They open in a split window. The file we’re working on is visible at the same time as the console.
  • If we press a certain shortcut (typically ⌘↵), it sends and executes code to the console. Bonus if it auto-opens the console if it was closed.
    • If no code was selected, the current caret line is sent. Bonus if the caret moves down to the next line with code.
    • If code was selected, only that portion is sent.
  • Bonus if it auto-detects the current language and opens the appropriate REPL.

I’ve been searching on and off for months, and never found that same functionality for Neovim. I found a few plugins that looked promising (e.g. neoterm, vim-slime) but I can’t get them to work, or the documentation is awful, or they don’t do exactly that.

It seems like Neovim should be capable of this. Is there no solution? I’m looking for something that in addition to the above functionality will have a fairly simple setup. A plugin would be ideal; having to create and maintain multiple files myself would be a recipe for breakage.

The lack of this functionality is the only reason why I don’t use Neovim full-time.

2
  • As you mentioned RStudio, did you check Nvim-R? I am very happy with that, but it is only for R. Feb 24, 2021 at 9:30
  • @JohannesRanke RStudio was an example.
    – user137369
    Feb 25, 2021 at 0:34

3 Answers 3

6

Disclaimer: this answer is more vim-centric than it is neovim-centric because I use the one and not the other. However, techniques shown are broadly applicable to both editors, as they share the same features.

TL;DR

Simply making use of built-in features and some scripting/maps, we can accomplish a lot.

But I also turned my approach into a plugin called simpl.

Feature Number 1: Terminals in the Editor

(Neo)Vim provides a builtin terminal emulator via :terminal. There are other techniques to access the shell (including job control, !, and tmux), but this is the closest to a REPL in the editor.

Learn to use it (both in Normal and Terminal modes!).

Feature Number 2: Terminal by Filetype

I recently realized I was using :term python frequently when working on python code, and :term scala for scala code, etc. Noticing a repeatable pattern, I generalized and abstracted.

I created a function to control terminal behavior based on a buffer setting:

" ~/.vim/autoload/terminal.vim
" Functions for dealing with the terminal

" Global function for calling terminal with the appropriate interpreter
" b:interpreter controls the program run
function! terminal#run(...) abort
  let l:interpreter = get(b:, 'interpreter', &shell)
  let l:command = 'term'
  if a:0
    for l:opt in a:000
      let l:command .= printf(' %s', opt)
    endfor
  endif
  let l:command .= printf(' %s', l:interpreter)
  execute l:command
endfunction

Then, in my (many) after/ftplugin files (available in the same repo), I set b:interpreter as needed—the default is to launch a shell, but I get python, irb, scala, even jdb as necessary.

Feature Number 3: Mappings

Finally, I set up mappings to launch said terminal in my vimrc.

" Terms and interpreters
" set b:interpreter for filetypes to affect the term
nnoremap <silent> <Leader>t :call terminal#run('++close')<CR>
nnoremap <silent> <Leader>T :call terminal#run()<CR

Feature Number 4: Scriptability and Custom Operators

Vim’s scriptability is what makes it king, so we script some more. Custom operators mean we can operate over any motion of text to do whatever we want (in this case, send it to a terminal).

The plugin habamax/vim-sendtoterm accomplishes the last piece of the puzzle in a single, short script. It even supports neovim, and the Readme has instructions to use control-enter and go to next line.

Once you have this infrastructure in place, it should be fairly easy to map a key sequence of your choosing to

  1. Start REPL if not running (may have to set a b:repl_running to the window number or buffer number when opening the specified terminal)
  2. Get the text of the current line and send it to said REPL
  3. Move the cursor down one line.

Copy/paste work well enough that I’ve never needed the send-line functionality.

I ended up creating vim-simpl to meet both the original terminal#run needs and the "load this file in the REPL" needs. It still requires the old b:interpreter setup, and you get to make your own mappings/commands. It also only supports a few languages out-of-box (mostly because I haven't implemented any others), but it's easy to register new ones.

5
  • 2
    Thank you for the reply, but this does not cover my case. Phrases like “copy/paste work well enough that I’ve never needed the send-line functionality” make it far from from what I want. As I mentioned, I’ve been looking for months; half-solutions abound, I’m looking for something that does everything I asked. I should also add setup should be fairly basic. If I have to make multiple files myself for every filetype, that’s a recipe for breakage.
    – user137369
    Mar 16, 2019 at 18:03
  • 2
    Point-by-point: your criticism is well-received. This is a first draft—I hope to put together the send-line functionality. This gets you a fair bit of the way there, and because it’s not a plugin you can customize it to your needs. Im always willing to hack on my editor—especially when it only requires one line of change in the long term per filetype that I care about. Notice—not multiple files, a single line in a single file per type. Example: in after/ftplugin/python.vim, I have let b:interpreter = 'python'. Expect to put a little work in to your solution.
    – D. Ben Knoble
    Mar 16, 2019 at 18:10
  • @user137369 see my update.
    – D. Ben Knoble
    Mar 17, 2019 at 23:00
  • sendtoterm, while is able to solve some pain points, is vim only though. Hopefully there will be neovim support in the future when I have time or smdy else will PR.
    – Maxim Kim
    Mar 18, 2019 at 6:45
  • 1
    And now there is neovim support ;) It should be tested more of course, but it works for me.
    – Maxim Kim
    Mar 18, 2019 at 8:37
2

After further searching and testing, it looks like kassio/neoterm can tick all the boxes, with the following configuration:

let g:neoterm_default_mod='belowright' " open terminal in bottom split
let g:neoterm_size=16 " terminal split size
let g:neoterm_autoscroll=1 " scroll to the bottom when running a command
nnoremap <leader><cr> :TREPLSendLine<cr>j " send current line and move down
vnoremap <leader><cr> :TREPLSendSelection<cr> " send current selection
1

use this:

:split | terminal
:vsplit | terminal

Or you can use the shorter :vs | te or :vs +te.

and you can assign maps for that like:

nmap <C-S-P> :split | terminal <CR>

this rocks!

author: https://github.com/neovim/neovim/issues/5073#issuecomment-233172421

1
  • That only covers the first point, it has no REPL functionality.
    – user137369
    Nov 26, 2020 at 22:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.