1
0
Fork 0
5 Queries Commands and Actions
Agent edited this page 2026-05-26 05:18:08 -04:00

Queries, commands, and actions

This page explains the three main ways Lua interacts with runtime state:

  1. queries
  2. direct commands
  3. returned actions

Understanding the difference makes configs much easier to read and design. In the current evil examples, direct commands are the default style. Returned actions are still supported when they make a hook clearer.


1. Queries: read current state

Queries do not change anything. They just ask for information.

Examples:

  • evil.state()
  • evil.window.get(id)
  • evil.window.focused()
  • evil.output.list()
  • evil.output.primary()
  • evil.pointer.position()
  • evil.canvas.viewport()

Example

local focused = evil.window.focused()
if focused then
  print(focused.title)
end

2. Commands: change state directly

Commands apply a change immediately.

Examples:

  • evil.window.focus(id)
  • evil.window.move(id, x, y)
  • evil.window.resize(id, w, h)
  • evil.window.set_bounds(id, x, y, w, h)
  • evil.window.close(id)
  • evil.canvas.pan(dx, dy)
  • evil.canvas.zoom(factor)

Example

evil.on.move_update = function(ctx)
  evil.window.move(ctx.window.id, ctx.window.x + ctx.dx, ctx.window.y + ctx.dy)
end

Return values

Commands usually return:

  • true on success
  • false if the target is invalid or the operation is rejected

3. Actions: describe what should happen

Instead of mutating state directly, a hook can return an action table.

Example:

return {
  kind = "move_window",
  id = ctx.window.id,
  x = ctx.window.x + ctx.dx,
  y = ctx.window.y + ctx.dy,
}

Or several actions:

return {
  actions = {
    { kind = "focus_window", id = ctx.window.id },
    { kind = "begin_move", id = ctx.window.id },
  }
}

This is still useful when a hook wants to describe several related operations together.


For a complete list of queries, commands, and returned actions, see the Lua API cheat sheet.

Which style should you use?

Prefer queries when:

  • you only need to inspect state
  • you are deciding what to do next

Prefer commands when:

  • you want simple direct behavior
  • the hook is tiny
  • you want a quick prototype
  • you want to match the default style used by the current examples

Prefer returned actions when:

  • the hook is clearer as a description than as a sequence of commands
  • you want to return several related operations together

Examples side by side

Direct command style

evil.on.move_update = function(ctx)
  evil.window.move(ctx.window.id, ctx.window.x + ctx.dx, ctx.window.y + ctx.dy)
end

Returned action style

evil.on.move_update = function(ctx)
  return {
    kind = "move_window",
    id = ctx.window.id,
    x = ctx.window.x + ctx.dx,
    y = ctx.window.y + ctx.dy,
  }
end

Both are valid. The second is still useful in more structured configs, but the current examples prefer the first style by default.


A note about spawn

There are two related ways to launch things:

Bindings

evil.bind("Super+Return", "spawn", { command = "foot" })

Imperative command from a hook

evil.spawn("foot")

That is now the default example style when a key hook wants full ownership of launch behavior.


Common mistakes

Returning the wrong hook shape

Good:

return nil
return { kind = "focus_window", id = 10 }
return { actions = { ... } }

Bad:

return { 1, 2, 3 }

Confusing queries and commands

  • evil.window.get(id) reads state
  • evil.window.move(id, x, y) changes state

Using a command when the hook wants a returned action style

This is not always wrong, but if a hook becomes hard to follow, returning actions can make it clearer.