I have been using nvim terminal mode to learn python and I want to have Ctrl-C break the python script from the terminal window even when I am not in insert mode. However, I also want Ctrl-C in my python script to result in entering an escape command directly to the terminal.

I have

if exists(':tnoremap')
    nnoremap <C-c> <C-w>wi<C-c><C-\><C-n><C-w>w  
    " Ctrl-c from py sends key interupt to python terminal

in my .vimrc file which gives me the functionality for sending Ctrl-C to the terminal when I am in the python script buffer.

I have tried :tnoremap <C-c> i<C-c> which does not work, I think because I am trying to issue the command from normal mode. This is a documented 'feature' of tmap.

I have found that nnoremap <buffer> <C-c> i<C-c> works but because the buffer has a name like :term\##:python where ## is the process id, and therefore varies, I cannot figure out how to incorporate the above into my rc file such that the terminal buffer automatically inherits this (local) functionality.

I have tried (from this answer)

augroup MyTermMappings
  autocmd TermOpen * nnoremap <buffer> <C-c> i<C-c>
augroup END

but this makes my whole rc file crap out with the error no such group or event TermOpen * nnoremap <buffer>...

I have also tried changing my launch of the terminal command (which is currently nnoremap <leader>r :split \| terminal python<CR> <C-w>w) to send the local remap to the command line on launch but all I have been able to accomplish is crashing the python terminal.

I know I can just run the nnoremap <buffer> <C-c> i<C-c> after launching terminal but I would like to have any buffer that is launched with :terminal have this command.

Alternatively, I tried having the default behavior be that in normal mode Ctrl-C mapped to enter insert mode and type Ctrl-c e.g. nnoremap <C-c> i<C-c> and then further in the .vimrc file autocmd FileType python nnoremap <buffer> <C-w>wi<C-c><C-\><C-n><C-w>w but that also did not work.

I think this can be solved using autocmd options in Vim or if the nmap cmd can take a pattern but I am fairly unfamiliar with these and have not had any luck looking at other responses.

  • Why not create an autoloaded function that runs :term and sets up your desired mappings, and bind that to a key or command in your python after/ftplugin ? – D. Ben Knoble Apr 1 at 1:57
  • Tried the autocmd TermOpen .. approach, it works. My nvim is v0.3.1. – dedowsdi Apr 1 at 2:36
  • @D.BenKnoble could you explain that a bit more? I am a little bit new to all of this – nmb Apr 1 at 3:45
  • I can confirm that autocmd TermOpen ... does work when I write, save and load in nvim but it does cause the above error when using regular vim. – nmb Apr 1 at 3:54
  • @nmb ill try to put together an answer with some different options. – D. Ben Knoble Apr 1 at 12:41

Using autocmds

Neovim provides TermOpen, while vim provides TerminalOpen:

augroup MyTermMappings
  if has('nvim')
    autocmd TermOpen * nnoremap <buffer> <C-c> i<C-c>
  else if exists('##TerminalOpen')
    audocmd TerminalOpen * nnoremap <buffer> <C-c> i<C-c>
augroup END

I've provided another solution, but this is probably simpler and more general.

Via function and ftplugin

Write a function in ~/.vim/autoload/terminal.vim (may need to adjust ~/.vim for neovim users):

" ~/.vim/autoload/terminal.vim
" The name <terminal.vim> is arbitrary, but affects function declarations
" within
" cf :help autload
function! terminal#python() abort
  terminal python
  " this is the equivalent of <C-w>w
  wincmd w
  nnoremap <buffer> <C-c> i<C-c>

And bind it to your terminal mapping:

" ~/.vim/after/ftplugin/python.vim
" cf
" - :help ftplugin
" - :help after-directory
" (after notes that after is rarely necessary, but I like it to overrule the
" settings)

" Use whatever keys you want
nnoremap <silent> <buffer> <LocalLeader>r :call terminal#python()<CR>

" Don't forget to add to 'b:undo_ftplugin'
" I've got a specialized framework for that over at
" https://github.com/benknoble/Dotfiles/blob/master/links/vim/autoload/ftplugin.vim
" but this is simpler and should work for your purpose
let b:undo_ftplugin = get(b:, 'undo_ftplugin', '')
if ! empty(b:undo_ftplugin)
  let b:undo_ftplugin .= ' | '
let b:undo_ftplugin .= "execute 'silent! nunmap <buffer> <LocalLeader>r'"

Side Note: your normal mapping for python files could also go in the ftplugin file above, unless you want it for all files.


Adding this to my .vimrc seems to have fixed it for my purposes without slowing down vim load or python launch

augroup pythonTerminal      " name localized commands so they don'g get
                            " written on every rc read
  autocmd!                  " delete all exisiting au commands in group
  autocmd BufWinEnter,WinEnter term://* nnoremap <buffer> <C-c> i<C-c>
                            " <C-c> interrupts python in terminal normal
  autocmd BufWinEnter,WinEnter *.py :nnoremap <buffer> <C-c> 
      \ <C-w>wi<C-c><C-\><C-n><C-w>w
      " <C-c> kills python terminal running from script file
augroup END

I am not sure if I should add all my other python interactive mode commands to this and/or when autocmd is better to use than just the remap command but, to date at least, I haven't run into any issues in either vim or nvim with this snippet.

Thanks to everyone for their help!

  • 1
    Note that you don't need to add : in an autocommand because you are already executing a command. : is only needed in normal mode to start command line mode. Also it is normal that termOpen doesn't work in Vim since this event is implemented only by neovim. – statox Apr 1 at 7:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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