diff options
Diffstat (limited to 'main.zig')
| -rw-r--r-- | main.zig | 265 |
1 files changed, 240 insertions, 25 deletions
@@ -41,7 +41,11 @@ pub const Command = struct { }; const Loop = vaxis.Loop(@TypeOf(Command.value)); -const MAX_FILES: usize = 0x200; +const MAX_LINES: usize = 0x1000; +const MAX_LINE_LEN: usize = 0x200; +const MAX_HISTORY: usize = 0x100; +const FILE_PANE: usize = 0; +const TTY_PANE: usize = 1; fn ticker(io: std.Io, loop: *Loop) void { while (true) { @@ -79,12 +83,104 @@ fn readPty(io: std.Io, gpa: std.mem.Allocator, pty: std.Io.File, loop: *Loop) !v try loop.postEvent(.quit); } +fn putLine(lines: *[MAX_LINES][MAX_LINE_LEN]u8, idx: usize, text: []const u8) void { + const len = @min(text.len, MAX_LINE_LEN - 1); + @memcpy(lines[idx][0..len], text[0..len]); + lines[idx][len] = 0; +} + +fn putString(buf: *[MAX_LINE_LEN]u8, text: []const u8) usize { + const len = @min(text.len, MAX_LINE_LEN - 1); + @memcpy(buf[0..len], text[0..len]); + buf[len] = 0; + return len; +} + +fn pathFrom(base: []const u8, name: []const u8, buf: *[MAX_LINE_LEN]u8) []const u8 { + if (std.fs.path.isAbsolute(name)) { + const len = putString(buf, name); + return buf[0..len]; + } + + return std.fmt.bufPrint(buf, "{s}/{s}", .{ base, name }) catch blk: { + const len = putString(buf, name); + break :blk buf[0..len]; + }; +} + +fn splitLines(lines: *[MAX_LINES][MAX_LINE_LEN]u8, text: []const u8) usize { + var iter = std.mem.splitScalar(u8, text, '\n'); + var i: usize = 0; + while (iter.next()) |line| : (i += 1) { + if (i >= MAX_LINES) break; + putLine(lines, i, line); + } + if (i == 0) { + putLine(lines, 0, ""); + i = 1; + } + return i; +} + +fn extractWord(str: []const u8, i: usize) []const u8 { + if (str.len == 0) return ""; + var l = @min(i, str.len - 1); + var r = @min(i, str.len); + + while (l > 0 and !std.ascii.isWhitespace(str[l - 1])) : (l -= 1) {} + while (r < str.len and !std.ascii.isWhitespace(str[r])) : (r += 1) {} + + var word = str[l..r]; + word = std.mem.trim(u8, word, " \t\r\n\"'`()[]{}<>;,"); + if (std.mem.indexOfScalar(u8, word, ':')) |colon| { + word = word[0..colon]; + } + return word; +} + +fn loadFile( + io: std.Io, + gpa: std.mem.Allocator, + path: []const u8, + lines: *[MAX_LINES][MAX_LINE_LEN]u8, + rendered_lines: *usize, + current_path: *[MAX_LINE_LEN]u8, + current_path_len: *usize, +) !void { + const path_len = @min(path.len, MAX_LINE_LEN - 1); + @memcpy(current_path[0..path_len], path[0..path_len]); + current_path[path_len] = 0; + current_path_len.* = path_len; + + var file = std.Io.Dir.cwd().openFile(io, path, .{ .mode = .read_only }) catch |err| { + const msg = try std.fmt.bufPrint(&lines[0], "could not open {s}: {}", .{ path, err }); + lines[0][msg.len] = 0; + rendered_lines.* = 1; + return; + }; + defer file.close(io); + + var read_buf: [0x1000]u8 = undefined; + const flen = try file.length(io); + var reader = file.reader(io, &read_buf); + const text = reader.interface.readAlloc(gpa, @min(flen, 0x100000)) catch |err| { + const msg = try std.fmt.bufPrint(&lines[0], "could not read {s}: {}", .{ path, err }); + lines[0][msg.len] = 0; + rendered_lines.* = 1; + return; + }; + defer gpa.free(text); + + rendered_lines.* = splitLines(lines, text); +} + const nrows = 64; const ncols = 100; pub fn main(init: std.process.Init) !void { const io = init.io; const gpa = init.gpa; + const args = try init.minimal.args.toSlice(init.arena.allocator()); var term: ghostty_vt.Terminal = try .init(gpa, .{ .cols = ncols, @@ -95,6 +191,26 @@ pub fn main(init: std.process.Init) !void { defer term_stream.deinit(); var tty_buf: [0x10000]u8 = undefined; + var display_lines: [2][MAX_LINES][MAX_LINE_LEN]u8 = undefined; + var rendered_lines = [_]usize{ 0, 0 }; + var line_nums: [MAX_LINES][8]u8 = undefined; + var file_scroll: usize = 0; + var current_path: [MAX_LINE_LEN]u8 = undefined; + var current_path_len: usize = 0; + var term_cwd: [MAX_LINE_LEN]u8 = undefined; + var term_cwd_len = putString(&term_cwd, init.environ_map.get("PWD") orelse "."); + var cwd_history: [MAX_HISTORY][MAX_LINE_LEN]u8 = undefined; + var cwd_history_lens: [MAX_HISTORY]usize = undefined; + var cwd_history_len: usize = 0; + try loadFile( + io, + gpa, + if (args.len > 1) args[1] else "main.zig", + &display_lines[FILE_PANE], + &rendered_lines[FILE_PANE], + ¤t_path, + ¤t_path_len, + ); var tty = try vaxis.Tty.init(io, &tty_buf); @@ -125,19 +241,99 @@ pub fn main(init: std.process.Init) !void { var pty_reader = try io.concurrent(readPty, .{ io, gpa, pty_master, &loop }); defer _ = pty_reader.cancel(io) catch {}; defer pty_master.close(io); - var dbg_print_buf: [0x1000]u8 = undefined; while (true) { const event = try loop.nextEvent(); const win = vx.window(); + const left_outer_width: u16 = @max(1, win.width / 2); + const right_x: i16 = @intCast(left_outer_width); switch (event) { .mouse => |mouse| { + const mouse_on_left = mouse.col < right_x; + if (mouse.button == .wheel_down) { - term.screens.active.scroll(.{ .delta_row = 1 }); + if (mouse_on_left) { + if (file_scroll + 1 < rendered_lines[FILE_PANE]) file_scroll += 1; + } else { + term.screens.active.scroll(.{ .delta_row = 1 }); + } } if (mouse.button == .wheel_up) { - term.screens.active.scroll(.{ .delta_row = -1 }); + if (mouse_on_left) { + if (file_scroll > 0) file_scroll -= 1; + } else { + term.screens.active.scroll(.{ .delta_row = -1 }); + } + } + + if (mouse.type == .press and (mouse.button == .left or mouse.button == .right)) { + var selected_word: ?[]const u8 = null; + + if (mouse_on_left) { + const line_idx = @as(usize, @intCast(@max(mouse.row, 0))) + file_scroll; + if (line_idx < rendered_lines[FILE_PANE] and mouse.col >= 5) { + const len = std.mem.findSentinel(u8, 0, @ptrCast(&display_lines[FILE_PANE][line_idx])); + const selected_col: usize = @intCast(mouse.col - 5); + const selected_line = display_lines[FILE_PANE][line_idx][0..len]; + selected_word = extractWord(selected_line, selected_col); + } + } else { + const line_idx: usize = @intCast(@max(mouse.row, 0)); + if (line_idx < rendered_lines[TTY_PANE]) { + const len = std.mem.findSentinel(u8, 0, @ptrCast(&display_lines[TTY_PANE][line_idx])); + const selected_col: usize = @intCast(@max(mouse.col - right_x, 0)); + const selected_line = display_lines[TTY_PANE][line_idx][0..len]; + selected_word = extractWord(selected_line, selected_col); + } + } + + if (selected_word) |word| { + if (word.len > 0) { + var found_path_buf: [MAX_LINE_LEN]u8 = undefined; + var candidate_buf: [MAX_LINE_LEN]u8 = undefined; + var found_path: []const u8 = ""; + var found_kind: ?std.Io.File.Kind = null; + + const current_candidate = pathFrom(term_cwd[0..term_cwd_len], word, &candidate_buf); + if (std.Io.Dir.cwd().statFile(io, current_candidate, .{})) |stat| { + const found_len = putString(&found_path_buf, current_candidate); + found_path = found_path_buf[0..found_len]; + found_kind = stat.kind; + } else |_| {} + + var h = cwd_history_len; + while (found_kind == null and h > 0) { + h -= 1; + const old_cwd = cwd_history[h][0..cwd_history_lens[h]]; + const candidate = pathFrom(old_cwd, word, &candidate_buf); + if (std.Io.Dir.cwd().statFile(io, candidate, .{})) |stat| { + const found_len = putString(&found_path_buf, candidate); + found_path = found_path_buf[0..found_len]; + found_kind = stat.kind; + } else |_| {} + } + + if (found_kind) |kind| { + if (kind == .directory) { + if (cwd_history_len < MAX_HISTORY) { + cwd_history_lens[cwd_history_len] = putString(&cwd_history[cwd_history_len], term_cwd[0..term_cwd_len]); + cwd_history_len += 1; + } + term_cwd_len = putString(&term_cwd, found_path); + + try writer.interface.writeAll("cd "); + try writer.interface.writeAll(found_path); + try writer.interface.writeAll(" && ls\r"); + try writer.flush(); + if (!mouse_on_left) term.scrollViewport(.bottom); + } else { + loadFile(io, gpa, found_path, &display_lines[FILE_PANE], &rendered_lines[FILE_PANE], ¤t_path, ¤t_path_len) catch {}; + file_scroll = 0; + } + } + } + } } }, .key_press => |key| { @@ -188,32 +384,51 @@ pub fn main(init: std.process.Init) !void { // term.scrollViewport(.bottom); const tty_str = try term.plainString(gpa); defer gpa.free(tty_str); + rendered_lines[TTY_PANE] = splitLines(&display_lines[TTY_PANE], tty_str); - const win_child = win.child(.{ - .border = .{ .where = .bottom }, - .height = nrows + 2, - .width = ncols + 2, + const win_file = win.child(.{ + .border = .{ .where = .right }, + .width = left_outer_width, }); - const win_dbg = win.child(.{ - .border = .{ .where = .all }, - .x_off = ncols + 2, - .height = nrows + 2, - .width = ncols + 2, + const win_term = win.child(.{ + .x_off = @intCast(left_outer_width), + .width = win.width -| left_outer_width, }); - win_child.clear(); - _ = win_child.printSegment(.{ .text = tty_str }, .{}); - win_child.showCursor(term.screens.active.cursor.x, term.screens.active.cursor.y); + win.clear(); + _ = win_file.printSegment(.{ .text = current_path[0..current_path_len] }, .{ + .row_offset = 0, + .col_offset = 5, + .wrap = .none, + }); - const dbg_str = try std.fmt.bufPrintSentinel( - &dbg_print_buf, - "c {} {}\npty pid {}\nwow it's working! debugging things will show here", - .{ - pty_master.handle, term.screens.active.cursor.x, term.screens.active.cursor.y, - }, - 0, - ); - _ = win_dbg.printSegment(.{ .text = dbg_str }, .{}); + const visible_lines = if (win_file.height > 1) win_file.height - 1 else 0; + var i: usize = 0; + while (i < visible_lines and i + file_scroll < rendered_lines[FILE_PANE]) : (i += 1) { + const line_idx = i + file_scroll; + const len = std.mem.findSentinel(u8, 0, @ptrCast(&display_lines[FILE_PANE][line_idx])); + const num = try std.fmt.bufPrint(&line_nums[i], "{d:0>4}", .{line_idx + 1}); + _ = win_file.printSegment(.{ .text = num }, .{ + .row_offset = @intCast(i + 1), + .col_offset = 0, + .wrap = .none, + }); + _ = win_file.printSegment(.{ .text = display_lines[FILE_PANE][line_idx][0..len] }, .{ + .row_offset = @intCast(i + 1), + .col_offset = 5, + .wrap = .none, + }); + } + + i = 0; + while (i < win_term.height and i < rendered_lines[TTY_PANE]) : (i += 1) { + const len = std.mem.findSentinel(u8, 0, @ptrCast(&display_lines[TTY_PANE][i])); + _ = win_term.printSegment(.{ .text = display_lines[TTY_PANE][i][0..len] }, .{ + .row_offset = @intCast(i), + .wrap = .none, + }); + } + win_term.showCursor(term.screens.active.cursor.x, term.screens.active.cursor.y); try vx.render(tty.writer()); } |
