Skip to main content

Configuration

cy is not really a terminal multiplexer. It's actually an API for making terminal multiplexers that happens to come with a decent default configuration. This is because cy was designed to be as configurable as possible:

  1. Features are first built to expand the functionality available in the API.
  2. They then are incorporated into the default configuration where appropriate.

As a result, cy is the best choice for users who enjoy tinkering with the tools they use.

cy uses Janet as its configuration language. Janet is a fun, embeddable Lisp-like language that is easy to learn. If you are new to Janet, I recommend starting out with its documentation and Ian Henry's fantastic Janet for Mortals.

Why Janet?​

This might come as a surprise, but I am far from being a Lisp zealot and have never seriously used emacs. I found Janet several years ago and enjoyed experimenting with it. The language avoids most of the scary levels of abstraction present in some Lisps and strikes a balance between functional programming orthodoxy and practical imperative programming needs.

Configuration files​

On startup, cy will search for and execute the first file containing Janet source code that it finds in the following locations. cy adheres to the XDG base directory specification.

  1. $XDG_CONFIG_HOME/cy/cyrc.janet
  2. $XDG_CONFIG_HOME/cyrc.janet
  3. $XDG_CONFIG_HOME/.cy.janet
  4. $HOME/cy/cyrc.janet
  5. $HOME/cyrc.janet
  6. $HOME/.cy.janet
  7. $HOME/.config/cy/cyrc.janet
  8. $HOME/.config/cyrc.janet
  9. $HOME/.config/.cy.janet
  10. $XDG_CONFIG_DIRS/cy/cyrc.janet (each directory in $XDG_CONFIG_DIRS, defaulting to /etc/xdg)

You can reload your configuration at any time using action/reload-config, which by default is bound to ctrl+a r [?].

Example configuration​

An example configuration that uses functionality from this API is shown below. You may also refer to the default configuration for examples of more advanced API usage.

# Define a new action (which is just a function) with the name toast-pane-path
(key/action
# the name of the function by which it can be referenced in the current
# scope
toast-pane-path
# a (required) docstring for the action
"show the path of the current pane"
# all subsequent statements are executed when this action is invoked (usually
# by a key binding)
#
# (pane/current): gets the ID of the current pane
# (cmd/path): gets the path of the pane with the given ID
# (msg/toast): shows a toast popup in the top-right corner of the screen
(msg/toast :info (cmd/path (pane/current))))

# Bind a key sequence to this function
(key/bind :root ["ctrl+a" "g"] toast-pane-path)

Execution context​

The Janet code executed in cy can be executed in two different contexts:

  • The context of the server: This is the context in which your configuration file is run, because when the cy server starts up, there are no clients.
  • The context of a client: When a client invokes an action or types a keybinding sequence, the executed Janet code can see what client ran the action and respond appropriately.

Because of this, some API functionality can only be used in a keybinding or action. This is because some kinds of state, such as the state that (layout/*) family of functions uses to work, only makes sense in the context of a user.

To run code in the context of a client when it first connects, you can use hooks.

Hooks​

Hooks allow you to run Janet code in response to events in cy. If you define a function with a specific name, cy will call it automatically when the corresponding event occurs.

hook/init​

The hook/init function is called when a new client connects to the server. It runs in the context of that client, so you can use client-specific API functions like cy/id and layout/set.

This is useful for:

  • Setting client-specific parameters
  • Displaying a welcome message
  • Customizing the layout for each client
(defn hook/init []
# Show a welcome message
(msg/toast :info (string "Welcome, client #" (cy/id) "!"))

# Disable animations for this client
(param/set :client :animate false))

If hook/init is not defined, no error occurs; hooks are optional.

Plugins​

cy has a rudimentary plugin system. A plugin is any directory with an init.janet at its root. On startup, cy checks whether one of these directories exists:

  1. $XDG_CONFIG_HOME/cy/plugins/
  2. $HOME/.cy/plugins/
  3. $HOME/.config/cy/plugins/
  4. $XDG_CONFIG_DIRS/cy/plugins/ (each directory in $XDG_CONFIG_DIRS, defaulting to /etc/xdg)

If one does, it executes all plugins it finds in that directory. Plugins are loaded in alphabetical order. Your cy.janet (or .cyrc.janet) runs last.

Error handling​

If an uncaught error is thrown while running Janet code, cy will send all connected users a toast with that error and log the error to the /logs pane, which you can attach to like any other pane.