PranitInitial commit439f83c3 months agoHistory
271 lines · 9.2 KB · Zig
RawDownload
const std = @import("std"); pub const ActivityPlacement = union(enum) {    const Transient = struct {        row: u16,        row_count: u16 = 1,        gap_above_rows: u16,        footer_clearance_rows: u16 = 0,        tool_row: ?u16 = null,    };     none,    transient_row: Transient,    overlay_entry: u16,     pub fn reservedFooterRows(self: ActivityPlacement) u16 {        return switch (self) {            .transient_row => |activity| activity.footer_clearance_rows,            .overlay_entry, .none => 0,        };    }     pub fn reservesFooterRow(self: ActivityPlacement) bool {        return self.reservedFooterRows() > 0;    }     pub fn reservedTopRow(self: ActivityPlacement) ?u16 {        return switch (self) {            .transient_row => |activity| {                const first_row = activity.tool_row orelse activity.row;                if (first_row <= activity.gap_above_rows) return 1;                return first_row - activity.gap_above_rows;            },            .overlay_entry, .none => null,        };    }     pub fn transcriptReservationRows(self: ActivityPlacement) u16 {        return switch (self) {            .transient_row => |activity| activity.gap_above_rows +| (activity.row -| (activity.tool_row orelse activity.row)) +| activity.row_count +| activity.footer_clearance_rows,            .overlay_entry, .none => 0,        };    }     pub fn requiredFooterTop(self: ActivityPlacement) ?u16 {        return switch (self) {            .transient_row => |activity| activity.row +| activity.row_count +| activity.footer_clearance_rows,            .overlay_entry, .none => null,        };    }     pub fn row(self: ActivityPlacement) ?u16 {        return switch (self) {            .transient_row => |activity| activity.row,            .overlay_entry => |r| r,            .none => null,        };    }     pub fn primaryRowCount(self: ActivityPlacement) u16 {        return switch (self) {            .transient_row => |activity| activity.row_count,            .overlay_entry => 1,            .none => 0,        };    }     pub fn toolRow(self: ActivityPlacement) ?u16 {        return switch (self) {            .transient_row => |activity| activity.tool_row,            .overlay_entry, .none => null,        };    }}; pub const Input = struct {    active_label_present: bool,    activity_rows: u16 = 1,    tool_before_activity: bool = false,    cursor_row: u16,    cursor_col: u16,    transient_gap_rows: u16,    place_mid_line_active: bool,    entry_bound_active: bool,    stream_active: bool,    footer_top_for_extra: u16,    footer_clearance_rows: u16 = 0,}; pub fn resolve(input: Input) ActivityPlacement {    const activity_rows = @max(input.activity_rows, 1);    if (input.tool_before_activity) {        const tool_row = if (input.cursor_col == 1)            input.cursor_row +| input.transient_gap_rows        else if (input.place_mid_line_active)            input.cursor_row +| input.transient_gap_rows +| 1        else            return .none;        return .{ .transient_row = .{            .row = tool_row +| 2,            .row_count = activity_rows,            .gap_above_rows = input.transient_gap_rows,            .footer_clearance_rows = input.footer_clearance_rows,            .tool_row = tool_row,        } };    }     if (input.active_label_present) {        const gap_rows = input.transient_gap_rows;        if (input.cursor_col == 1) {            return .{ .transient_row = .{                .row = input.cursor_row +| gap_rows,                .row_count = activity_rows,                .gap_above_rows = gap_rows,                .footer_clearance_rows = input.footer_clearance_rows,            } };        }        if (input.place_mid_line_active) {            return .{ .transient_row = .{                .row = input.cursor_row +| gap_rows +| 1,                .row_count = activity_rows,                .gap_above_rows = gap_rows,                .footer_clearance_rows = input.footer_clearance_rows,            } };        }    }     if (!input.active_label_present and input.entry_bound_active and input.stream_active) {        if (input.cursor_col == 1) {            return .{ .transient_row = .{ .row = input.cursor_row, .gap_above_rows = 0, .footer_clearance_rows = input.footer_clearance_rows } };        }        const sticky_row = if (input.footer_top_for_extra > 1) input.footer_top_for_extra - 1 else input.footer_top_for_extra;        return .{ .transient_row = .{ .row = sticky_row, .gap_above_rows = 0, .footer_clearance_rows = input.footer_clearance_rows } };    }     return .none;} pub fn suppressEntryOverlayForBanner(activity: *ActivityPlacement, banner_active: bool) void {    if (!banner_active) return;    switch (activity.*) {        .overlay_entry => {            activity.* = .none;        },        .none, .transient_row => {},    }} test "activity placement reserves row only for thinking shimmer" {    const thinking = resolve(.{        .active_label_present = true,        .cursor_row = 5,        .cursor_col = 1,        .transient_gap_rows = 0,        .place_mid_line_active = false,        .entry_bound_active = false,        .stream_active = false,        .footer_top_for_extra = 9,    });    try std.testing.expect(!thinking.reservesFooterRow());    try std.testing.expectEqual(@as(u16, 0), thinking.reservedFooterRows());    try std.testing.expectEqual(@as(u16, 1), thinking.transcriptReservationRows());    try std.testing.expectEqual(@as(u16, 5), thinking.row().?);     const none = resolve(.{        .active_label_present = true,        .cursor_row = 5,        .cursor_col = 3,        .transient_gap_rows = 0,        .place_mid_line_active = false,        .entry_bound_active = false,        .stream_active = false,        .footer_top_for_extra = 9,    });    try std.testing.expect(!none.reservesFooterRow());    try std.testing.expectEqual(@as(u16, 0), none.reservedFooterRows());    try std.testing.expectEqual(@as(u16, 0), none.transcriptReservationRows());    try std.testing.expectEqual(@as(?u16, null), none.row());} test "transient activity tracks leading gap separately from footer clearance" {    const activity = resolve(.{        .active_label_present = true,        .cursor_row = 3,        .cursor_col = 1,        .transient_gap_rows = 1,        .place_mid_line_active = false,        .entry_bound_active = false,        .stream_active = false,        .footer_top_for_extra = 6,    });     try std.testing.expect(!activity.reservesFooterRow());    try std.testing.expectEqual(@as(u16, 4), activity.row().?);    try std.testing.expectEqual(@as(?u16, 3), activity.reservedTopRow());    try std.testing.expectEqual(@as(u16, 2), activity.transcriptReservationRows());    try std.testing.expect(activity.row().? < 6);} test "transient activity does not count its leading gap as footer clearance" {    const activity = resolve(.{        .active_label_present = true,        .cursor_row = 10,        .cursor_col = 1,        .transient_gap_rows = 1,        .place_mid_line_active = false,        .entry_bound_active = false,        .stream_active = false,        .footer_top_for_extra = 12,    });     try std.testing.expectEqual(@as(u16, 11), activity.row().?);    try std.testing.expectEqual(@as(u16, 0), activity.reservedFooterRows());    try std.testing.expectEqual(@as(?u16, 10), activity.reservedTopRow());} test "mid-line transient activity reserves gap plus status row only" {    const activity = resolve(.{        .active_label_present = true,        .cursor_row = 5,        .cursor_col = 12,        .transient_gap_rows = 1,        .place_mid_line_active = true,        .entry_bound_active = false,        .stream_active = false,        .footer_top_for_extra = 6,    });     switch (activity) {        .transient_row => |transient| {            try std.testing.expectEqual(@as(u16, 7), transient.row);            try std.testing.expectEqual(@as(u16, 1), transient.gap_above_rows);            try std.testing.expectEqual(@as(?u16, 6), activity.reservedTopRow());            try std.testing.expectEqual(@as(u16, 2), activity.transcriptReservationRows());        },        .none, .overlay_entry => return error.TestUnexpectedResult,    }} test "active entry remains in the transient activity band" {    const activity = resolve(.{        .active_label_present = false,        .cursor_row = 9,        .cursor_col = 1,        .transient_gap_rows = 0,        .place_mid_line_active = false,        .entry_bound_active = true,        .stream_active = true,        .footer_top_for_extra = 10,    });     try std.testing.expectEqual(@as(u16, 9), activity.row().?);    try std.testing.expectEqual(@as(u16, 0), activity.reservedFooterRows());    switch (activity) {        .transient_row => |transient| {            try std.testing.expectEqual(@as(u16, 9), transient.row);            try std.testing.expectEqual(@as(u16, 0), transient.gap_above_rows);        },        .none, .overlay_entry => return error.TestUnexpectedResult,    }} test "banner suppression converts entry overlay to none" {    var activity = ActivityPlacement{ .overlay_entry = 7 };    suppressEntryOverlayForBanner(&activity, true);     switch (activity) {        .none => {},        .overlay_entry, .transient_row => return error.TestUnexpectedResult,    }}