Get the Most Out of Your Terminal: A Comprehensive Guide to WezTerm Configuration

Written by maksimmuravev | Published 2023/02/22
Tech Story Tags: terminal | wezterm | vim | lua | dotfiles | alacritty | kitty | foot

TLDRAlacritty, Kitty, Foot, and WezTerm are all powerful alternatives to classic terminals. They can be faster at displaying and refreshing large amounts of information. configuring Wez term can be challenging due to the lack of a Getting Started guide and limited examples. I'll walk you through my configuration, explaining each line in detail.via the TL;DR App

GPU terminals are all the rage these days. The competition between players like Alacritty, Kitty, Foot, Warp, and WezTerm is heating up, and there's still no clear winner. Although they don't have all the fancy features that classic terminals like iTerm2 or Terminator have, such as good support for bitmap fonts and ligatures, they can be faster at displaying and refreshing large amounts of information. And even though the average person (not a Counter Strike champion with a 240Hz monitor) is slow at processing all that information, the ability to optimize terminal performance can be a fun way to show off your nerdy side đŸ€“.

I chose to change from iTerm2 to one of the powered alternatives... The question now is, which one should I choose? But first, I'll give an ultra-brief rundown on each one.

Alacritty 💹

In my opinion, it's the slowest one, and its stability is questionable. It has a minimalistic style, with a simple UI that doesn't have tabs or panes and lacks font ligatures. Some people have reported that it crashes frequently. Also, the author is well-known for their meticulous attention to detail, which is why new features are added slowly. Despite its shortcomings, it still has 2.5 times more Github stars than our next option.

Kitty đŸ±

The Terminal offers more customization options and boasts a latency of 35ms, with occasional spikes of 7ms, while Alacritty has a latency of 50ms. The Terminal has better font rendering and supports ligatures, although they may need to be set up. Unfortunately, Kitty is not available on Windows.

Foot đŸŠ¶

A lesser-known option in the area. Even though it doesn't have GPU support, I still wanted to mention it because it's known for its low resource usage. In my testing, it only used 13-19 MB of RAM. It has fast performance thanks to its clever design that minimizes input latency even without GPU rendering. So, the terminal is both high-performing and resource-efficient. The author thoroughly explains the performance trade-offs and compares them to Alacritty.

Warp 🌀

A truly innovative and reinvented version of classic terminals that operates like a code editor, featuring AI command search powered by GPT-3. With a new stdin/stdout interaction workflow and many other exciting features, it's a game-changer for some hipster-like individuals (if you don’t afraid of telemetry).

WezTerm đŸ€ 

My favorite one. Although the cowboy emoji might suggest a connection to the Western genre, it has nothing to do with the terminal itself. The name Wez is simply the author's name.

If you're looking for a terminal that's the same across Windows, Mac, and Linux, then WezTerm is the one for you. Built using Rust, just like Alacritty, so boasts high performance. It's loaded with many well-documented features and easily customizable using Lua configs, making it a favorite among Vim users. Despite being a relatively new release, its documentation is already top-notch, better than any other terminal.

Base Configuration

That being said, configuring WezTerm can be challenging due to the lack of a Getting Started guide and limited examples. When you first install WezTerm, your ~/.wezterm.lua file will be blank, leaving you in the dark about all the fantastic features the terminal offers. But don't worry! That's where this article comes in. I'll walk you through my configuration, explaining each line in detail and providing a complete example you can use or modify to fit your specific needs.

Let's start by defining some global variables that we'll use later.

The wezterm module provides access to WezTerm configuration and control in your config file. It's a good practice to put this line at the top of your config file.

local wezterm = require 'wezterm'

Now, let's add two more variables. The wezterm.mux module manages the multiplexer layer, which is responsible for everything related to panes, tabs, windows, and workspaces, but not the terminal itself. And the wezterm.action module helps us bind keys to actions. Technically, action is a specific enum constructor, while mux is a separate Lua module, but for now, it doesn't matter.

local mux = wezterm.mux
local act = wezterm.action

Let's go a bit further.

The wezterm.on function triggers a callback after a specific event occurs. As you can see, the gui-startup event invokes function(cmd). Simply saying, the function runs immediately after the terminal starts. It's pretty straightforward.

Next, we create a new window using mux.spawn_window, which returns objects associated with that window. Although we may not utilize these returned values, it is necessary to capture them anyway. That's just how it works. If you need a more precise understanding, you can find definitions of available functions on the search page of WezTerm's website.

