intellij-ideapycharmzshoh-my-zshgoland

How do I get IntelliJ Terminal to work properly with Oh My Zsh?


I love Oh My Zsh, but it has never worked properly in the JetBrains product's Terminals:

  • cannot find executables
  • cannot use version managers like pyenv, sdkman, rvm

Oh My Zsh is zsh shell augmentation, so the actual problem could be reduced to just getting zsh to work properly. I have tried toggling all of the Terminal config options (individually and en masse) after reading some intellij issues, to an avail.

ref: https://github.com/robbyrussell/oh-my-zsh


Solution

  • Can't find binaries, can't run stuff? Obviously a $PATH problem, but what and why?

    I echo'd a known good path in iTerm2

    /Users/starver/.sdkman/candidates/maven/current/bin:/Users/starver/.sdkman/candidates/groovy/current/bin:/Users/starver/.sdkman/candidates/gradle/current/bin:/usr/local/Cellar/pyenv-virtualenv/1.1.3/shims:/Users/starver/.pyenv/shims:/Users/starver/.pyenv/bin:/Users/starver/.cargo/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/go/bin:/opt/X11/bin:/usr/local/git/bin:/Users/starver/bin/:/Users/starver/code/go/bin/:/Users/starver/.rvm/bin

    and in IntelliJ:

    /usr/bin:/bin:/usr/sbin:/sbin
    

    This hints at a startup file loading problem. My zsh man page says the load order should be:

    • /etc/zshenv
    • $ZDOTDIR/.zshenv
    • If a login shell:
      • /etc/zprofile
      • $ZDOTDIR/.zprofile
    • If an interactive shell:
      • /etc/zshrc
      • $ZDOTDIR/.zshrc
    • If a login shell:
      • /etc/zlogin
      • $ZDOTDIR/.zlogin

    After adding an echo to each of those files that existed, I got the following for iTerm2:

    /etc/zprofile
    /Users/starver/.zprofile
    /etc/zshrc
    /Users/starver/.zshrc
    /Users/starver/.zlogin
    

    and this in IntelliJ

    /etc/zshrc
    /Users/starver/.zshrc
    

    IntelliJ thinks this is not a login shell. In Jetbrains terminal configuration, you cannot enter /bin/zsh --login; it has no effect. After playing a bit, I found that turning on Tools -> Terminal -> Shell Integration makes the terminal a "login shell" and the startup file load story improved a bit:

    /etc/zshrc
    /Users/starver/.zprofile
    /Users/starver/.zshrc
    /Users/starver/.zlogin
    

    Notice that none of the global zsh startup files and this is the root problem: /etc/zprofile contains:

    # system-wide environment settings for zsh(1)
    if [ -x /usr/libexec/path_helper ]; then
        eval `/usr/libexec/path_helper -s`
    fi
    

    which man path_helper explains:

    The path_helper utility reads the contents of the files in the directories /etc/paths.d and /etc/manpaths.d and appends their contents to the PATH and MANPATH environment variables respectively. (The MANPATH environment variable will not be modified unless it is already set in the environment.)

    Executing path_helper at least once during shell startup is REALLY important: paths and paths.d are where the system and third party installers define their path additions. Not executing the system profile startup file is why /usr/local/bin, /usr/local/go, etc. are not on the path.

    I tried several approaches, looking for an elegant solution. Apparently, the jediterm terminal implementation prevents hooking into a standard terminal startup process - so they implement startup file loading in /Applications/IntelliJ IDEA.app/Contents/plugins/terminal/.zshrc. We can fix that implementation!! Replace that file with:

    #!/bin/zsh
    
    # starver mod
    # Jetbrains uses jediterm as a java terminal emulator for all terminal uses.
    # There are some apparent limits on use:
    # - must use old-style shebang - not the #!/usr/bin/env zsh
    # - must implement the startup file loading here
    #
    # Note: original contents are in lib/terminal.jar
    
    # mappings for Ctrl-left-arrow and Ctrl-right-arrow for word moving
    bindkey '^[^[[C' forward-word
    bindkey '^[^[[D' backward-word
    
    ZDOTDIR=$_OLD_ZDOTDIR
    
    if [ -n "$JEDITERM_USER_RCFILE" ]
    then
      source "$JEDITERM_USER_RCFILE"
      unset JEDITERM_USER_RCFILE
    fi
    
    if [ -n "$ZDOTDIR" ]
    then
      DOTDIR=$ZDOTDIR
    else
      DOTDIR=$HOME
    fi
    
    if [ -f "/etc/zshenv" ]; then
         source "/etc/zshenv"
    fi
    
    if [ -f "$DOTDIR/.zshenv" ]; then
         source "$DOTDIR/.zshenv"
    fi
    
    if [ -n $LOGIN_SHELL ]; then
      if [ -f "/etc/zprofile" ]; then
           source "/etc/zprofile"
      fi
      if [ -f "$DOTDIR/.zprofile" ]; then
           source "$DOTDIR/.zprofile"
      fi
    fi
    
    if [ -f "/etc/zshrc" ]; then
         source "/etc/zshrc"
    fi
    
    if [ -f "$DOTDIR/.zshrc" ]; then
         source "$DOTDIR/.zshrc"
    fi
    
    if [ -n $LOGIN_SHELL ]; then
      if [ -f "/etc/zlogin" ]; then
           source "/etc/zlogin"
      fi
      if [ -f "$DOTDIR/.zlogin" ]; then
           source "$DOTDIR/.zlogin"
      fi
    fi
    
    if [ -n "$JEDITERM_SOURCE" ]
    then
      source $(echo $JEDITERM_SOURCE)
      unset JEDITERM_SOURCE
    fi
    

    Now, on IntelliJ terminal startup, I see

    /etc/zshrc
    /etc/zprofile
    /Users/starver/.zprofile
    /Users/starver/.shell-common
    /etc/zshrc
    /Users/starver/.zshrc
    /Users/starver/.zlogin
    

    The first /etc/zshrc is executed before the plugin's .zshrc, nothing I can do about that, and it is not causing any bad side effects...

    Repeat the process for every JetBrains product and you can have the joy that is Oh My Zsh everywhere.

    Note: Issue reported to JetBrains in https://youtrack.jetbrains.com/issue/IDEA-194488.