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

Highlight for non-printable characters

As a first proof-of-concept for our highlight, we want non-printable characters to be printed with a reversed highlight (black on white), for example we'll turn Ctrl-A into A with reversed colors. If the character is not a Ctrl character, it will be printed as ? with reversed colors.

It won't work for some charcters like Tab or Backspace, though, but for now it will do.

This kind of highlight will work with all filetypes, so we aren't talking about syntax highlighting yet.

We'll need a way to insert non-printable characters, so we define a key (Ctrk-K) which will let us insert characters verbatim, even those that we couldn't type anyway. For example Ctrl-Q would quit, it would not insert it. But while inserting characters verbatim we'll be able to type it.

Process verbatim keypresses

In processKeypress(), we add a variable verbatim in the static struct:

Editor.zig: processKeypress()
    const static = struct {
        var q: u8 = opt.quit_times;
        var verbatim: bool = false;

Just below the static struct definition, before processing keypresses, we check if the variable was set, in this case we reset the variable, insert the character and return. There is a set of characters that we don't insert, because we cannot handle them at this point, they would just break our text.

Editor.zig: processKeypress()
    if (static.verbatim) {
        static.verbatim = false;
        switch (k) {
            // these cause trouble, don't insert them
            .enter,
            .ctrl_h,
            .backspace,
            .ctrl_j,
            .ctrl_k,
            .ctrl_l,
            .ctrl_u,
            .ctrl_z,
            => {
                try e.errorMessage(message.errors.get("nonprint").?, .{ k });
                return;
            },
            else => try e.insertChar(@intFromEnum(k)),
        }
        return;
    }

We'll make Ctrl-K set this variable to true:

Editor.zig: processKeypress() switch
        .ctrl_k => static.verbatim = true,

For the error, we need the nonprint error message:

message.zig: error_messages
    .{ "nonprint", "Can't insert character: {any}" },

Highlight the verbatim characters

This highlight group is filetype-independent, so we just handle it in the drawRows() inner loop:

Editor.zig: drawRows() inner loop
                if (hl[i] != current_color) {
                if (c != '\t' and !asc.isPrint(c)) {
                    // for example, turn Ctrl-A into 'A' with reversed colors
                    current_color = t.Highlight.nonprint;
                    try e.toSurface(t.HlGroup.attr(.nonprint));
                    try e.toSurface(switch (c) {
                        0...26 => '@' + c,
                        else => '?',
                    });
                }
                else if (hl[i] != current_color) {

We also need to add nonprint to the Highlight enum:

types.zig: Highlight enum
    /// Highlight for non-printable characters
    nonprint,

Define the highlight group

Now, if you try to compile, the compiler will say something like:

src/types.zig|162 col 20| error: use of undefined value here causes illegal behavior
||             if (hlg.bold) "1;" else "22;",

That's because we didn't define the highlight group in hlGroups, but the hlAttrs initializer tries to access it. This means that our system is really ok! We can't forget to define groups without the compiler telling us.

So we add the highlight group in the hlGroups labeled block:

types.zig
    hlg[int(.nonprint)] = .{
        .fg = FgColor.white,
        .bg = BgColor.default,
        .reverse = true,
        .bold = false,
        .italic = false,
        .underline = false,
    };

Now it should compile and the following should work:

  • try inserting a non-printable character with Ctrl-K followed by Ctrl-A

  • now try pressing two times Ctrl-K: we decided not insert certain characters and print an error message instead, this should have the .err highlight.