Initial Commit!
This commit is contained in:
commit
105eb205e6
|
@ -0,0 +1,9 @@
|
|||
# Zig
|
||||
zig-*
|
||||
|
||||
# Packages
|
||||
deps.zig
|
||||
|
||||
# Gyro
|
||||
.gyro/
|
||||
gyro.lock
|
|
@ -0,0 +1,33 @@
|
|||
# zellzig
|
||||
A zig framework for writing [zellij](https://zellij.dev/) plugins.
|
||||
|
||||
# usage
|
||||
For an example of how to use it, see the example directory.
|
||||
|
||||
Here's a quick overview:
|
||||
```zig
|
||||
const std = @import("std");
|
||||
const zz = @import("zellzig");
|
||||
|
||||
comptime {
|
||||
// register plugin
|
||||
zz.createPlugin(@This());
|
||||
}
|
||||
|
||||
pub fn init() void {
|
||||
// do initialization stuff
|
||||
}
|
||||
|
||||
pub fn update(ev: zz.Event) void {
|
||||
// handle events
|
||||
}
|
||||
|
||||
pub fn render(rows: i32, cols: i32) void {
|
||||
// draw UI
|
||||
}
|
||||
```
|
||||
|
||||
# development
|
||||
PRs are always welcome if you feel that something needs improvement/fixing! Make sure to follow [Conventional Commits](https://www.conventionalcommits.org/) and to run tests first, though.
|
||||
|
||||
Run tests using `gyro build test`. Note that tests are run on the native target, not WASM.
|
|
@ -0,0 +1,29 @@
|
|||
const std = @import("std");
|
||||
const pkgs = @import("deps.zig").pkgs;
|
||||
|
||||
pub fn build(b: *std.build.Builder) void {
|
||||
const docs = b.option(bool, "docs", "emit docs") orelse false;
|
||||
// Standard release options allow the person running `zig build` to select
|
||||
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
||||
const mode = b.standardReleaseOptions();
|
||||
|
||||
const lib = b.addStaticLibrary("zellzig", "src/main.zig");
|
||||
lib.setBuildMode(mode);
|
||||
lib.target.cpu_arch = .wasm32;
|
||||
lib.target.os_tag = .wasi;
|
||||
|
||||
pkgs.addAllTo(lib);
|
||||
|
||||
if (docs) {
|
||||
lib.emit_docs = .{ .emit_to = "zig-out/docs" };
|
||||
}
|
||||
|
||||
lib.install();
|
||||
|
||||
const main_tests = b.addTest("src/main.zig");
|
||||
main_tests.setBuildMode(mode);
|
||||
pkgs.addAllTo(main_tests);
|
||||
|
||||
const test_step = b.step("test", "Run library tests");
|
||||
test_step.dependOn(&main_tests.step);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
# Zig
|
||||
zig-*
|
||||
|
||||
# Packages
|
||||
deps.zig
|
||||
|
||||
# Gyro
|
||||
.gyro/
|
||||
gyro.lock
|
|
@ -0,0 +1,12 @@
|
|||
# example
|
||||
This is an example of a simple zellij plugin in zig.
|
||||
It's a super simple status bar.
|
||||
|
||||
If you plan on making your own plugin, be sure to pay special attention to `build.zig` and `gyro.zzz` to configure dependancies and WASM compilation.
|
||||
|
||||
# building & running
|
||||
```bash
|
||||
gyro build -Drelease-fast
|
||||
zellij --layout-path plugin.yaml
|
||||
```
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
const std = @import("std");
|
||||
const deps = @import("deps.zig");
|
||||
|
||||
pub fn build(b: *std.build.Builder) void {
|
||||
// Standard release options allow the person running `zig build` to select
|
||||
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
||||
const mode = b.standardReleaseOptions();
|
||||
|
||||
const lib = b.addSharedLibrary("example", "src/main.zig", .{ .unversioned = {} });
|
||||
lib.setBuildMode(mode);
|
||||
lib.target.cpu_arch = .wasm32;
|
||||
lib.target.os_tag = .wasi;
|
||||
deps.pkgs.addAllTo(lib);
|
||||
lib.install();
|
||||
|
||||
const main_tests = b.addTest("src/main.zig");
|
||||
main_tests.setBuildMode(mode);
|
||||
deps.pkgs.addAllTo(main_tests);
|
||||
|
||||
const test_step = b.step("test", "Run library tests");
|
||||
test_step.dependOn(&main_tests.step);
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
pkgs:
|
||||
zellzig-example:
|
||||
version: 0.0.0
|
||||
description: A basic zellzig plugin
|
||||
license: GPL-3.0
|
||||
root: src/main.zig
|
||||
deps:
|
||||
zellzig:
|
||||
local: ..
|
||||
root: src/main.zig
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
template:
|
||||
direction: Horizontal
|
||||
parts:
|
||||
- direction: Vertical
|
||||
borderless: true
|
||||
split_size:
|
||||
Fixed: 1
|
||||
run:
|
||||
plugin:
|
||||
location: "zellij:tab-bar"
|
||||
|
||||
- direction: Vertical
|
||||
plugin: "zig-out/lib/example.wasm"
|
||||
|
||||
- direction: Vertical
|
||||
borderless: true
|
||||
split_size:
|
||||
Fixed: 1
|
||||
run:
|
||||
plugin:
|
||||
location: "file:zig-out/lib/example.wasm"
|
|
@ -0,0 +1,49 @@
|
|||
const std = @import("std");
|
||||
const zz = @import("zellzig");
|
||||
|
||||
comptime {
|
||||
// This function creates all the exports needed by zellij plugins.
|
||||
// As a paremeter, you should pass the struct that contains the
|
||||
// `init`, `update` and `render` functions. It is recommended
|
||||
// to put these functions in your root file, and pass the root
|
||||
// struct by using `@This()`.
|
||||
zz.createPlugin(@This());
|
||||
}
|
||||
|
||||
// assign out allocator to make sure it doesn't get free'd once init returns
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
var mode: ?zz.types.InputMode = null;
|
||||
|
||||
// called on startup
|
||||
pub fn init() void {
|
||||
// set zellzig's allocator
|
||||
// This is required to receive events.
|
||||
zz.allocator = gpa.allocator();
|
||||
|
||||
// This is required to make zellij close once everything but our plugin is gone.
|
||||
zz.api.setSelectable(false);
|
||||
|
||||
// Make sure we get events.
|
||||
zz.api.subscribe(&[_]zz.types.EventType{.ModeUpdate}) catch unreachable;
|
||||
}
|
||||
|
||||
// called on every event
|
||||
pub fn update(ev: zz.Event) void {
|
||||
switch (ev) {
|
||||
.ModeUpdate => |mode_info| mode = mode_info.mode,
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
// called to draw the UI
|
||||
pub fn render(rows: i32, cols: i32) void {
|
||||
_ = rows;
|
||||
_ = cols;
|
||||
|
||||
if (mode) |m| {
|
||||
var out = std.io.getStdOut();
|
||||
var writer = out.writer();
|
||||
writer.writeAll("Super sophisticated status bar: ") catch {};
|
||||
writer.writeAll(@tagName(m)) catch {};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
pkgs:
|
||||
zellzig:
|
||||
version: 0.0.0
|
||||
description: Build zellij plugins in zig
|
||||
license: GPL-3.0
|
||||
source_url: "https://mzte.de/git/LordMZTE/zellzig"
|
||||
root: src/main.zig
|
||||
deps:
|
||||
getty:
|
||||
git:
|
||||
url: "https://github.com/getty-zig/getty.git"
|
||||
ref: 69b4df59511203f91d38660794d8cb7a073eb815
|
||||
root: src/lib.zig
|
||||
json:
|
||||
git:
|
||||
url: "https://github.com/lordmzte/json.git"
|
||||
ref: fix/math-changes
|
||||
root: src/lib.zig
|
|
@ -0,0 +1,64 @@
|
|||
const std = @import("std");
|
||||
const zz = @import("main.zig");
|
||||
const json = @import("json");
|
||||
const zapi = @import("zellij_api.zig");
|
||||
const types = @import("types.zig");
|
||||
|
||||
pub fn sendObj(data: anytype) !void {
|
||||
var stdout = std.io.getStdOut();
|
||||
try json.toWriter(data, stdout.writer());
|
||||
try stdout.writeAll("\n");
|
||||
}
|
||||
|
||||
pub fn recvObj(comptime T: type) !types.OwnedDeserData(T) {
|
||||
var buf: [4096]u8 = undefined;
|
||||
var stdin = std.io.getStdIn();
|
||||
const data = (try stdin.reader().readUntilDelimiterOrEof(&buf, '\n')) orelse unreachable;
|
||||
return types.OwnedDeserData(T).deserialize(zz.allocator.?, data);
|
||||
}
|
||||
|
||||
// Subscription Handling
|
||||
pub fn subscribe(event_types: []const types.EventType) !void {
|
||||
try sendObj(event_types);
|
||||
zapi.host_subscribe();
|
||||
}
|
||||
|
||||
pub fn unsubscribe(event_types: []const types.EventType) !void {
|
||||
try sendObj(event_types);
|
||||
zapi.host_unsubscribe();
|
||||
}
|
||||
|
||||
// Plugin Settings
|
||||
pub fn setSelectable(selectable: bool) void {
|
||||
zapi.host_set_selectable(@boolToInt(selectable));
|
||||
}
|
||||
|
||||
// Query Functions
|
||||
pub fn getPluginIds() !types.OwnedDeserData(types.PluginIds) {
|
||||
zapi.host_get_plugin_ids();
|
||||
return try recvObj(types.PluginIds);
|
||||
}
|
||||
|
||||
pub fn getZellijVersion() !types.OwnedDeserData([]const u8) {
|
||||
zapi.host_get_zellij_version();
|
||||
return try recvObj([]const u8);
|
||||
}
|
||||
|
||||
pub fn openFile(path: []const u8) !void {
|
||||
try sendObj(path);
|
||||
zapi.host_open_file();
|
||||
}
|
||||
|
||||
pub fn switchTabTo(tab_idx: u32) void {
|
||||
zapi.host_switch_tab_to(tab_idx);
|
||||
}
|
||||
|
||||
pub fn setTimeout(secs: f64) void {
|
||||
zapi.host_set_timeout(secs);
|
||||
}
|
||||
|
||||
pub fn execCmd(cmd: []const []const u8) !void {
|
||||
try sendObj(cmd);
|
||||
zapi.host_exec_cmd();
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
const std = @import("std");
|
||||
const getty = @import("getty");
|
||||
const types = @import("../types.zig");
|
||||
|
||||
const Vis = struct {
|
||||
pub usingnamespace getty.de.Visitor(
|
||||
@This(),
|
||||
types.CharOrArrow,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
visitString,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
|
||||
pub fn visitString(
|
||||
_: @This(),
|
||||
_: ?std.mem.Allocator,
|
||||
comptime Deserializer: type,
|
||||
input: anytype,
|
||||
) Deserializer.Error!types.CharOrArrow {
|
||||
if (input.len == 1) {
|
||||
return types.CharOrArrow{ .Char = input[0] };
|
||||
}
|
||||
|
||||
return types.CharOrArrow{
|
||||
.Direction = std.meta.stringToEnum(types.Direction, input) orelse
|
||||
return error.UnknownVariant,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn is(comptime T: type) bool {
|
||||
return T == types.CharOrArrow;
|
||||
}
|
||||
|
||||
pub fn Visitor(comptime _: type) type {
|
||||
return Vis;
|
||||
}
|
||||
|
||||
pub fn deserialize(
|
||||
alloc: ?std.mem.Allocator,
|
||||
comptime _: type,
|
||||
deserializer: anytype,
|
||||
visitor: anytype,
|
||||
) !@TypeOf(visitor).Value {
|
||||
return try deserializer.deserializeString(alloc, visitor);
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
const std = @import("std");
|
||||
const getty = @import("getty");
|
||||
const types = @import("../types.zig");
|
||||
|
||||
const Vis = struct {
|
||||
pub usingnamespace getty.de.Visitor(
|
||||
@This(),
|
||||
types.LineAndColumn,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
visitSeq,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
|
||||
pub fn visitSeq(
|
||||
_: @This(),
|
||||
alloc: ?std.mem.Allocator,
|
||||
comptime Deserializer: type,
|
||||
input: anytype,
|
||||
) Deserializer.Error!types.LineAndColumn {
|
||||
const line = (try input.nextElement(alloc, isize)) orelse
|
||||
return error.InvalidLength;
|
||||
const column = (try input.nextElement(alloc, usize)) orelse
|
||||
return error.InvalidLength;
|
||||
|
||||
return types.LineAndColumn{
|
||||
.line = line,
|
||||
.column = column,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn is(comptime T: type) bool {
|
||||
return T == types.LineAndColumn;
|
||||
}
|
||||
|
||||
pub fn Visitor(comptime _: type) type {
|
||||
return Vis;
|
||||
}
|
||||
|
||||
pub fn deserialize(
|
||||
alloc: ?std.mem.Allocator,
|
||||
comptime _: type,
|
||||
deserializer: anytype,
|
||||
visitor: anytype,
|
||||
) !@TypeOf(visitor).Value {
|
||||
return try deserializer.deserializeSeq(alloc, visitor);
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
const std = @import("std");
|
||||
const getty = @import("getty");
|
||||
const types = @import("../types.zig");
|
||||
|
||||
pub fn is(comptime T: type) bool {
|
||||
return switch (@typeInfo(T)) {
|
||||
.Union => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn Visitor(comptime T: type) type {
|
||||
return struct {
|
||||
pub usingnamespace getty.de.Visitor(
|
||||
@This(),
|
||||
T,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
visitMap,
|
||||
undefined,
|
||||
undefined,
|
||||
visitString,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
|
||||
pub fn visitMap(
|
||||
_: @This(),
|
||||
alloc: ?std.mem.Allocator,
|
||||
comptime Deserializer: type,
|
||||
input: anytype,
|
||||
) Deserializer.Error!T {
|
||||
const tag = (try input.nextKey(alloc, []const u8)) orelse
|
||||
return error.InvalidLength;
|
||||
|
||||
var val: ?T = null;
|
||||
inline for (@typeInfo(T).Union.fields) |field| {
|
||||
if (val == null and std.mem.eql(u8, field.name, tag)) {
|
||||
if (field.field_type == void) {
|
||||
return error.InvalidType;
|
||||
}
|
||||
|
||||
const data = try input.nextValue(alloc, field.field_type);
|
||||
val = @unionInit(T, field.name, data);
|
||||
}
|
||||
}
|
||||
|
||||
if (val) |v| {
|
||||
while (try input.nextKey(alloc, []const u8)) |_| {}
|
||||
return v;
|
||||
}
|
||||
|
||||
return error.UnknownVariant;
|
||||
}
|
||||
|
||||
pub fn visitString(
|
||||
_: @This(),
|
||||
_: ?std.mem.Allocator,
|
||||
comptime Deserializer: type,
|
||||
input: anytype,
|
||||
) Deserializer.Error!T {
|
||||
inline for (@typeInfo(T).Union.fields) |field| {
|
||||
if (std.mem.eql(u8, field.name, input))
|
||||
if (field.field_type == void)
|
||||
return @unionInit(T, field.name, {})
|
||||
else
|
||||
return error.InvalidType;
|
||||
}
|
||||
|
||||
return error.UnknownVariant;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deserialize(
|
||||
alloc: ?std.mem.Allocator,
|
||||
comptime _: type,
|
||||
deserializer: anytype,
|
||||
visitor: anytype,
|
||||
) !@TypeOf(visitor).Value {
|
||||
// This horrifying hack is needed since just trying deserializeString
|
||||
// would consume tokens needed by deserializeMap
|
||||
var tokens_copy = deserializer.context.tokens;
|
||||
const token = (try tokens_copy.next()) orelse
|
||||
return error.UnexpectedEndOfJson;
|
||||
|
||||
return switch (token) {
|
||||
.String => try deserializer.deserializeString(alloc, visitor),
|
||||
.ObjectBegin => try deserializer.deserializeMap(alloc, visitor),
|
||||
else => error.InvalidType,
|
||||
};
|
||||
}
|
||||
|
||||
test "union_db deserialize" {
|
||||
const TestUnion = union(enum) {
|
||||
Foo: u8,
|
||||
Bar: struct { a: u8, b: u8 },
|
||||
Baz: void,
|
||||
};
|
||||
|
||||
const json_src_a =
|
||||
\\ {
|
||||
\\ "Foo": 42
|
||||
\\ }
|
||||
;
|
||||
|
||||
const json_src_b =
|
||||
\\ {
|
||||
\\ "Bar": {
|
||||
\\ "a": 42,
|
||||
\\ "b": 69
|
||||
\\ }
|
||||
\\ }
|
||||
;
|
||||
|
||||
const json_src_c =
|
||||
\\"Baz"
|
||||
;
|
||||
|
||||
var deser_a = try types.OwnedDeserData(TestUnion).deserialize(
|
||||
std.testing.allocator,
|
||||
json_src_a,
|
||||
);
|
||||
defer deser_a.deinit();
|
||||
|
||||
var deser_b = try types.OwnedDeserData(TestUnion).deserialize(
|
||||
std.testing.allocator,
|
||||
json_src_b,
|
||||
);
|
||||
defer deser_b.deinit();
|
||||
|
||||
var deser_c = try types.OwnedDeserData(TestUnion).deserialize(
|
||||
std.testing.allocator,
|
||||
json_src_c,
|
||||
);
|
||||
defer deser_c.deinit();
|
||||
|
||||
try std.testing.expectEqual(TestUnion{ .Foo = 42 }, deser_a.data);
|
||||
|
||||
try std.testing.expectEqual(TestUnion{
|
||||
.Bar = .{
|
||||
.a = 42,
|
||||
.b = 69,
|
||||
},
|
||||
}, deser_b.data);
|
||||
|
||||
try std.testing.expectEqual(TestUnion{ .Baz = {} }, deser_c.data);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
const std = @import("std");
|
||||
pub const types = @import("types.zig");
|
||||
pub const Event = types.Event;
|
||||
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.
|
||||
pub var allocator: ?std.mem.Allocator = null;
|
||||
|
||||
comptime {
|
||||
if (@import("builtin").is_test)
|
||||
std.testing.refAllDecls(@This());
|
||||
}
|
||||
|
||||
/// Creates a plugin that can be called into by zellij. This function should take a
|
||||
/// struct type with the functions of your plugin. Usage of this function should
|
||||
/// look like this:
|
||||
/// ```zig
|
||||
/// // main.zig
|
||||
/// const zz = @import("zellzig");
|
||||
/// comptime {
|
||||
/// zz.createPlugin(@This());
|
||||
/// }
|
||||
///
|
||||
/// pub fn init() void {
|
||||
/// const alloc = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
/// zz.allocator = alloc.allocator();
|
||||
/// }
|
||||
/// pub fn update() void {}
|
||||
/// pub fn render(rows: i32, cols: i32) void {}
|
||||
/// ```
|
||||
pub fn createPlugin(comptime Plugin: type) void {
|
||||
if (@TypeOf(Plugin.init) != fn () void)
|
||||
@compileError("Function 'init' has invalid signature!");
|
||||
|
||||
if (@TypeOf(Plugin.update) != fn (Event) void)
|
||||
@compileError("Function 'update' has invalid signature!");
|
||||
|
||||
if (@TypeOf(Plugin.render) != fn (i32, i32) void)
|
||||
@compileError("Function 'render' has invalid signature!");
|
||||
|
||||
_ = struct {
|
||||
export fn _start() void {
|
||||
Plugin.init();
|
||||
}
|
||||
|
||||
export fn render(rows: i32, cols: i32) void {
|
||||
Plugin.render(rows, cols);
|
||||
}
|
||||
|
||||
export fn update() void {
|
||||
if (allocator == null) {
|
||||
@panic("Got event while allocator is null! Did you forget to set it?");
|
||||
}
|
||||
|
||||
var ev = api.recvObj(types.Event) catch |err| {
|
||||
std.log.err("Deserialize error: {}", .{err});
|
||||
return;
|
||||
};
|
||||
defer ev.deinit();
|
||||
|
||||
Plugin.update(ev.data);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,379 @@
|
|||
const std = @import("std");
|
||||
const getty = @import("getty");
|
||||
const json = @import("json");
|
||||
|
||||
/// getty deserialization blocks required to properly deserialize messages
|
||||
pub const dbs = .{
|
||||
@import("deser/char_or_arrow_db.zig"),
|
||||
@import("deser/line_and_column_db.zig"),
|
||||
@import("deser/union_db.zig"),
|
||||
};
|
||||
|
||||
// required because the only way to properly free deserilized
|
||||
// json is using an arena alloc.
|
||||
pub fn OwnedDeserData(comptime T: type) type {
|
||||
return struct {
|
||||
data: T,
|
||||
arena: std.heap.ArenaAllocator,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn deserialize(alloc: std.mem.Allocator, 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());
|
||||
|
||||
return Self{
|
||||
.data = deser_data,
|
||||
.arena = arena,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.arena.deinit();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const EventType = enum {
|
||||
ModeUpdate,
|
||||
TabUpdate,
|
||||
Key,
|
||||
Mouse,
|
||||
Timer,
|
||||
CopyToClipboard,
|
||||
SystemClipboardFailure,
|
||||
InputReceived,
|
||||
Visible,
|
||||
};
|
||||
|
||||
pub const Event = union(EventType) {
|
||||
ModeUpdate: ModeInfo,
|
||||
TabUpdate: []TabInfo,
|
||||
Key: Key,
|
||||
Mouse: Mouse,
|
||||
Timer: f64,
|
||||
CopyToClipboard: CopyDestination,
|
||||
SystemClipboardFailure,
|
||||
InputReceived,
|
||||
Visible: bool,
|
||||
};
|
||||
|
||||
test "deserialize Event" {
|
||||
const json_src =
|
||||
\\{
|
||||
\\ "ModeUpdate": {
|
||||
\\ "mode": "Normal",
|
||||
\\ "keybinds": [],
|
||||
\\ "style": {
|
||||
\\ "colors": {
|
||||
\\ "source": "Default",
|
||||
\\ "theme_hue": "Dark",
|
||||
\\ "fg": {
|
||||
\\ "EightBit": 15
|
||||
\\ },
|
||||
\\ "bg": {
|
||||
\\ "Rgb": [
|
||||
\\ 40,
|
||||
\\ 42,
|
||||
\\ 54
|
||||
\\ ]
|
||||
\\ },
|
||||
\\ "black": {
|
||||
\\ "EightBit": 0
|
||||
\\ },
|
||||
\\ "red": {
|
||||
\\ "EightBit": 1
|
||||
\\ },
|
||||
\\ "green": {
|
||||
\\ "EightBit": 2
|
||||
\\ },
|
||||
\\ "yellow": {
|
||||
\\ "EightBit": 3
|
||||
\\ },
|
||||
\\ "blue": {
|
||||
\\ "EightBit": 6
|
||||
\\ },
|
||||
\\ "magenta": {
|
||||
\\ "EightBit": 5
|
||||
\\ },
|
||||
\\ "cyan": {
|
||||
\\ "EightBit": 14
|
||||
\\ },
|
||||
\\ "white": {
|
||||
\\ "EightBit": 15
|
||||
\\ },
|
||||
\\ "orange": {
|
||||
\\ "EightBit": 3
|
||||
\\ },
|
||||
\\ "gray": {
|
||||
\\ "EightBit": 0
|
||||
\\ },
|
||||
\\ "purple": {
|
||||
\\ "EightBit": 0
|
||||
\\ },
|
||||
\\ "gold": {
|
||||
\\ "EightBit": 0
|
||||
\\ },
|
||||
\\ "silver": {
|
||||
\\ "EightBit": 0
|
||||
\\ },
|
||||
\\ "pink": {
|
||||
\\ "EightBit": 0
|
||||
\\ },
|
||||
\\ "brown": {
|
||||
\\ "EightBit": 0
|
||||
\\ }
|
||||
\\ },
|
||||
\\ "rounded_corners": false
|
||||
\\ },
|
||||
\\ "capabilities": {
|
||||
\\ "arrow_fonts": false
|
||||
\\ },
|
||||
\\ "session_name": "tasteful-root"
|
||||
\\ }
|
||||
\\}
|
||||
;
|
||||
|
||||
var deser = try OwnedDeserData(Event).deserialize(std.testing.allocator, json_src);
|
||||
defer deser.deinit();
|
||||
}
|
||||
|
||||
pub const ModeInfo = struct {
|
||||
mode: InputMode,
|
||||
keybinds: [][2][]const u8,
|
||||
style: Style,
|
||||
capabilities: PluginCapabilities,
|
||||
session_name: ?[]const u8,
|
||||
};
|
||||
|
||||
pub const InputMode = enum {
|
||||
Normal,
|
||||
Locked,
|
||||
Resize,
|
||||
Pane,
|
||||
Tab,
|
||||
Scroll,
|
||||
RenameTab,
|
||||
RenamePane,
|
||||
Session,
|
||||
Move,
|
||||
Prompt,
|
||||
Tmux,
|
||||
};
|
||||
|
||||
pub const Style = struct {
|
||||
colors: Palette,
|
||||
rounded_corners: bool,
|
||||
};
|
||||
|
||||
pub const Palette = struct {
|
||||
source: PaletteSource,
|
||||
theme_hue: ThemeHue,
|
||||
fg: PaletteColor,
|
||||
bg: PaletteColor,
|
||||
black: PaletteColor,
|
||||
red: PaletteColor,
|
||||
green: PaletteColor,
|
||||
yellow: PaletteColor,
|
||||
blue: PaletteColor,
|
||||
magenta: PaletteColor,
|
||||
cyan: PaletteColor,
|
||||
white: PaletteColor,
|
||||
orange: PaletteColor,
|
||||
gray: PaletteColor,
|
||||
purple: PaletteColor,
|
||||
gold: PaletteColor,
|
||||
silver: PaletteColor,
|
||||
pink: PaletteColor,
|
||||
brown: PaletteColor,
|
||||
};
|
||||
|
||||
test "deserialize Palette" {
|
||||
const json_src =
|
||||
\\ {
|
||||
\\ "source": "Default",
|
||||
\\ "theme_hue": "Dark",
|
||||
\\ "fg": {
|
||||
\\ "EightBit": 15
|
||||
\\ },
|
||||
\\ "bg": {
|
||||
\\ "Rgb": [
|
||||
\\ 40,
|
||||
\\ 42,
|
||||
\\ 54
|
||||
\\ ]
|
||||
\\ },
|
||||
\\ "black": {
|
||||
\\ "EightBit": 0
|
||||
\\ },
|
||||
\\ "red": {
|
||||
\\ "EightBit": 1
|
||||
\\ },
|
||||
\\ "green": {
|
||||
\\ "EightBit": 2
|
||||
\\ },
|
||||
\\ "yellow": {
|
||||
\\ "EightBit": 3
|
||||
\\ },
|
||||
\\ "blue": {
|
||||
\\ "EightBit": 6
|
||||
\\ },
|
||||
\\ "magenta": {
|
||||
\\ "EightBit": 5
|
||||
\\ },
|
||||
\\ "cyan": {
|
||||
\\ "EightBit": 14
|
||||
\\ },
|
||||
\\ "white": {
|
||||
\\ "EightBit": 15
|
||||
\\ },
|
||||
\\ "orange": {
|
||||
\\ "EightBit": 3
|
||||
\\ },
|
||||
\\ "gray": {
|
||||
\\ "EightBit": 0
|
||||
\\ },
|
||||
\\ "purple": {
|
||||
\\ "EightBit": 0
|
||||
\\ },
|
||||
\\ "gold": {
|
||||
\\ "EightBit": 0
|
||||
\\ },
|
||||
\\ "silver": {
|
||||
\\ "EightBit": 0
|
||||
\\ },
|
||||
\\ "pink": {
|
||||
\\ "EightBit": 0
|
||||
\\ },
|
||||
\\ "brown": {
|
||||
\\ "EightBit": 0
|
||||
\\ }
|
||||
\\ }
|
||||
;
|
||||
|
||||
var deser = try OwnedDeserData(Palette).deserialize(std.testing.allocator, json_src);
|
||||
defer deser.deinit();
|
||||
}
|
||||
|
||||
pub const PaletteSource = enum {
|
||||
Default,
|
||||
Xresources,
|
||||
};
|
||||
|
||||
pub const PaletteColor = union(enum) {
|
||||
Rgb: [3]u8,
|
||||
EightBit: u8,
|
||||
};
|
||||
|
||||
pub const TabInfo = struct {
|
||||
position: usize,
|
||||
name: []u8,
|
||||
active: bool,
|
||||
panes_to_hide: usize,
|
||||
is_fullscreen_active: bool,
|
||||
is_sync_panes_active: bool,
|
||||
are_floating_panes_visible: bool,
|
||||
other_focused_clients: []u16,
|
||||
};
|
||||
|
||||
pub const Key = union(enum) {
|
||||
Backspace,
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down,
|
||||
Home,
|
||||
End,
|
||||
PageUp,
|
||||
PageDown,
|
||||
BackTab,
|
||||
Delete,
|
||||
Insert,
|
||||
F: u8,
|
||||
Char: []u8,
|
||||
Alt: CharOrArrow,
|
||||
Ctrl: []u8,
|
||||
Null,
|
||||
Esc,
|
||||
};
|
||||
|
||||
pub const CharOrArrow = union(enum) {
|
||||
Char: u8,
|
||||
Direction: Direction,
|
||||
};
|
||||
|
||||
test "deserialize CharOrArrow" {
|
||||
var data_1 = try OwnedDeserData(CharOrArrow).deserialize(
|
||||
std.testing.allocator,
|
||||
"\"A\"",
|
||||
);
|
||||
defer data_1.deinit();
|
||||
|
||||
var data_2 = try OwnedDeserData(CharOrArrow).deserialize(
|
||||
std.testing.allocator,
|
||||
"\"Left\"",
|
||||
);
|
||||
defer data_2.deinit();
|
||||
|
||||
try std.testing.expectEqual(CharOrArrow{ .Char = 'A' }, data_1.data);
|
||||
try std.testing.expectEqual(CharOrArrow{ .Direction = .Left }, data_2.data);
|
||||
}
|
||||
|
||||
pub const Direction = enum {
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down,
|
||||
};
|
||||
|
||||
pub const Mouse = union(enum) {
|
||||
ScrollUp: usize,
|
||||
ScrollDown: usize,
|
||||
};
|
||||
|
||||
// TODO: implement deserialization block
|
||||
pub const LineAndColumn = struct {
|
||||
line: isize,
|
||||
column: usize,
|
||||
};
|
||||
|
||||
test "deserialize LineAndColumn" {
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
try std.testing.expectEqual(
|
||||
LineAndColumn{ .line = 42, .column = 123 },
|
||||
try json.fromSliceWith(alloc, LineAndColumn, "[42, 123]", dbs),
|
||||
);
|
||||
|
||||
try std.testing.expectEqual(
|
||||
LineAndColumn{ .line = -42, .column = 123 },
|
||||
try json.fromSliceWith(alloc, LineAndColumn, "[-42, 123]", dbs),
|
||||
);
|
||||
}
|
||||
|
||||
pub const CopyDestination = enum {
|
||||
Command,
|
||||
Primary,
|
||||
System,
|
||||
};
|
||||
|
||||
pub const PluginCapabilities = struct {
|
||||
arrow_fonts: bool,
|
||||
};
|
||||
|
||||
pub const ThemeHue = enum {
|
||||
Light,
|
||||
Dark,
|
||||
};
|
||||
|
||||
pub const PluginIds = struct {
|
||||
plugin_id: u32,
|
||||
zellij_pid: u32,
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
pub extern "zellij" fn host_subscribe() void;
|
||||
pub extern "zellij" fn host_unsubscribe() void;
|
||||
pub extern "zellij" fn host_set_selectable(selectable: i32) void;
|
||||
pub extern "zellij" fn host_get_plugin_ids() void;
|
||||
pub extern "zellij" fn host_get_zellij_version() void;
|
||||
pub extern "zellij" fn host_open_file() void;
|
||||
pub extern "zellij" fn host_switch_tab_to(tab_idx: u32) void;
|
||||
pub extern "zellij" fn host_set_timeout(secs: f64) void;
|
||||
pub extern "zellij" fn host_exec_cmd() void;
|
||||
|
Loading…
Reference in New Issue