PranitInitial commit439f83c2 months agoHistory
210 lines · 8.2 KB · Zig
RawDownload
const std = @import("std");const composer_history = @import("composer_history.zig");const editor_state = @import("editor_state.zig");const edit_history = @import("edit_history.zig");const gesture_state = @import("gesture_state.zig");const input_limit_rejection = @import("input_limit_rejection.zig");const kill_ring = @import("kill_ring.zig");const paste_framing = @import("paste_framing.zig");const picker_state = @import("picker_state.zig");const registered_entities = @import("registered_entities.zig");const text_scalar = @import("text_scalar.zig");const vertical_navigation = @import("vertical_navigation.zig");const debug_trace = @import("../shared/debug_trace.zig"); const Allocator = std.mem.Allocator; /// Non-owning view of the Core state affected by current-input and session/// resets. Callers retain ownership of every referenced state value.pub const State = struct {    edit: *editor_state.State,    picker: *picker_state.State,    composer_history: *composer_history.State,    paste: *paste_framing.State,    entities: *registered_entities.State,    text_scalar: *text_scalar.State,    gestures: *gesture_state.State,    input_limit_rejection: *input_limit_rejection.State,    kill_ring: *kill_ring.State,    edit_history: *edit_history.State,    vertical_navigation: *vertical_navigation.State,     pub fn clearCurrent(self: State, alloc: Allocator) void {        self.vertical_navigation.reset();        resetPendingTextScalarWithTrace(self.text_scalar, "input_cleared");        discardSelectionWithTrace(self.edit, "input_cleared");        self.input_limit_rejection.* = input_limit_rejection.clear();        self.edit.clearRetainingCapacity();        self.entities.clearImageAndSkillTokens(alloc);        self.picker.resetInlinePickerEpisode();        self.picker.model_completion_index = 0;        self.picker.model_completion_window_start = 0;        self.picker.resetFilePickerIndex();        self.picker.clearModelPickerFlow();        self.composer_history.resetNavigation(alloc);        self.edit_history.reset(alloc);    }     pub fn resetForSession(self: State, alloc: Allocator) void {        self.paste.resetWithTrace(.session_reset);        self.clearCurrent(alloc);        self.entities.resetForSession(alloc);        self.kill_ring.resetForSession(alloc);         const gesture_reset = gesture_state.reset(self.gestures.*);        self.gestures.* = gesture_reset.next;        if (gesture_reset.cleared_ctrl_c_exit) {            debug_trace.logf(                "input",                "event=ctrl_c_exit_disarmed reason=pending_gesture_reset",                .{},            );        }        if (gesture_reset.cleared_escape_clear) {            debug_trace.logf(                "input",                "event=esc_clear_disarmed reason=pending_gesture_reset",                .{},            );        }    }}; pub fn resetPendingTextScalarWithTrace(    state: *text_scalar.State,    reason: []const u8,) void {    const transition = text_scalar.reset(state.*);    state.* = transition.next;    if (transition.dropped) |drop| {        debug_trace.logf(            "input",            "text scalar dropped owner={s} bytes={d} reason={s}",            .{ @tagName(drop.owner), drop.bytes, reason },        );    }} pub fn discardSelectionWithTrace(    edit: *editor_state.State,    reason: []const u8,) void {    if (edit.discardSelection()) |dropped_bytes| {        debug_trace.logf(            "input",            "selection dropped bytes={d} reason={s}",            .{ dropped_bytes, reason },        );    }} test "session input reset clears transient state and preserves prompt history" {    const alloc = std.testing.allocator;     var edit: editor_state.State = .{};    defer edit.deinit(alloc);    var picker: picker_state.State = .{};    defer picker.deinit(alloc);    var history: composer_history.State = .{};    defer history.deinit(alloc);    var paste: paste_framing.State = .{};    defer paste.deinit(alloc);    var entities: registered_entities.State = .{};    defer entities.deinit(alloc);    var scalar: text_scalar.State = .{};    var gestures: gesture_state.State = .{};    var rejection: input_limit_rejection.State = .{};    var killed: kill_ring.State = .{};    defer killed.deinit(alloc);    var edits: edit_history.State = .{};    defer edits.deinit(alloc);    var vertical: vertical_navigation.State = .{};     try history.installTextEntries(alloc, &.{ "one", "two" });    try edit.setText(alloc, "draft");    try std.testing.expectEqual(        composer_history.NavigationResult{ .moved = 0 },        try history.navigate(            alloc,            -1,            .{                .edit = &edit,                .entities = &entities,                .picker = &picker,                .vertical_navigation = &vertical,                .input_limit_rejection = &rejection,                .images = null,            },            1,            4096,        ),    );    picker.dismissInlinePicker(.slash);    picker.model_completion_index = 3;    picker.model_completion_window_start = 2;    picker.file_completion_index = 4;    picker.file_completion_window_start = 1;    try picker.beginModelPickerFlow(alloc, "provider/model", 2, true, .effort);    try std.testing.expect(vertical.applyTarget(&edit, &entities, edit.input.items.len, 4));    _ = edit.beginSelection(0);    _ = edit.extendSelection(edit.input.items.len);    {        const pasted_backing = try alloc.dupe(u8, "pasted backing");        errdefer alloc.free(pasted_backing);        try entities.pasted_blocks.append(alloc, .{            .id = 7,            .text = pasted_backing,            .line_count = 1,        });    }    try killed.text.appendSlice(alloc, "killed draft");    var prepared_edit = try edits.prepare(alloc, 0, "", "draft", 0, "draft".len);    defer prepared_edit.deinit(alloc);    edits.commit(alloc, &prepared_edit);    entities.next_paste_id = 8;    paste.begin(.composer, std.math.maxInt(usize));    try paste.buffer.appendSlice(alloc, "pending paste");    scalar = text_scalar.advance(scalar, .composer, 0xE2).next;    gestures = gesture_state.pressCtrlCExit(gestures, 123).next;    gestures = gesture_state.pressEscapeClear(gestures, 456).next;    rejection = input_limit_rejection.begin(rejection, .composer).next;    try std.testing.expect(edit.selectionRange() != null);    try std.testing.expectEqual(@as(?usize, 4), vertical.preferredColumn());     (State{        .edit = &edit,        .picker = &picker,        .composer_history = &history,        .paste = &paste,        .entities = &entities,        .text_scalar = &scalar,        .gestures = &gestures,        .input_limit_rejection = &rejection,        .kill_ring = &killed,        .edit_history = &edits,        .vertical_navigation = &vertical,    }).resetForSession(alloc);     try std.testing.expectEqualStrings("", edit.input.items);    try std.testing.expect(edit.selectionRange() == null);    try std.testing.expectEqual(@as(usize, 0), entities.pasted_blocks.items.len);    try std.testing.expectEqualStrings("", killed.text.items);    try std.testing.expect(edits.peekUndo() == null);    try std.testing.expectEqual(@as(usize, 1), entities.next_paste_id);    try std.testing.expectEqual(paste_framing.Owner.none, paste.owner);    try std.testing.expect(!text_scalar.hasPending(scalar));    try std.testing.expect(!gestures.ctrlCExitArmed());    try std.testing.expect(!gestures.escapeClearArmed());    try std.testing.expect(rejection.owner == null);    try std.testing.expect(vertical.preferredColumn() == null);    try std.testing.expectEqual(@as(usize, 2), history.count());    try std.testing.expect(history.activeIndex() == null);    try std.testing.expectEqualStrings("one", history.entryText(0).?);    try std.testing.expectEqualStrings("two", history.entryText(1).?);    try std.testing.expect(!picker.isInlinePickerDismissed(.slash));    try std.testing.expectEqual(@as(usize, 0), picker.model_completion_index);    try std.testing.expectEqual(@as(usize, 0), picker.model_completion_window_start);    try std.testing.expectEqual(@as(usize, 0), picker.file_completion_index);    try std.testing.expectEqual(@as(usize, 0), picker.file_completion_window_start);    try std.testing.expect(!picker.hasPendingModelPickerSelection());}