Skip to content

wezterm

Darrion Burgess edited this page Nov 20, 2023 · 1 revision

WezTerm is the next-generation terminal emulator made by Wez Furlong himself, one of the core maintainers of PHP and other great software projects

  • more than that it feels like the terminal emulator that is designed to go hand-in-hand with Neovim as our primary editor

This is obvious from the very start with the fact that our configuration is created using a standard lua file and we can find evidence of a Neovim-like mindset everywhere I look.

  • from the fact that many vim keybindings are already set
  • as well as the fact that we have a vim-like understanding of panes, windows, and tabs which roughly translate to the buffers, windows, and tabs mentality of (neo)vim

Config Builder

First, we need to import and define the config which we will use to set all of our settings

local wezterm = require("wezterm")

local config = {}

if wezterm.config_builder then
  config = wezterm.config_builder()
end

local act = wezterm.action
  1. First, we just need to import the wezterm library which is available any time we are within wezterm itself
  2. Then we start an empty config table, and we will proceed to add to this table throughout our work in the future sections
  3. Next, if we have the config builder available (more just about versioning since this is a new feature) we can turn the wezterm config variable into a builder object
    1. These builder objects make it easier to see good error messages and make it easier to configure the terminal in general
  4. Finally we will sometimes use the action module of the wezterm module so we are going to make this specific module a variable so we can easily refer to it going forward

General Settings

First, we set things that are always on for wezterm

config.color_scheme = "nord"
config.font = wezterm.font("JetBrains Mono")
config.default_prog = { "C:\\Program Files\\PowerShell\\7\\pwsh.exe", "-ExecutionPolicy", "RemoteSigned" }
  1. First, we set the colorscheme out of the literally hundered colorschemes available
  2. Next, we set the font to Monoid NF which is a mono nerd font that I usually download manually from the normal nerdfont page
  3. finally, since we always interact through wezterm through windows for now we are going to be sure to set the shell to powershell 7
    1. this prevents it from doing the default of setting it to the cmd shell

Telling What Programs are Running

We will need the ability to check what program is running in the foreground for later.

For now, here is the equivalent the unix basename() command this was lifted from the knowledge documentation of smart-splits.nvim

local function basename(s)
  return string.gsub(s, '(.*[/\\])(.*)', '%2')
end
  • here we are taking in a string and removing the leading /, and \\ characters, as well as the text between them so we just get to the final piece of text
    • This is assumed to be the process running in the current pane which will be used later

Telling if we are in Neovim or Vim

Now that we have the process name, we want to check wether or not we are in vim or neovim. This is relevant because for a few of the hotkeys we may opt to send the key combination to (neo)vim rather than just doing the wezterm hotkey itself

local function is_vim(pane)
  local process_name = basename(pane:get_foreground_process_name())
  return process_name == 'nvim.exe' or process_name == 'vim.exe'
end
  • This is a simple function that applies the basename function defined about to the foreground process name, and will ask if we are in vim or neovim
    • We are using this for a check later on as we try to define this shared behavior with smart-splits.nvim

Keymaps

The traditional keymap layout is already okay but we want to expand this

The mechanic that wezterm introduces is key tables. These are similar to vim modes in that we can define keymaps that are only applicable for certain contexts

  • We can then set these contexts and do the hotkeys we have defined specifically for these context panes

Defining the Leader Key

Like Neovim, we can have a leader key(s) which will serve as a sort of gateway to the rest of the hotkeys we will be defining later on

config.leader = { key = "Space", mods = "CTRL|SHIFT" }
  • while we want to implement the leader concept, we cant do the same thing as vim because it isn't actually a modal editor, space is still an input character most of the time
    • therefore, we need to use some modifier keys on top to ensure we can activite it no matter what application we are using (including neovim!)
  • Now we can simply say the mod is "LEADER" on top of our keys and it will know to put the hotkey behind our newly defined leader key for wezterm

Bringing it All Together

The part is to actually set both the keymaps, and the key tables themselves within the wezterm config builder

local keymaps = {}

local direction_keys = {
  Left = 'h',
  Down = 'j',
  Up = 'k',
  Right = 'l',
  -- reverse lookup
  h = 'Left',
  j = 'Down',
  k = 'Up',
  l = 'Right',
}
  1. First, we set the main keymaps that will either do an action or enter one of our keymap modes
  2. Next, we set the keymaps that are used during our various modes in wezterm
  3. Finally we define some direction_keys for movement with a vim-like mindset to use later on

Resizing and Selecting Panes

