In this article, I’m going to describe how you can do almost all of your development work in terminal windows. By using the right combination of command line tools along with some nifty customization, you can dramatically improve user experience!
If you work with remote servers, you will almost always be accessing those servers via ssh and using the command line. If you have a Mac or Linux based workstation, you likely spend a lot of time at the command line already. If you use both, you can trivially have your customized command line environment everywhere.
Graphical User Interfaces (GUIs) have become better and better of the past decade, but they introduce certain functionality that is productivity killing. Frequently moving my hand from the keyboard to the trackpad/mouse is a distraction from what I actually want to do. Getting my hands back onto the home position of the keyboard takes time, too. I find myself often typing gobbledegook because one of my hands ends up shifting one letter over from the home position. I end up with dozens of shell windows and browser windows on the screen and often the one I really want to see gets buried under several of the others; getting that window to the front takes several keystrokes (Command-Tab a few times) or several gestures and trackpad operations.
Working with the command line (as much as possible!), you can focus on one window to do the bulk of your work. Working with your fingers on the home row of your keyboard most of the time is more productive than moving your hand to the mouse/trackpad and back to the keyboard. I observe that I have a lot of windows on top of other windows on my Mac and Linux desktops – finding a window that you want to see, among all that mess, also hurts productivity.
Key bindings and macros further increase your productivity; you type a character or two and your environment performs operations or types much longer text for you. The more your environment types for you, the more productive you are. This is a key piece of the shell environment strategy.
Setting up a command line environment for development involves choosing a shell (bash, zsh, etc.), an editor (vim, emacs, etc.), a terminal multiplexer (screen, tmux, etc.), and perhaps ultimately a tiled window manager (Linux only, i3wm, i3gaps, etc.). I’m going to give you a taste of what these programs are like, enough to inspire you to do deeper dives into each.
Keep your dot files in a GitHub Repository
All of these programs, along with many others that you already use via the command line, have configuration files. These configuration files are generally called “dot files” since the configuration files tend to have names like .bashrc (dot bashrc), .vimrc (dot vimrc), and so on.
I urge you to keep your dot files in a GitHub repository. You may view mine to get an idea of what you can do with such a repository. Not only do you get the advantages of source control, but you can trivially get your dot files onto any system (like a remote server) with a simple git clone. Some of these files you will be editing quite often; changes you make on one system are ones you will want to propagate to the rest of the systems you use.
The trick to using a dot files repository is to have shell scripts to install your dot files. I have ~/dotfiles/ on my systems, the cloned repository. The install.sh script installs all the needed packages using apt on Linux or brew on Mac – it does the “right thing” on either operating system. The script replaces existing dot files in the home directory with soft links to the files in ~/dotfiles/, adding any additional links as needed. To get my environment on any machine, it’s
just two commands: git clone, and install.sh.
ZSH
I use zsh as my shell, along with Robby Russell’s great (~66,000 stars on GitHub) oh-my-zsh configuration framework. The configuration provides hundreds of optional plugins for interaction with git, brew, and other command line programs. There are hundreds of themes available as well. I installed nerd fonts and used the agnoster theme that gets me the nice command line, with the user@host, current directory, and git repo/branch info (when I’m in a git project directory).
If you prefer bash or some other shell, you’ll likely find a similar framework. The pretty information display is generated by a program called neofetch, executed at the end of my .zshrc.
I’ve split up my .zshrc into aliases.zsh, env.zsh, and functions.zsh. These are sourced from ~/dotfiles/zsh/ by .zshrc, which is otherwise mostly the one that oh-my-zsh installs. This separation of functionality keeps my train of thought on the tracks and my files less cluttered.
My aliases are of four basic types: handy, ssh hosts, and vim. Aliases are a lot like macros I described earlier. To ssh into my bigmoney host, I could type ‘ssh -X bigmoney’, but my bigmoney alias allows me to type a few letters of “bigmoney” and hit the tab key to complete.
Saving keystrokes and avoiding switching to/from mouse/trackpad constantly is the ideal.
I have two functions to mention here. One, sssh, lets me ssh into a remote host and also starts a tmux session on that host (more on tmux shortly). The other replaces the cd command with cd followed by ls. I find I almost always do a cd to change directory and then immediately do an ls to see what’s there. This shortcut saves me keystrokes!
Another important thing to note about zsh is that you can configure it to use vim key bindings for editing the command line. As I have things set up, I am using vim key bindings for as many apps as possible. This makes the integration of terminal, editor, and commands ubiquitous and consistent.
You will want to create functions and aliases of your own to optimize how you do things. Save keystrokes, and add intelligence to the commands you type in.
TMUX
I use tmux as a manager for my console/terminal sessions. It is quite powerful. When you start tmux (just type “tmux” at the command line), a session is started, the window clears, and you get a new zsh prompt. If you close your window, open a new window, and run tmux again, you will resume your session – the shell and programs running in it will be in the same state as when you closed the first window.
There are a number of tmux cheat sheets to be found that describe much of the functionality and many of the key bindings. I’ll describe a few of the more useful ones I use most often, and leave it up to you to learn tmux’s full functionality. You will want to customize its functionality by editing the .tmux configuration file; this way you can set up key bindings as you see fit, and even add functionality through 3rd party plugins.
You will typically name your sessions via the -s and -t switches to the tmux command. Things now are interesting – you can have a session for editing configuration files, and another session for editing one project, and another session for editing another. Switching contexts is as simple as typing the tmux “attach” command.
I have the following alias in my .zshrc:
alias lvim='! tmux -2 detach-client -s lvim; tmux -2 new -A -s lvim'
This command detaches any existing clients using the “lvim” session and then creates/attaches to the “lvim” session. The -2 switch forces tmux to support 256 colors.
There is a command key that you use to start a command sequence for tmux; I configured control-a as my command key.
To get help, control-a followed by ? (or C-a ? For brevity’s sake).
To detach from the current session, C-a d (or close the console window).
In addition to sessions, tmux allows each session to have multiple “windows.”
To create a new window, C-a c. To switch between windows, C-a n (next window) or C-a p (previous window). You can jump to a specific window by number, too: C-a 1 (switch to window 1). Two windows in one session allows you to have two zsh running; one perhaps an editor, the other a zsh prompt for command entry.
Note that I now have as many terminals/command lines in one window as I want and can trivially switch between them from the keyboard.
A feature of tmux is its ability to create terminals within split panes of any of your tmux windows (tmux window, not GUI window). Switching focus between these panes is also done via keyboard shortcuts, so again, I do not have to use the trackpad or mouse. The split pane feature becomes more interesting when you’re running terminal applications, like vim or ranger.
vim
When you think of editing source code, you probably think of using an editor with a GUI, like one of the JetBrains IDEs or Sublime Text. While these solutions are wonderful in their own right, they are subject to the distractions of moving our hand from keyboard to trackpad/mouse. In keeping with the productivity theme, we use the vim editor.
Vim is one of the oldest editors around. Over the past decades, it has become feature rich and incredibly powerful and flexible as driven by its large user community. It loads very fast and uses a modest amount of your computer’s resources. You will likely find it already installed on most (if not all) *nix flavored systems, so learning it will allow you to comfortably edit no matter what system you are using.
At first, vim may seem awkward to use, but it is well worth learning. It has a fairly unique concept of editing modes – normal mode, insert mode, command line mode, etc. In normal mode, you move the cursor around and perform operations on already entered text. In insert mode, you are typing in new text, for the most part. Command line mode allows you to perform edits, like search and replace, and you exit the editor via a command. Most GUI based editors are always in insert mode, and use of any command line like feature isn’t necessarily a common task.
One thing that will certainly throw you off, the first time (or two) you run vim is how you quit! Hit Esc key, then :, then type q! then the enter key. Or Esc then Z then Q.
In normal mode, you move the cursor around, one character at a time, using the h (left), j (down), k (up), and l (right) keys. They are the right hand home keys for proper typing position. The b key moves back a word and the w key moves forward a word. The e key moves forward a word, but leaves the cursor on the last character of the word. The 0 key moves the cursor to the beginning of line and $ moves to the end of the line. The x key deletes the character under the cursor.
The cursor, page up, page down, etc., keys work as you expect, but many vim users bind those to no-op in their .vimrc to train themselves to use the hjkl style cursor movement. Moving your hand to the cursor keys is a distraction, and harms productivity!
The hjkl, and other normal mode movements, becomes muscle memory in short order. They are also important to learn because many of the terminal mode applications also support vim key bindings. Zsh, for example, allows command line editing using vim keys (right down to normal and insert modes), as does bash and ranger.
To enter insert mode, you simply press the i key and start typing. To type text after the cursor, press the a key instead. If you want to add text to the end of a line, use shift-a, which will move the cursor to the end of the line and go into insert mode (basically shorthand for $ then i). To exit insert mode, hit the Esc key. As you become proficient with vim, you will be going in and out of insert mode a lot.
It is easy enough to turn vim into a powerful IDE for most languages. There is a rich ecosystem of 3rd party plugins that provide all sorts of functionality – pick and choose the ones that suit your requirements. It’s easy to use plugins if you first install a plugin manager, such as Vundle; then you just add “Plugin” lines to your .vimrc, one for each plugin you want to install.
You can split vim (using :hsplit or :split), much like tmux, into multiple windows/tabs and panes. As you can see in the screenshot above, there’s a plugin for the file/project tree in the left pane, and two stacked panes with source code in them. Vim syntax highlights many languages out of the box, but I have installed an enhanced highlighter for JavaScript.
As I mentioned earlier, my dotfiles are publicly available at GitHub. I don’t suggest you necessarily clone my dotfiles and use them as-is. Rather, you should look at mine and at many others to figure out how you want your vim personalized. You can see which plugins I chose, google those to read up on them, etc.
Your .vimrc file (and other dotfiles) are how you customize your environment. Especially for vim, you are likely to be editing your configuration a lot. As you find yourself typing the same long strings of text over and over again, you should edit your .vimrc and create a key binding to type that string in for you. For example, I have ,ir bound to insert
import react, {Component} from ‘react’;
I have ,e bound to
:e ~/.vimrc
to make getting my .vimrc into the editor super easy. When I save my .vimrc, I have set up vim to reload it so my changes take effect immediately without extra keystrokes or quitting and restarting the editor. It’s a good idea to have similar shortcuts to edit your more commonly edited dotfiles, too.
The learning curve for vim may appear steep, but it’s well worth it. To get started, make sure you have vim installed on your system and then type: vimtutor
at the command line.
Ranger
I’ve mentioned ranger a few times. Ranger is a command line file manager and file system navigator. As you can see, you are presented with 3 columns. You navigate through your file system using vim hjkl keys. As you navigate to a file in the right column, and go “right” to open it, it is automatically opened using the appropriate application. For text files, vim is opened. For .zip files, the contents are shown:
When you quit the opened app, you are returned to your previous ranger view.
Ranger has an configuration file, found in ~/.config/ranger/rc.conf where you can customize how it works. With this flexibility and default vim keybindings, it fits right in with your command line integrations. A typical customization is to add key bindings to, say, do git operations on the current directory or file. Your imagination is the only limit.
Workflow: vim + tmux + zsh
I use the vim-tmux-navigator plugin for tmux and vim. The plugin allows me to seamlessly navigate between vim and tmux panes using ctrl + the hjkl navigation keys.
Let’s consider a personal project I work on from time to time. It consists of multiple Docker containers, each running a microservice written for Node.js. Each of those are in their own GitHub project. I have another project that has an Express.js server that dynamically builds and serves a React web application. For the most part, I have most of the microservices already built as containers and running on a dedicated server. For those I am currently working on, I run those microservices on my development machine. I run the Express.js server on my development machine as well.
I ssh into the server and run tmux there. In the tmux session, I am monitoring the docker logs and watching the mqtt messages come and go. I can close the window any time and then ssh into the machine later, reattach to the tmux session, and be right where I left off.
On my workstation, I run vim and 2 zsh panes, below, in a tmux session. The left pane is where I start/restart the Express.js app. The right one is a general purpose command line – where I do my git commands, etc. I open a second terminal window with a tmux session with one window per microservice being worked on.
I can close any of these terminal windows and reattach to the tmux sessions at a later time. The programs running in the tmux sessions continue to run in the background. When I need to switch contexts to work on another project, I do close the windows (or I can leave them open) and attach to other tmux sessions with working state for that other project.
I still have to use the trackpad/mouse some of the time, particularly when working with Chrome and its developer tools. That is simply the nature of having to work with GUI applications, and to be expected.
I3WM/i3-GAPS
What I’ve described so far is a decent balance between a desktop GUI and emphasis on the command line. If you are willing to use Linux, or run Linux in a virtual machine on Mac (or Windows), you can use i3wm or i3-gaps instead of a traditional graphical desktop.
I3 is a tiling window manager as opposed to a graphical user interface. As you launch applications, i3 resizes the existing windows so the new one will fit. Windows do not ordinarily overlap, though there are times when some still need to. The difference between i3 and i3-gaps is that i3 uses every pixel of your screen to display windows, while i3-gaps allows for some visual spacing around/between windows. Both are effectively the same otherwise, though many like the look of i3-gaps better.
Launching apps and navigating between them is all done via the keyboard. The first time you launch i3, you are asked whether you want the alt key or the command (or Windows) key to be the “mod” key. The mod key is what you will press, along with other keys, to command i3 to do your bidding.
When you first log in, you are presented with a blank screen with only the status bar showing. When you press mod+enter (mod+enter means open a terminal window), a full screen terminal window opens. If you press mod+enter again, the screen is split vertically and you’ll have two equal size terminal windows. If you press mod+enter again, you’ll have three equal size terminal windows.
To command i3 to open the next window with a horizontal split, press mod+h. To command i3 to open the next window with a vertical split, press mod+v. As you can see in the screenshot above, I was able to arrange several tiled windows to my liking, some vertically split, some horizontally split.
To navigate between your open windows, you use the mod+hjkl vim style keys. Or you can use the mouse; but we want to avoid that as much as we can. To close the currently focused window, press mod+shift+q.
The mod+number (e.g. 1-9, 0) keys switches you to a separate distinct “desktop” where you can open new windows using the described methods. Mod+shift+number will move the currently focused window to the numbered desktop.
To launch a program, like Google Chrome, press mod+d to run dmenu, which replaces the status bar with an autocomplete “text field” where you can search for and launch any application installed on your system. If you type “goo”, you will see google-chrome displayed in the list on the right of the field. You can just hit enter or cursor over to “google-chrome” to launch it.
You can force a window to float by pressing mod+shift+spacebar, then drag it around like with a traditional GUI desktop (hold down mod key, click the mouse and drag). Pressing mod+shift+spacebar again will move the window back into the tiled mode.
Pressing mod+f will toggle the currently active window to or from full screen display, for distraction free interaction.
You might be tempted to skip using tmux with i3, but I still do. I am able to close windows and reopen them, retaining the state. I can close a window, ssh in from another machine, and resume where I left off. And so on.
Ricing i3
The configuration file for i3 is located in ~/.config/i3/config. You can bind keys to launch your favorite apps with a keystroke, or to more comfortably navigate your layouts. To “rice” your desktop means to customize it for your desired look and operation. As you edit and save it, you can hit mod+shift+r to reload it.
The screenshot above is of the rxvt-unicode (urxvt) console, running ranger. I’ve navigated to the wallpapers folder and “opened” kdi.jpg and you can see a nice preview right in the terminal window itself. The terminal must support this and urxvt is one of the few that does.
There are multiple options for rendering the status bar at the top. I3 simply runs your “status” program (configured in the config file) and renders the output in the status bar area. If the status program returns JSON, it is rendered appropriately. If it is plain text, it is rendered verbatim. You can install font-awesome and have your status program render icons to look nice.
If you want transparency, you will need to install compton, a compositor for X Windows. Then you can configure your various programs, urxvt at least, for some amount of transparency. Again, see the screenshot just above.
You can use the “feh” program to set a “desktop background” image. You can use the “conky” program to render a widget on your desktop:
More Applications
Another way to “rice” your setup is by which applications you use. I mentioned urxvt and ranger already, but there are plenty more.
Long before there were GUI desktops, there were feature rich email clients. Alpine and Mutt are two obvious ones you might consider. Though I use the GUI app, Wavebox.
SImilarly, there are a number of IRC or chat programs designed for the terminal. WeeChat supports multiple back ends, including Slack.
Conclusion
This article was intended to provide you with the reasoning behind keyboard focused development as well as an overview of a complete solution for product or documentation development with minimal interaction using the trackpad or mouse.
If you found this useful but aren’t ready to explore further, the least you can do is to install lesspipe, which will syntax highlight files you view from the command line using less.
Mike Schwartz
Related Posts
-
Efficient DOM and CSS Operations
Eventually, the output of our web applications end up displayed as a part of user…
-
Everything You Need to Know About Refs in React
The React API has managed to remain fairly simple despite its substantial growth. However, new…