diff --git a/src/git.zig b/src/git.zig index 714ba86..29a27fe 100644 --- a/src/git.zig +++ b/src/git.zig @@ -47,132 +47,6 @@ pub fn workspace_ignored_files(context: usize) Error!void { ); } -const StatusRecordType = enum { - @"#", // header - @"1", // ordinary file - @"2", // rename or copy - u, // unmerged file - @"?", // untracked file - @"!", // ignored file -}; - -pub fn status(context_: usize) Error!void { - const tag = @src().fn_name; - try git_err(context_, .{ - "--no-optional-locks", - "status", - "--porcelain=v2", - "--branch", - "--show-stash", - // "--untracked-files=no", - "--null", - }, struct { - fn result(context: usize, parent: tp.pid_ref, output: []const u8) void { - var it_ = std.mem.splitScalar(u8, output, 0); - while (it_.next()) |line| { - var it = std.mem.splitScalar(u8, line, ' '); - const rec_type = if (it.next()) |type_tag| - std.meta.stringToEnum(StatusRecordType, type_tag) orelse return - else - return; - switch (rec_type) { - .@"#" => { // header - const name = it.next() orelse return; - const value1 = it.next() orelse return; - if (it.next()) |value2| - parent.send(.{ module_name, context, tag, "#", name, value1, value2 }) catch {} - else - parent.send(.{ module_name, context, tag, "#", name, value1 }) catch {}; - }, - .@"1" => { // ordinary file: - const XY = it.next() orelse return; - const sub = it.next() orelse return; - const mH = it.next() orelse return; - const mI = it.next() orelse return; - const mW = it.next() orelse return; - const hH = it.next() orelse return; - const hI = it.next() orelse return; - - var path: std.ArrayListUnmanaged(u8) = .empty; - defer path.deinit(allocator); - while (it.next()) |path_part| { - if (path.items.len > 0) path.append(allocator, ' ') catch return; - path.appendSlice(allocator, path_part) catch return; - } - - parent.send(.{ module_name, context, tag, "1", XY, sub, mH, mI, mW, hH, hI, path.items }) catch {}; - }, - .@"2" => { // rename or copy: - const XY = it.next() orelse return; - const sub = it.next() orelse return; - const mH = it.next() orelse return; - const mI = it.next() orelse return; - const mW = it.next() orelse return; - const hH = it.next() orelse return; - const hI = it.next() orelse return; - const Xscore = it.next() orelse return; - - var path: std.ArrayListUnmanaged(u8) = .empty; - defer path.deinit(allocator); - while (it.next()) |path_part| { - if (path.items.len > 0) path.append(allocator, ' ') catch return; - path.appendSlice(allocator, path_part) catch return; - } - - const origPath = it_.next() orelse return; // NOTE: this is the next zero terminated part - - parent.send(.{ module_name, context, tag, "2", XY, sub, mH, mI, mW, hH, hI, Xscore, path.items, origPath }) catch {}; - }, - .u => { // unmerged file:

- const XY = it.next() orelse return; - const sub = it.next() orelse return; - const m1 = it.next() orelse return; - const m2 = it.next() orelse return; - const m3 = it.next() orelse return; - const mW = it.next() orelse return; - const h1 = it.next() orelse return; - const h2 = it.next() orelse return; - const h3 = it.next() orelse return; - - var path: std.ArrayListUnmanaged(u8) = .empty; - defer path.deinit(allocator); - while (it.next()) |path_part| { - if (path.items.len > 0) path.append(allocator, ' ') catch return; - path.appendSlice(allocator, path_part) catch return; - } - - parent.send(.{ module_name, context, tag, "u", XY, sub, m1, m2, m3, mW, h1, h2, h3, path.items }) catch {}; - }, - .@"?" => { // untracked file: - var path: std.ArrayListUnmanaged(u8) = .empty; - defer path.deinit(allocator); - while (it.next()) |path_part| { - if (path.items.len > 0) path.append(allocator, ' ') catch return; - path.appendSlice(allocator, path_part) catch return; - } - parent.send(.{ module_name, context, tag, "?", path.items }) catch {}; - }, - .@"!" => { // ignored file: - var path: std.ArrayListUnmanaged(u8) = .empty; - defer path.deinit(allocator); - while (it.next()) |path_part| { - if (path.items.len > 0) path.append(allocator, ' ') catch return; - path.appendSlice(allocator, path_part) catch return; - } - parent.send(.{ module_name, context, tag, "!", path.items }) catch {}; - }, - } - // parent.send(.{ module_name, context, tag, value }) catch {}; - } - } - }.result, struct { - fn result(_: usize, _: tp.pid_ref, output: []const u8) void { - var it = std.mem.splitScalar(u8, output, '\n'); - while (it.next()) |line| std.log.err("{s}: {s}", .{ module_name, line }); - } - }.result, exit_null(tag)); -} - fn git_line_output(context_: usize, comptime tag: []const u8, cmd: anytype) Error!void { try git_err(context_, cmd, struct { fn result(context: usize, parent: tp.pid_ref, output: []const u8) void { diff --git a/src/tui/Button.zig b/src/tui/Button.zig index 60fbc41..ac9fa5b 100644 --- a/src/tui/Button.zig +++ b/src/tui/Button.zig @@ -57,7 +57,6 @@ pub fn create(ctx_type: type, allocator: std.mem.Allocator, parent: Plane, opts: .opts = opts, }; self.opts.label = try self.allocator.dupe(u8, opts.label); - try self.init(); return self; } @@ -76,18 +75,8 @@ pub fn State(ctx_type: type) type { const Self = @This(); pub const Context = ctx_type; - const child: type = switch (@typeInfo(Context)) { - .pointer => |p| p.child, - .@"struct" => Context, - else => struct {}, - }; - - pub fn init(self: *Self) error{OutOfMemory}!void { - if (@hasDecl(child, "ctx_init")) return self.opts.ctx.ctx_init(); - } pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - if (@hasDecl(child, "ctx_deinit")) self.opts.ctx.ctx_deinit(); self.allocator.free(self.opts.label); self.plane.deinit(); allocator.destroy(self); diff --git a/src/tui/MessageFilter.zig b/src/tui/MessageFilter.zig index afc1a37..0bdc1d9 100644 --- a/src/tui/MessageFilter.zig +++ b/src/tui/MessageFilter.zig @@ -114,7 +114,7 @@ pub const List = struct { self.list.deinit(); } - pub fn add(self: *List, h: MessageFilter) error{OutOfMemory}!void { + pub fn add(self: *List, h: MessageFilter) !void { (try self.list.addOne()).* = h; // @import("log").print("MessageFilter", "add: {d} {s}", .{ self.list.items.len, self.list.items[self.list.items.len - 1].vtable.type_name }); } diff --git a/src/tui/status/branch.zig b/src/tui/status/branch.zig index d11b9ab..a505d9f 100644 --- a/src/tui/status/branch.zig +++ b/src/tui/status/branch.zig @@ -3,66 +3,40 @@ const tp = @import("thespian"); const EventHandler = @import("EventHandler"); const Plane = @import("renderer").Plane; -const command = @import("command"); const git = @import("git"); const Widget = @import("../Widget.zig"); -const Button = @import("../Button.zig"); const MessageFilter = @import("../MessageFilter.zig"); const tui = @import("../tui.zig"); -const branch_symbol = "󰘬 "; -const ahead_symbol = "⇡"; -const behind_symbol = "⇣"; -const stash_symbol = "*"; -const changed_symbol = "+"; -const untracked_symbol = "?"; +const branch_symbol = "󰘬"; allocator: std.mem.Allocator, -workspace_path: ?[]const u8 = null, +plane: Plane, branch: ?[]const u8 = null, -ahead: ?[]const u8 = null, -behind: ?[]const u8 = null, -stash: ?[]const u8 = null, -changed: usize = 0, -untracked: usize = 0, -done: bool = true, const Self = @This(); pub fn create( allocator: std.mem.Allocator, parent: Plane, - event_handler: ?EventHandler, + _: ?EventHandler, _: ?[]const u8, ) @import("widget.zig").CreateError!Widget { - return Button.create_widget(Self, allocator, parent, .{ - .ctx = .{ - .allocator = allocator, - }, - .label = "", - .on_click = on_click, - .on_layout = layout, - .on_render = render, - .on_event = event_handler, - }); -} - -pub fn ctx_init(self: *Self) error{OutOfMemory}!void { + const self: *Self = try allocator.create(Self); + self.* = .{ + .allocator = allocator, + .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent), + }; try tui.message_filters().add(MessageFilter.bind(self, receive_git)); git.workspace_path(0) catch {}; + return Widget.to(self); } -pub fn ctx_deinit(self: *Self) void { - tui.message_filters().remove_ptr(self); +pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { if (self.branch) |p| self.allocator.free(p); - if (self.ahead) |p| self.allocator.free(p); - if (self.behind) |p| self.allocator.free(p); -} - -fn on_click(_: *Self, _: *Button.State(Self)) void { - git.status(0) catch {}; - command.executeName("show_git_status", .{}) catch {}; + self.plane.deinit(); + allocator.destroy(self); } fn receive_git(self: *Self, _: tp.pid_ref, m: tp.message) MessageFilter.Error!bool { @@ -72,115 +46,45 @@ fn receive_git(self: *Self, _: tp.pid_ref, m: tp.message) MessageFilter.Error!bo false; } -fn process_git(self: *Self, m: tp.message) MessageFilter.Error!bool { - var value: []const u8 = undefined; +fn process_git( + self: *Self, + m: tp.message, +) MessageFilter.Error!bool { + var branch: []const u8 = undefined; if (try match(m.buf, .{ any, any, "workspace_path", null_ })) { // do nothing, we do not have a git workspace - } else if (try match(m.buf, .{ any, any, "workspace_path", extract(&value) })) { - if (self.workspace_path) |p| self.allocator.free(p); - self.workspace_path = try self.allocator.dupe(u8, value); - // git.current_branch(0) catch {}; - git.status(0) catch {}; - } else if (try match(m.buf, .{ any, any, "current_branch", extract(&value) })) { + } else if (try match(m.buf, .{ any, any, "workspace_path", string })) { + git.current_branch(0) catch {}; + } else if (try match(m.buf, .{ any, any, "current_branch", extract(&branch) })) { if (self.branch) |p| self.allocator.free(p); - self.branch = try self.allocator.dupe(u8, value); - } else if (try match(m.buf, .{ any, any, "status", tp.more })) { - return self.process_status(m); + self.branch = try self.allocator.dupe(u8, branch); } else { return false; } return true; } -fn process_status(self: *Self, m: tp.message) MessageFilter.Error!bool { - var value: []const u8 = undefined; - var ahead: []const u8 = undefined; - var behind: []const u8 = undefined; - if (self.done) { - self.done = false; - self.changed = 0; - self.untracked = 0; - if (self.ahead) |p| self.allocator.free(p); - self.ahead = null; - if (self.behind) |p| self.allocator.free(p); - self.behind = null; - if (self.stash) |p| self.allocator.free(p); - self.stash = null; - } +const format = " {s} {s} "; - if (try match(m.buf, .{ any, any, "status", "#", "branch.oid", extract(&value) })) { - // commit | (initial) - } else if (try match(m.buf, .{ any, any, "status", "#", "branch.head", extract(&value) })) { - if (self.branch) |p| self.allocator.free(p); - self.branch = try self.allocator.dupe(u8, value); - } else if (try match(m.buf, .{ any, any, "status", "#", "branch.upstream", extract(&value) })) { - // upstream-branch - } else if (try match(m.buf, .{ any, any, "status", "#", "branch.ab", extract(&ahead), extract(&behind) })) { - if (self.ahead) |p| self.allocator.free(p); - self.ahead = try self.allocator.dupe(u8, ahead); - if (self.behind) |p| self.allocator.free(p); - self.behind = try self.allocator.dupe(u8, behind); - } else if (try match(m.buf, .{ any, any, "status", "#", "stash", extract(&value) })) { - if (self.stash) |p| self.allocator.free(p); - self.stash = try self.allocator.dupe(u8, value); - } else if (try match(m.buf, .{ any, any, "status", "1", tp.more })) { - // ordinary file: - self.changed += 1; - } else if (try match(m.buf, .{ any, any, "status", "2", tp.more })) { - // rename or copy: - self.changed += 1; - } else if (try match(m.buf, .{ any, any, "status", "u", tp.more })) { - // unmerged file:

- self.changed += 1; - } else if (try match(m.buf, .{ any, any, "status", "?", tp.more })) { - // untracked file: - self.untracked += 1; - } else if (try match(m.buf, .{ any, any, "status", "!", tp.more })) { - // ignored file: - } else if (try match(m.buf, .{ any, any, "status", null_ })) { - self.done = true; - } else return false; - return true; -} - -fn format(self: *Self, buf: []u8) []const u8 { - const branch = self.branch orelse return ""; - var fbs = std.io.fixedBufferStream(buf); - const writer = fbs.writer(); - writer.print(" {s}{s}", .{ branch_symbol, branch }) catch {}; - if (self.ahead) |ahead| if (ahead.len > 1 and ahead[1] != '0') - writer.print(" {s}{s}", .{ ahead_symbol, ahead[1..] }) catch {}; - if (self.behind) |behind| if (behind.len > 1 and behind[1] != '0') - writer.print(" {s}{s}", .{ behind_symbol, behind[1..] }) catch {}; - if (self.stash) |stash| if (stash.len > 0 and stash[0] != '0') - writer.print(" {s}{s}", .{ stash_symbol, stash }) catch {}; - if (self.changed > 0) - writer.print(" {s}{d}", .{ changed_symbol, self.changed }) catch {}; - if (self.untracked > 0) - writer.print(" {s}{d}", .{ untracked_symbol, self.untracked }) catch {}; - writer.print(" ", .{}) catch {}; - return fbs.getWritten(); -} - -pub fn layout(self: *Self, btn: *Button.State(Self)) Widget.Layout { +pub fn layout(self: *Self) Widget.Layout { + const branch = self.branch orelse return .{ .static = 0 }; var buf: [256]u8 = undefined; - const text = self.format(&buf); - const len = btn.plane.egc_chunk_width(text, 0, 1); + var fbs = std.io.fixedBufferStream(&buf); + const writer = fbs.writer(); + writer.print(format, .{ branch_symbol, branch }) catch {}; + const len = self.plane.egc_chunk_width(fbs.getWritten(), 0, 1); return .{ .static = len }; } -pub fn render(self: *Self, btn: *Button.State(Self), theme: *const Widget.Theme) bool { - var buf: [256]u8 = undefined; - const text = self.format(&buf); - if (text.len == 0) return false; - const bg_style = if (btn.active) theme.editor_cursor else if (btn.hover) theme.statusbar_hover else theme.statusbar; - btn.plane.set_base_style(theme.editor); - btn.plane.erase(); - btn.plane.home(); - btn.plane.set_style(bg_style); - btn.plane.fill(" "); - btn.plane.home(); - _ = btn.plane.putstr(text) catch {}; +pub fn render(self: *Self, theme: *const Widget.Theme) bool { + const branch = self.branch orelse return false; + self.plane.set_base_style(theme.editor); + self.plane.erase(); + self.plane.home(); + self.plane.set_style(theme.statusbar); + self.plane.fill(" "); + self.plane.home(); + _ = self.plane.print(format, .{ branch_symbol, branch }) catch {}; return false; }