Like (neo)vim, wezterm understands things through panes, windows, and tabs, similar to vim's buffers, windows, and tabs.

More than that, thanks to smart-splits.nvim, we can actually use the same hotkeys we define in neovim to manage the splits of wezterm, giving us a crazy good workflow for finally opening, moving, selecting, and closing panes like a normal neovim window.

We define the function to do both resizing and movement of panes below:

local function split_nav(resize_or_move, key)
  return {
    key = key,
    mods = resize_or_move == 'resize' and 'META' or 'CTRL',
    action = wezterm.action_callback(function(win, pane)
      if is_vim(pane) then
        -- pass the keys through to vim/nvim
        win:perform_action({
          SendKey = { key = key, mods = resize_or_move == 'resize' and 'META' or 'CTRL' },
        }, pane)
      else
        if resize_or_move == 'resize' then
          win:perform_action({ AdjustPaneSize = { direction_keys[key], 3 } }, pane)
        else
          win:perform_action({ ActivatePaneDirection = direction_keys[key] }, pane)
        end
      end
    end),
  }
end
  • This returns a table that defines a keymap given 2 paramaters, the key, and the meta key used
  • The real magic is that we use that is_vim() function to check if we are in vim at the same time and instead pass this hotkey to vim if that is what we are looking to do
    • this is awesome because we can seamlessly use the same hotkey between neovim and wezterm, we don't have to define and memorize a different set of hotkeys for wezterm stuff as neovim stuff
  • If we arent in vim though, we actually run the relevant wezterm function
    • AdjustPaneSize for resizing
    • and ActivatePaneDirection for changing pane focus

Setting Resizing Keymaps

With the groundwork laid, we should just be able to insert the relevant hotkey into our larger list of keymaps using the standard insert method and falling the function for the insertion

table.insert(keymaps,split_nav('resize', 'h'))
table.insert(keymaps,split_nav('resize', 'j'))
table.insert(keymaps,split_nav('resize', 'k'))
table.insert(keymaps,split_nav('resize', 'l'))

Setting Change Focus Keymaps

Same as before, just inserting the appropriate keymaps for the function we are going for at this point

table.insert(keymaps,split_nav('move', 'h'))
table.insert(keymaps,split_nav('move', 'j'))
table.insert(keymaps,split_nav('move', 'k'))
table.insert(keymaps,split_nav('move', 'l'))

Activating Command Pallet

Everyone is in love with the command pallet idea after VSCode popularized it so it goes with the wezterm emulator so that we can bring up a quick menu to quickly do several commands including splitting, making new tabs, and other requirements of working through these pieces

We can set the command pallet hotkey with the below snippet:

table.insert(keymaps,{
  key = 'p',
  mods = 'CTRL|ALT',
  action = act.ActivateCommandPalette,
})

Quick Select Mode

Not to be confused with quick search mode, quick select mode can be thought of as a more precise version of quick search. Whereas one is mostly just a case of searching for instances of a certain string based on the users' input, quick select is specficially tailored towards quickly grabbing pre-defined text patterns and giving users the ability to do more with these selections than merely putting them in their clipboard

The following snippet will bring up the quick select mode without any arguments

table.insert(keymaps, { key = 's', mods = 'LEADER', action = act.QuickSelect })

Closing Panes

Wezterm makes it easy to close windows by default, but gives not hotkeys for closing panes, so to remedy that we just add the following keymap for that specific use case:

table.insert(keymaps, {
  key = "c",
  mods = "LEADER",
  action = act.CloseCurrentPane({
    confirm = true,
  }),
})

Open New Window Using Launcher

Wezterm has a launcher that can be used similar to windows terminal where we can search for the different kinds of shells and machines to connect to and open them as a new window within the current instance of wezterm

Here is a simple leader snippet to pull up the launcher in a new window:

table.insert(keymaps, {
  key = "l",
  mods = "LEADER",
  action = act.ShowLauncher,
})

Unbind the full screen keybinding

Next we want to unbind the default keybinding to put WezTerm into Full Screen mode because it is conflicting with the default binding in Neorg for continuing a list item

table.insert(keymaps, {
  key = "Enter",
  mods = "ALT",
  action = act.DisableDefaultAssignment,
})

table.insert(keymaps, {key = "F11", action = act.ToggleFullScreen})

Returning the Config

With all that done, we are ready to return our config to WezTerm so it can actually use the configuration settings we have defined

config.keys = keymaps
return config
  • remember, nothing is real until this config is returned at the end!
    • commenting this line will return us to a normal default config for testing purposes