Table of Contents
- Example shared helpers and rules
- How to use these helpers from a config
- Why use shared files at all?
- examples/lib/common.lua
- common.make_resolve_focus(...)
- Its default behavior covers: focus new windows on map, focus clicked windows, clear focus on empty-space clicks, pick another focusable window when one disappears, and optionally start interactive move/resize when a drag modifier is set.This lets several examples stay short without becoming vague: one line gives a readable baseline focus policy.
- common.resize_bounds(...)
- It lets example configs resize without repeating the math in every file.
- common.draw_background
- common.draw_focus_border
- common.commands
- Readable names like commands.terminal are easier to scan than repeating long shell command strings.
- examples/rules.lua
- Why these shared files matter for learning
- Read next
Example shared helpers and rules
Several example configs rely on two shared files:
examples/lib/common.luaexamples/rules.lua
This page explains what those files do, shows the most important code snippets, and explains how to reuse the same pattern in your own config.
How to use these helpers from a config
A typical example file starts like this:
local common = include("lib/common.lua")
local shared_rules = include("rules.lua")
local commands = common.commands
That gives the config access to:
- reusable focus helpers
- reusable resize math
- reusable draw helpers
- reusable example command strings
- reusable example rules
The point is not just code reuse. The point is keeping the main config readable.
Why use shared files at all?
Without shared files, every example would need to repeat:
- focus helper code
- resize math
- draw helpers
- common command strings
- shared rules
That would make the examples longer and harder to compare.
The shared files make it easier to see what each example is uniquely trying to teach.
examples/lib/common.lua
This file is a shared helper module.
It returns a table named M with helper functions and command strings.
Typical use:
local common = include("lib/common.lua")
Then you can call helpers like:
common.make_resolve_focus(...)common.resize_bounds(...)common.draw_backgroundcommon.draw_focus_bordercommon.commands
common.make_resolve_focus(...)
This helper builds a resolve_focus hook for the examples.
Its structure starts like this:
function M.make_resolve_focus(options)
local drag_modifier = options and options.drag_modifier or nil
return function(ctx)
if ctx.reason == "window_mapped" and ctx.window and not ctx.window.exclude_from_focus then
focus_window(ctx.window)
return
end
if ctx.reason == "pointer_button" and ctx.pressed then
local modifiers = ctx.modifiers or {}
if ctx.window and not ctx.window.exclude_from_focus then
focus_window(ctx.window)
if drag_modifier and modifiers[drag_modifier] and ctx.button == BTN_LEFT then
evil.window.begin_move(ctx.window.id)
return
end
if drag_modifier and modifiers[drag_modifier] and ctx.button == BTN_RIGHT then
evil.window.begin_resize(ctx.window.id, resize_edges_for_pointer(ctx.window, ctx.pointer))
return
end
return
end
evil.window.clear_focus()
return
end
...
end
end
Its default behavior covers: focus new windows on map, focus clicked windows, clear focus on empty-space clicks, pick another focusable window when one disappears, and optionally start interactive move/resize when a drag modifier is set. This lets several examples stay short without becoming vague: one line gives a readable baseline focus policy.
common.resize_bounds(...)
This helper takes:
- the current window snapshot
dx,dy- resize edge flags
and returns a new bounds table.
The key logic is:
function M.resize_bounds(window, dx, dy, edges)
local left = window.x
local top = window.y
local right = window.x + window.w
local bottom = window.y + window.h
left, right = apply_horizontal_resize(left, right, dx, edges)
top, bottom = apply_vertical_resize(top, bottom, dy, edges)
left, right = clamp_resize_width(left, right, edges, 120)
top, bottom = clamp_resize_height(top, bottom, edges, 80)
return {
x = left,
y = top,
w = right - left,
h = bottom - top,
}
end
It lets example configs resize without repeating the math in every file.
common.draw_background
The background helper eventually delegates to a dot-grid renderer:
function M.draw_background(ctx)
return M.draw_dot_grid(ctx)
end
Inside that grid renderer, the shapes are created from viewport facts like this:
local screen_x = math.floor(((world_x - visible.x) * viewport.zoom) + 0.5)
local screen_y = math.floor(((world_y - visible.y) * viewport.zoom) + 0.5)
...
shapes[#shapes + 1] = grid_dot_shape(screen_x, screen_y, size, color)
This is the correct draw-hook model: read the viewport, convert world positions to screen coordinates, return shapes, and never mutate runtime state.
common.draw_focus_border
The shared focus-border helper looks like this:
function M.draw_focus_border(ctx)
local window = M.focused_window(ctx)
if not window then
return {}
end
return {
evil.draw.stroke_rect({
space = "world",
x = window.x,
y = window.y,
w = window.w,
h = window.h,
width = 1,
outer = 4,
color = { 0.18, 0.15, 0.26, 0.95 },
}),
evil.draw.stroke_rect({
space = "world",
x = window.x,
y = window.y,
w = window.w,
h = window.h,
width = 2,
outer = 2,
color = { 0.74, 0.58, 0.98, 0.98 },
}),
}
end
A draw helper stays readable when it does one clear thing: find the focused window and return shapes around it.
common.commands
The shared command table is:
M.commands = {
terminal = "./scripts/example-launch.sh terminal",
launcher = "./scripts/example-launch.sh launcher",
x11_test = "./scripts/example-launch.sh x11-test",
screenshot = "./scripts/example-launch.sh screenshot",
browser = "./scripts/example-launch.sh browser",
file_manager = "./scripts/example-launch.sh file-manager",
}
Readable names like commands.terminal are easier to scan than repeating long shell command strings.
examples/rules.lua
The shared rules file is intentionally tiny:
return {
{ app_id = "foot", floating = true, size = { w = 900, h = 600 } },
{ title_contains = "scratch", exclude_from_focus = true },
}
These show the basic rule format: app_id matches for floating size, and title_contains matches for focus exclusion.
Why these shared files matter for learning
These files are useful to study because they show how to keep config files readable.
A good config does not need to put every line in one file. It can move:
- reusable helpers
- reusable rules
- shared command strings
into helper modules and keep the main config focused on its actual behavior.
Read next
evil wiki
- Home
- Getting started
- Architecture and philosophy
- Configuration guide
- Writing your first config
- Lua basics for evil users
- Lua API guide
- Lua API cheat sheet
- Hooks guide
- Hook payload summary
- Queries, commands, and actions
- Drawing and visuals
- Example configs overview
- Example shared helpers and rules
- IPC protocol
- Testing and debugging
- Contributor validation matrix
- Feature status and limitations
- Feature and support matrix
- Recipes