const std = @import("std"); const terminal = @import("../terminal.zig"); const isTaggedUnion = @import("../event.zig").isTaggedUnion; const Error = @import("../event.zig").Error; const Key = terminal.Key; const Style = terminal.Style; const log = std.log.scoped(.widget_rawtext); pub fn Widget(comptime Event: type) type { if (!isTaggedUnion(Event)) { @compileError("Provided user event `Event` for `Layout(comptime Event: type)` is not of type `union(enum)`."); } const Contents = std.ArrayList(u8); return struct { contents: Contents = undefined, line_index: std.ArrayList(usize) = undefined, line: usize = 0, size: terminal.Size = undefined, pub fn init(allocator: std.mem.Allocator, file: std.fs.File) @This() { var contents = Contents.init(allocator); var line_index = std.ArrayList(usize).init(allocator); file.reader().readAllArrayList(&contents, std.math.maxInt(usize)) catch {}; line_index.append(0) catch {}; for (contents.items, 0..) |item, i| { if (item == '\n') { line_index.append(i + 1) catch {}; } } return .{ .contents = contents, .line_index = line_index, }; } pub fn deinit(this: *@This()) void { this.contents.deinit(); this.line_index.deinit(); this.* = undefined; } pub fn handle(this: *@This(), event: Event) ?Event { switch (event) { // store the received size .resize => |size| { this.size = size; if (this.line > this.line_index.items.len -| 1 -| size.rows) { this.line = this.line_index.items.len -| 1 -| size.rows; } }, .key => |key| { if (key.matches(.{ .cp = 'g' })) { // top this.line = 0; } if (key.matches(.{ .cp = 'G' })) { // bottom this.line = this.line_index.items.len -| 1 -| this.size.rows; } if (key.matches(.{ .cp = 'j' })) { // down if (this.line < this.line_index.items.len -| 1 -| this.size.rows) { this.line +|= 1; } } if (key.matches(.{ .cp = 'k' })) { // up this.line -|= 1; } }, else => {}, } return null; } pub fn content(this: *@This()) ![]u8 { if (this.size.rows >= this.line_index.items.len) { return this.contents.items; } else { // more rows than we can display const i = this.line_index.items[this.line]; const e = this.size.rows + this.line + 1; if (e >= this.line_index.items.len) { return this.contents.items[i..]; } // last line should not end with the last character (likely a newline character) // FIX: what about files which do not end with a newline? const x = this.line_index.items[e] - 1; return this.contents.items[i..x]; } } }; }