Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The screen surface

Now we're finally ready to start drawing on the screen.

We'll use an ArrayList to hold all characters that will be printed on every screen refresh.

We'll add a new field to our Editor type:

Editor.zig
/// String that is printed on the terminal at every screen redraw
surface: t.Chars,

It is initialized in the init() function:

Editor.zig: init()
    return .{
        .alc = allocator,
    // multiply * 10, because each cell could contain escape sequences
    const surface_capacity = screen.rows * screen.cols * 10;
    return .{
        .alc = allocator,
        .surface = try t.Chars.initCapacity(allocator, surface_capacity),

We give our surface an initial capacity, so that it will probably never reallocate. We make enough room for escape sequences: potentially, almost every cell of the screen could contain an escape sequence.

surface must be deinitialized in deinit(), or it will leak:

Editor.zig: deinit()
/// Deinitialize the editor.
pub fn deinit(e: *Editor) void {
    e.buffer.deinit();
    e.surface.deinit(e.alc);

Note that we must pass the allocator as argument when deinitializing an ArrayList.

Appending to the surface

Every time we want to append to the surface, we'd need either:

try e.surface.appendSlice(e.alc, slice);

or

try e.surface.append(e.alc, character);

Let's create a helper function, because we'll append to the surface in lots of places, and we want our code to be more concise and readable.

Editor.zig
/// Append either a slice or a character to the editor surface.
fn toSurface(e: *Editor, value: anytype) !void {
    switch (@typeInfo(@TypeOf(value))) {
        .pointer => try e.surface.appendSlice(e.alc, value),
        else => try e.surface.append(e.alc, value),
    }
}

With this function we just need to do:

try e.toSurface(slice_or_character);

@TypeOf

Builtin function @TypeOf() returns a type, which can only be evaluated at compile time, hence our helper doesn't have any runtime cost, because the operation to perform is decided at compile time. As proof of this, you will get a compile error if you pass something wrong to this function.