Making Emacs ansi-term and zsh play nice together

Providing a solution to a common problem encountered when using zsh from inside emacs using *ansi-term*

This is a quick post to document a solution to a problem I have had today and for which no solution was found online. A little background first. I normally use x-term for all my terminal needs but since I am a heavy Emacs user I have also occasionally used emacs as a terminal emulator. In an effort to go even further towards Emacs integration I have been toying with Emacs *ansi-term* which seems to be the most fully functional terminal emulator inside emacs.

Unfortunately since I am using the z-shell, just like some other people ansi-term seemed to be echoing each command back to the buffer in between some numerical characters.

ansi-term and zsh not playing nice

As you can see from the screenshot above, surely enough we do get the exact behavior that people in the above linked pages mention. Their suggestion was to simply give up and change to a simpler shell emulator just like M-x shell

But this is not a proper solution. We have to figure out the root cause of the problem and decipher what the echoing and those strange numbers mean. We are in luck since behavior from a terminal like this possibly means that something is trying to set the title of the terminal window. Since *ansi-term* does not know how to interpret this command it simply echoes it back.

If you are like me and started using zsh in the past by cloning an initial pre-configured setup like prezto or ohmyzsh then this would be the place to look for the root of the problem. I personally use prezto and if you go to prezto/modules/terminal/init.zsh there you will see a zsh function that actually attempts to set the terminal window title after each command.

# Sets the tab and window titles with a given command.
function _terminal-set-titles-with-command {
  emulate -L zsh
  setopt EXTENDED_GLOB
 
  # Get the command name that is under job control.
  if [[ "${2[(w)1]}" == (fg|%*)(\;|) ]]; then
    # Get the job name, and, if missing, set it to the default %+.
    local job_name="${${2[(wr)%*(\;|)]}:-%+}"
 
    # Make a local copy for use in the subshell.
    local -A jobtexts_from_parent_shell
    jobtexts_from_parent_shell=(${(kv)jobtexts})
 
    jobs "$job_name" 2>/dev/null > >(
      read index discarded
      # The index is already surrounded by brackets: [1].
      _terminal-set-titles-with-command "${(e):-\$jobtexts_from_parent_shell$index}"
    )
  else
    # Set the command name, or in the case of sudo or ssh, the next command.
    local cmd="${${2[(wr)^(*=*|sudo|ssh|-*)]}:t}"
    local truncated_cmd="${cmd/(#m)?(#c15,)/${MATCH[1,12]}...}"
    unset MATCH
 
    set-window-title "$cmd"
    set-tab-title "$truncated_cmd"
  fi
}

Rings any bell? Yep you are right! Our zsh setup is simply too smart for *ansi-term*. But I don’t know a lot about zsh functions and stuff you say? How do I fix it? Easy enough! At the bottom of the aforementioned file there is a conditional check that allows us a clean and easy way to customize the behavior of the module for our emacs *ansi-term*.

# Set up non-Apple terminals.
if zstyle -t ':prezto:module:terminal' auto-title \
  && ( ! [[ -n "$STY" || -n "$TMUX" ]] )
then
# Sets the tab and window titles before the prompt is displayed.
add-zsh-hook precmd _terminal-set-titles-with-path
 
# Sets the tab and window titles before command execution.
add-zsh-hook preexec _terminal-set-titles-with-command
fi

What this means is that with the usage of the zstyle command you can add this to the bottom of your .zshrc, or right after all the prezto setup has finished.

if [[ -n ${EMACS} ]]; then
    zstyle ':prezto:module:terminal' auto-title 'no'
fi

Once that is done, the title of the window will be set before the prompt is displayed and as such *ansi-term* will not be echoing anything back to us.

Hope the above helped other people who have found the same annoying problem as me while using zsh and the emacs terminal emulator. If you are using emacs and ohmyzsh I can’t provide more detailed advice but use ack or grep to search for similar implementation of window title setting as I showed that prezto has above. Then proceed to circumvent it only for *ansi-term* by checking for the $EMACS environment variable.

That’s all. Till next time!

2 thoughts on “Making Emacs ansi-term and zsh play nice together”

Leave a Reply

Your email address will not be published. Required fields are marked *