diff --git a/src/DeserErrorCtx.zig b/src/DeserErrorCtx.zig new file mode 100644 index 0000000..4a5df71 --- /dev/null +++ b/src/DeserErrorCtx.zig @@ -0,0 +1,25 @@ +//! A type containing some information about an error that occured +//! while deserializing a message from Zelilj. +const std = @import("std"); + +pos: usize, + +/// A string of the json source starting at pos - 16 and ending at pos + 16 +sample: []const u8, + +/// The allocator that was used to create this object. +alloc: std.mem.Allocator, + +const Self = @This(); + +pub fn init(alloc: std.mem.Allocator, pos: usize, data: []const u8) !Self { + return .{ + .pos = pos, + .sample = try alloc.dupe(u8, data[pos -| 16..pos + 16]), + .alloc = alloc, + }; +} + +pub fn deinit(self: Self) void { + self.alloc.free(self.sample); +} diff --git a/src/api.zig b/src/api.zig index 848f06b..bbce8c9 100644 --- a/src/api.zig +++ b/src/api.zig @@ -7,6 +7,8 @@ const json = @import("json"); const zapi = @import("zellij_api.zig"); const types = @import("types.zig"); +const DeserErrorCtx = @import("DeserErrorCtx.zig"); + /// Sends an object as JSON to zellij pub fn sendObj(data: anytype) !void { var stdout = std.io.getStdOut(); @@ -15,7 +17,7 @@ pub fn sendObj(data: anytype) !void { } /// Receives a JSON object from zellij -pub fn recvObj(comptime T: type, alloc: std.mem.Allocator) !zz.OwnedDeserData(T) { +pub fn recvObj(comptime T: type, alloc: std.mem.Allocator, err_ctx: ?*?DeserErrorCtx) !zz.OwnedDeserData(T) { var stdin = std.io.getStdIn(); const data = (try stdin.reader().readUntilDelimiterOrEofAlloc( @@ -25,7 +27,7 @@ pub fn recvObj(comptime T: type, alloc: std.mem.Allocator) !zz.OwnedDeserData(T) )) orelse unreachable; defer alloc.free(data); - return zz.OwnedDeserData(T).deserialize(alloc, data); + return zz.OwnedDeserData(T).deserialize(alloc, err_ctx, data); } /// Subscribes to the given events. diff --git a/src/deserialization_blocks/line_and_column.zig b/src/deserialization_blocks/line_and_column.zig index 1423784..071683c 100644 --- a/src/deserialization_blocks/line_and_column.zig +++ b/src/deserialization_blocks/line_and_column.zig @@ -20,6 +20,11 @@ const Vis = struct { const column = (try input.nextElement(alloc, usize)) orelse return error.InvalidLength; + // Getty needs a final nextElement call + if (try input.nextElement(alloc, getty.de.Ignored)) |_| { + return error.InvalidLength; + } + return types.LineAndColumn{ .line = line, .column = column, diff --git a/src/main.zig b/src/main.zig index 81f5fb5..77c7d49 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,8 +1,10 @@ const std = @import("std"); +pub const api = @import("api.zig"); pub const types = @import("types.zig"); + +pub const DeserErrorCtx = @import("DeserErrorCtx.zig"); pub const Event = types.Event; pub const OwnedDeserData = types.OwnedDeserData; -pub const api = @import("api.zig"); /// This is the allocator that will be used by zellzig for communication. /// This must be set before events can be received. @@ -69,6 +71,6 @@ pub fn createPlugin(comptime Plugin: type) void { }; } -pub fn getEvent(alloc: std.mem.Allocator) !OwnedDeserData(types.Event) { - return try api.recvObj(types.Event, alloc); +pub fn getEvent(alloc: std.mem.Allocator, err_ctx: ?*?DeserErrorCtx) !OwnedDeserData(types.Event) { + return try api.recvObj(types.Event, alloc, err_ctx); } diff --git a/src/types.zig b/src/types.zig index 8021111..2837bff 100644 --- a/src/types.zig +++ b/src/types.zig @@ -2,6 +2,8 @@ const std = @import("std"); const getty = @import("getty"); const json = @import("json"); +const DeserErrorCtx = @import("DeserErrorCtx.zig"); + /// getty deserialization blocks required to properly deserialize messages pub const dbs = .{ @import("deserialization_blocks/bytearray.zig"), @@ -23,14 +25,19 @@ pub fn OwnedDeserData(comptime T: type) type { const Self = @This(); - pub fn deserialize(alloc: std.mem.Allocator, data: []const u8) !Self { + pub fn deserialize(alloc: std.mem.Allocator, err_ctx: ?*?DeserErrorCtx, data: []const u8) !Self { @setEvalBranchQuota(10000); var arena = std.heap.ArenaAllocator.init(alloc); errdefer arena.deinit(); const arena_alloc = arena.allocator(); var deser = json.Deserializer(dbs).withAllocator(arena_alloc, data); - const deser_data = try getty.deserialize(arena_alloc, T, deser.deserializer()); + const deser_data = getty.deserialize(arena_alloc, T, deser.deserializer()) catch |e| { + if (err_ctx) |ctx| { + ctx.* = try DeserErrorCtx.init(alloc, deser.tokens.i, data); + } + return e; + }; return Self{ .data = deser_data, @@ -55,7 +62,7 @@ test "deserialize Char" { \\"x" ; - var deser = try OwnedDeserData(Char).deserialize(std.testing.allocator, json_src); + var deser = try OwnedDeserData(Char).deserialize(std.testing.allocator, null, json_src); defer deser.deinit(); } @@ -77,7 +84,7 @@ test "deserialize Bytearray" { ; var valid_deser = try OwnedDeserData(Bytearray) - .deserialize(std.testing.allocator, valid_json_src); + .deserialize(std.testing.allocator, null, valid_json_src); defer valid_deser.deinit(); const invalid_json_src = @@ -86,7 +93,7 @@ test "deserialize Bytearray" { try std.testing.expectError( error.InvalidType, - OwnedDeserData(Bytearray).deserialize(std.testing.allocator, invalid_json_src), + OwnedDeserData(Bytearray).deserialize(std.testing.allocator, null, invalid_json_src), ); } @@ -190,7 +197,7 @@ test "deserialize Event" { \\} ; - var deser = try OwnedDeserData(Event).deserialize(std.testing.allocator, json_src); + var deser = try OwnedDeserData(Event).deserialize(std.testing.allocator, null, json_src); defer deser.deinit(); } @@ -208,7 +215,10 @@ test "deserialize KeybindSet" { \\ }, \\ [ \\ { - \\ "Resize": "Increase" + \\ "Resize": [ + \\ "Increase", + \\ null + \\ ] \\ } \\ ] \\ ] @@ -216,7 +226,7 @@ test "deserialize KeybindSet" { \\] ; - var deser = try OwnedDeserData(KeybindSet).deserialize(std.testing.allocator, json_src); + var deser = try OwnedDeserData(KeybindSet).deserialize(std.testing.allocator, null, json_src); defer deser.deinit(); } @@ -335,7 +345,7 @@ test "deserialize Palette" { \\ } ; - var deser = try OwnedDeserData(Palette).deserialize(std.testing.allocator, json_src); + var deser = try OwnedDeserData(Palette).deserialize(std.testing.allocator, null, json_src); defer deser.deinit(); } @@ -403,7 +413,7 @@ test "deserialize Key" { \\] ; - var deser = try OwnedDeserData([]Key).deserialize(std.testing.allocator, json_src); + var deser = try OwnedDeserData([]Key).deserialize(std.testing.allocator, null, json_src); defer deser.deinit(); } @@ -415,12 +425,14 @@ pub const CharOrArrow = union(enum) { test "deserialize CharOrArrow" { var data_1 = try OwnedDeserData(CharOrArrow).deserialize( std.testing.allocator, + null, "\"A\"", ); defer data_1.deinit(); var data_2 = try OwnedDeserData(CharOrArrow).deserialize( std.testing.allocator, + null, "\"Left\"", ); defer data_2.deinit(); @@ -553,9 +565,24 @@ pub const Resize = enum { Decrease, }; +pub const PercentOrFixed = union(enum) { + Percent: usize, + Fixed: usize, +}; + +pub const FloatingPanesLayout = struct { + name: ?[]u8, + height: ?PercentOrFixed, + width: ?PercentOrFixed, + x: ?PercentOrFixed, + y: ?PercentOrFixed, + run: ?Run, + focus: ?bool, +}; + pub const EditFileInfo = std.meta.Tuple(&.{ []u8, ?usize, ?Direction, bool }); pub const NewTiledPaneInfo = std.meta.Tuple(&.{ ?Direction, ?RunCommandAction, ?[]u8 }); -pub const NewTabInfo = std.meta.Tuple(&.{ ?PaneLayout, ?[]u8 }); +pub const NewTabInfo = std.meta.Tuple(&.{ ?PaneLayout, []FloatingPanesLayout, ?[]u8 }); pub const NewPaneInfo = std.meta.Tuple(&.{ ?Direction, ?[]u8 }); pub const NewFloatingPaneInfo = std.meta.Tuple(&.{ ?RunCommandAction, ?[]u8 }); pub const ResizeInfo = std.meta.Tuple(&.{ Resize, ?Direction });