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 Editor type

Create src/Editor.zig and let's start adding the struct members. This is an instantiable module, that's why the filename starts with a capital letter. It's not enforced, but it's the Zig convention for types to be capitalized.

At the top, we add comments followed by an exclamation mark: it's the module description. Such special comments may be used for documentation generation.

Editor.zig
//! Type that manages most of the editor functionalities.
//! It draws the main window, the statusline and the message area, and controls
//! the event loop.

At the bottom, we put our usual section with constants:

Editor.zig
///////////////////////////////////////////////////////////////////////////////
//
//                              Constants, variables
//
///////////////////////////////////////////////////////////////////////////////

const Editor = @This();

const std = @import("std");

const t = @import("types.zig");

@This() is a builtin function that returns the type of the struct. It is capitalized like all functions that return types. This constant means that in this file Editor refers to the same type we're defining. Others prefer to name such constants Self. I prefer more descriptive names.

Fields

Back at the top, below the module description, we start adding the type members:

Editor.zig
/// Allocator used by the editor instance
alc: std.mem.Allocator,

We'll use a single allocator for now, the Editor will pass its own to the types that will require it. We call the field simply alc, because it will be passed so often as argument, that I prefer to keep the name short.

Editor.zig
/// The size of the terminal window where the editor runs
screen: t.Screen,

/// Text buffer the user is currently editing
buffer: t.Buffer,

/// Tracks cursor position and part of the buffer that fits the screen
view: t.View,

/// Becomes true when the main loop should stop, causing the editor to quit
should_quit: bool,

We didn't create the Buffer nor the View type yet. should_quit is the variable that we'll use to control the main event loop. When this variable becomes true, the loop is interrupted and the program quits.

Initialization

Now we'll create functions to initialize/deinitialize the editor:

Editor.zig
///////////////////////////////////////////////////////////////////////////////
//
//                              Init/deinit
//
///////////////////////////////////////////////////////////////////////////////

/// Return the initialized editor instance.
pub fn init(allocator: std.mem.Allocator, screen: t.Screen) !Editor {
    return .{
        .alc = allocator,
        .screen = .{
            .rows = screen.rows - 2, // make room for statusline/message area
            .cols = screen.cols,
        },
        .buffer = try t.Buffer.init(allocator),
        .view = .{},
        .should_quit = false,
    };
}

This is a simple init() function that returns a new instance of the Editor. It's not a method because its first argument is not of type Editor. It is invoked in this way:

    var editor = Editor.init(allocator, screen);

The deinit() function, on the other hand, is a proper method, because it is used to deinitialize an instance.

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

Accordingly, it is invoked like this:

    editor.deinit();

Everything that has used an allocator should be deinitialized here. If you forget to deinitialize/deallocate something, while still using the DebugAllocator, you'll be told when exiting the program that your program has leaked memory, and the relative stack trace.

We'll also add a method called startUp(). This function will handle the event loop, and is also called from main().

Editor.zig
/// Start up the editor: open the path in args if valid, start the event loop.
pub fn startUp(e: *Editor, path: ?[]const u8) !void {
    if (path) |name| {
        _ = name;
        // we open the file
    }
    else {
        // we generate the welcome message
    }

    while (e.should_quit == false) {
        // refresh the screen
        // process keypresses
    }
}

It's only a stub, but you can see what it should do.

Before continuing the Editor type, we must define the other ones.