Configuration File

Rex configuration is written in Lua and lives in a single file: init.lua. It binds keys, defines custom actions, declares the other servers you connect to, and more. It can load other Lua files (via require) so you can split your configuration up as you’d like.

Rex doesn’t require any configuration, so start using Rex without it and start a configuration file when there is something you want to change.

File Location

On macOS and Linux, the file is at $XDG_CONFIG_HOME/rex/init.lua when that environment variable is set and ~/.config/rex/init.lua when it isn’t.

On Windows, the file is at %AppData%\rex\init.lua.

Set REX_CONFIG to the path of another file to use that one instead. This environment variable must be set in the environment of the Rex server process, since the server is what reads the file.

Quick Example

Here is a minimal configuration example with one keybinding:

rex.bind("cmd+d", "pane.split", { direction = "right" })

Reload the configuration with the CLI. Connected clients will be notified of the new configuration and it will take effect immediately. Rex does not currently watch for file changes; you must use the CLI.

rex config reload

The configuration file is read by the Rex server, so it lives on the same machine as the server. rex config reload must also be run on that machine; it is refused over a remote connection.

Press Cmd+D and the focused pane splits to the right.

The Lua API reference lists all available Lua APIs.

Validation

rex config check loads the file without changing anything and reports any issues. If there are no issues, it reports the changes it found:

$ rex config check
path: /Users/you/.config/rex/init.lua
exists: yes
bindings: 8
removals: 0
modes: 1
actions: 0

Both commands print the file’s mistakes and exit with status 1 if any of them is an error. Give rex config check a path to check a file other than your own, such as a draft.

Validation Errors

A bad call to a rex function is skipped, and the rest of the file still loads. Here line 2 passes a table where a key belongs:

rex.bind("cmd+d", "pane.split", { direction = "right" })
rex.bind({ "cmd+k" }, "pane.split")
rex.bind("ctrl+b>c", "window.new")
$ rex config check
...
bindings: 2
...
error init.lua:2: rex.bind: expected a string, got table
Error: the configuration has errors

The other two bindings still work.

A mistake Lua itself can’t get past, such as a syntax error, fails the whole load. When that happens on a reload, the server keeps the bindings and actions from the last load that worked.

Some mistakes can’t be seen from the file alone. Whether pane.split is a real action depends on the client, because clients provide those actions. rex keymap asks the running server for the merged result and reports these.

Splitting Configuration

require loads other Lua files stored beside it:

-- ~/.config/rex/init.lua
require("keys")
-- ~/.config/rex/keys.lua
rex.bind("cmd+d", "pane.split", { direction = "right" })
rex.bind("ctrl+b>c", "window.new")

Mistakes are reported with the name of the file they are in, such as keys.lua:2.

Execution Model

The file runs from top to bottom each time it loads.

This load-time execution is for defining bindings, actions, etc. The callbacks for these are deferred until they’re invoked. Rex APIs such as listing sessions or creating a new window do not work at load-time, since API calls are made on behalf of a client and load-time has no client. They’re reported as a warning and the rest of the file still loads.

Example:

rex.action{
  name  = "scratch",
  title = "Open Scratch Window",
  run   = function(ctx, args)
    rex.session.new_window{
      layout = rex.layout.block{
        flavor = "com.superlogical.terminal.shell",
      },
    }
  end,
}

This defines the scratch action, but it doesn’t invoke the callback with rex.session.new_window until the action is actually invoked via the command palette, a keybinding, etc.

To run Lua once from top to bottom against a live session, write a separate script and run it with rex do instead.