Persistent terminal sessions in VS Code

Since May 2016, Visual Studio Code has shipped with an awesome integrated terminal, leveraging the power of the Xterm.js library. Our very own Daniel has been hard at work making many improvements over time, which reflects in the terminal’s usefulness and stability. Just check out the following screenshot, featuring a fully functional htop with colour and mouse support.

The integrated terminal in VS Code

You can give it a try by pressing Ctrl+` or using the command palette by pressing F1 and searching for View: Toggle Integrated Terminal. It supports the Command Prompt on Windows and bash, zsh and even PowerShell on all three operating systems. You can track its issues and feature progress on Github.

As a terminal-centric VS Code developer, I admit to feeling guilty of not dogfooding on this feature. I used to always keep an instance of iTerm2 next to VS Code; I used it for running a gulp watch task and miscellaneous commands. As I often restart VS Code, this setup was perfect since the watch task always kept on running and my terminal’s state was always preserved.

One of the benefits of the integrated terminal is to always be launched on the directory of your currently open workspace. As soon as you open it, all commands will run under your workspace’s scope, which is something I lack in my iTerm2 setup. Wouldn’t it be nice to just get the best of both worlds?

Enter tmux.

What is a terminal multiplexer? It lets you switch easily between several programs in one terminal, detach them (they keep running in the background) and reattach them to a different terminal. And do a lot more.

I use it as a tool to persist terminals across VS Code instances. The idea is simple: when the VS Code terminal opens up, create a named tmux session for your workspace if one does not yet exist; else, simply attach to it.

SESSION="vscode`pwd | md5`"
tmux attach-session -d -t $SESSION || tmux new-session -s $SESSION

This script computes a session name based on your current working directory. It then proceeds to attach to that session or, if it fails to do so, create it. Put it in your scripts directory of choice (mine’s /Users/joao/bin/code-shell). Then, configure the integrated terminal to use that script as the integrated shell, in VS Code’s User Settings. My configuration looks like this:

"": "/Users/joao/bin/code-shell"

That’s it! You will always have the same terminal session once you open the integrated terminal in VS Code from now on. You can run any long running commands in the terminal, restart VS Code and the same session will be resumed.

My self-hosting setup has improved quite a bit with this little hack and it has allowed me to fully self-host on the integrated terminal. The gulp watch task keeps on watching and the terminal preserves whatever state it has, across VS Code restarts. Just check out my development setup, featuring awesome tmux features like panes:

Panes in tmux

This solution doesn’t come without drawbacks, though. For example, you can’t create more than one terminal in VS Code, since a newer one will always steal the previous’ tmux session. I don’t mind this behaviour, since tmux lets me create windows within itself.

Give it a try! Let me know how this works for you. Also, make sure to follow @code on Twitter and the official blog for news and updates on Visual Studio Code.

Just in case, here are the contents of my ~/.tmux.conf, which provide a cleaner look by hiding the status bar. The reattach-to-user-namespace option fixes some issues related to the macOS Pasteboard.

set -g mouse on
set -g status off
set-option -g default-command "reattach-to-user-namespace -l zsh"

If you want to know more about the terminal within VS Code, make sure to read Mastering VS Code’s Terminal by our very own Daniel Imms.