I'm getting something weird characters in :messages. How can I get the right results?

reproduce steps

  1. define functions.
let s:rails_console_id = 0
function common#utilities#open_rails_console() abort
  let s:rails_console_id = termopen("vagrant ssh -c 'bin/rails c'", 
        \ {'on_stdout': function('s:echo_result') })

function s:echo_result(id, data, status) abort
  echom join(a:data, "\n")

function common#utilities#send_keys(keys) abort
  call chansend(s:rails_console_id, a:keys)

  1. :call common#utilities#open_bash()
  2. :call common#utilities#send_keys("5.method('prime?').source_location\n")
  3. :messages

:messages outputs the following result. I want to convert into the right characters '^M', '^[[31m', '^[[0m', and so on.

^[[1A^[[0G[3] pry(main)> ^[[1;34m5^[[0m.method(^[[31m^[[1;31m'^[[0m^[[31mprime?^[[1;31m'^[[0m^[[31m^[[0m).source_location^[[1B^[[0
=> [^[[31m^[[1;31m"^[[0m^[[31m/home/vagrant/.rbenv/versions/2.3.1/lib/ruby/2.3.0/prime.rb^[[1;31m"^[[0m^[[31m^[[0m, ^[[1;34m34^[[0
m]^M^@[4] pry(main)>


  • For starters, there are ANSI escape sequences in the shell prompt. Those aren't going to render properly in a standard vim/nvim message. Would be easier to capture plain-text output from ls using a different approach. What exactly is your goal?
    – B Layer
    Nov 8, 2020 at 23:55
  • Are you deliberately trying to capture the prompts? If not why not just use system() to run shell commands rather than this elaborate set up? (This is really just an expanded version of my previous question "What exactly is your goal?")
    – B Layer
    Nov 9, 2020 at 0:39
  • My goal exactly is to interact with REPL(etc, rails console). I want to send expressions to REPL and get the result of it. So need to capture the prompts. Nov 9, 2020 at 1:02
  • Don’t use the messages buffer for this. Store the data or put it in a buffer... or just hide the terminal... also, your current Q doesnt show how you set b:terminal_job_id
    – D. Ben Knoble
    Nov 9, 2020 at 1:15
  • Interacting with a REPL from Vim is a problem that's been solved many times in many ways. I'd be surprised if this hasn't been answered here at Vi/Vim SE before. (Definitely don't want to use the approach you're using...even if the goal is to actually implement all this yourself.)
    – B Layer
    Nov 9, 2020 at 1:22

1 Answer 1


When you run a command in a terminal (such as what termopen() does), the program assumes it's running in an interactive environment with an user connected to a terminal emulation program. Many programs will use that fact to decide whether to use interactive features such as colorizing the output.

Terminal emulation programs implement colors and attributes through special escape sequences, that tell the terminal which colors to switch to (or can perform other terminal operations, such as moving the cursor, clearing lines, switching modes, etc.)

What you're seeing on your output is the terminal escape sequences printed by the rails console, which it's printing since it assumes it's talking to a human on a terminal. When you're taking the raw contents coming from it (through the on_stdout hook), you're capturing those as well. The Vim messages buffer (written to with echom) doesn't really understand terminal sequences (it's not meant for raw terminal output), so it will display all the escape sequences verbatim, which is what you're seeing.

The workarounds I would suggest involve removing the color output. If you want to keep dumping contents to the messages buffer, just removing the colors is probably the simpler way. (The messages buffer actually supports colors, but through a separate mechanism.involving the echohl command. While in theory it would be possible to translate escape sequences to keep colorizing output in the messages buffer, that's probably quite complex to implement, so I'll keep it out of the scope here.)

Programs typically detect that they're talking to a terminal by detecting that they're attached to a pty. The termopen() function will always provide a pty to the executed command, so that detection will always trigger. One possible alternative to consider is using the jobstart() function instead, which can take a pty option to control whether to run the external command in a pty or not.

Another alternative is to pass the external command an.option to tell it not to colorize the output (in other words, not to produce the escape sequences), even though it's still running in a pty. It turns out that the rails console has such an option that you can set, by running rails console -- --nocolorize. (Note: rails console uses irb, the interactive Ruby environment, under the hood. And it passes it all the arguments received after a single --. So you can use this technique to pass irb additional arguments to make it more friendly to your environment.)

The last possibility I'd mention is for you to use a different location for the output of the external command. Since it's a terminal command, using a terminal buffer would be the most appropriate. Vim's terminal does recognize those escape sequences natively, so it's pretty natural to just send the output there, rather than building all this plumbing to send it to the messages buffer, which just doesn't recognize the escape sequences and doesn't really seem to be the most appropriate destination for that output.

  • 1
    I don't I think it's applicable/desirable for this scenario but there's a plugin that translates ANSI sequences for colored text into Vim highlight format called AnsiEsc.
    – B Layer
    Nov 9, 2020 at 10:33
  • @BLayer Yes there's AnsiEsc and I think there are a couple more, but I believe they all apply to a Vim buffer and not the messages buffer... With Vim having built-in terminal buffers though, it's often easier to just use those, which support handling the escape sequences natively.
    – filbranden
    Nov 9, 2020 at 14:59
  • 1
    My point being that if one wants to go with a more custom route (which doesn't sound like OPs goal) and needs to do such a translation (e.g. to color echo'd messages) then a look at what AnsiEsc does would save oneself a whole lot of time.
    – B Layer
    Nov 10, 2020 at 8:56

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.