I want to map a keyboard shortcut to execute the last command in a terminal buffer that I have open.

I've made some progress: I am storing the buffer's channel id correctly but when I try to send keys to the terminal (up arrow and enter) they always appear as literal characters in the terminal rather than actually going to the previous command and executing it.

" remember the chan id (buffer id) of the last terminal buffer
augroup Terminal
  au TermOpen * let g:last_terminal_chan_id = b:terminal_job_id
augroup END

" WIP: doesn't work yet
function! REPLSend()
    " the following commented out lines were taken from another plugin that sends keys to a terminal - it doesn't seem to work
    " let isnormal = mode() ==# 'n'
    " let curwin = winnr()
    " exe "sb " . g:last_terminal_chan_id
    " call cursor('$', 1)
    " exe curwin . 'wincmd w'
    " if isnormal
    "     stopinsert
    " endif
    " call chansend(g:last_terminal_chan_id, '<Up>' . '\n')
    call term_sendkeys(g:last_terminal_chan_id, "i\<Up>\<cr>")

command! REPLSendLine call REPLSend()

nnoremap <silent> <f5> :REPLSendLine<cr>
  • Welcome to Vi and Vim! Can you clarify "appearing as literal characters"? The help says {keys} are translated as key sequences. For example, "\<c-x>" means the character CTRL-X., so I would expect this to "work"
    – D. Ben Knoble
    Oct 8 '19 at 16:37
  • If under bash then it's enough to execute "!-1"
    – Matt
    Oct 8 '19 at 16:41
  • @Matt !! too...
    – D. Ben Knoble
    Oct 8 '19 at 18:31
  • @D.BenKnoble the characters "i\<Up>\<cr>" show up as terminal input. Nothing is executed. I tried some other combinations to the exact same effect: eg "<Up><CR>"
    – Salami
    Oct 9 '19 at 13:05
  • Did you try inserting them literally (with control-V)?
    – D. Ben Knoble
    Oct 9 '19 at 13:34

I don't have term_sendkeys() in my neovim instance, so I'll use chandsend() in my example. Also, the i is unnecessary, since this function can't put you into insert mode.

Now, regarding your problem. "\<Up>" does indeed have some unexpected behavior inside double quotes. But, I managed to work around it like this:

  1. Open vim preemptively to not get error on load.
  2. While in terminal, start by writing echo -n '' >> ~/.vimrc, then put cursor between quotes and press control-v followed by Up, then enter to execute your command.
  3. Reload .vimrc with :e!, delete the last line with literal Up sequence, and p it wherever you want (in this case, into chansend function).

Upd: Another way: enter keys by hex code directly in vim:

  1. Find out your Up key byte sequence in terminal by piping it to hexdump -C (same method with echo as described above). This is important, because Up key may produce different sequences based on your $TERM value, shell, and system-global key remappings. Here's what mine looks like:
    echo -n '^[OA' | hexdump -C
    6 00000000  1b 4f 41                                          |.OA|
    5 00000003
  1. Enter every byte in vim by pressing C-v, followed by x, and then hex code. E.g. C-v x 1 b for first byte, and so on...

This seem to work correctly:

call chansend(b:terminal_job_id, '^[OA' . "\<cr>")

Do not try to copy code from here, though. You need to get actual literal Up sequence from your shell.

Upd.2: Actually, scratch almost all of above, and just specify bytes as hex sequences, like this:

call chansend(b:terminal_job_id, "\x1b\x4f\x41\<cr>")
  • term_sendkeys is vim’s term api
    – D. Ben Knoble
    Oct 10 '19 at 0:00
  • You also dont need to do the literal echo from the shell; you can use C-v in vim
    – D. Ben Knoble
    Oct 10 '19 at 0:00
  • @D.BenKnoble In vim, if you do C-v Up, it inserts plain text <Up> instead of literal byte sequence of Up keystroke. And you need the latter to make this work. Oct 10 '19 at 0:08
  • duh /facepalm. I wonder if theres a way to do it, still, as that is a hard workaround
    – D. Ben Knoble
    Oct 10 '19 at 0:10
  • @D.BenKnoble Turns out, there is! I've updated my answer. Oct 10 '19 at 0:28

I couldn't get the up arrow key working, it seems tricky and I don't think it's the best way. But thanks to @thunderbeef's answer I got the enter key working, and a solution I'm happy with:

    call chansend(g:last_terminal_chan_id, "!!\<cr>")

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.