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?
5 Answers
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>
-
2I 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. Commented 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?– VishalCommented 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. Commented Mar 28, 2022 at 9:07
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
-
Welcome to Vi and Vim! Thanks for spreading the knowledge– D. Ben Knoble ♦Commented Jan 20, 2020 at 14:22
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.
:term %
or:term python %
not work?