5

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!
  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>")
endfunction

command! REPLSendLine call REPLSend()

nnoremap <silent> <f5> :REPLSendLine<cr>
9
  • 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
    Commented Oct 8, 2019 at 16:37
  • If under bash then it's enough to execute "!-1"
    – Matt
    Commented Oct 8, 2019 at 16:41
  • @Matt !! too...
    – D. Ben Knoble
    Commented Oct 8, 2019 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
    Commented Oct 9, 2019 at 13:05
  • Did you try inserting them literally (with control-V)?
    – D. Ben Knoble
    Commented Oct 9, 2019 at 13:34

4 Answers 4

2

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>")
7
  • term_sendkeys is vim’s term api
    – D. Ben Knoble
    Commented Oct 10, 2019 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
    Commented Oct 10, 2019 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. Commented Oct 10, 2019 at 0:08
  • duh /facepalm. I wonder if theres a way to do it, still, as that is a hard workaround
    – D. Ben Knoble
    Commented Oct 10, 2019 at 0:10
  • @D.BenKnoble Turns out, there is! I've updated my answer. Commented Oct 10, 2019 at 0:28
1

the neovim-lua 0.5 + fish shell + kitty terminal solution for this is

vim.cmd("call chansend(" .. job_id .. ', "\x1b\x5b\x41\\<cr>")')

its a different hex code, ^[[A for the mentioned combination. Took me a while to figure out, so I figured this is the best place to mention it.

3
  • Welcome to Vi and Vim!
    – D. Ben Knoble
    Commented Nov 3, 2021 at 13:10
  • I'm surprised this doesn't work everywhere. \x1b is ESC, \x5b is [, and \x41 is A. That means it's sending ESC[A, which is the cursor up ANSI escape control: gist.github.com/fnky/…. At the very least it worked for me, too. Thanks for posting! Commented Mar 22, 2022 at 14:49
  • I can also do call chansend(job_id, "\<Esc>[A\<CR>") which makes it slightly clearer, at least for me. Commented Mar 22, 2022 at 14:54
0

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>")
0

I had been wanting to do the same thing. Your question helped so much! I wrote a super small plugin that does just this: https://gist.github.com/cowlicks/5d70685346bf341a71f26e564b795a76

1
  • Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
    – D. Ben Knoble
    Commented Feb 5 at 1:31

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.