7

I know there'a probably a plugin for that, but since Neovim has a built-in terminal I wanna use it. I just want to bind a key that runs the current file in Python 3. I tried using @% and %, but the shell does not recognize these variables. I think the best way would be to export the current file path as an environment variable, and make use it on the terminal. How can I do that?

1
  • 5
    do :term % or :term python % not work?
    – Mass
    Mar 4, 2018 at 17:31

4 Answers 4

4

I am myself pretty new to vim in general, but I guess you can easily do this. Here is my version of it, which will create a horizontal split, create a terminal buffer and then run the current python file in it and then you can create a another mapping which can close the terminal buffer for you.

nnoremap <C-R> :sp <CR> :term python % <CR>
nnoremap <C-W> :bd!<CR>
3
  • 2
    I thought that <C-R> and <C-W> respectively differentiates from <C-r> and <C-w> by a Shift key, but they don't. This solution, without modification, shadows some crucial key bindings. Aug 12, 2020 at 10:45
  • @UtkanGezer can you please clarify what you mean by this? Are there system key bindings (for <C-R> and <C-W>) that you are referring to? If not, then wouldn't this solution (suggested by @peterh) be okay?
    – Vishal
    Mar 25, 2022 at 22:09
  • 2
    @Vishal I might have said that because <C-r> is the default key binding to redo what has been undone, and it seems (according to my old comment) that <C-R> refers to the same key combination, therefore shadowing that default and crucial function when rebound. It's been a long time, and I don't remember what exactly my motivation was back then, but this might be it. Mar 28, 2022 at 9:07
1

The best solution for me:

" Create a function to open a neovim terminal in a small split window and run python 
function! Termpy()
  exec winheight(0)/4."split" | terminal python3 %
endfunction

Then use the solution above to map it to your prefered key combination:

" Press CTRL+R to run python script into separate term window 
nnoremap <C-R> :call Termpy() <CR>

Other cool things you can do is:

Use a command like :Pyrun

command Pyrun execute ":w<CR>:vert ter python3 "%"<CR>"

Use leader + p to save and run the script

map <leader>p :w<CR>:!python3 %<CR>

python2 and python3 are different,so change it for your projects or you can create different functions

1
0

It seems others have missed the fact that your workflow is to have a persistent terminal already open below your file, and you want to access the filename from inside your terminal.

I wrote a plugin that handles this by jumping to the regular file, grabbing the filename, and jumping back. In case you don't want to install it, I post the relevant script contents so you can copy that into your vimrc. Finally, a third way is in line with what you suggest, setting the filename every time you visit a buffer, so that it is already ready when you need it.

Method 1: Install and use vim-termhere

With vim-termhere installed, a binding such as the one suggested in the readme in your vimrc, and a split terminal below your code active in normal mode, you can do <leader>cf to paste the filename relative to the working dir*, or <leader>cF to paste the full filename.

*the working dir is not necessarily the same as the terminal's directory. To synchronise either way, you can use vim-dirhere. With the readme's suggested bindings, <leader>qq in normal mode with the cursor on a terminal status line (username@hostname:~/some/dir $ ) will set the working dir to ~/some/dir.

If anything fails, please open an issue on the relevant plugin.

Method 2: Copy some vimscript

If you don't want to install plugins, you can instead copy this and then use <leader>cf and <leader>cF:

function! termhere#JumpToTerminalBuffer() abort
  if &buftype ==# 'terminal'
    return
  endif
  let l:first_window_number = winnr()
  while v:true
    execute "wincmd W"
    if &buftype ==# 'terminal'
      return
    endif
    if winnr() == l:first_window_number
      break
    endif
  endwhile
  throw "Unable to find terminal window in current tab"
endfunction

function! termhere#JumpToNormalBuffer() abort
  if &buftype !=# 'terminal'
    return
  endif
  let l:first_window_number = winnr()
  while v:true
    execute "wincmd W"
    if &buftype !=# 'terminal'
      return
    endif
    if winnr() == l:first_window_number
      break
    endif
  endwhile
  throw "Unable to find non-terminal window in current tab to copy filename from"
endfunction

function! termhere#UseAbsoluteFilenameInTermBelow(prefix, ...) abort
  if &buftype ==# 'terminal'
    call termhere#JumpToNormalBuffer()
  endif
  let l:postfix = get(a:, 1, '')
  let l:filename = expand('%:p')
  call termhere#JumpToTerminalBuffer()
  call feedkeys('a' . a:prefix . l:filename . l:postfix)
endfunction

function! termhere#UseRelativeFilenameInTermBelow(prefix, ...) abort
  if &buftype ==# 'terminal'
    call termhere#JumpToNormalBuffer()
  endif
  let l:postfix = get(a:, 1, '')
  let l:filename = bufname('%')
  call termhere#JumpToTerminalBuffer()
  call feedkeys('a' . a:prefix . l:filename . l:postfix)
endfunction

nnoremap <Leader>cf :call termhere#UseRelativeFilenameInTermBelow('')<CR>
nnoremap <Leader>cF :call termhere#UseAbsoluteFilenameInTermBelow('')<CR>

Method 3: keeping track of the last filename

Although your suggestion to set an environment variable is not necessary, it is possible, and perhaps using a global may be better if you have some other setup than 'a single terminal below a single file':

First, ensure that g:last_regular_file_buffer has the last regular file name in it:

function! UpdateLastFile() abort
  if &buftype ==# 'terminal' || bufname('%') ==# ''
    return
  endif
  let g:last_regular_file_buffer = bufname('%')
endfunction

augroup LastRegularFileBuffer
  autocmd!
  autocmd BufEnter * call UpdateLastFile()
augroup END

Next, let's set up a keybinding to insert the filename.

function! InsertLastFilename() abort
  call feedkeys('a ' . g:last_regular_file_buffer)
endfunction

tnoremap <C-f> <c-\><c-n>:call InsertLastFilename()<CR>

Actually using an environment variable instead of a global, while possible, won't work the way you'd expect because a terminal external to neovim will need to be restarted to see changes to the global environment. If necessary, an external terminal can ask the current neovim instance for a value, but that is a whole different question.

0

I suggest code_runner plugin.

It is very good and the docs is good too.

Your Answer

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

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