~leon_plickat/nfm

2949af3ea56f2b58d01146b8d2f77f759c719f8f — Leon Henrik Plickat 4 months ago d3428e1
Print / after directory name

This required a change in to writeLine(). If the text is shorter than
the available width, the function will now return the difference instead
of filling the line. This make its usage slightly more verbose, but
definitely increases the flexibility.
1 files changed, 43 insertions(+), 27 deletions(-)

M src/UserInterface.zig
M src/UserInterface.zig => src/UserInterface.zig +43 -27
@@ 574,11 574,13 @@ fn drawTitle(self: *Self, writer: anytype, indicator: ?[]const u8) !void {
            try writer.writeByte(' ');
            try escape.moveCursor(writer, 0, prefix_len);
            // TODO input line scrolling
            try writeLine(
            if (try writeLine(
                writer,
                self.width - prefix_len,
                context.mode.user_input.buffer.buffer.items,
            );
            )) |d| {
                try writer.writeByteNTimes(' ', d);
            }

            // The title bar must be the last thing we draw, otherwise the
            // cursor will end up in an unexpected position.


@@ 591,18 593,24 @@ fn drawTitle(self: *Self, writer: anytype, indicator: ?[]const u8) !void {
        .message => {
            try context.mode.message.level.getAttr().dump(writer);
            try writer.writeByte(' ');
            try writeLine(writer, self.width - 1, context.mode.message.message);
            if (try writeLine(writer, self.width - 1, context.mode.message.message)) |d| {
                try writer.writeByteNTimes(' ', d);
            }
        },
        .nav => {
            try setAttr(writer, .{ .bg = .green, .fg = .black });
            try writer.writeByte(' ');
            if (indicator) |indctr| {
                debug.assert(indctr.len < min_width);
                try writeLine(writer, self.width - (indctr.len + 1), context.cwd.name);
                if (try writeLine(writer, self.width - (indctr.len + 1), context.cwd.name)) |d| {
                    try writer.writeByteNTimes(' ', d);
                }
                try setAttr(writer, .{ .bg = .green, .fg = .black, .bold = true });
                try writer.writeAll(indctr);
            } else {
                try writeLine(writer, self.width - 1, context.cwd.name);
                if (try writeLine(writer, self.width - 1, context.cwd.name)) |d| {
                    try writer.writeByteNTimes(' ', d);
                }
            }
        },
    }


@@ 653,8 661,7 @@ fn drawList(self: *Self, writer: anytype) !void {
    context.selection_delta = 0;
}

fn drawFileName(writer: anytype, y: usize, file: DirMap.Dir.File, selected: bool, width: usize) !void {
    // TODO Perhaps draw '/' to the end of directory names.
fn drawFileName(writer: anytype, y: usize, file: DirMap.Dir.File, selected: bool, _width: usize) !void {
    // TODO Draw '->target' after name of links
    try setAttr(writer, switch (file.kind) {
        .Directory => Attr{


@@ 678,16 685,25 @@ fn drawFileName(writer: anytype, y: usize, file: DirMap.Dir.File, selected: bool
            .reverse = selected,
        },
    });
    try escape.moveCursor(writer, y, 0);

    const prefix = if (file.mark) " -> " else " ";
    const width = blk: {
        const width = (if (_width >= 30) _width - 11 else _width) - prefix.len;
        break :blk if (file.kind == .Directory) width - 1 else width;
    };

    try escape.moveCursor(writer, y, 0);
    try writer.writeAll(prefix);
    if (width >= 30) {
        try writeLine(writer, width - (prefix.len + 11), file.name);
    const d = try writeLine(writer, width, file.name);
    if (file.kind == .Directory) {
        try writer.writeByte('/');
    }
    if (d) |e| try writer.writeByteNTimes(' ', e);

    if (_width >= 30) {
        // TODO this is a workaround for writeLine() not recognizing
        //      double-wide characters.
        try escape.moveCursor(writer, y, width - 11);

        //        try escape.moveCursor(writer, y, _width - 11);
        try writer.writeByte(' ');
        try writer.writeByte(if (file.r_user) 'r' else '-');
        try writer.writeByte(if (file.w_user) 'w' else '-');


@@ 699,27 715,27 @@ fn drawFileName(writer: anytype, y: usize, file: DirMap.Dir.File, selected: bool
        try writer.writeByte(if (file.w_other) 'w' else '-');
        try writer.writeByte(if (file.x_other) 'x' else '-');
        try writer.writeByte(' ');
    } else {
        try writeLine(writer, width - prefix.len, file.name);
    }
}

fn writeLine(writer: anytype, width: usize, bytes: []const u8) !void {
/// Use writer to write at most `width` of `bytes`, abbreviating with '…' if
/// necessary. If the amount of written codepoints is less than `width`, returns
/// the difference, null otherwise.
fn writeLine(writer: anytype, width: usize, bytes: []const u8) !?usize {
    var view = unicode.Utf8View.init(bytes) catch {
        // File names with unicode characters not recognized by zigs unicode
        // view are not uncommon, for example if a file name containts "emojis"
        // or got corrupted during a download. Treating those bytes as u8 chars
        // is definitely wrong, but better than crashing or displaying nothing.
        try writeLineNoUnicode(writer, width, bytes);
        return;
        return try writeLineNoUnicode(writer, width, bytes);
    };

    var i: usize = 0;
    var written: usize = 0;
    var it = view.iterator();
    while (it.nextCodepointSlice()) |cp| : (i += 1) {
        if (i == width) {
            return;
        } else if (i == width - 1) {
    while (it.nextCodepointSlice()) |cp| : (written += 1) {
        if (written == width) {
            return null;
        } else if (written == width - 1) {
            // We only have room for one more codepoint. Look ahead to see if we
            // need to draw '…'.
            if (it.nextCodepointSlice()) |_| {


@@ 727,22 743,22 @@ fn writeLine(writer: anytype, width: usize, bytes: []const u8) !void {
            } else {
                try writer.writeAll(cp);
            }
            return;
            return null;
        } else {
            try writeCodePoint(writer, cp);
        }
    }

    if (i < width) try writer.writeByteNTimes(' ', width - i);
    return width - written;
}

fn writeLineNoUnicode(writer: anytype, width: usize, bytes: []const u8) !void {
fn writeLineNoUnicode(writer: anytype, width: usize, bytes: []const u8) !?usize {
    if (bytes.len > width) {
        for (bytes[0 .. width - 1]) |char| try writeAscii(writer, char);
        try writer.writeAll("…");
        return null;
    } else {
        for (bytes) |char| try writeAscii(writer, char);
        try writer.writeByteNTimes(' ', width - (bytes.len));
        return width - bytes.len;
    }
}