search thread needs to take an allocated needle
parent
5ee000f58f
commit
ad8a6e0642
|
|
@ -71,6 +71,7 @@ extension Ghostty {
|
|||
if let searchState {
|
||||
searchNeedleCancellable = searchState.$needle.sink { [weak self] needle in
|
||||
guard let surface = self?.surface else { return }
|
||||
guard needle.count > 1 else { return }
|
||||
let action = "search:\(needle)"
|
||||
ghostty_surface_binding_action(surface, action, UInt(action.count))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4933,7 +4933,10 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||
}
|
||||
|
||||
_ = s.state.mailbox.push(
|
||||
.{ .change_needle = text },
|
||||
.{ .change_needle = try .init(
|
||||
self.alloc,
|
||||
text,
|
||||
) },
|
||||
.forever,
|
||||
);
|
||||
s.state.wakeup.notify() catch {};
|
||||
|
|
|
|||
|
|
@ -6,15 +6,15 @@ const build_config = @import("../build_config.zig");
|
|||
const App = @import("../App.zig");
|
||||
const Surface = @import("../Surface.zig");
|
||||
const renderer = @import("../renderer.zig");
|
||||
const termio = @import("../termio.zig");
|
||||
const terminal = @import("../terminal/main.zig");
|
||||
const Config = @import("../config.zig").Config;
|
||||
const MessageData = @import("../datastruct/main.zig").MessageData;
|
||||
|
||||
/// The message types that can be sent to a single surface.
|
||||
pub const Message = union(enum) {
|
||||
/// Represents a write request. Magic number comes from the max size
|
||||
/// we want this union to be.
|
||||
pub const WriteReq = termio.MessageData(u8, 255);
|
||||
pub const WriteReq = MessageData(u8, 255);
|
||||
|
||||
/// Set the title of the surface.
|
||||
/// TODO: we should change this to a "WriteReq" style structure in
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ pub const BlockingQueue = blocking_queue.BlockingQueue;
|
|||
pub const CacheTable = cache_table.CacheTable;
|
||||
pub const CircBuf = circ_buf.CircBuf;
|
||||
pub const IntrusiveDoublyLinkedList = intrusive_linked_list.DoublyLinkedList;
|
||||
pub const MessageData = @import("message_data.zig").MessageData;
|
||||
pub const SegmentedPool = segmented_pool.SegmentedPool;
|
||||
pub const SplitTree = split_tree.SplitTree;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,124 @@
|
|||
const std = @import("std");
|
||||
const assert = @import("../quirks.zig").inlineAssert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
/// Creates a union that can be used to accommodate data that fit within an array,
|
||||
/// are a stable pointer, or require deallocation. This is helpful for thread
|
||||
/// messaging utilities.
|
||||
pub fn MessageData(comptime Elem: type, comptime small_size: comptime_int) type {
|
||||
return union(enum) {
|
||||
pub const Self = @This();
|
||||
|
||||
pub const Small = struct {
|
||||
pub const Max = small_size;
|
||||
pub const Array = [Max]Elem;
|
||||
pub const Len = std.math.IntFittingRange(0, small_size);
|
||||
data: Array = undefined,
|
||||
len: Len = 0,
|
||||
};
|
||||
|
||||
pub const Alloc = struct {
|
||||
alloc: Allocator,
|
||||
data: []Elem,
|
||||
};
|
||||
|
||||
pub const Stable = []const Elem;
|
||||
|
||||
/// A small write where the data fits into this union size.
|
||||
small: Small,
|
||||
|
||||
/// A stable pointer so we can just pass the slice directly through.
|
||||
/// This is useful i.e. for const data.
|
||||
stable: Stable,
|
||||
|
||||
/// Allocated and must be freed with the provided allocator. This
|
||||
/// should be rarely used.
|
||||
alloc: Alloc,
|
||||
|
||||
/// Initializes the union for a given data type. This will
|
||||
/// attempt to fit into a small value if possible, otherwise
|
||||
/// will allocate and put into alloc.
|
||||
///
|
||||
/// This can't and will never detect stable pointers.
|
||||
pub fn init(alloc: Allocator, data: anytype) !Self {
|
||||
switch (@typeInfo(@TypeOf(data))) {
|
||||
.pointer => |info| {
|
||||
assert(info.size == .slice);
|
||||
assert(info.child == Elem);
|
||||
|
||||
// If it fits in our small request, do that.
|
||||
if (data.len <= Small.Max) {
|
||||
var buf: Small.Array = undefined;
|
||||
@memcpy(buf[0..data.len], data);
|
||||
return Self{
|
||||
.small = .{
|
||||
.data = buf,
|
||||
.len = @intCast(data.len),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Otherwise, allocate
|
||||
const buf = try alloc.dupe(Elem, data);
|
||||
errdefer alloc.free(buf);
|
||||
return Self{
|
||||
.alloc = .{
|
||||
.alloc = alloc,
|
||||
.data = buf,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self) void {
|
||||
switch (self) {
|
||||
.small, .stable => {},
|
||||
.alloc => |v| v.alloc.free(v.data),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a const slice of the data pointed to by this request.
|
||||
pub fn slice(self: *const Self) []const Elem {
|
||||
return switch (self.*) {
|
||||
.small => |*v| v.data[0..v.len],
|
||||
.stable => |v| v,
|
||||
.alloc => |v| v.data,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "MessageData init small" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
const Data = MessageData(u8, 10);
|
||||
const input = "hello!";
|
||||
const io = try Data.init(alloc, @as([]const u8, input));
|
||||
try testing.expect(io == .small);
|
||||
}
|
||||
|
||||
test "MessageData init alloc" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
const Data = MessageData(u8, 10);
|
||||
const input = "hello! " ** 100;
|
||||
const io = try Data.init(alloc, @as([]const u8, input));
|
||||
try testing.expect(io == .alloc);
|
||||
io.alloc.alloc.free(io.alloc.data);
|
||||
}
|
||||
|
||||
test "MessageData small fits non-u8 sized data" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
const len = 500;
|
||||
const Data = MessageData(u8, len);
|
||||
const input: []const u8 = "X" ** len;
|
||||
const io = try Data.init(alloc, input);
|
||||
try testing.expect(io == .small);
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ const Mutex = std.Thread.Mutex;
|
|||
const xev = @import("../../global.zig").xev;
|
||||
const internal_os = @import("../../os/main.zig");
|
||||
const BlockingQueue = @import("../../datastruct/main.zig").BlockingQueue;
|
||||
const MessageData = @import("../../datastruct/main.zig").MessageData;
|
||||
const point = @import("../point.zig");
|
||||
const FlattenedHighlight = @import("../highlight.zig").Flattened;
|
||||
const UntrackedHighlight = @import("../highlight.zig").Untracked;
|
||||
|
|
@ -242,7 +243,10 @@ fn drainMailbox(self: *Thread) !void {
|
|||
while (self.mailbox.pop()) |message| {
|
||||
log.debug("mailbox message={}", .{message});
|
||||
switch (message) {
|
||||
.change_needle => |v| try self.changeNeedle(v),
|
||||
.change_needle => |v| {
|
||||
defer v.deinit();
|
||||
try self.changeNeedle(v.slice());
|
||||
},
|
||||
.select => |v| try self.select(v),
|
||||
}
|
||||
}
|
||||
|
|
@ -414,10 +418,14 @@ pub const Mailbox = BlockingQueue(Message, 64);
|
|||
|
||||
/// The messages that can be sent to the thread.
|
||||
pub const Message = union(enum) {
|
||||
/// Represents a write request. Magic number comes from the max size
|
||||
/// we want this union to be.
|
||||
pub const WriteReq = MessageData(u8, 255);
|
||||
|
||||
/// Change the search term. If no prior search term is given this
|
||||
/// will start a search. If an existing search term is given this will
|
||||
/// stop the prior search and start a new one.
|
||||
change_needle: []const u8,
|
||||
change_needle: WriteReq,
|
||||
|
||||
/// Select a search result.
|
||||
select: ScreenSearch.Select,
|
||||
|
|
@ -820,7 +828,10 @@ test {
|
|||
|
||||
// Start our search
|
||||
_ = thread.mailbox.push(
|
||||
.{ .change_needle = "world" },
|
||||
.{ .change_needle = try .init(
|
||||
alloc,
|
||||
@as([]const u8, "world"),
|
||||
) },
|
||||
.forever,
|
||||
);
|
||||
try thread.wakeup.notify();
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ pub const Backend = backend.Backend;
|
|||
pub const DerivedConfig = Termio.DerivedConfig;
|
||||
pub const Mailbox = mailbox.Mailbox;
|
||||
pub const Message = message.Message;
|
||||
pub const MessageData = message.MessageData;
|
||||
pub const StreamHandler = stream_handler.StreamHandler;
|
||||
|
||||
test {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const apprt = @import("../apprt.zig");
|
|||
const renderer = @import("../renderer.zig");
|
||||
const terminal = @import("../terminal/main.zig");
|
||||
const termio = @import("../termio.zig");
|
||||
const MessageData = @import("../datastruct/main.zig").MessageData;
|
||||
|
||||
/// The messages that can be sent to an IO thread.
|
||||
///
|
||||
|
|
@ -97,95 +98,6 @@ pub const Message = union(enum) {
|
|||
};
|
||||
};
|
||||
|
||||
/// Creates a union that can be used to accommodate data that fit within an array,
|
||||
/// are a stable pointer, or require deallocation. This is helpful for thread
|
||||
/// messaging utilities.
|
||||
pub fn MessageData(comptime Elem: type, comptime small_size: comptime_int) type {
|
||||
return union(enum) {
|
||||
pub const Self = @This();
|
||||
|
||||
pub const Small = struct {
|
||||
pub const Max = small_size;
|
||||
pub const Array = [Max]Elem;
|
||||
pub const Len = std.math.IntFittingRange(0, small_size);
|
||||
data: Array = undefined,
|
||||
len: Len = 0,
|
||||
};
|
||||
|
||||
pub const Alloc = struct {
|
||||
alloc: Allocator,
|
||||
data: []Elem,
|
||||
};
|
||||
|
||||
pub const Stable = []const Elem;
|
||||
|
||||
/// A small write where the data fits into this union size.
|
||||
small: Small,
|
||||
|
||||
/// A stable pointer so we can just pass the slice directly through.
|
||||
/// This is useful i.e. for const data.
|
||||
stable: Stable,
|
||||
|
||||
/// Allocated and must be freed with the provided allocator. This
|
||||
/// should be rarely used.
|
||||
alloc: Alloc,
|
||||
|
||||
/// Initializes the union for a given data type. This will
|
||||
/// attempt to fit into a small value if possible, otherwise
|
||||
/// will allocate and put into alloc.
|
||||
///
|
||||
/// This can't and will never detect stable pointers.
|
||||
pub fn init(alloc: Allocator, data: anytype) !Self {
|
||||
switch (@typeInfo(@TypeOf(data))) {
|
||||
.pointer => |info| {
|
||||
assert(info.size == .slice);
|
||||
assert(info.child == Elem);
|
||||
|
||||
// If it fits in our small request, do that.
|
||||
if (data.len <= Small.Max) {
|
||||
var buf: Small.Array = undefined;
|
||||
@memcpy(buf[0..data.len], data);
|
||||
return Self{
|
||||
.small = .{
|
||||
.data = buf,
|
||||
.len = @intCast(data.len),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Otherwise, allocate
|
||||
const buf = try alloc.dupe(Elem, data);
|
||||
errdefer alloc.free(buf);
|
||||
return Self{
|
||||
.alloc = .{
|
||||
.alloc = alloc,
|
||||
.data = buf,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self) void {
|
||||
switch (self) {
|
||||
.small, .stable => {},
|
||||
.alloc => |v| v.alloc.free(v.data),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a const slice of the data pointed to by this request.
|
||||
pub fn slice(self: *const Self) []const Elem {
|
||||
return switch (self.*) {
|
||||
.small => |*v| v.data[0..v.len],
|
||||
.stable => |v| v,
|
||||
.alloc => |v| v.data,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test {
|
||||
std.testing.refAllDecls(@This());
|
||||
}
|
||||
|
|
@ -195,35 +107,3 @@ test {
|
|||
const testing = std.testing;
|
||||
try testing.expectEqual(@as(usize, 40), @sizeOf(Message));
|
||||
}
|
||||
|
||||
test "MessageData init small" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
const Data = MessageData(u8, 10);
|
||||
const input = "hello!";
|
||||
const io = try Data.init(alloc, @as([]const u8, input));
|
||||
try testing.expect(io == .small);
|
||||
}
|
||||
|
||||
test "MessageData init alloc" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
const Data = MessageData(u8, 10);
|
||||
const input = "hello! " ** 100;
|
||||
const io = try Data.init(alloc, @as([]const u8, input));
|
||||
try testing.expect(io == .alloc);
|
||||
io.alloc.alloc.free(io.alloc.data);
|
||||
}
|
||||
|
||||
test "MessageData small fits non-u8 sized data" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
const len = 500;
|
||||
const Data = MessageData(u8, len);
|
||||
const input: []const u8 = "X" ** len;
|
||||
const io = try Data.init(alloc, input);
|
||||
try testing.expect(io == .small);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue