Lua Events
A Lua script can keep running and react to what
happens in Rex: a terminal changes directory, a program exits, a window
closes, a session is created. A script asks for an event with rex.on.
-- label.lua: label each terminal with the directory it is in
rex.on("terminal.pwd_changed", function(ctx, ev)
local dir = ev.pwd:match("([^/]+)/?$")
rex.session.set_block_label{
block_id = ctx.block_id,
label = dir,
}
end)$ rex do label.lua
listening: 1 handler, session work; Ctrl-C to stopChange directory in any terminal in the session and its label will update.
Handlers
rex.on takes the name of an event and a function to call each time that
event happens:
rex.on(event, function(ctx, ev)
-- ...
end)A script that calls rex.on doesn’t exit when it reaches its last line. rex do keeps it running and calls its handlers as events arrive, one at a
time.
Handlers are registered while the script first runs. Calling rex.on later,
from inside a handler, is an error.
ev holds the event’s fields, and ctx says where it happened:
| Field | Description |
|---|---|
ctx.session_id | The session the event happened in. |
ctx.block_id | The block the event came from, for a block event. |
API calls inside a handler default to the event’s session rather than the one the script was started in, so one handler works across every session it hears.
Event Names
There are three kinds of event name:
| Name | Matches |
|---|---|
block_closed, window_created, … | A session or server event, by its type. |
terminal.pwd_changed, terminal.bell, … | One kind of event from a block. |
block_event | Every event from every block. |
A block event is named for the kind of block and the event, joined by a dot. terminal.bell is short for com.superlogical.terminal.bell. Either spelling
works.
These are common events:
| Event | When | Fields on ev |
|---|---|---|
terminal.pwd_changed | A terminal changes directory. | pwd, a file:// URL |
terminal.title_changed | A terminal’s title changes. | title |
terminal.bell | A terminal rings its bell. | |
terminal.child_exited | A terminal’s program exits. | exit_code |
block_created, block_closed | A block is created or closed. | block_id |
window_created, window_closed | A window is created or closed. | window_id |
session_created, session_destroyed | A session is created or destroyed. |
The events reference has the full list.
Rex doesn’t check an event name against a list, so a misspelled name is not an error. The handler is registered and never called.
Event Scope
By default, a script reacts to events of one session: the one it was run in,
or the one named with -s.
rex do label.lua # the current session
rex do -s build label.lua # the session labeled "build"Pass --all to hear every session, including ones created after the script
starts:
rex do label.lua --allStopping
A script with handlers runs until one of these happens:
- A handler calls
rex.stop(code).rex doexits with that status, or 0 when it is left out. - The time given with
--forruns out, such as--for 10m. - The session it hears is destroyed. A script run with
--allnever stops this way. - You press Ctrl+C.
-- Exit with the status of the first program to fail
rex.on("terminal.child_exited", function(ctx, ev)
if ev.exit_code ~= 0 then rex.stop(ev.exit_code) end
end)Handler Failures
An error in a handler is printed and the script keeps running, so one bad event doesn’t end a long-lived script:
Error: watch.lua:7: attempt to index a nil valuePass --fail-fast to exit with status 1 at the first failure instead.
Remember that a failed API call returns nil and a message rather than
raising an error. Check the second return value where it matters.
Handlers or rex.wait
rex.wait and rex.on both
receive events, and suit different scripts.
- Use
rex.waitwhen the script does one thing after another: start a command, wait for it to exit, then act on the result. - Use
rex.onwhen the script should respond every time something happens, for as long as it runs.
rex.wait and rex.sleep also work inside a handler. No other event is
handled until they return.
Invalid in Configuration Files
rex.on only works in a script run with rex do. The configuration file is never sent events, so a handler
registered there is reported as a warning and never called.
To have a handler running all the time, start the script when you start your session, for example from a terminal you keep open for it.
Example: Notify on Bell
Many programs ring the terminal bell when they finish or need attention. This script turns a bell in any terminal, in any session, into a macOS notification that names the session:
-- notify.lua
local function notify(title, text)
local script = string.format(
"display notification %q with title %q", text, title)
os.execute("osascript -e '" .. script .. "'")
end
rex.on("terminal.bell", function(ctx, ev)
local view = rex.session.view{}
notify("Rex", "Bell in " .. view.label)
end)rex do notify.lua --allrex.session.view is called without a session_id, so it describes the
session the bell rang in. That is how one handler can name whichever session
needs you.