Also, I like to maximize the window right away.

wezterm.on('gui-startup', function()
 local tab, pane, window = mux.spawn_window({})
 window:gui_window():maximize()
end)

The next substantial final part begins with a return statement.

return {
 default_prog = { '/usr/local/bin/fish', '-l' }
 window_decorations = "RESIZE"
 ...

}

Yes, in the WezTerm structure, we need return the result of our configuration so that the rest of the configuration will be enclosed in return's brackets.

With default_prog, we set our favorite shell. Mine is fish. The flag -l means running as if it started as a login shell. By setting window_decorations to RESIZE, we achieve a minimalist design by disabling the title bar and enabling the resizable borders. When we open WezTerm, we'll get a maximized window without a title bar, just the way we like it.

Okay, got it. Let's have some fun with WezTerm's panes. You can change the background colors, but to avoid any health issues due to a lack of design skills, the only options available for adjustment are saturation and brightness. This is done by design in WezTerm, so you can easily see which pane you're currently on when you switch between them.

inactive_pane_hsb = {
 saturation = 0.8
 brightness = 0.7
}

Let's check out the desired outcome.

The actual style will depend on the color scheme, so we're going to the next chunk of the configuration, called theme. Hardcore is a good theme, but you can find 746 others to your taste on the official website. Don't need to install something. Just put the theme's name under color_scheme.

color_scheme = 'Hardcore'
font = wezterm.font('JetBrainsMono Nerd Font')
font_size = 14
line_height = 1.2

The font is a big deal for hackers. I like JetBrainsMono Nerd 'cause it's top-notch and free. I might like Berkeley Mono even more, but it's too pricey.

The size I set to 14 depends on your monitor and personal preference. For me, the font is ideal when it's too small but not relatively small enough. The height follows the same rule, I think. But it's better to leave the height at 1 because the font designer won't be happy if you change it. The font was developed with its original height, and if you mess with it, you might mess up the thing creator intended.

Moving next.

use_dead_keys = false
scrollback_lines = 5000

You may not know about dead keys. If so, let me introduce you to Wikipedia. These keys can cause some problems in Vim editors and specific shell hotkeys. Secondly, the scrollback lines refer to the number of lines you want to keep in view per tab. I don't remember when I need to keep more than 5000 lines.

Next, let's tweak the font size to avoid issues when using tiling window managers like Yabai (which I use). As for the second option, I'm not sure if I need to add something.

adjust_window_size_when_changing_font_size = false
hide_tab_bar_if_only_one_tab = true

Moving on, let's take control of the appearance of the tab bar. A unique font for tabs can break your eyes from the main font and make your setup look more professional and (maybe) designer-like.

window_frame = {
 font = wezterm.font { family = 'Noto Sans', weight = 'Regular' },
}

Hotkeys Configuration

Now, let's dive into customizing our hotkeys. The command where our key customization start. By doing the following step, we're turning off all the default key assignments, so we'll have to set each binding explicitly.

disable_default_key_bindings = true

The leader key is a combination of keys that allows users to quickly access various features and commands in a software program using keyboard shortcuts. In VSCode, the default leader key is usually set to Ctrl on Windows or Linux and Cmd on Mac. However, this can be customized to suit individual preferences. The way it works is that you first press the leader key and then press another key to trigger a specific function. This differs from hotkeys, which require you to press multiple keys simultaneously.

leader = { key = 'b', mods = 'CMD', timeout_milliseconds = 2000 }

Although I set a leader key, I rarely use it because it makes you slower. Classical hotkeys work faster for me, so most of the configuration I will share later is about them.

The following chunk is vast. Most of the values are taken from defaults. I’ll say only about the keys I changed.

