const std = @import("std");const host = @import("../hosts/host.zig");const composer_selection = @import("../input/composer_selection.zig");const composer_replacement = @import("../input/composer_replacement.zig");const composer_undo = @import("../input/composer_undo.zig");const edit_history = @import("../input/edit_history.zig");const editor_state = @import("../input/editor_state.zig");const input_limit_rejection = @import("../input/input_limit_rejection.zig");const picker_state = @import("../input/picker_state.zig");const registered_entities = @import("../input/registered_entities.zig");const vertical_navigation = @import("../input/vertical_navigation.zig"); pub const ClipboardResult = enum { inactive, copied, cut, copy_failed, delete_failed,}; pub fn copySelection( edit: *const editor_state.State, clipboard: host.Clipboard,) ClipboardResult { const selected = edit.selectedText() orelse return .inactive; if (!(clipboard.copy(selected) catch false)) return .copy_failed; return .copied;} pub fn cutSelection( alloc: std.mem.Allocator, selection: composer_selection.State, pending_images: ?*composer_replacement.ImageBlocks, clipboard: host.Clipboard,) ClipboardResult { const selected = selection.selectedText() orelse return .inactive; if (!(clipboard.copy(selected) catch false)) return .copy_failed; if (!selection.delete(alloc, pending_images)) return .delete_failed; return .cut;} const TestInput = struct { edit: editor_state.State = .{}, history: edit_history.State = .{}, picker: picker_state.State = .{}, entities: registered_entities.State = .{}, vertical_navigation: vertical_navigation.State = .{}, input_limit_rejection: input_limit_rejection.State = .{}, fn deinit(self: *TestInput, alloc: std.mem.Allocator) void { self.edit.deinit(alloc); self.history.deinit(alloc); self.picker.deinit(alloc); self.entities.deinit(alloc); } fn selection(self: *TestInput) composer_selection.State { return .{ .insertion = .{ .edit = &self.edit, .history = &self.history, .picker = &self.picker, .entities = &self.entities, .vertical_navigation = &self.vertical_navigation, .input_limit_rejection = &self.input_limit_rejection, } }; } fn undo(self: *TestInput) composer_undo.State { return .{ .edit = &self.edit, .history = &self.history, .picker = &self.picker, .entities = &self.entities, .vertical_navigation = &self.vertical_navigation, .input_limit_rejection = &self.input_limit_rejection, }; }}; const TestClipboard = struct { copied: std.ArrayList(u8) = .empty, succeed: bool = true, fn deinit(self: *TestClipboard, alloc: std.mem.Allocator) void { self.copied.deinit(alloc); } fn clipboard(self: *TestClipboard) host.Clipboard { return .{ .context = self, .copy_fn = copy }; } fn copy(raw_context: ?*anyopaque, bytes: []const u8) host.ClipboardError!bool { const self: *TestClipboard = @ptrCast(@alignCast(raw_context.?)); if (!self.succeed) return false; self.copied.appendSlice(std.testing.allocator, bytes) catch return error.CopyFailed; return true; }}; test "selection clipboard operations copy before cut" { const alloc = std.testing.allocator; var input: TestInput = .{}; defer input.deinit(alloc); try input.edit.setText(alloc, "alpha beta"); _ = input.selection().begin(0); _ = input.selection().extend("alpha".len); var test_clipboard: TestClipboard = .{}; defer test_clipboard.deinit(alloc); try std.testing.expectEqual( ClipboardResult.copied, copySelection(&input.edit, test_clipboard.clipboard()), ); try std.testing.expectEqualStrings("alpha", test_clipboard.copied.items); try std.testing.expectEqualStrings("alpha beta", input.edit.input.items); test_clipboard.copied.clearRetainingCapacity(); try std.testing.expectEqual( ClipboardResult.cut, cutSelection(alloc, input.selection(), null, test_clipboard.clipboard()), ); try std.testing.expectEqualStrings("alpha", test_clipboard.copied.items); try std.testing.expectEqualStrings(" beta", input.edit.input.items); try std.testing.expect(try input.undo().undo(alloc)); try std.testing.expectEqualStrings("alpha beta", input.edit.input.items);} test "failed clipboard copy preserves selection and text" { const alloc = std.testing.allocator; var input: TestInput = .{}; defer input.deinit(alloc); try input.edit.setText(alloc, "alpha beta"); _ = input.selection().begin(0); _ = input.selection().extend("alpha".len); var test_clipboard: TestClipboard = .{ .succeed = false }; defer test_clipboard.deinit(alloc); try std.testing.expectEqual( ClipboardResult.copy_failed, cutSelection(alloc, input.selection(), null, test_clipboard.clipboard()), ); try std.testing.expectEqualStrings("alpha beta", input.edit.input.items); try std.testing.expectEqualStrings("alpha", input.selection().selectedText().?);}
const std = @import("std");
const host = @import("../hosts/host.zig");
const composer_selection = @import("../input/composer_selection.zig");
const composer_replacement = @import("../input/composer_replacement.zig");
const composer_undo = @import("../input/composer_undo.zig");
const edit_history = @import("../input/edit_history.zig");
const editor_state = @import("../input/editor_state.zig");
const input_limit_rejection = @import("../input/input_limit_rejection.zig");
const picker_state = @import("../input/picker_state.zig");
const registered_entities = @import("../input/registered_entities.zig");
const vertical_navigation = @import("../input/vertical_navigation.zig");
pub const ClipboardResult = enum {
inactive,
copied,
cut,
copy_failed,
delete_failed,
};
pub fn copySelection(
edit: *const editor_state.State,
clipboard: host.Clipboard,
) ClipboardResult {
const selected = edit.selectedText() orelse return .inactive;
if (!(clipboard.copy(selected) catch false)) return .copy_failed;
return .copied;
}
pub fn cutSelection(
alloc: std.mem.Allocator,
selection: composer_selection.State,
pending_images: ?*composer_replacement.ImageBlocks,
clipboard: host.Clipboard,
) ClipboardResult {
const selected = selection.selectedText() orelse return .inactive;
if (!(clipboard.copy(selected) catch false)) return .copy_failed;
if (!selection.delete(alloc, pending_images)) return .delete_failed;
return .cut;
}
const TestInput = struct {
edit: editor_state.State = .{},
history: edit_history.State = .{},
picker: picker_state.State = .{},
entities: registered_entities.State = .{},
vertical_navigation: vertical_navigation.State = .{},
input_limit_rejection: input_limit_rejection.State = .{},
fn deinit(self: *TestInput, alloc: std.mem.Allocator) void {
self.edit.deinit(alloc);
self.history.deinit(alloc);
self.picker.deinit(alloc);
self.entities.deinit(alloc);
}
fn selection(self: *TestInput) composer_selection.State {
return .{ .insertion = .{
.edit = &self.edit,
.history = &self.history,
.picker = &self.picker,
.entities = &self.entities,
.vertical_navigation = &self.vertical_navigation,
.input_limit_rejection = &self.input_limit_rejection,
} };
}
fn undo(self: *TestInput) composer_undo.State {
return .{
.edit = &self.edit,
.history = &self.history,
.picker = &self.picker,
.entities = &self.entities,
.vertical_navigation = &self.vertical_navigation,
.input_limit_rejection = &self.input_limit_rejection,
};
}
};
const TestClipboard = struct {
copied: std.ArrayList(u8) = .empty,
succeed: bool = true,
fn deinit(self: *TestClipboard, alloc: std.mem.Allocator) void {
self.copied.deinit(alloc);
}
fn clipboard(self: *TestClipboard) host.Clipboard {
return .{ .context = self, .copy_fn = copy };
}
fn copy(raw_context: ?*anyopaque, bytes: []const u8) host.ClipboardError!bool {
const self: *TestClipboard = @ptrCast(@alignCast(raw_context.?));
if (!self.succeed) return false;
self.copied.appendSlice(std.testing.allocator, bytes) catch return error.CopyFailed;
return true;
}
};
test "selection clipboard operations copy before cut" {
const alloc = std.testing.allocator;
var input: TestInput = .{};
defer input.deinit(alloc);
try input.edit.setText(alloc, "alpha beta");
_ = input.selection().begin(0);
_ = input.selection().extend("alpha".len);
var test_clipboard: TestClipboard = .{};
defer test_clipboard.deinit(alloc);
try std.testing.expectEqual(
ClipboardResult.copied,
copySelection(&input.edit, test_clipboard.clipboard()),
);
try std.testing.expectEqualStrings("alpha", test_clipboard.copied.items);
try std.testing.expectEqualStrings("alpha beta", input.edit.input.items);
test_clipboard.copied.clearRetainingCapacity();
try std.testing.expectEqual(
ClipboardResult.cut,
cutSelection(alloc, input.selection(), null, test_clipboard.clipboard()),
);
try std.testing.expectEqualStrings("alpha", test_clipboard.copied.items);
try std.testing.expectEqualStrings(" beta", input.edit.input.items);
try std.testing.expect(try input.undo().undo(alloc));
try std.testing.expectEqualStrings("alpha beta", input.edit.input.items);
}
test "failed clipboard copy preserves selection and text" {
const alloc = std.testing.allocator;
var input: TestInput = .{};
defer input.deinit(alloc);
try input.edit.setText(alloc, "alpha beta");
_ = input.selection().begin(0);
_ = input.selection().extend("alpha".len);
var test_clipboard: TestClipboard = .{ .succeed = false };
defer test_clipboard.deinit(alloc);
try std.testing.expectEqual(
ClipboardResult.copy_failed,
cutSelection(alloc, input.selection(), null, test_clipboard.clipboard()),
);
try std.testing.expectEqualStrings("alpha beta", input.edit.input.items);
try std.testing.expectEqualStrings("alpha", input.selection().selectedText().?);
}