Lua Scripts
rex do runs a Lua script against a Rex server.
A script is extremely powerful. It can do anything a full-fledged client can: inspect servers, create sessions, create windows, start terminals, read a terminal screen, react to events, and more.
This script opens a new window running htop:
-- htop.lua
rex.session.new_window{
layout = rex.layout.block{
flavor = "com.superlogical.terminal.shell",
options = { command = { "htop" } },
},
}rex do htop.luaA script uses the same rex API as the configuration file.
Unlike the configuration file, a script runs once from top to bottom
and exits (unless there are active event handlers running).
Running a Script
rex do takes a script in three ways:
rex do ./layout.lua # a file
rex do -e 'return rex.call("session.list")' # inline with -e
rex do - < layout.lua # stdinThe argument is treated as a file when it ends in .lua or contains a slash.
Anything else is the name of an action to run.
A script run inside a Rex terminal acts on that terminal’s session and block.
Use -s and -b to target a different one:
rex do -s build ./layout.luaScripts have Lua’s full standard library, including io and os, and require loads other Lua files.
Scripts are executed locally where the rex CLI is, so security and program
access is up to you.
Arguments
Arguments are passed as key=value pairs after the script and read from the rex.args table:
-- greet.lua
return "hello " .. (rex.args.name or "world")$ rex do greet.lua name=rex
"hello rex"A value’s type comes from how it is written. n=2 is a number, on=true is a
boolean, and name=x is a string. To pass a string that looks like something
else, or a nested value, pass all the arguments as JSON with --args:
rex do greet.lua --args '{"name": "true"}'
rex do greet.lua --args @args.jsonResults and Errors
The value a script returns is printed as JSON. Tables become objects or
arrays. Add --json for indented output. A script that returns nothing
prints nothing.
$ rex do -e 'return { name = "rex", tabs = { 1, 2 } }'
{"name":"rex","tabs":[1,2]}A script that raises an error prints the message and exits with status 1:
$ rex do -e 'error("no tests found", 0)'
Error: no tests foundTo exit with a status of your choosing, call rex.stop with it. The script
ends immediately:
rex.stop(3)print writes to the terminal as usual, and rex.log writes a log line.
Calling Rex
A script starts with a few values describing where it was run:
| Value | Description |
|---|---|
rex.session_id | The session the script is running against. |
rex.block_id | The block the script is running against, when there is one. |
rex.args | The script’s arguments. |
Rex’s API is grouped under rex.session. Each call takes one table of
arguments and returns one table:
rex.session.set_block_label{
block_id = rex.block_id,
label = "tests",
}Calls fill in the script’s session, so most calls don’t need a session_id.
A call that acts on one block, such as set_block_label, still needs its block_id.
A failed call doesn’t stop the script. It returns nil and an error message,
so check the second value when the result matters:
local w, err = rex.session.new_window{ layout = layout }
if err ~= nil then error(err, 0) endThree more ways to call Rex:
rex.call(method, args)calls any server method by name, including those outsiderex.session, such asrex.call("session.list").rex.block.call(creator, method, args)calls a method on a block, such as reading a terminal’s screen.rex.invoke(name, args)runs an action, either one the script defines or one from your configuration file.rex.client.queue(name, args)asks the app to perform one of its own.
The Lua API reference has the full list.
Layouts
Calls that create blocks take a layout. Build one with rex.layout:
local shell = "com.superlogical.terminal.shell"
rex.session.new_window{
layout = rex.layout.horizontal(0.5,
rex.layout.block{ flavor = shell },
rex.layout.block{
flavor = shell,
options = { command = { "htop" } },
}),
}rex.layout.block is one block. rex.layout.horizontal and rex.layout.vertical split the space between two layouts, with the first
argument giving the first one’s share. They can be nested.
Sleeping and Waiting
A script often needs to start something and then act on how it ended.
rex.sleep(seconds) pauses the script:
rex.sleep(0.5)rex.wait(event, filter, timeout) pauses until an event arrives:
local ev = rex.wait(
"terminal.child_exited", { block_id = id }, 600)eventis the name of the event to wait for.filteris optional. A table matches an event whose fields equal the table’s. A function is called with the event and matches when it returnstrue.timeoutis optional, in seconds. Without it,rex.waitwaits forever.
rex.wait returns the event as a table. If the timeout passes first, it
returns nil and "timeout".
The events reference lists the events and their fields.
Example: Run Tests in a Split
This script runs a command in a new split beside the current terminal. If the command fails, it zooms the split so the failure is front and center.
-- tests.lua
local cmd = rex.args.cmd or "make test"
local split, err = rex.session.new_split{
direction = "horizontal",
side = "after",
focus = false,
layout = rex.layout.block{
flavor = "com.superlogical.terminal.shell",
options = {
command = { "sh", "-c", cmd },
exit = { on_completion = false }, -- keep the output
},
},
}
if err ~= nil then error(err, 0) end
local block_id = split.block_ids[1]
-- Wait up to ten minutes for the command to exit
local ev = rex.wait(
"terminal.child_exited", { block_id = block_id }, 600)
if ev == nil then error("timed out waiting for: " .. cmd, 0) end
if ev.exit_code ~= 0 then
rex.session.zoom_block{ block_id = block_id, zoom = true }
end
return { block_id = block_id, exit_code = ev.exit_code }$ rex do tests.lua cmd="go test ./..."
{"block_id":"block:01a10deb-c752-789f-9add-5dfd3a5b3214","exit_code":1}