keys = {
  { key = 'l', mods = 'CMD|SHIFT', action = act.ActivateTabRelative(1) }
  { key = 'h', mods = 'CMD|SHIFT', action = act.ActivateTabRelative(-1) }
  { key = 'j', mods = 'CMD', action = act.ActivatePaneDirection 'Down', }
  { key = 'k', mods = 'CMD', action = act.ActivatePaneDirection 'Up', }
  { key = 'Enter', mods = 'CMD', action = act.ActivateCopyMode }
  { key = 'R', mods = 'SHIFT|CTRL', action = act.ReloadConfiguration }
  { key = '+', mods = 'CTRL', action = act.IncreaseFontSize }
  { key = '-', mods = 'CTRL', action = act.DecreaseFontSize }
  { key = '0', mods = 'CTRL', action = act.ResetFontSize }
  { key = 'C', mods = 'SHIFT|CTRL', action = act.CopyTo 'Clipboard' }
  { key = 'N', mods = 'SHIFT|CTRL', action = act.SpawnWindow }
  { key = 'U', mods = 'SHIFT|CTRL', action = act.CharSelect{ copy_on_select = true, copy_to =  'ClipboardAndPrimarySelection' } }
  { key = 'v', mods = 'CMD', action = act.PasteFrom 'Clipboard' }
  { key = 'PageUp', mods = 'CTRL', action = act.ActivateTabRelative(-1) }
  { key = 'PageDown', mods = 'CTRL', action = act.ActivateTabRelative(1) }
  { key = 'LeftArrow', mods = 'SHIFT|CTRL', action = act.ActivatePaneDirection 'Left' }
  { key = 'RightArrow', mods = 'SHIFT|CTRL', action = act.ActivatePaneDirection 'Right' }
  { key = 'UpArrow', mods = 'SHIFT|CTRL', action = act.ActivatePaneDirection 'Up' }
  { key = 'DownArrow', mods = 'SHIFT|CTRL', action = act.ActivatePaneDirection 'Down' }
  { key = 'f', mods = 'CMD', action = act.SplitVertical { domain = 'CurrentPaneDomain' }, }
  { key = 'd', mods = 'CMD', action = act.SplitHorizontal { domain = 'CurrentPaneDomain' }, }
  { key = 'h', mods = 'CMD', action = act.ActivatePaneDirection 'Left', }
  { key = 'l', mods = 'CMD', action = act.ActivatePaneDirection 'Right', }
  { key = 't', mods = 'CMD', action = act.SpawnTab 'CurrentPaneDomain' }
  { key = 'w', mods = 'CMD', action = act.CloseCurrentTab{ confirm = false } }
  { key = 'x', mods = 'CMD', action = act.CloseCurrentPane{ confirm = false } }
  { key = 'b', mods = 'LEADER|CTRL', action = act.SendString '\x02', }
  { key = 'Enter', mods = 'LEADER', action = act.ActivateCopyMode, }
  { key = 'p', mods = 'LEADER', action = act.PastePrimarySelection, }
  { key = 'k', mods = 'CTRL|ALT', action = act.Multiple
    {
      act.ClearScrollback 'ScrollbackAndViewport'
      act.SendKey { key = 'L', mods = 'CTRL' }
    }
  }
  { key = 'r', mods = 'LEADER', action = act.ActivateKeyTable { name = 'resize_pane', one_shot = false, }, }
}

Switch Tabs

Switching tabs in browsers has always been done with the Ctrl + Tab and Ctrl + Shift + Tab hotkeys. That was until I learned Neovim, and I discovered a better way. Instead of using the traditional method, you can press Ctrl + Shift + H to move to the right tab and Ctrl + Shift + L to the left tab. This way of switching tabs is faster because it involves using both hands, making it easier to switch between tabs like a pro. It's a plus if you're a Neovim user and need to switch between tabs in both Neovim and WezTerm while working with your shell. And it becomes even better if you configure your browser (I use Chrome) with the same hotkeys, allowing you to switch tabs smoothly without moving your fingers much on the keyboard.


Spawn Tabs

The tried and true method of using Ctrl + Tab to open a new tab and Ctrl + W to close it works seamlessly with browsers. Your muscle memory will thank you for using this familiar technique.

Split Panes

Quite easy. Similar to iTerm2, but probably more accessible because you need always two fingers. Just Cmd + F to split the pane vertically, and Cmd + D to split horizontally. You can close the pane by Ctrl + X, however shell Ctrl + D work as well.

Copying Text

You can use the Enter key if you're lazy, but WezTerm is just as comfortable with a mouse. In that case, double-click (or highlight) the text, and it'll be copied right away without having to press Ctrl + C or anything similar.


That's probably it! Just a heads up, you can set up keys for different sections like copy_mode, search_mode, and more. I don't get into details since I mostly use the defaults. I hope this guide helped you take your WezTerm terminal configuration to the next level.


Written by maksimmuravev | DevOps Engineer.
Published by HackerNoon on 2023/02/22